From 81cc01626769f1a4d43460dcd8313f72c5856680 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 13 Nov 2023 06:50:45 +0100 Subject: [PATCH] LttP: write fairy bottle fill to spoiler and prevent fart in a bottle (#2424) --- worlds/alttp/Rom.py | 9 ++--- worlds/alttp/__init__.py | 74 +++++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index e1ae0cc6..b80cec57 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -783,6 +783,7 @@ def get_nonnative_item_sprite(code: int) -> int: def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool): local_random = world.per_slot_randoms[player] + local_world = world.worlds[player] # patch items @@ -1190,12 +1191,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool): ]) # set Fountain bottle exchange items - if world.difficulty[player] in ['hard', 'expert']: - rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][local_random.randint(0, 5)]) - rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][local_random.randint(0, 5)]) - else: - rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][local_random.randint(0, 6)]) - rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][local_random.randint(0, 6)]) + rom.write_byte(0x348FF, item_table[local_world.waterfall_fairy_bottle_fill].item_code) + rom.write_byte(0x3493B, item_table[local_world.pyramid_fairy_bottle_fill].item_code) # enable Fat Fairy Chests rom.write_bytes(0x1FC16, [0xB1, 0xC6, 0xF9, 0xC9, 0xC6, 0xF9]) diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index d89e65c5..2cae70e0 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -249,6 +249,8 @@ class ALTTPWorld(World): rom_name_available_event: threading.Event has_progressive_bows: bool dungeons: typing.Dict[str, Dungeon] + waterfall_fairy_bottle_fill: str + pyramid_fairy_bottle_fill: str def __init__(self, *args, **kwargs): self.dungeon_local_item_names = set() @@ -256,6 +258,8 @@ class ALTTPWorld(World): self.rom_name_available_event = threading.Event() self.has_progressive_bows = False self.dungeons = {} + self.waterfall_fairy_bottle_fill = "Bottle" + self.pyramid_fairy_bottle_fill = "Bottle" super(ALTTPWorld, self).__init__(*args, **kwargs) @classmethod @@ -273,52 +277,62 @@ class ALTTPWorld(World): def generate_early(self): player = self.player - world = self.multiworld + multiworld = self.multiworld - if world.mode[player] == 'standard' \ - and world.smallkey_shuffle[player] \ - and world.smallkey_shuffle[player] != smallkey_shuffle.option_universal \ - and world.smallkey_shuffle[player] != smallkey_shuffle.option_own_dungeons \ - and world.smallkey_shuffle[player] != smallkey_shuffle.option_start_with: + # fairy bottle fills + bottle_options = [ + "Bottle (Red Potion)", "Bottle (Green Potion)", "Bottle (Blue Potion)", + "Bottle (Bee)", "Bottle (Good Bee)" + ] + if multiworld.difficulty[player] not in ["hard", "expert"]: + bottle_options.append("Bottle (Fairy)") + self.waterfall_fairy_bottle_fill = self.random.choice(bottle_options) + self.pyramid_fairy_bottle_fill = self.random.choice(bottle_options) + + if multiworld.mode[player] == 'standard' \ + and multiworld.smallkey_shuffle[player] \ + and multiworld.smallkey_shuffle[player] != smallkey_shuffle.option_universal \ + and multiworld.smallkey_shuffle[player] != smallkey_shuffle.option_own_dungeons \ + and multiworld.smallkey_shuffle[player] != smallkey_shuffle.option_start_with: self.multiworld.local_early_items[self.player]["Small Key (Hyrule Castle)"] = 1 # system for sharing ER layouts - self.er_seed = str(world.random.randint(0, 2 ** 64)) + self.er_seed = str(multiworld.random.randint(0, 2 ** 64)) - if "-" in world.shuffle[player]: - shuffle, seed = world.shuffle[player].split("-", 1) - world.shuffle[player] = shuffle + if "-" in multiworld.shuffle[player]: + shuffle, seed = multiworld.shuffle[player].split("-", 1) + multiworld.shuffle[player] = shuffle if shuffle == "vanilla": self.er_seed = "vanilla" - elif seed.startswith("group-") or world.is_race: - self.er_seed = get_same_seed(world, ( - shuffle, seed, world.retro_caves[player], world.mode[player], world.logic[player])) + elif seed.startswith("group-") or multiworld.is_race: + self.er_seed = get_same_seed(multiworld, ( + shuffle, seed, multiworld.retro_caves[player], multiworld.mode[player], multiworld.logic[player])) else: # not a race or group seed, use set seed as is. self.er_seed = seed - elif world.shuffle[player] == "vanilla": + elif multiworld.shuffle[player] == "vanilla": self.er_seed = "vanilla" for dungeon_item in ["smallkey_shuffle", "bigkey_shuffle", "compass_shuffle", "map_shuffle"]: - option = getattr(world, dungeon_item)[player] + option = getattr(multiworld, dungeon_item)[player] if option == "own_world": - world.local_items[player].value |= self.item_name_groups[option.item_name_group] + multiworld.local_items[player].value |= self.item_name_groups[option.item_name_group] elif option == "different_world": - world.non_local_items[player].value |= self.item_name_groups[option.item_name_group] - if world.mode[player] == "standard": - world.non_local_items[player].value -= {"Small Key (Hyrule Castle)"} + multiworld.non_local_items[player].value |= self.item_name_groups[option.item_name_group] + if multiworld.mode[player] == "standard": + multiworld.non_local_items[player].value -= {"Small Key (Hyrule Castle)"} elif option.in_dungeon: self.dungeon_local_item_names |= self.item_name_groups[option.item_name_group] if option == "original_dungeon": self.dungeon_specific_item_names |= self.item_name_groups[option.item_name_group] - world.difficulty_requirements[player] = difficulties[world.difficulty[player]] + multiworld.difficulty_requirements[player] = difficulties[multiworld.difficulty[player]] # enforce pre-defined local items. - if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]: - world.local_items[player].value.add('Triforce Piece') + if multiworld.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]: + multiworld.local_items[player].value.add('Triforce Piece') # Not possible to place crystals outside boss prizes yet (might as well make it consistent with pendants too). - world.non_local_items[player].value -= item_name_groups['Pendants'] - world.non_local_items[player].value -= item_name_groups['Crystals'] + multiworld.non_local_items[player].value -= item_name_groups['Pendants'] + multiworld.non_local_items[player].value -= item_name_groups['Crystals'] create_dungeons = create_dungeons @@ -364,7 +378,6 @@ class ALTTPWorld(World): world.register_indirect_condition(world.get_region(region_name, player), world.get_entrance(entrance_name, player)) - def collect_item(self, state: CollectionState, item: Item, remove=False): item_name = item.name if item_name.startswith('Progressive '): @@ -693,13 +706,18 @@ class ALTTPWorld(World): spoiler_handle.write('Prize shuffle %s\n' % self.multiworld.shuffle_prizes[self.player]) def write_spoiler(self, spoiler_handle: typing.TextIO) -> None: + player_name = self.multiworld.get_player_name(self.player) spoiler_handle.write("\n\nMedallions:\n") - spoiler_handle.write(f"\nMisery Mire ({self.multiworld.get_player_name(self.player)}):" + spoiler_handle.write(f"\nMisery Mire ({player_name}):" f" {self.multiworld.required_medallions[self.player][0]}") spoiler_handle.write( - f"\nTurtle Rock ({self.multiworld.get_player_name(self.player)}):" + f"\nTurtle Rock ({player_name}):" f" {self.multiworld.required_medallions[self.player][1]}") - + spoiler_handle.write("\n\nFairy Fountain Bottle Fill:\n") + spoiler_handle.write(f"\nPyramid Fairy ({player_name}):" + f" {self.pyramid_fairy_bottle_fill}") + spoiler_handle.write(f"\nWaterfall Fairy ({player_name}):" + f" {self.waterfall_fairy_bottle_fill}") if self.multiworld.boss_shuffle[self.player] != "none": def create_boss_map() -> typing.Dict: boss_map = {