From f90653f1b8b7afddd779c98f60f7d999f2c2baf2 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Wed, 24 Jan 2018 20:03:34 -0500 Subject: [PATCH 1/7] Fix Dam Softlocks Also remove obsolete comments --- Rules.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Rules.py b/Rules.py index 36b044f1..6f384fd7 100644 --- a/Rules.py +++ b/Rules.py @@ -119,7 +119,6 @@ def global_rules(world): set_rule(world.get_location('Master Sword Pedestal'), lambda state: state.has('Red Pendant') and state.has('Blue Pendant') and state.has('Green Pendant')) set_rule(world.get_location('Sahasrahla'), lambda state: state.has('Green Pendant')) set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has_beam_sword() or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle - # FIXME: VT has a can_kill_most_things(8) call on Aga Tower's entrance. I think this is supposed to reflect that a better weapon than 10 bombs is needed to reach the two chests in this tower set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has('Small Key (Agahnims Tower)', 2)) set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has('Small Key (Agahnims Tower)')) set_rule(world.get_entrance('Top of Pyramid'), lambda state: state.has('Beat Agahnim 1')) @@ -185,6 +184,8 @@ def global_rules(world): set_rule(world.get_entrance('Fairy Ascension Mirror Spot'), lambda state: state.has_Mirror() and state.has_Pearl()) # need to lift flowers set_rule(world.get_entrance('Isolated Ledge Mirror Spot'), lambda state: state.has_Mirror()) set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling + + # Todo: Update the below to reflect that latest ROM has full strengh potions in all difficulties in spike cave set_rule(world.get_location('Spike Cave'), lambda state: state.has('Hammer') and state.can_lift_rocks() and ( @@ -316,8 +317,8 @@ def global_rules(world): set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and - (state.has('Hammer') or state.has_beam_sword() or (state.has_sword and state.can_extend_magic(32)))) - # TODO: Per VT, possibly allow a regular sword with 4x extended magic (ie. quater magic, or half magic+bottle or 3 bottles) + (state.has('Hammer') or state.has_beam_sword() or (state.has_sword() and state.can_extend_magic(32)))) + set_trock_key_rules(world) set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.has('Bow')) @@ -660,6 +661,12 @@ def set_bunny_rules(world): bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge'] + if not world.get_region('Dam').is_light_world: + # if Dam is is dark world, then it is required to have the pearl to get the sunken item + add_rule(world.get_location('Sunken Treasure'), lambda state: state.has_Pearl()) + # similarly we need perl to get across the swamp palace moat + add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Pearl()) + # Add pearl requirements for bunny-impassible caves if they occur in the dark world for region in [world.get_region(name) for name in bunny_impassable_caves]: From d29d298d1f48e0077dcfca377b5e36a8e590675c Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Wed, 24 Jan 2018 20:07:33 -0500 Subject: [PATCH 2/7] Implement Fake World fix Fixes the issues caused by a light world escape with dark world hyrule castle --- BaseClasses.py | 1 + EntranceShuffle.py | 1 + Rom.py | 1 + 3 files changed, 3 insertions(+) diff --git a/BaseClasses.py b/BaseClasses.py index 71760838..6e05c369 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -57,6 +57,7 @@ class World(object): self.customitemarray = customitemarray self.can_take_damage = True self.difficulty_requirements = None + self.fix_fake_world = True self.spoiler = Spoiler(self) def intialize_regions(self): diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 87671a86..3ebf82cb 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -499,6 +499,7 @@ def link_entrances(world): connect_doors(world, single_doors, door_targets) elif world.shuffle == 'insanity': + world.fix_fake_world = False # beware ye who enter here entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)'] diff --git a/Rom.py b/Rom.py index ac20671a..9f731053 100644 --- a/Rom.py +++ b/Rom.py @@ -655,6 +655,7 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None): rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x180034, 0x0A) # starting max bombs rom.write_byte(0x180035, 30) # starting max bombs + rom.write_byte(0x180174, 0x01 if world.fix_fake_world else 0x00) if world.goal in ['pedestal', 'triforcehunt']: rom.write_byte(0x18003E, 0x01) # make ganon invincible From 95bc7c954b641fcd184ecb5f1cc5430a56f0449c Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 27 Jan 2018 14:16:47 -0500 Subject: [PATCH 3/7] Fix TT basement keylock This is fixed by requiring that hammer be accessible before the always allow logic will permit key in the big chest. --- Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 6f384fd7..f9f02d3d 100644 --- a/Rules.py +++ b/Rules.py @@ -258,7 +258,7 @@ def global_rules(world): set_rule(world.get_entrance('Thieves Town Big Key Door'), lambda state: state.has('Big Key (Thieves Town)')) set_rule(world.get_entrance('Blind Fight'), lambda state: state.has('Small Key (Thieves Town)') and (state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna'))) set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer')) - set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)') + set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)' and state.has('Hammer')) set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has('Small Key (Thieves Town)')) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves Town - Blind']: forbid_item(world.get_location(location), 'Big Key (Thieves Town)') From b523d5bf8a8f1692a12bdbf9a604ff58549c0a20 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 27 Jan 2018 14:58:19 -0500 Subject: [PATCH 4/7] Don't mute mirror sound effect --- Rom.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Rom.py b/Rom.py index 9f731053..af360e3d 100644 --- a/Rom.py +++ b/Rom.py @@ -824,8 +824,8 @@ def apply_rom_settings(rom, beep, quickswap, fastmenu, disable_music, sprite): 0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05, 0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD, 0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4, - 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, 0xD3D34, 0xD3D55, 0xD3D6E, - 0xD3DC6, 0xD3E04, 0xD3E38, 0xD3F65, 0xD3FA6, 0xD404F, 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, + 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, + 0xD3F65, 0xD3FA6, 0xD404F, 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, 0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1, 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, 0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27, 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, 0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF, 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, @@ -848,13 +848,22 @@ def apply_rom_settings(rom, beep, quickswap, fastmenu, disable_music, sprite): 0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]), (0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]), (0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]), - (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD3DAA, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, + (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, 0xD9A9E, 0xD9AE4, 0xDA289]), (0xFF, [0xD2085, 0xD21C5, 0xD5F28]) ] for volume, addresses in music_volumes: for address in addresses: rom.write_byte(address, volume if not disable_music else 0x00) + + # restore Mirror sound effect volumes (for existing seeds that lack it) + rom.write_byte(0xD3E04, 0xC8) + rom.write_byte(0xD3DC6, 0xC8) + rom.write_byte(0xD3D6E, 0xC8) + rom.write_byte(0xD3D34, 0xC8) + rom.write_byte(0xD3D55, 0xC8) + rom.write_byte(0xD3E38, 0xC8) + rom.write_byte(0xD3DAA, 0xFA) # set heart beep rate rom.write_byte(0x180033, {'off': 0x00, 'half': 0x40, 'quarter': 0x80, 'normal': 0x20}[beep]) From 405e157da8d37886b0bd8c456cbf4e3c69a7ecc8 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 27 Jan 2018 15:01:16 -0500 Subject: [PATCH 5/7] Adjust white space for music_volumes --- Rom.py | 109 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/Rom.py b/Rom.py index af360e3d..f5204780 100644 --- a/Rom.py +++ b/Rom.py @@ -797,65 +797,64 @@ def apply_rom_settings(rom, beep, quickswap, fastmenu, disable_music, sprite): 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) music_volumes = [ - (0x00, [0xD373B, 0xD375B, 0xD90F8]), - (0x14, [0xDA710, 0xDA7A4, 0xDA7BB, 0xDA7D2]), - (0x3C, [0xD5954, 0xD653B, 0xDA736, 0xDA752, 0xDA772, 0xDA792]), - (0x50, [0xD5B47, 0xD5B5E]), - (0x54, [0xD4306]), - (0x64, [0xD6878, 0xD6883, 0xD6E48, 0xD6E76, 0xD6EFB, 0xD6F2D, 0xDA211, 0xDA35B, 0xDA37B, 0xDA38E, 0xDA39F, 0xDA5C3, 0xDA691, 0xDA6A8, 0xDA6DF]), - (0x78, [0xD2349, 0xD3F45, 0xD42EB, 0xD48B9, 0xD48FF, 0xD543F, 0xD5817, 0xD5957, 0xD5ACB, 0xD5AE8, 0xD5B4A, 0xDA5DE, 0xDA608, 0xDA635, - 0xDA662, 0xDA71F, 0xDA7AF, 0xDA7C6, 0xDA7DD]), - (0x82, [0xD2F00, 0xDA3D5]), - (0xA0, [0xD249C, 0xD24CD, 0xD2C09, 0xD2C53, 0xD2CAF, 0xD2CEB, 0xD2D91, 0xD2EE6, 0xD38ED, 0xD3C91, 0xD3CD3, 0xD3CE8, 0xD3F0C, - 0xD3F82, 0xD405F, 0xD4139, 0xD4198, 0xD41D5, 0xD41F6, 0xD422B, 0xD4270, 0xD42B1, 0xD4334, 0xD4371, 0xD43A6, 0xD43DB, - 0xD441E, 0xD4597, 0xD4B3C, 0xD4BAB, 0xD4C03, 0xD4C53, 0xD4C7F, 0xD4D9C, 0xD5424, 0xD65D2, 0xD664F, 0xD6698, 0xD66FF, - 0xD6985, 0xD6C5C, 0xD6C6F, 0xD6C8E, 0xD6CB4, 0xD6D7D, 0xD827D, 0xD960C, 0xD9828, 0xDA233, 0xDA3A2, 0xDA49E, 0xDA72B, - 0xDA745, 0xDA765, 0xDA785, 0xDABF6, 0xDAC0D, 0xDAEBE, 0xDAFAC]), - (0xAA, [0xD9A02, 0xD9BD6]), - (0xB4, [0xD21CD, 0xD2279, 0xD2E66, 0xD2E70, 0xD2EAB, 0xD3B97, 0xD3BAC, 0xD3BE8, 0xD3C0D, 0xD3C39, 0xD3C68, 0xD3C9F, 0xD3CBC, - 0xD401E, 0xD4290, 0xD443E, 0xD456F, 0xD47D3, 0xD4D43, 0xD4DCC, 0xD4EBA, 0xD4F0B, 0xD4FE5, 0xD5012, 0xD54BC, 0xD54D5, - 0xD54F0, 0xD5509, 0xD57D8, 0xD59B9, 0xD5A2F, 0xD5AEB, 0xD5E5E, 0xD5FE9, 0xD658F, 0xD674A, 0xD6827, 0xD69D6, 0xD69F5, - 0xD6A05, 0xD6AE9, 0xD6DCF, 0xD6E20, 0xD6ECB, 0xD71D4, 0xD71E6, 0xD7203, 0xD721E, 0xD8724, 0xD8732, 0xD9652, 0xD9698, - 0xD9CBC, 0xD9DC0, 0xD9E49, 0xDAA68, 0xDAA77, 0xDAA88, 0xDAA99, 0xDAF04]), - (0x8c, [0xD1D28, 0xD1D41, 0xD1D5C, 0xD1D77, 0xD1EEE, 0xD311D, 0xD31D1, 0xD4148, 0xD5543, 0xD5B6F, 0xD65B3, 0xD6760, 0xD6B6B, - 0xD6DF6, 0xD6E0D, 0xD73A1, 0xD814C, 0xD825D, 0xD82BE, 0xD8340, 0xD8394, 0xD842C, 0xD8796, 0xD8903, 0xD892A, 0xD91E8, - 0xD922B, 0xD92E0, 0xD937E, 0xD93C1, 0xDA958, 0xDA971, 0xDA98C, 0xDA9A7]), - (0xC8, [0xD1D92, 0xD1DBD, 0xD1DEB, 0xD1F5D, 0xD1F9F, 0xD1FBD, 0xD1FDC, 0xD1FEA, 0xD20CA, 0xD21BB, 0xD22C9, 0xD2754, 0xD284C, - 0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05, - 0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD, - 0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4, - 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, - 0xD3F65, 0xD3FA6, 0xD404F, 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, - 0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1, 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, - 0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27, 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, - 0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF, 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, - 0xD52AF, 0xD52BD, 0xD52CD, 0xD52DB, 0xD549C, 0xD5801, 0xD58A4, 0xD5A68, 0xD5A7F, 0xD5C12, 0xD5D71, 0xD5E10, 0xD5E9A, - 0xD5F8B, 0xD5FA4, 0xD651A, 0xD6542, 0xD65ED, 0xD661D, 0xD66D7, 0xD6776, 0xD68BD, 0xD68E5, 0xD6956, 0xD6973, 0xD69A8, - 0xD6A51, 0xD6A86, 0xD6B96, 0xD6C3E, 0xD6D4A, 0xD6E9C, 0xD6F80, 0xD717E, 0xD7190, 0xD71B9, 0xD811D, 0xD8139, 0xD816B, - 0xD818A, 0xD819E, 0xD81BE, 0xD829C, 0xD82E1, 0xD8306, 0xD830E, 0xD835E, 0xD83AB, 0xD83CA, 0xD83F0, 0xD83F8, 0xD844B, - 0xD8479, 0xD849E, 0xD84CB, 0xD84EB, 0xD84F3, 0xD854A, 0xD8573, 0xD859D, 0xD85B4, 0xD85CE, 0xD862A, 0xD8681, 0xD87E3, - 0xD87FF, 0xD887B, 0xD88C6, 0xD88E3, 0xD8944, 0xD897B, 0xD8C97, 0xD8CA4, 0xD8CB3, 0xD8CC2, 0xD8CD1, 0xD8D01, 0xD917B, - 0xD918C, 0xD919A, 0xD91B5, 0xD91D0, 0xD91DD, 0xD9220, 0xD9273, 0xD9284, 0xD9292, 0xD92AD, 0xD92C8, 0xD92D5, 0xD9311, - 0xD9322, 0xD9330, 0xD934B, 0xD9366, 0xD9373, 0xD93B6, 0xD97A6, 0xD97C2, 0xD97DC, 0xD97FB, 0xD9811, 0xD98FF, 0xD996F, - 0xD99A8, 0xD99D5, 0xD9A30, 0xD9A4E, 0xD9A6B, 0xD9A88, 0xD9AF7, 0xD9B1D, 0xD9B43, 0xD9B7C, 0xD9BA9, 0xD9C84, 0xD9C8D, - 0xD9CAC, 0xD9CE8, 0xD9CF3, 0xD9CFD, 0xD9D46, 0xDA35E, 0xDA37E, 0xDA391, 0xDA478, 0xDA4C3, 0xDA4D7, 0xDA4F6, 0xDA515, - 0xDA6E2, 0xDA9C2, 0xDA9ED, 0xDAA1B, 0xDAA57, 0xDABAF, 0xDABC9, 0xDABE2, 0xDAC28, 0xDAC46, 0xDAC63, 0xDACB8, 0xDACEC, - 0xDAD08, 0xDAD25, 0xDAD42, 0xDAD5F, 0xDAE17, 0xDAE34, 0xDAE51, 0xDAF2E, 0xDAF55, 0xDAF6B, 0xDAF81, 0xDB14F, 0xDB16B, - 0xDB180, 0xDB195, 0xDB1AA]), - (0xD2, [0xD2B88, 0xD364A, 0xD369F, 0xD3747]), - (0xDC, [0xD213F, 0xD2174, 0xD229E, 0xD2426, 0xD4731, 0xD4753, 0xD4774, 0xD4795, 0xD47B6, 0xD4AA5, 0xD4AE4, 0xD4B96, 0xD4CA5, - 0xD5477, 0xD5A3D, 0xD6566, 0xD672C, 0xD67C0, 0xD69B8, 0xD6AB1, 0xD6C05, 0xD6DB3, 0xD71AB, 0xD8E2D, 0xD8F0D, 0xD94E0, - 0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]), - (0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]), - (0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]), - (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, - 0xD9A9E, 0xD9AE4, 0xDA289]), - (0xFF, [0xD2085, 0xD21C5, 0xD5F28]) + (0x00, [0xD373B, 0xD375B, 0xD90F8]), + (0x14, [0xDA710, 0xDA7A4, 0xDA7BB, 0xDA7D2]), + (0x3C, [0xD5954, 0xD653B, 0xDA736, 0xDA752, 0xDA772, 0xDA792]), + (0x50, [0xD5B47, 0xD5B5E]), + (0x54, [0xD4306]), + (0x64, [0xD6878, 0xD6883, 0xD6E48, 0xD6E76, 0xD6EFB, 0xD6F2D, 0xDA211, 0xDA35B, 0xDA37B, 0xDA38E, 0xDA39F, 0xDA5C3, 0xDA691, 0xDA6A8, 0xDA6DF]), + (0x78, [0xD2349, 0xD3F45, 0xD42EB, 0xD48B9, 0xD48FF, 0xD543F, 0xD5817, 0xD5957, 0xD5ACB, 0xD5AE8, 0xD5B4A, 0xDA5DE, 0xDA608, 0xDA635, + 0xDA662, 0xDA71F, 0xDA7AF, 0xDA7C6, 0xDA7DD]), + (0x82, [0xD2F00, 0xDA3D5]), + (0xA0, [0xD249C, 0xD24CD, 0xD2C09, 0xD2C53, 0xD2CAF, 0xD2CEB, 0xD2D91, 0xD2EE6, 0xD38ED, 0xD3C91, 0xD3CD3, 0xD3CE8, 0xD3F0C, + 0xD3F82, 0xD405F, 0xD4139, 0xD4198, 0xD41D5, 0xD41F6, 0xD422B, 0xD4270, 0xD42B1, 0xD4334, 0xD4371, 0xD43A6, 0xD43DB, + 0xD441E, 0xD4597, 0xD4B3C, 0xD4BAB, 0xD4C03, 0xD4C53, 0xD4C7F, 0xD4D9C, 0xD5424, 0xD65D2, 0xD664F, 0xD6698, 0xD66FF, + 0xD6985, 0xD6C5C, 0xD6C6F, 0xD6C8E, 0xD6CB4, 0xD6D7D, 0xD827D, 0xD960C, 0xD9828, 0xDA233, 0xDA3A2, 0xDA49E, 0xDA72B, + 0xDA745, 0xDA765, 0xDA785, 0xDABF6, 0xDAC0D, 0xDAEBE, 0xDAFAC]), + (0xAA, [0xD9A02, 0xD9BD6]), + (0xB4, [0xD21CD, 0xD2279, 0xD2E66, 0xD2E70, 0xD2EAB, 0xD3B97, 0xD3BAC, 0xD3BE8, 0xD3C0D, 0xD3C39, 0xD3C68, 0xD3C9F, 0xD3CBC, + 0xD401E, 0xD4290, 0xD443E, 0xD456F, 0xD47D3, 0xD4D43, 0xD4DCC, 0xD4EBA, 0xD4F0B, 0xD4FE5, 0xD5012, 0xD54BC, 0xD54D5, + 0xD54F0, 0xD5509, 0xD57D8, 0xD59B9, 0xD5A2F, 0xD5AEB, 0xD5E5E, 0xD5FE9, 0xD658F, 0xD674A, 0xD6827, 0xD69D6, 0xD69F5, + 0xD6A05, 0xD6AE9, 0xD6DCF, 0xD6E20, 0xD6ECB, 0xD71D4, 0xD71E6, 0xD7203, 0xD721E, 0xD8724, 0xD8732, 0xD9652, 0xD9698, + 0xD9CBC, 0xD9DC0, 0xD9E49, 0xDAA68, 0xDAA77, 0xDAA88, 0xDAA99, 0xDAF04]), + (0x8c, [0xD1D28, 0xD1D41, 0xD1D5C, 0xD1D77, 0xD1EEE, 0xD311D, 0xD31D1, 0xD4148, 0xD5543, 0xD5B6F, 0xD65B3, 0xD6760, 0xD6B6B, + 0xD6DF6, 0xD6E0D, 0xD73A1, 0xD814C, 0xD825D, 0xD82BE, 0xD8340, 0xD8394, 0xD842C, 0xD8796, 0xD8903, 0xD892A, 0xD91E8, + 0xD922B, 0xD92E0, 0xD937E, 0xD93C1, 0xDA958, 0xDA971, 0xDA98C, 0xDA9A7]), + (0xC8, [0xD1D92, 0xD1DBD, 0xD1DEB, 0xD1F5D, 0xD1F9F, 0xD1FBD, 0xD1FDC, 0xD1FEA, 0xD20CA, 0xD21BB, 0xD22C9, 0xD2754, 0xD284C, + 0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05, + 0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD, + 0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4, + 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, 0xD3F65, 0xD3FA6, 0xD404F, + 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, 0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1, + 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, 0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27, + 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, 0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF, + 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, 0xD52AF, 0xD52BD, 0xD52CD, 0xD52DB, 0xD549C, 0xD5801, + 0xD58A4, 0xD5A68, 0xD5A7F, 0xD5C12, 0xD5D71, 0xD5E10, 0xD5E9A, 0xD5F8B, 0xD5FA4, 0xD651A, 0xD6542, 0xD65ED, 0xD661D, + 0xD66D7, 0xD6776, 0xD68BD, 0xD68E5, 0xD6956, 0xD6973, 0xD69A8, 0xD6A51, 0xD6A86, 0xD6B96, 0xD6C3E, 0xD6D4A, 0xD6E9C, + 0xD6F80, 0xD717E, 0xD7190, 0xD71B9, 0xD811D, 0xD8139, 0xD816B, 0xD818A, 0xD819E, 0xD81BE, 0xD829C, 0xD82E1, 0xD8306, + 0xD830E, 0xD835E, 0xD83AB, 0xD83CA, 0xD83F0, 0xD83F8, 0xD844B, 0xD8479, 0xD849E, 0xD84CB, 0xD84EB, 0xD84F3, 0xD854A, + 0xD8573, 0xD859D, 0xD85B4, 0xD85CE, 0xD862A, 0xD8681, 0xD87E3, 0xD87FF, 0xD887B, 0xD88C6, 0xD88E3, 0xD8944, 0xD897B, + 0xD8C97, 0xD8CA4, 0xD8CB3, 0xD8CC2, 0xD8CD1, 0xD8D01, 0xD917B, 0xD918C, 0xD919A, 0xD91B5, 0xD91D0, 0xD91DD, 0xD9220, + 0xD9273, 0xD9284, 0xD9292, 0xD92AD, 0xD92C8, 0xD92D5, 0xD9311, 0xD9322, 0xD9330, 0xD934B, 0xD9366, 0xD9373, 0xD93B6, + 0xD97A6, 0xD97C2, 0xD97DC, 0xD97FB, 0xD9811, 0xD98FF, 0xD996F, 0xD99A8, 0xD99D5, 0xD9A30, 0xD9A4E, 0xD9A6B, 0xD9A88, + 0xD9AF7, 0xD9B1D, 0xD9B43, 0xD9B7C, 0xD9BA9, 0xD9C84, 0xD9C8D, 0xD9CAC, 0xD9CE8, 0xD9CF3, 0xD9CFD, 0xD9D46, 0xDA35E, + 0xDA37E, 0xDA391, 0xDA478, 0xDA4C3, 0xDA4D7, 0xDA4F6, 0xDA515, 0xDA6E2, 0xDA9C2, 0xDA9ED, 0xDAA1B, 0xDAA57, 0xDABAF, + 0xDABC9, 0xDABE2, 0xDAC28, 0xDAC46, 0xDAC63, 0xDACB8, 0xDACEC, 0xDAD08, 0xDAD25, 0xDAD42, 0xDAD5F, 0xDAE17, 0xDAE34, + 0xDAE51, 0xDAF2E, 0xDAF55, 0xDAF6B, 0xDAF81, 0xDB14F, 0xDB16B, 0xDB180, 0xDB195, 0xDB1AA]), + (0xD2, [0xD2B88, 0xD364A, 0xD369F, 0xD3747]), + (0xDC, [0xD213F, 0xD2174, 0xD229E, 0xD2426, 0xD4731, 0xD4753, 0xD4774, 0xD4795, 0xD47B6, 0xD4AA5, 0xD4AE4, 0xD4B96, 0xD4CA5, + 0xD5477, 0xD5A3D, 0xD6566, 0xD672C, 0xD67C0, 0xD69B8, 0xD6AB1, 0xD6C05, 0xD6DB3, 0xD71AB, 0xD8E2D, 0xD8F0D, 0xD94E0, + 0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]), + (0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]), + (0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]), + (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, 0xD9A9E, + 0xD9AE4, 0xDA289]), + (0xFF, [0xD2085, 0xD21C5, 0xD5F28]) ] for volume, addresses in music_volumes: for address in addresses: rom.write_byte(address, volume if not disable_music else 0x00) - + # restore Mirror sound effect volumes (for existing seeds that lack it) rom.write_byte(0xD3E04, 0xC8) rom.write_byte(0xD3DC6, 0xC8) From e19c0ada7017af6be48f43f6763b71fe39703d3b Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 27 Jan 2018 16:21:32 -0500 Subject: [PATCH 6/7] Retry Logic for Dungeon Prizes Will help make avoid seed failure for custom pool seeds. This won't help with a seed that has a layout that is not compatible with the item pool though. --- Fill.py | 14 ++++++++------ ItemList.py | 29 ++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Fill.py b/Fill.py index 2ac444de..b663e088 100644 --- a/Fill.py +++ b/Fill.py @@ -1,6 +1,8 @@ import random import logging +class FillError(RuntimeError): + pass def distribute_items_cutoff(world, cutoffrate=0.33): # get list of locations to fill in @@ -53,7 +55,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33): logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.') progress_done = True continue - raise RuntimeError('No more progress items left to place.') + raise FillError('No more progress items left to place.') spot_to_fill = None for location in fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed(fill_locations): @@ -66,7 +68,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33): if world.can_beat_game(): logging.getLogger('').warning('Not all items placed. Game beatable anyway.') break - raise RuntimeError('No more spots to place %s' % item_to_place) + raise FillError('No more spots to place %s' % item_to_place) world.push_item(spot_to_fill, item_to_place, True) itempool.remove(item_to_place) @@ -121,7 +123,7 @@ def distribute_items_staleness(world): logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.') progress_done = True continue - raise RuntimeError('No more progress items left to place.') + raise FillError('No more progress items left to place.') spot_to_fill = None for location in fill_locations: @@ -147,7 +149,7 @@ def distribute_items_staleness(world): if world.can_beat_game(): logging.getLogger('').warning('Not all items placed. Game beatable anyway.') break - raise RuntimeError('No more spots to place %s' % item_to_place) + raise FillError('No more spots to place %s' % item_to_place) world.push_item(spot_to_fill, item_to_place, True) itempool.remove(item_to_place) @@ -185,7 +187,7 @@ def fill_restrictive(world, base_state, locations, itempool): if not world.check_beatable_only: logging.getLogger('').warning('Not all items placed. Game beatable anyway.') break - raise RuntimeError('No more spots to place %s' % item_to_place) + raise FillError('No more spots to place %s' % item_to_place) world.push_item(spot_to_fill, item_to_place, False) locations.remove(spot_to_fill) @@ -281,7 +283,7 @@ def flood_items(world): if candidate_item_to_place is not None: item_to_place = candidate_item_to_place else: - raise RuntimeError('No more progress items left to place.') + raise FillError('No more progress items left to place.') # find item to replace with progress item location_list = world.get_reachable_locations() diff --git a/ItemList.py b/ItemList.py index 6709fd10..065e4abc 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1,8 +1,9 @@ from collections import namedtuple +import logging import random from Items import ItemFactory -from Fill import fill_restrictive +from Fill import FillError, fill_restrictive from Dungeons import get_dungeon_item_pool #This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. @@ -248,14 +249,36 @@ def generate_itempool(world): world.required_medallions = (mm_medallion, tr_medallion) # distribute crystals + fill_prizes(world) + +def fill_prizes(world, attempts=15): crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6']) crystal_locations = [world.get_location('Turtle Rock - Prize'), world.get_location('Eastern Palace - Prize'), world.get_location('Desert Palace - Prize'), world.get_location('Tower of Hera - Prize'), world.get_location('Palace of Darkness - Prize'), world.get_location('Thieves Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'), world.get_location('Misery Mire - Prize')] + placed_prizes = [loc.item.name for loc in crystal_locations if loc.item is not None] + unplaced_prizes = [crystal for crystal in crystals if crystal.name not in placed_prizes] + empty_crystal_locations = [loc for loc in crystal_locations if loc.item is None] + + while attempts: + attempts -= 1 + try: + prizepool = list(unplaced_prizes) + prize_locs = list(empty_crystal_locations) + random.shuffle(prizepool) + random.shuffle(prize_locs) + fill_restrictive(world, world.get_all_state(keys=True), prize_locs, prizepool) + except FillError: + logging.getLogger('').info("Failed to place dungeon prizes. Will retry %s more times", attempts) + for location in empty_crystal_locations: + location.item = None + continue + break + else: + raise FillError('Unable to place dungeon prizes') + - random.shuffle(crystal_locations) - fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals) def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode): pool = [] From 570ee24fc012a879c66c991374f88727f66ae3b9 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 27 Jan 2018 17:11:53 -0500 Subject: [PATCH 7/7] Make Shuffle Ganon the Default --- EntranceRandomizer.py | 10 ++++++---- Gui.py | 1 + README.md | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 91b3a633..fc759ec6 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -174,10 +174,12 @@ def start(): ensure all locations are reachable. This only has an effect on the restrictive algorithm currently. ''', action='store_true') - parser.add_argument('--shuffleganon', help='''\ - If set, include the Pyramid Hole and Ganon's Tower in the - entrance shuffle pool. - ''', action='store_true') + # included for backwards compatibility + parser.add_argument('--shuffleganon', help=argparse.SUPPRESS, action='store_true', default=True) + parser.add_argument('--no-shuffleganon', help='''\ + If set, the Pyramid Hole and Ganon's Tower are not + included entrance shuffle pool. + ''', action='store_false', dest='shuffleganon') parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'], help='''\ Select the rate at which the heart beep sound is played at diff --git a/Gui.py b/Gui.py index c04ba514..0a806d1d 100755 --- a/Gui.py +++ b/Gui.py @@ -69,6 +69,7 @@ def guiMain(args=None): disableMusicVar = IntVar() disableMusicCheckbutton = Checkbutton(checkBoxFrame, text="Disable game music", variable=disableMusicVar) shuffleGanonVar = IntVar() + shuffleGanonVar.set(1) #set default shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool", variable=shuffleGanonVar) customVar = IntVar() customCheckbutton = Checkbutton(checkBoxFrame, text="Use custom item pool", variable=customVar) diff --git a/README.md b/README.md index e5455b9b..1c68328a 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Insane 50/50 Standard game completion requiring you to collect the 7 crystals and then beat Ganon. -This is only noticeably different if the --shuffleganon option is enabled. +This is only noticeably different if the the Ganon shuffle option is enabled. ## Game Difficulty @@ -422,10 +422,10 @@ Use to select a different sprite sheet to use for Link. Path to a binary file of Enables the "Only Ensure Seed Beatable" option (default: False) ``` ---shuffleganon +--no-shuffleganon ``` -Enables the "Include Ganon's Tower and Pyramid Hole in Shuffle pool" option. (default: false) +Disables the "Include Ganon's Tower and Pyramid Hole in Shuffle pool" option. (default: Enabled) ``` --suppress_rom