From 7389a55cd20465c89cc87dff3f998b2c61c5ede9 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sun, 16 Nov 2025 22:07:55 -0400 Subject: [PATCH 01/20] shuffle_great_fairy_rewards: Conversion from "Toggle" to "Choice" --- worlds/mm_recomp/Items.py | 14 +++++++------- worlds/mm_recomp/Locations.py | 18 ++++++++++++------ worlds/mm_recomp/Options.py | 11 +++++++++-- worlds/mm_recomp/__init__.py | 33 +++++++++++++++++++++------------ 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 13b42b4c..7d573163 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -23,18 +23,18 @@ class MMRItemData(NamedTuple): "Progressive Magic": MMRItemData( code=0x3469420020000, type=ItemClassification.progression, - can_create=lambda options: options.shuffle_great_fairy_rewards.value, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 1, num_exist=2 ), "Great Spin Attack": MMRItemData( code=0x3469420020001, type=ItemClassification.useful, - can_create=lambda options: options.shuffle_great_fairy_rewards.value + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 1 ), "Double Defense": MMRItemData( code=0x3469420020003, type=ItemClassification.useful, - can_create=lambda options: options.shuffle_great_fairy_rewards.value + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 1 ), "Bomber's Notebook": MMRItemData( code=0x3469420000050, @@ -195,7 +195,7 @@ class MMRItemData(NamedTuple): "Great Fairy Mask": MMRItemData( code=0x3469420000086, type=ItemClassification.progression, - can_create=lambda options: options.shuffle_great_fairy_rewards.value + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 1 ), "Gibdo Mask": MMRItemData( code=0x3469420000087, @@ -271,7 +271,7 @@ class MMRItemData(NamedTuple): "Great Fairy Sword": MMRItemData( code=0x346942000003B, type=ItemClassification.progression, - can_create=lambda options: options.shuffle_great_fairy_rewards.value + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 1 ), "Progressive Bow": MMRItemData( code=0x3469420000022, @@ -508,13 +508,13 @@ class MMRItemData(NamedTuple): "Blue Rupee": MMRItemData( code=0x3469420000002, type=ItemClassification.filler, - num_exist=14 + num_exist=12 # ~ num_exist=6 ), "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=45 + num_exist=41 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index ecd10792..9686d965 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -339,11 +339,13 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "North Clock Town Great Fairy Reward": MMRLocationData( region="Clock Town", - address=0x3469420030000 + address=0x3469420030000, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "North Clock Town Great Fairy Reward (Has Transformation Mask)": MMRLocationData( region="Clock Town", - address=0x3469420000086 + address=0x3469420000086, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "West Clock Town Lottery Any Day": MMRLocationData( region="Clock Town", @@ -777,7 +779,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Woodfall Great Fairy Reward": MMRLocationData( region="Woodfall", - address=0x3469420030001 + address=0x3469420030001, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "Woodfall Temple Entrance Chest SF": MMRLocationData( region="Woodfall Temple", @@ -1001,7 +1004,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Snowhead Great Fairy Reward": MMRLocationData( region="Snowhead Temple", - address=0x3469420030002 + address=0x3469420030002, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "Snowhead Temple Elevator Room Invisible Platform Chest SF": MMRLocationData( region="Snowhead Temple", @@ -1215,7 +1219,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Great Bay Great Fairy Reward": MMRLocationData( region="Zora Cape", - address=0x3469420030003 + address=0x3469420030003, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "Zora Hall Shop Item 1": MMRLocationData( region="Zora Hall", @@ -1654,7 +1659,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Stone Tower Great Fairy Reward": MMRLocationData( region="Ikana Canyon", - address=0x3469420030004 + address=0x3469420030004, + can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), "Ikana Well Final Chest": MMRLocationData( region="Beneath the Well", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 04cc77eb..b9f1b2c4 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -167,9 +167,16 @@ class Cowsanity(Toggle): display_name = "Shuffle Cows" -class ShuffleGreatFairyRewards(Toggle): - """Choose whether to shuffle Great Fairy rewards.""" +class ShuffleGreatFairyRewards(Choice): + """Choose how Great Fairy rewards are shuffled. + disabled: Great Fairy rewards won't be shuffled into the pool. + vanilla: Great Fairy rewards will be vanilla. For example, Magic will be behind Clock Town and Snowhead rewards. + enabled: Great Fairy rewards will be shuffled. Any item can be shuffled at their locations.""" display_name = "Shuffle Great Fairy Rewards" + option_disabled = 0 + option_vanilla = 1 + option_enabled = 2 + default = 0 class RequiredStrayFairies(Range): diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index b2696ff3..b3291fa3 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -2,7 +2,7 @@ from typing import Dict from typing import TextIO -from BaseClasses import Region, Tutorial +from BaseClasses import Region, Tutorial, ItemClassification from worlds.AutoWorld import WebWorld, World from .Items import MMRItem, item_data_table, item_table, code_to_item_table from .Locations import MMRLocation, location_data_table, location_table, code_to_location_table, locked_locations @@ -37,7 +37,7 @@ class MMRWorld(World): options = MMROptions location_name_to_id = location_table item_name_to_id = item_table - + prices_ints: List[int] prices: str @@ -66,9 +66,16 @@ def generate_early(self): self.prices += str(price) + " " self.prices = self.prices[:-1] - + def create_item(self, name: str) -> MMRItem: - return MMRItem(name, item_data_table[name].type, item_data_table[name].code, self.player) + if (name == "Stray Fairy (Clock Town)" or + name == "Stray Fairy (Woodfall)" or + name == "Stray Fairy (Snowhead)" or + name == "Stray Fairy (Great Bay)" or + name == "Stray Fairy (Stone Tower)") and self.options.shuffle_great_fairy_rewards == 0: + return MMRItem(name, ItemClassification.filler, item_data_table[name].code, self.player) + else: + return MMRItem(name, item_data_table[name].type, item_data_table[name].code, self.player) def place(self, location, item): player = self.player @@ -98,11 +105,11 @@ def create_items(self) -> None: if self.options.shieldless.value: mw.itempool.append(self.create_item("Progressive Shield")) - + if self.options.start_with_soaring.value: mw.push_precollected(self.create_item("Song of Soaring")) self.create_and_add_filler_items() - + if self.options.shuffle_spiderhouse_reward.value: mw.itempool.append(self.create_item("Progressive Wallet")) @@ -120,22 +127,25 @@ def create_items(self) -> None: mw.itempool.append(self.create_item("Red Rupee")) mw.itempool.append(self.create_item("Purple Rupee")) mw.itempool.append(self.create_item("Gold Rupee")) - + if self.options.scrubsanity.value != 0: self.create_and_add_filler_items(4) - + if self.options.shopsanity.value != 0: self.create_and_add_filler_items(27) if self.options.shopsanity.value == 2: self.create_and_add_filler_items(11) - + if self.options.cowsanity.value != 0: self.create_and_add_filler_items(8) - + if self.options.intro_checks.value: self.create_and_add_filler_items(1) + if self.options.shuffle_great_fairy_rewards.value != 0: + self.create_and_add_filler_items(6) + shp = self.options.starting_hearts.value if self.options.starting_hearts_are_containers_or_pieces.value == 0: for i in range(0, int((12 - shp)/4)): @@ -204,9 +214,8 @@ def create_regions(self) -> None: self.place(code_to_location_table[0x3469420062700 | i], "Swamp Skulltula Token") if i != 0: self.place(code_to_location_table[0x3469420062800 | i], "Ocean Skulltula Token") - - if not self.options.shuffle_great_fairy_rewards.value: + if self.options.shuffle_great_fairy_rewards.value == 1: #vanilla self.place("North Clock Town Great Fairy Reward", "Progressive Magic") self.place("North Clock Town Great Fairy Reward (Has Transformation Mask)", "Great Fairy Mask") self.place("Woodfall Great Fairy Reward", "Great Spin Attack") From 51d06dafb963357b9c5ef97275338290b752b522 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sat, 20 Dec 2025 01:09:19 -0400 Subject: [PATCH 02/20] Add spacing to ShuffleGreatFairyRewards description --- worlds/mm_recomp/Options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index b9f1b2c4..4e52a708 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -169,6 +169,7 @@ class Cowsanity(Toggle): class ShuffleGreatFairyRewards(Choice): """Choose how Great Fairy rewards are shuffled. + disabled: Great Fairy rewards won't be shuffled into the pool. vanilla: Great Fairy rewards will be vanilla. For example, Magic will be behind Clock Town and Snowhead rewards. enabled: Great Fairy rewards will be shuffled. Any item can be shuffled at their locations.""" From 256c6b93729b71b71bbcf7f5f5a01397ad8d335e Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Wed, 21 Jan 2026 21:55:03 -0400 Subject: [PATCH 03/20] Added the option to disable ShuffleSpiderhouseReward Tokens will be filler items when disabled --- worlds/mm_recomp/Items.py | 4 ++-- worlds/mm_recomp/Locations.py | 6 ++++-- worlds/mm_recomp/Options.py | 11 +++++++++-- worlds/mm_recomp/__init__.py | 10 ++++++++-- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 7d573163..57297ed3 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -212,7 +212,7 @@ class MMRItemData(NamedTuple): "Mask of Truth": MMRItemData( code=0x346942000008A, type=ItemClassification.progression, - can_create=lambda options: options.shuffle_spiderhouse_reward.value + can_create=lambda options: options.shuffle_spiderhouse_reward.value != 1 ), "Stone Mask": MMRItemData( code=0x346942000008B, @@ -514,7 +514,7 @@ class MMRItemData(NamedTuple): "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=41 + num_exist=39 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 9686d965..16af7921 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -735,7 +735,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Swamp Spider House Reward": MMRLocationData( region="Swamp Spider House", - address=0x346942000008A + address=0x346942000008A, + can_create=lambda options: options.shuffle_spiderhouse_reward.value != 0 ), "Southern Swamp Grotto Chest": MMRLocationData( region="Southern Swamp (Deku Palace)", @@ -1482,7 +1483,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Ocean Spider House Reward": MMRLocationData( region="Ocean Spider House", - address=0x3469420000009 + address=0x3469420000009, + can_create=lambda options: options.shuffle_spiderhouse_reward.value != 0 ), "Great Bay Temple Blender Pot SF": MMRLocationData( region="Great Bay Temple", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 4e52a708..cecdaf72 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -98,9 +98,16 @@ class BossWarpsWithRemains(DefaultOnToggle): display_name = "Warp to Bosses Using Remains" -class ShuffleSpiderHouseReward(Toggle): - """Choose whether to shuffle the Mask of Truth given at the end of the Southern Spider House and the Wallet Upgrade at the end of the Ocean Spider House.""" +class ShuffleSpiderHouseReward(Choice): + """Choose how Swamp Spider House and Ocean Spider House rewards are shuffled. + + disabled: Spider House rewards won't be shuffled into the pool. + vanilla: Spider House rewards will be vanilla. Mask of Truth will be in Swamp and a wallet upgrade in Ocean. + enabled: Spider House rewards will be shuffled. Any item can be shuffled at their locations.""" display_name = "Shuffle Spider House Rewards" + option_disabled = 0 + option_vanilla = 1 + option_enabled = 2 class RequiredSkullTokens(Range): diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index b3291fa3..da86fcab 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -74,6 +74,9 @@ def create_item(self, name: str) -> MMRItem: name == "Stray Fairy (Great Bay)" or name == "Stray Fairy (Stone Tower)") and self.options.shuffle_great_fairy_rewards == 0: return MMRItem(name, ItemClassification.filler, item_data_table[name].code, self.player) + elif (name == "Swamp Skulltula Token" or + name == "Ocean Skulltula Token") and self.options.shuffle_spiderhouse_reward == 0: + return MMRItem(name, ItemClassification.filler, item_data_table[name].code, self.player) else: return MMRItem(name, item_data_table[name].type, item_data_table[name].code, self.player) @@ -110,7 +113,7 @@ def create_items(self) -> None: mw.push_precollected(self.create_item("Song of Soaring")) self.create_and_add_filler_items() - if self.options.shuffle_spiderhouse_reward.value: + if self.options.shuffle_spiderhouse_reward.value != 1: mw.itempool.append(self.create_item("Progressive Wallet")) if self.options.shuffle_regional_maps.value == 1: @@ -146,6 +149,9 @@ def create_items(self) -> None: if self.options.shuffle_great_fairy_rewards.value != 0: self.create_and_add_filler_items(6) + if self.options.shuffle_spiderhouse_reward.value != 0: + self.create_and_add_filler_items(2) + shp = self.options.starting_hearts.value if self.options.starting_hearts_are_containers_or_pieces.value == 0: for i in range(0, int((12 - shp)/4)): @@ -204,7 +210,7 @@ def create_regions(self) -> None: self.place("Great Bay Temple Gyorg's Remains", remains_list.pop(self.random.randint(0, 1))) self.place("Stone Tower Temple Inverted Twinmold's Remains", remains_list[0]) - if not self.options.shuffle_spiderhouse_reward.value: + if self.options.shuffle_spiderhouse_reward.value == 1: self.place("Swamp Spider House Reward", "Mask of Truth") self.place("Ocean Spider House Reward", "Progressive Wallet") From 41762d092b355911dcd78894ccbb5cd260623b0c Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Thu, 22 Jan 2026 23:59:03 -0400 Subject: [PATCH 04/20] Added ShuffleTreasureChestGame option Allows to disable or enable all chests, or to only enable Goron chest. --- worlds/mm_recomp/Items.py | 4 ++-- worlds/mm_recomp/Locations.py | 12 ++++++++---- worlds/mm_recomp/Options.py | 13 +++++++++++++ worlds/mm_recomp/__init__.py | 6 ++++++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 57297ed3..7cded359 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -508,13 +508,13 @@ class MMRItemData(NamedTuple): "Blue Rupee": MMRItemData( code=0x3469420000002, type=ItemClassification.filler, - num_exist=12 + num_exist=11 # ~ num_exist=6 ), "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=39 + num_exist=36 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 16af7921..39bc9073 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -299,19 +299,23 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "East Clock Town Treasure Game Chest (Human)": MMRLocationData( region="Clock Town", - address=0x3469420061705 + address=0x3469420061705, + can_create=lambda options: options.shuffle_treasure_chest_game.value == 2 ), "East Clock Town Treasure Game Chest (Deku)": MMRLocationData( region="Clock Town", - address=0x346942006172A + address=0x346942006172A, + can_create=lambda options: options.shuffle_treasure_chest_game.value == 2 ), "East Clock Town Treasure Game Chest (Goron)": MMRLocationData( region="Clock Town", - address=0x346942006170C + address=0x346942006170C, + can_create=lambda options: options.shuffle_treasure_chest_game.value != 0 ), "East Clock Town Treasure Game Chest (Zora)": MMRLocationData( region="Clock Town", - address=0x3469420061704 + address=0x3469420061704, + can_create=lambda options: options.shuffle_treasure_chest_game.value == 2 ), "Bomber's Hideout Chest": MMRLocationData( region="Clock Town", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index cecdaf72..36a60f26 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -222,6 +222,18 @@ class IntroChecks(Toggle): A way backwards through these areas has been added through the stone door at the bottom of the Clock Tower Interior.""" display_name = "Enable Intro Checks" +class ShuffleTreasureChestGame(Choice): + """Choose which chests in the Treasure Chest minigame are shuffled. + + disabled: Chests are not shuffled. + goron_only: Only the reward as Goron is shuffled. + everything: Rewards for human, Deku, Goron, and Zora are shuffled.""" + display_name = "Treasure Chest Minigame Shuffle" + option_disabled = 0 + option_goron_only = 1 + option_everything = 2 + default = 1 + class StartWithConsumables(DefaultOnToggle): """Choose whether to start with basic consumables (99 rupees, 10 deku sticks, 20 deku nuts).""" @@ -310,6 +322,7 @@ class MMROptions(PerGameCommonOptions): bosskeysanity: BossKeysanity curiostity_shop_trades: CuriostityShopTrades intro_checks: IntroChecks + shuffle_treasure_chest_game: ShuffleTreasureChestGame start_with_consumables: StartWithConsumables permanent_chateau_romani: PermanentChateauRomani start_with_inverted_time: StartWithInvertedTime diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index da86fcab..04c7014e 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -152,6 +152,11 @@ def create_items(self) -> None: if self.options.shuffle_spiderhouse_reward.value != 0: self.create_and_add_filler_items(2) + if self.options.shuffle_treasure_chest_game.value == 1: + self.create_and_add_filler_items(1) + elif self.options.shuffle_treasure_chest_game.value == 2: + self.create_and_add_filler_items(4) + shp = self.options.starting_hearts.value if self.options.starting_hearts_are_containers_or_pieces.value == 0: for i in range(0, int((12 - shp)/4)): @@ -438,6 +443,7 @@ def fill_slot_data(self): "shuffle_regional_maps": self.options.shuffle_regional_maps.value, "shuffle_spiderhouse_reward": self.options.shuffle_spiderhouse_reward.value, "shuffle_great_fairy_rewards": self.options.shuffle_great_fairy_rewards.value, + "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, "link_tunic_color": ((self.options.link_tunic_color.value[0] & 0xFF) << 16) | ((self.options.link_tunic_color.value[1] & 0xFF) << 8) | (self.options.link_tunic_color.value[2] & 0xFF), "random_seed": self.random.getrandbits(32), "logic_difficulty": self.options.logic_difficulty.value From 00f40ec94ccca48e4751e7113550d95a54474ce6 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sun, 1 Feb 2026 18:13:42 -0400 Subject: [PATCH 05/20] Added ShuffleMinigames option --- worlds/mm_recomp/Items.py | 4 ++-- worlds/mm_recomp/Locations.py | 27 ++++++++++++++++++--------- worlds/mm_recomp/Options.py | 17 +++++++++++++++++ worlds/mm_recomp/__init__.py | 6 ++++++ 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 7cded359..f81f2966 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -508,13 +508,13 @@ class MMRItemData(NamedTuple): "Blue Rupee": MMRItemData( code=0x3469420000002, type=ItemClassification.filler, - num_exist=11 + num_exist=8 # ~ num_exist=6 ), "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=36 + num_exist=30 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 39bc9073..3b6ef08c 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -283,19 +283,23 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "East Clock Town Shooting Gallery 40-49 Points": MMRLocationData( region="Clock Town", - address=0x3469420000023 + address=0x3469420000023, + can_create=lambda options: options.shuffle_minigames.value != 0 ), "East Clock Town Shooting Gallery Perfect 50 Points": MMRLocationData( region="Clock Town", - address=0x346942007011D + address=0x346942007011D, + can_create=lambda options: options.shuffle_minigames.value == 2 ), "East Clock Town Honey and Darling Any Day": MMRLocationData( region="Clock Town", - address=0x34694200800B5 + address=0x34694200800B5, + can_create=lambda options: options.shuffle_minigames.value != 0 ), "East Clock Town Honey and Darling All Days": MMRLocationData( region="Clock Town", - address=0x34694200700B5 + address=0x34694200700B5, + can_create=lambda options: options.shuffle_minigames.value == 2 ), "East Clock Town Treasure Game Chest (Human)": MMRLocationData( region="Clock Town", @@ -331,11 +335,13 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "North Clock Town Deku Playground Any Day": MMRLocationData( region="Clock Town", - address=0x34694200801C9 + address=0x34694200801C9, + can_create=lambda options: options.shuffle_minigames.value != 0 ), "North Clock Town Deku Playground All Days": MMRLocationData( region="Clock Town", - address=0x34694200701C9 + address=0x34694200701C9, + can_create=lambda options: options.shuffle_minigames.value == 2 ), "North Clock Town Save Old Lady": MMRLocationData( region="Clock Town", @@ -529,11 +535,13 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Swamp Shooting Gallery 2120 Points": MMRLocationData( region="Southern Swamp", - address=0x3469420000024 + address=0x3469420000024, + can_create=lambda options: options.shuffle_minigames.value != 0 ), "Swamp Shooting Gallery 2180 Points": MMRLocationData( region="Southern Swamp", - address=0x346942008011D + address=0x346942008011D, + can_create=lambda options: options.shuffle_minigames.value == 2 ), "Southern Swamp Deku Trade": MMRLocationData( region="Southern Swamp", @@ -1188,7 +1196,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Great Bay Fisherman Game": MMRLocationData( region="Great Bay", - address=0x3469420070292 + address=0x3469420070292, + can_create=lambda options: options.shuffle_minigames.value != 0 ), "Zora Cape Underwater Like-Like HP": MMRLocationData( region="Zora Cape", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 36a60f26..1ba2ca1e 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -222,6 +222,22 @@ class IntroChecks(Toggle): A way backwards through these areas has been added through the stone door at the bottom of the Clock Tower Interior.""" display_name = "Enable Intro Checks" +class ShuffleMinigames(Choice): + """Choose whether the minigames are shuffled or not. The minigames affected are: + - Town and Swamp Shooting Galleries; + - Honey & Darling; + - Deku Playground; + - Great Bay Fisherman Game. + + disabled: Listed minigames are not shuffled. + single: Listed minigames only have one location. Where applicable, the easier locations (any day/lowest points requirements) are shuffled. + everything: Listed minigames are fully shuffled.""" + display_name = "Shuffle Minigames" + option_disabled = 0 + option_single = 1 + option_everything = 2 + default = 1 + class ShuffleTreasureChestGame(Choice): """Choose which chests in the Treasure Chest minigame are shuffled. @@ -322,6 +338,7 @@ class MMROptions(PerGameCommonOptions): bosskeysanity: BossKeysanity curiostity_shop_trades: CuriostityShopTrades intro_checks: IntroChecks + shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame start_with_consumables: StartWithConsumables permanent_chateau_romani: PermanentChateauRomani diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 04c7014e..71ab3352 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -151,6 +151,11 @@ def create_items(self) -> None: if self.options.shuffle_spiderhouse_reward.value != 0: self.create_and_add_filler_items(2) + + if self.options.shuffle_minigames.value == 1: + self.create_and_add_filler_items(5) + elif self.options.shuffle_minigames.value == 2: + self.create_and_add_filler_items(9) if self.options.shuffle_treasure_chest_game.value == 1: self.create_and_add_filler_items(1) @@ -443,6 +448,7 @@ def fill_slot_data(self): "shuffle_regional_maps": self.options.shuffle_regional_maps.value, "shuffle_spiderhouse_reward": self.options.shuffle_spiderhouse_reward.value, "shuffle_great_fairy_rewards": self.options.shuffle_great_fairy_rewards.value, + "shuffle_minigames": self.options.shuffle_minigames.value, "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, "link_tunic_color": ((self.options.link_tunic_color.value[0] & 0xFF) << 16) | ((self.options.link_tunic_color.value[1] & 0xFF) << 8) | (self.options.link_tunic_color.value[2] & 0xFF), "random_seed": self.random.getrandbits(32), From 10d448628b3d87f14fe2ac94e4678d8a22db8bcc Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sat, 16 May 2026 14:37:37 -0300 Subject: [PATCH 06/20] Changed keysanity, bosskeysanity and fairysanity, and added maps & compass shuffle - keysanity and bosskeysanity have been changed to shuffle_small_keys and shuffle_boss_keys, respectively. Added options to start with them or have them be placed locally. - fairysanity changed to shuffle_stray_fairies. Added option to have them be placed locally. *** This PR has features that were cut out due to lack of knowledge. On top of the options I added, I also wanted to add options that would shuffle items in either their own dungeons or any of the four. I will likely revisit this someday. --- worlds/mm_recomp/Items.py | 52 +++--- worlds/mm_recomp/Options.py | 86 ++++++++-- worlds/mm_recomp/__init__.py | 297 +++++++++++++++++++++++++++++++++-- 3 files changed, 392 insertions(+), 43 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index f81f2966..73dd5e46 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -8,6 +8,7 @@ class MMRItem(Item): class MMRItemData(NamedTuple): + category: str = "Item" code: Optional[int] = None type: ItemClassification = ItemClassification.filler num_exist: int = 1 @@ -16,9 +17,10 @@ class MMRItemData(NamedTuple): item_data_table: Dict[str, MMRItemData] = { "Stray Fairy (Clock Town)": MMRItemData( + category="Stray Fairy", code=0x346942001007F, type=ItemClassification.progression, - can_create=lambda options: options.fairysanity.value + can_create=lambda options: options.shuffle_stray_fairies.value == 4 or 5 ), "Progressive Magic": MMRItemData( code=0x3469420020000, @@ -356,101 +358,109 @@ class MMRItemData(NamedTuple): code=0x3469420010000, type=ItemClassification.progression, num_exist=15, - can_create=lambda options: options.fairysanity.value + can_create=lambda options: options.shuffle_stray_fairies.value == 4 or options.shuffle_stray_fairies.value == 5 ), "Stray Fairy (Snowhead)": MMRItemData( code=0x3469420010001, type=ItemClassification.progression, num_exist=15, - can_create=lambda options: options.fairysanity.value + can_create=lambda options: options.shuffle_stray_fairies.value == 4 or options.shuffle_stray_fairies.value == 5 ), "Stray Fairy (Great Bay)": MMRItemData( code=0x3469420010002, type=ItemClassification.progression, num_exist=15, - can_create=lambda options: options.fairysanity.value + can_create=lambda options: options.shuffle_stray_fairies.value == 4 or options.shuffle_stray_fairies.value == 5 ), "Stray Fairy (Stone Tower)": MMRItemData( code=0x3469420010003, type=ItemClassification.progression, num_exist=15, - can_create=lambda options: options.fairysanity.value + can_create=lambda options: options.shuffle_stray_fairies.value == 4 or options.shuffle_stray_fairies.value == 5 ), "Small Key (Woodfall)": MMRItemData( code=0x3469420090078, type=ItemClassification.progression, num_exist=1, - can_create=lambda options: options.keysanity.value + can_create=lambda options: options.shuffle_small_keys.value == 4 or options.shuffle_small_keys.value == 5 ), "Small Key (Snowhead)": MMRItemData( code=0x3469420090178, type=ItemClassification.progression, num_exist=3, - can_create=lambda options: options.keysanity.value + can_create=lambda options: options.shuffle_small_keys.value == 4 or options.shuffle_small_keys.value == 5 ), "Small Key (Great Bay)": MMRItemData( code=0x3469420090278, type=ItemClassification.progression, num_exist=1, - can_create=lambda options: options.keysanity.value + can_create=lambda options: options.shuffle_small_keys.value == 4 or options.shuffle_small_keys.value == 5 ), "Small Key (Stone Tower)": MMRItemData( code=0x3469420090378, type=ItemClassification.progression, num_exist=4, - can_create=lambda options: options.keysanity.value + can_create=lambda options: options.shuffle_small_keys.value == 4 or options.shuffle_small_keys.value == 5 ), "Dungeon Map (Woodfall)": MMRItemData( code=0x3469420090076, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Dungeon Map (Snowhead)": MMRItemData( code=0x3469420090176, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Dungeon Map (Great Bay)": MMRItemData( code=0x3469420090276, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Dungeon Map (Stone Tower)": MMRItemData( code=0x3469420090376, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Compass (Woodfall)": MMRItemData( code=0x3469420090075, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Compass (Snowhead)": MMRItemData( code=0x3469420090175, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Compass (Great Bay)": MMRItemData( code=0x3469420090275, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Compass (Stone Tower)": MMRItemData( code=0x3469420090375, - type=ItemClassification.useful + type=ItemClassification.useful, + can_create=lambda options: options.shuffle_maps_and_compasses.value == 4 or options.shuffle_maps_and_compasses.value == 5 ), "Boss Key (Woodfall)": MMRItemData( code=0x3469420090074, type=ItemClassification.progression, - can_create=lambda options: options.bosskeysanity.value + can_create=lambda options: options.shuffle_boss_keys.value == 4 or options.shuffle_boss_keys.value == 5 ), "Boss Key (Snowhead)": MMRItemData( code=0x3469420090174, type=ItemClassification.progression, - can_create=lambda options: options.bosskeysanity.value + can_create=lambda options: options.shuffle_boss_keys.value == 4 or options.shuffle_boss_keys.value == 5 ), "Boss Key (Great Bay)": MMRItemData( code=0x3469420090274, type=ItemClassification.progression, - can_create=lambda options: options.bosskeysanity.value + can_create=lambda options: options.shuffle_boss_keys.value == 4 or options.shuffle_boss_keys.value == 5 ), "Boss Key (Stone Tower)": MMRItemData( code=0x3469420090374, type=ItemClassification.progression, - can_create=lambda options: options.bosskeysanity.value + can_create=lambda options: options.shuffle_boss_keys.value == 4 or options.shuffle_boss_keys.value == 5 ), "Odolwa's Remains": MMRItemData( code=0x3469420000055, diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 1ba2ca1e..d4b25e07 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -196,19 +196,80 @@ class RequiredStrayFairies(Range): range_end = 15 default = 15 +class DungeonItems(Choice): + """Base class for shuffle options for dungeon items (keys, maps, compasses).""" + value: int + option_vanilla = 1 + #option_dungeon = 2 + #option_any_dungeon = 3 + option_local = 4 + option_keysanity = 5 + default = 4 + + @property + def in_dungeon(self) -> bool: + """ + Return whether the item should be shuffled into a dungeon. + + :return: Whether the item is shuffled into a dungeon. + """ + return self.value in (2, 3) + -class Fairysanity(Toggle): - """Choose whether Stray Fairies are shuffled into the pool.""" - display_name = "Fairysanity" +class ShuffleStrayFairies(DungeonItems): + """ + Choose how stray fairies will be shuffled in the pool. + Vanilla: Stray fairies will be places where they can be found in vanilla. + Local: Stray fairies will be placed anywhere in your own world. + Fairysanity: Stray fairies will be placed in any world. + """ + item_name_group = "Stray Fairies" + display_name = "Shuffle Stray Fairies" + option_fairysanity = 5 + default = 4 -class Keysanity(Toggle): - """Choose whether Small Keys are shuffled into the pool or placed in their vanilla locations.""" - display_name = "Keysanity" +class ShuffleMapsAndCompasses(DungeonItems): + """ + Choose how dungeon maps and compasses will be shuffled in the pool. + + Start With: Start the seed with dungeon maps and compasses. + Vanilla: Dungeon maps and compasses will be placed where they can be found in vanilla. + Local: Dungeon maps and compasses will be placed anywhere in your own world. + Keysanity: Dungeon maps and compasses will be placed in any world. + """ + item_name_group = "Maps and Compasses" + display_name = "Shuffle Maps and Compasses" + option_start_with = 0 + default = 4 -class BossKeysanity(Toggle): - """Choose whether Boss Keys are shuffled into the pool or placed in their vanilla locations.""" - display_name = "BossKeysanity" +class ShuffleSmallKeys(DungeonItems): + """ + Choose how small keys will be shuffled in the pool. + + Start With: Start the seed with small keys. + Vanilla: Small keys will be placed where they can be found in vanilla. + Local: Small keys will be placed anywhere in your own world. + Keysanity: Small keys will be placed in any world. + """ + item_name_group = "Small Keys" + display_name = "Shuffle Small Keys" + option_start_with = 0 + default = 4 + +class ShuffleBossKeys(DungeonItems): + """ + Choose how boss keys will be shuffled in the pool. + + Start With: Start the seed with boss keys. + Vanilla: Boss keys will be placed where they can be found in vanilla. + Local: Boss keys will be placed anywhere in your own world. + Keysanity: Boss keys will be placed in any world. + """ + item_name_group = "Boss Keys" + display_name = "Shuffle Boss Keys" + option_start_with = 0 + default = 4 class CuriostityShopTrades(Toggle): @@ -333,9 +394,10 @@ class MMROptions(PerGameCommonOptions): cowsanity: Cowsanity shuffle_great_fairy_rewards: ShuffleGreatFairyRewards required_stray_fairies: RequiredStrayFairies - fairysanity: Fairysanity - keysanity: Keysanity - bosskeysanity: BossKeysanity + shuffle_stray_fairies: ShuffleStrayFairies + shuffle_maps_and_compasses: ShuffleMapsAndCompasses + shuffle_small_keys: ShuffleSmallKeys + shuffle_boss_keys: ShuffleBossKeys curiostity_shop_trades: CuriostityShopTrades intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 71ab3352..41db6126 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -1,6 +1,6 @@ from typing import List from typing import Dict -from typing import TextIO +from typing import TextIO, ClassVar from BaseClasses import Region, Tutorial, ItemClassification from worlds.AutoWorld import WebWorld, World @@ -41,7 +41,56 @@ class MMRWorld(World): prices_ints: List[int] prices: str + #item_name_groups: ClassVar[dict[str, set[str]]] = item_name_groups + + #def __init__(self, *args, **kwargs): + # super().__init__(*args, **kwargs) + # + # self.dungeon_local_item_names: set[str] = set() + # self.dungeon_specific_item_names: set[str] = set() + def generate_early(self): + + #for dungeon_item in ["shuffle_small_keys", "shuffle_boss_keys", "shuffle_maps_and_compasses", "shuffle_stray_fairies"]: + # option = getattr(self.options, dungeon_item) + # if option == "local": + # self.options.local_items.value |= self.item_name_groups[option.item_name_group] + # elif option.in_dungeon: + # self.dungeon_local_item_names |= self.item_name_groups[option.item_name_group] + # if option == "dungeon": + # self.dungeon_specific_item_names |= self.item_name_groups[option.item_name_group] + # else: + # self.options.local_items.value |= self.dungeon_local_item_names + + if self.options.shuffle_stray_fairies.value == 4: + self.options.local_items.value.add("Stray Fairy (Clock Town)") + self.options.local_items.value.add("Stray Fairy (Woodfall)") + self.options.local_items.value.add("Stray Fairy (Snowhead)") + self.options.local_items.value.add("Stray Fairy (Great Bay)") + self.options.local_items.value.add("Stray Fairy (Stone Tower)") + + if self.options.shuffle_maps_and_compasses.value == 4: + self.options.local_items.value.add("Dungeon Map (Woodfall)") + self.options.local_items.value.add("Dungeon Map (Snowhead)") + self.options.local_items.value.add("Dungeon Map (Great Bay)") + self.options.local_items.value.add("Dungeon Map (Stone Tower)") + self.options.local_items.value.add("Compass (Woodfall)") + self.options.local_items.value.add("Compass (Snowhead)") + self.options.local_items.value.add("Compass (Great Bay)") + self.options.local_items.value.add("Compass (Stone Tower)") + + if self.options.shuffle_small_keys.value == 4: + self.options.local_items.value.add("Small Key (Woodfall)") + self.options.local_items.value.add("Small Key (Snowhead)") + self.options.local_items.value.add("Small Key (Great Bay)") + self.options.local_items.value.add("Small Key (Stone Tower)") + + if self.options.shuffle_boss_keys.value == 4: + self.options.local_items.value.add("Boss Key (Woodfall)") + self.options.local_items.value.add("Boss Key (Snowhead)") + self.options.local_items.value.add("Boss Key (Great Bay)") + self.options.local_items.value.add("Boss Key (Stone Tower)") + # Create shop prices. self.prices_ints = [] self.prices = "" @@ -125,6 +174,33 @@ def create_items(self) -> None: mw.push_precollected(self.create_item("Stone Tower Map")) self.create_and_add_filler_items(6) + if self.options.shuffle_maps_and_compasses.value == 0: + mw.push_precollected(self.create_item("Dungeon Map (Woodfall)")) + mw.push_precollected(self.create_item("Compass (Woodfall)")) + mw.push_precollected(self.create_item("Dungeon Map (Snowhead)")) + mw.push_precollected(self.create_item("Compass (Snowhead)")) + mw.push_precollected(self.create_item("Dungeon Map (Great Bay)")) + mw.push_precollected(self.create_item("Compass (Great Bay)")) + mw.push_precollected(self.create_item("Dungeon Map (Stone Tower)")) + mw.push_precollected(self.create_item("Compass (Stone Tower)")) + + if self.options.shuffle_small_keys.value == 0: + mw.push_precollected(self.create_item("Small Key (Woodfall)")) + mw.push_precollected(self.create_item("Small Key (Snowhead)")) + mw.push_precollected(self.create_item("Small Key (Snowhead)")) + mw.push_precollected(self.create_item("Small Key (Snowhead)")) + mw.push_precollected(self.create_item("Small Key (Great Bay)")) + mw.push_precollected(self.create_item("Small Key (Stone Tower)")) + mw.push_precollected(self.create_item("Small Key (Stone Tower)")) + mw.push_precollected(self.create_item("Small Key (Stone Tower)")) + mw.push_precollected(self.create_item("Small Key (Stone Tower)")) + + if self.options.shuffle_boss_keys == 0: + mw.push_precollected(self.create_item("Boss Key (Woodfall)")) + mw.push_precollected(self.create_item("Boss Key (Snowhead)")) + mw.push_precollected(self.create_item("Boss Key (Great Bay)")) + mw.push_precollected(self.create_item("Boss Key (Stone Tower)")) + if self.options.curiostity_shop_trades.value: mw.itempool.append(self.create_item("Blue Rupee")) mw.itempool.append(self.create_item("Red Rupee")) @@ -162,6 +238,15 @@ def create_items(self) -> None: elif self.options.shuffle_treasure_chest_game.value == 2: self.create_and_add_filler_items(4) + if self.options.shuffle_maps_and_compasses.value == 0: + self.create_and_add_filler_items(8) + + if self.options.shuffle_small_keys.value == 0: + self.create_and_add_filler_items(9) + + if self.options.shuffle_boss_keys == 0: + self.create_and_add_filler_items(4) + shp = self.options.starting_hearts.value if self.options.starting_hearts_are_containers_or_pieces.value == 0: for i in range(0, int((12 - shp)/4)): @@ -175,6 +260,19 @@ def create_items(self) -> None: def create_regions(self) -> None: player = self.player mw = self.multiworld + + available_dungeon_locations = [ + "Woodfall Temple Dinolfos Chest", + "Snowhead Temple Lower Wizzrobe Chest", + "Great Bay Temple Behind Locked Door Chest", + "Stone Tower Temple Garo Master Chest", + "Stone Tower Temple Inverted Eyegore Chest" + ] + available_wft_locations = ["Woodfall Temple Dinolfos Chest"] + available_sht_locations = ["Snowhead Temple Lower Wizzrobe Chest"] + available_gbt_locations = ["Great Bay Temple Behind Locked Door Chest"] + available_stt_locations = ["Stone Tower Temple Garo Master Chest", "Stone Tower Temple Inverted Eyegore Chest"] + item_name_groups_to_shuffle = [] # Create regions. for region_name in region_data_table.keys(): @@ -239,7 +337,53 @@ def create_regions(self) -> None: self.place("Great Bay Great Fairy Reward", "Double Defense") self.place("Stone Tower Great Fairy Reward", "Great Fairy Sword") - if not self.options.keysanity.value: + if self.options.shuffle_maps_and_compasses.value == "vanilla": + self.place("Woodfall Temple Turtle Chest", "Dungeon Map (Woodfall)") + self.place("Woodfall Temple Dragonfly Chest", "Compass (Woodfall)") + self.place("Snowhead Temple Elevator Room Lower Chest", "Dungeon Map (Snowhead)") + self.place("Snowhead Temple Frozen Block Chest", "Compass (Snowhead)") + self.place("Great Bay Temple Before Red Valve Room Chest", "Dungeon Map (Great Bay)") + self.place("Great Bay Temple Caged Chest Room Upper Chest", "Compass (Great Bay)") + self.place("Stone Tower Temple Armos Room Back Chest", "Dungeon Map (Stone Tower)") + self.place("Stone Tower Temple Eastern Water Room Sun Block Chest", "Compass (Stone Tower)") +# elif self.options.shuffle_maps_and_compasses.value == "dungeon": +# available_wft_locations.append("Woodfall Temple Turtle Chest") +# available_wft_locations.append("Woodfall Temple Dragonfly Chest") +# available_sht_locations.append("Snowhead Temple Elevator Room Lower Chest") +# available_sht_locations.append("Snowhead Temple Frozen Block Chest") +# available_gbt_locations.append("Great Bay Temple Before Red Valve Room Chest") +# available_gbt_locations.append("Great Bay Temple Caged Chest Room Upper Chest") +# available_stt_locations.append("Stone Tower Temple Armos Room Back Chest") +# available_stt_locations.append("Stone Tower Temple Eastern Water Room Sun Block Chest") +# +# available_wft_locations.append("Dungeon Map (Woodfall)") +# available_wft_locations.append("Compass (Woodfall)") +# available_sht_locations.append("Dungeon Map (Snowhead)") +# available_sht_locations.append("Compass (Snowhead)") +# available_gbt_locations.append("Dungeon Map (Great Bay)") +# available_gbt_locations.append("Compass (Great Bay)") +# available_stt_locations.append("Dungeon Map (Stone Tower)") +# available_stt_locations.append("Compass (Stone Tower)") +# elif self.options.shuffle_maps_and_compasses.value == "any_dungeon": +# available_dungeon_locations.append("Woodfall Temple Turtle Chest") +# available_dungeon_locations.append("Woodfall Temple Dragonfly Chest") +# available_dungeon_locations.append("Snowhead Temple Elevator Room Lower Chest") +# available_dungeon_locations.append("Snowhead Temple Frozen Block Chest") +# available_dungeon_locations.append("Great Bay Temple Before Red Valve Room Chest") +# available_dungeon_locations.append("Great Bay Temple Caged Chest Room Upper Chest") +# available_dungeon_locations.append("Stone Tower Temple Armos Room Back Chest") +# available_dungeon_locations.append("Stone Tower Temple Eastern Water Room Sun Block Chest") +# +# item_name_groups_to_shuffle.append("Dungeon Map (Woodfall)") +# item_name_groups_to_shuffle.append("Compass (Woodfall)") +# item_name_groups_to_shuffle.append("Dungeon Map (Snowhead)") +# item_name_groups_to_shuffle.append("Compass (Snowhead)") +# item_name_groups_to_shuffle.append("Dungeon Map (Great Bay)") +# item_name_groups_to_shuffle.append("Compass (Great Bay)") +# item_name_groups_to_shuffle.append("Dungeon Map (Stone Tower)") +# item_name_groups_to_shuffle.append("Compass (Stone Tower)") + + if self.options.shuffle_small_keys.value == 1: self.place("Woodfall Temple Ledge Chest", "Small Key (Woodfall)") self.place("Snowhead Temple Behind Stacked Block Chest", "Small Key (Snowhead)") @@ -252,14 +396,74 @@ def create_regions(self) -> None: self.place("Stone Tower Temple Eyegore Room Dexi Hand Ledge Chest", "Small Key (Stone Tower)") self.place("Stone Tower Temple Inverted Eastern Air Gust Room Switch Chest", "Small Key (Stone Tower)") self.place("Stone Tower Temple Inverted Death Armos Maze Chest", "Small Key (Stone Tower)") +# elif self.options.shuffle_small_keys.value == "dungeon": +# available_wft_locations.append("Woodfall Temple Ledge Chest") +# available_sht_locations.append("Snowhead Temple Behind Stacked Block Chest") +# available_sht_locations.append("Snowhead Temple Icicle Room Snowball Chest") +# available_sht_locations.append("Snowhead Temple Bridge Room Freezard Chest") +# available_gbt_locations.append("Great Bay Temple Caged Chest Room Underwater Chest") +# available_stt_locations.append("Stone Tower Temple Armos Room Lava Chest") +# available_stt_locations.append("Stone Tower Temple Eyegore Room Dexi Hand Ledge Chest") +# available_stt_locations.append("Stone Tower Temple Inverted Eastern Air Gust Room Switch Chest") +# available_stt_locations.append("Stone Tower Temple Inverted Death Armos Maze Chest") +# +# available_wft_locations.append("Small Key (Woodfall)") +# available_sht_locations.append("Small Key (Snowhead)") +# available_sht_locations.append("Small Key (Snowhead)") +# available_sht_locations.append("Small Key (Snowhead)") +# available_gbt_locations.append("Small Key (Great Bay)") +# available_sst_locations.append("Small Key (Stone Tower)") +# available_sst_locations.append("Small Key (Stone Tower)") +# available_sst_locations.append("Small Key (Stone Tower)") +# available_sst_locations.append("Small Key (Stone Tower)") +# elif self.options.shuffle_small_keys.value == "any_dungeon": +# available_dungeon_locations.append("Woodfall Temple Ledge Chest") +# available_dungeon_locations.append("Snowhead Temple Behind Stacked Block Chest") +# available_dungeon_locations.append("Snowhead Temple Icicle Room Snowball Chest") +# available_dungeon_locations.append("Snowhead Temple Bridge Room Freezard Chest") +# available_dungeon_locations.append("Great Bay Temple Caged Chest Room Underwater Chest") +# available_dungeon_locations.append("Stone Tower Temple Armos Room Lava Chest") +# available_dungeon_locations.append("Stone Tower Temple Eyegore Room Dexi Hand Ledge Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Eastern Air Gust Room Switch Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Death Armos Maze Chest") +# +# item_name_groups_to_shuffle.append("Small Key (Woodfall)") +# item_name_groups_to_shuffle.append("Small Key (Snowhead)") +# item_name_groups_to_shuffle.append("Small Key (Snowhead)") +# item_name_groups_to_shuffle.append("Small Key (Snowhead)") +# item_name_groups_to_shuffle.append("Small Key (Great Bay)") +# item_name_groups_to_shuffle.append("Small Key (Stone Tower)") +# item_name_groups_to_shuffle.append("Small Key (Stone Tower)") +# item_name_groups_to_shuffle.append("Small Key (Stone Tower)") +# item_name_groups_to_shuffle.append("Small Key (Stone Tower)") - if not self.options.bosskeysanity.value: + if self.options.shuffle_boss_keys.value == "vanilla": self.place("Woodfall Temple Gekko Chest", "Boss Key (Woodfall)") self.place("Snowhead Temple Upper Wizzrobe Chest", "Boss Key (Snowhead)") self.place("Great Bay Temple Mad Jellied Gekko Chest", "Boss Key (Great Bay)") self.place("Stone Tower Temple Inverted Gomess Chest", "Boss Key (Stone Tower)") - - if not self.options.fairysanity.value: +# elif self.options.shuffle_boss_keys.value == "dungeon": +# available_wft_locations.append("Woodfall Temple Gekko Chest") +# available_sht_locations.append("Snowhead Temple Upper Wizzrobe Chest") +# available_gbt_locations.append("Great Bay Temple Mad Jellied Gekko Chest") +# available_stt_locations.append("Stone Tower Temple Inverted Gomess Chest") +# +# available_wft_locations.append("Boss Key (Woodfall)") +# available_sht_locations.append("Boss Key (Snowhead)") +# available_gbt_locations.append("Boss Key (Great Bay)") +# available_stt_locations.append("Boss Key (Stone Tower)") +# elif self.options.shuffle_boss_keys.value == "any_dungeon": +# available_dungeon_locations.append("Woodfall Temple Gekko Chest") +# available_dungeon_locations.append("Snowhead Temple Upper Wizzrobe Chest") +# available_dungeon_locations.append("Great Bay Temple Mad Jellied Gekko Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Gomess Chest") +# +# item_name_groups_to_shuffle.append("Boss Key (Woodfall)") +# item_name_groups_to_shuffle.append("Boss Key (Snowhead)") +# item_name_groups_to_shuffle.append("Boss Key (Great Bay)") +# item_name_groups_to_shuffle.append("Boss Key (Stone Tower)") + + if self.options.shuffle_stray_fairies.value == "vanilla": self.place("Laundry Pool Stray Fairy (Clock Town)", "Stray Fairy (Clock Town)") self.place("Woodfall Temple Entrance Chest SF", "Stray Fairy (Woodfall)") @@ -277,7 +481,7 @@ def create_regions(self) -> None: self.place("Woodfall Temple Pre-Boss Upper Right Bubble SF", "Stray Fairy (Woodfall)") self.place("Woodfall Temple Pre-Boss Upper Left Bubble SF", "Stray Fairy (Woodfall)") self.place("Woodfall Temple Pre-Boss Pillar Bubble SF", "Stray Fairy (Woodfall)") - + self.place("Snowhead Temple Basement Switch Chest SF", "Stray Fairy (Snowhead)") self.place("Snowhead Temple Elevator Room Invisible Platform Chest SF", "Stray Fairy (Snowhead)") self.place("Snowhead Temple Stacked Block Upper Chest SF", "Stray Fairy (Snowhead)") @@ -325,7 +529,79 @@ def create_regions(self) -> None: self.place("Stone Tower Temple Inverted Eastern Air Gust Room Fire Chest", "Stray Fairy (Stone Tower)") self.place("Stone Tower Temple Entrance Room Lower Chest", "Stray Fairy (Stone Tower)") self.place("Stone Tower Temple After Garo Upside Down Chest", "Stray Fairy (Stone Tower)") - +# else: +# available_dungeon_locations.append("Woodfall Temple Entrance Chest SF") +# available_dungeon_locations.append("Woodfall Temple Switch Chest SF") +# available_dungeon_locations.append("Woodfall Temple Dark Room Chest SF") +# available_dungeon_locations.append("Woodfall Temple Entrance Freestanding SF") +# available_dungeon_locations.append("Woodfall Temple Deku Baba SF") +# available_dungeon_locations.append("Woodfall Temple Pot SF") +# available_dungeon_locations.append("Woodfall Temple Platform Hive SF") +# available_dungeon_locations.append("Woodfall Temple Main Room Bubble SF") +# available_dungeon_locations.append("Woodfall Temple Skulltula SF") +# available_dungeon_locations.append("Woodfall Temple Bridge Room Bubble SF") +# available_dungeon_locations.append("Woodfall Temple Bridge Room Hive SF") +# available_dungeon_locations.append("Woodfall Temple Pre-Boss Lower Right Bubble SF") +# available_dungeon_locations.append("Woodfall Temple Pre-Boss Upper Right Bubble SF") +# available_dungeon_locations.append("Woodfall Temple Pre-Boss Upper Left Bubble SF") +# available_dungeon_locations.append("Woodfall Temple Pre-Boss Pillar Bubble SF") +# +# available_dungeon_locations.append("Snowhead Temple Basement Switch Chest SF") +# available_dungeon_locations.append("Snowhead Temple Elevator Room Invisible Platform Chest SF") +# available_dungeon_locations.append("Snowhead Temple Stacked Block Upper Chest SF") +# available_dungeon_locations.append("Snowhead Temple Freezard Torch Room Chest SF") +# available_dungeon_locations.append("Snowhead Temple Frozen Block Upper Chest SF") +# available_dungeon_locations.append("Snowhead Temple Icicle Room Hidden Chest SF") +# available_dungeon_locations.append("Snowhead Temple Main Room Wall Chest SF") +# available_dungeon_locations.append("Snowhead Temple Bridge Room Pillar Bubble SF") +# available_dungeon_locations.append("Snowhead Temple Bridge Room Under Platform Bubble SF") +# available_dungeon_locations.append("Snowhead Temple Elevator Freestanding SF") +# available_dungeon_locations.append("Snowhead Temple Bombable Stairs Crate SF") +# available_dungeon_locations.append("Snowhead Temple Timed Switch Room Bubble SF") +# available_dungeon_locations.append("Snowhead Temple Snowmen Bubble SF") +# available_dungeon_locations.append("Snowhead Temple Dinolfos Room First SF") +# available_dungeon_locations.append("Snowhead Temple Dinolfos Room Second SF") +# +# available_dungeon_locations.append("Great Bay Temple Entrance Torches Chest SF") +# available_dungeon_locations.append("Great Bay Temple Bio-Baba Hall Chest SF") +# available_dungeon_locations.append("Great Bay Temple Freezable Waterwheel Upper Chest SF") +# available_dungeon_locations.append("Great Bay Temple Freezable Waterwheel Lower Chest SF") +# available_dungeon_locations.append("Great Bay Temple Seesaw Room Chest SF") +# available_dungeon_locations.append("Great Bay Temple Room Behind Waterfall Ceiling Chest SF") +# available_dungeon_locations.append("Great Bay Temple Waterwheel Room Skulltula SF") +# available_dungeon_locations.append("Great Bay Temple Waterwheel Room Bubble SF") +# available_dungeon_locations.append("Great Bay Temple Blender Pot SF") +# available_dungeon_locations.append("Great Bay Temple Blender Room Barrel SF") +# available_dungeon_locations.append("Great Bay Temple Before Red Valve Room Pot SF") +# available_dungeon_locations.append("Great Bay Temple Caged Chest Room Pot SF") +# available_dungeon_locations.append("Great Bay Temple Seesaw Room Underwater Barrel SF") +# available_dungeon_locations.append("Great Bay Temple Pre-Boss Room Platform Bubble SF") +# available_dungeon_locations.append("Great Bay Temple Pre-Boss Room Tunnel Bubble SF") +# +# available_dungeon_locations.append("Stone Tower Temple Entrance Room Eye Switch Chest") +# available_dungeon_locations.append("Stone Tower Temple Armos Room Upper Chest") +# available_dungeon_locations.append("Stone Tower Temple Eyegore Room Switch Chest") +# available_dungeon_locations.append("Stone Tower Temple Mirror Room Sun Face Chest") +# available_dungeon_locations.append("Stone Tower Temple Mirror Room Sun Block Chest") +# available_dungeon_locations.append("Stone Tower Temple Air Gust Room Side Chest") +# available_dungeon_locations.append("Stone Tower Temple Air Gust Room Goron Switch Chest") +# available_dungeon_locations.append("Stone Tower Temple Eyegore Chest") +# available_dungeon_locations.append("Stone Tower Temple Eastern Water Room Underwater Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Entrance Room Sun Face Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Eastern Air Gust Room Frozen Switch Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Wizzrobe Chest") +# available_dungeon_locations.append("Stone Tower Temple Inverted Eastern Air Gust Room Fire Chest") +# available_dungeon_locations.append("Stone Tower Temple Entrance Room Lower Chest") +# available_dungeon_locations.append("Stone Tower Temple After Garo Upside Down Chest") +# for i in range(1, 15, 1): +# item_name_groups_to_shuffle.append("Stray Fairy (Woodfall)") +# for i in range(1, 15, 1): +# item_name_groups_to_shuffle.append("Stray Fairy (Snowhead)") +# for i in range(1, 15, 1): +# item_name_groups_to_shuffle.append("Stray Fairy (Great Bay)") +# for i in range(1, 15, 1): +# item_name_groups_to_shuffle.append("Stray Fairy (Stone Tower)") + sword_location = mw.get_location("Link's Inventory (Kokiri Sword)", player) if self.options.swordless.value: sword_location.item_rule = lambda item: item.name != "Progressive Sword" @@ -420,14 +696,15 @@ def fill_slot_data(self): shuffled_pieces = (12 - shp) % 4 return { "skullsanity": self.options.skullsanity.value, - "fairysanity": self.options.fairysanity.value, + "shuffle_stray_fairies": self.options.shuffle_stray_fairies.value, "shopsanity": self.options.shopsanity.value, "scrubsanity": self.options.scrubsanity.value, "shop_prices": self.prices, "shop_prices_ints": self.prices_ints, "cowsanity": self.options.cowsanity.value, - "keysanity": self.options.keysanity.value, - "bosskeysanity": self.options.bosskeysanity.value, + "shuffle_maps_and_compasses": self.options.shuffle_maps_and_compasses.value, + "shuffle_small_keys": self.options.shuffle_small_keys.value, + "shuffle_boss_keys": self.options.shuffle_boss_keys.value, "intro_checks": self.options.intro_checks.value, "curiostity_shop_trades": self.options.curiostity_shop_trades.value, "damage_multiplier": self.options.damage_multiplier.value, From 67fa1fe6e5f6b0bded371022c5f1117c1b28d244 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sun, 17 May 2026 12:37:07 -0300 Subject: [PATCH 07/20] Added lottery shuffle option Currently untested --- worlds/mm_recomp/Items.py | 2 +- worlds/mm_recomp/Locations.py | 3 ++- worlds/mm_recomp/Options.py | 5 +++++ worlds/mm_recomp/__init__.py | 4 ++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 73dd5e46..2be8d94d 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -524,7 +524,7 @@ class MMRItemData(NamedTuple): "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=30 + num_exist=29 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 3b6ef08c..dfd81e81 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -359,7 +359,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "West Clock Town Lottery Any Day": MMRLocationData( region="Clock Town", - address=0x3469420080239 + address=0x3469420080239, + can_create=lambda options: options.shuffle_lottery.value ), "West Clock Town Swordsman Expert Course": MMRLocationData( region="Clock Town", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index d4b25e07..527590d4 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -311,6 +311,10 @@ class ShuffleTreasureChestGame(Choice): option_everything = 2 default = 1 +class ShuffleLottery(Toggle): + """Choose whether to shuffle lottery reward or not.""" + display_name = "Shuffle Lottery" + class StartWithConsumables(DefaultOnToggle): """Choose whether to start with basic consumables (99 rupees, 10 deku sticks, 20 deku nuts).""" @@ -402,6 +406,7 @@ class MMROptions(PerGameCommonOptions): intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame + shuffle_lottery: ShuffleLottery start_with_consumables: StartWithConsumables permanent_chateau_romani: PermanentChateauRomani start_with_inverted_time: StartWithInvertedTime diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 41db6126..8806d0e3 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -238,6 +238,9 @@ def create_items(self) -> None: elif self.options.shuffle_treasure_chest_game.value == 2: self.create_and_add_filler_items(4) + if self.options.shuffle_lottery.value: + self.create_and_add_filler_items(1) + if self.options.shuffle_maps_and_compasses.value == 0: self.create_and_add_filler_items(8) @@ -727,6 +730,7 @@ def fill_slot_data(self): "shuffle_great_fairy_rewards": self.options.shuffle_great_fairy_rewards.value, "shuffle_minigames": self.options.shuffle_minigames.value, "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, + "shuffle_lottery": self.options.shuffle_lottery.value, "link_tunic_color": ((self.options.link_tunic_color.value[0] & 0xFF) << 16) | ((self.options.link_tunic_color.value[1] & 0xFF) << 8) | (self.options.link_tunic_color.value[2] & 0xFF), "random_seed": self.random.getrandbits(32), "logic_difficulty": self.options.logic_difficulty.value From 26c5af849391c7918781f1bc6166d9b68bcda8df Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sun, 17 May 2026 13:52:17 -0300 Subject: [PATCH 08/20] Added beaver race shuffle option Currently untested. --- worlds/mm_recomp/Items.py | 2 +- worlds/mm_recomp/Locations.py | 6 ++++-- worlds/mm_recomp/Options.py | 12 ++++++++++++ worlds/mm_recomp/__init__.py | 6 ++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 2be8d94d..199919fe 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -518,7 +518,7 @@ class MMRItemData(NamedTuple): "Blue Rupee": MMRItemData( code=0x3469420000002, type=ItemClassification.filler, - num_exist=8 + num_exist=6 # ~ num_exist=6 ), "Red Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index dfd81e81..a719f532 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -1226,11 +1226,13 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Beaver Bros. Race 1": MMRLocationData( region="Zora Cape", - address=0x346942009018D + address=0x346942009018D, + can_create=lambda options: options.shuffle_beaver_races.value ), "Beaver Bros. Race 2 HP": MMRLocationData( region="Zora Cape", - address=0x346942007018D + address=0x346942007018D, + can_create=lambda options: options.shuffle_beaver_races.value == 2 ), "Great Bay Great Fairy Reward": MMRLocationData( region="Zora Cape", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 527590d4..0050702d 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -311,6 +311,17 @@ class ShuffleTreasureChestGame(Choice): option_everything = 2 default = 1 +class ShuffleBeaverRace(Range): + """ + Choose how many beaver race rewards are shuffled. + + Valid amounts are within the range 0-2. + """ + display_name = "Shuffle Beaver Race" + range_start = 0 + range_end = 2 + default = 0 + class ShuffleLottery(Toggle): """Choose whether to shuffle lottery reward or not.""" display_name = "Shuffle Lottery" @@ -406,6 +417,7 @@ class MMROptions(PerGameCommonOptions): intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame + shuffle_beaver_races: ShuffleBeaverRace shuffle_lottery: ShuffleLottery start_with_consumables: StartWithConsumables permanent_chateau_romani: PermanentChateauRomani diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 8806d0e3..0159f3f4 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -238,6 +238,11 @@ def create_items(self) -> None: elif self.options.shuffle_treasure_chest_game.value == 2: self.create_and_add_filler_items(4) + if self.options.shuffle_beaver_races.value: + self.create_and_add_filler_items(1) + if self.options.shuffle_beaver_races.value == 2: + self.create_and_add_filler_items(1) + if self.options.shuffle_lottery.value: self.create_and_add_filler_items(1) @@ -730,6 +735,7 @@ def fill_slot_data(self): "shuffle_great_fairy_rewards": self.options.shuffle_great_fairy_rewards.value, "shuffle_minigames": self.options.shuffle_minigames.value, "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, + "shuffle_beaver_races": self.options.shuffle_beaver_races.value, "shuffle_lottery": self.options.shuffle_lottery.value, "link_tunic_color": ((self.options.link_tunic_color.value[0] & 0xFF) << 16) | ((self.options.link_tunic_color.value[1] & 0xFF) << 8) | (self.options.link_tunic_color.value[2] & 0xFF), "random_seed": self.random.getrandbits(32), From 26cc6e1ac8184c39c3cdc91a8f8885ac25a5efe6 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Mon, 18 May 2026 15:40:11 -0300 Subject: [PATCH 09/20] Added picture rewards shuffle option --- worlds/mm_recomp/Items.py | 4 ++-- worlds/mm_recomp/Locations.py | 15 ++++++++++----- worlds/mm_recomp/Options.py | 15 +++++++++++++++ worlds/mm_recomp/__init__.py | 6 ++++++ 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 199919fe..52389f32 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -524,13 +524,13 @@ class MMRItemData(NamedTuple): "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=29 + num_exist=25 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( code=0x3469420000005, type=ItemClassification.filler, - num_exist=11 + num_exist=10 ), "Silver Rupee": MMRItemData( code=0x3469420000006, diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index a719f532..8375ef80 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -571,15 +571,18 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Southern Swamp Winning Picture": MMRLocationData( region="Southern Swamp", - address=0x34694200701C5 + address=0x34694200701C5, + can_create=lambda options: options.shuffle_picture_rewards ), "Southern Swamp Good Picture": MMRLocationData( region="Southern Swamp", - address=0x3469420071C54 + address=0x3469420071C54, + can_create=lambda options: options.shuffle_picture_rewards == 2 ), "Southern Swamp Okay Picture": MMRLocationData( region="Southern Swamp", - address=0x3469420071C52 + address=0x3469420071C52, + can_create=lambda options: options.shuffle_picture_rewards == 2 ), "Southern Swamp Witch Shop Item 1": MMRLocationData( region="Southern Swamp", @@ -1277,11 +1280,13 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Zora Hall Good Picture of Lulu": MMRLocationData( region="Zora Hall", - address=0x3469420082284 + address=0x3469420082284, + can_create=lambda options: options.shuffle_picture_rewards == 2 ), "Zora Hall Bad Picture of Lulu": MMRLocationData( region="Zora Hall", - address=0x3469420082282 + address=0x3469420082282, + can_create=lambda options: options.shuffle_picture_rewards == 2 ), "Pirates' Fortress Sewers Cage HP": MMRLocationData( region="Pirates' Fortress Sewers", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 0050702d..371f9064 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -326,6 +326,20 @@ class ShuffleLottery(Toggle): """Choose whether to shuffle lottery reward or not.""" display_name = "Shuffle Lottery" +class ShufflePictureRewards(Choice): + """ + Choose whether the rewards for the Tourist Center picture contest and the Lulu fan are shuffled or not. + + disabled: Picture rewards will be disabled. + winning_only: Only the winning picture for the Tourist Center reward will be shuffled. + all_pictures: All picture rewards will be shuffled. + """ + display_name = "Shuffle Picture Rewards" + option_disabled = 0 + option_winning_only = 1 + option_all_pictures = 2 + default = 1 + class StartWithConsumables(DefaultOnToggle): """Choose whether to start with basic consumables (99 rupees, 10 deku sticks, 20 deku nuts).""" @@ -419,6 +433,7 @@ class MMROptions(PerGameCommonOptions): shuffle_treasure_chest_game: ShuffleTreasureChestGame shuffle_beaver_races: ShuffleBeaverRace shuffle_lottery: ShuffleLottery + shuffle_picture_rewards: ShufflePictureRewards start_with_consumables: StartWithConsumables permanent_chateau_romani: PermanentChateauRomani start_with_inverted_time: StartWithInvertedTime diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 0159f3f4..b71ee92b 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -246,6 +246,11 @@ def create_items(self) -> None: if self.options.shuffle_lottery.value: self.create_and_add_filler_items(1) + if self.options.shuffle_picture_rewards.value: + self.create_and_add_filler_items(1) + if self.options.shuffle_picture_rewards.value == 2: + self.create_and_add_filler_items(4) + if self.options.shuffle_maps_and_compasses.value == 0: self.create_and_add_filler_items(8) @@ -736,6 +741,7 @@ def fill_slot_data(self): "shuffle_minigames": self.options.shuffle_minigames.value, "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, "shuffle_beaver_races": self.options.shuffle_beaver_races.value, + "shuffle_picture_rewards": self.options.shuffle_picture_rewards.value, "shuffle_lottery": self.options.shuffle_lottery.value, "link_tunic_color": ((self.options.link_tunic_color.value[0] & 0xFF) << 16) | ((self.options.link_tunic_color.value[1] & 0xFF) << 8) | (self.options.link_tunic_color.value[2] & 0xFF), "random_seed": self.random.getrandbits(32), From 9f6f89dacb17403f98fc3da0c2ae07ca10d3ec4c Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Thu, 23 Jul 2026 22:40:44 -0300 Subject: [PATCH 10/20] Changed option PermanentChateauRomani for InfiniteMagicBehavior This name change is for the implementation of a new feature. vanilla: Vanilla behavior, like it was when PermanentChateauRomani was toggled off. consume: Permanent magic when drinking Chateau Romani, like it was when PermanentChateauRomani was toggled on. upgrade: (NEW) Adds a third Progressive Magic in the pool that gives permanent infinite magic. Drinking Chateau Romani before getting the third upgrade will still give infinite magic, but will end after a cycle reset. --- worlds/mm_recomp/Items.py | 2 +- worlds/mm_recomp/Options.py | 16 +++++++++++++--- worlds/mm_recomp/__init__.py | 7 ++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 52389f32..0d47d1d5 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -524,7 +524,7 @@ class MMRItemData(NamedTuple): "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=25 + num_exist=24 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 371f9064..026fcd81 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -346,9 +346,19 @@ class StartWithConsumables(DefaultOnToggle): display_name = "Start With Consumables" -class PermanentChateauRomani(DefaultOnToggle): - """Choose whether the Chateau Romani stays even after a reset.""" +class InfiniteMagicBehavior(Choice): + """ + Choose how infinite magic is handled. + + vanilla: Vanilla behavior. Infinite magic will end after a cycle reset. + consume: Drinking Chateau Romani will give permanent infinite magic and will persist through cycle resets. + upgrade: Adds a third Progressive Magic in the pool that gives permanent infinite magic. Drinking Chateau Romani + before getting the third upgrade will still give infinite magic, but will end after a cycle reset. + """ display_name = "Permanent Chateau Romani" + option_vanilla = 0 + option_consume = 1 + option_upgrade = 2 class StartWithInvertedTime(Toggle): @@ -435,7 +445,7 @@ class MMROptions(PerGameCommonOptions): shuffle_lottery: ShuffleLottery shuffle_picture_rewards: ShufflePictureRewards start_with_consumables: StartWithConsumables - permanent_chateau_romani: PermanentChateauRomani + infinite_magic_behavior: InfiniteMagicBehavior start_with_inverted_time: StartWithInvertedTime receive_filled_wallets: ReceiveFilledWallets magic_is_a_trap: MagicIsATrap diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index b71ee92b..8163c216 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -251,6 +251,11 @@ def create_items(self) -> None: if self.options.shuffle_picture_rewards.value == 2: self.create_and_add_filler_items(4) + if self.options.infinite_magic_behavior. value == 2: + mw.itempool.append(self.create_item("Progressive Magic")) + else: + self.create_and_add_filler_items(1) + if self.options.shuffle_maps_and_compasses.value == 0: self.create_and_add_filler_items(8) @@ -730,7 +735,7 @@ def fill_slot_data(self): "required_skull_tokens": self.options.required_skull_tokens.value, "required_stray_fairies": self.options.required_stray_fairies.value, "start_with_consumables": self.options.start_with_consumables.value, - "permanent_chateau_romani": self.options.permanent_chateau_romani.value, + "infinite_magic_behavior": self.options.infinite_magic_behavior.value, "start_with_inverted_time": self.options.start_with_inverted_time.value, "receive_filled_wallets": self.options.receive_filled_wallets.value, "remains_allow_boss_warps": self.options.remains_allow_boss_warps.value, From 2d7943bab139b57734e8bc205e4e4cc49065178e Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Fri, 24 Jul 2026 18:31:24 -0300 Subject: [PATCH 11/20] Fixed Honey & Darling logic --- worlds/mm_recomp/NormalRules.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/worlds/mm_recomp/NormalRules.py b/worlds/mm_recomp/NormalRules.py index fe784c5f..1bc46d5e 100644 --- a/worlds/mm_recomp/NormalRules.py +++ b/worlds/mm_recomp/NormalRules.py @@ -398,21 +398,25 @@ def get_location_rules(player, options, prices): lambda state: state.has("Progressive Bow", player), "East Clock Town Honey and Darling Any Day": lambda state: ( - state.has("Progressive Bow", player) or - ( - state.has("Progressive Bomb Bag", player) or - has_bombchus(state, player) - ) or + has_bombchus(state, player) or + state.has("Progressive Bomb Bag", player) or + state.has("Progressive Bow", player) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Progressive Magic", player) ) ), "East Clock Town Honey and Darling All Days": lambda state: ( - state.has("Progressive Bow", player) and - state.has("Progressive Bomb Bag", player) and - has_bombchus(state, player) + has_bombchus(state, player) and + state.has("Progressive Bomb Bag", player) and + ( + state.has("Progressive Bow", player) or + ( + state.has("Deku Mask", player) and + state.has("Progressive Magic", player) + ) + ) ), "East Clock Town Treasure Game Chest (Human)": lambda state: True, From f0bbbb95d2127bb9d4f0eb2114968e2cc6416036 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Fri, 24 Jul 2026 18:51:18 -0300 Subject: [PATCH 12/20] Fixed Tingle Stone Tower Map Purchase logic --- worlds/mm_recomp/NormalRules.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/worlds/mm_recomp/NormalRules.py b/worlds/mm_recomp/NormalRules.py index 1bc46d5e..51ffb2cb 100644 --- a/worlds/mm_recomp/NormalRules.py +++ b/worlds/mm_recomp/NormalRules.py @@ -2030,14 +2030,17 @@ def get_location_rules(player, options, prices): "Tingle Stone Tower Map Purchase": lambda state: ( - has_projectiles(state, player) and ( + state.can_reach("Ikana Canyon", 'Region', player) and + can_use_ice_arrows(state, player) and + state.has("Hookshot", player) + ) or + ( + state.can_reach("Great Bay", 'Region', player) and ( - state.can_reach("Ikana Canyon", 'Region', player) and - can_use_ice_arrows(state, player) and - state.has("Hookshot", player) - ) or - state.can_reach("Great Bay", 'Region', player) + state.has("Hookshot", player) or + state.has("Progressive Bow", player) + ) ) ), "Ikana Canyon Spirit House": From 7abff4a5129ffb7494ceea1908e3928c107c5213 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Fri, 24 Jul 2026 19:23:53 -0300 Subject: [PATCH 13/20] Fixed H&D All Days logic (I made the initial mistake) --- worlds/mm_recomp/NormalRules.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/worlds/mm_recomp/NormalRules.py b/worlds/mm_recomp/NormalRules.py index 51ffb2cb..2abf014b 100644 --- a/worlds/mm_recomp/NormalRules.py +++ b/worlds/mm_recomp/NormalRules.py @@ -410,13 +410,7 @@ def get_location_rules(player, options, prices): lambda state: ( has_bombchus(state, player) and state.has("Progressive Bomb Bag", player) and - ( - state.has("Progressive Bow", player) or - ( - state.has("Deku Mask", player) and - state.has("Progressive Magic", player) - ) - ) + state.has("Progressive Bow", player) ), "East Clock Town Treasure Game Chest (Human)": lambda state: True, From 8874fac64ded426468f7e7001edddde534346911 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sat, 25 Jul 2026 22:28:06 -0300 Subject: [PATCH 14/20] Added option to customize max shop price, prices are now multiples of 5 --- worlds/mm_recomp/Options.py | 36 ++++++++++++++++++++++++------------ worlds/mm_recomp/__init__.py | 11 +---------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 026fcd81..d0916237 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -2,7 +2,7 @@ from typing import Dict -from Options import Choice, Option, DefaultOnToggle, Toggle, Range, OptionList, StartInventoryPool, DeathLink, PerGameCommonOptions +from Options import Choice, Option, DefaultOnToggle, Toggle, Range, NamedRange, OptionList, StartInventoryPool, DeathLink, PerGameCommonOptions class LogicDifficulty(Choice): @@ -151,23 +151,34 @@ class Scrubsanity(Toggle): display_name = "Shuffle Business Scrub Purchases" class ShopPrices(Choice): - """Choose how expensive shop items are. - These only apply to the main shops of the game. - This has no effect if shopsanity is disabled. + """ + Choose whether prices for shop items are vanilla or random. + This only apply to the main shops of the game. This has no effect if shopsanity is disabled. vanilla: Shop items have their normal prices. - free: All shop items are free and cost 0 Rupees. - cheap: Shop items vary in price but can all be purchased with the starting wallet. - expensive: Shop items vary in price but may require the Adult's Wallet. No shop items will require the Giant's Wallet. - offensive: Shop items vary in price but may require the Adult's Wallet and sometimes even the Giant's Wallet.""" + randomized: Shop items have their prices randomized. The maximum price can be configured in max_shop_prices. + """ display_name = "Shop Prices" option_vanilla = 0 - option_free = 1 - option_cheap = 2 - option_expensive = 3 - option_offensive = 4 + option_randomized = 1 default = 0 +class MaxShopPrices(NamedRange): + """ + Choose the maximum price shop items can be. This only has an effect if shop_prices is set to random. + """ + display_name = "Maximum Shop Prices" + range_start = 0 + range_end = 500 + default = 300 + special_range_names = { + "balanced": 300, + "free": 0, + "child": 99, + "adult": 200, + "giant": 500, + } + class Cowsanity(Toggle): """Choose whether to shuffle Cows.""" @@ -430,6 +441,7 @@ class MMROptions(PerGameCommonOptions): shopsanity: Shopsanity scrubsanity: Scrubsanity shop_prices: ShopPrices + max_shop_prices: MaxShopPrices cowsanity: Cowsanity shuffle_great_fairy_rewards: ShuffleGreatFairyRewards required_stray_fairies: RequiredStrayFairies diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 8163c216..afb8cfae 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -96,21 +96,12 @@ def generate_early(self): self.prices = "" if self.options.shopsanity.value != 0: - price_max = 0 - - if self.options.shop_prices.value == 2: - price_max = 99 - elif self.options.shop_prices.value == 3: - price_max = 200 - elif self.options.shop_prices.value == 4: - price_max = 500 - # There are 34 (+2 fake) shop locations that need prices for i in range(0, 36): if self.options.shop_prices.value == 0: price = default_shop_prices[i] else: - price = self.random.randint(0, price_max) + price = self.random.randrange(0, self.options.max_shop_prices.value, 5) self.prices_ints.append(price) self.prices += str(price) + " " From bede2538e65ae8dc49c3f3c168416cca30ab0547 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Fri, 31 Jul 2026 16:14:46 -0300 Subject: [PATCH 15/20] Prepared option groups It's commented out because I can't generate a template YAML if I import mm_option_groups from .Options and I have no idea why... --- worlds/mm_recomp/Locations.py | 8 ++--- worlds/mm_recomp/Options.py | 68 ++++++++++++++++++++++++++++++++--- worlds/mm_recomp/__init__.py | 6 ++-- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 8375ef80..16780e4d 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -206,22 +206,22 @@ def can_create_heart_location(shp, c_or_p, loc_index): "Curiosity Shop Blue Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C402, - can_create=lambda options: options.curiostity_shop_trades.value + can_create=lambda options: options.curiosity_shop_trades.value ), "Curiosity Shop Red Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C404, - can_create=lambda options: options.curiostity_shop_trades.value + can_create=lambda options: options.curiosity_shop_trades.value ), "Curiosity Shop Purple Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C405, - can_create=lambda options: options.curiostity_shop_trades.value + can_create=lambda options: options.curiosity_shop_trades.value ), "Curiosity Shop Gold Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C407, - can_create=lambda options: options.curiostity_shop_trades.value + can_create=lambda options: options.curiosity_shop_trades.value ), "Curiosity Shop Night 3 (Stop Thief)": MMRLocationData( region="Clock Town", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index d0916237..3c8e9201 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -2,7 +2,7 @@ from typing import Dict -from Options import Choice, Option, DefaultOnToggle, Toggle, Range, NamedRange, OptionList, StartInventoryPool, DeathLink, PerGameCommonOptions +from Options import Choice, Option, DefaultOnToggle, Toggle, Range, NamedRange, OptionList, StartInventoryPool, DeathLink, OptionGroup, PerGameCommonOptions class LogicDifficulty(Choice): @@ -283,9 +283,9 @@ class ShuffleBossKeys(DungeonItems): default = 4 -class CuriostityShopTrades(Toggle): - """Choose whether to shuffle the rupees given for trading bottled items at the Curiostity Shop.""" - display_name = "Curiostity Shop Trades" +class CuriosityShopTrades(Toggle): + """Choose whether to shuffle the rupees given for trading bottled items at the Curiosity Shop.""" + display_name = "Curiosity Shop Trades" class IntroChecks(Toggle): @@ -420,6 +420,64 @@ class LinkTunicColor(OptionList): default = [30, 105, 27] +#mm_option_groups = [ +# OptionGroup("Goal Requirements", [ +# MajoraRemainsRequired, +# MoonRemainsRequired, +# ]), +# OptionGroup("Starting Item Shuffle", [ +# Swordless, +# Shieldless, +# StartingHeartQuarters, +# StartingHeartsAreContainersOrPieces, +# ]), +# OptionGroup("Dungeon Options", [ +# ShuffleBossRemains, +# BossWarpsWithRemains, +# ShuffleMapsAndCompasses, +# ShuffleSmallKeys, +# ShuffleBossKeys, +# ShuffleStrayFairies, +# RequiredStrayFairies, +# ShuffleGreatFairyRewards, +# ]), +# OptionGroup("Shuffles" [ +# ShuffleRegionalMaps, +# ShuffleMinigames, +# ShuffleTreasureChestGame, +# ShuffleLottery, +# ShufflePictureRewards, +# ShuffleBeaverRace, +# IntroChecks, +# ]), +# OptionGroup("Sanities", [ +# Skullsanity, +# RequiredSkullTokens, +# ShuffleSpiderHouseReward, +# Shopsanity, +# ShopPrices, +# MaxShopPrices, +# Scrubsanity, +# CuriosityShopTrades, +# Cowsanity, +# ]), +# OptionGroup("Quality of Life", [ +# StartWithSoaring, +# StartWithInvertedTime, +# StartWithConsumables, +# ReceiveFilledWallets, +# InfiniteMagicBehavior, +# CAMC, +# ]), +# OptionGroup("Other", [ +# MagicIsATrap, +# DamageMultiplier, +# DeathBehavior, +# LinkTunicColor, +# ]), +#] + + @dataclass class MMROptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool @@ -449,7 +507,7 @@ class MMROptions(PerGameCommonOptions): shuffle_maps_and_compasses: ShuffleMapsAndCompasses shuffle_small_keys: ShuffleSmallKeys shuffle_boss_keys: ShuffleBossKeys - curiostity_shop_trades: CuriostityShopTrades + curiosity_shop_trades: CuriosityShopTrades intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index afb8cfae..bba49498 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -26,6 +26,8 @@ class MMRWebWorld(WebWorld): tutorials = [setup_en] + #option_groups = mm_option_groups + class MMRWorld(World): """A Zelda game we're not completely burnt out on.""" @@ -192,7 +194,7 @@ def create_items(self) -> None: mw.push_precollected(self.create_item("Boss Key (Great Bay)")) mw.push_precollected(self.create_item("Boss Key (Stone Tower)")) - if self.options.curiostity_shop_trades.value: + if self.options.curiosity_shop_trades.value: mw.itempool.append(self.create_item("Blue Rupee")) mw.itempool.append(self.create_item("Red Rupee")) mw.itempool.append(self.create_item("Purple Rupee")) @@ -715,7 +717,7 @@ def fill_slot_data(self): "shuffle_small_keys": self.options.shuffle_small_keys.value, "shuffle_boss_keys": self.options.shuffle_boss_keys.value, "intro_checks": self.options.intro_checks.value, - "curiostity_shop_trades": self.options.curiostity_shop_trades.value, + "curiosity_shop_trades": self.options.curiosity_shop_trades.value, "damage_multiplier": self.options.damage_multiplier.value, "death_behavior": self.options.death_behavior.value, "death_link": self.options.death_link.value, From fce5068c6c15ac900d4a7a1d1e70a8dd5a2cf7d0 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sat, 1 Aug 2026 00:02:57 -0300 Subject: [PATCH 16/20] Added option to skip Graveyard night 3 Dampe digging This will instantly spawn the big poe, skipping the whole digging part. --- worlds/mm_recomp/Locations.py | 8 ++-- worlds/mm_recomp/Options.py | 70 +++++------------------------------ worlds/mm_recomp/__init__.py | 7 ++-- 3 files changed, 16 insertions(+), 69 deletions(-) diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 16780e4d..8375ef80 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -206,22 +206,22 @@ def can_create_heart_location(shp, c_or_p, loc_index): "Curiosity Shop Blue Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C402, - can_create=lambda options: options.curiosity_shop_trades.value + can_create=lambda options: options.curiostity_shop_trades.value ), "Curiosity Shop Red Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C404, - can_create=lambda options: options.curiosity_shop_trades.value + can_create=lambda options: options.curiostity_shop_trades.value ), "Curiosity Shop Purple Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C405, - can_create=lambda options: options.curiosity_shop_trades.value + can_create=lambda options: options.curiostity_shop_trades.value ), "Curiosity Shop Gold Rupee Trade": MMRLocationData( region="Clock Town", address=0x346942007C407, - can_create=lambda options: options.curiosity_shop_trades.value + can_create=lambda options: options.curiostity_shop_trades.value ), "Curiosity Shop Night 3 (Stop Thief)": MMRLocationData( region="Clock Town", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 3c8e9201..94ed690e 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -2,7 +2,7 @@ from typing import Dict -from Options import Choice, Option, DefaultOnToggle, Toggle, Range, NamedRange, OptionList, StartInventoryPool, DeathLink, OptionGroup, PerGameCommonOptions +from Options import Choice, DefaultOnToggle, Toggle, Range, NamedRange, OptionList, StartInventoryPool, DeathLink, PerGameCommonOptions class LogicDifficulty(Choice): @@ -283,7 +283,7 @@ class ShuffleBossKeys(DungeonItems): default = 4 -class CuriosityShopTrades(Toggle): +class CuriostityShopTrades(Toggle): """Choose whether to shuffle the rupees given for trading bottled items at the Curiosity Shop.""" display_name = "Curiosity Shop Trades" @@ -372,6 +372,11 @@ class InfiniteMagicBehavior(Choice): option_upgrade = 2 +class SkipDigging(Toggle): + """Enabling this will skip the Graveyard night 3 grave digging section, immediately spawning the big poe.""" + display_name = "Skip Digging" + + class StartWithInvertedTime(Toggle): """Choose whether time starts out inverted at Day 1, even after a reset.""" display_name = "Reset With Inverted Time" @@ -420,64 +425,6 @@ class LinkTunicColor(OptionList): default = [30, 105, 27] -#mm_option_groups = [ -# OptionGroup("Goal Requirements", [ -# MajoraRemainsRequired, -# MoonRemainsRequired, -# ]), -# OptionGroup("Starting Item Shuffle", [ -# Swordless, -# Shieldless, -# StartingHeartQuarters, -# StartingHeartsAreContainersOrPieces, -# ]), -# OptionGroup("Dungeon Options", [ -# ShuffleBossRemains, -# BossWarpsWithRemains, -# ShuffleMapsAndCompasses, -# ShuffleSmallKeys, -# ShuffleBossKeys, -# ShuffleStrayFairies, -# RequiredStrayFairies, -# ShuffleGreatFairyRewards, -# ]), -# OptionGroup("Shuffles" [ -# ShuffleRegionalMaps, -# ShuffleMinigames, -# ShuffleTreasureChestGame, -# ShuffleLottery, -# ShufflePictureRewards, -# ShuffleBeaverRace, -# IntroChecks, -# ]), -# OptionGroup("Sanities", [ -# Skullsanity, -# RequiredSkullTokens, -# ShuffleSpiderHouseReward, -# Shopsanity, -# ShopPrices, -# MaxShopPrices, -# Scrubsanity, -# CuriosityShopTrades, -# Cowsanity, -# ]), -# OptionGroup("Quality of Life", [ -# StartWithSoaring, -# StartWithInvertedTime, -# StartWithConsumables, -# ReceiveFilledWallets, -# InfiniteMagicBehavior, -# CAMC, -# ]), -# OptionGroup("Other", [ -# MagicIsATrap, -# DamageMultiplier, -# DeathBehavior, -# LinkTunicColor, -# ]), -#] - - @dataclass class MMROptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool @@ -507,7 +454,7 @@ class MMROptions(PerGameCommonOptions): shuffle_maps_and_compasses: ShuffleMapsAndCompasses shuffle_small_keys: ShuffleSmallKeys shuffle_boss_keys: ShuffleBossKeys - curiosity_shop_trades: CuriosityShopTrades + curiostity_shop_trades: CuriostityShopTrades intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame @@ -516,6 +463,7 @@ class MMROptions(PerGameCommonOptions): shuffle_picture_rewards: ShufflePictureRewards start_with_consumables: StartWithConsumables infinite_magic_behavior: InfiniteMagicBehavior + skip_digging: SkipDigging start_with_inverted_time: StartWithInvertedTime receive_filled_wallets: ReceiveFilledWallets magic_is_a_trap: MagicIsATrap diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index bba49498..0d22c02a 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -26,8 +26,6 @@ class MMRWebWorld(WebWorld): tutorials = [setup_en] - #option_groups = mm_option_groups - class MMRWorld(World): """A Zelda game we're not completely burnt out on.""" @@ -194,7 +192,7 @@ def create_items(self) -> None: mw.push_precollected(self.create_item("Boss Key (Great Bay)")) mw.push_precollected(self.create_item("Boss Key (Stone Tower)")) - if self.options.curiosity_shop_trades.value: + if self.options.curiostity_shop_trades.value: mw.itempool.append(self.create_item("Blue Rupee")) mw.itempool.append(self.create_item("Red Rupee")) mw.itempool.append(self.create_item("Purple Rupee")) @@ -717,7 +715,7 @@ def fill_slot_data(self): "shuffle_small_keys": self.options.shuffle_small_keys.value, "shuffle_boss_keys": self.options.shuffle_boss_keys.value, "intro_checks": self.options.intro_checks.value, - "curiosity_shop_trades": self.options.curiosity_shop_trades.value, + "curiostity_shop_trades": self.options.curiostity_shop_trades.value, "damage_multiplier": self.options.damage_multiplier.value, "death_behavior": self.options.death_behavior.value, "death_link": self.options.death_link.value, @@ -729,6 +727,7 @@ def fill_slot_data(self): "required_stray_fairies": self.options.required_stray_fairies.value, "start_with_consumables": self.options.start_with_consumables.value, "infinite_magic_behavior": self.options.infinite_magic_behavior.value, + "skip_digging": self.options.skip_digging.value, "start_with_inverted_time": self.options.start_with_inverted_time.value, "receive_filled_wallets": self.options.receive_filled_wallets.value, "remains_allow_boss_warps": self.options.remains_allow_boss_warps.value, From 2d56a66d94dc286e6e7d4067159634bbce6aef4b Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Sun, 2 Aug 2026 00:10:31 -0300 Subject: [PATCH 17/20] Added option to disable Zora Cape Pot Game --- worlds/mm_recomp/Locations.py | 3 ++- worlds/mm_recomp/Options.py | 5 +++++ worlds/mm_recomp/__init__.py | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 8375ef80..958bcb54 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -1213,7 +1213,8 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Zora Cape Pot Game": MMRLocationData( region="Zora Cape", - address=0x3469420072806 + address=0x3469420072806, + can_create=lambda options: options.shuffle_zora_pot_game.value ), "Zora Cape Deku Flower Chest": MMRLocationData( region="Zora Cape", diff --git a/worlds/mm_recomp/Options.py b/worlds/mm_recomp/Options.py index 94ed690e..02e14344 100644 --- a/worlds/mm_recomp/Options.py +++ b/worlds/mm_recomp/Options.py @@ -322,6 +322,10 @@ class ShuffleTreasureChestGame(Choice): option_everything = 2 default = 1 +class ShuffleZoraPotGame(Toggle): + """Enabling this will shuffle the Zora pot minigame at Zora Cape.""" + display_name = "Shuffle Zora Pot Game" + class ShuffleBeaverRace(Range): """ Choose how many beaver race rewards are shuffled. @@ -458,6 +462,7 @@ class MMROptions(PerGameCommonOptions): intro_checks: IntroChecks shuffle_minigames: ShuffleMinigames shuffle_treasure_chest_game: ShuffleTreasureChestGame + shuffle_zora_pot_game: ShuffleZoraPotGame shuffle_beaver_races: ShuffleBeaverRace shuffle_lottery: ShuffleLottery shuffle_picture_rewards: ShufflePictureRewards diff --git a/worlds/mm_recomp/__init__.py b/worlds/mm_recomp/__init__.py index 0d22c02a..fef2f751 100644 --- a/worlds/mm_recomp/__init__.py +++ b/worlds/mm_recomp/__init__.py @@ -229,6 +229,9 @@ def create_items(self) -> None: elif self.options.shuffle_treasure_chest_game.value == 2: self.create_and_add_filler_items(4) + if self.options.shuffle_zora_pot_game.value == 0: + self.create_and_add_filler_items(1) + if self.options.shuffle_beaver_races.value: self.create_and_add_filler_items(1) if self.options.shuffle_beaver_races.value == 2: @@ -737,6 +740,7 @@ def fill_slot_data(self): "shuffle_great_fairy_rewards": self.options.shuffle_great_fairy_rewards.value, "shuffle_minigames": self.options.shuffle_minigames.value, "shuffle_treasure_chest_game": self.options.shuffle_treasure_chest_game.value, + "shuffle_zora_pot_game": self.options.shuffle_zora_pot_game.value, "shuffle_beaver_races": self.options.shuffle_beaver_races.value, "shuffle_picture_rewards": self.options.shuffle_picture_rewards.value, "shuffle_lottery": self.options.shuffle_lottery.value, From aa8c93016c9101523a7a009634305803ea386cab Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Mon, 3 Aug 2026 02:06:20 -0300 Subject: [PATCH 18/20] Many changes to approximately match 1.0's - New regions; - Added 3rd empty bottle; - Logic parity with 1.0 with some differences and fixes that are not implemented in 1.0. --- worlds/mm_recomp/Items.py | 4 +- worlds/mm_recomp/Locations.py | 54 +- worlds/mm_recomp/NormalRules.py | 1410 +++++++++++++++++-------------- worlds/mm_recomp/Regions.py | 50 +- 4 files changed, 857 insertions(+), 661 deletions(-) diff --git a/worlds/mm_recomp/Items.py b/worlds/mm_recomp/Items.py index 0d47d1d5..420a461d 100644 --- a/worlds/mm_recomp/Items.py +++ b/worlds/mm_recomp/Items.py @@ -255,7 +255,7 @@ class MMRItemData(NamedTuple): "Bottle": MMRItemData( code=0x346942000005A, type=ItemClassification.progression, - num_exist=2 + num_exist=3 ), "Bottle of Milk": MMRItemData( code=0x3469420000060, @@ -524,7 +524,7 @@ class MMRItemData(NamedTuple): "Red Rupee": MMRItemData( code=0x3469420000004, type=ItemClassification.filler, - num_exist=24 + num_exist=23 # ~ num_exist=29 ), "Purple Rupee": MMRItemData( diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index 958bcb54..d75e08c5 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -527,20 +527,20 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x346942007024C ), "Road to Swamp Tree HP": MMRLocationData( - region="Southern Swamp", + region="Road to Southern Swamp", address=0x3469420054001 ), "Road to Swamp Grotto Chest": MMRLocationData( - region="Southern Swamp", + region="Road to Southern Swamp", address=0x346942006071E ), "Swamp Shooting Gallery 2120 Points": MMRLocationData( - region="Southern Swamp", + region="Road to Southern Swamp", address=0x3469420000024, can_create=lambda options: options.shuffle_minigames.value != 0 ), "Swamp Shooting Gallery 2180 Points": MMRLocationData( - region="Southern Swamp", + region="Road to Southern Swamp", address=0x346942008011D, can_create=lambda options: options.shuffle_minigames.value == 2 ), @@ -880,11 +880,11 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x3469420011B2D ), "Woodfall Temple Heart Container": MMRLocationData( - region="Woodfall Temple", + region="Odolwa's Lair", address=0x3469420051F00 ), "Woodfall Temple Odolwa's Remains": MMRLocationData( - region="Woodfall Temple", + region="Odolwa's Lair", address=0x3469420000055 ), "Southern Swamp Boat Archery": MMRLocationData( @@ -940,7 +940,7 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x3469420060702 ), "Goron Racetrack Prize": MMRLocationData( - region="Twin Islands", + region="Goron Racetrack", address=0x346942000006A ), "Goron Village Lens Cave Rock Chest": MMRLocationData( @@ -956,36 +956,36 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x3469420060701 ), "Goron Village Baby Goron Lullaby": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x34694200000AD ), "Goron Village Shop Item 1": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x346942009001E, can_create=lambda options: options.shopsanity.value != 0 ), "Goron Village Shop Item 2": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x346942009001F, can_create=lambda options: options.shopsanity.value != 0 ), "Goron Village Shop Item 3": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x3469420090020, can_create=lambda options: options.shopsanity.value != 0 ), "Goron Village Shop (Spring) Item 1": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x3469420090021, can_create=lambda options: options.shopsanity.value == 2 ), "Goron Village Shop (Spring) Item 2": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x3469420090022, can_create=lambda options: options.shopsanity.value == 2 ), "Goron Village Shop (Spring) Item 3": MMRLocationData( - region="Goron Village", + region="Goron Shrine", address=0x3469420090023, can_create=lambda options: options.shopsanity.value == 2 ), @@ -1113,11 +1113,11 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x3469420062117 ), "Snowhead Temple Heart Container": MMRLocationData( - region="Snowhead Temple", + region="Goht's Lair", address=0x3469420054400 ), "Snowhead Temple Goht's Remains": MMRLocationData( - region="Snowhead Temple", + region="Goht's Lair", address=0x3469420000056 ), "Milk Road Gorman Ranch Race": MMRLocationData( @@ -1589,11 +1589,11 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x346942001492F ), "Great Bay Temple Heart Container": MMRLocationData( - region="Great Bay Temple", + region="Gyorg's Lair", address=0x3469420055F00 ), "Great Bay Temple Gyorg's Remains": MMRLocationData( - region="Great Bay Temple", + region="Gyorg's Lair", address=0x3469420000057 ), "Road to Ikana Pillar Chest": MMRLocationData( @@ -1657,32 +1657,32 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x346942006600A ), "Ikana Canyon Grotto Chest": MMRLocationData( - region="Ikana Canyon", + region="Lower Ikana Canyon", address=0x3469420060714 ), "Ikana Canyon Scrub Purchase": MMRLocationData( - region="Ikana Canyon", + region="Lower Ikana Canyon", address=0x346942009015D, can_create=lambda options: options.scrubsanity.value ), "Ikana Canyon Zora Scrub Trade": MMRLocationData( - region="Ikana Canyon", + region="Lower Ikana Canyon", address=0x3469420001307 ), "Ikana Canyon Zora Trade Freestanding HP": MMRLocationData( - region="Ikana Canyon", + region="Lower Ikana Canyon", address=0x346942005131E ), "Ikana Canyon Healing Pamela's Father": MMRLocationData( - region="Ikana Canyon", + region="Upper Ikana Canyon", address=0x3469420000087 ), "Ikana Canyon Spirit House": MMRLocationData( - region="Ikana Canyon", + region="Upper Ikana Canyon", address=0x34694200701DE ), "Stone Tower Great Fairy Reward": MMRLocationData( - region="Ikana Canyon", + region="Upper Ikana Canyon", address=0x3469420030004, can_create=lambda options: options.shuffle_great_fairy_rewards.value != 0 ), @@ -1824,11 +1824,11 @@ def can_create_heart_location(shp, c_or_p, loc_index): address=0x346942006181A ), "Stone Tower Temple Inverted Heart Container": MMRLocationData( - region="Stone Tower Temple (Inverted)", + region="Twinmold's Lair", address=0x3469420053600 ), "Stone Tower Temple Inverted Twinmold's Remains": MMRLocationData( - region="Stone Tower Temple (Inverted)", + region="Twinmold's Lair", address=0x3469420000058 ), "Oath to Order": MMRLocationData( diff --git a/worlds/mm_recomp/NormalRules.py b/worlds/mm_recomp/NormalRules.py index 2abf014b..e4bf5746 100644 --- a/worlds/mm_recomp/NormalRules.py +++ b/worlds/mm_recomp/NormalRules.py @@ -4,43 +4,42 @@ def can_play_song(song, state, player): return state.has(song, player) and state.has("Ocarina of Time", player) def can_get_magic_beans(state, player): - return (state.has("Magic Bean", player) and - state.has("Deku Mask", player) and - state.can_reach("Deku Palace", 'Region', player)) + return (state.has("Magic Bean", player) and + ( + state.can_reach("Deku Palace", 'Region', player) or + ( + state.has("Deku Mask", player) and + state.can_reach("Southern Swamp", 'Region', player) + ) + ) + ) def has_bombchus(state, player): return state.has("Progressive Bombchu Bag", player) def has_explosives(state, player): - return (state.has("Progressive Bomb Bag", player) or - has_bombchus(state, player) or + return (state.has("Progressive Bomb Bag", player) or + has_bombchus(state, player) or state.has("Blast Mask", player)) def has_hard_projectiles(state, player): - return (state.has("Progressive Bow", player) or - state.has("Zora Mask", player) or + return (state.has("Progressive Bow", player) or + state.has("Zora Mask", player) or state.has("Hookshot", player)) def has_projectiles(state, player): - return ((state.has("Deku Mask", player) and state.has("Progressive Magic", player)) or + return ((state.has("Deku Mask", player) and state.has("Progressive Magic", player)) or has_hard_projectiles(state, player)) def can_smack_hard(state, player): - return (state.has("Progressive Sword", player) or - state.has("Fierce Deity's Mask", player) or - state.has("Great Fairy Sword", player) or - state.has("Goron Mask", player) or + return (state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) or + state.has("Goron Mask", player) or state.has("Zora Mask", player)) def can_smack(state, player): return can_smack_hard(state, player) or state.has("Deku Mask", player) -def can_reach_stonetower(state, player): - return (can_use_ice_arrows(state, player) and - can_play_song("Elegy of Emptiness", state, player) and - state.has("Goron Mask", player) and - state.has("Zora Mask", player)) - def can_clear_woodfall(state, player): return state.can_reach("Woodfall Temple Odolwa's Remains", 'Location', player) @@ -54,32 +53,50 @@ def can_clear_stonetower(state, player): return state.can_reach("Stone Tower Temple Inverted Twinmold's Remains", 'Location', player) def has_paper(state, player): - return (state.has("Land Title Deed", player) or - state.has("Swamp Title Deed", player) or - state.has("Mountain Title Deed", player) or - state.has("Ocean Title Deed", player) or - state.has("Letter to Kafei", player) or + return (state.has("Land Title Deed", player) or + state.has("Swamp Title Deed", player) or + state.has("Mountain Title Deed", player) or + state.has("Ocean Title Deed", player) or + state.has("Letter to Kafei", player) or state.has("Priority Mail", player)) def can_get_cow_milk(state, player): - return (has_bottle(state, player) and - can_play_song("Epona's Song", state, player) and - (has_explosives(state, player) or - can_use_powder_keg(state, player) or - state.has("Hookshot", player) or - (state.has("Gibdo Mask", player) and - has_bottle(state, player) and - can_plant_beans(state, player) and - state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) or - can_use_light_arrows(state, player) and - (state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) or - (state.has("Goron Mask", player) and - can_use_lens(state, player)) or - state.can_reach("Ikana Well Invisible Chest", 'Location', player))))) + return ( + has_bottle(state, player) and + can_play_song("Epona's Song", state, player) and + ( + has_explosives(state, player) or + can_use_powder_keg(state, player) or + state.has("Hookshot", player) or + state.has("Romani's Mask", player) or + ( + state.can_reach("Beneath the Well", 'Region', player) and + state.has("Gibdo Mask", player) and + ( + can_plant_beans(state, player) and + state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) + ) or + ( + can_use_light_arrows(state, player) and + ( + state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) or + ( + state.has("Goron Mask", player) and + can_use_lens(state, player) and + can_play_song("Song of Soaring", state, player) + ) or + state.can_reach("Ikana Well Invisible Chest", 'Location', player) + ) + ) + ) + ) + ) def has_bottle(state, player, need_count=1): bottle_count = 0 - if state.has("Bottle", player, 2): + if state.has("Bottle", player, 3): + bottle_count += 3 + elif state.has("Bottle", player, 2): bottle_count += 2 elif state.has("Bottle", player): bottle_count += 1 @@ -91,17 +108,53 @@ def has_bottle(state, player, need_count=1): bottle_count += 1 return bottle_count >= need_count +def can_get_red_potion(state, player, prices, options): + return ( + has_bottle(state, player) and + ( + ( + can_purchase(state, player, prices, options, SHOP_ID_WITCH_POTION_3) + ) or + ( + state.can_reach("Clock Town Trading Post Shop Item 1", "Location", player) or + ( + options.shopsanity.value == 2 and + state.can_reach("Clock Town Trading Post Shop (Night) Item 1", "Location", player) + ) + ) + ) + ) + +def can_get_blue_potion(state, player, prices, options): + return ( + has_bottle(state, player) and + ( + state.can_reach("Southern Swamp Witch Shop Item 1", "Location", player) or + state.can_reach("Ikana Canyon Scrub Purchase", "Location", player) + ) + ) + def can_plant_beans(state, player): - return (can_get_magic_beans(state, player) and - (has_bottle(state, player) or can_play_song("Song of Storms", state, player))) + return ( + can_get_magic_beans(state, player) and + ( + has_bottle(state, player) or + can_play_song("Song of Storms", state, player) + ) + ) def can_use_powder_keg(state, player): - return state.has("Powder Keg", player) and state.has("Goron Mask", player) + return ( + state.has("Powder Keg", player) and + state.has("Goron Mask", player) + ) def can_use_magic_arrow(item, state, player): - return (state.has(item, player) and - state.has("Progressive Bow", player) and - state.has("Progressive Magic", player)) + return ( + state.has(item, player) and + state.has("Progressive Bow", player) and + state.has("Progressive Magic", player) + ) def can_use_fire_arrows(state, player): return can_use_magic_arrow("Fire Arrow", state, player) @@ -112,8 +165,9 @@ def can_use_ice_arrows(state, player): def can_use_light_arrows(state, player): return can_use_magic_arrow("Light Arrow", state, player) -def has_gilded_sword(state, player): - return state.has("Progressive Sword", player, 3) +# Unused +# def has_gilded_sword(state, player): +# return state.has("Progressive Sword", player, 3) def has_mirror_shield(state, player): return state.has("Progressive Shield", player, 2) @@ -125,14 +179,24 @@ def can_bring_to_player(state, player): return state.has("Hookshot", player) or state.has("Zora Mask", player) def can_reach_scarecrow(state, player): - return (state.can_reach("Astral Observatory", 'Region', player) or - state.can_reach("Trading Post", 'Region', player)) + return ( + state.has("Ocarina of Time", player) and + ( + state.can_reach("Astral Observatory", 'Region', player) or + state.can_reach("Trading Post", 'Region', player) + ) + ) def can_reach_seahorse(state, player): - return (state.can_reach("Fisherman's House", 'Region', player) and - state.has("Zora Mask", player) and - state.has("Pictograph Box", player) and - (state.has("Hookshot", player) or state.has("Goron Mask", player))) + return ( + state.can_reach("Fisherman's House", 'Region', player) and + state.has("Zora Mask", player) and + state.has("Pictograph Box", player) and + ( + state.has("Hookshot", player) or + state.has("Goron Mask", player) + ) + ) def can_afford_price(state, player, price): if price > 200: @@ -165,19 +229,19 @@ def get_region_rules(player, options): return { "Clock Town -> The Moon": lambda state: ( - state.has("Ocarina of Time", player) and - state.has("Oath to Order", player) and + state.has("Ocarina of Time", player) and + state.has("Oath to Order", player) and has_enough_remains(state, player, options.moon_remains_required.value) ), "Southern Swamp -> Southern Swamp (Deku Palace)": lambda state: ( - has_bottle(state, player) or + has_bottle(state, player) or ( - has_hard_projectiles(state, player) and + has_hard_projectiles(state, player) and state.has("Deku Mask", player) - ) or + ) or ( - state.has("Pictograph Box", player) and + state.has("Pictograph Box", player) and state.has("Deku Mask", player) ) ), @@ -188,26 +252,74 @@ def get_region_rules(player, options): "Southern Swamp (Deku Palace) -> Woodfall": lambda state: state.has("Deku Mask", player), "Woodfall -> Woodfall Temple": - lambda state: can_play_song("Sonata of Awakening", state, player), + lambda state: ( + state.has("Deku Mask", player) and + can_play_song("Sonata of Awakening", state, player) + ), + "Woodfall Temple -> Odolwa's Lair": + lambda state: ( + state.has("Progressive Bow", player) and + ( + can_smack(state, player) or + state.has("Fierce Deity Mask", player) + ) and ( + state.has("Boss Key (Woodfall)", player) or + ( + state.has("Odolwa's Remains", player) and + options.remains_allow_boss_warps.value + ) + ) + ), "Termina Field -> Path to Mountain Village": lambda state: state.has("Progressive Bow", player), "Path to Mountain Village -> Mountain Village": lambda state: ( - state.has("Goron Mask", player) or - has_explosives(state, player) or + state.has("Goron Mask", player) or + has_explosives(state, player) or can_use_fire_arrows(state, player) ), + "Twin Islands -> Goron Village": + lambda state: True, + "Goron Village -> Goron Shrine": + lambda state: state.has("Goron Mask", player), + "Twin Islands -> Goron Racetrack": + lambda state: ( + state.has("Goron Mask", player) and + ( + state.has("Powder Keg", player) or + can_use_fire_arrows(state, player) + ) + ), "Path to Snowhead -> Snowhead": lambda state: ( - state.has("Goron Mask", player) and - can_play_song("Goron Lullaby", state, player) and + state.has("Goron Mask", player) and + can_play_song("Goron Lullaby", state, player) and state.has("Progressive Magic", player) ), - "Snowhead -> Snowhead Temple": lambda state: ( - state.has("Goron Mask", player) and - can_play_song("Goron Lullaby", state, player) and + "Snowhead -> Snowhead Temple": + lambda state: ( + state.has("Goron Mask", player) and + can_play_song("Goron Lullaby", state, player) and state.has("Progressive Magic", player) - ), + ), + "Snowhead Temple -> Goht's Lair": + lambda state: ( + can_use_fire_arrows(state, player) and + ( + ( + state.has("Small Key (Snowhead)", player, 1) and + state.has("Boss Key (Snowhead)", player) + ) or + ( + state.has("Goht's Remains", player) and + options.remains_allow_boss_warps.value + ) + ) + ), + "Termina Field -> Milk Road": + lambda state: True, + "Milk Road -> Gorman Brothers Track": + lambda state: True, "Termina Field -> Great Bay": lambda state: can_play_song("Epona's Song", state, player), "Great Bay -> Ocean Spider House": @@ -220,77 +332,115 @@ def get_region_rules(player, options): lambda state: state.has("Goron Mask", player), "Pirates' Fortress Sewers -> Pirates' Fortress (Interior)": lambda state: state.has("Zora Mask", player), + "Great Bay -> Zora Cape": + lambda state: True, "Zora Cape -> Zora Hall": lambda state: state.has("Zora Mask", player), "Zora Cape -> Great Bay Temple": lambda state: ( - can_play_song("New Wave Bossa Nova", state, player) and - state.has("Hookshot", player) and + can_play_song("New Wave Bossa Nova", state, player) and + state.has("Hookshot", player) and state.has("Zora Mask", player) ), + "Great Bay Temple -> Gyorg's Lair": + lambda state: ( + state.has("Hookshot", player) and + ( + ( + state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player) and + state.has("Boss Key (Great Bay)", player) + ) or + ( + state.has("Gyorg's Remains", player) and + options.remains_allow_boss_warps.value + ) + ) + ), "Road to Ikana -> Ikana Graveyard": lambda state: can_play_song("Epona's Song", state, player), - "Road to Ikana -> Ikana Canyon": + "Road to Ikana -> Lower Ikana Canyon": lambda state: ( + can_play_song("Epona's Song", state, player) and + state.has("Hookshot", player) and ( - state.has("Garo Mask", player) and - can_play_song("Epona's Song", state, player) and - state.has("Hookshot", player) - ) or - ( - state.has("Gibdo Mask", player) and - can_play_song("Epona's Song", state, player) and - state.has("Hookshot", player) + state.has("Garo Mask", player) or + state.has("Gibdo Mask", player) ) ), - "Ikana Canyon -> Secret Shrine": - lambda state: can_use_light_arrows(state, player), - "Ikana Canyon -> Beneath the Well": + "Lower Ikana Canyon -> Secret Shrine": + lambda state: True, + "Lower Ikana Canyon -> Upper Ikana Canyon": + lambda state: ( + can_use_ice_arrows(state, player) and + state.has("Hookshot", player) + ), + "Upper Ikana Canyon -> Beneath the Well": lambda state: ( - can_use_ice_arrows(state, player) and - state.has("Hookshot", player) and - state.has("Gibdo Mask", player) and + state.has("Gibdo Mask", player) and has_bottle(state, player) ), - "Ikana Canyon -> Ikana Castle": + "Upper Ikana Canyon -> Ikana Castle": lambda state: ( - can_use_ice_arrows(state, player) and - state.has("Hookshot", player) and - ( - can_use_light_arrows(state, player) or - has_mirror_shield(state, player) - ) + can_use_light_arrows(state, player) or + has_mirror_shield(state, player) ), "Beneath the Well -> Ikana Castle": lambda state: ( ( # Ikana Well Final Chest Logic - state.has("Gibdo Mask", player) and - has_bottle(state, player) and - can_plant_beans(state, player) and + state.has("Gibdo Mask", player) and + has_bottle(state, player) and + can_plant_beans(state, player) and + state.has("Progressive Bow", player) ( - state.has("Progressive Bomb Bag", player) or + state.has("Progressive Bomb Bag", player) or ( - state.has("Captain's Hat", player) and - state.has("Progressive Bow", player) + state.has("Captain's Hat", player) ) ) ) and ( - can_use_light_arrows(state, player) or + can_use_light_arrows(state, player) or has_mirror_shield(state, player) ) ), + "Ikana Castle -> Beneath the Well": + lambda state: + can_use_light_arrows(state, player), "Stone Tower -> Stone Tower Temple": lambda state: ( - can_reach_stonetower(state, player) + can_play_song("Elegy of Emptiness", state, player) and + ( + state.has("Hookshot", player) and + state.has("Goron Mask", player) and + state.has("Zora Mask", player) + ) ), "Stone Tower -> Stone Tower (Inverted)": lambda state: ( - can_reach_stonetower(state, player) and - can_use_light_arrows(state, player) and - can_play_song("Elegy of Emptiness", state, player) + state.can_reach("Stone Tower Temple", 'Region', player) and + can_use_light_arrows(state, player) ), "Stone Tower (Inverted) -> Stone Tower Temple (Inverted)": - lambda state: True, + lambda state: True, + "Stone Tower Temple (Inverted) -> Twinmold's Lair": + lambda state: ( + state.can_reach("Stone Tower Temple Inverted Eyegore Chest", 'Location', player) and + ( + state.has("Progressive Bow", player) or + state.has("Fierce Deity's Mask", player) or + ( + state.has("Giant's Mask", player) and + state.has("Progressive Magic", player) and + state.has("Progressive Sword", player) + ) + ) and ( + state.has("Small Key (Stone Tower)", player, 4) and + state.has("Boss Key (Stone Tower)", player) or + ( + state.has("Twinmold's Remains", player) and + options.remains_allow_boss_warps.value + ) + ) + ), } def get_location_rules(player, options, prices): @@ -333,17 +483,17 @@ def get_location_rules(player, options, prices): lambda state: state.has("Deku Mask", player), "North Clock Town Save Old Lady": lambda state: ( - state.has("Progressive Sword", player) or - state.has("Great Fairy Sword", player) or - state.has("Zora Mask", player) or + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) or + state.has("Zora Mask", player) or state.has("Goron Mask", player) ), "North Clock Town Great Fairy Reward (Has Transformation Mask)": lambda state: ( - state.has("Stray Fairy (Clock Town)", player) and + state.has("Stray Fairy (Clock Town)", player) and ( - state.has("Deku Mask", player) or - state.has("Goron Mask", player) or + state.has("Deku Mask", player) or + state.has("Goron Mask", player) or state.has("Zora Mask", player) ) ), @@ -353,12 +503,12 @@ def get_location_rules(player, options, prices): lambda state: has_projectiles(state, player), "Tingle Clock Town Map Purchase": lambda state: ( - has_projectiles(state, player) and + has_projectiles(state, player) and ( - state.can_reach("Clock Town", 'Region', player) or + state.can_reach("Clock Town", 'Region', player) or ( - state.can_reach("Ikana Canyon", 'Region', player) and - can_use_ice_arrows(state, player) and + state.can_reach("Ikana Canyon", 'Region', player) and + can_use_ice_arrows(state, player) and state.has("Hookshot", player) ) ) @@ -372,16 +522,16 @@ def get_location_rules(player, options, prices): lambda state: state.has("Hookshot", player), "South Clock Town Final Day Tower Chest": lambda state: ( - state.has("Hookshot", player) or + state.has("Hookshot", player) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Moon's Tear", player) ) ), "Clock Tower Happy Mask Salesman #1": - lambda state: True, + lambda state: state.has("Ocarina of Time", player), "Clock Tower Happy Mask Salesman #2": - lambda state: True, + lambda state: state.has("Ocarina of Time", player), "Before Clock Town Chest": lambda state: state.has("Deku Mask", player), @@ -422,34 +572,36 @@ def get_location_rules(player, options, prices): lambda state: state.has("Zora Mask", player), "Bomber's Hideout Chest": lambda state: ( - state.can_reach("Clock Town Hide-and-Seek", 'Location', player) and + state.can_reach("Clock Town Hide-and-Seek", 'Location', player) and has_explosives(state, player) ), "Bomber's Hideout Astral Observatory": - lambda state: has_projectiles(state, player) or - state.has("Deku Mask", player), + lambda state: ( + has_projectiles(state, player) or + state.has("Deku Mask", player) + ), "Milk Bar Show": lambda state: ( - state.has("Romani Mask", player) and - state.has("Deku Mask", player) and - state.has("Goron Mask", player) and - state.has("Zora Mask", player) and + state.has("Romani Mask", player) and + state.has("Deku Mask", player) and + state.has("Goron Mask", player) and + state.has("Zora Mask", player) and state.has("Ocarina of Time", player) ), "Milk Bar Priority Mail to Aroma": lambda state: ( - state.has("Romani Mask", player) and - state.has("Kafei's Mask", player) and + state.has("Romani Mask", player) and + state.has("Kafei's Mask", player) and state.has("Priority Mail", player) ), "East Clock Town Milk Bar Milk Purchase": lambda state: ( - state.has("Romani Mask", player) and + state.has("Romani Mask", player) and can_afford_price(state, player, 40) ), "East Clock Town Milk Bar Chateau Romani Purchase": lambda state: ( - state.has("Romani Mask", player) and + state.has("Romani Mask", player) and can_afford_price(state, player, 200) ), "West Clock Town Lottery Any Day": @@ -510,14 +662,14 @@ def get_location_rules(player, options, prices): lambda state: state.has("Goron Mask", player) and state.has("Powder Keg", player), "Clock Town Bomb Shop Item 3 (Stop Thief)": lambda state: ( - state.can_reach("North Clock Town Save Old Lady", 'Location', player) and + state.can_reach("North Clock Town Save Old Lady", 'Location', player) and can_purchase(state, player, prices, SHOP_ID_BOMB_SHOP_3_UPGRADE) ), "Curiosity Shop Blue Rupee Trade": lambda state: ( - has_bottle(state, player) and + has_bottle(state, player) and ( - state.has("Mask of Scents", player) or + state.has("Mask of Scents", player) or can_get_cow_milk(state, player) ) ), @@ -525,24 +677,24 @@ def get_location_rules(player, options, prices): lambda state: has_bottle(state, player), "Curiosity Shop Purple Rupee Trade": lambda state: ( - has_bottle(state, player) and - state.can_reach("Stone Tower Temple Inverted Death Armos Maze Chest", 'Location', player) + has_bottle(state, player) and + state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player) ), "Curiosity Shop Gold Rupee Trade": lambda state: ( - has_bottle(state, player) and + has_bottle(state, player) and ( ( - state.can_reach("Graveyard Day 3 Dampe Big Poe Chest", 'Location', player) or + state.can_reach("Graveyard Day 3 Dampe Big Poe Chest", 'Location', player) or ( - state.can_reach("Ikana Well Rightside Torch Chest", 'Location', player) and + state.can_reach("Ikana Well Rightside Torch Chest", 'Location', player) and state.has("Progressive Bomb Bag", player) ) - ) or + ) or ( - state.has("Romani Mask", player) and + state.has("Romani Mask", player) and can_afford_price(state, player, 200) - ) or + ) or state.can_reach("Goron Racetrack Prize", 'Location', player) ) ), @@ -558,9 +710,9 @@ def get_location_rules(player, options, prices): lambda state: True, "Stock Pot Inn Midnight Meeting": lambda state: ( - state.has("Kafei's Mask", player) and + state.has("Kafei's Mask", player) and ( - state.has("Deku Mask", player) or + state.has("Deku Mask", player) or state.has("Room Key", player) ) ), @@ -576,13 +728,13 @@ def get_location_rules(player, options, prices): lambda state: state.has("All-Night Mask", player), "Stock Pot Inn Anju and Kafei": lambda state: ( - state.has("Kafei's Mask", player) and - can_play_song("Epona's Song", state, player) and - state.has("Letter to Kafei", player) and - state.has("Pendant of Memories", player) and - state.has("Hookshot", player) and + state.has("Kafei's Mask", player) and + can_play_song("Epona's Song", state, player) and + state.has("Letter to Kafei", player) and + state.has("Pendant of Memories", player) and + state.has("Hookshot", player) and ( - state.has("Garo Mask", player) or + state.has("Garo Mask", player) or state.has("Gibdo Mask", player) ) ), @@ -606,7 +758,7 @@ def get_location_rules(player, options, prices): lambda state: True, "Termina Stump Chest": lambda state: ( - state.has("Hookshot", player) or + state.has("Hookshot", player) or can_plant_beans(state, player) ), "Termina Underwater Chest": @@ -618,73 +770,66 @@ def get_location_rules(player, options, prices): "Termina Bio Baba Grotto HP": lambda state: ( ( - has_explosives(state, player) or + has_explosives(state, player) or state.has("Goron Mask", player) ) and state.has("Zora Mask", player) ), "Termina Healing Kamaro": lambda state: ( - state.has("Ocarina of Time", player) and + state.has("Ocarina of Time", player) and state.has("Song of Healing", player) ), "Termina Gossip Stones HP": lambda state: ( ( - has_explosives(state, player) or + has_explosives(state, player) or state.has("Goron Mask", player) - ) and + ) and ( ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and can_play_song("Sonata of Awakening", state, player) - ) or + ) or ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and can_play_song("Goron Lullaby", state, player) - ) or + ) or ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_play_song("New Wave Bossa Nova", state, player) ) ) ), "Termina Scrub Grotto HP": lambda state: ( - ( - state.can_reach("Bomber's Hideout Astral Observatory", 'Location', player) and - state.has("Ocarina of Time", player) and - can_afford_price(state, player, 100) - ) or - ( - state.has("Deku Mask", player) and - state.has("Ocarina of Time", player) and - can_afford_price(state, player, 100) - ) + state.can_reach("Bomber's Hideout Astral Observatory", 'Location', player) and + state.has("Ocarina of Time", player) and + can_afford_price(state, player, 100) ), "Termina Log Bombable Grotto Left Cow": lambda state: ( - has_explosives(state, player) and + has_explosives(state, player) and can_play_song("Epona's Song", state, player) ), "Termina Log Bombable Grotto Right Cow": lambda state: ( - has_explosives(state, player) and + has_explosives(state, player) and can_play_song("Epona's Song", state, player) ), "Milk Road Gorman Ranch Race": lambda state: ( - state.has("Ocarina of Time", player) and + state.has("Ocarina of Time", player) and state.has("Epona's Song", player) ), "Milk Road Gorman Ranch Purchase": lambda state: True, "Tingle Romani Ranch Map Purchase": lambda state: ( - has_projectiles(state, player) and + has_projectiles(state, player) and ( - state.can_reach("Milk Road", 'Region', player) or + state.can_reach("Milk Road", 'Region', player) or state.can_reach("Twin Islands", 'Region', player) ) ), @@ -695,9 +840,9 @@ def get_location_rules(player, options, prices): lambda state: has_projectiles(state, player), "Tingle Woodfall Map Purchase": lambda state: ( - has_projectiles(state, player) and + has_projectiles(state, player) and ( - state.can_reach("Southern Swamp", 'Region', player) or + state.can_reach("Southern Swamp", 'Region', player) or state.can_reach("Clock Town", 'Region', player) ) ), @@ -708,21 +853,20 @@ def get_location_rules(player, options, prices): "Southern Swamp Deku Scrub Purchase": lambda state: ( + state.has("Magic Beans", player) and ( - state.has("Deku Mask", player) and - can_plant_beans(state, player) + state.has("Deku Mask", player) ) or ( - state.has("Land Title Deed", player) and - state.has("Moon's Tear", player) and - can_plant_beans(state, player) + state.has("Land Title Deed", player) and + state.has("Moon's Tear", player) ) ), "Southern Swamp Deku Trade": lambda state: state.has("Land Title Deed", player), "Southern Swamp Freestanding HP": lambda state: ( - state.has("Land Title Deed", player) and + state.has("Land Title Deed", player) and state.has("Deku Mask", player) ), "Southern Swamp Kotake Item": @@ -750,8 +894,8 @@ def get_location_rules(player, options, prices): lambda state: True, "Southern Swamp Witch Shop Item 1": lambda state: ( - state.has("Mask of Scents", player) and - has_bottle(state, player) and + state.has("Mask of Scents", player) and + has_bottle(state, player) and can_purchase(state, player, prices, SHOP_ID_WITCH_POTION_1) ), "Southern Swamp Witch Shop Item 2": @@ -770,31 +914,31 @@ def get_location_rules(player, options, prices): "Swamp Spider House First Room Against Far Wall Token": lambda state: ( ( - can_bring_to_player(state, player) and + can_bring_to_player(state, player) and has_projectiles(state, player) - ) or + ) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Progressive Magic", player) - ) or + ) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Progressive Bow", player) ) ), "Swamp Spider House First Room Lower Left Bugpatch Token": lambda state: ( - can_smack(state, player) and + can_smack(state, player) and has_bottle(state, player) ), "Swamp Spider House First Room Lower Right Bugpatch Token": lambda state: ( - can_smack(state, player) and + can_smack(state, player) and has_bottle(state, player) ), "Swamp Spider House First Room Upper Right Bugpatch Token": lambda state: ( - can_smack(state, player) and + can_smack(state, player) and has_bottle(state, player) ), "Swamp Spider House Monument Room Left Crate Token": @@ -803,34 +947,27 @@ def get_location_rules(player, options, prices): lambda state: can_smack(state, player), "Swamp Spider House Monument Room Crawling Wall Token": lambda state: ( - can_bring_to_player(state, player) or + can_bring_to_player(state, player) or ( - can_smack(state, player) and - can_plant_beans(state, player) and + can_smack(state, player) and + can_plant_beans(state, player) and ( - has_explosives(state, player) or + has_explosives(state, player) or state.has("Goron Mask", player) ) ) ), "Swamp Spider House Monument Room Crawling On Monument Token": lambda state: ( - can_smack(state, player) and - can_bring_to_player(state, player) and + can_smack(state, player) or has_projectiles(state, player) ), "Swamp Spider House Monument Room Behind Torch Token": lambda state: can_smack(state, player), "Swamp Spider House Pottery Room Beehive #1 Token": - lambda state: ( - can_smack(state, player) and - has_projectiles(state, player) - ), + lambda state: has_projectiles(state, player), "Swamp Spider House Pottery Room Beehive #2 Token": - lambda state: ( - can_smack(state, player) and - has_projectiles(state, player) - ), + lambda state: has_projectiles(state, player), "Swamp Spider House Pottery Room Small Pot Token": lambda state: can_smack(state, player), "Swamp Spider House Pottery Room Left Large Pot Token": @@ -839,9 +976,8 @@ def get_location_rules(player, options, prices): lambda state: can_smack(state, player), "Swamp Spider House Pottery Room Behind Vines Token": lambda state: ( - state.has("Progressive Sword", player) or - state.has("Great Fairy Sword", player) or - state.has("Fierce Deity's Mask", player) + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) ), "Swamp Spider House Pottery Room Upper Wall Token": lambda state: can_smack(state, player), @@ -851,15 +987,15 @@ def get_location_rules(player, options, prices): lambda state: can_smack(state, player), "Swamp Spider House Golden Room Against Far Wall Token": lambda state: ( - can_smack(state, player) and + can_smack(state, player) and ( - can_bring_to_player(state, player) or + can_bring_to_player(state, player) or can_plant_beans(state, player) ) ), "Swamp Spider House Golden Room Beehive Token": lambda state: ( - can_smack(state, player) and + can_smack(state, player) and has_projectiles(state, player) ), "Swamp Spider House Tree Room Tall Grass #1 Token": @@ -873,36 +1009,66 @@ def get_location_rules(player, options, prices): "Swamp Spider House Tree Room Tree #3 Token": lambda state: can_smack(state, player), "Swamp Spider House Tree Room Beehive Token": + lambda state: has_projectiles(state, player), + "Swamp Spider House Reward": lambda state: ( - can_smack(state, player) and - has_projectiles(state, player) + ( + state.has("Swamp Skulltula Token", player, options.required_skull_tokens.value) + ) or ( + options.skullsanity.value == 2 and + can_smack(state, player) and + has_bottle(state, player) and + has_projectiles(state, player) and + ( + can_bring_to_player(state, player) or + ( + state.has("Deku Mask", player) and + state.has("Progressive Magic", player) + ) or ( + state.has("Deku Mask", player) and + state.has("Progressive Bow", player) + ) + ) and ( + can_bring_to_player(state, player) or + ( + can_plant_beans(state, player) and + ( + has_explosives(state, player) or + state.has("Goron Mask", player) + ) + ) + ) and ( + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) + ) and ( + can_bring_to_player(state, player) or + can_plant_beans(state, player) + ) + ) ), - "Swamp Spider House Reward": - lambda state: state.has("Swamp Skulltula Token", player, options.required_skull_tokens.value), "Deku Palace Bean Seller": lambda state: state.has("Deku Mask", player), "Deku Palace Bean Grotto Chest": lambda state: ( - can_plant_beans(state, player) or + can_plant_beans(state, player) or state.has("Hookshot", player) ), "Deku Palace HP": lambda state: state.has("Deku Mask", player), "Deku Palace Monkey Song": lambda state: ( - state.has("Ocarina of Time", player) and - can_plant_beans(state, player) and + state.has("Ocarina of Time", player) and + can_plant_beans(state, player) and state.has("Deku Mask", player) ), "Deku Palace Butler Race": lambda state: ( - can_clear_woodfall(state, player) and - has_bottle(state, player) and + can_clear_woodfall(state, player) and + has_bottle(state, player) and ( - state.has("Progressive Sword", player) or - state.has("Great Fairy Sword", player) or - state.has("Fierce Deity's Mask", player) + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) ) ), @@ -924,12 +1090,12 @@ def get_location_rules(player, options, prices): lambda state: True, "Woodfall Temple Dragonfly Chest": lambda state: ( - state.has("Small Key (Woodfall)", player) or + state.has("Small Key (Woodfall)", player) or state.has("Progressive Bow", player) ), "Woodfall Temple Dark Room Chest SF": lambda state: ( - state.has("Small Key (Woodfall)", player) or + state.has("Small Key (Woodfall)", player) or state.has("Progressive Bow", player) ), "Woodfall Temple Switch Chest SF": @@ -937,14 +1103,14 @@ def get_location_rules(player, options, prices): "Woodfall Temple Dinolfos Chest": lambda state: ( ( - state.has("Small Key (Woodfall)", player) and + state.has("Small Key (Woodfall)", player) and can_smack(state, player) ) or state.has("Progressive Bow", player) ), "Woodfall Temple Gekko Chest": lambda state: ( - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and can_smack(state, player) ), "Woodfall Temple Entrance Freestanding SF": @@ -956,7 +1122,7 @@ def get_location_rules(player, options, prices): "Woodfall Temple Main Room Bubble SF": lambda state: ( ( - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and state.has("Great Fairy Mask", player) ) or can_use_fire_arrows(state, player) @@ -964,16 +1130,16 @@ def get_location_rules(player, options, prices): "Woodfall Temple Platform Hive SF": lambda state: ( ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Progressive Magic", player) ) ) or ( - state.has("Great Fairy Mask", player) and + state.has("Great Fairy Mask", player) and ( - state.has("Hookshot", player) or + state.has("Hookshot", player) or state.has("Zora Mask", player) ) ) @@ -981,17 +1147,17 @@ def get_location_rules(player, options, prices): "Woodfall Temple Skulltula SF": lambda state: ( ( - state.has("Small Key (Woodfall)", player) and + state.has("Small Key (Woodfall)", player) and can_smack(state, player) ) or state.has("Progressive Bow", player) ), "Woodfall Temple Bridge Room Bubble SF": lambda state: ( - state.has("Great Fairy Mask", player) and + state.has("Great Fairy Mask", player) and ( ( - state.has("Small Key (Woodfall)", player) and + state.has("Small Key (Woodfall)", player) and has_projectiles(state, player) ) or state.has("Progressive Bow", player) @@ -999,10 +1165,10 @@ def get_location_rules(player, options, prices): ), "Woodfall Temple Bridge Room Hive SF": lambda state: ( - state.has("Great Fairy Mask", player) and + state.has("Great Fairy Mask", player) and ( ( - state.has("Small Key (Woodfall)", player) and + state.has("Small Key (Woodfall)", player) and has_projectiles(state, player) ) or state.has("Progressive Bow", player) @@ -1010,44 +1176,48 @@ def get_location_rules(player, options, prices): ), "Woodfall Temple Pre-Boss Lower Right Bubble SF": lambda state: ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or can_use_fire_arrows(state, player) ), "Woodfall Temple Pre-Boss Upper Right Bubble SF": lambda state: ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or can_use_fire_arrows(state, player) ), "Woodfall Temple Pre-Boss Upper Left Bubble SF": lambda state: ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or can_use_fire_arrows(state, player) ), "Woodfall Temple Pre-Boss Pillar Bubble SF": lambda state: ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or can_use_fire_arrows(state, player) ), "Woodfall Temple Heart Container": lambda state: ( - can_smack(state, player) and - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and ( - state.has("Boss Key (Woodfall)", player) or + can_smack(state, player) or + state.has("Fierce Deity Mask", player) + ) and ( + state.has("Boss Key (Woodfall)", player) or ( - state.has("Odolwa's Remains", player) and + state.has("Odolwa's Remains", player) and options.remains_allow_boss_warps.value ) ) ), "Woodfall Temple Odolwa's Remains": lambda state: ( - can_smack(state, player) and - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and ( - state.has("Boss Key (Woodfall)", player) or + can_smack(state, player) or + state.has("Fierce Deity Mask", player) + ) and ( + state.has("Boss Key (Woodfall)", player) or ( - state.has("Odolwa's Remains", player) and + state.has("Odolwa's Remains", player) and options.remains_allow_boss_warps.value ) ) @@ -1055,22 +1225,22 @@ def get_location_rules(player, options, prices): "Southern Swamp Boat Archery": lambda state: ( - can_clear_woodfall(state, player) and - has_bottle(state, player) and + can_clear_woodfall(state, player) and + has_bottle(state, player) and state.has("Progressive Bow", player) ), "Mountain Village Healing Darmani": lambda state: ( - can_use_lens(state, player) and + can_use_lens(state, player) and can_play_song("Song of Healing", state, player) ), "Mountain Village Hungry Goron": lambda state: ( - state.has("Goron Mask", player) and - state.has("Progressive Magic", player) and + state.has("Goron Mask", player) and + state.has("Progressive Magic", player) and ( - can_play_song("Goron Lullaby", state, player) or + can_play_song("Goron Lullaby", state, player) or can_use_fire_arrows(state, player) ) ), @@ -1080,98 +1250,100 @@ def get_location_rules(player, options, prices): lambda state: can_clear_snowhead(state, player), "Mountain Village Spring Frog Choir HP": lambda state: ( - state.has("Don Gero Mask", player) and - can_clear_snowhead(state, player) and - state.can_reach("Woodfall Temple Gekko Chest", 'Location', player) and - state.can_reach("Great Bay Temple", 'Region', player) and - can_use_ice_arrows(state, player) and + state.has("Don Gero Mask", player) and + can_clear_snowhead(state, player) and + state.can_reach("Woodfall Temple Gekko Chest", 'Location', player) and + state.can_reach("Great Bay Temple", 'Region', player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Mountain Village Smithy Upgrade": lambda state: ( - can_afford_price(state, player, 100) and + can_afford_price(state, player, 100) and ( - can_use_fire_arrows(state, player) or - state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) or + can_use_fire_arrows(state, player) or + state.can_reach("Twin Islands Hot Water Grotto Chest", 'Location', player) or can_clear_snowhead(state, player) ) ), "Mountain Village Smithy Gold Dust Upgrade": lambda state: ( - state.can_reach("Mountain Village Smithy Upgrade", 'Location', player) and - state.can_reach("Goron Racetrack Prize", 'Location', player) and + state.can_reach("Mountain Village Smithy Upgrade", 'Location', player) and + state.can_reach("Goron Racetrack Prize", 'Location', player) and has_bottle(state, player) ), "Tingle Snowhead Map Purchase": lambda state: ( - has_projectiles(state, player) and + has_projectiles(state, player) and ( - state.can_reach("Twin Islands", 'Region', player) or + state.can_reach("Twin Islands", 'Region', player) or state.can_reach("Southern Swamp", 'Region', player) ) ), "Twin Islands Ramp Grotto Chest": lambda state: ( - has_explosives(state, player) and + has_explosives(state, player) and ( - state.has("Goron Mask", player) or + state.has("Goron Mask", player) or state.has("Hookshot", player) ) ), "Twin Islands Goron Elder Request": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and ( - can_use_fire_arrows(state, player) or + can_use_fire_arrows(state, player) or ( ( - can_use_lens(state, player) or + can_use_lens(state, player) or ( - state.can_reach("Ikana Well Invisible Chest", 'Location', player) and + state.can_reach("Ikana Well Invisible Chest", 'Location', player) and can_play_song("Song of Soaring", state, player) ) - ) and + ) and has_bottle(state, player) ) ) ), "Twin Islands Hot Water Grotto Chest": lambda state: ( + has_explosives(state, player) and ( - has_explosives(state, player) and - can_use_fire_arrows(state, player) - ) or - ( - can_use_lens(state, player) and - has_bottle(state, player) and - state.has("Goron Mask", player) and - has_explosives(state, player) - ) or - ( - can_clear_snowhead(state, player) or ( - state.can_reach("Ikana Well Invisible Chest", 'Location', player) and - can_play_song("Song of Soaring", state, player) + can_use_fire_arrows(state, player) + ) or + ( + can_use_lens(state, player) and + has_bottle(state, player) and + state.has("Goron Mask", player) and + has_explosives(state, player) + ) or + ( + can_clear_snowhead(state, player) or + ( + state.can_reach("Ikana Well Invisible Chest", 'Location', player) and + can_play_song("Song of Soaring", state, player) + ) ) ) ), "Twin Islands Spring Underwater Cave Chest": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_clear_snowhead(state, player) ), "Twin Islands Spring Underwater Ramp Chest": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_clear_snowhead(state, player) ), "Goron Racetrack Prize": lambda state: ( ( - can_use_powder_keg(state, player) or + can_use_powder_keg(state, player) or state.can_reach("Powder Keg Goron Reward", 'Location', player) - ) and + ) and can_clear_snowhead(state, player) ), @@ -1183,19 +1355,19 @@ def get_location_rules(player, options, prices): lambda state: True, "Goron Village Scrub Purchase": lambda state: ( - can_afford_price(state, player, 200) and + can_afford_price(state, player, 200) and ( - state.has("Goron Mask", player) or + state.has("Goron Mask", player) or ( - state.can_reach("Goron Village Freestanding HP", 'Location', player) and - state.can_reach("Southern Swamp Freestanding HP", 'Location', player) and + state.can_reach("Goron Village Freestanding HP", 'Location', player) and + state.can_reach("Southern Swamp Freestanding HP", 'Location', player) and state.has("Moon's Tear", player) ) ) ), "Goron Village Deku Trade": lambda state: ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Swamp Title Deed", player) ), "Goron Village Freestanding HP": @@ -1204,68 +1376,68 @@ def get_location_rules(player, options, prices): ), "Powder Keg Goron Reward": lambda state: ( - can_clear_snowhead(state, player) or + state.has("Goron Mask", player) and ( - can_use_fire_arrows(state, player) and - state.has("Goron Mask", player) + can_use_fire_arrows(state, player) or + can_clear_snowhead(state, player) ) ), "Goron Village Baby Goron Lullaby": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and can_play_song("Goron Lullaby", state, player) ), "Goron Village Shop Item 1": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_1) ), "Goron Village Shop Item 2": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_2) ), "Goron Village Shop Item 3": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_3) ), "Goron Village Shop (Spring) Item 1": lambda state: ( - state.has("Goron Mask", player) and - can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_1) and + state.has("Goron Mask", player) and + can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_1) and can_clear_snowhead(state, player) ), "Goron Village Shop (Spring) Item 2": lambda state: ( - state.has("Goron Mask", player) and - can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_2) and + state.has("Goron Mask", player) and + can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_2) and can_clear_snowhead(state, player) ), "Goron Village Shop (Spring) Item 3": lambda state: ( - state.has("Goron Mask", player) and - can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_3) and + state.has("Goron Mask", player) and + can_purchase(state, player, prices, SHOP_ID_GORON_SHOP_SPRING_3) and can_clear_snowhead(state, player) ), "Goron Village Freestanding HP (Spring)": lambda state: ( - can_clear_snowhead(state, player) and - state.has("Deku Mask", player) and + can_clear_snowhead(state, player) and + state.has("Deku Mask", player) and state.has("Swamp Title Deed", player) ), "Path to Snowhead Grotto Chest": lambda state: ( - state.has("Goron Mask", player) and - state.has("Progressive Magic", player) and + state.has("Goron Mask", player) and + state.has("Progressive Magic", player) and has_explosives(state, player) ), "Path to Snowhead Scarecrow Pillar HP": lambda state: ( - can_reach_scarecrow(state, player) and - state.has("Goron Mask", player) and - can_use_lens(state, player) and + can_reach_scarecrow(state, player) and + state.has("Goron Mask", player) and + can_use_lens(state, player) and state.has("Hookshot", player) ), @@ -1274,68 +1446,66 @@ def get_location_rules(player, options, prices): "Snowhead Temple Bridge Room Under Platform Bubble SF": lambda state: ( - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and state.has("Great Fairy Mask", player) ), "Snowhead Temple Bridge Room Pillar Bubble SF": lambda state: ( - state.has("Progressive Bow", player) and + state.has("Progressive Bow", player) and state.has("Great Fairy Mask", player) ), "Snowhead Temple Bombable Stairs Crate SF": lambda state: ( + has_explosives(state, player) and ( - state.has("Small Key (Snowhead)", player) and - state.has("Great Fairy Mask", player) and - has_explosives(state, player) - ) or - ( - state.has("Hookshot", player) and - state.can_reach("Snowhead Temple Bridge Room Under Platform Bubble SF", 'Location', player) and - has_explosives(state, player) + state.has("Hookshot", player) or + ( + state.has("Small Key (Snowhead)", player) and + state.has("Great Fairy Mask", player) + ) ) ), "Snowhead Temple Timed Switch Room Bubble SF": lambda state: ( ( - state.has("Small Key (Snowhead)", player, 2) and - state.has("Great Fairy Mask", player) and - state.has("Progressive Bow", player) and - can_use_lens(state, player) and + state.has("Small Key (Snowhead)", player, 2) and + state.has("Great Fairy Mask", player) and + state.has("Progressive Bow", player) and + can_use_lens(state, player) and has_explosives(state, player) ) or ( - can_reach_scarecrow(state, player) and - state.has("Hookshot", player) and - state.has("Great Fairy Mask", player) and - state.has("Progressive Bow", player) and + can_reach_scarecrow(state, player) and + state.has("Hookshot", player) and + state.has("Great Fairy Mask", player) and + state.has("Progressive Bow", player) and can_use_lens(state, player) ) or ( - can_use_fire_arrows(state, player) and - state.has("Great Fairy's Mask", player) and + can_use_fire_arrows(state, player) and + state.has("Great Fairy's Mask", player) and can_use_lens(state, player) ) ), "Snowhead Temple Snowmen Bubble SF": lambda state: ( - state.has("Small Key (Snowhead)", player) and - can_use_fire_arrows(state, player) and + state.has("Small Key (Snowhead)", player) and + can_use_fire_arrows(state, player) and state.has("Great Fairy Mask", player) ), "Snowhead Temple Dinolfos Room First SF": lambda state: ( - state.has("Small Key (Snowhead)", player) and + state.has("Small Key (Snowhead)", player) and can_use_fire_arrows(state, player) ), "Snowhead Temple Dinolfos Room Second SF": lambda state: ( - state.has("Small Key (Snowhead)", player) and + state.has("Small Key (Snowhead)", player) and can_use_fire_arrows(state, player) ), "Snowhead Temple Bridge Room Freezard Chest": lambda state: ( - can_use_fire_arrows(state, player) or + can_use_fire_arrows(state, player) or state.has("Hookshot", player) ), "Snowhead Temple Basement Switch Chest SF": @@ -1344,9 +1514,9 @@ def get_location_rules(player, options, prices): lambda state: can_use_fire_arrows(state, player), "Snowhead Temple Stacked Block Upper Chest SF": lambda state: ( - state.has("Hookshot", player) or + state.has("Hookshot", player) or ( - state.has("Small Key (Snowhead)", player) and + state.has("Small Key (Snowhead)", player) and can_use_fire_arrows(state, player) ) ), @@ -1354,51 +1524,51 @@ def get_location_rules(player, options, prices): lambda state: True, "Snowhead Temple Frozen Block Chest": lambda state: ( - state.has("Small Key (Snowhead)", player) or + state.has("Small Key (Snowhead)", player) or state.has("Hookshot", player) ), "Snowhead Temple Frozen Block Upper Chest SF": lambda state: ( - can_use_fire_arrows(state, player) and + can_use_fire_arrows(state, player) and ( - state.has("Small Key (Snowhead)", player) or + state.has("Small Key (Snowhead)", player) or state.has("Hookshot", player) ) ), "Snowhead Temple Icicle Room Hidden Chest SF": lambda state: ( - can_use_lens(state, player) and - has_explosives(state, player) and - state.has("Progressive Bow", player) and + can_use_lens(state, player) and + has_explosives(state, player) and + state.has("Progressive Bow", player) and ( - state.has("Small Key (Snowhead)", player) or + state.has("Small Key (Snowhead)", player) or state.has("Hookshot", player) ) ), "Snowhead Temple Icicle Room Snowball Chest": lambda state: ( - has_explosives(state, player) and + has_explosives(state, player) and ( ( - state.has("Small Key (Snowhead)", player) and + state.has("Small Key (Snowhead)", player) and state.has("Progressive Bow", player) - ) or + ) or state.has("Hookshot", player) ) ), "Snowhead Temple Elevator Room Invisible Platform Chest SF": lambda state: ( ( - can_use_lens(state, player) and - state.has("Small Key (Snowhead)", player, 2) and + can_use_lens(state, player) and + state.has("Small Key (Snowhead)", player, 2) and has_explosives(state, player) - ) or + ) or ( - can_use_lens(state, player) and + can_use_lens(state, player) and can_use_fire_arrows(state, player) - ) or + ) or ( - can_use_lens(state, player) and + can_use_lens(state, player) and state.has("Hookshot", player) ) ), @@ -1409,64 +1579,65 @@ def get_location_rules(player, options, prices): "Snowhead Temple Lower Wizzrobe Chest": lambda state: ( ( - state.has("Small Key (Snowhead)", player, 2) and + state.has("Small Key (Snowhead)", player, 2) and has_explosives(state, player) - ) or + ) or can_use_fire_arrows(state, player) ), "Snowhead Temple Main Room Wall Chest SF": lambda state: ( ( - state.has("Small Key (Snowhead)", player, 3) and - can_use_fire_arrows(state, player) and - can_use_lens(state, player) and - has_explosives(state, player) and + state.has("Small Key (Snowhead)", player, 3) and + can_use_fire_arrows(state, player) and + can_use_lens(state, player) and + has_explosives(state, player) and state.has("Deku Mask", player) ) or ( - can_use_fire_arrows(state, player) and - can_reach_scarecrow(state, player) and - state.has("Hookshot", player) and + can_use_fire_arrows(state, player) and + can_reach_scarecrow(state, player) and + state.has("Hookshot", player) and can_use_lens(state, player) ) ), "Snowhead Temple Upper Wizzrobe Chest": lambda state: ( + can_use_fire_arrows(state, player) and ( - state.has("Small Key (Snowhead)", player, 3) and - can_use_fire_arrows(state, player) and - has_explosives(state, player) - ) or - ( - state.has("Small Key (Snowhead)", player, 1) and - can_use_fire_arrows(state, player) and - state.has("Deku Mask", player) + ( + state.has("Small Key (Snowhead)", player, 3) and + has_explosives(state, player) + ) or + ( + state.has("Small Key (Snowhead)", player, 1) and + state.has("Deku Mask", player) + ) ) ), "Snowhead Temple Heart Container": lambda state: ( - can_use_fire_arrows(state, player) and + can_use_fire_arrows(state, player) and ( ( - state.has("Small Key (Snowhead)", player, 1) and + state.has("Small Key (Snowhead)", player, 1) and state.has("Boss Key (Snowhead)", player) ) or ( - state.has("Goht's Remains", player) and + state.has("Goht's Remains", player) and options.remains_allow_boss_warps.value ) ) ), "Snowhead Temple Goht's Remains": lambda state: ( - can_use_fire_arrows(state, player) and + can_use_fire_arrows(state, player) and ( ( - state.has("Small Key (Snowhead)", player, 1) and + state.has("Small Key (Snowhead)", player, 1) and state.has("Boss Key (Snowhead)", player) ) or ( - state.has("Goht's Remains", player) and + state.has("Goht's Remains", player) and options.remains_allow_boss_warps.value ) ) @@ -1476,40 +1647,40 @@ def get_location_rules(player, options, prices): lambda state: state.has("Bremen Mask", player), "Romani Ranch Helping Cremia": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and state.has("Progressive Bow", player) ), "Romani Ranch Doggy Racetrack Rooftop Chest": lambda state: ( - state.has("Hookshot", player) or - can_plant_beans(state, player) or + state.has("Hookshot", player) or + can_plant_beans(state, player) or state.has("Zora Mask", player) ), "Romani Ranch Doggy Race": lambda state: state.has("Mask of Truth", player), "Romani Ranch Romani Game": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and state.has("Progressive Bow", player) ), "Romani Ranch Aliens": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and state.has("Progressive Bow", player) ), "Romani Ranch Barn Free Cow": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and can_play_song("Epona's Song", state, player) ), "Romani Ranch Barn Stables Front Cow": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and can_play_song("Epona's Song", state, player) ), "Romani Ranch Barn Stables Back Cow": lambda state: ( - can_use_powder_keg(state, player) and + can_use_powder_keg(state, player) and can_play_song("Epona's Song", state, player) ), @@ -1517,51 +1688,56 @@ def get_location_rules(player, options, prices): lambda state: can_play_song("Song of Healing", state, player), "Great Bay Scarecrow Ledge HP": lambda state: ( - can_plant_beans(state, player) and - can_reach_scarecrow(state, player) and + can_plant_beans(state, player) and + can_reach_scarecrow(state, player) and state.has("Hookshot", player) ), "Tingle Great Bay Map Purchase": lambda state: ( - has_projectiles(state, player) and ( - state.can_reach("Great Bay", 'Region', player) or - state.can_reach("Milk Road", 'Region', player) + state.can_reach("Milk Road", 'Region', player) and + has_projectiles(state, player) + ) or ( + state.can_reach("Great Bay", 'Region', player) and + ( + state.has("Progressive Bow", player) or + state.has("Hookshot", player) + ) ) ), "Great Bay Fisherman's Grotto Chest": lambda state: True, "Great Bay Ledge Grotto Left Cow": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and can_play_song("Epona's Song", state, player) ), "Great Bay Ledge Grotto Right Cow": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and can_play_song("Epona's Song", state, player) ), "Pinnacle Rock Seahorse HP": lambda state: ( - can_reach_seahorse(state, player) and - has_bottle(state, player) and + can_reach_seahorse(state, player) and + has_bottle(state, player) and state.has("Zora Mask", player) ), "Pinnacle Rock Upper Eel Chest": lambda state: ( - can_reach_seahorse(state, player) and - has_bottle(state, player) and + can_reach_seahorse(state, player) and + has_bottle(state, player) and state.has("Zora Mask", player) ), "Pinnacle Rock Lower Eel Chest": lambda state: ( - can_reach_seahorse(state, player) and - has_bottle(state, player) and + can_reach_seahorse(state, player) and + has_bottle(state, player) and state.has("Zora Mask", player) ), "Great Bay Baby Zora Song": lambda state: ( - has_bottle(state, player) and + has_bottle(state, player) and ( can_reach_seahorse(state, player) or state.can_reach("Pirates' Fortress Leader's Room Chest", "Location", player) @@ -1584,7 +1760,7 @@ def get_location_rules(player, options, prices): ), "Ocean Spider House Lobby Ceiling Token": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and can_use_fire_arrows(state, player) and has_explosives(state, player) ), @@ -1700,7 +1876,7 @@ def get_location_rules(player, options, prices): ), "Ocean Spider House Storage Room Web Token": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and can_use_fire_arrows(state, player) and has_explosives(state, player) ), @@ -1726,13 +1902,22 @@ def get_location_rules(player, options, prices): ), "Ocean Spider House Coloured Mask Sequence HP": lambda state: ( - state.has("Hookshot", player) and - state.has("Captain's Hat", player) and - state.has("Progressive Bow", player) and + state.has("Hookshot", player) and + state.has("Captain's Hat", player) and + state.has("Progressive Bow", player) and has_explosives(state, player) ), "Ocean Spider House Reward": - lambda state: state.has("Ocean Skulltula Token", player, options.required_skull_tokens.value), + lambda state: ( + ( + state.has("Ocean Skulltula Token", player, options.required_skull_tokens.value) + ) or ( + options.skullsanity.value == 2 and + has_explosives(state, player) and + state.has("Hookshot", player) and + can_use_fire_arrows(state, player) + ) + ), "Pirates' Fortress Exterior Underwater Log Chest": lambda state: state.has("Zora Mask", player), @@ -1757,20 +1942,20 @@ def get_location_rules(player, options, prices): "Pirates' Fortress Leader's Room Chest": lambda state: ( ( - state.has("Hookshot", player) or + state.has("Hookshot", player) or state.has("Goron Mask", player) - ) and + ) and ( - state.has("Progressive Bow", player) or + state.has("Progressive Bow", player) or ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.has("Progressive Magic", player) ) ) ), "Pirates' Fortress Interior Tank Chest": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and can_smack_hard(state, player) ), "Pirates' Fortress Interior Guarded Chest": @@ -1778,7 +1963,7 @@ def get_location_rules(player, options, prices): "Zora Cape Grotto Chest": lambda state: ( - state.has("Goron Mask", player) or + state.has("Goron Mask", player) or has_explosives(state, player) ), "Zora Cape Underwater Chest": @@ -1791,17 +1976,17 @@ def get_location_rules(player, options, prices): lambda state: state.has("Hookshot", player), "Zora Cape Scarecrow Chest": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and state.has("Deku Mask", player) ), "Beaver Bros. Race 1": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and state.has("Zora Mask", player) ), "Beaver Bros. Race 2 HP": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and state.has("Zora Mask", player) ), @@ -1809,55 +1994,56 @@ def get_location_rules(player, options, prices): lambda state: state.has("Zora Mask", player), "Zora Hall Torches Reward": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_use_fire_arrows(state, player) ), "Zora Hall Good Picture of Lulu": lambda state: ( - state.has("Pictograph Box", player) and + state.has("Pictograph Box", player) and state.has("Zora Mask", player) ), "Zora Hall Bad Picture of Lulu": lambda state: ( - state.has("Pictograph Box", player) and + state.has("Pictograph Box", player) and state.has("Zora Mask", player) ), "Zora Hall Deku Scrub Purchase": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and has_bottle(state, player) ), "Zora Hall Goron Scrub Trade": lambda state: ( - state.has("Zora Mask", player) and - state.has("Mountain Title Deed", player) and + state.has("Zora Mask", player) and + state.has("Mountain Title Deed", player) and state.has("Goron Mask", player) ), "Zora Hall Goron Scrub Trade Freestanding HP": lambda state: ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and state.can_reach("Zora Hall Goron Scrub Trade", 'Location', player) ), "Zora Hall Shop Item 1": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_purchase(state, player, prices, SHOP_ID_ZORA_SHOP_1) ), "Zora Hall Shop Item 2": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_purchase(state, player, prices, SHOP_ID_ZORA_SHOP_2) ), "Zora Hall Shop Item 3": lambda state: ( - state.has("Zora Mask", player) and + state.has("Zora Mask", player) and can_purchase(state, player, prices, SHOP_ID_ZORA_SHOP_3) ), "Great Bay Great Fairy Reward": lambda state: ( - state.has("Stray Fairy (Great Bay)", player, options.required_stray_fairies.value) and - state.has("Hookshot", player) + state.has("Stray Fairy (Great Bay)", player, options.required_stray_fairies.value) and + state.has("Hookshot", player) and + has_explosives(state, player) ), "Great Bay Temple Entrance Torches Chest SF": @@ -1880,9 +2066,9 @@ def get_location_rules(player, options, prices): lambda state: can_use_ice_arrows(state, player), "Great Bay Temple Before Red Valve Room Pot SF": lambda state: ( - can_use_ice_arrows(state, player) or + can_use_ice_arrows(state, player) or ( - has_projectiles(state, player) and + has_projectiles(state, player) and state.has("Great Fairy Mask", player) ) or state.has("Deku Mask", player) @@ -1895,7 +2081,7 @@ def get_location_rules(player, options, prices): lambda state: True, "Great Bay Temple Mad Jellied Gekko Chest": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Caged Chest Room Underwater Chest": @@ -1903,15 +2089,15 @@ def get_location_rules(player, options, prices): "Great Bay Temple Behind Locked Door Chest": lambda state: ( ( - state.has("Small Key (Great Bay)", player) and + state.has("Small Key (Great Bay)", player) and can_smack_hard(state, player) - ) or + ) or ( - state.has("Small Key (Great Bay)", player) and + state.has("Small Key (Great Bay)", player) and has_explosives(state, player) - ) or + ) or ( - state.has("Small Key (Great Bay)", player) and + state.has("Small Key (Great Bay)", player) and state.has("Progressive Bow", player) ) ), @@ -1919,55 +2105,55 @@ def get_location_rules(player, options, prices): lambda state: can_use_ice_arrows(state, player), "Great Bay Temple Freezable Waterwheel Upper Chest SF": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Freezable Waterwheel Lower Chest SF": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Seesaw Room Underwater Barrel SF": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Seesaw Room Chest SF": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Pre-Boss Room Platform Bubble SF": lambda state: ( - can_use_ice_arrows(state, player) and + can_use_ice_arrows(state, player) and can_use_fire_arrows(state, player) ), "Great Bay Temple Pre-Boss Room Tunnel Bubble SF": lambda state: state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player), "Great Bay Temple Heart Container": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and ( ( - state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player) and + state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player) and state.has("Boss Key (Great Bay)", player) ) or ( - state.has("Gyorg's Remains", player) and + state.has("Gyorg's Remains", player) and options.remains_allow_boss_warps.value ) ) ), "Great Bay Temple Gyorg's Remains": lambda state: ( - state.has("Hookshot", player) and + state.has("Hookshot", player) and ( ( - state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player) and + state.can_reach("Great Bay Temple Pre-Boss Room Platform Bubble SF", 'Location', player) and state.has("Boss Key (Great Bay)", player) ) or ( - state.has("Gyorg's Remains", player) and + state.has("Gyorg's Remains", player) and options.remains_allow_boss_warps.value ) ) @@ -1979,8 +2165,8 @@ def get_location_rules(player, options, prices): lambda state: state.has("Goron Mask", player), "Road to Ikana Invisible Soldier": lambda state: ( - can_play_song("Epona's Song", state, player) and - has_bottle(state, player) and + can_play_song("Epona's Song", state, player) and + has_bottle(state, player) and can_use_lens(state, player) ), @@ -1988,47 +2174,52 @@ def get_location_rules(player, options, prices): lambda state: has_explosives(state, player), "Graveyard Day 1 Bats Chest": lambda state: ( - state.has("Captain's Hat", player) and + state.has("Captain's Hat", player) and can_smack(state, player) ), "Graveyard Day 2 Dampe Bats": lambda state: has_projectiles(state, player), "Graveyard Day 2 Iron Knuckle Chest": lambda state: ( - state.has("Captain's Hat", player) and - can_smack_hard(state, player) and - has_explosives(state, player) and + state.has("Captain's Hat", player) and + ( + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) or + state.has("Goron Mask", player) + ) and + has_explosives(state, player) and can_use_lens(state, player) ), "Graveyard Day 3 Dampe Big Poe Chest": lambda state: ( + state.has("Captain's Hat", player) and ( - state.has("Captain's Hat", player) and - state.has("Progressive Bow", player) - ) or - ( - state.has("Captain's Hat", player) and + state.has("Progressive Bow", player) or state.has("Zora Mask", player) ) ), "Graveyard Captain Keeta Chest": lambda state: ( - can_play_song("Sonata of Awakening", state, player) and - can_smack_hard(state, player) + can_play_song("Sonata of Awakening", state, player) and + can_smack_hard(state, player) and + has_hard_projectiles(state, player) ), "Graveyard Day 1 Iron Knuckle Song": lambda state: ( - state.has("Captain's Hat", player) and - can_smack_hard(state, player) + state.has("Captain's Hat", player) and + ( + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) or + state.has("Goron Mask", player) + ) ), "Tingle Stone Tower Map Purchase": lambda state: ( ( - state.can_reach("Ikana Canyon", 'Region', player) and - can_use_ice_arrows(state, player) and - state.has("Hookshot", player) - ) or + state.can_reach("Upper Ikana Canyon", 'Region', player) and + has_projectiles(state, player) + ) or ( state.can_reach("Great Bay", 'Region', player) and ( @@ -2038,13 +2229,9 @@ def get_location_rules(player, options, prices): ) ), "Ikana Canyon Spirit House": - lambda state: ( - can_use_ice_arrows(state, player) and - state.has("Hookshot", player) - ), + lambda state: True, "Ikana Canyon Healing Pamela's Father": lambda state: ( - can_use_ice_arrows(state, player) and can_play_song("Song of Healing", state, player) and can_play_song("Song of Storms", state, player) ), @@ -2068,30 +2255,27 @@ def get_location_rules(player, options, prices): lambda state: True, "Stone Tower Great Fairy Reward": - lambda state: ( - state.has("Stray Fairy (Stone Tower)", player, options.required_stray_fairies.value) and - can_use_ice_arrows(state, player) - ), + lambda state: state.has("Stray Fairy (Stone Tower)", player, options.required_stray_fairies.value), "Secret Shrine Dinolfos Chest": lambda state: ( - can_smack_hard(state, player) and + can_smack_hard(state, player) and can_use_light_arrows(state, player) ), "Secret Shrine Wizzrobe Chest": lambda state: ( - can_smack_hard(state, player) and + can_smack_hard(state, player) and can_use_light_arrows(state, player) ), "Secret Shrine Wart Chest": lambda state: ( - can_smack_hard(state, player) and + can_smack_hard(state, player) and can_use_light_arrows(state, player) ), "Secret Shrine Garo Master Chest": lambda state: ( - can_use_light_arrows(state, player) and - can_smack_hard(state, player) + can_smack_hard(state, player) and + can_use_light_arrows(state, player) ), "Secret Shrine Completion Chest": lambda state: ( @@ -2103,65 +2287,63 @@ def get_location_rules(player, options, prices): "Ikana Well Rightside Torch Chest": lambda state: ( - state.has("Gibdo Mask", player) and - has_bottle(state, player) and + state.has("Gibdo Mask", player) and + has_bottle(state, player) and ( - can_plant_beans(state, player) or + can_plant_beans(state, player) or can_use_light_arrows(state, player) ) ), "Ikana Well Invisible Chest": lambda state: ( - state.has("Gibdo Mask", player) and - has_bottle(state, player) and + state.has("Gibdo Mask", player) and + has_bottle(state, player) and ( - can_afford_price(state, player, 100) or + can_afford_price(state, player, 100) or state.has("Mask of Scents", player) ) ), "Ikana Well Final Chest": lambda state: ( ( - state.has("Gibdo Mask", player) and - has_bottle(state, player) and - can_plant_beans(state, player) and + state.has("Gibdo Mask", player) and + has_bottle(state, player) and + can_plant_beans(state, player) and ( - state.has("Progressive Bomb Bag", player) or + state.has("Progressive Bomb Bag", player) or ( - state.has("Captain's Hat", player) and + state.has("Captain's Hat", player) and state.has("Progressive Bow", player) ) ) - ) or + ) or ( - can_use_light_arrows(state, player) and + can_use_light_arrows(state, player) and can_use_fire_arrows(state, player) ) ), "Ikana Well Cow": lambda state: ( - state.has("Gibdo Mask", player) and - has_bottle(state, player) and + state.has("Gibdo Mask", player) and + has_bottle(state, player) and ( - can_play_song("Epona's Song", state, player) and + can_play_song("Epona's Song", state, player) and ( - can_plant_beans(state, player) or + can_plant_beans(state, player) or can_use_light_arrows(state, player) - ) and - ( + ) and ( ( - can_play_song("Song of Soaring", state, player) and + can_play_song("Song of Soaring", state, player) and ( ( - state.can_reach("Twin Islands", 'Region', player) and + state.can_reach("Twin Islands", 'Region', player) and can_use_fire_arrows(state, player) - ) or - ( - can_use_lens(state, player) and + ) or ( + can_use_lens(state, player) and state.has("Goron Mask", player) ) ) - ) or + ) or state.can_reach("Ikana Well Invisible Chest", 'Location', player) ) ) @@ -2169,23 +2351,23 @@ def get_location_rules(player, options, prices): "Ikana Castle Pillar Freestanding HP": lambda state: ( - state.has("Deku Mask", player) and - can_use_lens(state, player) and + state.has("Deku Mask", player) and + can_use_lens(state, player) and can_use_fire_arrows(state, player) ), "Ikana Castle King Song": lambda state: ( ( - state.has("Deku Mask", player) and - can_use_lens(state, player) and - can_use_fire_arrows(state, player) and - state.has("Powder Keg", player) and - state.has("Goron Mask", player) and + state.has("Deku Mask", player) and + can_use_lens(state, player) and + can_use_fire_arrows(state, player) and + state.has("Powder Keg", player) and + state.has("Goron Mask", player) and has_mirror_shield(state, player) ) or ( - can_use_fire_arrows(state, player) and - has_mirror_shield(state, player) and + can_use_fire_arrows(state, player) and + has_mirror_shield(state, player) and can_use_light_arrows(state, player) ) ), @@ -2201,16 +2383,16 @@ def get_location_rules(player, options, prices): lambda state: state.has("Progressive Bow", player), "Stone Tower Temple Entrance Room Lower Chest": lambda state: ( - state.has("Small Key (Stone Tower)", player, 4) and - state.has("Deku Mask", player) and + state.has("Small Key (Stone Tower)", player, 4) and + state.has("Deku Mask", player) and can_use_light_arrows(state, player) ), "Stone Tower Temple Armos Room Back Chest": lambda state: ( ( - has_explosives(state, player) and + has_explosives(state, player) and has_mirror_shield(state, player) - ) or + ) or can_use_light_arrows(state, player) ), "Stone Tower Temple Armos Room Upper Chest": @@ -2218,80 +2400,82 @@ def get_location_rules(player, options, prices): "Stone Tower Temple Armos Room Lava Chest": lambda state: ( ( - has_explosives(state, player) and + has_explosives(state, player) and has_mirror_shield(state, player) - ) or + ) or can_use_light_arrows(state, player) ), "Stone Tower Temple Eyegore Room Switch Chest": lambda state: can_use_light_arrows(state, player), "Stone Tower Temple Eyegore Room Dexi Hand Ledge Chest": lambda state: ( - state.has("Small Key (Stone Tower)", player) or + state.has("Small Key (Stone Tower)", player) or can_use_light_arrows(state, player) ), "Stone Tower Temple Eastern Water Room Underwater Chest": lambda state: can_use_light_arrows(state, player), "Stone Tower Temple Eastern Water Room Sun Block Chest": lambda state: ( - can_use_light_arrows(state, player) or + can_use_light_arrows(state, player) or ( - state.has("Small Key (Stone Tower)", player) and + state.has("Small Key (Stone Tower)", player) and has_mirror_shield(state, player) ) ), "Stone Tower Temple Mirror Room Sun Block Chest": lambda state: ( ( - state.has("Small Key (Stone Tower)", player, 2) and + state.has("Small Key (Stone Tower)", player, 2) and has_mirror_shield(state, player) - ) or + ) or ( - can_use_light_arrows(state, player) and + can_use_light_arrows(state, player) and state.has("Small Key (Stone Tower)", player, 1) ) ), "Stone Tower Temple Mirror Room Sun Face Chest": lambda state: ( ( - state.has("Small Key (Stone Tower)", player, 2) and + state.has("Small Key (Stone Tower)", player, 2) and has_mirror_shield(state, player) - ) or + ) or ( - can_use_light_arrows(state, player) and + can_use_light_arrows(state, player) and state.has("Small Key (Stone Tower)", player, 1) ) ), "Stone Tower Temple Air Gust Room Side Chest": lambda state: ( + state.has("Deku Mask", player) and ( - state.has("Small Key (Stone Tower)", player, 2) and - has_mirror_shield(state, player) and - state.has("Deku Mask", player) - ) or + state.has("Small Key (Stone Tower)", player, 2) and + has_mirror_shield(state, player) + ) or ( - can_use_light_arrows(state, player) and - state.has("Small Key (Stone Tower)", player, 1) and - state.has("Deku Mask", player) + can_use_light_arrows(state, player) and + state.has("Small Key (Stone Tower)", player, 1) ) ), "Stone Tower Temple Air Gust Room Goron Switch Chest": lambda state: ( state.can_reach("Stone Tower Temple Mirror Room Sun Block Chest", 'Location', player) and - state.has("Goron Mask", player) + state.has("Goron Mask", player) and + ( + state.has("Deku Mask", player) or + can_use_light_arrows(state, player) + ) ), "Stone Tower Temple Garo Master Chest": lambda state: ( + can_smack_hard(state, player) and ( - state.has("Small Key (Stone Tower)", player, 2) and - has_mirror_shield(state, player) and - state.has("Deku Mask", player) and - can_smack_hard(state, player) - ) or + state.has("Small Key (Stone Tower)", player, 2) and + has_mirror_shield(state, player) and + state.has("Deku Mask", player) + ) or ( - can_use_light_arrows(state, player) and - state.has("Small Key (Stone Tower)", player, 1) and - can_smack_hard(state, player) + can_use_light_arrows(state, player) and + state.has("Small Key (Stone Tower)", player, 1) ) ), "Stone Tower Temple After Garo Upside Down Chest": @@ -2302,75 +2486,75 @@ def get_location_rules(player, options, prices): lambda state: can_use_light_arrows(state, player), "Stone Tower Temple Inverted Eastern Air Gust Room Fire Chest": lambda state: ( - state.has("Deku Mask", player) and + state.has("Deku Mask", player) and can_use_light_arrows(state, player) ), "Stone Tower Temple Inverted Eastern Air Gust Room Frozen Switch Chest": lambda state: ( - can_use_light_arrows(state, player) and - state.has("Deku Mask", player) and + can_use_light_arrows(state, player) and + state.has("Deku Mask", player) and can_use_fire_arrows(state, player) ), "Stone Tower Temple Inverted Eastern Air Gust Room Switch Chest": lambda state: ( - can_use_light_arrows(state, player) and + can_use_light_arrows(state, player) and state.has("Deku Mask", player) ), "Stone Tower Temple Inverted Wizzrobe Chest": lambda state: ( - can_use_light_arrows(state, player) and - state.has("Deku Mask", player) and + can_use_light_arrows(state, player) and + state.has("Deku Mask", player) and state.has("Small Key (Stone Tower)", player, 3) ), "Stone Tower Temple Inverted Death Armos Maze Chest": lambda state: state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player), "Stone Tower Temple Inverted Gomess Chest": lambda state: ( - state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player) and - can_use_light_arrows(state, player) and + state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player) and + can_use_light_arrows(state, player) and can_smack_hard(state, player) ), "Stone Tower Temple Inverted Eyegore Chest": lambda state: ( - state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player) and + state.can_reach("Stone Tower Temple Inverted Wizzrobe Chest", 'Location', player) and state.has("Small Key (Stone Tower)", player, 4) ), "Stone Tower Temple Inverted Heart Container": lambda state: ( - state.can_reach("Stone Tower Temple Inverted Eyegore Chest", 'Location', player) and + state.can_reach("Stone Tower Temple Inverted Eyegore Chest", 'Location', player) and ( - state.has("Progressive Bow", player) or - state.has("Fierce Deity's Mask", player) or + state.has("Progressive Bow", player) or + state.has("Fierce Deity's Mask", player) or ( - state.has("Giant's Mask", player) and - state.has("Progressive Magic", player) and + state.has("Giant's Mask", player) and + state.has("Progressive Magic", player) and state.has("Progressive Sword", player) ) - ) and - ( - state.has("Boss Key (Stone Tower)", player) or + ) and ( + state.has("Small Key (Stone Tower)", player, 4) and + state.has("Boss Key (Stone Tower)", player) or ( - state.has("Twinmold's Remains", player) and + state.has("Twinmold's Remains", player) and options.remains_allow_boss_warps.value ) ) ), "Stone Tower Temple Inverted Twinmold's Remains": lambda state: ( - state.can_reach("Stone Tower Temple Inverted Eyegore Chest", 'Location', player) and + state.can_reach("Stone Tower Temple Inverted Eyegore Chest", 'Location', player) and ( - state.has("Progressive Bow", player) or - state.has("Fierce Deity's Mask", player) or + state.has("Progressive Bow", player) or + state.has("Fierce Deity's Mask", player) or ( - state.has("Giant's Mask", player) and - state.has("Progressive Magic", player) and + state.has("Giant's Mask", player) and + state.has("Progressive Magic", player) and state.has("Progressive Sword", player) ) - ) and - ( - state.has("Boss Key (Stone Tower)", player) or + ) and ( + state.has("Small Key (Stone Tower)", player, 4) and + state.has("Boss Key (Stone Tower)", player) or ( - state.has("Twinmold's Remains", player) and + state.has("Twinmold's Remains", player) and options.remains_allow_boss_warps.value ) ) @@ -2378,9 +2562,9 @@ def get_location_rules(player, options, prices): "Oath to Order": lambda state: ( - can_clear_woodfall(state, player) or - can_clear_snowhead(state, player) or - can_clear_greatbay(state, player) or + can_clear_woodfall(state, player) or + can_clear_snowhead(state, player) or + can_clear_greatbay(state, player) or can_clear_stonetower(state, player) ), @@ -2388,68 +2572,72 @@ def get_location_rules(player, options, prices): lambda state: state.has("Deku Mask", player), "Moon Goron Trial HP": lambda state: ( - state.has("Goron Mask", player) and + state.has("Goron Mask", player) and state.has("Progressive Magic", player) ), "Moon Zora Trial HP": lambda state: state.has("Zora Mask", player), "Moon Link Trial Garo Master Chest": lambda state: ( - can_smack_hard(state, player) and - state.has("Hookshot", player) + state.has("Hookshot", player) and + ( + state.has("Progressive Sword", player) or + state.has("Great Fairy Sword", player) or + state.has("Goron Mask", player) + ) ), "Moon Link Trial Iron Knuckle Chest": lambda state: state.can_reach("Moon Link Trial Garo Master Chest", 'Location', player), "Moon Link Trial HP": lambda state: ( - state.can_reach("Moon Link Trial Garo Master Chest", 'Location', player) and - has_bombchus(state, player) and + state.can_reach("Moon Link Trial Iron Knuckle Chest", 'Location', player) and + has_bombchus(state, player) and state.has("Progressive Bow", player) ), "Moon Trade All Masks": lambda state: ( - state.can_reach("Moon Deku Trial HP", 'Location', player) and - state.can_reach("Moon Goron Trial HP", 'Location', player) and - state.can_reach("Moon Zora Trial HP", 'Location', player) and - state.can_reach("Moon Link Trial HP", 'Location', player) and - can_use_fire_arrows(state, player) and - state.has("Captain's Hat", player) and - state.has("Giant's Mask", player) and - state.has("All-Night Mask", player) and - state.has("Bunny Hood", player) and - state.has("Keaton Mask", player) and - state.has("Garo Mask", player) and - state.has("Romani Mask", player) and - state.has("Circus Leader's Mask", player) and - state.has("Postman's Hat", player) and - state.has("Couple's Mask", player) and - state.has("Great Fairy Mask", player) and - state.has("Gibdo Mask", player) and - state.has("Don Gero Mask", player) and - state.has("Kamaro Mask", player) and - state.has("Mask of Truth", player) and - state.has("Stone Mask", player) and - state.has("Bremen Mask", player) and - state.has("Blast Mask", player) and - state.has("Mask of Scents", player) and + state.can_reach("Moon Deku Trial HP", 'Location', player) and + state.can_reach("Moon Goron Trial HP", 'Location', player) and + state.can_reach("Moon Zora Trial HP", 'Location', player) and + state.can_reach("Moon Link Trial HP", 'Location', player) and + can_use_fire_arrows(state, player) and + state.has("Captain's Hat", player) and + state.has("Giant's Mask", player) and + state.has("All-Night Mask", player) and + state.has("Bunny Hood", player) and + state.has("Keaton Mask", player) and + state.has("Garo Mask", player) and + state.has("Romani Mask", player) and + state.has("Circus Leader's Mask", player) and + state.has("Postman's Hat", player) and + state.has("Couple's Mask", player) and + state.has("Great Fairy Mask", player) and + state.has("Gibdo Mask", player) and + state.has("Don Gero Mask", player) and + state.has("Kamaro Mask", player) and + state.has("Mask of Truth", player) and + state.has("Stone Mask", player) and + state.has("Bremen Mask", player) and + state.has("Blast Mask", player) and + state.has("Mask of Scents", player) and state.has("Kafei's Mask", player) ), "Defeat Majora": lambda state: ( - can_smack_hard(state, player) and + can_smack_hard(state, player) and ( ( ( - state.has("Zora Mask", player) or + state.has("Zora Mask", player) or has_mirror_shield(state, player) - ) and + ) and can_use_light_arrows(state, player) ) or ( - state.has("Fierce Deity's Mask", player) and + state.has("Fierce Deity's Mask", player) and state.has("Progressive Magic", player) ) - ) and + ) and has_enough_remains(state, player, options.majora_remains_required.value) ), } diff --git a/worlds/mm_recomp/Regions.py b/worlds/mm_recomp/Regions.py index b60e91d3..48bd32d1 100644 --- a/worlds/mm_recomp/Regions.py +++ b/worlds/mm_recomp/Regions.py @@ -14,45 +14,53 @@ class MMRRegionData(NamedTuple): "Bomber's Hideout": MMRRegionData(["Astral Observatory"]), "Astral Observatory": MMRRegionData(["Astral Observatory (Outside)"]), "Astral Observatory (Outside)": MMRRegionData(["Termina Field"]), - "Termina Field": MMRRegionData(["Southern Swamp", "Milk Road", "Path to Mountain Village", "Great Bay", "Road to Ikana"]), - "Southern Swamp": MMRRegionData(["Southern Swamp (Deku Palace)"]), + "Termina Field": MMRRegionData(["Road to Southern Swamp", "Milk Road", "Path to Mountain Village", "Great Bay", "Road to Ikana"]), + "Road to Southern Swamp": MMRRegionData(["Termina Field", "Southern Swamp"]), + "Southern Swamp": MMRRegionData(["Road to Southern Swamp", "Southern Swamp (Deku Palace)"]), "Southern Swamp (Deku Palace)": MMRRegionData(["Swamp Spider House", "Deku Palace", "Woodfall"]), "Swamp Spider House": MMRRegionData([]), "Deku Palace": MMRRegionData([]), - "Woodfall": MMRRegionData(["Woodfall Temple"]), - "Woodfall Temple": MMRRegionData([]), + "Woodfall": MMRRegionData(["Woodfall Temple", "Southern Swamp (Deku Palace)"]), + "Woodfall Temple": MMRRegionData(["Odolwa's Lair"]), + "Odolwa's Lair": MMRRegionData([]), "Milk Road": MMRRegionData(["Gorman Brothers Track", "Romani Ranch"]), - "Gorman Brothers Track": MMRRegionData([]), + "Gorman Brothers Track": MMRRegionData(["Inside Gorman Brothers Track"]), + "Inside Gorman Brothers Track": MMRRegionData([]), "Romani Ranch": MMRRegionData([]), - "Path to Mountain Village": MMRRegionData(["Mountain Village"]), - "Mountain Village": MMRRegionData(["Twin Islands", "Path to Snowhead"]), - "Twin Islands": MMRRegionData(["Goron Village"]), + "Path to Mountain Village": MMRRegionData(["Mountain Village", "Termina Field"]), + "Mountain Village": MMRRegionData(["Twin Islands", "Path to Snowhead", "Path to Mountain Village", "Termina Field"]), + "Twin Islands": MMRRegionData(["Goron Village", "Goron Racetrack"]), + "Goron Racetrack": MMRRegionData(["Twin Islands"]), "Goron Village": MMRRegionData(["Goron Shrine"]), "Goron Shrine": MMRRegionData([]), - "Path to Snowhead": MMRRegionData(["Snowhead"]), - "Snowhead": MMRRegionData(["Snowhead Temple"]), - "Snowhead Temple": MMRRegionData([]), - "Great Bay": MMRRegionData(["Ocean Spider House", "Fisherman's House", "Pirates' Fortress", "Pinnacle Rock", "Zora Cape"]), + "Path to Snowhead": MMRRegionData(["Snowhead","Mountain Village"]), + "Snowhead": MMRRegionData(["Snowhead Temple", "Path to Snowhead"]), + "Snowhead Temple": MMRRegionData(["Goht's Lair"]), + "Goht's Lair": MMRRegionData([]), + "Great Bay": MMRRegionData(["Ocean Spider House", "Fisherman's House", "Pirates' Fortress", "Pinnacle Rock", "Zora Cape", "Termina Field"]), "Ocean Spider House": MMRRegionData([]), "Fisherman's House": MMRRegionData([]), "Pirates' Fortress": MMRRegionData(["Pirates' Fortress Sewers", "Pirates' Fortress (Interior)"]), "Pirates' Fortress Sewers": MMRRegionData(["Pirates' Fortress (Interior)"]), "Pirates' Fortress (Interior)": MMRRegionData([]), "Pinnacle Rock": MMRRegionData([]), - "Zora Cape": MMRRegionData(["Zora Hall", "Great Bay Temple"]), + "Zora Cape": MMRRegionData(["Zora Hall", "Great Bay Temple", "Great Bay"]), "Zora Hall": MMRRegionData([]), - "Great Bay Temple": MMRRegionData([]), - "Road to Ikana": MMRRegionData(["Ikana Graveyard", "Ikana Canyon"]), - "Ikana Graveyard": MMRRegionData([]), - "Ikana Canyon": MMRRegionData(["Secret Shrine", "Beneath the Well", "Ikana Castle", "Stone Tower"]), - "Secret Shrine": MMRRegionData([]), - "Beneath the Well": MMRRegionData(["Ikana Castle"]), + "Great Bay Temple": MMRRegionData(["Gyorg's Lair"]), + "Gyorg's Lair": MMRRegionData([]), + "Road to Ikana": MMRRegionData(["Ikana Graveyard", "Lower Ikana Canyon", "Termina Field"]), + "Ikana Graveyard": MMRRegionData(["Road to Ikana"]), + "Lower Ikana Canyon": MMRRegionData(["Secret Shrine", "Upper Ikana Canyon", "Road to Ikana"]), + "Upper Ikana Canyon": MMRRegionData(["Beneath the Well", "Ikana Castle", "Stone Tower", "Lower Ikana Canyon"]), + "Secret Shrine": MMRRegionData(["Lower Ikana Canyon"]), + "Beneath the Well": MMRRegionData(["Ikana Castle", "Upper Ikana Canyon"]), "Ikana Castle": MMRRegionData(["Beneath the Well", "Inside Ikana Castle"]), "Inside Ikana Castle": MMRRegionData([]), - "Stone Tower": MMRRegionData(["Stone Tower Temple", "Stone Tower (Inverted)"]), + "Stone Tower": MMRRegionData(["Stone Tower Temple", "Stone Tower (Inverted)", "Upper Ikana Canyon"]), "Stone Tower Temple": MMRRegionData([]), "Stone Tower (Inverted)": MMRRegionData(["Stone Tower Temple (Inverted)"]), - "Stone Tower Temple (Inverted)": MMRRegionData([]), + "Stone Tower Temple (Inverted)": MMRRegionData(["Twinmold's Lair"]), + "Twinmold's Lair": MMRRegionData([]), } def get_exit(region, exit_name): From b6b859160122f02448b67974069321c0f8359fc0 Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Tue, 4 Aug 2026 22:01:15 -0300 Subject: [PATCH 19/20] Swapped IDs for Pirates' Fortress Sewers Underwater Upper and Lower chests --- worlds/mm_recomp/Locations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/mm_recomp/Locations.py b/worlds/mm_recomp/Locations.py index d75e08c5..c3a3cbb9 100644 --- a/worlds/mm_recomp/Locations.py +++ b/worlds/mm_recomp/Locations.py @@ -1299,11 +1299,11 @@ def can_create_heart_location(shp, c_or_p, loc_index): ), "Pirates' Fortress Sewers Underwater Lower Chest": MMRLocationData( region="Pirates' Fortress Sewers", - address=0x3469420062304 + address=0x3469420062306 ), "Pirates' Fortress Sewers Underwater Upper Chest": MMRLocationData( region="Pirates' Fortress Sewers", - address=0x3469420062306 + address=0x3469420062304 ), "Pirates' Fortress Exterior Underwater Log Chest": MMRLocationData( region="Pirates' Fortress", From f247b431a13f166cb72391f5d780bc934a7d0b4b Mon Sep 17 00:00:00 2001 From: G4M3R L1F3 Date: Tue, 4 Aug 2026 22:16:11 -0300 Subject: [PATCH 20/20] Updated "Graveyard Captain Keeta Chest" logic Will require a bow instead of any projectile and make it possible with Goron Mask only. --- worlds/mm_recomp/NormalRules.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/worlds/mm_recomp/NormalRules.py b/worlds/mm_recomp/NormalRules.py index e4bf5746..2cf3fc7e 100644 --- a/worlds/mm_recomp/NormalRules.py +++ b/worlds/mm_recomp/NormalRules.py @@ -2201,8 +2201,13 @@ def get_location_rules(player, options, prices): "Graveyard Captain Keeta Chest": lambda state: ( can_play_song("Sonata of Awakening", state, player) and - can_smack_hard(state, player) and - has_hard_projectiles(state, player) + ( + state.has("Goron Mask", player) or + ( + can_smack_hard(state, player) and + state.has("Progressive Bow", player) + ) + ) ), "Graveyard Day 1 Iron Knuckle Song": lambda state: (