diff --git a/Patches.py b/Patches.py index 0a9d892ab..e2fbec405 100644 --- a/Patches.py +++ b/Patches.py @@ -1017,18 +1017,6 @@ def set_entrance_updates(entrances: Iterable[Entrance]) -> None: elif world.settings.open_kakariko != 'closed': rom.write_byte(rom.sym('OPEN_KAKARIKO'), 1) - # Mark starting trade items as owned - # The effective starting item seen in the player inventory will be the - # latest shuffled item in the trade sequence, calculated in - # Plandomizer.WorldDistribution.configure_effective_starting_items. - owned_flags = 0 - for item_name in world.distribution.starting_items.keys(): - if item_name in child_trade_items: - owned_flags += 0x1 << (child_trade_items.index(item_name)) - if item_name in trade_items: - owned_flags += 0x1 << (trade_items.index(item_name) + 11) - save_context.write_permanent_flags(Scenes.DEATH_MOUNTAIN_TRAIL, FlagType.UNK00, owned_flags) - # Mark unreachable trade-ins as traded. Only applicable with trade quest shuffle off, # and only practically affects the Blue Potion purchase from Granny's Potion Shop. if not world.settings.adult_trade_shuffle and len(world.settings.adult_trade_start) > 0: @@ -1068,7 +1056,7 @@ def calculate_traded_flags(world): save_context.write_bits(0x0EDD, 0x01) # "Obtained Zelda's Letter" save_context.write_bits(0x0EDE, 0x02) # "Learned Zelda's Lullaby" save_context.write_bits(0x00D4 + 0x5F * 0x1C + 0x04 + 0x3, 0x10) # "Moved crates to access the courtyard" - if world.skip_child_zelda or "Zeldas Letter" in world.distribution.starting_items.keys(): + if 'Zeldas Letter' in world.distribution.starting_items: if world.settings.open_kakariko != 'closed': save_context.write_bits(0x0F07, 0x40) # "Spoke to Gate Guard About Mask Shop" if world.settings.complete_mask_quest: diff --git a/Plandomizer.py b/Plandomizer.py index a01605610..307b5bd54 100644 --- a/Plandomizer.py +++ b/Plandomizer.py @@ -1108,32 +1108,18 @@ def configure_effective_starting_items(self, worlds: list[World], world: World) add_starting_item_with_ammo(items, location.item.name) effective_adult_trade_item_index = -1 - effective_child_trade_item_index = -1 - effective_adult_trade_item = None - effective_child_trade_item = None - trade_starting_items = list(items.keys()) - for item_name in trade_starting_items: + for item_name in items: if item_name in trade_items: if item_name in world.settings.adult_trade_start: if trade_items.index(item_name) > effective_adult_trade_item_index: effective_adult_trade_item_index = trade_items.index(item_name) - effective_adult_trade_item = items[item_name] else: raise RuntimeError(f'An unshuffled trade item was included as a starting item. Please either remove {item_name} from starting items or add it to Adult Trade Sequence Items.') - del items[item_name] if item_name in child_trade_items: - if item_name in world.settings.shuffle_child_trade or item_name == 'Zeldas Letter': - if child_trade_items.index(item_name) > effective_child_trade_item_index: - effective_child_trade_item_index = child_trade_items.index(item_name) - effective_child_trade_item = items[item_name] - else: + if item_name not in world.settings.shuffle_child_trade and item_name != 'Zeldas Letter': raise RuntimeError(f'An unshuffled trade item was included as a starting item. Please either remove {item_name} from starting items or add it to Shuffled Child Trade Sequence Items.') - del items[item_name] - if effective_child_trade_item_index >= 0: - items[child_trade_items[effective_child_trade_item_index]] = effective_child_trade_item if effective_adult_trade_item_index >= 0: - items[trade_items[effective_adult_trade_item_index]] = effective_adult_trade_item world.adult_trade_starting_inventory = trade_items[effective_adult_trade_item_index] self.effective_starting_items = items diff --git a/README.md b/README.md index b1489cbee..8685c26f2 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ issue. You should always Hard Reset to avoid this issue entirely. * Fix a Mac-specific issue when loading track .meta files. * Fix an error in the easy bite fishing hack. * The randomizer no longer ignores errors when decompressing the base rom or compressing the randomized rom. +* Trade quest items from skipped locations are no longer lost when another trade item is found. #### New Speedups * The first text box from each carpenter in the Thieves' Hideout is skipped. diff --git a/SaveContext.py b/SaveContext.py index 83ea11383..12b6a8aea 100644 --- a/SaveContext.py +++ b/SaveContext.py @@ -858,11 +858,37 @@ def get_save_context_addresses() -> AddressesDict: }, 'triforce_pieces' : Address(0xD4 + 0x1C * 0x48 + 0x10, size=4), # Unused word in scene x48 'pending_freezes' : Address(0xD4 + 0x1C * 0x49 + 0x10, size=4), # Unused word in scene x49 - 'Ocarina_A_Button' : Address(0xD4 + 0x1C * 0x50 + 0x10, mask=0x01), # Unused word in scene x50 - 'Ocarina_C_up_Button' : Address(0xD4 + 0x1C * 0x50 + 0x10, mask=0x02), # Unused word in scene x50 - 'Ocarina_C_down_Button' : Address(0xD4 + 0x1C * 0x50 + 0x10, mask=0x04), # Unused word in scene x50 - 'Ocarina_C_left_Button' : Address(0xD4 + 0x1C * 0x50 + 0x10, mask=0x08), # Unused word in scene x50 - 'Ocarina_C_right_Button' : Address(0xD4 + 0x1C * 0x50 + 0x10, mask=0x10), # Unused word in scene x50 + 'ocarina_buttons' : { # Unused word in scene x50 + 'a' : Address(0xD4 + 0x1C * 0x50 + 0x10, size=4, mask=0x00000001), + 'c_up' : Address(0xD4 + 0x1C * 0x50 + 0x10, size=4, mask=0x00000002), + 'c_down' : Address(0xD4 + 0x1C * 0x50 + 0x10, size=4, mask=0x00000004), + 'c_left' : Address(0xD4 + 0x1C * 0x50 + 0x10, size=4, mask=0x00000008), + 'c_right' : Address(0xD4 + 0x1C * 0x50 + 0x10, size=4, mask=0x00000010), + }, + 'owned_trade_items' : { # Unused word in scene x60 + 'weird_egg' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000001), + 'chicken' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000002), + 'zeldas_letter' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000004), + 'keaton_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000008), + 'skull_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000010), + 'spooky_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000020), + 'bunny_hood' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000040), + 'goron_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000080), + 'zora_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000100), + 'gerudo_mask' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000200), + 'mask_of_truth' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000400), + 'pocket_egg' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00000800), + 'pocket_cucco' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00001000), + 'cojiro' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00002000), + 'odd_mushroom' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00004000), + 'odd_potion' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00008000), + 'poachers_saw' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00010000), + 'broken_sword' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00020000), + 'prescription' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00040000), + 'eyeball_frog' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00080000), + 'eyedrops' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00100000), + 'claim_check' : Address(0xD4 + 0x1C * 0x60 + 0x10, size=4, mask=0x00200000), + }, # begin extended save data items 'silver_rupee_counts' : { @@ -1129,28 +1155,94 @@ def get_save_context_addresses() -> AddressesDict: "Boomerang" : {'item_slot.boomerang' : 'boomerang'}, "Lens of Truth" : {'item_slot.lens' : 'lens'}, "Megaton Hammer" : {'item_slot.hammer' : 'hammer'}, - "Pocket Egg" : {'item_slot.adult_trade' : 'pocket_egg'}, - "Pocket Cucco" : {'item_slot.adult_trade' : 'pocket_cucco'}, - "Cojiro" : {'item_slot.adult_trade' : 'cojiro'}, - "Odd Mushroom" : {'item_slot.adult_trade' : 'odd_mushroom'}, - "Odd Potion" : {'item_slot.adult_trade' : 'odd_potion'}, - "Poachers Saw" : {'item_slot.adult_trade' : 'poachers_saw'}, - "Broken Sword" : {'item_slot.adult_trade' : 'broken_sword'}, - "Prescription" : {'item_slot.adult_trade' : 'prescription'}, - "Eyeball Frog" : {'item_slot.adult_trade' : 'eyeball_frog'}, - "Eyedrops" : {'item_slot.adult_trade' : 'eye_drops'}, - "Claim Check" : {'item_slot.adult_trade' : 'claim_check'}, - "Weird Egg" : {'item_slot.child_trade' : 'weird_egg'}, - "Chicken" : {'item_slot.child_trade' : 'chicken'}, - "Zeldas Letter" : {'item_slot.child_trade' : 'zeldas_letter'}, - "Keaton Mask" : {'item_slot.child_trade' : 'keaton_mask'}, - "Skull Mask" : {'item_slot.child_trade' : 'skull_mask'}, - "Spooky Mask" : {'item_slot.child_trade' : 'spooky_mask'}, - "Bunny Hood" : {'item_slot.child_trade' : 'bunny_hood'}, - "Goron Mask" : {'item_slot.child_trade' : 'goron_mask'}, - "Zora Mask" : {'item_slot.child_trade' : 'zora_mask'}, - "Gerudo Mask" : {'item_slot.child_trade' : 'gerudo_mask'}, - "Mask of Truth" : {'item_slot.child_trade' : 'mask_of_truth'}, + "Pocket Egg" : { + 'item_slot.adult_trade' : 'pocket_egg', + 'owned_trade_items.pocket_egg' : True, + }, + "Pocket Cucco" : { + 'item_slot.adult_trade' : 'pocket_cucco', + 'owned_trade_items.pocket_cucco' : True, + }, + "Cojiro" : { + 'item_slot.adult_trade' : 'cojiro', + 'owned_trade_items.cojiro' : True, + }, + "Odd Mushroom" : { + 'item_slot.adult_trade' : 'odd_mushroom', + 'owned_trade_items.odd_mushroom' : True, + }, + "Odd Potion" : { + 'item_slot.adult_trade' : 'odd_potion', + 'owned_trade_items.odd_potion' : True, + }, + "Poachers Saw" : { + 'item_slot.adult_trade' : 'poachers_saw', + 'owned_trade_items.poachers_saw' : True, + }, + "Broken Sword" : { + 'item_slot.adult_trade' : 'broken_sword', + 'owned_trade_items.broken_sword' : True, + }, + "Prescription" : { + 'item_slot.adult_trade' : 'prescription', + 'owned_trade_items.prescription' : True, + }, + "Eyeball Frog" : { + 'item_slot.adult_trade' : 'eyeball_frog', + 'owned_trade_items.eyeball_frog' : True, + }, + "Eyedrops" : { + 'item_slot.adult_trade' : 'eye_drops', + 'owned_trade_items.eye_drops' : True, + }, + "Claim Check" : { + 'item_slot.adult_trade' : 'claim_check', + 'owned_trade_items.claim_check' : True, + }, + "Weird Egg" : { + 'item_slot.child_trade' : 'weird_egg', + 'owned_trade_items.weird_egg' : True, + }, + "Chicken" : { + 'item_slot.child_trade' : 'chicken', + 'owned_trade_items.chicken' : True, + }, + "Zeldas Letter" : { + 'item_slot.child_trade' : 'zeldas_letter', + 'owned_trade_items.zeldas_letter' : True, + }, + "Keaton Mask" : { + 'item_slot.child_trade' : 'keaton_mask', + 'owned_trade_items.keaton_mask' : True, + }, + "Skull Mask" : { + 'item_slot.child_trade' : 'skull_mask', + 'owned_trade_items.skull_mask' : True, + }, + "Spooky Mask" : { + 'item_slot.child_trade' : 'spooky_mask', + 'owned_trade_items.spooky_mask' : True, + }, + "Bunny Hood" : { + 'item_slot.child_trade' : 'bunny_hood', + 'owned_trade_items.bunny_hood' : True, + }, + "Goron Mask" : { + 'item_slot.child_trade' : 'goron_mask', + 'owned_trade_items.goron_mask' : True, + }, + "Zora Mask" : { + 'item_slot.child_trade' : 'zora_mask', + 'owned_trade_items.zora_mask' : True, + }, + "Gerudo Mask" : { + 'item_slot.child_trade' : 'gerudo_mask', + 'owned_trade_items.gerudo_mask' : True, + }, + "Mask of Truth" : { + 'item_slot.child_trade' : 'mask_of_truth', + 'owned_trade_items.mask_of_truth' : True, + }, "Goron Tunic" : {'equip_items.goron_tunic' : True}, "Zora Tunic" : {'equip_items.zora_tunic' : True}, "Iron Boots" : {'equip_items.iron_boots' : True}, @@ -1219,11 +1311,11 @@ def get_save_context_addresses() -> AddressesDict: }, "Ice Trap" : {'pending_freezes': None}, "Triforce Piece" : {'triforce_pieces': None}, - "Ocarina A Button" : {'Ocarina_A_Button': True}, - "Ocarina C up Button" : {'Ocarina_C_up_Button': True}, - "Ocarina C down Button" : {'Ocarina_C_down_Button': True}, - "Ocarina C left Button" : {'Ocarina_C_left_Button': True}, - "Ocarina C right Button" : {'Ocarina_C_right_Button': True}, + "Ocarina A Button" : {'ocarina_buttons.a': True}, + "Ocarina C up Button" : {'ocarina_buttons.c_up': True}, + "Ocarina C down Button" : {'ocarina_buttons.c_down': True}, + "Ocarina C left Button" : {'ocarina_buttons.c_left': True}, + "Ocarina C right Button" : {'ocarina_buttons.c_right': True}, "Boss Key (Forest Temple)" : {'dungeon_items.forest.boss_key': True}, "Boss Key (Fire Temple)" : {'dungeon_items.fire.boss_key': True}, "Boss Key (Water Temple)" : {'dungeon_items.water.boss_key': True}, diff --git a/version.py b/version.py index f1355b390..8d99e54ca 100644 --- a/version.py +++ b/version.py @@ -1,4 +1,4 @@ -__version__ = '8.2.39' +__version__ = '8.2.40' # This is a supplemental version number for branches based off of main dev. supplementary_version = 0