From 933e5bacff5393c04f8ed49375e5ca97337bd122 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 20 Feb 2024 00:25:51 +0100 Subject: [PATCH 01/12] Core: update requirements (#2716) --- requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index f604556809f1..e2ccb67c18d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,13 @@ -colorama>=0.4.5 +colorama>=0.4.6 websockets>=12.0 PyYAML>=6.0.1 jellyfish>=1.0.3 -jinja2>=3.1.2 +jinja2>=3.1.3 schema>=0.7.5 kivy>=2.3.0 bsdiff4>=1.2.4 -platformdirs>=4.0.0 +platformdirs>=4.1.0 certifi>=2023.11.17 -cython>=3.0.6 +cython>=3.0.8 cymem>=2.0.8 -orjson>=3.9.10 \ No newline at end of file +orjson>=3.9.10 From 7a86285807fe4a83ba6482268f4b1dac40dd35a1 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:07:49 -0500 Subject: [PATCH 02/12] LttP: Bombless Start and Options/Shops overhaul (#2357) ## What is this fixing or adding? Adds Bombless Start option, along with proper bomb logic. This involves updating `can_kill_most_things` to include checking how many bombs can be held. Many places where the ability to kill enemies was assumed, now have logic. This fixes some possible existing logic issues, for example: Mini Moldorm cave checks currently are always in logic despite the fact that on expert enemy health it would require 12 bombs to kill each mini moldorm. Overhauls options, pulling them out of core and in particular making large changes to how the shop options work. Co-authored-by: espeon65536 <81029175+espeon65536@users.noreply.github.com> Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> Co-authored-by: Bondo <38083232+BadmoonzZ@users.noreply.github.com> Co-authored-by: espeon65536 Co-authored-by: Fabian Dill --- BaseClasses.py | 6 +- Generate.py | 118 ---- data/basepatch.bsdiff4 | Bin 114127 -> 114116 bytes setup.py | 2 - worlds/alttp/Bosses.py | 7 +- worlds/alttp/Dungeons.py | 4 +- worlds/alttp/EntranceRandomizer.py | 12 +- worlds/alttp/EntranceShuffle.py | 591 ++---------------- worlds/alttp/InvertedRegions.py | 15 +- worlds/alttp/ItemPool.py | 428 +++++++------ worlds/alttp/Items.py | 13 +- worlds/alttp/Options.py | 389 +++++++++++- worlds/alttp/Regions.py | 16 +- worlds/alttp/Rom.py | 136 ++-- worlds/alttp/Rules.py | 261 +++++--- worlds/alttp/Shops.py | 472 +++++--------- worlds/alttp/StateHelpers.py | 50 +- worlds/alttp/SubClasses.py | 3 + worlds/alttp/UnderworldGlitchRules.py | 8 +- worlds/alttp/__init__.py | 115 ++-- .../alttp/test/dungeons/TestAgahnimsTower.py | 8 +- worlds/alttp/test/dungeons/TestDarkPalace.py | 21 +- worlds/alttp/test/dungeons/TestDungeon.py | 2 + .../alttp/test/dungeons/TestEasternPalace.py | 4 +- worlds/alttp/test/dungeons/TestGanonsTower.py | 12 +- worlds/alttp/test/dungeons/TestIcePalace.py | 39 +- worlds/alttp/test/dungeons/TestMiseryMire.py | 7 +- worlds/alttp/test/dungeons/TestSkullWoods.py | 6 +- worlds/alttp/test/dungeons/TestSwampPalace.py | 3 +- worlds/alttp/test/dungeons/TestThievesTown.py | 9 +- worlds/alttp/test/dungeons/TestTowerOfHera.py | 4 +- worlds/alttp/test/inverted/TestInverted.py | 4 +- .../test/inverted/TestInvertedBombRules.py | 2 +- .../test/inverted/TestInvertedDarkWorld.py | 22 +- .../inverted/TestInvertedDeathMountain.py | 77 ++- .../test/inverted/TestInvertedLightWorld.py | 105 ++-- .../test/inverted/TestInvertedTurtleRock.py | 19 +- .../TestInvertedDarkWorld.py | 22 +- .../TestInvertedDeathMountain.py | 71 ++- .../TestInvertedLightWorld.py | 105 ++-- .../TestInvertedMinor.py | 6 +- .../TestInvertedTurtleRock.py | 18 +- .../alttp/test/inverted_owg/TestDarkWorld.py | 22 +- .../test/inverted_owg/TestDeathMountain.py | 18 +- .../alttp/test/inverted_owg/TestDungeons.py | 26 +- .../test/inverted_owg/TestInvertedOWG.py | 6 +- .../alttp/test/inverted_owg/TestLightWorld.py | 42 +- .../test/minor_glitches/TestDarkWorld.py | 64 +- .../test/minor_glitches/TestDeathMountain.py | 66 +- .../test/minor_glitches/TestLightWorld.py | 61 +- worlds/alttp/test/minor_glitches/TestMinor.py | 4 +- worlds/alttp/test/owg/TestDarkWorld.py | 78 +-- worlds/alttp/test/owg/TestDeathMountain.py | 84 +-- worlds/alttp/test/owg/TestDungeons.py | 19 +- worlds/alttp/test/owg/TestLightWorld.py | 63 +- worlds/alttp/test/owg/TestVanillaOWG.py | 4 +- worlds/alttp/test/vanilla/TestDarkWorld.py | 64 +- .../alttp/test/vanilla/TestDeathMountain.py | 61 +- worlds/alttp/test/vanilla/TestLightWorld.py | 60 +- worlds/alttp/test/vanilla/TestVanilla.py | 4 +- 60 files changed, 1929 insertions(+), 2029 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4002800173ea..25e4e70741a1 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -159,11 +159,11 @@ def __init__(self, players: int): self.fix_trock_doors = self.AttributeProxy( lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted') self.fix_skullwoods_exit = self.AttributeProxy( - lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) + lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple']) self.fix_palaceofdarkness_exit = self.AttributeProxy( - lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) + lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple']) self.fix_trock_exit = self.AttributeProxy( - lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) + lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple']) for player in range(1, players + 1): def set_player_attr(attr, val): diff --git a/Generate.py b/Generate.py index e19a7a973f23..fd4a5a7e1930 100644 --- a/Generate.py +++ b/Generate.py @@ -315,20 +315,6 @@ def prefer_int(input_data: str) -> Union[str, int]: return input_data -goals = { - 'ganon': 'ganon', - 'crystals': 'crystals', - 'bosses': 'bosses', - 'pedestal': 'pedestal', - 'ganon_pedestal': 'ganonpedestal', - 'triforce_hunt': 'triforcehunt', - 'local_triforce_hunt': 'localtriforcehunt', - 'ganon_triforce_hunt': 'ganontriforcehunt', - 'local_ganon_triforce_hunt': 'localganontriforcehunt', - 'ice_rod_hunt': 'icerodhunt', -} - - def roll_percentage(percentage: Union[int, float]) -> bool: """Roll a percentage chance. percentage is expected to be in range [0, 100]""" @@ -357,15 +343,6 @@ def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any: if options[option_key].supports_weighting: return get_choice(option_key, category_dict) return category_dict[option_key] - if game == "A Link to the Past": # TODO wow i hate this - if option_key in {"glitches_required", "dark_room_logic", "entrance_shuffle", "goals", "triforce_pieces_mode", - "triforce_pieces_percentage", "triforce_pieces_available", "triforce_pieces_extra", - "triforce_pieces_required", "shop_shuffle", "mode", "item_pool", "item_functionality", - "boss_shuffle", "enemy_damage", "enemy_health", "timer", "countdown_start_time", - "red_clock_time", "blue_clock_time", "green_clock_time", "dungeon_counters", "shuffle_prizes", - "misery_mire_medallion", "turtle_rock_medallion", "sprite_pool", "sprite", - "random_sprite_on_event"}: - return get_choice(option_key, category_dict) raise Exception(f"Error generating meta option {option_key} for {game}.") @@ -504,101 +481,6 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): - if "dungeon_items" in weights and get_choice_legacy('dungeon_items', weights, "none") != "none": - raise Exception(f"dungeon_items key in A Link to the Past was removed, but is present in these weights as {get_choice_legacy('dungeon_items', weights, False)}.") - glitches_required = get_choice_legacy('glitches_required', weights) - if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'hybrid_major_glitches', 'minor_glitches']: - logging.warning("Only NMG, OWG, HMG and No Logic supported") - glitches_required = 'none' - ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches', - 'minor_glitches': 'minorglitches', 'hybrid_major_glitches': 'hybridglitches'}[ - glitches_required] - - ret.dark_room_logic = get_choice_legacy("dark_room_logic", weights, "lamp") - if not ret.dark_room_logic: # None/False - ret.dark_room_logic = "none" - if ret.dark_room_logic == "sconces": - ret.dark_room_logic = "torches" - if ret.dark_room_logic not in {"lamp", "torches", "none"}: - raise ValueError(f"Unknown Dark Room Logic: \"{ret.dark_room_logic}\"") - - entrance_shuffle = get_choice_legacy('entrance_shuffle', weights, 'vanilla') - if entrance_shuffle.startswith('none-'): - ret.shuffle = 'vanilla' - else: - ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' - - goal = get_choice_legacy('goals', weights, 'ganon') - - ret.goal = goals[goal] - - - extra_pieces = get_choice_legacy('triforce_pieces_mode', weights, 'available') - - ret.triforce_pieces_required = LttPOptions.TriforcePieces.from_any(get_choice_legacy('triforce_pieces_required', weights, 20)) - - # sum a percentage to required - if extra_pieces == 'percentage': - percentage = max(100, float(get_choice_legacy('triforce_pieces_percentage', weights, 150))) / 100 - ret.triforce_pieces_available = int(round(ret.triforce_pieces_required * percentage, 0)) - # vanilla mode (specify how many pieces are) - elif extra_pieces == 'available': - ret.triforce_pieces_available = LttPOptions.TriforcePieces.from_any( - get_choice_legacy('triforce_pieces_available', weights, 30)) - # required pieces + fixed extra - elif extra_pieces == 'extra': - extra_pieces = max(0, int(get_choice_legacy('triforce_pieces_extra', weights, 10))) - ret.triforce_pieces_available = ret.triforce_pieces_required + extra_pieces - - # change minimum to required pieces to avoid problems - ret.triforce_pieces_available = min(max(ret.triforce_pieces_required, int(ret.triforce_pieces_available)), 90) - - ret.shop_shuffle = get_choice_legacy('shop_shuffle', weights, '') - if not ret.shop_shuffle: - ret.shop_shuffle = '' - - ret.mode = get_choice_legacy("mode", weights) - - ret.difficulty = get_choice_legacy('item_pool', weights) - - ret.item_functionality = get_choice_legacy('item_functionality', weights) - - - ret.enemy_damage = {None: 'default', - 'default': 'default', - 'shuffled': 'shuffled', - 'random': 'chaos', # to be removed - 'chaos': 'chaos', - }[get_choice_legacy('enemy_damage', weights)] - - ret.enemy_health = get_choice_legacy('enemy_health', weights) - - ret.timer = {'none': False, - None: False, - False: False, - 'timed': 'timed', - 'timed_ohko': 'timed-ohko', - 'ohko': 'ohko', - 'timed_countdown': 'timed-countdown', - 'display': 'display'}[get_choice_legacy('timer', weights, False)] - - ret.countdown_start_time = int(get_choice_legacy('countdown_start_time', weights, 10)) - ret.red_clock_time = int(get_choice_legacy('red_clock_time', weights, -2)) - ret.blue_clock_time = int(get_choice_legacy('blue_clock_time', weights, 2)) - ret.green_clock_time = int(get_choice_legacy('green_clock_time', weights, 4)) - - ret.dungeon_counters = get_choice_legacy('dungeon_counters', weights, 'default') - - ret.shuffle_prizes = get_choice_legacy('shuffle_prizes', weights, "g") - - ret.required_medallions = [get_choice_legacy("misery_mire_medallion", weights, "random"), - get_choice_legacy("turtle_rock_medallion", weights, "random")] - - for index, medallion in enumerate(ret.required_medallions): - ret.required_medallions[index] = {"ether": "Ether", "quake": "Quake", "bombos": "Bombos", "random": "random"} \ - .get(medallion.lower(), None) - if not ret.required_medallions[index]: - raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}") ret.plando_texts = {} if PlandoOptions.texts in plando_options: diff --git a/data/basepatch.bsdiff4 b/data/basepatch.bsdiff4 index a578b248f57721cfc7e8c31da233bf732ed27db8..aa8f1375c522b724ae1944ebb6de113a7e592a35 100644 GIT binary patch literal 114116 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 wa4TS7uXZs4i)~=>>)= bool: def HelmasaurKingDefeatRule(state, player: int) -> bool: # TODO: technically possible with the hammer - return has_sword(state, player) or can_shoot_arrows(state, player) + return (can_use_bombs(state, player, 5) or state.has("Hammer", player)) and (has_sword(state, player) + or can_shoot_arrows(state, player)) def ArrghusDefeatRule(state, player: int) -> bool: @@ -143,7 +144,7 @@ def GanonDefeatRule(state, player: int) -> bool: can_hurt = has_beam_sword(state, player) common = can_hurt and has_fire_source(state, player) # silverless ganon may be needed in anything higher than no glitches - if state.multiworld.logic[player] != 'noglitches': + if state.multiworld.glitches_required[player] != 'no_glitches': # need to light torch a sufficient amount of times return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or ( state.has('Silver Bow', player) and can_shoot_arrows(state, player)) or diff --git a/worlds/alttp/Dungeons.py b/worlds/alttp/Dungeons.py index b456174f39b7..c886fce92079 100644 --- a/worlds/alttp/Dungeons.py +++ b/worlds/alttp/Dungeons.py @@ -9,7 +9,7 @@ from .Bosses import BossFactory, Boss from .Items import ItemFactory from .Regions import lookup_boss_drops, key_drop_data -from .Options import smallkey_shuffle +from .Options import small_key_shuffle if typing.TYPE_CHECKING: from .SubClasses import ALttPLocation, ALttPItem @@ -66,7 +66,7 @@ def create_dungeons(world: "ALTTPWorld"): def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items): dungeon = Dungeon(name, dungeon_regions, big_key, - [] if multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal else small_keys, + [] if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal else small_keys, dungeon_items, player) for item in dungeon.all_items: item.dungeon = dungeon diff --git a/worlds/alttp/EntranceRandomizer.py b/worlds/alttp/EntranceRandomizer.py index 47c36b6cde33..37486a9cde07 100644 --- a/worlds/alttp/EntranceRandomizer.py +++ b/worlds/alttp/EntranceRandomizer.py @@ -23,7 +23,7 @@ def defval(value): multiargs, _ = parser.parse_known_args(argv) parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) - parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'hybridglitches', 'nologic'], + parser.add_argument('--logic', default=defval('no_glitches'), const='no_glitches', nargs='?', choices=['no_glitches', 'minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'], help='''\ Select Enforcement of Item Requirements. (default: %(default)s) No Glitches: @@ -49,7 +49,7 @@ def defval(value): instead of a bunny. ''') parser.add_argument('--goal', default=defval('ganon'), const='ganon', nargs='?', - choices=['ganon', 'pedestal', 'bosses', 'triforcehunt', 'localtriforcehunt', 'ganontriforcehunt', 'localganontriforcehunt', 'crystals', 'ganonpedestal'], + choices=['ganon', 'pedestal', 'bosses', 'triforce_hunt', 'local_triforce_hunt', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'crystals', 'ganon_pedestal'], help='''\ Select completion goal. (default: %(default)s) Ganon: Collect all crystals, beat Agahnim 2 then @@ -92,7 +92,7 @@ def defval(value): Hard: Reduced functionality. Expert: Greatly reduced functionality. ''') - parser.add_argument('--timer', default=defval('none'), const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'], + parser.add_argument('--timer', default=defval('none'), const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed_ohko', 'ohko', 'timed_countdown'], help='''\ Select game timer setting. Affects available itempool. (default: %(default)s) None: No timer. @@ -151,7 +151,7 @@ def defval(value): slightly biased to placing progression items with less restrictions. ''') - parser.add_argument('--shuffle', default=defval('vanilla'), const='vanilla', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed'], + parser.add_argument('--shuffle', default=defval('vanilla'), const='vanilla', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed'], help='''\ Select Entrance Shuffling Algorithm. (default: %(default)s) Full: Mix cave and dungeon entrances freely while limiting @@ -178,9 +178,9 @@ def defval(value): parser.add_argument('--open_pyramid', default=defval('auto'), help='''\ Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it. Depending on goal, you might still need to beat Agahnim 2 in order to beat ganon. - fast ganon goals are crystals, ganontriforcehunt, localganontriforcehunt, pedestalganon + fast ganon goals are crystals, ganon_triforce_hunt, local_ganon_triforce_hunt, pedestalganon auto - Only opens pyramid hole if the goal specifies a fast ganon, and entrance shuffle - is vanilla, dungeonssimple or dungeonsfull. + is vanilla, dungeons_simple or dungeons_full. goal - Opens pyramid hole if the goal specifies a fast ganon. yes - Always opens the pyramid hole. no - Never opens the pyramid hole. diff --git a/worlds/alttp/EntranceShuffle.py b/worlds/alttp/EntranceShuffle.py index 07bb587eebe3..fceba86a739e 100644 --- a/worlds/alttp/EntranceShuffle.py +++ b/worlds/alttp/EntranceShuffle.py @@ -21,17 +21,17 @@ def link_entrances(world, player): connect_simple(world, exitname, regionname, player) # if we do not shuffle, set default connections - if world.shuffle[player] == 'vanilla': + if world.entrance_shuffle[player] == 'vanilla': for exitname, regionname in default_connections: connect_simple(world, exitname, regionname, player) for exitname, regionname in default_dungeon_connections: connect_simple(world, exitname, regionname, player) - elif world.shuffle[player] == 'dungeonssimple': + elif world.entrance_shuffle[player] == 'dungeons_simple': for exitname, regionname in default_connections: connect_simple(world, exitname, regionname, player) simple_shuffle_dungeons(world, player) - elif world.shuffle[player] == 'dungeonsfull': + elif world.entrance_shuffle[player] == 'dungeons_full': for exitname, regionname in default_connections: connect_simple(world, exitname, regionname, player) @@ -63,9 +63,9 @@ def link_entrances(world, player): connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player) connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player) connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player) - elif world.shuffle[player] == 'dungeonscrossed': + elif world.entrance_shuffle[player] == 'dungeons_crossed': crossed_shuffle_dungeons(world, player) - elif world.shuffle[player] == 'simple': + elif world.entrance_shuffle[player] == 'simple': simple_shuffle_dungeons(world, player) old_man_entrances = list(Old_Man_Entrances) @@ -136,7 +136,7 @@ def link_entrances(world, player): # place remaining doors connect_doors(world, single_doors, door_targets, player) - elif world.shuffle[player] == 'restricted': + elif world.entrance_shuffle[player] == 'restricted': simple_shuffle_dungeons(world, player) lw_entrances = list(LW_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances) @@ -207,62 +207,8 @@ def link_entrances(world, player): # place remaining doors connect_doors(world, doors, door_targets, player) - elif world.shuffle[player] == 'restricted_legacy': - simple_shuffle_dungeons(world, player) - - lw_entrances = list(LW_Entrances) - dw_entrances = list(DW_Entrances) - dw_must_exits = list(DW_Entrances_Must_Exit) - old_man_entrances = list(Old_Man_Entrances) - caves = list(Cave_Exits) - three_exit_caves = list(Cave_Three_Exits) - single_doors = list(Single_Cave_Doors) - bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors) - door_targets = list(Single_Cave_Targets) - - # only use two exit caves to do mandatory dw connections - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) - # add three exit doors to pool for remainder - caves.extend(three_exit_caves) - - # place old man, has limited options - # exit has to come from specific set of doors, the entrance is free to move about - world.random.shuffle(old_man_entrances) - old_man_exit = old_man_entrances.pop() - lw_entrances.extend(old_man_entrances) - world.random.shuffle(lw_entrances) - old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) - - # place Old Man House in Light World - connect_caves(world, lw_entrances, [], Old_Man_House, player) - # connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves - connect_caves(world, lw_entrances, dw_entrances, caves, player) - - # scramble holes - scramble_holes(world, player) - - # place blacksmith, has limited options - world.random.shuffle(blacksmith_doors) - blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) - bomb_shop_doors.extend(blacksmith_doors) - - # place dam and pyramid fairy, have limited options - world.random.shuffle(bomb_shop_doors) - bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) - single_doors.extend(bomb_shop_doors) - - # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern'], player) - - # place remaining doors - connect_doors(world, single_doors, door_targets, player) - elif world.shuffle[player] == 'full': + elif world.entrance_shuffle[player] == 'full': skull_woods_shuffle(world, player) lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances) @@ -368,7 +314,7 @@ def link_entrances(world, player): # place remaining doors connect_doors(world, doors, door_targets, player) - elif world.shuffle[player] == 'crossed': + elif world.entrance_shuffle[player] == 'crossed': skull_woods_shuffle(world, player) entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances + DW_Entrances + DW_Dungeon_Entrances + DW_Single_Cave_Doors) @@ -445,337 +391,8 @@ def link_entrances(world, player): # place remaining doors connect_doors(world, entrances, door_targets, player) - elif world.shuffle[player] == 'full_legacy': - skull_woods_shuffle(world, player) - - lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances) - dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) - dw_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) - lw_must_exits = list(LW_Dungeon_Entrances_Must_Exit) - old_man_entrances = list(Old_Man_Entrances + ['Tower of Hera']) - caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) # don't need to consider three exit caves, have one exit caves to avoid parity issues - single_doors = list(Single_Cave_Doors) - bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors) - door_targets = list(Single_Cave_Targets) - - if world.mode[player] == 'standard': - # must connect front of hyrule castle to do escape - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - else: - caves.append(tuple(world.random.sample( - ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) - lw_entrances.append('Hyrule Castle Entrance (South)') - - if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) - else: - dw_entrances.append('Ganons Tower') - caves.append('Ganons Tower Exit') - - # we randomize which world requirements we fulfill first so we get better dungeon distribution - if world.random.randint(0, 1) == 0: - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) - else: - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) - if world.mode[player] == 'standard': - # rest of hyrule castle must be in light world - connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player) - - # place old man, has limited options - # exit has to come from specific set of doors, the entrance is free to move about - old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] - world.random.shuffle(old_man_entrances) - old_man_exit = old_man_entrances.pop() - lw_entrances.remove(old_man_exit) - - world.random.shuffle(lw_entrances) - old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) - - # place Old Man House in Light World - connect_caves(world, lw_entrances, [], list(Old_Man_House), player) #need this to avoid badness with multiple seeds - - # now scramble the rest - connect_caves(world, lw_entrances, dw_entrances, caves, player) - - # scramble holes - scramble_holes(world, player) - - # place blacksmith, has limited options - world.random.shuffle(blacksmith_doors) - blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) - bomb_shop_doors.extend(blacksmith_doors) - - # place bomb shop, has limited options - world.random.shuffle(bomb_shop_doors) - bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) - single_doors.extend(bomb_shop_doors) - - # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern'], player) - - # place remaining doors - connect_doors(world, single_doors, door_targets, player) - elif world.shuffle[player] == 'madness_legacy': - # here lie dragons, connections are no longer two way - lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances) - dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) - dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) - - lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', - 'Bat Cave Cave', - 'North Fairy Cave', - 'Sanctuary', - 'Lost Woods Hideout Stump', - 'Lumberjack Tree Cave'] + list( - Old_Man_Entrances) - dw_doors = list( - DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + [ - 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', - 'Skull Woods Second Section Door (West)'] - - world.random.shuffle(lw_doors) - world.random.shuffle(dw_doors) - - dw_entrances_must_exits.append('Skull Woods Second Section Door (West)') - dw_entrances.append('Skull Woods Second Section Door (East)') - dw_entrances.append('Skull Woods First Section Door') - - lw_entrances.extend( - ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', - 'Lumberjack Tree Cave']) - - lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit) - - old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera'] - - mandatory_light_world = ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)'] - mandatory_dark_world = [] - caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) - - # shuffle up holes - - lw_hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave'] - dw_hole_entrances = ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - - hole_targets = [('Kakariko Well Exit', 'Kakariko Well (top)'), - ('Bat Cave Exit', 'Bat Cave (right)'), - ('North Fairy Cave Exit', 'North Fairy Cave'), - ('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'), - ('Lumberjack Tree Exit', 'Lumberjack Tree (top)'), - (('Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'), 'Skull Woods Second Section (Drop)')] - - if world.mode[player] == 'standard': - # cannot move uncle cave - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) - connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) - connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) - else: - lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop') - hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) - lw_doors.append('Hyrule Castle Secret Entrance Stairs') - lw_entrances.append('Hyrule Castle Secret Entrance Stairs') - - if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) - else: - dw_entrances.append('Ganons Tower') - caves.append('Ganons Tower Exit') - dw_hole_entrances.append('Pyramid Hole') - hole_targets.append(('Pyramid Exit', 'Pyramid')) - dw_entrances_must_exits.append('Pyramid Entrance') - dw_doors.extend(['Ganons Tower', 'Pyramid Entrance']) - - world.random.shuffle(lw_hole_entrances) - world.random.shuffle(dw_hole_entrances) - world.random.shuffle(hole_targets) - - # decide if skull woods first section should be in light or dark world - sw_light = world.random.randint(0, 1) == 0 - if sw_light: - sw_hole_pool = lw_hole_entrances - mandatory_light_world.append('Skull Woods First Section Exit') - else: - sw_hole_pool = dw_hole_entrances - mandatory_dark_world.append('Skull Woods First Section Exit') - for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', - 'Skull Woods First Section (Top)']: - connect_entrance(world, sw_hole_pool.pop(), target, player) - - # sanctuary has to be in light world - connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop', player) - mandatory_light_world.append('Sanctuary Exit') - - # fill up remaining holes - for hole in dw_hole_entrances: - exits, target = hole_targets.pop() - mandatory_dark_world.append(exits) - connect_entrance(world, hole, target, player) - - for hole in lw_hole_entrances: - exits, target = hole_targets.pop() - mandatory_light_world.append(exits) - connect_entrance(world, hole, target, player) - - # hyrule castle handling - if world.mode[player] == 'standard': - # must connect front of hyrule castle to do escape - connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player) - mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) - else: - lw_doors.append('Hyrule Castle Entrance (South)') - lw_entrances.append('Hyrule Castle Entrance (South)') - caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) - - # now let's deal with mandatory reachable stuff - def extract_reachable_exit(cavelist): - world.random.shuffle(cavelist) - candidate = None - for cave in cavelist: - if isinstance(cave, tuple) and len(cave) > 1: - # special handling: TRock and Spectracle Rock cave have two entries that we should consider entrance only - # ToDo this should be handled in a more sensible manner - if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2: - continue - candidate = cave - break - if candidate is None: - raise KeyError('No suitable cave.') - cavelist.remove(candidate) - return candidate - - def connect_reachable_exit(entrance, general, worldspecific, worldoors): - # select which one is the primary option - if world.random.randint(0, 1) == 0: - primary = general - secondary = worldspecific - else: - primary = worldspecific - secondary = general - - try: - cave = extract_reachable_exit(primary) - except KeyError: - cave = extract_reachable_exit(secondary) - - exit = cave[-1] - cave = cave[:-1] - connect_exit(world, exit, entrance, player) - connect_entrance(world, worldoors.pop(), exit, player) - # rest of cave now is forced to be in this world - worldspecific.append(cave) - - # we randomize which world requirements we fulfill first so we get better dungeon distribution - if world.random.randint(0, 1) == 0: - for entrance in lw_entrances_must_exits: - connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors) - for entrance in dw_entrances_must_exits: - connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors) - else: - for entrance in dw_entrances_must_exits: - connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors) - for entrance in lw_entrances_must_exits: - connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors) - - # place old man, has limited options - # exit has to come from specific set of doors, the entrance is free to move about - old_man_entrances = [entrance for entrance in old_man_entrances if entrance in lw_entrances] - world.random.shuffle(old_man_entrances) - old_man_exit = old_man_entrances.pop() - lw_entrances.remove(old_man_exit) - - connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) - connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)', player) - mandatory_light_world.append('Old Man Cave Exit (West)') - - # we connect up the mandatory associations we have found - for mandatory in mandatory_light_world: - if not isinstance(mandatory, tuple): - mandatory = (mandatory,) - for exit in mandatory: - # point out somewhere - connect_exit(world, exit, lw_entrances.pop(), player) - # point in from somewhere - connect_entrance(world, lw_doors.pop(), exit, player) - - for mandatory in mandatory_dark_world: - if not isinstance(mandatory, tuple): - mandatory = (mandatory,) - for exit in mandatory: - # point out somewhere - connect_exit(world, exit, dw_entrances.pop(), player) - # point in from somewhere - connect_entrance(world, dw_doors.pop(), exit, player) - # handle remaining caves - while caves: - # connect highest exit count caves first, prevent issue where we have 2 or 3 exits accross worlds left to fill - cave_candidate = (None, 0) - for i, cave in enumerate(caves): - if isinstance(cave, str): - cave = (cave,) - if len(cave) > cave_candidate[1]: - cave_candidate = (i, len(cave)) - cave = caves.pop(cave_candidate[0]) - - place_lightworld = world.random.randint(0, 1) == 0 - if place_lightworld: - target_doors = lw_doors - target_entrances = lw_entrances - else: - target_doors = dw_doors - target_entrances = dw_entrances - - if isinstance(cave, str): - cave = (cave,) - - # check if we can still fit the cave into our target group - if len(target_doors) < len(cave): - if not place_lightworld: - target_doors = lw_doors - target_entrances = lw_entrances - else: - target_doors = dw_doors - target_entrances = dw_entrances - - for exit in cave: - connect_exit(world, exit, target_entrances.pop(), player) - connect_entrance(world, target_doors.pop(), exit, player) - - # handle simple doors - - single_doors = list(Single_Cave_Doors) - bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors) - door_targets = list(Single_Cave_Targets) - - # place blacksmith, has limited options - world.random.shuffle(blacksmith_doors) - blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) - bomb_shop_doors.extend(blacksmith_doors) - - # place dam and pyramid fairy, have limited options - world.random.shuffle(bomb_shop_doors) - bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) - single_doors.extend(bomb_shop_doors) - - # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern'], player) - - # place remaining doors - connect_doors(world, single_doors, door_targets, player) - elif world.shuffle[player] == 'insanity': + elif world.entrance_shuffle[player] == 'insanity': # beware ye who enter here entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_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'] @@ -922,157 +539,15 @@ def connect_reachable_exit(entrance, caves, doors): # place remaining doors connect_doors(world, doors, door_targets, player) - elif world.shuffle[player] == 'insanity_legacy': - world.fix_fake_world[player] = False - # beware ye who enter here - - entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_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'] - entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] - - doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\ - DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] - - world.random.shuffle(doors) - - old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera'] - - caves = Cave_Exits + Dungeon_Exits + Cave_Three_Exits + ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', - 'Kakariko Well Exit', 'Bat Cave Exit', 'North Fairy Cave Exit', 'Lost Woods Hideout Exit', 'Lumberjack Tree Exit', 'Sanctuary Exit'] - - # shuffle up holes - - hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', - 'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - - hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Woods Second Section (Drop)', - 'Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)'] - - if world.mode[player] == 'standard': - # cannot move uncle cave - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) - connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) - connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) - else: - hole_entrances.append('Hyrule Castle Secret Entrance Drop') - hole_targets.append('Hyrule Castle Secret Entrance') - doors.append('Hyrule Castle Secret Entrance Stairs') - entrances.append('Hyrule Castle Secret Entrance Stairs') - caves.append('Hyrule Castle Secret Entrance Exit') - - if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) - else: - entrances.append('Ganons Tower') - caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) - hole_entrances.append('Pyramid Hole') - hole_targets.append('Pyramid') - entrances_must_exits.append('Pyramid Entrance') - doors.extend(['Ganons Tower', 'Pyramid Entrance']) - - world.random.shuffle(hole_entrances) - world.random.shuffle(hole_targets) - world.random.shuffle(entrances) - - # fill up holes - for hole in hole_entrances: - connect_entrance(world, hole, hole_targets.pop(), player) - - # hyrule castle handling - if world.mode[player] == 'standard': - # must connect front of hyrule castle to do escape - connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player) - caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) - else: - doors.append('Hyrule Castle Entrance (South)') - entrances.append('Hyrule Castle Entrance (South)') - caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) - - # now let's deal with mandatory reachable stuff - def extract_reachable_exit(cavelist): - world.random.shuffle(cavelist) - candidate = None - for cave in cavelist: - if isinstance(cave, tuple) and len(cave) > 1: - # special handling: TRock has two entries that we should consider entrance only - # ToDo this should be handled in a more sensible manner - if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2: - continue - candidate = cave - break - if candidate is None: - raise KeyError('No suitable cave.') - cavelist.remove(candidate) - return candidate - - def connect_reachable_exit(entrance, caves, doors): - cave = extract_reachable_exit(caves) - - exit = cave[-1] - cave = cave[:-1] - connect_exit(world, exit, entrance, player) - connect_entrance(world, doors.pop(), exit, player) - # rest of cave now is forced to be in this world - caves.append(cave) - - # connect mandatory exits - for entrance in entrances_must_exits: - connect_reachable_exit(entrance, caves, doors) - - # place old man, has limited options - # exit has to come from specific set of doors, the entrance is free to move about - old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] - world.random.shuffle(old_man_entrances) - old_man_exit = old_man_entrances.pop() - entrances.remove(old_man_exit) - - connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) - connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player) - caves.append('Old Man Cave Exit (West)') - - # handle remaining caves - for cave in caves: - if isinstance(cave, str): - cave = (cave,) - - for exit in cave: - connect_exit(world, exit, entrances.pop(), player) - connect_entrance(world, doors.pop(), exit, player) - - # handle simple doors - single_doors = list(Single_Cave_Doors) - bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors) - door_targets = list(Single_Cave_Targets) - - # place blacksmith, has limited options - world.random.shuffle(blacksmith_doors) - blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) - bomb_shop_doors.extend(blacksmith_doors) - - # place dam and pyramid fairy, have limited options - world.random.shuffle(bomb_shop_doors) - bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) - single_doors.extend(bomb_shop_doors) - - # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern'], player) - - # place remaining doors - connect_doors(world, single_doors, door_targets, player) else: raise NotImplementedError( - f'{world.shuffle[player]} Shuffling not supported yet. Player {world.get_player_name(player)}') + f'{world.entrance_shuffle[player]} Shuffling not supported yet. Player {world.get_player_name(player)}') - if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: + if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']: overworld_glitch_connections(world, player) # mandatory hybrid major glitches connections - if world.logic[player] in ['hybridglitches', 'nologic']: + if world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']: underworld_glitch_connections(world, player) # check for swamp palace fix @@ -1106,17 +581,17 @@ def link_inverted_entrances(world, player): connect_simple(world, exitname, regionname, player) # if we do not shuffle, set default connections - if world.shuffle[player] == 'vanilla': + if world.entrance_shuffle[player] == 'vanilla': for exitname, regionname in inverted_default_connections: connect_simple(world, exitname, regionname, player) for exitname, regionname in inverted_default_dungeon_connections: connect_simple(world, exitname, regionname, player) - elif world.shuffle[player] == 'dungeonssimple': + elif world.entrance_shuffle[player] == 'dungeons_simple': for exitname, regionname in inverted_default_connections: connect_simple(world, exitname, regionname, player) simple_shuffle_dungeons(world, player) - elif world.shuffle[player] == 'dungeonsfull': + elif world.entrance_shuffle[player] == 'dungeons_full': for exitname, regionname in inverted_default_connections: connect_simple(world, exitname, regionname, player) @@ -1171,9 +646,9 @@ def link_inverted_entrances(world, player): connect_mandatory_exits(world, lw_entrances, dungeon_exits, lw_dungeon_entrances_must_exit, player) connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player) - elif world.shuffle[player] == 'dungeonscrossed': + elif world.entrance_shuffle[player] == 'dungeons_crossed': inverted_crossed_shuffle_dungeons(world, player) - elif world.shuffle[player] == 'simple': + elif world.entrance_shuffle[player] == 'simple': simple_shuffle_dungeons(world, player) old_man_entrances = list(Inverted_Old_Man_Entrances) @@ -1270,7 +745,7 @@ def link_inverted_entrances(world, player): # place remaining doors connect_doors(world, single_doors, door_targets, player) - elif world.shuffle[player] == 'restricted': + elif world.entrance_shuffle[player] == 'restricted': simple_shuffle_dungeons(world, player) lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Single_Cave_Doors) @@ -1355,7 +830,7 @@ def link_inverted_entrances(world, player): doors = lw_entrances + dw_entrances # place remaining doors connect_doors(world, doors, door_targets, player) - elif world.shuffle[player] == 'full': + elif world.entrance_shuffle[player] == 'full': skull_woods_shuffle(world, player) lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors) @@ -1506,7 +981,7 @@ def link_inverted_entrances(world, player): # place remaining doors connect_doors(world, doors, door_targets, player) - elif world.shuffle[player] == 'crossed': + elif world.entrance_shuffle[player] == 'crossed': skull_woods_shuffle(world, player) entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors + Inverted_Old_Man_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_DW_Single_Cave_Doors) @@ -1617,7 +1092,7 @@ def link_inverted_entrances(world, player): # place remaining doors connect_doors(world, entrances, door_targets, player) - elif world.shuffle[player] == 'insanity': + elif world.entrance_shuffle[player] == 'insanity': # beware ye who enter here entrances = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', '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)'] @@ -1776,10 +1251,10 @@ def connect_reachable_exit(entrance, caves, doors): else: raise NotImplementedError('Shuffling not supported yet') - if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: + if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']: overworld_glitch_connections(world, player) # mandatory hybrid major glitches connections - if world.logic[player] in ['hybridglitches', 'nologic']: + if world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']: underworld_glitch_connections(world, player) # patch swamp drain @@ -1880,14 +1355,14 @@ def scramble_holes(world, player): hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) # do not shuffle sanctuary into pyramid hole unless shuffle is crossed - if world.shuffle[player] == 'crossed': + if world.entrance_shuffle[player] == 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) if world.shuffle_ganon: world.random.shuffle(hole_targets) exit, target = hole_targets.pop() connect_two_way(world, 'Pyramid Entrance', exit, player) connect_entrance(world, 'Pyramid Hole', target, player) - if world.shuffle[player] != 'crossed': + if world.entrance_shuffle[player] != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) world.random.shuffle(hole_targets) @@ -1922,14 +1397,14 @@ def scramble_inverted_holes(world, player): hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) # do not shuffle sanctuary into pyramid hole unless shuffle is crossed - if world.shuffle[player] == 'crossed': + if world.entrance_shuffle[player] == 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) if world.shuffle_ganon: world.random.shuffle(hole_targets) exit, target = hole_targets.pop() connect_two_way(world, 'Inverted Pyramid Entrance', exit, player) connect_entrance(world, 'Inverted Pyramid Hole', target, player) - if world.shuffle[player] != 'crossed': + if world.entrance_shuffle[player] != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) world.random.shuffle(hole_targets) @@ -1958,7 +1433,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): invalid_connections = Must_Exit_Invalid_Connections.copy() invalid_cave_connections = defaultdict(set) - if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: + if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']: from worlds.alttp import OverworldGlitchRules for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'): invalid_connections[entrance] = set() @@ -3038,6 +2513,7 @@ def plando_connect(world, player: int): ('Sanctuary Push Door', 'Sanctuary'), ('Sewer Drop', 'Sewers'), ('Sewers Back Door', 'Sewers (Dark)'), + ('Sewers Secret Room', 'Sewers Secret Room'), ('Agahnim 1', 'Agahnim 1'), ('Flute Spot 1', 'Death Mountain'), ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), @@ -3053,6 +2529,8 @@ def plando_connect(world, player: int): ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), + ('Hookshot Cave Bomb Wall (South)', 'Hookshot Cave (Upper)'), + ('Hookshot Cave Bomb Wall (North)', 'Hookshot Cave'), ('East Death Mountain (Top)', 'East Death Mountain (Top)'), ('Death Mountain (Top)', 'Death Mountain (Top)'), ('Death Mountain Drop', 'Death Mountain'), @@ -3227,6 +2705,7 @@ def plando_connect(world, player: int): ('Sanctuary Push Door', 'Sanctuary'), ('Sewer Drop', 'Sewers'), ('Sewers Back Door', 'Sewers (Dark)'), + ('Sewers Secret Room', 'Sewers Secret Room'), ('Agahnim 1', 'Agahnim 1'), ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), ('Death Mountain Entrance Drop', 'Light World'), @@ -3241,6 +2720,8 @@ def plando_connect(world, player: int): ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), + ('Hookshot Cave Bomb Wall (South)', 'Hookshot Cave (Upper)'), + ('Hookshot Cave Bomb Wall (North)', 'Hookshot Cave'), ('East Death Mountain (Top)', 'East Death Mountain (Top)'), ('Death Mountain (Top)', 'Death Mountain (Top)'), ('Death Mountain Drop', 'Death Mountain'), @@ -3572,7 +3053,7 @@ def plando_connect(world, player: int): ('Superbunny Cave Exit (Bottom)', 'Dark Death Mountain (East Bottom)'), ('Hookshot Cave Exit (South)', 'Dark Death Mountain (Top)'), ('Hookshot Cave Exit (North)', 'Death Mountain Floating Island (Dark World)'), - ('Hookshot Cave Back Entrance', 'Hookshot Cave'), + ('Hookshot Cave Back Entrance', 'Hookshot Cave (Upper)'), ('Mimic Cave', 'Mimic Cave'), ('Pyramid Hole', 'Pyramid'), @@ -3703,7 +3184,7 @@ def plando_connect(world, player: int): ('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'), ('Superbunny Cave Exit (Bottom)', 'Dark Death Mountain (East Bottom)'), ('Hookshot Cave Exit (North)', 'Death Mountain Floating Island (Dark World)'), - ('Hookshot Cave Back Entrance', 'Hookshot Cave'), + ('Hookshot Cave Back Entrance', 'Hookshot Cave (Upper)'), ('Mimic Cave', 'Mimic Cave'), ('Inverted Pyramid Hole', 'Pyramid'), ('Inverted Links House', 'Inverted Links House'), diff --git a/worlds/alttp/InvertedRegions.py b/worlds/alttp/InvertedRegions.py index f89eebec3339..2e30fde8cc85 100644 --- a/worlds/alttp/InvertedRegions.py +++ b/worlds/alttp/InvertedRegions.py @@ -133,7 +133,7 @@ def create_inverted_regions(world, player): create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'), create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']), create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']), - create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'), + create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']), create_cave_region(world, player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'], @@ -176,8 +176,9 @@ def create_inverted_regions(world, player): 'Throne Room']), create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']), - create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', - 'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']), + create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']), + create_dungeon_region(world, player, 'Sewers Secret Room', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', + 'Sewers - Secret Room - Right']), create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']), create_dungeon_region(world, player, 'Inverted Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze', 'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop'], ['Agahnim 1', 'Inverted Agahnims Tower Exit']), create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None), @@ -346,7 +347,9 @@ def create_inverted_regions(world, player): create_cave_region(world, player, 'Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'], - ['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']), + ['Hookshot Cave Exit (South)', 'Hookshot Cave Bomb Wall (South)']), + create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)', + 'Hookshot Cave Bomb Wall (North)']), create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance']), create_cave_region(world, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']), @@ -380,8 +383,8 @@ def create_inverted_regions(world, player): create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest', 'Skull Woods - West Lobby Pot Key'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']), create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room', 'Skull Woods - Spike Corner Key Drop'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']), create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']), - create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop'], ['Ice Palace (Second Section)', 'Ice Palace Exit']), - create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Main)']), + create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Second Section)', 'Ice Palace Exit']), + create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']), create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Freezor Chest', 'Ice Palace - Many Pots Pot Key', 'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']), diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py index 1c3f3e44f72c..bb5bbaa61a02 100644 --- a/worlds/alttp/ItemPool.py +++ b/worlds/alttp/ItemPool.py @@ -5,12 +5,12 @@ from Fill import FillError from .SubClasses import ALttPLocation, LTTPRegion, LTTPRegionType -from .Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops, create_dynamic_shop_locations +from .Shops import TakeAny, total_shop_slots, set_up_shops, shop_table_by_location, ShopType from .Bosses import place_bosses from .Dungeons import get_dungeon_item_pool_player from .EntranceShuffle import connect_entrance -from .Items import ItemFactory, GetBeemizerItem -from .Options import smallkey_shuffle, compass_shuffle, bigkey_shuffle, map_shuffle, LTTPBosses +from .Items import ItemFactory, GetBeemizerItem, trap_replaceable, item_name_groups +from .Options import small_key_shuffle, compass_shuffle, big_key_shuffle, map_shuffle, TriforcePiecesMode from .StateHelpers import has_triforce_pieces, has_melee_weapon from .Regions import key_drop_data @@ -189,104 +189,62 @@ ), } -ice_rod_hunt_difficulties = dict() -for diff in {'easy', 'normal', 'hard', 'expert'}: - ice_rod_hunt_difficulties[diff] = Difficulty( - baseitems=['Nothing'] * 41, - bottles=['Nothing'] * 4, - bottle_count=difficulties[diff].bottle_count, - same_bottle=difficulties[diff].same_bottle, - progressiveshield=['Nothing'] * 3, - basicshield=['Nothing'] * 3, - progressivearmor=['Nothing'] * 2, - basicarmor=['Nothing'] * 2, - swordless=['Nothing'] * 4, - progressivemagic=['Nothing'] * 2, - basicmagic=['Nothing'] * 2, - progressivesword=['Nothing'] * 4, - basicsword=['Nothing'] * 4, - progressivebow=['Nothing'] * 2, - basicbow=['Nothing'] * 2, - timedohko=difficulties[diff].timedohko, - timedother=difficulties[diff].timedother, - progressiveglove=['Nothing'] * 2, - basicglove=['Nothing'] * 2, - alwaysitems=['Ice Rod'] + ['Nothing'] * 19, - legacyinsanity=['Nothing'] * 2, - universal_keys=['Nothing'] * 29, - extras=[['Nothing'] * 15, ['Nothing'] * 15, ['Nothing'] * 10, ['Nothing'] * 5, ['Nothing'] * 25], - progressive_sword_limit=difficulties[diff].progressive_sword_limit, - progressive_shield_limit=difficulties[diff].progressive_shield_limit, - progressive_armor_limit=difficulties[diff].progressive_armor_limit, - progressive_bow_limit=difficulties[diff].progressive_bow_limit, - progressive_bottle_limit=difficulties[diff].progressive_bottle_limit, - boss_heart_container_limit=difficulties[diff].boss_heart_container_limit, - heart_piece_limit=difficulties[diff].heart_piece_limit, - ) + +items_reduction_table = ( + ("Piece of Heart", "Boss Heart Container", 4, 1), + # the order of the upgrades is important + ("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 8, 4), + ("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 7, 4), + ("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 6, 3), + ("Arrow Upgrade (+10)", "Arrow Upgrade (70)", 4, 1), + ("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 8, 4), + ("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 7, 4), + ("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 6, 3), + ("Bomb Upgrade (+10)", "Bomb Upgrade (50)", 5, 1), + ("Bomb Upgrade (+10)", "Bomb Upgrade (50)", 4, 1), + ("Progressive Sword", 4), + ("Fighter Sword", 1), + ("Master Sword", 1), + ("Tempered Sword", 1), + ("Golden Sword", 1), + ("Progressive Shield", 3), + ("Blue Shield", 1), + ("Red Shield", 1), + ("Mirror Shield", 1), + ("Progressive Mail", 2), + ("Blue Mail", 1), + ("Red Mail", 1), + ("Progressive Bow", 2), + ("Bow", 1), + ("Silver Bow", 1), + ("Lamp", 1), + ("Bottles",) +) def generate_itempool(world): player = world.player multiworld = world.multiworld - if multiworld.difficulty[player] not in difficulties: - raise NotImplementedError(f"Diffulty {multiworld.difficulty[player]}") - if multiworld.goal[player] not in {'ganon', 'pedestal', 'bosses', 'triforcehunt', 'localtriforcehunt', 'icerodhunt', - 'ganontriforcehunt', 'localganontriforcehunt', 'crystals', 'ganonpedestal'}: + if multiworld.item_pool[player].current_key not in difficulties: + raise NotImplementedError(f"Diffulty {multiworld.item_pool[player]}") + if multiworld.goal[player] not in ('ganon', 'pedestal', 'bosses', 'triforce_hunt', 'local_triforce_hunt', + 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'crystals', + 'ganon_pedestal'): raise NotImplementedError(f"Goal {multiworld.goal[player]} for player {player}") - if multiworld.mode[player] not in {'open', 'standard', 'inverted'}: + if multiworld.mode[player] not in ('open', 'standard', 'inverted'): raise NotImplementedError(f"Mode {multiworld.mode[player]} for player {player}") - if multiworld.timer[player] not in {False, 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'}: + if multiworld.timer[player] not in {False, 'display', 'timed', 'timed_ohko', 'ohko', 'timed_countdown'}: raise NotImplementedError(f"Timer {multiworld.mode[player]} for player {player}") - if multiworld.timer[player] in ['ohko', 'timed-ohko']: + if multiworld.timer[player] in ['ohko', 'timed_ohko']: multiworld.can_take_damage[player] = False - if multiworld.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt', 'icerodhunt']: + if multiworld.goal[player] in ['pedestal', 'triforce_hunt', 'local_triforce_hunt']: multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Nothing', player), False) else: multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Triforce', player), False) - if multiworld.goal[player] == 'icerodhunt': - multiworld.progression_balancing[player].value = 0 - loc = multiworld.get_location('Turtle Rock - Boss', player) - multiworld.push_item(loc, ItemFactory('Triforce Piece', player), False) - multiworld.treasure_hunt_count[player] = 1 - if multiworld.boss_shuffle[player] != 'none': - if isinstance(multiworld.boss_shuffle[player].value, str) and 'turtle rock-' not in multiworld.boss_shuffle[player].value: - multiworld.boss_shuffle[player] = LTTPBosses.from_text(f'Turtle Rock-Trinexx;{multiworld.boss_shuffle[player].current_key}') - elif isinstance(multiworld.boss_shuffle[player].value, int): - multiworld.boss_shuffle[player] = LTTPBosses.from_text(f'Turtle Rock-Trinexx;{multiworld.boss_shuffle[player].current_key}') - else: - logging.warning(f'Cannot guarantee that Trinexx is the boss of Turtle Rock for player {player}') - loc.event = True - loc.locked = True - itemdiff = difficulties[multiworld.difficulty[player]] - itempool = [] - itempool.extend(itemdiff.alwaysitems) - itempool.remove('Ice Rod') - - itempool.extend(['Single Arrow', 'Sanctuary Heart Container']) - itempool.extend(['Boss Heart Container'] * itemdiff.boss_heart_container_limit) - itempool.extend(['Piece of Heart'] * itemdiff.heart_piece_limit) - itempool.extend(itemdiff.bottles) - itempool.extend(itemdiff.basicbow) - itempool.extend(itemdiff.basicarmor) - if not multiworld.swordless[player]: - itempool.extend(itemdiff.basicsword) - itempool.extend(itemdiff.basicmagic) - itempool.extend(itemdiff.basicglove) - itempool.extend(itemdiff.basicshield) - itempool.extend(itemdiff.legacyinsanity) - itempool.extend(['Rupees (300)'] * 34) - itempool.extend(['Bombs (10)'] * 5) - itempool.extend(['Arrows (10)'] * 7) - if multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal: - itempool.extend(itemdiff.universal_keys) - - for item in itempool: - multiworld.push_precollected(ItemFactory(item, player)) - - if multiworld.goal[player] in ['triforcehunt', 'localtriforcehunt', 'icerodhunt']: + if multiworld.goal[player] in ['triforce_hunt', 'local_triforce_hunt']: region = multiworld.get_region('Light World', player) loc = ALttPLocation(player, "Murahdahla", parent=region) @@ -308,7 +266,8 @@ def generate_itempool(world): ('Missing Smith', 'Return Smith'), ('Floodgate', 'Open Floodgate'), ('Agahnim 1', 'Beat Agahnim 1'), - ('Flute Activation Spot', 'Activated Flute') + ('Flute Activation Spot', 'Activated Flute'), + ('Capacity Upgrade Shop', 'Capacity Upgrade Shop') ] for location_name, event_name in event_pairs: location = multiworld.get_location(location_name, player) @@ -340,17 +299,31 @@ def generate_itempool(world): if not found_sword: found_sword = True possible_weapons.append(item) - if item in ['Progressive Bow', 'Bow'] and not found_bow: + elif item in ['Progressive Bow', 'Bow'] and not found_bow: found_bow = True possible_weapons.append(item) - if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: + elif item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: if item not in possible_weapons: possible_weapons.append(item) + elif (item == 'Bombs (10)' and (not multiworld.bombless_start[player]) and item not in + possible_weapons): + possible_weapons.append(item) + elif (item in ['Bomb Upgrade (+10)', 'Bomb Upgrade (50)'] and multiworld.bombless_start[player] and item + not in possible_weapons): + possible_weapons.append(item) + starting_weapon = multiworld.random.choice(possible_weapons) placed_items["Link's Uncle"] = starting_weapon pool.remove(starting_weapon) - if placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria', 'Cane of Byrna'] and multiworld.enemy_health[player] not in ['default', 'easy']: - multiworld.escape_assist[player].append('bombs') + if (placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Bomb Upgrade (+10)', + 'Bomb Upgrade (50)', 'Cane of Somaria', 'Cane of Byrna'] and multiworld.enemy_health[player] not in ['default', 'easy']): + if multiworld.bombless_start[player] and "Bomb Upgrade" not in placed_items["Link's Uncle"]: + if 'Bow' in placed_items["Link's Uncle"]: + multiworld.escape_assist[player].append('arrows') + elif 'Cane' in placed_items["Link's Uncle"]: + multiworld.escape_assist[player].append('magic') + else: + multiworld.escape_assist[player].append('bombs') for (location, item) in placed_items.items(): multiworld.get_location(location, player).place_locked_item(ItemFactory(item, player)) @@ -377,7 +350,7 @@ def generate_itempool(world): for key_loc in key_drop_data: key_data = key_drop_data[key_loc] drop_item = ItemFactory(key_data[3], player) - if multiworld.goal[player] == 'icerodhunt' or not multiworld.key_drop_shuffle[player]: + if not multiworld.key_drop_shuffle[player]: if drop_item in dungeon_items: dungeon_items.remove(drop_item) else: @@ -391,88 +364,151 @@ def generate_itempool(world): world.dungeons[dungeon].small_keys.remove(drop_item) elif world.dungeons[dungeon].big_key is not None and world.dungeons[dungeon].big_key == drop_item: world.dungeons[dungeon].big_key = None - if not multiworld.key_drop_shuffle[player]: - # key drop item was removed from the pool because key drop shuffle is off - # and it will now place the removed key into its original location + loc = multiworld.get_location(key_loc, player) loc.place_locked_item(drop_item) loc.address = None - elif multiworld.goal[player] == 'icerodhunt': - # key drop item removed because of icerodhunt - multiworld.itempool.append(ItemFactory(GetBeemizerItem(world, player, 'Nothing'), player)) - multiworld.push_precollected(drop_item) - elif "Small" in key_data[3] and multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + elif "Small" in key_data[3] and multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: # key drop shuffle and universal keys are on. Add universal keys in place of key drop keys. - multiworld.itempool.append(ItemFactory(GetBeemizerItem(world, player, 'Small Key (Universal)'), player)) + multiworld.itempool.append(ItemFactory(GetBeemizerItem(multiworld, player, 'Small Key (Universal)'), player)) dungeon_item_replacements = sum(difficulties[multiworld.difficulty[player]].extras, []) * 2 multiworld.random.shuffle(dungeon_item_replacements) - if multiworld.goal[player] == 'icerodhunt': - for item in dungeon_items: - multiworld.itempool.append(ItemFactory(GetBeemizerItem(multiworld, player, 'Nothing'), player)) + + for x in range(len(dungeon_items)-1, -1, -1): + item = dungeon_items[x] + if ((multiworld.small_key_shuffle[player] == small_key_shuffle.option_start_with and item.type == 'SmallKey') + or (multiworld.big_key_shuffle[player] == big_key_shuffle.option_start_with and item.type == 'BigKey') + or (multiworld.compass_shuffle[player] == compass_shuffle.option_start_with and item.type == 'Compass') + or (multiworld.map_shuffle[player] == map_shuffle.option_start_with and item.type == 'Map')): + dungeon_items.pop(x) multiworld.push_precollected(item) + multiworld.itempool.append(ItemFactory(dungeon_item_replacements.pop(), player)) + multiworld.itempool.extend([item for item in dungeon_items]) + + set_up_shops(multiworld, player) + + if multiworld.retro_bow[player]: + shop_items = 0 + shop_locations = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if + shop.type == ShopType.Shop and shop.region.player == player) for location in shop_locations if + location.shop_slot is not None] + for location in shop_locations: + if location.shop.inventory[location.shop_slot]["item"] == "Single Arrow": + location.place_locked_item(ItemFactory("Single Arrow", player)) + else: + shop_items += 1 else: - for x in range(len(dungeon_items)-1, -1, -1): - item = dungeon_items[x] - if ((multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_start_with and item.type == 'SmallKey') - or (multiworld.bigkey_shuffle[player] == bigkey_shuffle.option_start_with and item.type == 'BigKey') - or (multiworld.compass_shuffle[player] == compass_shuffle.option_start_with and item.type == 'Compass') - or (multiworld.map_shuffle[player] == map_shuffle.option_start_with and item.type == 'Map')): - dungeon_items.pop(x) - multiworld.push_precollected(item) - multiworld.itempool.append(ItemFactory(dungeon_item_replacements.pop(), player)) - multiworld.itempool.extend([item for item in dungeon_items]) - # logic has some branches where having 4 hearts is one possible requirement (of several alternatives) - # rather than making all hearts/heart pieces progression items (which slows down generation considerably) - # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) - if multiworld.goal[player] != 'icerodhunt' and multiworld.difficulty[player] in ['easy', 'normal', 'hard'] and not (multiworld.custom and multiworld.customitemarray[30] == 0): - next(item for item in items if item.name == 'Boss Heart Container').classification = ItemClassification.progression - elif multiworld.goal[player] != 'icerodhunt' and multiworld.difficulty[player] in ['expert'] and not (multiworld.custom and multiworld.customitemarray[29] < 4): - adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart') - for i in range(4): - next(adv_heart_pieces).classification = ItemClassification.progression - - - progressionitems = [] - nonprogressionitems = [] - for item in items: - if item.advancement or item.type: - progressionitems.append(item) + shop_items = min(multiworld.shop_item_slots[player], 30 if multiworld.include_witch_hut[player] else 27) + + if multiworld.shuffle_capacity_upgrades[player]: + shop_items += 2 + chance_100 = int(multiworld.retro_bow[player]) * 0.25 + int( + multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal) * 0.5 + for _ in range(shop_items): + if multiworld.random.random() < chance_100: + items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (100)"), player)) else: - nonprogressionitems.append(GetBeemizerItem(multiworld, item.player, item)) - multiworld.random.shuffle(nonprogressionitems) - - if additional_triforce_pieces: - if additional_triforce_pieces > len(nonprogressionitems): - raise FillError(f"Not enough non-progression items to replace with Triforce pieces found for player " - f"{multiworld.get_player_name(player)}.") - progressionitems += [ItemFactory("Triforce Piece", player) for _ in range(additional_triforce_pieces)] - nonprogressionitems.sort(key=lambda item: int("Heart" in item.name)) # try to keep hearts in the pool - nonprogressionitems = nonprogressionitems[additional_triforce_pieces:] - multiworld.random.shuffle(nonprogressionitems) - - # shuffle medallions - if multiworld.required_medallions[player][0] == "random": - mm_medallion = multiworld.random.choice(['Ether', 'Quake', 'Bombos']) - else: - mm_medallion = multiworld.required_medallions[player][0] - if multiworld.required_medallions[player][1] == "random": - tr_medallion = multiworld.random.choice(['Ether', 'Quake', 'Bombos']) + items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (50)"), player)) + + multiworld.random.shuffle(items) + pool_count = len(items) + new_items = ["Triforce Piece" for _ in range(additional_triforce_pieces)] + if multiworld.shuffle_capacity_upgrades[player] or multiworld.bombless_start[player]: + progressive = multiworld.progressive[player] + progressive = multiworld.random.choice([True, False]) if progressive == 'grouped_random' else progressive == 'on' + if multiworld.shuffle_capacity_upgrades[player] == "on_combined": + new_items.append("Bomb Upgrade (50)") + elif multiworld.shuffle_capacity_upgrades[player] == "on": + new_items += ["Bomb Upgrade (+5)"] * 6 + new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)") + if multiworld.shuffle_capacity_upgrades[player] != "on_combined" and multiworld.bombless_start[player]: + new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)") + + if multiworld.shuffle_capacity_upgrades[player] and not multiworld.retro_bow[player]: + if multiworld.shuffle_capacity_upgrades[player] == "on_combined": + new_items += ["Arrow Upgrade (70)"] + else: + new_items += ["Arrow Upgrade (+5)"] * 6 + new_items.append("Arrow Upgrade (+5)" if progressive else "Arrow Upgrade (+10)") + + items += [ItemFactory(item, player) for item in new_items] + removed_filler = [] + + multiworld.random.shuffle(items) # Decide what gets tossed randomly. + + while len(items) > pool_count: + for i, item in enumerate(items): + if item.classification in (ItemClassification.filler, ItemClassification.trap): + removed_filler.append(items.pop(i)) + break + else: + # no more junk to remove, condense progressive items + def condense_items(items, small_item, big_item, rem, add): + small_item = ItemFactory(small_item, player) + # while (len(items) >= pool_count + rem - 1 # minus 1 to account for the replacement item + # and items.count(small_item) >= rem): + if items.count(small_item) >= rem: + for _ in range(rem): + items.remove(small_item) + removed_filler.append(ItemFactory(small_item.name, player)) + items += [ItemFactory(big_item, player) for _ in range(add)] + return True + return False + + def cut_item(items, item_to_cut, minimum_items): + item_to_cut = ItemFactory(item_to_cut, player) + if items.count(item_to_cut) > minimum_items: + items.remove(item_to_cut) + removed_filler.append(ItemFactory(item_to_cut.name, player)) + return True + return False + + while len(items) > pool_count: + items_were_cut = False + for reduce_item in items_reduction_table: + if len(items) <= pool_count: + break + if len(reduce_item) == 2: + items_were_cut = items_were_cut or cut_item(items, *reduce_item) + elif len(reduce_item) == 4: + items_were_cut = items_were_cut or condense_items(items, *reduce_item) + elif len(reduce_item) == 1: # Bottles + bottles = [item for item in items if item.name in item_name_groups["Bottles"]] + if len(bottles) > 4: + bottle = multiworld.random.choice(bottles) + items.remove(bottle) + removed_filler.append(bottle) + items_were_cut = True + assert items_were_cut, f"Failed to limit item pool size for player {player}" + if len(items) < pool_count: + items += removed_filler[len(items) - pool_count:] + + if multiworld.randomize_cost_types[player]: + # Heart and Arrow costs require all Heart Container/Pieces and Arrow Upgrades to be advancement items for logic + for item in items: + if (item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart") + or "Arrow Upgrade" in item.name): + item.classification = ItemClassification.progression else: - tr_medallion = multiworld.required_medallions[player][1] - multiworld.required_medallions[player] = (mm_medallion, tr_medallion) + # Otherwise, logic has some branches where having 4 hearts is one possible requirement (of several alternatives) + # rather than making all hearts/heart pieces progression items (which slows down generation considerably) + # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) + if multiworld.item_pool[player] in ['easy', 'normal', 'hard'] and not (multiworld.custom and multiworld.customitemarray[30] == 0): + next(item for item in items if item.name == 'Boss Heart Container').classification = ItemClassification.progression + elif multiworld.item_pool[player] in ['expert'] and not (multiworld.custom and multiworld.customitemarray[29] < 4): + adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart') + for i in range(4): + next(adv_heart_pieces).classification = ItemClassification.progression + + multiworld.required_medallions[player] = (multiworld.misery_mire_medallion[player].current_key.title(), + multiworld.turtle_rock_medallion[player].current_key.title()) place_bosses(world) - set_up_shops(multiworld, player) - - if multiworld.shop_shuffle[player]: - shuffle_shops(multiworld, nonprogressionitems, player) - multiworld.itempool += progressionitems + nonprogressionitems + multiworld.itempool += items if multiworld.retro_caves[player]: set_up_take_anys(multiworld, player) # depends on world.itempool to be set - # set_up_take_anys needs to run first - create_dynamic_shop_locations(multiworld, player) take_any_locations = { @@ -516,9 +552,14 @@ def set_up_take_anys(world, player): sword = world.random.choice(swords) world.itempool.remove(sword) world.itempool.append(ItemFactory('Rupees (20)', player)) - old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True) + old_man_take_any.shop.add_inventory(0, sword.name, 0, 0) + loc_name = "Old Man Sword Cave" + location = ALttPLocation(player, loc_name, shop_table_by_location[loc_name], parent=old_man_take_any) + location.shop_slot = 0 + old_man_take_any.locations.append(location) + location.place_locked_item(sword) else: - old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=True) + old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0) for num in range(4): take_any = LTTPRegion("Take-Any #{}".format(num+1), LTTPRegionType.Cave, 'a cave of choice', player, world) @@ -532,18 +573,22 @@ def set_up_take_anys(world, player): take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True, total_shop_slots + num + 1) world.shops.append(take_any.shop) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0) - take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=True) + take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0) + location = ALttPLocation(player, take_any.name, shop_table_by_location[take_any.name], parent=take_any) + location.shop_slot = 1 + take_any.locations.append(location) + location.place_locked_item(ItemFactory("Boss Heart Container", player)) def get_pool_core(world, player: int): - shuffle = world.shuffle[player] - difficulty = world.difficulty[player] - timer = world.timer[player] - goal = world.goal[player] - mode = world.mode[player] + shuffle = world.entrance_shuffle[player].current_key + difficulty = world.item_pool[player].current_key + timer = world.timer[player].current_key + goal = world.goal[player].current_key + mode = world.mode[player].current_key swordless = world.swordless[player] retro_bow = world.retro_bow[player] - logic = world.logic[player] + logic = world.glitches_required[player] pool = [] placed_items = {} @@ -552,7 +597,7 @@ def get_pool_core(world, player: int): treasure_hunt_count = None treasure_hunt_icon = None - diff = ice_rod_hunt_difficulties[difficulty] if goal == 'icerodhunt' else difficulties[difficulty] + diff = difficulties[difficulty] pool.extend(diff.alwaysitems) def place_item(loc, item): @@ -560,7 +605,7 @@ def place_item(loc, item): placed_items[loc] = item # provide boots to major glitch dependent seeds - if logic in {'owglitches', 'hybridglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt': + if logic in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and world.glitch_boots[player]: precollected_items.append('Pegasus Boots') pool.remove('Pegasus Boots') pool.append('Rupees (20)') @@ -611,7 +656,7 @@ def place_item(loc, item): if want_progressives(world.random): pool.extend(diff.progressivebow) world.worlds[player].has_progressive_bows = True - elif (swordless or logic == 'noglitches') and goal != 'icerodhunt': + elif (swordless or logic == 'no_glitches'): swordless_bows = ['Bow', 'Silver Bow'] if difficulty == "easy": swordless_bows *= 2 @@ -627,21 +672,32 @@ def place_item(loc, item): extraitems = total_items_to_place - len(pool) - len(placed_items) - if timer in ['timed', 'timed-countdown']: + if timer in ['timed', 'timed_countdown']: pool.extend(diff.timedother) extraitems -= len(diff.timedother) clock_mode = 'stopwatch' if timer == 'timed' else 'countdown' - elif timer == 'timed-ohko': + elif timer == 'timed_ohko': pool.extend(diff.timedohko) extraitems -= len(diff.timedohko) clock_mode = 'countdown-ohko' additional_pieces_to_place = 0 - if 'triforcehunt' in goal: - pieces_in_core = min(extraitems, world.triforce_pieces_available[player]) - additional_pieces_to_place = world.triforce_pieces_available[player] - pieces_in_core + if 'triforce_hunt' in goal: + + if world.triforce_pieces_mode[player].value == TriforcePiecesMode.option_extra: + triforce_pieces = world.triforce_pieces_available[player].value + world.triforce_pieces_extra[player].value + elif world.triforce_pieces_mode[player].value == TriforcePiecesMode.option_percentage: + percentage = float(max(100, world.triforce_pieces_percentage[player].value)) / 100 + triforce_pieces = int(round(world.triforce_pieces_required[player].value * percentage, 0)) + else: # available + triforce_pieces = world.triforce_pieces_available[player].value + + triforce_pieces = max(triforce_pieces, world.triforce_pieces_required[player].value) + + pieces_in_core = min(extraitems, triforce_pieces) + additional_pieces_to_place = triforce_pieces - pieces_in_core pool.extend(["Triforce Piece"] * pieces_in_core) extraitems -= pieces_in_core - treasure_hunt_count = world.triforce_pieces_required[player] + treasure_hunt_count = world.triforce_pieces_required[player].value treasure_hunt_icon = 'Triforce Piece' for extra in diff.extras: @@ -659,12 +715,12 @@ def place_item(loc, item): pool.remove("Rupees (20)") if retro_bow: - replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)'} + replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (50)'} pool = ['Rupees (5)' if item in replace else item for item in pool] - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if world.small_key_shuffle[player] == small_key_shuffle.option_universal: pool.extend(diff.universal_keys) if mode == 'standard': - if world.key_drop_shuffle[player] and world.goal[player] != 'icerodhunt': + if world.key_drop_shuffle[player]: key_locations = ['Secret Passage', 'Hyrule Castle - Map Guard Key Drop'] key_location = world.random.choice(key_locations) key_locations.remove(key_location) @@ -688,8 +744,8 @@ def place_item(loc, item): def make_custom_item_pool(world, player): - shuffle = world.shuffle[player] - difficulty = world.difficulty[player] + shuffle = world.entrance_shuffle[player] + difficulty = world.item_pool[player] timer = world.timer[player] goal = world.goal[player] mode = world.mode[player] @@ -798,9 +854,9 @@ def place_item(loc, item): treasure_hunt_count = world.triforce_pieces_required[player] treasure_hunt_icon = 'Triforce Piece' - if timer in ['display', 'timed', 'timed-countdown']: - clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch' - elif timer == 'timed-ohko': + if timer in ['display', 'timed', 'timed_countdown']: + clock_mode = 'countdown' if timer == 'timed_countdown' else 'stopwatch' + elif timer == 'timed_ohko': clock_mode = 'countdown-ohko' elif timer == 'ohko': clock_mode = 'ohko' @@ -810,7 +866,7 @@ def place_item(loc, item): itemtotal = itemtotal + 1 if mode == 'standard': - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if world.small_key_shuffle[player] == small_key_shuffle.option_universal: key_location = world.random.choice( ['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) @@ -833,7 +889,7 @@ def place_item(loc, item): pool.extend(['Magic Mirror'] * customitemarray[22]) pool.extend(['Moon Pearl'] * customitemarray[28]) - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if world.small_key_shuffle[player] == small_key_shuffle.option_universal: itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in universal Mode if world.key_drop_shuffle[player]: itemtotal = itemtotal - (len(key_drop_data) - 1) diff --git a/worlds/alttp/Items.py b/worlds/alttp/Items.py index 18f96b2ddb81..8e513552ad10 100644 --- a/worlds/alttp/Items.py +++ b/worlds/alttp/Items.py @@ -112,13 +112,15 @@ def as_init_dict(self) -> typing.Dict[str, typing.Any]: 'Crystal 7': ItemData(IC.progression, 'Crystal', (0x08, 0x34, 0x64, 0x40, 0x7C, 0x06), None, None, None, None, None, None, "a blue crystal"), 'Single Arrow': ItemData(IC.filler, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Arrows (10)': ItemData(IC.filler, None, 0x44, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack','stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again','ten arrows'), - 'Arrow Upgrade (+10)': ItemData(IC.filler, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), - 'Arrow Upgrade (+5)': ItemData(IC.filler, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+10)': ItemData(IC.useful, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+5)': ItemData(IC.useful, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (70)': ItemData(IC.useful, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Single Bomb': ItemData(IC.filler, None, 0x27, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Bombs (3)': ItemData(IC.filler, None, 0x28, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': ItemData(IC.filler, None, 0x31, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), - 'Bomb Upgrade (+10)': ItemData(IC.filler, None, 0x52, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), - 'Bomb Upgrade (+5)': ItemData(IC.filler, None, 0x51, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+10)': ItemData(IC.progression, None, 0x52, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+5)': ItemData(IC.progression, None, 0x51, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (50)': ItemData(IC.progression, None, 0x4C, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Blue Mail': ItemData(IC.useful, None, 0x22, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'), 'Red Mail': ItemData(IC.useful, None, 0x23, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'), 'Progressive Mail': ItemData(IC.useful, None, 0x60, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), @@ -222,6 +224,7 @@ def as_init_dict(self) -> typing.Dict[str, typing.Any]: 'Return Smith': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None), 'Pick Up Purple Chest': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None), 'Open Floodgate': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None), + 'Capacity Upgrade Shop': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None), } item_init_table = {name: data.as_init_dict() for name, data in item_table.items()} @@ -287,5 +290,5 @@ def as_init_dict(self) -> typing.Dict[str, typing.Any]: item_table[name].classification in {IC.progression, IC.progression_skip_balancing}} item_name_groups['Progression Items'] = progression_items item_name_groups['Non Progression Items'] = everything - progression_items - +item_name_groups['Upgrades'] = {name for name in everything if 'Upgrade' in name} trap_replaceable = item_name_groups['Rupees'] | {'Arrows (10)', 'Single Bomb', 'Bombs (3)', 'Bombs (10)', 'Nothing'} diff --git a/worlds/alttp/Options.py b/worlds/alttp/Options.py index a89a9adb83a7..ed6af6dd674f 100644 --- a/worlds/alttp/Options.py +++ b/worlds/alttp/Options.py @@ -1,10 +1,18 @@ import typing from BaseClasses import MultiWorld -from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool, PlandoBosses - - -class Logic(Choice): +from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool, PlandoBosses,\ + FreeText + + +class GlitchesRequired(Choice): + """Determine the logic required to complete the seed + None: No glitches required + Minor Glitches: Puts fake flipper, waterwalk, super bunny shenanigans, and etc into logic + Overworld Glitches: Assumes the player has knowledge of both overworld major glitches (boots clips, mirror clips) and minor glitches + Hybrid Major Glitches: In addition to overworld glitches, also requires underworld clips between dungeons. + No Logic: Your own items are placed with no regard to any logic; such as your Fire Rod can be on your Trinexx.""" + display_name = "Glitches Required" option_no_glitches = 0 option_minor_glitches = 1 option_overworld_glitches = 2 @@ -12,20 +20,122 @@ class Logic(Choice): option_no_logic = 4 alias_owg = 2 alias_hmg = 3 + alias_none = 0 -class Objective(Choice): - option_crystals = 0 - # option_pendants = 1 - option_triforce_pieces = 2 - option_pedestal = 3 - option_bingo = 4 +class DarkRoomLogic(Choice): + """Logic for unlit dark rooms. Lamp: require the Lamp for these rooms to be considered accessible. + Torches: in addition to lamp, allow the fire rod and presence of easily accessible torches for access. + None: all dark rooms are always considered doable, meaning this may force completion of rooms in complete darkness.""" + display_name = "Dark Room Logic" + option_lamp = 0 + option_torches = 1 + option_none = 2 + default = 0 class Goal(Choice): - option_kill_ganon = 0 - option_kill_ganon_and_gt_agahnim = 1 - option_hand_in = 2 + """Ganon: Climb GT, defeat Agahnim 2, and then kill Ganon + Crystals: Only killing Ganon is required. However, items may still be placed in GT + Bosses: Defeat the boss of all dungeons, including Agahnim's tower and GT (Aga 2) + Pedestal: Pull the Triforce from the Master Sword pedestal + Ganon Pedestal: Pull the Master Sword pedestal, then kill Ganon + Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then turn them in to Murahadala in front of Hyrule Castle + Local Triforce Hunt: Collect Triforce pieces spread throughout your world, then turn them in to Murahadala in front of Hyrule Castle + Ganon Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then kill Ganon + Local Ganon Triforce Hunt: Collect Triforce pieces spread throughout your world, then kill Ganon + Ice Rod Hunt: You start with everything except Ice Rod. Find the Ice rod, then kill Trinexx at Turtle rock.""" + display_name = "Goal" + default = 0 + option_ganon = 0 + option_crystals = 1 + option_bosses = 2 + option_pedestal = 3 + option_ganon_pedestal = 4 + option_triforce_hunt = 5 + option_local_triforce_hunt = 6 + option_ganon_triforce_hunt = 7 + option_local_ganon_triforce_hunt = 8 + + +class EntranceShuffle(Choice): + """Dungeons Simple: Shuffle just dungeons amongst each other, swapping dungeons entirely, so Hyrule Castle is always 1 dungeon. + Dungeons Full: Shuffle any dungeon entrance with any dungeon interior, so Hyrule Castle can be 4 different dungeons, but keep dungeons to a specific world. + Dungeons Crossed: like dungeons_full, but allow cross-world traversal through a dungeon. Warning: May force repeated dungeon traversal. + Simple: Entrances are grouped together before being randomized. Interiors with two entrances are grouped shuffled together with each other, + and Death Mountain entrances are shuffled only on Death Mountain. Dungeons are swapped entirely. + Restricted: Like Simple, but single entrance interiors, multi entrance interiors, and Death Mountain interior entrances are all shuffled with each other. + Full: Like Restricted, but all Dungeon entrances are shuffled with all non-Dungeon entrances. + Crossed: Like Full, but interiors with multiple entrances are no longer confined to the same world, which may allow crossing worlds. + Insanity: Like Crossed, but entrances and exits may be decoupled from each other, so that leaving through an exit may not return you to the entrance you entered from.""" + display_name = "Entrance Shuffle" + default = 0 + alias_none = 0 + option_vanilla = 0 + option_dungeons_simple = 1 + option_dungeons_full = 2 + option_dungeons_crossed = 3 + option_simple = 4 + option_restricted = 5 + option_full = 6 + option_crossed = 7 + option_insanity = 8 + alias_dungeonssimple = 1 + alias_dungeonsfull = 2 + alias_dungeonscrossed = 3 + + +class EntranceShuffleSeed(FreeText): + """You can specify a number to use as an entrance shuffle seed, or a group name. Everyone with the same group name + will get the same entrance shuffle result as long as their Entrance Shuffle, Mode, Retro Caves, and Glitches + Required options are the same.""" + default = "random" + display_name = "Entrance Shuffle Seed" + + +class TriforcePiecesMode(Choice): + """Determine how to calculate the extra available triforce pieces. + Extra: available = triforce_pieces_extra + triforce_pieces_required + Percentage: available = (triforce_pieces_percentage /100) * triforce_pieces_required + Available: available = triforce_pieces_available""" + display_name = "Triforce Pieces Mode" + default = 2 + option_extra = 0 + option_percentage = 1 + option_available = 2 + + +class TriforcePiecesPercentage(Range): + """Set to how many triforce pieces according to a percentage of the required ones, are available to collect in the world.""" + display_name = "Triforce Pieces Percentage" + range_start = 100 + range_end = 1000 + default = 150 + + +class TriforcePiecesAvailable(Range): + """Set to how many triforces pieces are available to collect in the world. Default is 30. Max is 90, Min is 1""" + display_name = "Triforce Pieces Available" + range_start = 1 + range_end = 90 + default = 30 + + +class TriforcePiecesRequired(Range): + """Set to how many out of X triforce pieces you need to win the game in a triforce hunt. + Default is 20. Max is 90, Min is 1.""" + display_name = "Triforce Pieces Required" + range_start = 1 + range_end = 90 + default = 20 + + +class TriforcePiecesExtra(Range): + """Set to how many extra triforces pieces are available to collect in the world.""" + display_name = "Triforce Pieces Extra" + range_start = 0 + range_end = 89 + default = 10 class OpenPyramid(Choice): @@ -44,10 +154,10 @@ class OpenPyramid(Choice): def to_bool(self, world: MultiWorld, player: int) -> bool: if self.value == self.option_goal: - return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} + return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} elif self.value == self.option_auto: - return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} \ - and (world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed'} or not + return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \ + and (world.entrance_shuffle[player] in {'vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed'} or not world.shuffle_ganon) elif self.value == self.option_open: return True @@ -76,13 +186,13 @@ def hints_useful(self): return self.value in {1, 2, 3, 4} -class bigkey_shuffle(DungeonItem): +class big_key_shuffle(DungeonItem): """Big Key Placement""" item_name_group = "Big Keys" display_name = "Big Key Shuffle" -class smallkey_shuffle(DungeonItem): +class small_key_shuffle(DungeonItem): """Small Key Placement""" option_universal = 5 item_name_group = "Small Keys" @@ -107,6 +217,149 @@ class key_drop_shuffle(Toggle): display_name = "Key Drop Shuffle" + +class DungeonCounters(Choice): + """On: Always display amount of items checked in a dungeon. Pickup: Show when compass is picked up. + Default: Show when compass is picked up if the compass itself is shuffled. Off: Never show item count in dungeons.""" + display_name = "Dungeon Counters" + default = 1 + option_on = 0 + option_pickup = 1 + option_default = 2 + option_off = 4 + + +class Mode(Choice): + """Standard: Begin the game by rescuing Zelda from her cell and escorting her to the Sanctuary + Open: Begin the game from your choice of Link's House or the Sanctuary + Inverted: Begin in the Dark World. The Moon Pearl is required to avoid bunny-state in Light World, and the Light World game map is altered""" + option_standard = 0 + option_open = 1 + option_inverted = 2 + default = 1 + display_name = "Mode" + + +class ItemPool(Choice): + """Easy: Doubled upgrades, progressives, and etc. Normal: Item availability remains unchanged from vanilla game. + Hard: Reduced upgrade availability (max: 14 hearts, blue mail, tempered sword, fire shield, no silvers unless swordless). + Expert: Minimum upgrade availability (max: 8 hearts, green mail, master sword, fighter shield, no silvers unless swordless).""" + display_name = "Item Pool" + default = 1 + option_easy = 0 + option_normal = 1 + option_hard = 2 + option_expert = 3 + + +class ItemFunctionality(Choice): + """Easy: Allow Hammer to damage ganon, Allow Hammer tablet collection, Allow swordless medallion use everywhere. + Normal: Vanilla item functionality + Hard: Reduced helpfulness of items (potions less effective, can't catch faeries, cape uses double magic, byrna does not grant invulnerability, boomerangs do not stun, silvers disabled outside ganon) + Expert: Vastly reduces the helpfulness of items (potions barely effective, can't catch faeries, cape uses double magic, byrna does not grant invulnerability, boomerangs and hookshot do not stun, silvers disabled outside ganon)""" + display_name = "Item Functionality" + default = 1 + option_easy = 0 + option_normal = 1 + option_hard = 2 + option_expert = 3 + + +class EnemyHealth(Choice): + """Default: Vanilla enemy HP. Easy: Enemies have reduced health. Hard: Enemies have increased health. + Expert: Enemies have greatly increased health.""" + display_name = "Enemy Health" + default = 1 + option_easy = 0 + option_default = 1 + option_hard = 2 + option_expert = 3 + + +class EnemyDamage(Choice): + """Default: Vanilla enemy damage. Shuffled: 0 # Enemies deal 0 to 4 hearts and armor helps. + Chaos: Enemies deal 0 to 8 hearts and armor just reshuffles the damage.""" + display_name = "Enemy Damage" + default = 0 + option_default = 0 + option_shuffled = 2 + option_chaos = 3 + + +class ShufflePrizes(Choice): + """Shuffle "general" prize packs, as in enemy, tree pull, dig etc.; "bonk" prizes; or both.""" + display_name = "Shuffle Prizes" + default = 1 + option_off = 0 + option_general = 1 + option_bonk = 2 + option_both = 3 + + +class Medallion(Choice): + default = "random" + option_ether = 0 + option_bombos = 1 + option_quake = 2 + + +class MiseryMireMedallion(Medallion): + """Required medallion to open Misery Mire front entrance.""" + display_name = "Misery Mire Medallion" + + +class TurtleRockMedallion(Medallion): + """Required medallion to open Turtle Rock front entrance.""" + display_name = "Turtle Rock Medallion" + + +class Timer(Choice): + """None: No timer will be displayed. OHKO: Timer always at zero. Permanent OHKO. + Timed: Starts with clock at zero. Green clocks subtract 4 minutes (total 20). Blue clocks subtract 2 minutes (total 10). Red clocks add two minutes (total 10). Winner is the player with the lowest time at the end. + Timed OHKO: Starts the clock at ten minutes. Green clocks add five minutes (total 25). As long as the clock as at zero, Link will die in one hit. + Timed Countdown: Starts the clock with forty minutes. Same clocks as timed mode, but if the clock hits zero you lose. You can still keep playing, though. + Display: Displays a timer, but otherwise does not affect gameplay or the item pool.""" + display_name = "Timer" + option_none = 0 + option_timed = 1 + option_timed_ohko = 2 + option_ohko = 3 + option_timed_countdown = 4 + option_display = 5 + default = 0 + + +class CountdownStartTime(Range): + """For Timed OHKO and Timed Countdown timer modes, the amount of time in minutes to start with.""" + display_name = "Countdown Start Time" + range_start = 0 + range_end = 480 + default = 10 + + +class ClockTime(Range): + range_start = -60 + range_end = 60 + + +class RedClockTime(ClockTime): + """For all timer modes, the amount of time in minutes to gain or lose when picking up a red clock.""" + display_name = "Red Clock Time" + default = -2 + + +class BlueClockTime(ClockTime): + """For all timer modes, the amount of time in minutes to gain or lose when picking up a blue clock.""" + display_name = "Blue Clock Time" + default = 2 + + +class GreenClockTime(ClockTime): + """For all timer modes, the amount of time in minutes to gain or lose when picking up a green clock.""" + display_name = "Green Clock Time" + default = 4 + + class Crystals(Range): range_start = 0 range_end = 7 @@ -137,18 +390,52 @@ class ShopItemSlots(Range): range_end = 30 +class RandomizeShopInventories(Choice): + """Generate new default inventories for overworld/underworld shops, and unique shops; or each shop independently""" + display_name = "Randomize Shop Inventories" + default = 0 + option_default = 0 + option_randomize_by_shop_type = 1 + option_randomize_each = 2 + + +class ShuffleShopInventories(Toggle): + """Shuffle default inventories of the shops around""" + display_name = "Shuffle Shop Inventories" + + +class RandomizeShopPrices(Toggle): + """Randomize the prices of the items in shop inventories""" + display_name = "Randomize Shop Prices" + + +class RandomizeCostTypes(Toggle): + """Prices of the items in shop inventories may cost hearts, arrow, or bombs instead of rupees""" + display_name = "Randomize Cost Types" + + class ShopPriceModifier(Range): """Percentage modifier for shuffled item prices in shops""" - display_name = "Shop Price Cost Percent" + display_name = "Shop Price Modifier" range_start = 0 default = 100 range_end = 400 -class WorldState(Choice): - option_standard = 1 - option_open = 0 - option_inverted = 2 +class IncludeWitchHut(Toggle): + """Consider witch's hut like any other shop and shuffle/randomize it too""" + display_name = "Include Witch's Hut" + + +class ShuffleCapacityUpgrades(Choice): + """Shuffle capacity upgrades into the item pool (and allow them to traverse the multiworld). + On Combined will shuffle only a single bomb upgrade and arrow upgrade each which bring you to the maximum capacity.""" + display_name = "Shuffle Capacity Upgrades" + option_off = 0 + option_on = 1 + option_on_combined = 2 + alias_false = 0 + alias_true = 1 class LTTPBosses(PlandoBosses): @@ -236,6 +523,11 @@ class Swordless(Toggle): display_name = "Swordless" +class BomblessStart(Toggle): + """Start with a max of 0 bombs available, requiring Bomb Upgrade items in order to use bombs""" + display_name = "Bombless Start" + + # Might be a decent idea to split "Bow" into its own option with choices of # Defer to Progressive Option (default), Progressive, Non-Progressive, Bow + Silvers, Retro class RetroBow(Toggle): @@ -433,29 +725,66 @@ class AllowCollect(Toggle): alttp_options: typing.Dict[str, type(Option)] = { + "start_inventory_from_pool": StartInventoryPool, + "goal": Goal, + "mode": Mode, + "glitches_required": GlitchesRequired, + "dark_room_logic": DarkRoomLogic, + "open_pyramid": OpenPyramid, "crystals_needed_for_gt": CrystalsTower, "crystals_needed_for_ganon": CrystalsGanon, - "open_pyramid": OpenPyramid, - "bigkey_shuffle": bigkey_shuffle, - "smallkey_shuffle": smallkey_shuffle, + "triforce_pieces_mode": TriforcePiecesMode, + "triforce_pieces_percentage": TriforcePiecesPercentage, + "triforce_pieces_required": TriforcePiecesRequired, + "triforce_pieces_available": TriforcePiecesAvailable, + "triforce_pieces_extra": TriforcePiecesExtra, + "entrance_shuffle": EntranceShuffle, + "entrance_shuffle_seed": EntranceShuffleSeed, + "big_key_shuffle": big_key_shuffle, + "small_key_shuffle": small_key_shuffle, "key_drop_shuffle": key_drop_shuffle, "compass_shuffle": compass_shuffle, "map_shuffle": map_shuffle, + "restrict_dungeon_item_on_boss": RestrictBossItem, + "item_pool": ItemPool, + "item_functionality": ItemFunctionality, + "enemy_health": EnemyHealth, + "enemy_damage": EnemyDamage, "progressive": Progressive, "swordless": Swordless, + "dungeon_counters": DungeonCounters, "retro_bow": RetroBow, "retro_caves": RetroCaves, "hints": Hints, "scams": Scams, - "restrict_dungeon_item_on_boss": RestrictBossItem, "boss_shuffle": LTTPBosses, "pot_shuffle": PotShuffle, "enemy_shuffle": EnemyShuffle, "killable_thieves": KillableThieves, "bush_shuffle": BushShuffle, "shop_item_slots": ShopItemSlots, + "randomize_shop_inventories": RandomizeShopInventories, + "shuffle_shop_inventories": ShuffleShopInventories, + "include_witch_hut": IncludeWitchHut, + "randomize_shop_prices": RandomizeShopPrices, + "randomize_cost_types": RandomizeCostTypes, "shop_price_modifier": ShopPriceModifier, + "shuffle_capacity_upgrades": ShuffleCapacityUpgrades, + "bombless_start": BomblessStart, + "shuffle_prizes": ShufflePrizes, "tile_shuffle": TileShuffle, + "misery_mire_medallion": MiseryMireMedallion, + "turtle_rock_medallion": TurtleRockMedallion, + "glitch_boots": GlitchBoots, + "beemizer_total_chance": BeemizerTotalChance, + "beemizer_trap_chance": BeemizerTrapChance, + "timer": Timer, + "countdown_start_time": CountdownStartTime, + "red_clock_time": RedClockTime, + "blue_clock_time": BlueClockTime, + "green_clock_time": GreenClockTime, + "death_link": DeathLink, + "allow_collect": AllowCollect, "ow_palettes": OWPalette, "uw_palettes": UWPalette, "hud_palettes": HUDPalette, @@ -469,10 +798,4 @@ class AllowCollect(Toggle): "music": Music, "reduceflashing": ReduceFlashing, "triforcehud": TriforceHud, - "glitch_boots": GlitchBoots, - "beemizer_total_chance": BeemizerTotalChance, - "beemizer_trap_chance": BeemizerTrapChance, - "death_link": DeathLink, - "allow_collect": AllowCollect, - "start_inventory_from_pool": StartInventoryPool, } diff --git a/worlds/alttp/Regions.py b/worlds/alttp/Regions.py index 0cc8a3d6a71f..dc3adb108af1 100644 --- a/worlds/alttp/Regions.py +++ b/worlds/alttp/Regions.py @@ -94,7 +94,7 @@ def create_regions(world, player): create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'), create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']), create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']), - create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'), + create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']), create_cave_region(world, player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), create_cave_region(world, player, '50 Rupee Cave', 'a cave with some cash'), @@ -121,8 +121,9 @@ def create_regions(world, player): ['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']), create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']), - create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', - 'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']), + create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']), + create_dungeon_region(world, player, 'Sewers Secret Room', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', + 'Sewers - Secret Room - Right']), create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']), create_dungeon_region(world, player, 'Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze', 'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop'], ['Agahnim 1', 'Agahnims Tower Exit']), create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None), @@ -275,7 +276,9 @@ def create_regions(world, player): create_cave_region(world, player, 'Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'], - ['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']), + ['Hookshot Cave Exit (South)', 'Hookshot Cave Bomb Wall (South)']), + create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)', + 'Hookshot Cave Bomb Wall (North)']), create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']), create_lw_region(world, player, 'Death Mountain Floating Island (Light World)', ['Floating Island']), @@ -311,8 +314,8 @@ def create_regions(world, player): create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest', 'Skull Woods - West Lobby Pot Key'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']), create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']), create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop', 'Skull Woods - Boss', 'Skull Woods - Prize']), - create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop'], ['Ice Palace (Second Section)', 'Ice Palace Exit']), - create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Main)']), + create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Second Section)', 'Ice Palace Exit']), + create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']), create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Freezor Chest', 'Ice Palace - Many Pots Pot Key', 'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']), @@ -735,6 +738,7 @@ def mark_light_world_regions(world, player: int): 'Missing Smith': (None, None, False, None), 'Dark Blacksmith Ruins': (None, None, False, None), 'Flute Activation Spot': (None, None, False, None), + 'Capacity Upgrade Shop': (None, None, False, None), 'Eastern Palace - Prize': ([0x1209D, 0x53EF8, 0x53EF9, 0x180052, 0x18007C, 0xC6FE], None, True, 'Eastern Palace'), 'Desert Palace - Prize': ([0x1209E, 0x53F1C, 0x53F1D, 0x180053, 0x180078, 0xC6FF], None, True, 'Desert Palace'), 'Tower of Hera - Prize': ( diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index b80cec578a97..ff4947bb0198 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -4,7 +4,7 @@ import worlds.Files LTTPJPN10HASH: str = "03a63945398191337e896e5771f77173" -RANDOMIZERBASEHASH: str = "9952c2a3ec1b421e408df0d20c8f0c7f" +RANDOMIZERBASEHASH: str = "35d010bc148e0ea0ee68e81e330223f1" ROM_PLAYER_LIMIT: int = 255 import io @@ -36,7 +36,7 @@ SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names from .Items import ItemFactory, item_table, item_name_groups, progression_items from .EntranceShuffle import door_addresses -from .Options import smallkey_shuffle +from .Options import small_key_shuffle try: from maseya import z3pr @@ -294,7 +294,7 @@ def patch_enemizer(world, rom: LocalRom, enemizercli, output_directory): 'RandomizeBushEnemyChance': multiworld.bush_shuffle[player].value, 'RandomizeEnemyHealthRange': multiworld.enemy_health[player] != 'default', 'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[ - multiworld.enemy_health[player]], + multiworld.enemy_health[player].current_key], 'OHKO': False, 'RandomizeEnemyDamage': multiworld.enemy_damage[player] != 'default', 'AllowEnemyZeroDamage': True, @@ -858,13 +858,13 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool): # Thanks to Zarby89 for originally finding these values # todo fix screen scrolling - if world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness_legacy'} and \ + if world.entrance_shuffle[player] != 'insanity' and \ exit.name in {'Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Palace of Darkness Exit', 'Swamp Palace Exit', 'Ganons Tower Exit', 'Desert Palace Exit (North)', 'Agahnims Tower Exit', 'Spiral Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)', 'Turtle Rock Ledge Exit (East)'} and \ - (world.logic[player] not in ['hybridglitches', 'nologic'] or + (world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic'] or exit.name not in {'Palace of Darkness Exit', 'Tower of Hera Exit', 'Swamp Palace Exit'}): # For exits that connot be reached from another, no need to apply offset fixes. rom.write_int16(0x15DB5 + 2 * offset, link_y) # same as final else @@ -907,7 +907,9 @@ def credits_digit(num): if world.retro_caves[player]: # Old man cave and Take any caves will count towards collection rate. credits_total += 5 if world.shop_item_slots[player]: # Potion shop only counts towards collection rate if included in the shuffle. - credits_total += 30 if 'w' in world.shop_shuffle[player] else 27 + credits_total += 30 if world.include_witch_hut[player] else 27 + if world.shuffle_capacity_upgrades[player]: + credits_total += 2 rom.write_byte(0x187010, credits_total) # dynamic credits @@ -1059,7 +1061,7 @@ def credits_digit(num): # Set stun items rom.write_byte(0x180180, 0x03) # All standard items # Set overflow items for progressive equipment - if world.timer[player] in ['timed', 'timed-countdown', 'timed-ohko']: + if world.timer[player] in ['timed', 'timed_countdown', 'timed_ohko']: overflow_replacement = GREEN_CLOCK else: overflow_replacement = GREEN_TWENTY_RUPEES @@ -1079,7 +1081,7 @@ def credits_digit(num): difficulty.progressive_bow_limit, item_table[difficulty.basicbow[-1]].item_code]) if difficulty.progressive_bow_limit < 2 and ( - world.swordless[player] or world.logic[player] == 'noglitches'): + world.swordless[player] or world.glitches_required[player] == 'no_glitches'): rom.write_bytes(0x180098, [2, item_table["Silver Bow"].item_code]) rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon rom.write_byte(0x180182, 0x00) # Don't auto equip silvers on pickup @@ -1095,7 +1097,7 @@ def credits_digit(num): prize_replacements[0xE1] = 0xDA # 5 Arrows -> Blue Rupee prize_replacements[0xE2] = 0xDB # 10 Arrows -> Red Rupee - if "g" in world.shuffle_prizes[player]: + if world.shuffle_prizes[player] in ("general", "both"): # shuffle prize packs prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF, @@ -1157,7 +1159,7 @@ def chunk(l, n): byte = int(rom.read_byte(address)) rom.write_byte(address, prize_replacements.get(byte, byte)) - if "b" in world.shuffle_prizes[player]: + if world.shuffle_prizes[player] in ("bonk", "both"): # set bonk prizes bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, @@ -1274,7 +1276,7 @@ def chunk(l, n): rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed gametype = 0x04 # item - if world.shuffle[player] != 'vanilla': + if world.entrance_shuffle[player] != 'vanilla': gametype |= 0x02 # entrance if enemized: gametype |= 0x01 # enemizer @@ -1312,7 +1314,7 @@ def chunk(l, n): equip[0x36C] = 0x18 equip[0x36D] = 0x18 equip[0x379] = 0x68 - starting_max_bombs = 10 + starting_max_bombs = 0 if world.bombless_start[player] else 10 starting_max_arrows = 30 startingstate = CollectionState(world) @@ -1430,8 +1432,8 @@ def chunk(l, n): 'Bottle (Fairy)': 6, 'Bottle (Bee)': 7, 'Bottle (Good Bee)': 8} rupees = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)': 50, 'Rupees (100)': 100, 'Rupees (300)': 300} - bomb_caps = {'Bomb Upgrade (+5)': 5, 'Bomb Upgrade (+10)': 10} - arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10} + bomb_caps = {'Bomb Upgrade (+5)': 5, 'Bomb Upgrade (+10)': 10, 'Bomb Upgrade (50)': 50} + arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10, 'Arrow Upgrade (70)': 70} bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10} arrows = {'Single Arrow': 1, 'Arrows (10)': 10} @@ -1498,7 +1500,7 @@ def chunk(l, n): rom.write_byte(0x3A96D, 0xF0 if world.mode[ player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) - if 'u' in world.shop_shuffle[player]: + if world.shuffle_capacity_upgrades[player]: rom.write_bytes(0x180080, [5, 10, 5, 10]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) else: @@ -1509,11 +1511,11 @@ def chunk(l, n): (0x02 if 'bombs' in world.escape_assist[player] else 0x00) | (0x04 if 'magic' in world.escape_assist[player] else 0x00))) # Escape assist - if world.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt', 'icerodhunt']: + if world.goal[player] in ['pedestal', 'triforce_hunt', 'local_triforce_hunt']: rom.write_byte(0x18003E, 0x01) # make ganon invincible - elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: + elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']: rom.write_byte(0x18003E, 0x05) # make ganon invincible until enough triforce pieces are collected - elif world.goal[player] in ['ganonpedestal']: + elif world.goal[player] in ['ganon_pedestal']: rom.write_byte(0x18003E, 0x06) elif world.goal[player] in ['bosses']: rom.write_byte(0x18003E, 0x02) # make ganon invincible until all bosses are beat @@ -1534,12 +1536,12 @@ def chunk(l, n): # c - enabled for inside compasses # s - enabled for inside small keys # block HC upstairs doors in rain state in standard mode - rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00) + rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.entrance_shuffle[player] != 'vanilla' else 0x00) - rom.write_byte(0x18016A, 0x10 | ((0x01 if world.smallkey_shuffle[player] else 0x00) + rom.write_byte(0x18016A, 0x10 | ((0x01 if world.small_key_shuffle[player] else 0x00) | (0x02 if world.compass_shuffle[player] else 0x00) | (0x04 if world.map_shuffle[player] else 0x00) - | (0x08 if world.bigkey_shuffle[ + | (0x08 if world.big_key_shuffle[ player] else 0x00))) # free roaming item text boxes rom.write_byte(0x18003B, 0x01 if world.map_shuffle[player] else 0x00) # maps showing crystals on overworld @@ -1561,9 +1563,9 @@ def chunk(l, n): # b - Big Key # a - Small Key # - rom.write_byte(0x180045, ((0x00 if (world.smallkey_shuffle[player] == smallkey_shuffle.option_original_dungeon or - world.smallkey_shuffle[player] == smallkey_shuffle.option_universal) else 0x01) - | (0x02 if world.bigkey_shuffle[player] else 0x00) + rom.write_byte(0x180045, ((0x00 if (world.small_key_shuffle[player] == small_key_shuffle.option_original_dungeon or + world.small_key_shuffle[player] == small_key_shuffle.option_universal) else 0x01) + | (0x02 if world.big_key_shuffle[player] else 0x00) | (0x04 if world.map_shuffle[player] else 0x00) | (0x08 if world.compass_shuffle[player] else 0x00))) # free roaming items in menu @@ -1595,8 +1597,8 @@ def get_reveal_bytes(itemName): rom.write_int16(0x18017C, get_reveal_bytes('Crystal 5') | get_reveal_bytes('Crystal 6') if world.map_shuffle[ player] else 0x0000) # Bomb Shop Reveal - rom.write_byte(0x180172, 0x01 if world.smallkey_shuffle[ - player] == smallkey_shuffle.option_universal else 0x00) # universal keys + rom.write_byte(0x180172, 0x01 if world.small_key_shuffle[ + player] == small_key_shuffle.option_universal else 0x00) # universal keys rom.write_byte(0x18637E, 0x01 if world.retro_bow[player] else 0x00) # Skip quiver in item shops once bought rom.write_byte(0x180175, 0x01 if world.retro_bow[player] else 0x00) # rupee bow rom.write_byte(0x180176, 0x0A if world.retro_bow[player] else 0x00) # wood arrow cost @@ -1613,9 +1615,9 @@ def get_reveal_bytes(itemName): rom.write_byte(0x180020, digging_game_rng) rom.write_byte(0xEFD95, digging_game_rng) rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills - rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix - rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.logic[ - player] == 'nologic' else 0x00) # disable glitching to Triforce from Ganons Room + rom.write_byte(0x1800A4, 0x01 if world.glitches_required[player] != 'no_logic' else 0x00) # enable POD EG fix + rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.glitches_required[ + player] == 'no_logic' else 0x00) # disable glitching to Triforce from Ganons Room rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill # remove shield from uncle @@ -1660,8 +1662,8 @@ def get_reveal_bytes(itemName): 0x4F]) # allow smith into multi-entrance caves in appropriate shuffles - if world.shuffle[player] in ['restricted', 'full', 'crossed', 'insanity', 'madness'] or ( - world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): + if world.entrance_shuffle[player] in ['restricted', 'full', 'crossed', 'insanity'] or ( + world.entrance_shuffle[player] == 'simple' and world.mode[player] == 'inverted'): rom.write_byte(0x18004C, 0x01) # set correct flag for hera basement item @@ -1758,8 +1760,8 @@ def write_custom_shops(rom, world, player): if item is None: break if world.shop_item_slots[player] or shop.type == ShopType.TakeAny: - count_shop = (shop.region.name != 'Potion Shop' or 'w' in world.shop_shuffle[player]) and \ - shop.region.name != 'Capacity Upgrade' + count_shop = (shop.region.name != 'Potion Shop' or world.include_witch_hut[player]) and \ + (shop.region.name != 'Capacity Upgrade' or world.shuffle_capacity_upgrades[player]) rom.write_byte(0x186560 + shop.sram_offset + slot, 1 if count_shop else 0) if item['item'] == 'Single Arrow' and item['player'] == 0: arrow_mask |= 1 << index @@ -2201,7 +2203,7 @@ def write_strings(rom, world, player): tt.removeUnwantedText() # Let's keep this guy's text accurate to the shuffle setting. - if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']: tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.' tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.' @@ -2255,7 +2257,7 @@ def hint_text(dest, ped_hint=False): entrances_to_hint.update({'Inverted Ganons Tower': 'The sealed castle door'}) else: entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) - if world.shuffle[player] in ['simple', 'restricted', 'restricted_legacy']: + if world.entrance_shuffle[player] in ['simple', 'restricted']: for entrance in all_entrances: if entrance.name in entrances_to_hint: this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text( @@ -2265,9 +2267,9 @@ def hint_text(dest, ped_hint=False): break # Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones. entrances_to_hint.update(InconvenientOtherEntrances) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: hint_count = 0 - elif world.shuffle[player] in ['simple', 'restricted', 'restricted_legacy']: + elif world.entrance_shuffle[player] in ['simple', 'restricted']: hint_count = 2 else: hint_count = 4 @@ -2284,14 +2286,14 @@ def hint_text(dest, ped_hint=False): # Next we handle hints for randomly selected other entrances, # curating the selection intelligently based on shuffle. - if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: + if world.entrance_shuffle[player] not in ['simple', 'restricted']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) if world.mode[player] == 'inverted': entrances_to_hint.update({'Inverted Agahnims Tower': 'The dark mountain tower'}) else: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) - elif world.shuffle[player] == 'restricted': + elif world.entrance_shuffle[player] == 'restricted': entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(OtherEntrances) if world.mode[player] == 'inverted': @@ -2301,15 +2303,15 @@ def hint_text(dest, ped_hint=False): else: entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'}) entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) - if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']: + if world.entrance_shuffle[player] != 'insanity': entrances_to_hint.update(InsanityEntrances) if world.shuffle_ganon: if world.mode[player] == 'inverted': entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'}) else: entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'}) - hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', - 'dungeonscrossed'] else 0 + hint_count = 4 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full', + 'dungeons_crossed'] else 0 for entrance in all_entrances: if entrance.name in entrances_to_hint: if hint_count: @@ -2323,11 +2325,11 @@ def hint_text(dest, ped_hint=False): # Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable. locations_to_hint = InconvenientLocations.copy() - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: locations_to_hint.extend(InconvenientVanillaLocations) local_random.shuffle(locations_to_hint) - hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', - 'dungeonscrossed'] else 5 + hint_count = 3 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full', + 'dungeons_crossed'] else 5 for location in locations_to_hint[:hint_count]: if location == 'Swamp Left': if local_random.randint(0, 1): @@ -2381,16 +2383,16 @@ def hint_text(dest, ped_hint=False): # Lastly we write hints to show where certain interesting items are. items_to_hint = RelevantItems.copy() - if world.smallkey_shuffle[player].hints_useful: + if world.small_key_shuffle[player].hints_useful: items_to_hint |= item_name_groups["Small Keys"] - if world.bigkey_shuffle[player].hints_useful: + if world.big_key_shuffle[player].hints_useful: items_to_hint |= item_name_groups["Big Keys"] if world.hints[player] == "full": hint_count = len(hint_locations) # fill all remaining hint locations with Item hints. else: - hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', - 'dungeonscrossed'] else 8 + hint_count = 5 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full', + 'dungeons_crossed'] else 8 hint_count = min(hint_count, len(items_to_hint), len(hint_locations)) if hint_count: locations = world.find_items_in_locations(items_to_hint, player, True) @@ -2417,7 +2419,7 @@ def hint_text(dest, ped_hint=False): tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint if world.worlds[player].has_progressive_bows and (world.difficulty_requirements[player].progressive_bow_limit >= 2 or ( - world.swordless[player] or world.logic[player] == 'noglitches')): + world.swordless[player] or world.glitches_required[player] == 'no_glitches')): prog_bow_locs = world.find_item_locations('Progressive Bow', player, True) world.per_slot_randoms[player].shuffle(prog_bow_locs) found_bow = False @@ -2448,7 +2450,7 @@ def hint_text(dest, ped_hint=False): if world.goal[player] == 'bosses': tt['sign_ganon'] = 'You need to kill all bosses, Ganon last.' - elif world.goal[player] == 'ganonpedestal': + elif world.goal[player] == 'ganon_pedestal': tt['sign_ganon'] = 'You need to pull the pedestal to defeat Ganon.' elif world.goal[player] == "ganon": if world.crystals_needed_for_ganon[player] == 1: @@ -2456,14 +2458,6 @@ def hint_text(dest, ped_hint=False): else: tt['sign_ganon'] = f'You need {world.crystals_needed_for_ganon[player]} crystals to beat Ganon and ' \ f'have beaten Agahnim atop Ganons Tower' - elif world.goal[player] == "icerodhunt": - tt['sign_ganon'] = 'Go find the Ice Rod and Kill Trinexx, then talk to Murahdahla... Ganon is invincible!' - tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Go kill Trinexx instead.' - tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' - tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\n" \ - "invisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\n" \ - "hidden in a hollow tree. " \ - "If you bring me the Triforce piece from Turtle Rock, I can reassemble it." else: if world.crystals_needed_for_ganon[player] == 1: tt['sign_ganon'] = 'You need a crystal to beat Ganon.' @@ -2478,10 +2472,10 @@ def hint_text(dest, ped_hint=False): tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[local_random.randint(0, len(Sahasrahla2_texts) - 1)] tt['blind_by_the_light'] = Blind_texts[local_random.randint(0, len(Blind_texts) - 1)] - if world.goal[player] in ['triforcehunt', 'localtriforcehunt', 'icerodhunt']: + if world.goal[player] in ['triforce_hunt', 'local_triforce_hunt']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' - if world.goal[player] == 'triforcehunt' and world.players > 1: + if world.goal[player] == 'triforce_hunt' and world.players > 1: tt['sign_ganon'] = 'Go find the Triforce pieces with your friends... Ganon is invincible!' else: tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' @@ -2504,17 +2498,17 @@ def hint_text(dest, ped_hint=False): tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!' tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!' if world.treasure_hunt_count[player] > 1: - if world.goal[player] == 'ganontriforcehunt' and world.players > 1: + if world.goal[player] == 'ganon_triforce_hunt' and world.players > 1: tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d with your friends to defeat Ganon.' % \ (world.treasure_hunt_count[player], world.triforce_pieces_available[player]) - elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: + elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']: tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d to defeat Ganon.' % \ (world.treasure_hunt_count[player], world.triforce_pieces_available[player]) else: - if world.goal[player] == 'ganontriforcehunt' and world.players > 1: + if world.goal[player] == 'ganon_triforce_hunt' and world.players > 1: tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d with your friends to defeat Ganon.' % \ (world.treasure_hunt_count[player], world.triforce_pieces_available[player]) - elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: + elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']: tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d to defeat Ganon.' % \ (world.treasure_hunt_count[player], world.triforce_pieces_available[player]) @@ -2614,12 +2608,12 @@ def set_inverted_mode(world, player, rom): rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof # the following bytes should only be written in vanilla # or they'll overwrite the randomizer's shuffles - if world.shuffle[player] == 'vanilla': + if world.entrance_shuffle[player] == 'vanilla': rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT rom.write_byte(0xDBB73 + 0x36, 0x24) rom.write_int16(0x15AEE + 2 * 0x38, 0x00E0) rom.write_int16(0x15AEE + 2 * 0x25, 0x000C) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: rom.write_byte(0x15B8C, 0x6C) rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house rom.write_byte(0xDBB73 + 0x52, 0x01) @@ -2677,7 +2671,7 @@ def set_inverted_mode(world, player, rom): rom.write_int16(snes_to_pc(0x02D9A6), 0x005A) rom.write_byte(snes_to_pc(0x02D9B3), 0x12) # keep the old man spawn point at old man house unless shuffle is vanilla - if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']: rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) rom.write_int16(snes_to_pc(0x02D8DE), 0x00F1) rom.write_bytes(snes_to_pc(0x02D910), [0x1F, 0x1E, 0x1F, 0x1F, 0x03, 0x02, 0x03, 0x03]) @@ -2740,7 +2734,7 @@ def set_inverted_mode(world, player, rom): rom.write_int16s(snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B]) rom.write_int16(snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance rom.write_int16(snes_to_pc(0x308320), 0x001B) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: rom.write_byte(snes_to_pc(0x308340), 0x7B) rom.write_int16(snes_to_pc(0x1af504), 0x148B) rom.write_int16(snes_to_pc(0x1af50c), 0x149B) @@ -2777,10 +2771,10 @@ def set_inverted_mode(world, player, rom): rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) rom.write_int16(0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door rom.write_int16(0xDBA71 + 2 * 0x35, 0x06A4) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: rom.write_byte(0xDBB73 + 0x35, 0x36) rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: rom.write_int16(0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area rom.write_byte(0x15B8C + 0x37, 0x1B) rom.write_int16(0x15BDB + 2 * 0x37, 0x0418) diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py index 98ab805b5c08..17061842dde9 100644 --- a/worlds/alttp/Rules.py +++ b/worlds/alttp/Rules.py @@ -9,25 +9,25 @@ from . import OverworldGlitchRules from .Bosses import GanonDefeatRule from .Items import ItemFactory, item_name_groups, item_table, progression_items -from .Options import smallkey_shuffle +from .Options import small_key_shuffle from .OverworldGlitchRules import no_logic_rules, overworld_glitches_rules from .Regions import LTTPRegionType, location_table from .StateHelpers import (can_extend_magic, can_kill_most_things, can_lift_heavy_rocks, can_lift_rocks, can_melt_things, can_retrieve_tablet, can_shoot_arrows, has_beam_sword, has_crystals, - has_fire_source, has_hearts, + has_fire_source, has_hearts, has_melee_weapon, has_misery_mire_medallion, has_sword, has_turtle_rock_medallion, - has_triforce_pieces) + has_triforce_pieces, can_use_bombs, can_bomb_or_bonk) from .UnderworldGlitchRules import underworld_glitches_rules def set_rules(world): player = world.player world = world.multiworld - if world.logic[player] == 'nologic': + if world.glitches_required[player] == 'no_logic': if player == next(player_id for player_id in world.get_game_players("A Link to the Past") - if world.logic[player_id] == 'nologic'): # only warn one time + if world.glitches_required[player_id] == 'no_logic'): # only warn one time logging.info( 'WARNING! Seeds generated under this logic often require major glitches and may be impossible!') @@ -45,8 +45,8 @@ def set_rules(world): else: world.completion_condition[player] = lambda state: state.has('Triforce', player) - global_rules(world, player) dungeon_boss_rules(world, player) + global_rules(world, player) if world.mode[player] != 'inverted': default_rules(world, player) @@ -61,24 +61,24 @@ def set_rules(world): else: raise NotImplementedError(f'World state {world.mode[player]} is not implemented yet') - if world.logic[player] == 'noglitches': + if world.glitches_required[player] == 'no_glitches': no_glitches_rules(world, player) - elif world.logic[player] == 'owglitches': + elif world.glitches_required[player] == 'overworld_glitches': # Initially setting no_glitches_rules to set the baseline rules for some # entrances. The overworld_glitches_rules set is primarily additive. no_glitches_rules(world, player) fake_flipper_rules(world, player) overworld_glitches_rules(world, player) - elif world.logic[player] in ['hybridglitches', 'nologic']: + elif world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']: no_glitches_rules(world, player) fake_flipper_rules(world, player) overworld_glitches_rules(world, player) underworld_glitches_rules(world, player) - elif world.logic[player] == 'minorglitches': + elif world.glitches_required[player] == 'minor_glitches': no_glitches_rules(world, player) fake_flipper_rules(world, player) else: - raise NotImplementedError(f'Not implemented yet: Logic - {world.logic[player]}') + raise NotImplementedError(f'Not implemented yet: Logic - {world.glitches_required[player]}') if world.goal[player] == 'bosses': # require all bosses to beat ganon @@ -89,7 +89,7 @@ def set_rules(world): if world.mode[player] != 'inverted': set_big_bomb_rules(world, player) - if world.logic[player] in {'owglitches', 'hybridglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}: + if world.glitches_required[player] in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and world.entrance_shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}: path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.multiworld.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or') else: @@ -97,18 +97,18 @@ def set_rules(world): # if swamp and dam have not been moved we require mirror for swamp palace # however there is mirrorless swamp in hybrid MG, so we don't necessarily want this. HMG handles this requirement itself. - if not world.swamp_patch_required[player] and world.logic[player] not in ['hybridglitches', 'nologic']: + if not world.swamp_patch_required[player] and world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic']: add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player)) # GT Entrance may be required for Turtle Rock for OWG and < 7 required ganons_tower = world.get_entrance('Inverted Ganons Tower' if world.mode[player] == 'inverted' else 'Ganons Tower', player) - if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and world.mode[player] != 'inverted'): + if world.crystals_needed_for_gt[player] == 7 and not (world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and world.mode[player] != 'inverted'): set_rule(ganons_tower, lambda state: False) set_trock_key_rules(world, player) set_rule(ganons_tower, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_gt[player], player)) - if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: + if world.mode[player] != 'inverted' and world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']: add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.multiworld.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') set_bunny_rules(world, player, world.mode[player] == 'inverted') @@ -139,6 +139,7 @@ def set_defeat_dungeon_boss_rule(location): add_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state)) + def set_always_allow(spot, rule): spot.always_allow = rule @@ -184,6 +185,7 @@ def dungeon_boss_rules(world, player): for location in boss_locations: set_defeat_dungeon_boss_rule(world.get_location(location, player)) + def global_rules(world, player): # ganon can only carry triforce add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) @@ -213,14 +215,61 @@ def global_rules(world, player): set_rule(world.get_location('Ether Tablet', player), lambda state: can_retrieve_tablet(state, player)) set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) - set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) set_rule(world.get_location('Sick Kid', player), lambda state: state.has_group("Bottles", player)) set_rule(world.get_location('Library', player), lambda state: state.has('Pegasus Boots', player)) - set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)) + + if world.enemy_shuffle[player]: + set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player) and + can_kill_most_things(state, player, 4)) + else: + set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player) + and ((state.multiworld.enemy_health[player] in ("easy", "default") and can_use_bombs(state, player, 4)) + or can_shoot_arrows(state, player) or state.has("Cane of Somaria", player) + or has_beam_sword(state, player))) + set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player)) + set_rule(world.get_location('Aginah\'s Cave', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Blind\'s Hideout - Top', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Chicken House', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Kakariko Well - Top', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Graveyard Cave', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Sahasrahla\'s Hut - Left', player), lambda state: can_bomb_or_bonk(state, player)) + set_rule(world.get_location('Sahasrahla\'s Hut - Middle', player), lambda state: can_bomb_or_bonk(state, player)) + set_rule(world.get_location('Sahasrahla\'s Hut - Right', player), lambda state: can_bomb_or_bonk(state, player)) + set_rule(world.get_location('Paradox Cave Lower - Left', player), lambda state: can_use_bombs(state, player) + or has_beam_sword(state, player) or can_shoot_arrows(state, player) + or state.has_any(["Fire Rod", "Cane of Somaria"], player)) + set_rule(world.get_location('Paradox Cave Lower - Right', player), lambda state: can_use_bombs(state, player) + or has_beam_sword(state, player) or can_shoot_arrows(state, player) + or state.has_any(["Fire Rod", "Cane of Somaria"], player)) + set_rule(world.get_location('Paradox Cave Lower - Far Right', player), lambda state: can_use_bombs(state, player) + or has_beam_sword(state, player) or can_shoot_arrows(state, player) + or state.has_any(["Fire Rod", "Cane of Somaria"], player)) + set_rule(world.get_location('Paradox Cave Lower - Middle', player), lambda state: can_use_bombs(state, player) + or has_beam_sword(state, player) or can_shoot_arrows(state, player) + or state.has_any(["Fire Rod", "Cane of Somaria"], player)) + set_rule(world.get_location('Paradox Cave Lower - Far Left', player), lambda state: can_use_bombs(state, player) + or has_beam_sword(state, player) or can_shoot_arrows(state, player) + or state.has_any(["Fire Rod", "Cane of Somaria"], player)) + set_rule(world.get_location('Paradox Cave Upper - Left', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Paradox Cave Upper - Right', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Mini Moldorm Cave - Far Left', player), lambda state: can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Mini Moldorm Cave - Left', player), lambda state: can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Mini Moldorm Cave - Far Right', player), lambda state: can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Mini Moldorm Cave - Right', player), lambda state: can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Mini Moldorm Cave - Generous Guy', player), lambda state: can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Hype Cave - Bottom', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Hype Cave - Middle Left', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Hype Cave - Middle Right', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_location('Hype Cave - Top', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Light World Death Mountain Shop', player), lambda state: can_use_bombs(state, player)) + + set_rule(world.get_entrance('Two Brothers House Exit (West)', player), lambda state: can_bomb_or_bonk(state, player)) + set_rule(world.get_entrance('Two Brothers House Exit (East)', player), lambda state: can_bomb_or_bonk(state, player)) set_rule(world.get_location('Spike Cave', player), lambda state: state.has('Hammer', player) and can_lift_rocks(state, player) and @@ -238,61 +287,81 @@ def global_rules(world, player): set_rule(world.get_entrance('Sewers Door', player), lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) or ( - world.smallkey_shuffle[player] == smallkey_shuffle.option_universal and world.mode[ + world.small_key_shuffle[player] == small_key_shuffle.option_universal and world.mode[ player] == 'standard')) # standard universal small keys cannot access the shop set_rule(world.get_entrance('Sewers Back Door', player), lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4)) + set_rule(world.get_entrance('Sewers Secret Room', player), lambda state: can_bomb_or_bonk(state, player)) + set_rule(world.get_entrance('Agahnim 1', player), lambda state: has_sword(state, player) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 4)) - set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: can_kill_most_things(state, player, 8)) + set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: can_kill_most_things(state, player, 4)) set_rule(world.get_location('Castle Tower - Dark Maze', player), - lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)', + lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)', player)) set_rule(world.get_location('Castle Tower - Dark Archer Key Drop', player), - lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)', + lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 2)) set_rule(world.get_location('Castle Tower - Circle of Pots Key Drop', player), - lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)', + lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 3)) set_always_allow(world.get_location('Eastern Palace - Big Key Chest', player), lambda state, item: item.name == 'Big Key (Eastern Palace)' and item.player == player) set_rule(world.get_location('Eastern Palace - Big Key Chest', player), - lambda state: state._lttp_has_key('Small Key (Eastern Palace)', player, 2) or - ((location_item_name(state, 'Eastern Palace - Big Key Chest', player) == ('Big Key (Eastern Palace)', player) - and state.has('Small Key (Eastern Palace)', player)))) + lambda state: can_kill_most_things(state, player, 5) and (state._lttp_has_key('Small Key (Eastern Palace)', + player, 2) or ((location_item_name(state, 'Eastern Palace - Big Key Chest', player) + == ('Big Key (Eastern Palace)', player) and state.has('Small Key (Eastern Palace)', + player))))) set_rule(world.get_location('Eastern Palace - Dark Eyegore Key Drop', player), - lambda state: state.has('Big Key (Eastern Palace)', player)) + lambda state: state.has('Big Key (Eastern Palace)', player) and can_kill_most_things(state, player, 1)) set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player)) + # not bothering to check for can_kill_most_things in the rooms leading to boss, as if you can kill a boss you should + # be able to get through these rooms ep_boss = world.get_location('Eastern Palace - Boss', player) - set_rule(ep_boss, lambda state: state.has('Big Key (Eastern Palace)', player) and + add_rule(ep_boss, lambda state: state.has('Big Key (Eastern Palace)', player) and state._lttp_has_key('Small Key (Eastern Palace)', player, 2) and ep_boss.parent_region.dungeon.boss.can_defeat(state)) ep_prize = world.get_location('Eastern Palace - Prize', player) - set_rule(ep_prize, lambda state: state.has('Big Key (Eastern Palace)', player) and + add_rule(ep_prize, lambda state: state.has('Big Key (Eastern Palace)', player) and state._lttp_has_key('Small Key (Eastern Palace)', player, 2) and ep_prize.parent_region.dungeon.boss.can_defeat(state)) if not world.enemy_shuffle[player]: add_rule(ep_boss, lambda state: can_shoot_arrows(state, player)) add_rule(ep_prize, lambda state: can_shoot_arrows(state, player)) + # You can always kill the Stalfos' with the pots on easy/normal + if world.enemy_health[player] in ("hard", "expert") or world.enemy_shuffle[player]: + stalfos_rule = lambda state: can_kill_most_things(state, player, 4) + for location in ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', + 'Eastern Palace - Dark Square Pot Key', 'Eastern Palace - Dark Eyegore Key Drop', + 'Eastern Palace - Big Key Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize']: + add_rule(world.get_location(location, player), stalfos_rule) + set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player)) set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4)) - set_rule(world.get_location('Desert Palace - Big Key Chest', player), lambda state: can_kill_most_things(state, player)) - set_rule(world.get_location('Desert Palace - Beamos Hall Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 2) and can_kill_most_things(state, player)) - set_rule(world.get_location('Desert Palace - Desert Tiles 2 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 3) and can_kill_most_things(state, player)) - set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) - set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Big Key Chest', player), lambda state: can_kill_most_things(state, player, 3)) + set_rule(world.get_location('Desert Palace - Beamos Hall Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 2) and can_kill_most_things(state, player, 4)) + set_rule(world.get_location('Desert Palace - Desert Tiles 2 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 3) and can_kill_most_things(state, player, 4)) + add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) + add_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) # logic patch to prevent placing a crystal in Desert that's required to reach the required keys - if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]): + if not (world.small_key_shuffle[player] and world.big_key_shuffle[player]): add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.multiworld.get_region('Desert Palace Main (Outer)', player).can_reach(state)) set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or location_item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) + if world.enemy_shuffle[player]: + add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: can_kill_most_things(state, player, 3)) + else: + add_rule(world.get_entrance('Tower of Hera Big Key Door', player), + lambda state: (has_melee_weapon(state, player) or (state.has('Silver Bow', player) + and can_shoot_arrows(state, player)) or state.has("Cane of Byrna", player) + or state.has("Cane of Somaria", player))) set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: has_fire_source(state, player)) if world.accessibility[player] != 'locations': @@ -300,9 +369,13 @@ def global_rules(world, player): set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player)) set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player)) + set_rule(world.get_location('Swamp Palace - Map Chest', player), lambda state: can_use_bombs(state, player)) set_rule(world.get_location('Swamp Palace - Trench 1 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 2)) set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player) and state._lttp_has_key('Small Key (Swamp Palace)', player, 3)) set_rule(world.get_location('Swamp Palace - Hookshot Pot Key', player), lambda state: state.has('Hookshot', player)) + if world.pot_shuffle[player]: + # it could move the key to the top right platform which can only be reached with bombs + add_rule(world.get_location('Swamp Palace - Hookshot Pot Key', player), lambda state: can_use_bombs(state, player)) set_rule(world.get_entrance('Swamp Palace (West)', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6) if state.has('Hookshot', player) else state._lttp_has_key('Small Key (Swamp Palace)', player, 4)) @@ -310,15 +383,18 @@ def global_rules(world, player): if world.accessibility[player] != 'locations': allow_self_locking_items(world.get_location('Swamp Palace - Big Chest', player), 'Big Key (Swamp Palace)') set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player) and state._lttp_has_key('Small Key (Swamp Palace)', player, 5)) - if not world.smallkey_shuffle[player] and world.logic[player] not in ['hybridglitches', 'nologic']: + if not world.small_key_shuffle[player] and world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic']: forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player) set_rule(world.get_location('Swamp Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6)) set_rule(world.get_location('Swamp Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6)) + if world.pot_shuffle[player]: + # key can (and probably will) be moved behind bombable wall + set_rule(world.get_location('Swamp Palace - Waterway Pot Key', player), lambda state: can_use_bombs(state, player)) set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player)) if world.worlds[player].dungeons["Thieves Town"].boss.enemizer_name == "Blind": - set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3)) + set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3) and can_use_bombs(state, player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player, 3)) and state.has('Hammer', player)) @@ -334,7 +410,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5)) set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5)) set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5)) - set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player)) + set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) and can_use_bombs(state, player)) if world.accessibility[player] != 'locations': allow_self_locking_items(world.get_location('Skull Woods - Big Chest', player), 'Big Key (Skull Woods)') set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 4) and state.has('Fire Rod', player) and has_sword(state, player)) # sword required for curtain @@ -342,7 +418,13 @@ def global_rules(world, player): add_rule(world.get_location('Skull Woods - Boss', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5)) set_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: can_melt_things(state, player)) - set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player)) + set_rule(world.get_location('Ice Palace - Compass Chest', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player)) + set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player) and can_use_bombs(state, player)) + if not world.enemy_shuffle[player]: + # Stalfos Knights can be killed by damaging them repeatedly with boomerang, swords, etc. if bombs are + # unavailable. If bombs are available, the pots can be thrown at them, so no other weapons are needed + add_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: (can_use_bombs(state, player) + or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or has_sword(state, player) or state.has("Hammer", player))) set_rule(world.get_entrance('Ice Palace (Main)', player), lambda state: state._lttp_has_key('Small Key (Ice Palace)', player, 2)) set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: can_lift_rocks(state, player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 6) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 5)))) @@ -387,16 +469,21 @@ def global_rules(world, player): else state._lttp_has_key('Small Key (Misery Mire)', player, 6)) set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: has_fire_source(state, player)) set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: has_fire_source(state, player)) - set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player) and can_use_bombs(state, player)) set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_location('Turtle Rock - Pokey 1 Key Drop', player), lambda state: can_kill_most_things(state, player, 5)) + set_rule(world.get_location('Turtle Rock - Pokey 2 Key Drop', player), lambda state: can_kill_most_things(state, player, 5)) set_rule(world.get_location('Turtle Rock - Compass Chest', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_location('Turtle Rock - Roller Room - Left', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) set_rule(world.get_location('Turtle Rock - Roller Room - Right', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player) and (state.has('Cane of Somaria', player) or state.has('Hookshot', player))) set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player)) - set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player)) + set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player) and can_kill_most_things(state, player, 10)) + set_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: can_use_bombs(state, player) and can_kill_most_things(state, player, 10)) + set_rule(world.get_location('Turtle Rock - Chain Chomps', player), lambda state: can_use_bombs(state, player) or can_shoot_arrows(state, player) + or has_beam_sword(state, player) or state.has_any(["Blue Boomerang", "Red Boomerang", "Hookshot", "Cane of Somaria", "Fire Rod", "Ice Rod"], player)) set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) @@ -405,16 +492,22 @@ def global_rules(world, player): set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 6) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player)) - if not world.enemy_shuffle[player]: - set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_shoot_arrows(state, player)) + if world.enemy_shuffle[player]: + set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_bomb_or_bonk(state, player) and can_kill_most_things(state, player, 3)) + else: + set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_bomb_or_bonk(state, player) and can_shoot_arrows(state, player)) set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Palace of Darkness Bridge Room', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 1)) # If we can reach any other small key door, we already have back door access to this area set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and can_shoot_arrows(state, player) and state.has('Hammer', player)) set_rule(world.get_entrance('Palace of Darkness (North)', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 4)) - set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) + set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: can_use_bombs(state, player) and state.has('Big Key (Palace of Darkness)', player)) + set_rule(world.get_location('Palace of Darkness - The Arena - Ledge', player), lambda state: can_use_bombs(state, player)) + if world.pot_shuffle[player]: + # chest switch may be up on ledge where bombs are required + set_rule(world.get_location('Palace of Darkness - Stalfos Basement', player), lambda state: can_use_bombs(state, player)) - set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or ( - location_item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3))) + set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: can_use_bombs(state, player) and (state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or ( + location_item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3)))) if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5)) @@ -430,13 +523,9 @@ def global_rules(world, player): compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right', 'Ganons Tower - Conveyor Star Pits Pot Key'] back_chests = ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left', 'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest'] - set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player))) - if world.pot_shuffle[player]: - # Pot Shuffle can move this check into the hookshot room - set_rule(world.get_location('Ganons Tower - Conveyor Cross Pot Key', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player))) set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 8) or ( location_item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player)] and state._lttp_has_key('Small Key (Ganons Tower)', player, 6))) @@ -465,17 +554,17 @@ def global_rules(world, player): item_name_in_location_names(state, 'Big Key (Ganons Tower)', player, zip(back_chests, [player] * len(back_chests))) and state._lttp_has_key('Small Key (Ganons Tower)', player, 5))) # Actual requirements for location in compass_room_chests: - set_rule(world.get_location(location, player), lambda state: state.has('Fire Rod', player) and (state._lttp_has_key('Small Key (Ganons Tower)', player, 7) or ( + set_rule(world.get_location(location, player), lambda state: (can_use_bombs(state, player) or state.has("Cane of Somaria", player)) and state.has('Fire Rod', player) and (state._lttp_has_key('Small Key (Ganons Tower)', player, 7) or ( item_name_in_location_names(state, 'Big Key (Ganons Tower)', player, zip(compass_room_chests, [player] * len(compass_room_chests))) and state._lttp_has_key('Small Key (Ganons Tower)', player, 5)))) set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player)) set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player), - lambda state: state.multiworld.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + lambda state: can_use_bombs(state, player) and state.multiworld.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) set_rule(world.get_location('Ganons Tower - Big Key Chest', player), - lambda state: state.multiworld.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + lambda state: can_use_bombs(state, player) and state.multiworld.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player), - lambda state: state.multiworld.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + lambda state: can_use_bombs(state, player) and state.multiworld.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) if world.enemy_shuffle[player]: set_rule(world.get_entrance('Ganons Tower Big Key Door', player), lambda state: state.has('Big Key (Ganons Tower)', player)) @@ -483,7 +572,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower Big Key Door', player), lambda state: state.has('Big Key (Ganons Tower)', player) and can_shoot_arrows(state, player)) set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), - lambda state: has_fire_source(state, player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) + lambda state: can_kill_most_things(state, player, 8) and has_fire_source(state, player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Mini Helmasaur Key Drop', player), lambda state: can_kill_most_things(state, player, 1)) set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 7)) set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), @@ -493,9 +583,9 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player)) ganon = world.get_location('Ganon', player) set_rule(ganon, lambda state: GanonDefeatRule(state, player)) - if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: + if world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']: add_rule(ganon, lambda state: has_triforce_pieces(state, player)) - elif world.goal[player] == 'ganonpedestal': + elif world.goal[player] == 'ganon_pedestal': add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player)) else: add_rule(ganon, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_ganon[player], player)) @@ -507,6 +597,12 @@ def global_rules(world, player): def default_rules(world, player): """Default world rules when world state is not inverted.""" # overworld requirements + + set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) @@ -562,12 +658,12 @@ def default_rules(world, player): set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has('Moon Pearl', player) and state.has('Flippers', player) or state.has('Magic Mirror', player))) # Overworld Bunny Revival set_rule(world.get_location('Bombos Tablet', player), lambda state: can_retrieve_tablet(state, player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) # ToDo any fake flipper set up? - set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has('Moon Pearl', player)) # bomb required + set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has('Moon Pearl', player) and can_lift_heavy_rocks(state, player)) - set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has('Moon Pearl', player)) # bomb required - set_rule(world.get_entrance('Brewery', player), lambda state: state.has('Moon Pearl', player)) # bomb required + set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) + set_rule(world.get_entrance('Brewery', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) set_rule(world.get_entrance('Thieves Town', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot pull set_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot lift bush set_rule(world.get_entrance('Skull Woods Second Section Hole', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot lift bush @@ -621,9 +717,9 @@ def inverted_rules(world, player): # overworld requirements set_rule(world.get_location('Maze Race', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) + set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) + set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) set_rule(world.get_entrance('Potion Shop Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Light World Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player) and state.has('Moon Pearl', player)) @@ -669,7 +765,7 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Bush Covered Lawn Outer Bushes', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Bomb Hut Inner Bushes', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Bomb Hut Outer Bushes', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: state.has('Moon Pearl', player)) # need bomb + set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player)) set_rule(world.get_entrance('North Fairy Cave Drop', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Lost Woods Hideout Drop', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy @@ -715,6 +811,11 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) + set_rule(world.get_entrance('Hype Cave', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Brewery', player), lambda state: can_use_bombs(state, player)) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: can_use_bombs(state, player)) + + set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Misery Mire', player), lambda state: has_sword(state, player) and has_misery_mire_medallion(state, player)) # sword required to cast magic (!) @@ -900,20 +1001,25 @@ def add_conditional_lamp(spot, region, spottype='Location', accessible_torch=Fal def open_rules(world, player): + + set_rule(world.get_location('Hyrule Castle - Map Guard Key Drop', player), + lambda state: can_kill_most_things(state, player, 1)) + def basement_key_rule(state): if location_item_name(state, 'Sewers - Key Rat Key Drop', player) == ("Small Key (Hyrule Castle)", player): return state._lttp_has_key("Small Key (Hyrule Castle)", player, 2) else: return state._lttp_has_key("Small Key (Hyrule Castle)", player, 3) - set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), basement_key_rule) + set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), + lambda state: basement_key_rule(state) and can_kill_most_things(state, player, 2)) set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), basement_key_rule) set_rule(world.get_location('Sewers - Key Rat Key Drop', player), - lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 3)) + lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 3) and can_kill_most_things(state, player, 1)) set_rule(world.get_location('Hyrule Castle - Big Key Drop', player), - lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4)) + lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) and can_kill_most_things(state, player, 1)) set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) and state.has('Big Key (Hyrule Castle)', player)) @@ -924,6 +1030,7 @@ def swordless_rules(world, player): set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain set_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) + set_rule(world.get_location('Ice Palace - Compass Chest', player), lambda state: (state.has('Fire Rod', player) or state.has('Bombos', player)) and state._lttp_has_key('Small Key (Ice Palace)', player)) set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: (state.has('Fire Rod', player) or state.has('Bombos', player)) and state._lttp_has_key('Small Key (Ice Palace)', player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop @@ -954,7 +1061,7 @@ def standard_rules(world, player): set_rule(world.get_entrance('Links House S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) - if world.smallkey_shuffle[player] != smallkey_shuffle.option_universal: + if world.small_key_shuffle[player] != small_key_shuffle.option_universal: set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 1)) set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), @@ -1062,15 +1169,15 @@ def tr_big_key_chest_keys_needed(state): return 6 # If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential - if not can_reach_front and not world.smallkey_shuffle[player]: + if not can_reach_front and not world.small_key_shuffle[player]: # Must not go in the Big Key Chest - only 1 other chest available and 2+ keys required for all other chests forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player) if not can_reach_big_chest: # Must not go in the Chain Chomps chest - only 2 other chests available and 3+ keys required for all other chests forbid_item(world.get_location('Turtle Rock - Chain Chomps', player), 'Big Key (Turtle Rock)', player) forbid_item(world.get_location('Turtle Rock - Pokey 2 Key Drop', player), 'Big Key (Turtle Rock)', player) - if world.accessibility[player] == 'locations' and world.goal[player] != 'icerodhunt': - if world.bigkey_shuffle[player] and can_reach_big_chest: + if world.accessibility[player] == 'locations': + if world.big_key_shuffle[player] and can_reach_big_chest: # Must not go in the dungeon - all 3 available chests (Chomps, Big Chest, Crystaroller) must be keys to access laser bridge, and the big key is required first for location in ['Turtle Rock - Chain Chomps', 'Turtle Rock - Compass Chest', 'Turtle Rock - Pokey 1 Key Drop', 'Turtle Rock - Pokey 2 Key Drop', @@ -1515,8 +1622,10 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool): # regions for the exits of multi-entrance caves/drops that bunny cannot pass # Note spiral cave and two brothers house are passable in superbunny state for glitch logic with extra requirements. - bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)', - 'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', 'Spiral Cave (Top)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)'] + bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', + 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', + 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)', 'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', + 'Spiral Cave (Top)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)'] bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', @@ -1549,7 +1658,7 @@ def is_link(region): def get_rule_to_add(region, location = None, connecting_entrance = None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. - if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic']: + if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic']: if region.name == 'Swamp Palace (Entrance)': # Need to 0hp revive - not in logic return lambda state: state.has('Moon Pearl', player) if region.name == 'Tower of Hera (Bottom)': # Need to hit the crystal switch @@ -1589,7 +1698,7 @@ def get_rule_to_add(region, location = None, connecting_entrance = None): seen.add(new_region) if not is_link(new_region): # For glitch rulesets, establish superbunny and revival rules. - if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): + if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions(): possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and has_sword(state, player)) elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions() @@ -1626,7 +1735,7 @@ def get_rule_to_add(region, location = None, connecting_entrance = None): # Add requirements for all locations that are actually in the dark world, except those available to the bunny, including dungeon revival for entrance in world.get_entrances(player): if is_bunny(entrance.connected_region): - if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] : + if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] : if entrance.connected_region.type == LTTPRegionType.Dungeon: if entrance.parent_region.type != LTTPRegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance)) @@ -1634,7 +1743,7 @@ def get_rule_to_add(region, location = None, connecting_entrance = None): if entrance.connected_region.name == 'Turtle Rock (Entrance)': add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance)) for location in entrance.connected_region.locations: - if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): continue if location.name in bunny_accessible_locations: continue diff --git a/worlds/alttp/Shops.py b/worlds/alttp/Shops.py index c0f2e2236e69..64a385a18587 100644 --- a/worlds/alttp/Shops.py +++ b/worlds/alttp/Shops.py @@ -5,11 +5,14 @@ from Utils import int16_as_bytes +from worlds.generic.Rules import add_rule + +from BaseClasses import CollectionState from .SubClasses import ALttPLocation from .EntranceShuffle import door_addresses -from .Items import item_name_groups, item_table, ItemFactory, trap_replaceable, GetBeemizerItem -from .Options import smallkey_shuffle - +from .Items import item_name_groups +from .Options import small_key_shuffle, RandomizeShopInventories +from .StateHelpers import has_hearts, can_use_bombs, can_hold_arrows logger = logging.getLogger("Shops") @@ -36,9 +39,9 @@ class ShopPriceType(IntEnum): Item = 10 -class Shop(): +class Shop: slots: int = 3 # slot count is not dynamic in asm, however inventory can have None as empty slots - blacklist: Set[str] = set() # items that don't work, todo: actually check against this + blacklist: Set[str] = set() # items that don't work type = ShopType.Shop slot_names: Dict[int, str] = { 0: " Left", @@ -103,7 +106,7 @@ def clear_inventory(self): self.inventory = [None] * self.slots def add_inventory(self, slot: int, item: str, price: int, max: int = 0, - replacement: Optional[str] = None, replacement_price: int = 0, create_location: bool = False, + replacement: Optional[str] = None, replacement_price: int = 0, player: int = 0, price_type: int = ShopPriceType.Rupees, replacement_price_type: int = ShopPriceType.Rupees): self.inventory[slot] = { @@ -114,33 +117,23 @@ def add_inventory(self, slot: int, item: str, price: int, max: int = 0, 'replacement': replacement, 'replacement_price': replacement_price, 'replacement_price_type': replacement_price_type, - 'create_location': create_location, 'player': player } def push_inventory(self, slot: int, item: str, price: int, max: int = 1, player: int = 0, price_type: int = ShopPriceType.Rupees): - if not self.inventory[slot]: - raise ValueError("Inventory can't be pushed back if it doesn't exist") - - if not self.can_push_inventory(slot): - logging.warning(f'Warning, there is already an item pushed into this slot.') self.inventory[slot] = { 'item': item, 'price': price, 'price_type': price_type, 'max': max, - 'replacement': self.inventory[slot]["item"], - 'replacement_price': self.inventory[slot]["price"], - 'replacement_price_type': self.inventory[slot]["price_type"], - 'create_location': self.inventory[slot]["create_location"], + 'replacement': self.inventory[slot]["item"] if self.inventory[slot] else None, + 'replacement_price': self.inventory[slot]["price"] if self.inventory[slot] else 0, + 'replacement_price_type': self.inventory[slot]["price_type"] if self.inventory[slot] else ShopPriceType.Rupees, 'player': player } - def can_push_inventory(self, slot: int): - return self.inventory[slot] and not self.inventory[slot]["replacement"] - class TakeAny(Shop): type = ShopType.TakeAny @@ -156,6 +149,10 @@ class UpgradeShop(Shop): # Potions break due to VRAM flags set in UpgradeShop. # Didn't check for more things breaking as not much else can be shuffled here currently blacklist = item_name_groups["Potions"] + slot_names: Dict[int, str] = { + 0: " Left", + 1: " Right" + } shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop, @@ -163,191 +160,84 @@ class UpgradeShop(Shop): ShopType.TakeAny: TakeAny} -def FillDisabledShopSlots(world): - shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops) - for location in shop_locations - if location.shop_slot is not None and location.shop_slot_disabled} - for location in shop_slots: - location.shop_slot_disabled = True - shop: Shop = location.parent_region.shop - location.item = ItemFactory(shop.inventory[location.shop_slot]['item'], location.player) - location.item_rule = lambda item: item.name == location.item.name and item.player == location.player - location.locked = True +def push_shop_inventories(multiworld): + shop_slots = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if shop.type + != ShopType.TakeAny) for location in shop_locations if location.shop_slot is not None] + for location in shop_slots: + item_name = location.item.name + # Retro Bow arrows will already have been pushed + if (not multiworld.retro_bow[location.player]) or ((item_name, location.item.player) + != ("Single Arrow", location.player)): + location.shop.push_inventory(location.shop_slot, item_name, location.shop_price, + 1, location.item.player if location.item.player != location.player else 0, + location.shop_price_type) + location.shop_price = location.shop.inventory[location.shop_slot]["price"] = min(location.shop_price, + get_price(multiworld, location.shop.inventory[location.shop_slot], location.player, + location.shop_price_type)[1]) -def ShopSlotFill(multiworld): - shop_slots: Set[ALttPLocation] = {location for shop_locations in - (shop.region.locations for shop in multiworld.shops if shop.type != ShopType.TakeAny) - for location in shop_locations if location.shop_slot is not None} - removed = set() - for location in shop_slots: - shop: Shop = location.parent_region.shop - if not shop.can_push_inventory(location.shop_slot) or location.shop_slot_disabled: - location.shop_slot_disabled = True - removed.add(location) - - if removed: - shop_slots -= removed - - if shop_slots: - logger.info("Filling LttP Shop Slots") - del shop_slots - - from Fill import swap_location_item - # TODO: allow each game to register a blacklist to be used here? - blacklist_words = {"Rupee"} - blacklist_words = {item_name for item_name in item_table if any( - blacklist_word in item_name for blacklist_word in blacklist_words)} - blacklist_words.add("Bee") - - locations_per_sphere = [sorted(sphere, key=lambda location: (location.name, location.player)) - for sphere in multiworld.get_spheres()] - - # currently special care needs to be taken so that Shop.region.locations.item is identical to Shop.inventory - # Potentially create Locations as needed and make inventory the only source, to prevent divergence - cumu_weights = [] - shops_per_sphere = [] - candidates_per_sphere = [] - - # sort spheres into piles of valid candidates and shops - for sphere in locations_per_sphere: - current_shops_slots = [] - current_candidates = [] - shops_per_sphere.append(current_shops_slots) - candidates_per_sphere.append(current_candidates) - for location in sphere: - if isinstance(location, ALttPLocation) and location.shop_slot is not None: - if not location.shop_slot_disabled: - current_shops_slots.append(location) - elif not location.locked and location.item.name not in blacklist_words: - current_candidates.append(location) - if cumu_weights: - x = cumu_weights[-1] - else: - x = 0 - cumu_weights.append(len(current_candidates) + x) - - multiworld.random.shuffle(current_candidates) - - del locations_per_sphere - - for i, current_shop_slots in enumerate(shops_per_sphere): - if current_shop_slots: - # getting all candidates and shuffling them feels cpu expensive, there may be a better method - candidates = [(location, i) for i, candidates in enumerate(candidates_per_sphere[i:], start=i) - for location in candidates] - multiworld.random.shuffle(candidates) - for location in current_shop_slots: - shop: Shop = location.parent_region.shop - for index, (c, swapping_sphere_id) in enumerate(candidates): # chosen item locations - if c.item_rule(location.item) and location.item_rule(c.item): - swap_location_item(c, location, check_locked=False) - logger.debug(f"Swapping {c} into {location}:: {location.item}") - # remove candidate - candidates_per_sphere[swapping_sphere_id].remove(c) - candidates.pop(index) - break - - else: - # This *should* never happen. But let's fail safely just in case. - logger.warning("Ran out of ShopShuffle Item candidate locations.") - location.shop_slot_disabled = True - continue - - item_name = location.item.name - if location.item.game != "A Link to the Past": - if location.item.advancement: - price = multiworld.random.randrange(8, 56) - elif location.item.useful: - price = multiworld.random.randrange(4, 28) - else: - price = multiworld.random.randrange(2, 14) - elif any(x in item_name for x in - ['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']): - price = multiworld.random.randrange(1, 7) - elif any(x in item_name for x in ['Arrow', 'Bomb', 'Clock']): - price = multiworld.random.randrange(2, 14) - elif any(x in item_name for x in ['Small Key', 'Heart']): - price = multiworld.random.randrange(4, 28) - else: - price = multiworld.random.randrange(8, 56) - - shop.push_inventory(location.shop_slot, item_name, - min(int(price * multiworld.shop_price_modifier[location.player] / 100) * 5, 9999), 1, - location.item.player if location.item.player != location.player else 0) - if 'P' in multiworld.shop_shuffle[location.player]: - price_to_funny_price(multiworld, shop.inventory[location.shop_slot], location.player) - - FillDisabledShopSlots(multiworld) - - -def create_shops(world, player: int): - option = world.shop_shuffle[player] +def create_shops(multiworld, player: int): player_shop_table = shop_table.copy() - if "w" in option: + if multiworld.include_witch_hut[player]: player_shop_table["Potion Shop"] = player_shop_table["Potion Shop"]._replace(locked=False) dynamic_shop_slots = total_dynamic_shop_slots + 3 else: dynamic_shop_slots = total_dynamic_shop_slots + if multiworld.shuffle_capacity_upgrades[player]: + player_shop_table["Capacity Upgrade"] = player_shop_table["Capacity Upgrade"]._replace(locked=False) - num_slots = min(dynamic_shop_slots, world.shop_item_slots[player]) + num_slots = min(dynamic_shop_slots, multiworld.shop_item_slots[player]) single_purchase_slots: List[bool] = [True] * num_slots + [False] * (dynamic_shop_slots - num_slots) - world.random.shuffle(single_purchase_slots) + multiworld.random.shuffle(single_purchase_slots) - if 'g' in option or 'f' in option: + if multiworld.randomize_shop_inventories[player]: default_shop_table = [i for l in [shop_generation_types[x] for x in ['arrows', 'bombs', 'potions', 'shields', 'bottle'] if - not world.retro_bow[player] or x != 'arrows'] for i in l] - new_basic_shop = world.random.sample(default_shop_table, k=3) - new_dark_shop = world.random.sample(default_shop_table, k=3) + not multiworld.retro_bow[player] or x != 'arrows'] for i in l] + new_basic_shop = multiworld.random.sample(default_shop_table, k=3) + new_dark_shop = multiworld.random.sample(default_shop_table, k=3) for name, shop in player_shop_table.items(): typ, shop_id, keeper, custom, locked, items, sram_offset = shop if not locked: - new_items = world.random.sample(default_shop_table, k=3) - if 'f' not in option: + new_items = multiworld.random.sample(default_shop_table, k=len(items)) + if multiworld.randomize_shop_inventories[player] == RandomizeShopInventories.option_randomize_by_shop_type: if items == _basic_shop_defaults: new_items = new_basic_shop elif items == _dark_world_shop_defaults: new_items = new_dark_shop - keeper = world.random.choice([0xA0, 0xC1, 0xFF]) + keeper = multiworld.random.choice([0xA0, 0xC1, 0xFF]) player_shop_table[name] = ShopData(typ, shop_id, keeper, custom, locked, new_items, sram_offset) - if world.mode[player] == "inverted": + if multiworld.mode[player] == "inverted": # make sure that blue potion is available in inverted, special case locked = None; lock when done. player_shop_table["Dark Lake Hylia Shop"] = \ player_shop_table["Dark Lake Hylia Shop"]._replace(items=_inverted_hylia_shop_defaults, locked=None) - chance_100 = int(world.retro_bow[player]) * 0.25 + int( - world.smallkey_shuffle[player] == smallkey_shuffle.option_universal) * 0.5 for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram_offset) in player_shop_table.items(): - region = world.get_region(region_name, player) + region = multiworld.get_region(region_name, player) shop: Shop = shop_class_mapping[type](region, room_id, shopkeeper, custom, locked, sram_offset) # special case: allow shop slots, but do not allow overwriting of base inventory behind them if locked is None: shop.locked = True region.shop = shop - world.shops.append(shop) + multiworld.shops.append(shop) for index, item in enumerate(inventory): shop.add_inventory(index, *item) - if not locked and num_slots: + if not locked and (num_slots or type == ShopType.UpgradeShop): slot_name = f"{region.name}{shop.slot_names[index]}" loc = ALttPLocation(player, slot_name, address=shop_table_by_location[slot_name], parent=region, hint_text="for sale") + loc.shop_price_type, loc.shop_price = get_price(multiworld, None, player) + loc.item_rule = lambda item, spot=loc: not any(i for i in price_blacklist[spot.shop_price_type] if i in item.name) + add_rule(loc, lambda state, spot=loc: shop_price_rules(state, player, spot)) + loc.shop = shop loc.shop_slot = index - loc.locked = True - if single_purchase_slots.pop(): - if world.goal[player] != 'icerodhunt': - if world.random.random() < chance_100: - additional_item = 'Rupees (100)' - else: - additional_item = 'Rupees (50)' - else: - additional_item = GetBeemizerItem(world, player, 'Nothing') - loc.item = ItemFactory(additional_item, player) - else: - loc.item = ItemFactory(GetBeemizerItem(world, player, 'Nothing'), player) + if ((not (multiworld.shuffle_capacity_upgrades[player] and type == ShopType.UpgradeShop)) + and not single_purchase_slots.pop()): loc.shop_slot_disabled = True - shop.region.locations.append(loc) + loc.locked = True + else: + shop.region.locations.append(loc) class ShopData(NamedTuple): @@ -387,9 +277,10 @@ class ShopData(NamedTuple): SHOP_ID_START = 0x400000 shop_table_by_location_id = dict(enumerate( - (f"{name}{Shop.slot_names[num]}" for name, shop_data in - sorted(shop_table.items(), key=lambda item: item[1].sram_offset) - for num in range(3)), start=SHOP_ID_START)) + (f"{name}{UpgradeShop.slot_names[num]}" if shop_data.type == ShopType.UpgradeShop else + f"{name}{Shop.slot_names[num]}" for name, shop_data in sorted(shop_table.items(), + key=lambda item: item[1].sram_offset) + for num in range(2 if shop_data.type == ShopType.UpgradeShop else 3)), start=SHOP_ID_START)) shop_table_by_location_id[(SHOP_ID_START + total_shop_slots)] = "Old Man Sword Cave" shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 1)] = "Take-Any #1" @@ -409,114 +300,54 @@ class ShopData(NamedTuple): } -def set_up_shops(world, player: int): +def set_up_shops(multiworld, player: int): # TODO: move hard+ mode changes for shields here, utilizing the new shops - if world.retro_bow[player]: - rss = world.get_region('Red Shield Shop', player).shop + if multiworld.retro_bow[player]: + rss = multiworld.get_region('Red Shield Shop', player).shop replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50], ['Blue Shield', 50], ['Small Heart', 10]] # Can't just replace the single arrow with 10 arrows as retro doesn't need them. - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: replacement_items.append(['Small Key (Universal)', 100]) - replacement_item = world.random.choice(replacement_items) + replacement_item = multiworld.random.choice(replacement_items) rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1]) rss.locked = True - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal or world.retro_bow[player]: - for shop in world.random.sample([s for s in world.shops if - s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player], - 5): + if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal or multiworld.retro_bow[player]: + for shop in multiworld.random.sample([s for s in multiworld.shops if + s.custom and not s.locked and s.type == ShopType.Shop + and s.region.player == player], 5): shop.locked = True slots = [0, 1, 2] - world.random.shuffle(slots) + multiworld.random.shuffle(slots) slots = iter(slots) - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: shop.add_inventory(next(slots), 'Small Key (Universal)', 100) - if world.retro_bow[player]: + if multiworld.retro_bow[player]: shop.push_inventory(next(slots), 'Single Arrow', 80) - -def shuffle_shops(world, items, player: int): - option = world.shop_shuffle[player] - if 'u' in option: - progressive = world.progressive[player] - progressive = world.random.choice([True, False]) if progressive == 'grouped_random' else progressive == 'on' - progressive &= world.goal == 'icerodhunt' - new_items = ["Bomb Upgrade (+5)"] * 6 - new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)") - - if not world.retro_bow[player]: - new_items += ["Arrow Upgrade (+5)"] * 6 - new_items.append("Arrow Upgrade (+5)" if progressive else "Arrow Upgrade (+10)") - - world.random.shuffle(new_items) # Decide what gets tossed randomly if it can't insert everything. - - capacityshop: Optional[Shop] = None - for shop in world.shops: + if multiworld.shuffle_capacity_upgrades[player]: + for shop in multiworld.shops: if shop.type == ShopType.UpgradeShop and shop.region.player == player and \ shop.region.name == "Capacity Upgrade": shop.clear_inventory() - capacityshop = shop - - if world.goal[player] != 'icerodhunt': - for i, item in enumerate(items): - if item.name in trap_replaceable: - items[i] = ItemFactory(new_items.pop(), player) - if not new_items: - break - else: - logging.warning( - f"Not all upgrades put into Player{player}' item pool. Putting remaining items in Capacity Upgrade shop instead.") - bombupgrades = sum(1 for item in new_items if 'Bomb Upgrade' in item) - arrowupgrades = sum(1 for item in new_items if 'Arrow Upgrade' in item) - slots = iter(range(2)) - if bombupgrades: - capacityshop.add_inventory(next(slots), 'Bomb Upgrade (+5)', 100, bombupgrades) - if arrowupgrades: - capacityshop.add_inventory(next(slots), 'Arrow Upgrade (+5)', 100, arrowupgrades) - else: - for item in new_items: - world.push_precollected(ItemFactory(item, player)) - if any(setting in option for setting in 'ipP'): + if (multiworld.shuffle_shop_inventories[player] or multiworld.randomize_shop_prices[player] + or multiworld.randomize_cost_types[player]): shops = [] - upgrade_shops = [] total_inventory = [] - for shop in world.shops: + for shop in multiworld.shops: if shop.region.player == player: - if shop.type == ShopType.UpgradeShop: - upgrade_shops.append(shop) - elif shop.type == ShopType.Shop and not shop.locked: + if shop.type == ShopType.Shop and not shop.locked: shops.append(shop) total_inventory.extend(shop.inventory) - if 'p' in option: - def price_adjust(price: int) -> int: - # it is important that a base price of 0 always returns 0 as new price! - adjust = 2 if price < 100 else 5 - return int((price / adjust) * (0.5 + world.random.random() * 1.5)) * adjust - - def adjust_item(item): - if item: - item["price"] = price_adjust(item["price"]) - item['replacement_price'] = price_adjust(item["price"]) - - for item in total_inventory: - adjust_item(item) - for shop in upgrade_shops: - for item in shop.inventory: - adjust_item(item) - - if 'P' in option: - for item in total_inventory: - price_to_funny_price(world, item, player) - # Don't apply to upgrade shops - # Upgrade shop is only one place, and will generally be too easy to - # replenish hearts and bombs - - if 'i' in option: - world.random.shuffle(total_inventory) + for item in total_inventory: + item["price_type"], item["price"] = get_price(multiworld, item, player) + + if multiworld.shuffle_shop_inventories[player]: + multiworld.random.shuffle(total_inventory) i = 0 for shop in shops: @@ -539,16 +370,18 @@ def adjust_item(item): } price_chart = { - ShopPriceType.Rupees: lambda p: p, - ShopPriceType.Hearts: lambda p: min(5, p // 5) * 8, # Each heart is 0x8 in memory, Max of 5 hearts (20 total??) - ShopPriceType.Magic: lambda p: min(15, p // 5) * 8, # Each pip is 0x8 in memory, Max of 15 pips (16 total...) - ShopPriceType.Bombs: lambda p: max(1, min(10, p // 5)), # 10 Bombs max - ShopPriceType.Arrows: lambda p: max(1, min(30, p // 5)), # 30 Arrows Max - ShopPriceType.HeartContainer: lambda p: 0x8, - ShopPriceType.BombUpgrade: lambda p: 0x1, - ShopPriceType.ArrowUpgrade: lambda p: 0x1, - ShopPriceType.Keys: lambda p: min(3, (p // 100) + 1), # Max of 3 keys for a price - ShopPriceType.Potion: lambda p: (p // 5) % 5, + ShopPriceType.Rupees: lambda p, d: p, + # Each heart is 0x8 in memory, Max of 19 hearts on easy/normal, 9 on hard, 7 on expert + ShopPriceType.Hearts: lambda p, d: max(8, min([19, 19, 9, 7][d], p // 14) * 8), + # Each pip is 0x8 in memory, Max of 15 pips (16 total) + ShopPriceType.Magic: lambda p, d: max(8, min(15, p // 18) * 8), + ShopPriceType.Bombs: lambda p, d: max(1, min(50, p // 5)), # 50 Bombs max + ShopPriceType.Arrows: lambda p, d: max(1, min(70, p // 4)), # 70 Arrows Max + ShopPriceType.HeartContainer: lambda p, d: 0x8, + ShopPriceType.BombUpgrade: lambda p, d: 0x1, + ShopPriceType.ArrowUpgrade: lambda p, d: 0x1, + ShopPriceType.Keys: lambda p, d: max(1, min(3, (p // 90) + 1)), # Max of 3 keys for a price + ShopPriceType.Potion: lambda p, d: (p // 5) % 5, } price_type_display_name = { @@ -557,6 +390,8 @@ def adjust_item(item): ShopPriceType.Bombs: "Bombs", ShopPriceType.Arrows: "Arrows", ShopPriceType.Keys: "Keys", + ShopPriceType.Item: "Item", + ShopPriceType.Magic: "Magic" } # price division @@ -565,57 +400,74 @@ def adjust_item(item): ShopPriceType.Magic: 8, } -# prices with no? logic requirements -simple_price_types = [ - ShopPriceType.Rupees, - ShopPriceType.Hearts, - ShopPriceType.Bombs, - ShopPriceType.Arrows, - ShopPriceType.Keys -] + +def get_price_modifier(item): + if item.game == "A Link to the Past": + if any(x in item.name for x in + ['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']): + return 0.125 + elif any(x in item.name for x in + ['Arrow', 'Bomb', 'Clock']) and item.name != "Bombos" and "(50)" not in item.name: + return 0.25 + elif any(x in item.name for x in ['Small Key', 'Heart']): + return 0.5 + else: + return 1 + if item.advancement: + return 1 + elif item.useful: + return 0.5 + else: + return 0.25 -def price_to_funny_price(world, item: dict, player: int): - """ - Converts a raw Rupee price into a special price type - """ +def get_price(multiworld, item, player: int, price_type=None): + """Converts a raw Rupee price into a special price type""" + + if price_type: + price_types = [price_type] + else: + price_types = [ShopPriceType.Rupees] # included as a chance to not change price + if multiworld.randomize_cost_types[player]: + price_types += [ + ShopPriceType.Hearts, + ShopPriceType.Bombs, + ShopPriceType.Magic, + ] + if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: + if item and item["item"] == "Small Key (Universal)": + price_types = [ShopPriceType.Rupees, ShopPriceType.Magic] # no logical requirements for repeatable keys + else: + price_types.append(ShopPriceType.Keys) + if multiworld.retro_bow[player]: + if item and item["item"] == "Single Arrow": + price_types = [ShopPriceType.Rupees, ShopPriceType.Magic] # no logical requirements for arrows + else: + price_types.append(ShopPriceType.Arrows) + diff = multiworld.item_pool[player].value if item: - price_types = [ - ShopPriceType.Rupees, # included as a chance to not change price type - ShopPriceType.Hearts, - ShopPriceType.Bombs, - ] - # don't pay in universal keys to get access to universal keys - if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal \ - and not "Small Key (Universal)" == item['replacement']: - price_types.append(ShopPriceType.Keys) - if not world.retro_bow[player]: - price_types.append(ShopPriceType.Arrows) - world.random.shuffle(price_types) + # This is for a shop's regular inventory, the item is already determined, and we will decide the price here + price = item["price"] + if multiworld.randomize_shop_prices[player]: + adjust = 2 if price < 100 else 5 + price = int((price / adjust) * (0.5 + multiworld.random.random() * 1.5)) * adjust + multiworld.random.shuffle(price_types) for p_type in price_types: - # Ignore rupee prices - if p_type == ShopPriceType.Rupees: - return if any(x in item['item'] for x in price_blacklist[p_type]): continue - else: - item['price'] = min(price_chart[p_type](item['price']), 255) - item['price_type'] = p_type - break - - -def create_dynamic_shop_locations(world, player): - for shop in world.shops: - if shop.region.player == player: - for i, item in enumerate(shop.inventory): - if item is None: - continue - if item['create_location']: - slot_name = f"{shop.region.name}{shop.slot_names[i]}" - loc = ALttPLocation(player, slot_name, - address=shop_table_by_location[slot_name], parent=shop.region) - loc.place_locked_item(ItemFactory(item['item'], player)) - if shop.type == ShopType.TakeAny: - loc.shop_slot_disabled = True - shop.region.locations.append(loc) - loc.shop_slot = i + return p_type, price_chart[p_type](price, diff) + else: + # This is an AP location and the price will be adjusted after an item is shuffled into it + p_type = multiworld.random.choice(price_types) + return p_type, price_chart[p_type](min(int(multiworld.random.randint(8, 56) + * multiworld.shop_price_modifier[player] / 100) * 5, 9999), diff) + + +def shop_price_rules(state: CollectionState, player: int, location: ALttPLocation): + if location.shop_price_type == ShopPriceType.Hearts: + return has_hearts(state, player, (location.shop_price / 8) + 1) + elif location.shop_price_type == ShopPriceType.Bombs: + return can_use_bombs(state, player, location.shop_price) + elif location.shop_price_type == ShopPriceType.Arrows: + return can_hold_arrows(state, player, location.shop_price) + return True diff --git a/worlds/alttp/StateHelpers.py b/worlds/alttp/StateHelpers.py index 38ce00ef4537..4ed1b1caf205 100644 --- a/worlds/alttp/StateHelpers.py +++ b/worlds/alttp/StateHelpers.py @@ -10,7 +10,7 @@ def is_not_bunny(state: CollectionState, region: LTTPRegion, player: int) -> boo def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bool: - return is_not_bunny(state, region, player) and state.has('Pegasus Boots', player) + return can_use_bombs(state, player) and is_not_bunny(state, region, player) and state.has('Pegasus Boots', player) def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool: @@ -83,13 +83,47 @@ def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16, return basemagic >= smallmagic +def can_hold_arrows(state: CollectionState, player: int, quantity: int): + arrows = 30 + ((state.count("Arrow Upgrade (+5)", player) * 5) + (state.count("Arrow Upgrade (+10)", player) * 10) + + (state.count("Bomb Upgrade (50)", player) * 50)) + # Arrow Upgrade (+5) beyond the 6th gives +10 + arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10)) + return min(70, arrows) >= quantity + + +def can_use_bombs(state: CollectionState, player: int, quantity: int = 1) -> bool: + bombs = 0 if state.multiworld.bombless_start[player] else 10 + bombs += ((state.count("Bomb Upgrade (+5)", player) * 5) + (state.count("Bomb Upgrade (+10)", player) * 10) + + (state.count("Bomb Upgrade (50)", player) * 50)) + # Bomb Upgrade (+5) beyond the 6th gives +10 + bombs += max(0, ((state.count("Bomb Upgrade (+5)", player) - 6) * 10)) + if (not state.multiworld.shuffle_capacity_upgrades[player]) and state.has("Capacity Upgrade Shop", player): + bombs += 40 + return bombs >= min(quantity, 50) + + +def can_bomb_or_bonk(state: CollectionState, player: int) -> bool: + return state.has("Pegasus Boots", player) or can_use_bombs(state, player) + + def can_kill_most_things(state: CollectionState, player: int, enemies: int = 5) -> bool: - return (has_melee_weapon(state, player) - or state.has('Cane of Somaria', player) - or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player))) - or can_shoot_arrows(state, player) - or state.has('Fire Rod', player) - or (state.has('Bombs (10)', player) and enemies < 6)) + if state.multiworld.enemy_shuffle[player]: + # I don't fully understand Enemizer's logic for placing enemies in spots where they need to be killable, if any. + # Just go with maximal requirements for now. + return (has_melee_weapon(state, player) + and state.has('Cane of Somaria', player) + and state.has('Cane of Byrna', player) and can_extend_magic(state, player) + and can_shoot_arrows(state, player) + and state.has('Fire Rod', player) + and can_use_bombs(state, player, enemies * 4)) + else: + return (has_melee_weapon(state, player) + or state.has('Cane of Somaria', player) + or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player))) + or can_shoot_arrows(state, player) + or state.has('Fire Rod', player) + or (state.multiworld.enemy_health[player] in ("easy", "default") + and can_use_bombs(state, player, enemies * 4))) def can_get_good_bee(state: CollectionState, player: int) -> bool: @@ -159,4 +193,4 @@ def can_get_glitched_speed_dw(state: CollectionState, player: int) -> bool: rules = [state.has('Pegasus Boots', player), any([state.has('Hookshot', player), has_sword(state, player)])] if state.multiworld.mode[player] != 'inverted': rules.append(state.has('Moon Pearl', player)) - return all(rules) \ No newline at end of file + return all(rules) diff --git a/worlds/alttp/SubClasses.py b/worlds/alttp/SubClasses.py index 22eeebe181e5..769dcc199852 100644 --- a/worlds/alttp/SubClasses.py +++ b/worlds/alttp/SubClasses.py @@ -14,9 +14,12 @@ class ALttPLocation(Location): crystal: bool player_address: Optional[int] _hint_text: Optional[str] + shop: None shop_slot: Optional[int] = None """If given as integer, shop_slot is the shop's inventory index.""" shop_slot_disabled: bool = False + shop_price = 0 + shop_price_type = None parent_region: "LTTPRegion" def __init__(self, player: int, name: str, address: Optional[int] = None, crystal: bool = False, diff --git a/worlds/alttp/UnderworldGlitchRules.py b/worlds/alttp/UnderworldGlitchRules.py index a6aefc74129a..497d5de496c3 100644 --- a/worlds/alttp/UnderworldGlitchRules.py +++ b/worlds/alttp/UnderworldGlitchRules.py @@ -42,7 +42,7 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du fix_fake_worlds = world.fix_fake_world[player] dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0] - if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix + if not fix_dungeon_exits: # vanilla, simple, restricted, dungeons_simple; should never have fake worlds fix # Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially. if dungeon_entrance.name == 'Skull Woods Final Section': set_rule(clip, lambda state: False) # entrance doesn't exist until you fire rod it from the other side @@ -52,12 +52,12 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du add_rule(clip, lambda state: state.has('Cape', player) or has_beam_sword(state, player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) - elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix + elif not fix_fake_worlds: # full, dungeons_full; fixed dungeon exits, but no fake worlds fix # Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region. add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player))) # exiting restriction add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) - # Otherwise, the shuffle type is crossed, dungeonscrossed, or insanity; all of these do not need additional rules on where we can go, + # Otherwise, the shuffle type is crossed, dungeons_crossed, or insanity; all of these do not need additional rules on where we can go, # since the clip links directly to the exterior region. @@ -93,7 +93,7 @@ def underworld_glitches_rules(world, player): # We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT. # First we require a certain type of entrance shuffle, then build the rule from its pieces. if not world.swamp_patch_required[player]: - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: + if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']: rule_map = { 'Misery Mire (Entrance)': (lambda state: True), 'Tower of Hera (Bottom)': (lambda state: state.can_reach('Tower of Hera Big Key Door', 'Entrance', player)) diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 3f380d0037a2..e1216010e2b3 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -13,14 +13,14 @@ from .InvertedRegions import create_inverted_regions, mark_dark_world_regions from .ItemPool import generate_itempool, difficulties from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem -from .Options import alttp_options, smallkey_shuffle +from .Options import alttp_options, small_key_shuffle from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance, \ is_main_entrance, key_drop_data from .Client import ALTTPSNIClient from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \ get_hash_string, get_base_rom_path, LttPDeltaPatch from .Rules import set_rules -from .Shops import create_shops, Shop, ShopSlotFill, ShopType, price_rate_display, price_type_display_name +from .Shops import create_shops, Shop, push_shop_inventories, ShopType, price_rate_display, price_type_display_name from .SubClasses import ALttPItem, LTTPRegionType from worlds.AutoWorld import World, WebWorld, LogicMixin from .StateHelpers import can_buy_unlimited @@ -213,7 +213,7 @@ class ALTTPWorld(World): item_name_to_id = {name: data.item_code for name, data in item_table.items() if type(data.item_code) == int} location_name_to_id = lookup_name_to_id - data_version = 8 + data_version = 9 required_client_version = (0, 4, 1) web = ALTTPWeb() @@ -290,33 +290,34 @@ def generate_early(self): self.pyramid_fairy_bottle_fill = self.random.choice(bottle_options) if multiworld.mode[player] == 'standard': - if multiworld.smallkey_shuffle[player]: - if (multiworld.smallkey_shuffle[player] not in - (smallkey_shuffle.option_universal, smallkey_shuffle.option_own_dungeons, - smallkey_shuffle.option_start_with)): + if multiworld.small_key_shuffle[player]: + if (multiworld.small_key_shuffle[player] not in + (small_key_shuffle.option_universal, small_key_shuffle.option_own_dungeons, + small_key_shuffle.option_start_with)): self.multiworld.local_early_items[self.player]["Small Key (Hyrule Castle)"] = 1 self.multiworld.local_items[self.player].value.add("Small Key (Hyrule Castle)") self.multiworld.non_local_items[self.player].value.discard("Small Key (Hyrule Castle)") - if multiworld.bigkey_shuffle[player]: + if multiworld.big_key_shuffle[player]: self.multiworld.local_items[self.player].value.add("Big Key (Hyrule Castle)") self.multiworld.non_local_items[self.player].value.discard("Big Key (Hyrule Castle)") # system for sharing ER layouts self.er_seed = str(multiworld.random.randint(0, 2 ** 64)) - if "-" in multiworld.shuffle[player]: - shuffle, seed = multiworld.shuffle[player].split("-", 1) - multiworld.shuffle[player] = shuffle + if multiworld.entrance_shuffle[player] != "vanilla" and multiworld.entrance_shuffle_seed[player] != "random": + shuffle = multiworld.entrance_shuffle[player].current_key if shuffle == "vanilla": self.er_seed = "vanilla" - elif seed.startswith("group-") or multiworld.is_race: + elif (not multiworld.entrance_shuffle_seed[player].value.isdigit()) or multiworld.is_race: self.er_seed = get_same_seed(multiworld, ( - shuffle, seed, multiworld.retro_caves[player], multiworld.mode[player], multiworld.logic[player])) + shuffle, multiworld.entrance_shuffle_seed[player].value, multiworld.retro_caves[player], multiworld.mode[player], + multiworld.glitches_required[player])) else: # not a race or group seed, use set seed as is. - self.er_seed = seed - elif multiworld.shuffle[player] == "vanilla": + self.er_seed = int(multiworld.entrance_shuffle_seed[player].value) + elif multiworld.entrance_shuffle[player] == "vanilla": self.er_seed = "vanilla" - for dungeon_item in ["smallkey_shuffle", "bigkey_shuffle", "compass_shuffle", "map_shuffle"]: + + for dungeon_item in ["small_key_shuffle", "big_key_shuffle", "compass_shuffle", "map_shuffle"]: option = getattr(multiworld, dungeon_item)[player] if option == "own_world": multiworld.local_items[player].value |= self.item_name_groups[option.item_name_group] @@ -329,10 +330,10 @@ def generate_early(self): if option == "original_dungeon": self.dungeon_specific_item_names |= self.item_name_groups[option.item_name_group] - multiworld.difficulty_requirements[player] = difficulties[multiworld.difficulty[player]] + multiworld.difficulty_requirements[player] = difficulties[multiworld.item_pool[player].current_key] # enforce pre-defined local items. - if multiworld.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]: + if multiworld.goal[player] in ["local_triforce_hunt", "local_ganon_triforce_hunt"]: 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). @@ -345,9 +346,6 @@ def create_regions(self): player = self.player world = self.multiworld - world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player], - world.triforce_pieces_required[player]) - if world.mode[player] != 'inverted': create_regions(world, player) else: @@ -355,8 +353,8 @@ def create_regions(self): create_shops(world, player) self.create_dungeons() - if world.logic[player] not in ["noglitches", "minorglitches"] and world.shuffle[player] in \ - {"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}: + if world.glitches_required[player] not in ["no_glitches", "minor_glitches"] and world.entrance_shuffle[player] in \ + {"vanilla", "dungeons_simple", "dungeons_full", "simple", "restricted", "full"}: world.fix_fake_world[player] = False # seeded entrance shuffle @@ -455,7 +453,7 @@ def collect_item(self, state: CollectionState, item: Item, remove=False): if state.has('Silver Bow', item.player): return elif state.has('Bow', item.player) and (self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 2 - or self.multiworld.logic[item.player] == 'noglitches' + or self.multiworld.glitches_required[item.player] == 'no_glitches' or self.multiworld.swordless[item.player]): # modes where silver bow is always required for ganon return 'Silver Bow' elif self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 1: @@ -499,9 +497,9 @@ def pre_fill(self): break else: raise FillError('Unable to place dungeon prizes') - 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: + if world.mode[player] == 'standard' and world.small_key_shuffle[player] \ + and world.small_key_shuffle[player] != small_key_shuffle.option_universal and \ + world.small_key_shuffle[player] != small_key_shuffle.option_own_dungeons: world.local_early_items[player]["Small Key (Hyrule Castle)"] = 1 @classmethod @@ -509,10 +507,9 @@ def stage_pre_fill(cls, world): from .Dungeons import fill_dungeons_restrictive fill_dungeons_restrictive(world) - @classmethod def stage_post_fill(cls, world): - ShopSlotFill(world) + push_shop_inventories(world) @property def use_enemizer(self) -> bool: @@ -579,7 +576,7 @@ def generate_output(self, output_directory: str): @classmethod def stage_extend_hint_information(cls, world, hint_data: typing.Dict[int, typing.Dict[int, str]]): er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if - world.shuffle[player] != "vanilla" or world.retro_caves[player]} + world.entrance_shuffle[player] != "vanilla" or world.retro_caves[player]} for region in world.regions: if region.player in er_hint_data and region.locations: @@ -645,9 +642,9 @@ def stage_fill_hook(cls, world, progitempool, usefulitempool, filleritempool, fi trash_counts = {} for player in world.get_game_players("A Link to the Past"): if not world.ganonstower_vanilla[player] or \ - world.logic[player] in {'owglitches', 'hybridglitches', "nologic"}: + world.glitches_required[player] in {'overworld_glitches', 'hybrid_major_glitches', "no_logic"}: pass - elif 'triforcehunt' in world.goal[player] and ('local' in world.goal[player] or world.players == 1): + elif 'triforce_hunt' in world.goal[player].current_key and ('local' in world.goal[player].current_key or world.players == 1): trash_counts[player] = world.random.randint(world.crystals_needed_for_gt[player] * 2, world.crystals_needed_for_gt[player] * 4) else: @@ -681,35 +678,6 @@ def bool_to_text(variable: typing.Union[bool, str]) -> str: return variable return "Yes" if variable else "No" - spoiler_handle.write('Logic: %s\n' % self.multiworld.logic[self.player]) - spoiler_handle.write('Dark Room Logic: %s\n' % self.multiworld.dark_room_logic[self.player]) - spoiler_handle.write('Mode: %s\n' % self.multiworld.mode[self.player]) - spoiler_handle.write('Goal: %s\n' % self.multiworld.goal[self.player]) - if "triforce" in self.multiworld.goal[self.player]: # triforce hunt - spoiler_handle.write("Pieces available for Triforce: %s\n" % - self.multiworld.triforce_pieces_available[self.player]) - spoiler_handle.write("Pieces required for Triforce: %s\n" % - self.multiworld.triforce_pieces_required[self.player]) - spoiler_handle.write('Difficulty: %s\n' % self.multiworld.difficulty[self.player]) - spoiler_handle.write('Item Functionality: %s\n' % self.multiworld.item_functionality[self.player]) - spoiler_handle.write('Entrance Shuffle: %s\n' % self.multiworld.shuffle[self.player]) - if self.multiworld.shuffle[self.player] != "vanilla": - spoiler_handle.write('Entrance Shuffle Seed %s\n' % self.er_seed) - spoiler_handle.write('Shop inventory shuffle: %s\n' % - bool_to_text("i" in self.multiworld.shop_shuffle[self.player])) - spoiler_handle.write('Shop price shuffle: %s\n' % - bool_to_text("p" in self.multiworld.shop_shuffle[self.player])) - spoiler_handle.write('Shop upgrade shuffle: %s\n' % - bool_to_text("u" in self.multiworld.shop_shuffle[self.player])) - spoiler_handle.write('New Shop inventory: %s\n' % - bool_to_text("g" in self.multiworld.shop_shuffle[self.player] or - "f" in self.multiworld.shop_shuffle[self.player])) - spoiler_handle.write('Custom Potion Shop: %s\n' % - bool_to_text("w" in self.multiworld.shop_shuffle[self.player])) - spoiler_handle.write('Enemy health: %s\n' % self.multiworld.enemy_health[self.player]) - spoiler_handle.write('Enemy damage: %s\n' % self.multiworld.enemy_damage[self.player]) - 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") @@ -783,7 +751,7 @@ def build_shop_info(shop: Shop) -> typing.Dict[str, str]: if item["replacement"] is None: continue shop_data["item_{}".format(index)] +=\ - f", {item['replacement']} - {item['replacement_price']}" \ + f", {item['replacement']} - {item['replacement_price'] // price_rate_display.get(item['replacement_price_type'], 1)}" \ f" {price_type_display_name[item['replacement_price_type']]}" return shop_data @@ -796,10 +764,7 @@ def build_shop_info(shop: Shop) -> typing.Dict[str, str]: item))) def get_filler_item_name(self) -> str: - if self.multiworld.goal[self.player] == "icerodhunt": - item = "Nothing" - else: - item = self.multiworld.random.choice(extras_list) + item = self.multiworld.random.choice(extras_list) return GetBeemizerItem(self.multiworld, self.player, item) def get_pre_fill_items(self): @@ -819,20 +784,20 @@ def fill_slot_data(self): # for convenient auto-tracking of the generated settings and adjusting the tracker accordingly slot_options = ["crystals_needed_for_gt", "crystals_needed_for_ganon", "open_pyramid", - "bigkey_shuffle", "smallkey_shuffle", "compass_shuffle", "map_shuffle", + "big_key_shuffle", "small_key_shuffle", "compass_shuffle", "map_shuffle", "progressive", "swordless", "retro_bow", "retro_caves", "shop_item_slots", - "boss_shuffle", "pot_shuffle", "enemy_shuffle", "key_drop_shuffle"] + "boss_shuffle", "pot_shuffle", "enemy_shuffle", "key_drop_shuffle", "bombless_start", + "randomize_shop_inventories", "shuffle_shop_inventories", "shuffle_capacity_upgrades", + "entrance_shuffle", "dark_room_logic", "goal", "mode", + "triforce_pieces_mode", "triforce_pieces_percentage", "triforce_pieces_required", + "triforce_pieces_available", "triforce_pieces_extra", + ] slot_data = {option_name: getattr(self.multiworld, option_name)[self.player].value for option_name in slot_options} slot_data.update({ - 'mode': self.multiworld.mode[self.player], - 'goal': self.multiworld.goal[self.player], - 'dark_room_logic': self.multiworld.dark_room_logic[self.player], 'mm_medalion': self.multiworld.required_medallions[self.player][0], 'tr_medalion': self.multiworld.required_medallions[self.player][1], - 'shop_shuffle': self.multiworld.shop_shuffle[self.player], - 'entrance_shuffle': self.multiworld.shuffle[self.player], } ) return slot_data @@ -849,8 +814,8 @@ def get_same_seed(world, seed_def: tuple) -> str: class ALttPLogic(LogicMixin): def _lttp_has_key(self, item, player, count: int = 1): - if self.multiworld.logic[player] == 'nologic': + if self.multiworld.glitches_required[player] == 'no_logic': return True - if self.multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal: + if self.multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: return can_buy_unlimited(self, 'Small Key (Universal)', player) return self.prog_items[player][item] >= count diff --git a/worlds/alttp/test/dungeons/TestAgahnimsTower.py b/worlds/alttp/test/dungeons/TestAgahnimsTower.py index 94e785485882..c44a92be1ece 100644 --- a/worlds/alttp/test/dungeons/TestAgahnimsTower.py +++ b/worlds/alttp/test/dungeons/TestAgahnimsTower.py @@ -7,25 +7,25 @@ def testTower(self): self.starting_regions = ['Agahnims Tower'] self.run_tests([ ["Castle Tower - Room 03", False, []], - ["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Room 03", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Room 03", True, ['Progressive Sword']], ["Castle Tower - Dark Maze", False, []], ["Castle Tower - Dark Maze", False, [], ['Small Key (Agahnims Tower)']], ["Castle Tower - Dark Maze", False, [], ['Lamp']], - ["Castle Tower - Dark Maze", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Dark Maze", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Dark Maze", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Lamp']], ["Castle Tower - Dark Archer Key Drop", False, []], ["Castle Tower - Dark Archer Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']], ["Castle Tower - Dark Archer Key Drop", False, [], ['Lamp']], - ["Castle Tower - Dark Archer Key Drop", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Dark Archer Key Drop", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Dark Archer Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']], ["Castle Tower - Circle of Pots Key Drop", False, []], ["Castle Tower - Circle of Pots Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']], ["Castle Tower - Circle of Pots Key Drop", False, [], ['Lamp']], - ["Castle Tower - Circle of Pots Key Drop", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Circle of Pots Key Drop", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Circle of Pots Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']], ["Agahnim 1", False, []], diff --git a/worlds/alttp/test/dungeons/TestDarkPalace.py b/worlds/alttp/test/dungeons/TestDarkPalace.py index e3974e777da3..3912fbd282d9 100644 --- a/worlds/alttp/test/dungeons/TestDarkPalace.py +++ b/worlds/alttp/test/dungeons/TestDarkPalace.py @@ -11,29 +11,37 @@ def testDarkPalace(self): ["Palace of Darkness - The Arena - Ledge", False, []], ["Palace of Darkness - The Arena - Ledge", False, [], ['Progressive Bow']], - ["Palace of Darkness - The Arena - Ledge", True, ['Progressive Bow']], + ["Palace of Darkness - The Arena - Ledge", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Palace of Darkness - The Arena - Ledge", True, ['Progressive Bow', 'Bomb Upgrade (+5)']], ["Palace of Darkness - Map Chest", False, []], ["Palace of Darkness - Map Chest", False, [], ['Progressive Bow']], - ["Palace of Darkness - Map Chest", True, ['Progressive Bow']], + ["Palace of Darkness - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Palace of Darkness - Map Chest", True, ['Progressive Bow', 'Bomb Upgrade (+5)']], + ["Palace of Darkness - Map Chest", True, ['Progressive Bow', 'Pegasus Boots']], #Lower requirement for self-locking key #No lower requirement when bow/hammer is out of logic ["Palace of Darkness - Big Key Chest", False, []], ["Palace of Darkness - Big Key Chest", False, [key]*5, [key]], - ["Palace of Darkness - Big Key Chest", True, [key]*6], + ["Palace of Darkness - Big Key Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Palace of Darkness - Big Key Chest", True, [key]*6 + ['Bomb Upgrade (+5)']], ["Palace of Darkness - The Arena - Bridge", False, []], ["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Progressive Bow']], ["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Hammer']], + ["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], ["Palace of Darkness - The Arena - Bridge", True, [key]], - ["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer']], + ["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer', 'Bomb Upgrade (+5)']], + ["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer', 'Pegasus Boots']], ["Palace of Darkness - Stalfos Basement", False, []], ["Palace of Darkness - Stalfos Basement", False, [], [key, 'Progressive Bow']], ["Palace of Darkness - Stalfos Basement", False, [], [key, 'Hammer']], + ["Palace of Darkness - Stalfos Basement", False, [], [key, 'Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], ["Palace of Darkness - Stalfos Basement", True, [key]], - ["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer']], + ["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer', 'Bomb Upgrade (+5)']], + ["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer', 'Pegasus Boots']], ["Palace of Darkness - Compass Chest", False, []], ["Palace of Darkness - Compass Chest", False, [key]*3, [key]], @@ -67,8 +75,9 @@ def testDarkPalace(self): ["Palace of Darkness - Big Chest", False, []], ["Palace of Darkness - Big Chest", False, [], ['Lamp']], ["Palace of Darkness - Big Chest", False, [], ['Big Key (Palace of Darkness)']], + ["Palace of Darkness - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], ["Palace of Darkness - Big Chest", False, [key]*5, [key]], - ["Palace of Darkness - Big Chest", True, ['Lamp', 'Big Key (Palace of Darkness)'] + [key]*6], + ["Palace of Darkness - Big Chest", True, ['Bomb Upgrade (+5)', 'Lamp', 'Big Key (Palace of Darkness)'] + [key]*6], ["Palace of Darkness - Boss", False, []], ["Palace of Darkness - Boss", False, [], ['Lamp']], diff --git a/worlds/alttp/test/dungeons/TestDungeon.py b/worlds/alttp/test/dungeons/TestDungeon.py index 8ca2791dcfe4..1f8288ace07f 100644 --- a/worlds/alttp/test/dungeons/TestDungeon.py +++ b/worlds/alttp/test/dungeons/TestDungeon.py @@ -14,6 +14,8 @@ def setUp(self): self.starting_regions = [] # Where to start exploring self.remove_exits = [] # Block dungeon exits self.multiworld.difficulty_requirements[1] = difficulties['normal'] + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True create_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() create_shops(self.multiworld, 1) diff --git a/worlds/alttp/test/dungeons/TestEasternPalace.py b/worlds/alttp/test/dungeons/TestEasternPalace.py index 35c1b9928394..c1a978343b84 100644 --- a/worlds/alttp/test/dungeons/TestEasternPalace.py +++ b/worlds/alttp/test/dungeons/TestEasternPalace.py @@ -18,8 +18,8 @@ def testEastern(self): ["Eastern Palace - Big Key Chest", False, []], ["Eastern Palace - Big Key Chest", False, [], ['Lamp']], - ["Eastern Palace - Big Key Chest", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)']], - ["Eastern Palace - Big Key Chest", True, ['Lamp', 'Big Key (Eastern Palace)']], + ["Eastern Palace - Big Key Chest", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)', 'Progressive Sword']], + ["Eastern Palace - Big Key Chest", True, ['Lamp', 'Big Key (Eastern Palace)', 'Progressive Sword']], #@todo: Advanced? ["Eastern Palace - Boss", False, []], diff --git a/worlds/alttp/test/dungeons/TestGanonsTower.py b/worlds/alttp/test/dungeons/TestGanonsTower.py index d22dc92b366f..98bc6fa552e2 100644 --- a/worlds/alttp/test/dungeons/TestGanonsTower.py +++ b/worlds/alttp/test/dungeons/TestGanonsTower.py @@ -103,16 +103,16 @@ def testGanonsTower(self): ["Ganons Tower - Compass Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']], ["Ganons Tower - Big Key Chest", False, []], - ["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], - ["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + ["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], ["Ganons Tower - Big Key Room - Left", False, []], - ["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], - ["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + ["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], ["Ganons Tower - Big Key Room - Right", False, []], - ["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], - ["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + ["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], ["Ganons Tower - Mini Helmasaur Room - Left", False, []], ["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Progressive Bow']], diff --git a/worlds/alttp/test/dungeons/TestIcePalace.py b/worlds/alttp/test/dungeons/TestIcePalace.py index edc9f1fbae9e..7a15c5c09718 100644 --- a/worlds/alttp/test/dungeons/TestIcePalace.py +++ b/worlds/alttp/test/dungeons/TestIcePalace.py @@ -11,8 +11,9 @@ def testIcePalace(self): ["Ice Palace - Big Key Chest", False, [], ['Progressive Glove']], ["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], - ["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Big Key Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], #@todo: Change from item randomizer - Right side key door is only in logic if big key is in there #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], @@ -30,8 +31,9 @@ def testIcePalace(self): ["Ice Palace - Map Chest", False, [], ['Progressive Glove']], ["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Map Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], - ["Ice Palace - Map Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cape', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], @@ -40,8 +42,9 @@ def testIcePalace(self): ["Ice Palace - Spike Room", False, []], ["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Spike Room", True, ['Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']], - ["Ice Palace - Spike Room", True, ['Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Spike Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']], #["Ice Palace - Spike Room", True, ['Cape', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], #["Ice Palace - Spike Room", True, ['Cape', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], #["Ice Palace - Spike Room", True, ['Cane of Byrna', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], @@ -50,21 +53,24 @@ def testIcePalace(self): ["Ice Palace - Freezor Chest", False, []], ["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Freezor Chest", True, ['Fire Rod']], - ["Ice Palace - Freezor Chest", True, ['Bombos', 'Progressive Sword']], + ["Ice Palace - Freezor Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Fire Rod']], + ["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']], ["Ice Palace - Iced T Room", False, []], ["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Iced T Room", True, ['Fire Rod']], - ["Ice Palace - Iced T Room", True, ['Bombos', 'Progressive Sword']], + ["Ice Palace - Iced T Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Fire Rod']], + ["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']], ["Ice Palace - Big Chest", False, []], ["Ice Palace - Big Chest", False, [], ['Big Key (Ice Palace)']], ["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Progressive Sword']], - ["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Fire Rod']], - ["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']], + ["Ice Palace - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Fire Rod']], + ["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']], ["Ice Palace - Boss", False, []], ["Ice Palace - Boss", False, [], ['Hammer']], @@ -72,9 +78,10 @@ def testIcePalace(self): ["Ice Palace - Boss", False, [], ['Big Key (Ice Palace)']], ["Ice Palace - Boss", False, [], ['Fire Rod', 'Bombos']], ["Ice Palace - Boss", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], # need hookshot now to reach the right side for the 6th key - ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']], - ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']], - ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']], - ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']], + ["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']], + ["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']], + ["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']], + ["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']], ]) \ No newline at end of file diff --git a/worlds/alttp/test/dungeons/TestMiseryMire.py b/worlds/alttp/test/dungeons/TestMiseryMire.py index ea5fb288450d..6cbf42922fa4 100644 --- a/worlds/alttp/test/dungeons/TestMiseryMire.py +++ b/worlds/alttp/test/dungeons/TestMiseryMire.py @@ -78,7 +78,8 @@ def testMiseryMire(self): ["Misery Mire - Boss", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow']], ["Misery Mire - Boss", False, [], ['Big Key (Misery Mire)']], ["Misery Mire - Boss", False, [], ['Pegasus Boots', 'Hookshot']], - ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']], - ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']], - ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']], + ["Misery Mire - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']], + ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']], ]) \ No newline at end of file diff --git a/worlds/alttp/test/dungeons/TestSkullWoods.py b/worlds/alttp/test/dungeons/TestSkullWoods.py index 7f97c4d2f823..55c8d2e29a21 100644 --- a/worlds/alttp/test/dungeons/TestSkullWoods.py +++ b/worlds/alttp/test/dungeons/TestSkullWoods.py @@ -8,7 +8,8 @@ def testSkullWoodsFrontAllEntrances(self): self.run_tests([ ["Skull Woods - Big Chest", False, []], ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], - ["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']], ["Skull Woods - Compass Chest", True, []], @@ -64,7 +65,8 @@ def testSkullWoodsBackOnly(self): self.run_tests([ ["Skull Woods - Big Chest", False, []], ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], - ["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']], ["Skull Woods - Compass Chest", False, []], ["Skull Woods - Compass Chest", False, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], diff --git a/worlds/alttp/test/dungeons/TestSwampPalace.py b/worlds/alttp/test/dungeons/TestSwampPalace.py index 51440f6ccc4d..bddf40616f18 100644 --- a/worlds/alttp/test/dungeons/TestSwampPalace.py +++ b/worlds/alttp/test/dungeons/TestSwampPalace.py @@ -30,7 +30,8 @@ def testSwampPalace(self): ["Swamp Palace - Map Chest", False, [], ['Flippers']], ["Swamp Palace - Map Chest", False, [], ['Open Floodgate']], ["Swamp Palace - Map Chest", False, [], ['Small Key (Swamp Palace)']], - ["Swamp Palace - Map Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers']], + ["Swamp Palace - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Swamp Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers']], ["Swamp Palace - West Chest", False, []], ["Swamp Palace - West Chest", False, [], ['Flippers']], diff --git a/worlds/alttp/test/dungeons/TestThievesTown.py b/worlds/alttp/test/dungeons/TestThievesTown.py index 01f1570a2581..752b5305772a 100644 --- a/worlds/alttp/test/dungeons/TestThievesTown.py +++ b/worlds/alttp/test/dungeons/TestThievesTown.py @@ -41,8 +41,9 @@ def testThievesTown(self): ["Thieves' Town - Boss", False, []], ["Thieves' Town - Boss", False, [], ['Big Key (Thieves Town)']], ["Thieves' Town - Boss", False, [], ['Hammer', 'Progressive Sword', 'Cane of Somaria', 'Cane of Byrna']], - ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']], - ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']], - ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']], - ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']], + ["Thieves' Town - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']], + ["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']], + ["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']], + ["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']], ]) \ No newline at end of file diff --git a/worlds/alttp/test/dungeons/TestTowerOfHera.py b/worlds/alttp/test/dungeons/TestTowerOfHera.py index 04685a66a876..3299e20291b0 100644 --- a/worlds/alttp/test/dungeons/TestTowerOfHera.py +++ b/worlds/alttp/test/dungeons/TestTowerOfHera.py @@ -18,11 +18,11 @@ def testTowerOfHera(self): ["Tower of Hera - Compass Chest", False, []], ["Tower of Hera - Compass Chest", False, [], ['Big Key (Tower of Hera)']], - ["Tower of Hera - Compass Chest", True, ['Big Key (Tower of Hera)']], + ["Tower of Hera - Compass Chest", True, ['Big Key (Tower of Hera)', 'Progressive Sword']], ["Tower of Hera - Big Chest", False, []], ["Tower of Hera - Big Chest", False, [], ['Big Key (Tower of Hera)']], - ["Tower of Hera - Big Chest", True, ['Big Key (Tower of Hera)']], + ["Tower of Hera - Big Chest", True, ['Big Key (Tower of Hera)', 'Progressive Sword']], ["Tower of Hera - Boss", False, []], ["Tower of Hera - Boss", False, [], ['Big Key (Tower of Hera)']], diff --git a/worlds/alttp/test/inverted/TestInverted.py b/worlds/alttp/test/inverted/TestInverted.py index f5608ba07b2d..f2c585e46500 100644 --- a/worlds/alttp/test/inverted/TestInverted.py +++ b/worlds/alttp/test/inverted/TestInverted.py @@ -14,7 +14,9 @@ class TestInverted(TestBase, LTTPTestBase): def setUp(self): self.world_setup() self.multiworld.difficulty_requirements[1] = difficulties['normal'] - self.multiworld.mode[1] = "inverted" + self.multiworld.mode[1].value = 2 + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True create_inverted_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() create_shops(self.multiworld, 1) diff --git a/worlds/alttp/test/inverted/TestInvertedBombRules.py b/worlds/alttp/test/inverted/TestInvertedBombRules.py index d9eacb5ad98b..83a25812c9b6 100644 --- a/worlds/alttp/test/inverted/TestInvertedBombRules.py +++ b/worlds/alttp/test/inverted/TestInvertedBombRules.py @@ -11,8 +11,8 @@ class TestInvertedBombRules(LTTPTestBase): def setUp(self): self.world_setup() - self.multiworld.mode[1] = "inverted" self.multiworld.difficulty_requirements[1] = difficulties['normal'] + self.multiworld.mode[1].value = 2 create_inverted_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() diff --git a/worlds/alttp/test/inverted/TestInvertedDarkWorld.py b/worlds/alttp/test/inverted/TestInvertedDarkWorld.py index 710ee07f2b6d..16b837ee6556 100644 --- a/worlds/alttp/test/inverted/TestInvertedDarkWorld.py +++ b/worlds/alttp/test/inverted/TestInvertedDarkWorld.py @@ -5,7 +5,8 @@ class TestInvertedDarkWorld(TestInverted): def testNorthWest(self): self.run_location_tests([ - ["Brewery", True, []], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Brewery", True, ['Bomb Upgrade (+5)']], ["C-Shaped House", True, []], @@ -77,15 +78,16 @@ def testNorthEast(self): def testSouth(self): self.run_location_tests([ - ["Hype Cave - Top", True, []], - - ["Hype Cave - Middle Right", True, []], - - ["Hype Cave - Middle Left", True, []], - - ["Hype Cave - Bottom", True, []], - - ["Hype Cave - Generous Guy", True, []], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']], ["Stumpy", True, []], diff --git a/worlds/alttp/test/inverted/TestInvertedDeathMountain.py b/worlds/alttp/test/inverted/TestInvertedDeathMountain.py index aedec2a1daa6..605a9dc3f3a9 100644 --- a/worlds/alttp/test/inverted/TestInvertedDeathMountain.py +++ b/worlds/alttp/test/inverted/TestInvertedDeathMountain.py @@ -40,10 +40,12 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']], + ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']], + ["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Moon Pearl']], @@ -52,10 +54,12 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']], + ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']], + ["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']], @@ -64,10 +68,12 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Middle", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']], + ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']], + ["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Moon Pearl']], @@ -76,10 +82,12 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']], + ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']], + ["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']], @@ -88,10 +96,12 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']], + ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']], + ["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Moon Pearl']], @@ -100,10 +110,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Upper - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Moon Pearl']], @@ -112,20 +123,22 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Upper - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Mimic Cave", False, []], ["Mimic Cave", False, [], ['Moon Pearl']], ["Mimic Cave", False, [], ['Hammer']], ["Mimic Cave", False, [], ['Progressive Glove', 'Flute']], ["Mimic Cave", False, [], ['Lamp', 'Flute']], - ["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Hammer', 'Hookshot']], - ["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], - ["Mimic Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], - ["Mimic Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], + ["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Bow', 'Cane of Somaria', 'Progressive Sword']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Flute', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Progressive Bow', 'Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], + ["Mimic Cave", True, ['Cane of Somaria', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], ["Ether Tablet", False, []], ["Ether Tablet", False, [], ['Moon Pearl']], diff --git a/worlds/alttp/test/inverted/TestInvertedLightWorld.py b/worlds/alttp/test/inverted/TestInvertedLightWorld.py index 9d4b9099daae..77af09317241 100644 --- a/worlds/alttp/test/inverted/TestInvertedLightWorld.py +++ b/worlds/alttp/test/inverted/TestInvertedLightWorld.py @@ -44,15 +44,17 @@ def testKakariko(self): ["Chicken House", False, []], ["Chicken House", False, [], ['Moon Pearl']], - ["Chicken House", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Kakariko Well - Top", False, []], ["Kakariko Well - Top", False, [], ['Moon Pearl']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Kakariko Well - Left", False, []], ["Kakariko Well - Left", False, [], ['Moon Pearl']], @@ -80,9 +82,10 @@ def testKakariko(self): ["Blind's Hideout - Top", False, []], ["Blind's Hideout - Top", False, [], ['Moon Pearl']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Blind's Hideout - Left", False, []], ["Blind's Hideout - Left", False, [], ['Moon Pearl']], @@ -161,9 +164,10 @@ def testKakariko(self): ["Maze Race", False, []], ["Maze Race", False, [], ['Moon Pearl']], - ["Maze Race", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']], + ["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ]) def testSouthLightWorld(self): @@ -184,9 +188,10 @@ def testSouthLightWorld(self): ["Aginah's Cave", False, []], ["Aginah's Cave", False, [], ['Moon Pearl']], - ["Aginah's Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Bombos Tablet", False, []], ["Bombos Tablet", False, ['Progressive Sword'], ['Progressive Sword']], @@ -212,39 +217,45 @@ def testSouthLightWorld(self): ["Mini Moldorm Cave - Far Left", False, []], ["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']], ["Mini Moldorm Cave - Left", False, []], ["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']], ["Mini Moldorm Cave - Generous Guy", False, []], ["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']], ["Mini Moldorm Cave - Right", False, []], ["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']], ["Mini Moldorm Cave - Far Right", False, []], ["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']], ["Ice Rod Cave", False, []], ["Ice Rod Cave", False, [], ['Moon Pearl']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ]) def testZoraArea(self): @@ -302,21 +313,24 @@ def testLightWorld(self): ["Sahasrahla's Hut - Left", False, []], ["Sahasrahla's Hut - Left", False, [], ['Moon Pearl']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Sahasrahla's Hut - Middle", False, []], ["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Sahasrahla's Hut - Right", False, []], ["Sahasrahla's Hut - Right", False, [], ['Moon Pearl']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Sahasrahla", False, []], ["Sahasrahla", False, [], ['Green Pendant']], @@ -346,9 +360,10 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Moon Pearl']], - ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Graveyard Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], ["Potion Shop", False, []], ["Potion Shop", False, [], ['Mushroom']], diff --git a/worlds/alttp/test/inverted/TestInvertedTurtleRock.py b/worlds/alttp/test/inverted/TestInvertedTurtleRock.py index fe8979c1ef02..f3698c90ff06 100644 --- a/worlds/alttp/test/inverted/TestInvertedTurtleRock.py +++ b/worlds/alttp/test/inverted/TestInvertedTurtleRock.py @@ -21,10 +21,10 @@ def testTurtleRock(self): ["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], ["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']], - ["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']], + ["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Fire Rod']], ["Turtle Rock - Roller Room - Left", False, []], ["Turtle Rock - Roller Room - Left", False, [], ['Cane of Somaria']], @@ -54,8 +54,8 @@ def testTurtleRock(self): ["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']], ["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']], ["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], @@ -68,10 +68,10 @@ def testTurtleRock(self): ["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], # Mirror in from ledge, use left side entrance, have enough keys to get to the chest - ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Crystaroller Room", False, []], ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], @@ -102,8 +102,11 @@ def testTurtleRock(self): ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']] + ]) + + def testEyeBridge(self): for location in ["Turtle Rock - Eye Bridge - Top Right", "Turtle Rock - Eye Bridge - Top Left", "Turtle Rock - Eye Bridge - Bottom Right", "Turtle Rock - Eye Bridge - Bottom Left"]: diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedDarkWorld.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedDarkWorld.py index 69f564489700..dd4a74b6c4d2 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedDarkWorld.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedDarkWorld.py @@ -5,7 +5,8 @@ class TestInvertedDarkWorld(TestInvertedMinor): def testNorthWest(self): self.run_location_tests([ - ["Brewery", True, []], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Brewery", True, ['Bomb Upgrade (+5)']], ["C-Shaped House", True, []], @@ -67,15 +68,16 @@ def testNorthEast(self): def testSouth(self): self.run_location_tests([ - ["Hype Cave - Top", True, []], - - ["Hype Cave - Middle Right", True, []], - - ["Hype Cave - Middle Left", True, []], - - ["Hype Cave - Bottom", True, []], - - ["Hype Cave - Generous Guy", True, []], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']], ["Stumpy", True, []], diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedDeathMountain.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedDeathMountain.py index c68a8e5f0c89..c189d107d976 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedDeathMountain.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedDeathMountain.py @@ -40,10 +40,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']], + ["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Moon Pearl']], @@ -52,10 +53,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']], + ["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']], @@ -64,10 +66,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Middle", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']], + ["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Moon Pearl']], @@ -76,10 +79,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']], + ["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']], @@ -88,10 +92,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']], + ["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Moon Pearl']], @@ -100,10 +105,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Upper - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Moon Pearl']], @@ -112,20 +118,21 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], ["Paradox Cave Upper - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], ["Mimic Cave", False, []], ["Mimic Cave", False, [], ['Moon Pearl']], ["Mimic Cave", False, [], ['Hammer']], ["Mimic Cave", False, [], ['Progressive Glove', 'Flute']], ["Mimic Cave", False, [], ['Lamp', 'Flute']], - ["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Hammer', 'Hookshot']], - ["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], - ["Mimic Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], - ["Mimic Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Flute', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Progressive Sword', 'Progressive Sword', 'Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], + ["Mimic Cave", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Cane of Somaria', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], ["Ether Tablet", False, []], ["Ether Tablet", False, [], ['Moon Pearl']], diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedLightWorld.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedLightWorld.py index 376e7b4bec49..086c1c92b5dd 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedLightWorld.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedLightWorld.py @@ -43,16 +43,18 @@ def testKakariko(self): ["Chicken House", False, []], ["Chicken House", False, [], ['Moon Pearl']], - ["Chicken House", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Chicken House", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], # top can't be bombed as super bunny and needs Moon Pearl ["Kakariko Well - Top", False, []], ["Kakariko Well - Top", False, [], ['Moon Pearl']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Kakariko Well - Top", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Kakariko Well - Left", False, []], ["Kakariko Well - Left", True, ['Beat Agahnim 1']], @@ -76,9 +78,10 @@ def testKakariko(self): ["Blind's Hideout - Top", False, []], ["Blind's Hideout - Top", False, [], ['Moon Pearl', 'Magic Mirror']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Blind's Hideout - Top", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Blind's Hideout - Left", False, []], ["Blind's Hideout - Left", False, [], ['Moon Pearl', 'Magic Mirror']], @@ -157,9 +160,10 @@ def testKakariko(self): ["Maze Race", False, []], ["Maze Race", False, [], ['Moon Pearl']], - ["Maze Race", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Maze Race", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)', 'Pegasus Boots']], + ["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ]) def testSouthLightWorld(self): @@ -179,9 +183,10 @@ def testSouthLightWorld(self): ["Aginah's Cave", False, []], ["Aginah's Cave", False, [], ['Moon Pearl']], - ["Aginah's Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Bombos Tablet", False, []], ["Bombos Tablet", False, ['Progressive Sword'], ['Progressive Sword']], @@ -209,39 +214,45 @@ def testSouthLightWorld(self): ["Mini Moldorm Cave - Far Left", False, []], ["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Mini Moldorm Cave - Left", False, []], ["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Mini Moldorm Cave - Generous Guy", False, []], ["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Mini Moldorm Cave - Right", False, []], ["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Mini Moldorm Cave - Far Right", False, []], ["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ["Ice Rod Cave", False, []], ["Ice Rod Cave", False, [], ['Moon Pearl']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], ]) def testZoraArea(self): @@ -297,25 +308,28 @@ def testLightWorld(self): ["Sahasrahla's Hut - Left", False, []], ["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Magic Mirror']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Left", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Left", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], # super bunny bonk ["Sahasrahla's Hut - Left", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']], ["Sahasrahla's Hut - Middle", False, []], ["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Magic Mirror']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], # super bunny bonk ["Sahasrahla's Hut - Middle", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']], ["Sahasrahla's Hut - Right", False, []], ["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Magic Mirror']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], # super bunny bonk ["Sahasrahla's Hut - Right", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']], @@ -347,9 +361,10 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Moon Pearl']], - ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Graveyard Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], ["Potion Shop", False, []], ["Potion Shop", False, [], ['Mushroom']], diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py index 33e582298185..0219332e0748 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py @@ -13,8 +13,10 @@ class TestInvertedMinor(TestBase, LTTPTestBase): def setUp(self): self.world_setup() - self.multiworld.mode[1] = "inverted" - self.multiworld.logic[1] = "minorglitches" + self.multiworld.mode[1].value = 2 + self.multiworld.glitches_required[1] = "minor_glitches" + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedTurtleRock.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedTurtleRock.py index d7b5c9f79788..3c75a2c3684b 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedTurtleRock.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedTurtleRock.py @@ -22,10 +22,10 @@ def testTurtleRock(self): ["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], ["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']], - ["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']], + ["Turtle Rock - Chain Chomps", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']], ["Turtle Rock - Roller Room - Left", False, []], ["Turtle Rock - Roller Room - Left", False, [], ['Cane of Somaria']], @@ -55,8 +55,8 @@ def testTurtleRock(self): ["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']], ["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']], ["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']], ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], @@ -69,10 +69,10 @@ def testTurtleRock(self): ["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], # Mirror in from ledge, use left side entrance, have enough keys to get to the chest - ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], - ["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Turtle Rock - Crystaroller Room", False, []], ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], @@ -98,7 +98,7 @@ def testTurtleRock(self): ["Turtle Rock - Boss", False, [], ['Big Key (Turtle Rock)']], ["Turtle Rock - Boss", False, [], ['Magic Mirror', 'Lamp']], ["Turtle Rock - Boss", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']], - ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], diff --git a/worlds/alttp/test/inverted_owg/TestDarkWorld.py b/worlds/alttp/test/inverted_owg/TestDarkWorld.py index e7e720d2b853..8fb234f0b50a 100644 --- a/worlds/alttp/test/inverted_owg/TestDarkWorld.py +++ b/worlds/alttp/test/inverted_owg/TestDarkWorld.py @@ -5,15 +5,16 @@ class TestDarkWorld(TestInvertedOWG): def testSouthDarkWorld(self): self.run_location_tests([ - ["Hype Cave - Top", True, []], - - ["Hype Cave - Middle Right", True, []], - - ["Hype Cave - Middle Left", True, []], - - ["Hype Cave - Bottom", True, []], - - ["Hype Cave - Generous Guy", True, []], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']], ["Stumpy", True, []], @@ -22,7 +23,8 @@ def testSouthDarkWorld(self): def testWestDarkWorld(self): self.run_location_tests([ - ["Brewery", True, []], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Brewery", True, ['Bomb Upgrade (+5)']], ["C-Shaped House", True, []], diff --git a/worlds/alttp/test/inverted_owg/TestDeathMountain.py b/worlds/alttp/test/inverted_owg/TestDeathMountain.py index 79796a7aeb1e..b509643d0c5e 100644 --- a/worlds/alttp/test/inverted_owg/TestDeathMountain.py +++ b/worlds/alttp/test/inverted_owg/TestDeathMountain.py @@ -24,36 +24,38 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, []], ["Paradox Cave Lower - Far Left", False, [], ['Moon Pearl']], - ["Paradox Cave Lower - Far Left", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Moon Pearl']], - ["Paradox Cave Lower - Left", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']], - ["Paradox Cave Lower - Middle", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Moon Pearl']], - ["Paradox Cave Lower - Right", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']], - ["Paradox Cave Lower - Far Right", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Moon Pearl']], - ["Paradox Cave Upper - Left", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Moon Pearl']], - ["Paradox Cave Upper - Right", True, ['Moon Pearl', 'Pegasus Boots']], + ["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Mimic Cave", False, []], ["Mimic Cave", False, [], ['Moon Pearl']], ["Mimic Cave", False, [], ['Hammer']], - ["Mimic Cave", True, ['Moon Pearl', 'Hammer', 'Pegasus Boots']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Hammer', 'Pegasus Boots']], ["Ether Tablet", False, []], ["Ether Tablet", False, ['Progressive Sword'], ['Progressive Sword']], diff --git a/worlds/alttp/test/inverted_owg/TestDungeons.py b/worlds/alttp/test/inverted_owg/TestDungeons.py index f5d07544aaea..0d8445895eca 100644 --- a/worlds/alttp/test/inverted_owg/TestDungeons.py +++ b/worlds/alttp/test/inverted_owg/TestDungeons.py @@ -13,16 +13,16 @@ def testFirstDungeonChests(self): ["Sanctuary", False, []], ["Sanctuary", False, ['Beat Agahnim 1']], ["Sanctuary", True, ['Magic Mirror', 'Beat Agahnim 1']], - ["Sanctuary", True, ['Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)']], + ["Sanctuary", True, ['Progressive Sword', 'Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)']], ["Sanctuary", True, ['Moon Pearl', 'Pegasus Boots']], ["Sanctuary", True, ['Magic Mirror', 'Pegasus Boots']], ["Sewers - Secret Room - Left", False, []], ["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Progressive Glove', 'Pegasus Boots']], - ["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']], - ["Sewers - Secret Room - Left", True, - ['Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']], - ["Sewers - Secret Room - Left", True, ['Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']], + ["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']], + ["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']], + ["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']], + ["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+10)', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']], ["Eastern Palace - Compass Chest", False, []], ["Eastern Palace - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots']], @@ -45,7 +45,7 @@ def testFirstDungeonChests(self): ["Tower of Hera - Basement Cage", True, ['Pegasus Boots', 'Moon Pearl']], ["Castle Tower - Room 03", False, []], - ["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Room 03", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Room 03", True, ['Pegasus Boots', 'Progressive Sword']], ["Castle Tower - Room 03", True, ['Pegasus Boots', 'Progressive Bow']], @@ -62,7 +62,8 @@ def testFirstDungeonChests(self): ["Skull Woods - Big Chest", False, []], ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], - ["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']], ["Skull Woods - Big Key Chest", True, []], @@ -89,7 +90,16 @@ def testFirstDungeonChests(self): ["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Quake', 'Progressive Sword', 'Cane of Somaria']], ["Turtle Rock - Chain Chomps", False, []], - ["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Hookshot', 'Progressive Sword', 'Progressive Bow', 'Blue Boomerang', 'Red Boomerang', 'Cane of Somaria', 'Fire Rod', 'Ice Rod']], + ["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Hookshot', 'Pegasus Boots']], + ["Turtle Rock - Chain Chomps", True, ['Progressive Bow', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Blue Boomerang', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Red Boomerang', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Cane of Somaria', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Fire Rod', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Ice Rod', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']], + ["Turtle Rock - Chain Chomps", True, ['Progressive Sword', 'Progressive Sword', 'Pegasus Boots']], ["Turtle Rock - Crystaroller Room", False, []], ["Turtle Rock - Crystaroller Room", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl', 'Big Key (Turtle Rock)']], diff --git a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py index a4e84fce9b62..849f06098a44 100644 --- a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py +++ b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py @@ -13,8 +13,10 @@ class TestInvertedOWG(TestBase, LTTPTestBase): def setUp(self): self.world_setup() - self.multiworld.logic[1] = "owglitches" - self.multiworld.mode[1] = "inverted" + self.multiworld.glitches_required[1] = "overworld_glitches" + self.multiworld.mode[1].value = 2 + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() diff --git a/worlds/alttp/test/inverted_owg/TestLightWorld.py b/worlds/alttp/test/inverted_owg/TestLightWorld.py index de92b4ef854d..bd18259bec3f 100644 --- a/worlds/alttp/test/inverted_owg/TestLightWorld.py +++ b/worlds/alttp/test/inverted_owg/TestLightWorld.py @@ -40,40 +40,46 @@ def testLightWorld(self): ["Chicken House", False, []], ["Chicken House", False, [], ['Moon Pearl']], - ["Chicken House", True, ['Moon Pearl', 'Pegasus Boots']], + ["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Aginah's Cave", False, []], ["Aginah's Cave", False, [], ['Moon Pearl']], - ["Aginah's Cave", True, ['Moon Pearl', 'Pegasus Boots']], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Sahasrahla's Hut - Left", False, []], ["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Magic Mirror']], ["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Pegasus Boots']], ["Sahasrahla's Hut - Left", True, ['Magic Mirror', 'Pegasus Boots']], ##todo: Damage boost superbunny not in logic #["Sahasrahla's Hut - Left", True, ['Beat Agahnim 1', 'Pegasus Boots']], - ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], ["Sahasrahla's Hut - Middle", False, []], ["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Magic Mirror']], ["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Pegasus Boots']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Pegasus Boots']], ["Sahasrahla's Hut - Middle", True, ['Magic Mirror', 'Pegasus Boots']], #["Sahasrahla's Hut - Middle", True, ['Beat Agahnim 1', 'Pegasus Boots']], - ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], ["Sahasrahla's Hut - Right", False, []], ["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Magic Mirror']], ["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Pegasus Boots']], ["Sahasrahla's Hut - Right", True, ['Magic Mirror', 'Pegasus Boots']], #["Sahasrahla's Hut - Right", True, ['Beat Agahnim 1', 'Pegasus Boots']], - ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']], ["Kakariko Well - Top", False, []], ["Kakariko Well - Top", False, [], ['Moon Pearl']], - ["Kakariko Well - Top", True, ['Moon Pearl', 'Pegasus Boots']], + ["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Kakariko Well - Left", False, []], ["Kakariko Well - Left", True, ['Moon Pearl', 'Pegasus Boots']], @@ -101,7 +107,8 @@ def testLightWorld(self): ["Blind's Hideout - Top", False, []], ["Blind's Hideout - Top", False, [], ['Moon Pearl']], - ["Blind's Hideout - Top", True, ['Moon Pearl', 'Pegasus Boots']], + ["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Blind's Hideout - Left", False, []], ["Blind's Hideout - Left", False, [], ['Moon Pearl', 'Magic Mirror']], @@ -134,27 +141,33 @@ def testLightWorld(self): ["Mini Moldorm Cave - Far Left", False, []], ["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Pegasus Boots']], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']], ["Mini Moldorm Cave - Left", False, []], ["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Pegasus Boots']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']], ["Mini Moldorm Cave - Right", False, []], ["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Pegasus Boots']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']], ["Mini Moldorm Cave - Far Right", False, []], ["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Pegasus Boots']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']], ["Mini Moldorm Cave - Generous Guy", False, []], ["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Pegasus Boots']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']], ["Ice Rod Cave", False, []], ["Ice Rod Cave", False, [], ['Moon Pearl']], - ["Ice Rod Cave", True, ['Moon Pearl', 'Pegasus Boots']], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], #I don't think so #["Ice Rod Cave", True, ['Magic Mirror', 'Pegasus Boots', 'BigRedBomb']], #["Ice Rod Cave", True, ['Magic Mirror', 'Beat Agahnim 1', 'BigRedBomb']], @@ -236,7 +249,8 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Moon Pearl']], - ["Graveyard Cave", True, ['Moon Pearl', 'Pegasus Boots']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], ["Checkerboard Cave", False, []], ["Checkerboard Cave", False, [], ['Progressive Glove']], diff --git a/worlds/alttp/test/minor_glitches/TestDarkWorld.py b/worlds/alttp/test/minor_glitches/TestDarkWorld.py index 3a6f97254c95..9b0e43ea9494 100644 --- a/worlds/alttp/test/minor_glitches/TestDarkWorld.py +++ b/worlds/alttp/test/minor_glitches/TestDarkWorld.py @@ -7,43 +7,48 @@ def testSouthDarkWorld(self): self.run_location_tests([ ["Hype Cave - Top", False, []], ["Hype Cave - Top", False, [], ['Moon Pearl']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Right", False, []], ["Hype Cave - Middle Right", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Left", False, []], ["Hype Cave - Middle Left", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Bottom", False, []], ["Hype Cave - Bottom", False, [], ['Moon Pearl']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Generous Guy", False, []], ["Hype Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Stumpy", False, []], ["Stumpy", False, [], ['Moon Pearl']], @@ -66,10 +71,11 @@ def testWestDarkWorld(self): self.run_location_tests([ ["Brewery", False, []], ["Brewery", False, [], ['Moon Pearl']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["C-Shaped House", False, []], ["C-Shaped House", False, [], ['Moon Pearl']], diff --git a/worlds/alttp/test/minor_glitches/TestDeathMountain.py b/worlds/alttp/test/minor_glitches/TestDeathMountain.py index 2603aaeb7b9e..4446ee7e8f88 100644 --- a/worlds/alttp/test/minor_glitches/TestDeathMountain.py +++ b/worlds/alttp/test/minor_glitches/TestDeathMountain.py @@ -48,7 +48,8 @@ def testEastDeathMountain(self): ["Mimic Cave", False, [], ['Moon Pearl']], ["Mimic Cave", False, [], ['Cane of Somaria']], ["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']], - ["Mimic Cave", True, ['Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Spiral Cave", False, []], ["Spiral Cave", False, [], ['Progressive Glove', 'Flute']], @@ -73,10 +74,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Progressive Glove', 'Flute']], @@ -87,10 +89,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Progressive Glove', 'Flute']], @@ -101,10 +104,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Progressive Glove', 'Flute']], @@ -115,10 +119,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Progressive Glove', 'Flute']], @@ -129,10 +134,11 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Progressive Glove', 'Flute']], @@ -143,10 +149,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Upper - Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Progressive Glove', 'Flute']], @@ -157,10 +164,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Upper - Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ]) def testWestDarkWorldDeathMountain(self): diff --git a/worlds/alttp/test/minor_glitches/TestLightWorld.py b/worlds/alttp/test/minor_glitches/TestLightWorld.py index bdfdc2349691..017f2d64a8f8 100644 --- a/worlds/alttp/test/minor_glitches/TestLightWorld.py +++ b/worlds/alttp/test/minor_glitches/TestLightWorld.py @@ -29,17 +29,21 @@ def testLightWorld(self): ["Kakariko Tavern", True, []], - ["Chicken House", True, []], + ["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)']], - ["Aginah's Cave", True, []], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)']], - ["Sahasrahla's Hut - Left", True, []], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots']], - ["Sahasrahla's Hut - Middle", True, []], - - ["Sahasrahla's Hut - Right", True, []], - - ["Kakariko Well - Top", True, []], + ["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']], ["Kakariko Well - Left", True, []], @@ -49,7 +53,8 @@ def testLightWorld(self): ["Kakariko Well - Bottom", True, []], - ["Blind's Hideout - Top", True, []], + ["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']], ["Blind's Hideout - Left", True, []], @@ -63,15 +68,19 @@ def testLightWorld(self): ["Bonk Rock Cave", False, [], ['Pegasus Boots']], ["Bonk Rock Cave", True, ['Pegasus Boots']], - ["Mini Moldorm Cave - Far Left", True, []], - - ["Mini Moldorm Cave - Left", True, []], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], - ["Mini Moldorm Cave - Right", True, []], - - ["Mini Moldorm Cave - Far Right", True, []], - - ["Ice Rod Cave", True, []], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)']], ["Bottle Merchant", True, []], @@ -131,11 +140,12 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Magic Mirror']], ["Graveyard Cave", False, [], ['Moon Pearl']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Checkerboard Cave", False, []], ["Checkerboard Cave", False, [], ['Progressive Glove']], @@ -143,8 +153,6 @@ def testLightWorld(self): ["Checkerboard Cave", False, [], ['Magic Mirror']], ["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Mini Moldorm Cave - Generous Guy", True, []], - ["Library", False, []], ["Library", False, [], ['Pegasus Boots']], ["Library", True, ['Pegasus Boots']], @@ -155,7 +163,10 @@ def testLightWorld(self): ["Potion Shop", False, [], ['Mushroom']], ["Potion Shop", True, ['Mushroom']], - ["Maze Race", True, []], + ["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']], + ["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Maze Race", True, ['Bomb Upgrade (+5)']], + ["Maze Race", True, ['Pegasus Boots']], ["Desert Ledge", False, []], ["Desert Ledge", False, [], ['Book of Mudora', 'Flute']], diff --git a/worlds/alttp/test/minor_glitches/TestMinor.py b/worlds/alttp/test/minor_glitches/TestMinor.py index d5cfd3095b9c..c7de74d3a6c3 100644 --- a/worlds/alttp/test/minor_glitches/TestMinor.py +++ b/worlds/alttp/test/minor_glitches/TestMinor.py @@ -10,7 +10,9 @@ class TestMinor(TestBase, LTTPTestBase): def setUp(self): self.world_setup() - self.multiworld.logic[1] = "minorglitches" + self.multiworld.glitches_required[1] = "minor_glitches" + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] self.multiworld.worlds[1].er_seed = 0 self.multiworld.worlds[1].create_regions() diff --git a/worlds/alttp/test/owg/TestDarkWorld.py b/worlds/alttp/test/owg/TestDarkWorld.py index 93324656bd93..c671f6485c92 100644 --- a/worlds/alttp/test/owg/TestDarkWorld.py +++ b/worlds/alttp/test/owg/TestDarkWorld.py @@ -7,48 +7,53 @@ def testSouthDarkWorld(self): self.run_location_tests([ ["Hype Cave - Top", False, []], ["Hype Cave - Top", False, [], ['Moon Pearl']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Pegasus Boots']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Right", False, []], ["Hype Cave - Middle Right", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Pegasus Boots']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Left", False, []], ["Hype Cave - Middle Left", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Pegasus Boots']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Bottom", False, []], ["Hype Cave - Bottom", False, [], ['Moon Pearl']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Pegasus Boots']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Generous Guy", False, []], ["Hype Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Pegasus Boots']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Stumpy", False, []], ["Stumpy", False, [], ['Moon Pearl']], @@ -129,13 +134,14 @@ def testWestDarkWorld(self): self.run_location_tests([ ["Brewery", False, []], ["Brewery", False, [], ['Moon Pearl']], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], ["Brewery", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot', 'Progressive Glove']], - ["Brewery", True, ['Moon Pearl', 'Pegasus Boots']], - ["Brewery", True, ['Moon Pearl', 'Flute', 'Magic Mirror']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Flute', 'Magic Mirror']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["C-Shaped House", False, []], ["C-Shaped House", False, [], ['Moon Pearl', 'Magic Mirror']], diff --git a/worlds/alttp/test/owg/TestDeathMountain.py b/worlds/alttp/test/owg/TestDeathMountain.py index 41031c65c593..0933b2881e2d 100644 --- a/worlds/alttp/test/owg/TestDeathMountain.py +++ b/worlds/alttp/test/owg/TestDeathMountain.py @@ -48,9 +48,10 @@ def testEastDeathMountain(self): ["Mimic Cave", False, [], ['Hammer']], ["Mimic Cave", False, [], ['Pegasus Boots', 'Flute', 'Lamp']], ["Mimic Cave", False, [], ['Pegasus Boots', 'Flute', 'Progressive Glove']], - ["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Pegasus Boots']], - ["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Progressive Glove', 'Lamp']], - ["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Flute']], + ["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Hookshot', 'Hammer']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Pegasus Boots']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Progressive Glove', 'Lamp']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Flute']], ["Spiral Cave", False, []], ["Spiral Cave", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], @@ -64,65 +65,72 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, []], ["Paradox Cave Lower - Far Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Lower - Far Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Pegasus Boots']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Left", True, ['Fire Rod', 'Pegasus Boots']], + ["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Lower - Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Pegasus Boots']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Left", True, ['Fire Rod', 'Pegasus Boots']], + ["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Lower - Middle", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Pegasus Boots']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Middle", True, ['Fire Rod', 'Pegasus Boots']], + ["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Lower - Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Pegasus Boots']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Right", True, ['Fire Rod', 'Pegasus Boots']], + ["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Lower - Far Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Pegasus Boots']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Right", True, ['Fire Rod', 'Pegasus Boots']], + ["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Upper - Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Pegasus Boots']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']], ["Paradox Cave Upper - Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Pegasus Boots']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror']], + ["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']], ]) def testWestDarkWorldDeathMountain(self): diff --git a/worlds/alttp/test/owg/TestDungeons.py b/worlds/alttp/test/owg/TestDungeons.py index 4f878969679a..f4688b7a35f9 100644 --- a/worlds/alttp/test/owg/TestDungeons.py +++ b/worlds/alttp/test/owg/TestDungeons.py @@ -6,13 +6,14 @@ class TestDungeons(TestVanillaOWG): def testFirstDungeonChests(self): self.run_location_tests([ ["Hyrule Castle - Map Chest", True, []], - ["Hyrule Castle - Map Guard Key Drop", True, []], + ["Hyrule Castle - Map Guard Key Drop", False, []], + ["Hyrule Castle - Map Guard Key Drop", True, ['Progressive Sword']], ["Sanctuary", True, []], ["Sewers - Secret Room - Left", False, []], - ["Sewers - Secret Room - Left", True, ['Progressive Glove']], - ["Sewers - Secret Room - Left", True, ['Lamp', 'Small Key (Hyrule Castle)']], + ["Sewers - Secret Room - Left", True, ['Pegasus Boots', 'Progressive Glove']], + ["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Bomb Upgrade (+5)', 'Lamp', 'Small Key (Hyrule Castle)']], ["Eastern Palace - Compass Chest", True, []], @@ -41,10 +42,9 @@ def testFirstDungeonChests(self): ["Castle Tower - Room 03", False, []], ["Castle Tower - Room 03", False, ['Progressive Sword'], ['Progressive Sword', 'Cape', 'Beat Agahnim 1']], - ["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], ["Castle Tower - Room 03", True, ['Progressive Sword', 'Progressive Sword']], - ["Castle Tower - Room 03", True, ['Cape', 'Progressive Bow']], - ["Castle Tower - Room 03", True, ['Beat Agahnim 1', 'Fire Rod']], + ["Castle Tower - Room 03", True, ['Progressive Sword', 'Cape']], + ["Castle Tower - Room 03", True, ['Progressive Sword', 'Beat Agahnim 1']], ["Palace of Darkness - Shooter Room", False, []], ["Palace of Darkness - Shooter Room", False, [], ['Moon Pearl']], @@ -69,9 +69,10 @@ def testFirstDungeonChests(self): ["Skull Woods - Big Chest", False, []], ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], #todo: Bomb Jump in logic? #["Skull Woods - Big Chest", True, ['Magic Mirror', 'Pegasus Boots', 'Big Key (Skull Woods)']], - ["Skull Woods - Big Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots', 'Big Key (Skull Woods)']], ["Skull Woods - Big Key Chest", False, []], ["Skull Woods - Big Key Chest", True, ['Magic Mirror', 'Pegasus Boots']], @@ -111,8 +112,8 @@ def testFirstDungeonChests(self): ["Turtle Rock - Chain Chomps", False, []], #todo: does clip require sword? #["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots']], - ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots', 'Progressive Sword']], - ["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror']], + ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots', 'Progressive Sword', 'Progressive Sword']], + ["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror', 'Progressive Bow']], ["Turtle Rock - Crystaroller Room", False, []], ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)']], diff --git a/worlds/alttp/test/owg/TestLightWorld.py b/worlds/alttp/test/owg/TestLightWorld.py index f3f1ba0c2703..84342a33c856 100644 --- a/worlds/alttp/test/owg/TestLightWorld.py +++ b/worlds/alttp/test/owg/TestLightWorld.py @@ -25,17 +25,21 @@ def testLightWorld(self): ["Kakariko Tavern", True, []], - ["Chicken House", True, []], + ["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)']], - ["Aginah's Cave", True, []], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)']], - ["Sahasrahla's Hut - Left", True, []], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots']], - ["Sahasrahla's Hut - Middle", True, []], - - ["Sahasrahla's Hut - Right", True, []], - - ["Kakariko Well - Top", True, []], + ["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']], ["Kakariko Well - Left", True, []], @@ -45,7 +49,8 @@ def testLightWorld(self): ["Kakariko Well - Bottom", True, []], - ["Blind's Hideout - Top", True, []], + ["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']], ["Blind's Hideout - Left", True, []], @@ -59,15 +64,19 @@ def testLightWorld(self): ["Bonk Rock Cave", False, [], ['Pegasus Boots']], ["Bonk Rock Cave", True, ['Pegasus Boots']], - ["Mini Moldorm Cave - Far Left", True, []], - - ["Mini Moldorm Cave - Left", True, []], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], - ["Mini Moldorm Cave - Right", True, []], - - ["Mini Moldorm Cave - Far Right", True, []], - - ["Ice Rod Cave", True, []], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)']], ["Bottle Merchant", True, []], @@ -126,12 +135,13 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Pegasus Boots', 'Magic Mirror']], ["Graveyard Cave", False, [], ['Pegasus Boots', 'Moon Pearl']], - ["Graveyard Cave", True, ['Pegasus Boots']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Checkerboard Cave", False, []], ["Checkerboard Cave", False, [], ['Progressive Glove']], @@ -140,8 +150,6 @@ def testLightWorld(self): ["Checkerboard Cave", True, ['Pegasus Boots', 'Progressive Glove']], ["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Mini Moldorm Cave - Generous Guy", True, []], - ["Library", False, []], ["Library", False, [], ['Pegasus Boots']], ["Library", True, ['Pegasus Boots']], @@ -152,7 +160,10 @@ def testLightWorld(self): ["Potion Shop", False, [], ['Mushroom']], ["Potion Shop", True, ['Mushroom']], - ["Maze Race", True, []], + ["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']], + ["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Maze Race", True, ['Bomb Upgrade (+5)']], + ["Maze Race", True, ['Pegasus Boots']], ["Desert Ledge", False, []], ["Desert Ledge", False, [], ['Pegasus Boots', 'Book of Mudora', 'Flute']], diff --git a/worlds/alttp/test/owg/TestVanillaOWG.py b/worlds/alttp/test/owg/TestVanillaOWG.py index 37b0b6ccb868..1f8f2707edaa 100644 --- a/worlds/alttp/test/owg/TestVanillaOWG.py +++ b/worlds/alttp/test/owg/TestVanillaOWG.py @@ -11,7 +11,9 @@ class TestVanillaOWG(TestBase, LTTPTestBase): def setUp(self): self.world_setup() self.multiworld.difficulty_requirements[1] = difficulties['normal'] - self.multiworld.logic[1] = "owglitches" + self.multiworld.glitches_required[1] = "overworld_glitches" + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.worlds[1].er_seed = 0 self.multiworld.worlds[1].create_regions() self.multiworld.worlds[1].create_items() diff --git a/worlds/alttp/test/vanilla/TestDarkWorld.py b/worlds/alttp/test/vanilla/TestDarkWorld.py index ecb3e5583098..8ff09c527de8 100644 --- a/worlds/alttp/test/vanilla/TestDarkWorld.py +++ b/worlds/alttp/test/vanilla/TestDarkWorld.py @@ -7,43 +7,48 @@ def testSouthDarkWorld(self): self.run_location_tests([ ["Hype Cave - Top", False, []], ["Hype Cave - Top", False, [], ['Moon Pearl']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Right", False, []], ["Hype Cave - Middle Right", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Middle Left", False, []], ["Hype Cave - Middle Left", False, [], ['Moon Pearl']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Bottom", False, []], ["Hype Cave - Bottom", False, [], ['Moon Pearl']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Hype Cave - Generous Guy", False, []], ["Hype Cave - Generous Guy", False, [], ['Moon Pearl']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Stumpy", False, []], ["Stumpy", False, [], ['Moon Pearl']], @@ -66,10 +71,11 @@ def testWestDarkWorld(self): self.run_location_tests([ ["Brewery", False, []], ["Brewery", False, [], ['Moon Pearl']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], - ["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["C-Shaped House", False, []], ["C-Shaped House", False, [], ['Moon Pearl']], diff --git a/worlds/alttp/test/vanilla/TestDeathMountain.py b/worlds/alttp/test/vanilla/TestDeathMountain.py index ecb3831f6ad1..d77f1a8dd274 100644 --- a/worlds/alttp/test/vanilla/TestDeathMountain.py +++ b/worlds/alttp/test/vanilla/TestDeathMountain.py @@ -48,7 +48,8 @@ def testEastDeathMountain(self): ["Mimic Cave", False, [], ['Moon Pearl']], ["Mimic Cave", False, [], ['Cane of Somaria']], ["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']], - ["Mimic Cave", True, ['Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], ["Spiral Cave", False, []], ["Spiral Cave", False, [], ['Progressive Glove', 'Flute']], @@ -73,10 +74,10 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Far Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']], ["Paradox Cave Lower - Left", False, []], ["Paradox Cave Lower - Left", False, [], ['Progressive Glove', 'Flute']], @@ -87,10 +88,10 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Cane of Somaria']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']], ["Paradox Cave Lower - Middle", False, []], ["Paradox Cave Lower - Middle", False, [], ['Progressive Glove', 'Flute']], @@ -101,10 +102,10 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Middle", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Cane of Somaria']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']], ["Paradox Cave Lower - Right", False, []], ["Paradox Cave Lower - Right", False, [], ['Progressive Glove', 'Flute']], @@ -115,10 +116,10 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Cane of Somaria']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']], ["Paradox Cave Lower - Far Right", False, []], ["Paradox Cave Lower - Far Right", False, [], ['Progressive Glove', 'Flute']], @@ -129,10 +130,10 @@ def testEastDeathMountain(self): ["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Lower - Far Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Cane of Somaria']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']], ["Paradox Cave Upper - Left", False, []], ["Paradox Cave Upper - Left", False, [], ['Progressive Glove', 'Flute']], @@ -143,10 +144,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Left", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Upper - Left", False, ['Flute', 'Hammer']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ["Paradox Cave Upper - Right", False, []], ["Paradox Cave Upper - Right", False, [], ['Progressive Glove', 'Flute']], @@ -157,10 +159,11 @@ def testEastDeathMountain(self): ["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot']], ["Paradox Cave Upper - Right", False, ['Flute', 'Magic Mirror']], ["Paradox Cave Upper - Right", False, ['Flute', 'Hammer']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], - ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], - ["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']], + ["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']], ]) def testWestDarkWorldDeathMountain(self): diff --git a/worlds/alttp/test/vanilla/TestLightWorld.py b/worlds/alttp/test/vanilla/TestLightWorld.py index 977e807290d1..6d9284aba0d3 100644 --- a/worlds/alttp/test/vanilla/TestLightWorld.py +++ b/worlds/alttp/test/vanilla/TestLightWorld.py @@ -29,17 +29,21 @@ def testLightWorld(self): ["Kakariko Tavern", True, []], - ["Chicken House", True, []], + ["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Chicken House", True, ['Bomb Upgrade (+5)']], - ["Aginah's Cave", True, []], + ["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Aginah's Cave", True, ['Bomb Upgrade (+5)']], - ["Sahasrahla's Hut - Left", True, []], + ["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']], + ["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']], + ["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']], + ["Sahasrahla's Hut - Right", True, ['Pegasus Boots']], - ["Sahasrahla's Hut - Middle", True, []], - - ["Sahasrahla's Hut - Right", True, []], - - ["Kakariko Well - Top", True, []], + ["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']], ["Kakariko Well - Left", True, []], @@ -49,7 +53,8 @@ def testLightWorld(self): ["Kakariko Well - Bottom", True, []], - ["Blind's Hideout - Top", True, []], + ["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']], ["Blind's Hideout - Left", True, []], @@ -63,15 +68,19 @@ def testLightWorld(self): ["Bonk Rock Cave", False, [], ['Pegasus Boots']], ["Bonk Rock Cave", True, ['Pegasus Boots']], - ["Mini Moldorm Cave - Far Left", True, []], - - ["Mini Moldorm Cave - Left", True, []], - - ["Mini Moldorm Cave - Right", True, []], - - ["Mini Moldorm Cave - Far Right", True, []], + ["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']], - ["Ice Rod Cave", True, []], + ["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Ice Rod Cave", True, ['Bomb Upgrade (+5)']], ["Bottle Merchant", True, []], @@ -136,11 +145,12 @@ def testLightWorld(self): ["Graveyard Cave", False, []], ["Graveyard Cave", False, [], ['Magic Mirror']], ["Graveyard Cave", False, [], ['Moon Pearl']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], - ["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], + ["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']], + ["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']], ["Checkerboard Cave", False, []], ["Checkerboard Cave", False, [], ['Progressive Glove']], @@ -148,7 +158,6 @@ def testLightWorld(self): ["Checkerboard Cave", False, [], ['Magic Mirror']], ["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], - ["Mini Moldorm Cave - Generous Guy", True, []], ["Library", False, []], ["Library", False, [], ['Pegasus Boots']], @@ -160,7 +169,10 @@ def testLightWorld(self): ["Potion Shop", False, [], ['Mushroom']], ["Potion Shop", True, ['Mushroom']], - ["Maze Race", True, []], + ["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']], + ["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Maze Race", True, ['Bomb Upgrade (+5)']], + ["Maze Race", True, ['Pegasus Boots']], ["Desert Ledge", False, []], ["Desert Ledge", False, [], ['Book of Mudora', 'Flute']], diff --git a/worlds/alttp/test/vanilla/TestVanilla.py b/worlds/alttp/test/vanilla/TestVanilla.py index 3c983e98504c..3f4fbad8c2b6 100644 --- a/worlds/alttp/test/vanilla/TestVanilla.py +++ b/worlds/alttp/test/vanilla/TestVanilla.py @@ -9,8 +9,10 @@ class TestVanilla(TestBase, LTTPTestBase): def setUp(self): self.world_setup() - self.multiworld.logic[1] = "noglitches" + self.multiworld.glitches_required[1] = "no_glitches" self.multiworld.difficulty_requirements[1] = difficulties['normal'] + self.multiworld.bombless_start[1].value = True + self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.worlds[1].er_seed = 0 self.multiworld.worlds[1].create_regions() self.multiworld.worlds[1].create_items() From 38cc90efd08ee802d08f7aba76a33d2da44db304 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 20 Feb 2024 08:07:33 +0100 Subject: [PATCH 03/12] TextClient: fix logging not always showing up (#2846) --- CommonClient.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CommonClient.py b/CommonClient.py index 736cf4922f40..c75ca3fd806e 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -941,4 +941,5 @@ async def main(args): if __name__ == '__main__': + logging.getLogger().setLevel(logging.INFO) # force log-level to work around log level resetting to WARNING run_as_textclient() From 7fc159c8819a2e0b61c2cfe3824f485e6939b45b Mon Sep 17 00:00:00 2001 From: BootsinSoots <102177943+BootsinSoots@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:22:32 -0500 Subject: [PATCH 04/12] Docs: Make all guide titles say Guide, for my sanity (and the webhost) (#2304) --- worlds/alttp/__init__.py | 6 +++--- worlds/bumpstik/__init__.py | 2 +- worlds/checksfinder/__init__.py | 2 +- worlds/dark_souls_3/__init__.py | 2 +- worlds/dlcquest/__init__.py | 2 +- worlds/factorio/__init__.py | 2 +- worlds/meritous/__init__.py | 2 +- worlds/messenger/__init__.py | 2 +- worlds/minecraft/__init__.py | 2 +- worlds/oot/__init__.py | 2 +- worlds/overcooked2/__init__.py | 2 +- worlds/pokemon_rb/__init__.py | 2 +- worlds/tloz/__init__.py | 2 +- worlds/undertale/__init__.py | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index e1216010e2b3..7a2664b3f4bc 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -42,7 +42,7 @@ class RomFile(settings.SNESRomPath): class ALTTPWeb(WebWorld): setup_en = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago ALttP Software on your computer. This guide covers single-player, multiworld, and related software.", "English", "multiworld_en.md", @@ -78,7 +78,7 @@ class ALTTPWeb(WebWorld): ) msu = Tutorial( - "MSU-1 Setup Tutorial", + "MSU-1 Setup Guide", "A guide to setting up MSU-1, which allows for custom in-game music.", "English", "msu1_en.md", @@ -105,7 +105,7 @@ class ALTTPWeb(WebWorld): ) plando = Tutorial( - "Plando Tutorial", + "Plando Guide", "A guide to creating Multiworld Plandos with LTTP", "English", "plando_en.md", diff --git a/worlds/bumpstik/__init__.py b/worlds/bumpstik/__init__.py index c4e65d07b6a9..d93b25cda5e7 100644 --- a/worlds/bumpstik/__init__.py +++ b/worlds/bumpstik/__init__.py @@ -14,7 +14,7 @@ class BumpStikWeb(WebWorld): tutorials = [Tutorial( - "Bumper Stickers Setup Tutorial", + "Bumper Stickers Setup Guide", "A guide to setting up the Archipelago Bumper Stickers software on your computer.", "English", "setup_en.md", diff --git a/worlds/checksfinder/__init__.py b/worlds/checksfinder/__init__.py index 621e8f5c37b2..b70c65bb08f5 100644 --- a/worlds/checksfinder/__init__.py +++ b/worlds/checksfinder/__init__.py @@ -10,7 +10,7 @@ class ChecksFinderWeb(WebWorld): tutorials = [Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago ChecksFinder software on your computer. This guide covers " "single-player, multiworld, and related software.", "English", diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index 7ee6c2a6411b..6efe4e4bc961 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -15,7 +15,7 @@ class DarkSouls3Web(WebWorld): bug_report_page = "https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/issues" setup_en = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago Dark Souls III randomizer on your computer.", "English", "setup_en.md", diff --git a/worlds/dlcquest/__init__.py b/worlds/dlcquest/__init__.py index ca7a0157cb5c..db55b1903b61 100644 --- a/worlds/dlcquest/__init__.py +++ b/worlds/dlcquest/__init__.py @@ -14,7 +14,7 @@ class DLCqwebworld(WebWorld): setup_en = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago DLCQuest game on your computer.", "English", "setup_en.md", diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 17f3163e9026..3b7475738489 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -53,7 +53,7 @@ class BridgeChatOut(settings.Bool): class FactorioWeb(WebWorld): tutorials = [Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago Factorio software on your computer.", "English", "setup_en.md", diff --git a/worlds/meritous/__init__.py b/worlds/meritous/__init__.py index 1bf1bfc0f2e6..fd12734be9db 100644 --- a/worlds/meritous/__init__.py +++ b/worlds/meritous/__init__.py @@ -17,7 +17,7 @@ class MeritousWeb(WebWorld): tutorials = [Tutorial( - "Meritous Setup Tutorial", + "Meritous Setup Guide", "A guide to setting up the Archipelago Meritous software on your computer.", "English", "setup_en.md", diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py index b0d031905c92..f4a28729f1ed 100644 --- a/worlds/messenger/__init__.py +++ b/worlds/messenger/__init__.py @@ -17,7 +17,7 @@ class MessengerWeb(WebWorld): bug_report_page = "https://github.com/alwaysintreble/TheMessengerRandomizerModAP/issues" tut_en = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up The Messenger randomizer on your computer.", "English", "setup_en.md", diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index 187f1fdf196a..343b9bad19a9 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -37,7 +37,7 @@ class MinecraftWebWorld(WebWorld): bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here" setup = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago Minecraft software on your computer. This guide covers" "single-player, multiworld, and related software.", "English", diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index eb9c41f0b032..2f06500e81b6 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -92,7 +92,7 @@ class RomStart(str): class OOTWeb(WebWorld): setup = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago Ocarina of Time software on your computer.", "English", "setup_en.md", diff --git a/worlds/overcooked2/__init__.py b/worlds/overcooked2/__init__.py index 0451f32bdd49..24ac175cebb8 100644 --- a/worlds/overcooked2/__init__.py +++ b/worlds/overcooked2/__init__.py @@ -16,7 +16,7 @@ class Overcooked2Web(WebWorld): bug_report_page = "https://github.com/toasterparty/oc2-modding/issues" setup_en = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Overcooked! 2 randomizer on your computer.", "English", "setup_en.md", diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 169ff1d59f1e..56502f50299c 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -46,7 +46,7 @@ class BlueRomFile(settings.UserFilePath): class PokemonWebWorld(WebWorld): setup_en = Tutorial( "Multiworld Setup Guide", - "A guide to playing Pokemon Red and Blue with Archipelago.", + "A guide to playing Pokémon Red and Blue with Archipelago.", "English", "setup_en.md", "setup/en", diff --git a/worlds/tloz/__init__.py b/worlds/tloz/__init__.py index 259bfe204716..27230654b8ce 100644 --- a/worlds/tloz/__init__.py +++ b/worlds/tloz/__init__.py @@ -45,7 +45,7 @@ class DisplayMsgs(settings.Bool): class TLoZWeb(WebWorld): theme = "stone" setup = Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up The Legend of Zelda for Archipelago on your computer.", "English", "multiworld_en.md", diff --git a/worlds/undertale/__init__.py b/worlds/undertale/__init__.py index 9e784a4a59a0..0694456a6b12 100644 --- a/worlds/undertale/__init__.py +++ b/worlds/undertale/__init__.py @@ -29,7 +29,7 @@ def data_path(file_name: str): class UndertaleWeb(WebWorld): tutorials = [Tutorial( - "Multiworld Setup Tutorial", + "Multiworld Setup Guide", "A guide to setting up the Archipelago Undertale software on your computer. This guide covers " "single-player, multiworld, and related software.", "English", From 17c73916b70c2a7cf103f8ee0522ec83d5f5b0a9 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 21 Feb 2024 08:53:54 +0100 Subject: [PATCH 05/12] Speedups: no cinit, no pickling (#2851) * Speedups: remove unnecessary cinit This was meant for (memory) safety, but cython docs clearly state that this is done automatically. The code generated for cinit with args is what triggers a 'possible null deref' in clang's static analyzer, so by removing cinit, we can now use static analysis. * Speedups: disable pickling ... ... of LocationStore and internal classes. This reduces code size and avoids accidentally pickling them. --- _speedups.pyx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/_speedups.pyx b/_speedups.pyx index 9bf25cce2984..b4167ec5aa1e 100644 --- a/_speedups.pyx +++ b/_speedups.pyx @@ -48,6 +48,7 @@ cdef struct IndexEntry: size_t count +@cython.auto_pickle(False) cdef class LocationStore: """Compact store for locations and their items in a MultiServer""" # The original implementation uses Dict[int, Dict[int, Tuple(int, int, int]] @@ -78,18 +79,6 @@ cdef class LocationStore: size += sizeof(self._raw_proxies[0]) * self.sender_index_size return size - def __cinit__(self, locations_dict: Dict[int, Dict[int, Sequence[int]]]) -> None: - self._mem = None - self._keys = None - self._items = None - self._proxies = None - self._len = 0 - self.entries = NULL - self.entry_count = 0 - self.sender_index = NULL - self.sender_index_size = 0 - self._raw_proxies = NULL - def __init__(self, locations_dict: Dict[int, Dict[int, Sequence[int]]]) -> None: self._mem = Pool() cdef object key @@ -281,6 +270,7 @@ cdef class LocationStore: entry.location not in checked]) +@cython.auto_pickle(False) @cython.internal # unsafe. disable direct import cdef class PlayerLocationProxy: cdef LocationStore _store From ffdcb91a13860111b4e467e2436bb9fcb8964483 Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:51:22 -0600 Subject: [PATCH 06/12] CI: add missing core files to "affects: core" labelling (#2824) * add missing files * Change to wildcard --- .github/labeler.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index c58290283665..2743104f410e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -27,4 +27,5 @@ - '!WebHostLib/**' - any-glob-to-any-file: # exceptions to the above rules of "stuff that isn't core" - 'worlds/generic/**/*.py' + - 'worlds/*.py' - 'CommonClient.py' From 9f0d736aed464e945e4205289c86babaec3f1b73 Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:44:03 -0500 Subject: [PATCH 07/12] Generate: Fix sphere calculation debug message (#2788) --- BaseClasses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 25e4e70741a1..36d0bc267a84 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1272,12 +1272,12 @@ def create_playthrough(self, create_paths: bool = True) -> None: for location in sphere: state.collect(location.item, True, location) - required_locations -= sphere - collection_spheres.append(sphere) logging.debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations)) + + required_locations -= sphere if not sphere: raise RuntimeError(f'Not all required items reachable. Unreachable locations: {required_locations}') From f8981a463873ac4ea6093385427199cf58c089d0 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:49:02 +0100 Subject: [PATCH 08/12] Docs: Better description for LocationScouts (#2674) * Better description for LocationScouts * Update network protocol.md * typo * Update docs/network protocol.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update docs/network protocol.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update docs/network protocol.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update docs/network protocol.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --------- Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- docs/network protocol.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/network protocol.md b/docs/network protocol.md index 338db55299b6..c6d6cf6887e6 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -327,7 +327,11 @@ Sent to server to inform it of locations that the client has checked. Used to in | locations | list\[int\] | The ids of the locations checked by the client. May contain any number of checks, even ones sent before; duplicates do not cause issues with the Archipelago server. | ### LocationScouts -Sent to the server to inform it of locations the client has seen, but not checked. Useful in cases in which the item may appear in the game world, such as 'ledge items' in A Link to the Past. The server will always respond with a [LocationInfo](#LocationInfo) packet with the items located in the scouted location. +Sent to the server to retrieve the items that are on a specified list of locations. The server will respond with a [LocationInfo](#LocationInfo) packet containing the items located in the scouted locations. +Fully remote clients without a patch file may use this to "place" items onto their in-game locations, most commonly to display their names or item classifications before/upon pickup. + +LocationScouts can also be used to inform the server of locations the client has seen, but not checked. This creates a hint as if the player had run `!hint_location` on a location, but without deducting hint points. +This is useful in cases where an item appears in the game world, such as 'ledge items' in _A Link to the Past_. To do this, set the `create_as_hint` parameter to a non-zero value. #### Arguments | Name | Type | Notes | From b18641091f3090306cb2dc463ac1b43387f377b1 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:56:53 -0500 Subject: [PATCH 09/12] LTTP: Thieves' Town Big Chest fix (#2853) --- worlds/alttp/Rules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py index 17061842dde9..a87bfd5b0cde 100644 --- a/worlds/alttp/Rules.py +++ b/worlds/alttp/Rules.py @@ -397,9 +397,9 @@ def global_rules(world, player): set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3) and can_use_bombs(state, player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), - lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player, 3)) and state.has('Hammer', player)) + lambda state: ((state._lttp_has_key('Small Key (Thieves Town)', player, 3)) or (location_item_name(state, 'Thieves\' Town - Big Chest', player) == ("Small Key (Thieves Town)", player)) and state._lttp_has_key('Small Key (Thieves Town)', player, 2)) and state.has('Hammer', player)) if world.accessibility[player] != 'locations': - allow_self_locking_items(world.get_location('Thieves\' Town - Big Chest', player), 'Small Key (Thieves Town)') + set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player) set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3)) set_rule(world.get_location('Thieves\' Town - Spike Switch Pot Key', player), From afa5ce4afe63abf1dfccb6c311f85392b7f131d1 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:11:00 +0100 Subject: [PATCH 10/12] CI: add static analysis for native code / cython (#2852) * CI: add static analysis for native code / cython * CI: scan-build: also run for requirements.txt --- .github/workflows/scan-build.yml | 65 ++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/scan-build.yml diff --git a/.github/workflows/scan-build.yml b/.github/workflows/scan-build.yml new file mode 100644 index 000000000000..5234d862b4d3 --- /dev/null +++ b/.github/workflows/scan-build.yml @@ -0,0 +1,65 @@ +name: Native Code Static Analysis + +on: + push: + paths: + - '**.c' + - '**.cc' + - '**.cpp' + - '**.cxx' + - '**.h' + - '**.hh' + - '**.hpp' + - '**.pyx' + - 'setup.py' + - 'requirements.txt' + - '.github/workflows/scan-build.yml' + pull_request: + paths: + - '**.c' + - '**.cc' + - '**.cpp' + - '**.cxx' + - '**.h' + - '**.hh' + - '**.hpp' + - '**.pyx' + - 'setup.py' + - 'requirements.txt' + - '.github/workflows/scan-build.yml' + +jobs: + scan-build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install newer Clang + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x ./llvm.sh + sudo ./llvm.sh 17 + - name: Install scan-build command + run: | + sudo apt install clang-tools-17 + - name: Get a recent python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install dependencies + run: | + python -m venv venv + source venv/bin/activate + python -m pip install --upgrade pip -r requirements.txt + - name: scan-build + run: | + source venv/bin/activate + scan-build-17 --status-bugs -o scan-build-reports -disable-checker deadcode.DeadStores python setup.py build -y + - name: Store report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: scan-build-reports + path: scan-build-reports From 96163c640830545fed9b5a133b6989273e8d3a30 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 23 Feb 2024 10:32:14 +0100 Subject: [PATCH 11/12] Core: provide convenience getters on World class (#2827) --- worlds/AutoWorld.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index e8d48df58c53..b282c7deb8bd 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: import random - from BaseClasses import MultiWorld, Item, Location, Tutorial + from BaseClasses import MultiWorld, Item, Location, Tutorial, Region, Entrance from . import GamesPackage from settings import Group @@ -458,6 +458,16 @@ def remove(self, state: "CollectionState", item: "Item") -> bool: def create_filler(self) -> "Item": return self.create_item(self.get_filler_item_name()) + # convenience methods + def get_location(self, location_name: str) -> "Location": + return self.multiworld.get_location(location_name, self.player) + + def get_entrance(self, entrance_name: str) -> "Entrance": + return self.multiworld.get_entrance(entrance_name, self.player) + + def get_region(self, region_name: str) -> "Region": + return self.multiworld.get_region(region_name, self.player) + @classmethod def get_data_package_data(cls) -> "GamesPackage": sorted_item_name_groups = { From 6bf4a94537511afba4e8386361af59e31e315105 Mon Sep 17 00:00:00 2001 From: Scipio Wright Date: Fri, 23 Feb 2024 13:41:59 -0500 Subject: [PATCH 12/12] TUNIC: Use push_precollected for start_with_sword (#2857) --- worlds/tunic/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/tunic/__init__.py b/worlds/tunic/__init__.py index fb04570f22ca..b10ccd43af59 100644 --- a/worlds/tunic/__init__.py +++ b/worlds/tunic/__init__.py @@ -73,9 +73,6 @@ def generate_early(self) -> None: self.options.hexagon_quest.value = passthrough["hexagon_quest"] self.options.entrance_rando.value = passthrough["entrance_rando"] - if self.options.start_with_sword and "Sword" not in self.options.start_inventory: - self.options.start_inventory.value["Sword"] = 1 - def create_item(self, name: str) -> TunicItem: item_data = item_table[name] return TunicItem(name, item_data.classification, self.item_name_to_id[name], self.player) @@ -94,6 +91,9 @@ def create_items(self) -> None: items_to_create["Fool Trap"] += items_to_create[money_fool] items_to_create[money_fool] = 0 + if self.options.start_with_sword: + self.multiworld.push_precollected(self.create_item("Sword")) + if sword_progression: items_to_create["Stick"] = 0 items_to_create["Sword"] = 0

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