From eef8f7af1a4f6fea4722e2e28ade4d5cafc033b8 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 6 Apr 2023 03:48:30 -0500 Subject: [PATCH] The Messenger: Add Mega Time Shards and Quest 1 boss locations (#1661) * implement mega shards * create the option and locations, add to slot data and tests * add boss refights as locations * remove barma'thazel. it's apparently impossible to get to him * remove barma'thazel again * up max shard count to 85 * increment version * dynamically alter the power seal pool * revert host.yaml change * two mega shards were missing from the maps * add new checks to the info page * add some more rules to skylands * forgot to update my tests * explicit imports, remove unnecessary typing, lower required client ver * use generators for shard and seal creation --- worlds/messenger/Constants.py | 8 ++++ worlds/messenger/Options.py | 12 ++++-- worlds/messenger/Regions.py | 32 ++++++++++++--- worlds/messenger/Rules.py | 24 +++++++++-- worlds/messenger/SubClasses.py | 12 +++--- worlds/messenger/__init__.py | 50 ++++++++++++++++------- worlds/messenger/docs/en_The Messenger.md | 4 +- worlds/messenger/test/TestAccess.py | 38 ++++++++++++----- worlds/messenger/test/TestLogic.py | 8 ++-- worlds/messenger/test/TestShopChest.py | 30 ++++++++++++++ 10 files changed, 169 insertions(+), 49 deletions(-) diff --git a/worlds/messenger/Constants.py b/worlds/messenger/Constants.py index f967fec8..643c3f83 100644 --- a/worlds/messenger/Constants.py +++ b/worlds/messenger/Constants.py @@ -1,5 +1,6 @@ # items # listing individual groups first for easy lookup + NOTES = [ "Key of Hope", "Key of Chaos", @@ -151,3 +152,10 @@ SEALS = [ "Elemental Skylands Seal - Water", "Elemental Skylands Seal - Fire", ] + +BOSS_LOCATIONS = [ + "Leaf Golem", + "Ruxxtin", + "Emerald Golem", + "Queen of Quills", +] diff --git a/worlds/messenger/Options.py b/worlds/messenger/Options.py index 47ebf66f..636b6054 100644 --- a/worlds/messenger/Options.py +++ b/worlds/messenger/Options.py @@ -1,4 +1,4 @@ -from Options import DefaultOnToggle, DeathLink, Range, Accessibility, Choice +from Options import DefaultOnToggle, DeathLink, Range, Accessibility, Choice, Toggle class MessengerAccessibility(Accessibility): @@ -27,6 +27,11 @@ class PowerSeals(DefaultOnToggle): display_name = "Shuffle Seals" +class MegaShards(Toggle): + """Whether mega shards should be item locations.""" + display_name = "Shuffle Mega Time Shards" + + class Goal(Choice): """Requirement to finish the game. Power Seal Hunt will force power seal locations to be shuffled.""" display_name = "Goal" @@ -51,8 +56,8 @@ class AmountSeals(Range): """Number of power seals that exist in the item pool when power seal hunt is the goal.""" display_name = "Total Power Seals" range_start = 1 - range_end = 45 - default = range_end + range_end = 85 + default = 45 class RequiredSeals(Range): @@ -67,6 +72,7 @@ messenger_options = { "accessibility": MessengerAccessibility, "logic_level": Logic, "shuffle_seals": PowerSeals, + "shuffle_shards": MegaShards, "goal": Goal, "music_box": MusicBox, "notes_needed": NotesNeeded, diff --git a/worlds/messenger/Regions.py b/worlds/messenger/Regions.py index ab84f0b3..8a9cfffb 100644 --- a/worlds/messenger/Regions.py +++ b/worlds/messenger/Regions.py @@ -6,16 +6,17 @@ REGIONS: Dict[str, List[str]] = { "The Shop": [], "Tower of Time": [], "Ninja Village": ["Candle", "Astral Seed"], - "Autumn Hills": ["Climbing Claws", "Key of Hope"], + "Autumn Hills": ["Climbing Claws", "Key of Hope", "Leaf Golem"], "Forlorn Temple": ["Demon King Crown"], - "Catacombs": ["Necro", "Ruxxtin's Amulet"], + "Catacombs": ["Necro", "Ruxxtin's Amulet", "Ruxxtin"], "Bamboo Creek": ["Claustro"], - "Howling Grotto": ["Wingsuit"], - "Quillshroom Marsh": ["Seashell"], + "Howling Grotto": ["Wingsuit", "Emerald Golem"], + "Quillshroom Marsh": ["Seashell", "Queen of Quills"], "Searing Crags": ["Rope Dart"], "Searing Crags Upper": ["Power Thistle", "Key of Strength", "Astral Tea Leaves"], "Glacial Peak": [], - "Cloud Ruins": ["Acro"], + "Cloud Ruins": [], + "Cloud Ruins Right": ["Acro"], "Underworld": ["Pyro", "Key of Chaos"], "Dark Cave": [], "Riviere Turquoise": ["Fairy Bottle"], @@ -26,6 +27,24 @@ REGIONS: Dict[str, List[str]] = { } """seal locations have the region in their name and may not need to be created so skip them here""" +MEGA_SHARDS: Dict[str, List[str]] = { + "Autumn Hills": ["Autumn Hills Mega Shard", "Hidden Entrance Mega Shard"], + "Catacombs": ["Catacombs Mega Shard"], + "Bamboo Creek": ["Above Entrance Mega Shard", "Abandoned Mega Shard", "Time Loop Mega Shard"], + "Howling Grotto": ["Bottom Left Mega Shard", "Near Portal Mega Shard", "Pie in the Sky Mega Shard"], + "Quillshroom Marsh": ["Quillshroom Marsh Mega Shard"], + "Searing Crags Upper": ["Searing Crags Mega Shard"], + "Glacial Peak": ["Glacial Peak Mega Shard"], + "Tower of Time": [], + "Cloud Ruins": ["Cloud Entrance Mega Shard", "Time Warp Mega Shard"], + "Cloud Ruins Right": ["Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2"], + "Underworld": ["Under Entrance Mega Shard", "Hot Tub Mega Shard", "Projectile Pit Mega Shard"], + "Forlorn Temple": ["Sunny Day Mega Shard", "Down Under Mega Shard"], + "Sunken Shrine": ["Mega Shard of the Moon", "Beginner's Mega Shard", "Mega Shard of the Stars", "Mega Shard of the Sun"], + "Riviere Turquoise": ["Waterfall Mega Shard", "Quick Restock Mega Shard 1", "Quick Restock Mega Shard 2"], + "Elemental Skylands": ["Earth Mega Shard", "Water Mega Shard"], +} + REGION_CONNECTIONS: Dict[str, Set[str]] = { "Menu": {"Tower HQ"}, @@ -42,7 +61,8 @@ REGION_CONNECTIONS: Dict[str, Set[str]] = { "Searing Crags": {"Searing Crags Upper", "Quillshroom Marsh", "Underworld"}, "Searing Crags Upper": {"Searing Crags", "Glacial Peak"}, "Glacial Peak": {"Searing Crags Upper", "Tower HQ", "Cloud Ruins", "Elemental Skylands"}, - "Cloud Ruins": {"Underworld"}, + "Cloud Ruins": {"Cloud Ruins Right"}, + "Cloud Ruins Right": {"Underworld"}, "Underworld": set(), "Dark Cave": {"Catacombs", "Riviere Turquoise"}, "Riviere Turquoise": set(), diff --git a/worlds/messenger/Rules.py b/worlds/messenger/Rules.py index a459fdb7..c171eddf 100644 --- a/worlds/messenger/Rules.py +++ b/worlds/messenger/Rules.py @@ -27,7 +27,8 @@ class MessengerRules: "Catacombs": self.has_wingsuit, "Bamboo Creek": self.has_wingsuit, "Searing Crags Upper": self.has_vertical, - "Cloud Ruins": lambda state: self.has_wingsuit(state) and state.has("Ruxxtin's Amulet", self.player), + "Cloud Ruins": lambda state: self.has_vertical(state) and state.has("Ruxxtin's Amulet", self.player), + "Cloud Ruins Right": self.has_wingsuit, "Underworld": self.has_tabi, "Forlorn Temple": lambda state: state.has_all({"Wingsuit", *PHOBEKINS}, self.player), "Glacial Peak": self.has_vertical, @@ -43,6 +44,7 @@ class MessengerRules: # howling grotto "Howling Grotto Seal - Windy Saws and Balls": self.has_wingsuit, "Howling Grotto Seal - Crushing Pits": lambda state: self.has_wingsuit(state) and self.has_dart(state), + "Emerald Golem": self.has_wingsuit, # searing crags "Astral Tea Leaves": lambda state: state.can_reach("Astral Seed", "Location", self.player), "Key of Strength": lambda state: state.has("Power Thistle", self.player), @@ -64,14 +66,20 @@ class MessengerRules: "Key of Love": lambda state: state.has_all({"Sun Crest", "Moon Crest"}, self.player), "Sunken Shrine Seal - Waterfall Paradise": self.has_tabi, "Sunken Shrine Seal - Tabi Gauntlet": self.has_tabi, + "Mega Shard of the Moon": self.has_tabi, + "Mega Shard of the Sun": self.has_tabi, # riviere turquoise "Fairy Bottle": self.has_vertical, "Riviere Turquoise Seal - Flower Power": self.has_vertical, + "Quick Restock Mega Shard 1": self.has_vertical, + "Quick Restock Mega Shard 2": self.has_vertical, # elemental skylands "Key of Symbiosis": self.has_dart, "Elemental Skylands Seal - Air": self.has_wingsuit, "Elemental Skylands Seal - Water": self.has_dart, "Elemental Skylands Seal - Fire": self.has_dart, + "Earth Mega Shard": self.has_dart, + "Water Mega Shard": self.has_dart, # corrupted future "Key of Courage": lambda state: state.has_all({"Demon King Crown", "Fairy Bottle"}, self.player), # the shop @@ -130,12 +138,17 @@ class MessengerHardRules(MessengerRules): "Forlorn Temple": lambda state: self.has_vertical(state) and state.has_all(set(PHOBEKINS), self.player), "Searing Crags Upper": self.true, "Glacial Peak": self.true, + "Elemental Skylands": lambda state: state.has("Fairy Bottle", self.player) or self.has_windmill(state), }) self.location_rules.update({ "Howling Grotto Seal - Windy Saws and Balls": self.true, "Glacial Peak Seal - Projectile Spike Pit": self.true, "Claustro": self.has_wingsuit, + "Elemental Skylands Seal - Water": self.true, + "Elemental Skylands Seal - Fire": self.true, + "Earth Mega Shard": self.true, + "Water Mega Shard": self.true, }) self.extra_rules = { @@ -156,6 +169,8 @@ class MessengerHardRules(MessengerRules): for loc, rule in self.extra_rules.items(): if not self.world.multiworld.shuffle_seals[self.player] and "Seal" in loc: continue + if not self.world.multiworld.shuffle_shards[self.player] and "Shard" in loc: + continue add_rule(self.world.multiworld.get_location(loc, self.player), rule, "or") @@ -166,7 +181,8 @@ class MessengerChallengeRules(MessengerHardRules): self.region_rules.update({ "Forlorn Temple": lambda state: (self.has_vertical(state) and state.has_all(set(PHOBEKINS), self.player)) or state.has_all({"Wingsuit", "Windmill Shuriken"}, self.player), - "Elemental Skylands": lambda state: self.has_wingsuit(state) or state.has("Fairy Bottle", self.player), + "Elemental Skylands": lambda state: self.has_wingsuit(state) or state.has("Fairy Bottle", self.player) + or self.has_windmill(state), }) self.location_rules.update({ @@ -220,6 +236,6 @@ def set_self_locking_items(multiworld: MultiWorld, player: int) -> None: allow_self_locking_items(multiworld.get_location("Key of Courage", player), "Demon King Crown") # add these locations when seals aren't shuffled - if not multiworld.shuffle_seals[player]: - allow_self_locking_items(multiworld.get_region("Cloud Ruins", player), "Ruxxtin's Amulet") + if not multiworld.shuffle_seals[player] and not multiworld.shuffle_shards[player]: + allow_self_locking_items(multiworld.get_region("Cloud Ruins Right", player), "Ruxxtin's Amulet") allow_self_locking_items(multiworld.get_region("Forlorn Temple", player), *PHOBEKINS) diff --git a/worlds/messenger/SubClasses.py b/worlds/messenger/SubClasses.py index 1f26a426..3daf183e 100644 --- a/worlds/messenger/SubClasses.py +++ b/worlds/messenger/SubClasses.py @@ -3,7 +3,7 @@ from typing import Set, TYPE_CHECKING, Optional, Dict from BaseClasses import Region, Location, Item, ItemClassification, Entrance from .Constants import SEALS, NOTES, PROG_ITEMS, PHOBEKINS, USEFUL_ITEMS from .Options import Goal -from .Regions import REGIONS +from .Regions import REGIONS, MEGA_SHARDS if TYPE_CHECKING: from . import MessengerWorld @@ -23,10 +23,12 @@ class MessengerRegion(Region): if self.name == "The Shop" and self.multiworld.goal[self.player] > Goal.option_open_music_box: self.locations.append(MessengerLocation("Shop Chest", self, name_to_id.get("Shop Chest", None))) # putting some dumb special case for searing crags and ToT so i can split them into 2 regions - if self.multiworld.shuffle_seals[self.player] and self.name not in {"Searing Crags", "Tower HQ"}: - for seal_loc in SEALS: - if seal_loc.startswith(self.name.split(" ")[0]): - self.locations.append(MessengerLocation(seal_loc, self, name_to_id.get(seal_loc, None))) + if self.multiworld.shuffle_seals[self.player] and self.name not in {"Searing Crags", "Tower HQ", "Cloud Ruins"}: + self.locations += [MessengerLocation(seal_loc, self, name_to_id.get(seal_loc, None)) + for seal_loc in SEALS if seal_loc.startswith(self.name.split(" ")[0])] + if self.multiworld.shuffle_shards[self.player] and self.name in MEGA_SHARDS: + self.locations += [MessengerLocation(shard, self, name_to_id.get(shard, None)) + for shard in MEGA_SHARDS[self.name]] def add_exits(self, exits: Set[str]) -> None: for exit in exits: diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py index d23f4da3..5d304edd 100644 --- a/worlds/messenger/__init__.py +++ b/worlds/messenger/__init__.py @@ -1,10 +1,11 @@ -from typing import Dict, Any, List, Optional +import logging +from typing import Dict, Any, Optional, List from BaseClasses import Tutorial, ItemClassification from worlds.AutoWorld import World, WebWorld -from .Constants import NOTES, PROG_ITEMS, PHOBEKINS, USEFUL_ITEMS, ALWAYS_LOCATIONS, SEALS, ALL_ITEMS +from .Constants import NOTES, PHOBEKINS, ALL_ITEMS, ALWAYS_LOCATIONS, SEALS, BOSS_LOCATIONS from .Options import messenger_options, NotesNeeded, Goal, PowerSeals, Logic -from .Regions import REGIONS, REGION_CONNECTIONS +from .Regions import REGIONS, REGION_CONNECTIONS, MEGA_SHARDS from .SubClasses import MessengerRegion, MessengerItem from . import Rules @@ -48,21 +49,28 @@ class MessengerWorld(World): base_offset = 0xADD_000 item_name_to_id = {item: item_id for item_id, item in enumerate(ALL_ITEMS, base_offset)} + mega_shard_locs = [shard for region in MEGA_SHARDS for shard in MEGA_SHARDS[region]] location_name_to_id = {location: location_id - for location_id, location in enumerate([*ALWAYS_LOCATIONS, *SEALS], base_offset)} + for location_id, location in + enumerate([ + *ALWAYS_LOCATIONS, + *SEALS, + *mega_shard_locs, + *BOSS_LOCATIONS, + ], base_offset)} - data_version = 1 + data_version = 2 + required_client_version = (0, 3, 9) web = MessengerWeb() - total_seals: Optional[int] = None - required_seals: Optional[int] = None + total_seals: int = 0 + required_seals: int = 0 def generate_early(self) -> None: if self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: self.multiworld.shuffle_seals[self.player].value = PowerSeals.option_true self.total_seals = self.multiworld.total_seals[self.player].value - self.required_seals = int(self.multiworld.percent_seals_required[self.player].value / 100 * self.total_seals) def create_regions(self) -> None: for region in [MessengerRegion(reg_name, self) for reg_name in REGIONS]: @@ -71,12 +79,7 @@ class MessengerWorld(World): def create_items(self) -> None: itempool: List[MessengerItem] = [] - if self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: - seals = [self.create_item("Power Seal") for _ in range(self.total_seals)] - for i in range(self.required_seals): - seals[i].classification = ItemClassification.progression_skip_balancing - itempool += seals - else: + if self.multiworld.goal[self.player] == Goal.option_open_music_box: notes = self.multiworld.random.sample(NOTES, k=len(NOTES)) precollected_notes_amount = NotesNeeded.range_end - self.multiworld.notes_needed[self.player] if precollected_notes_amount: @@ -94,6 +97,20 @@ class MessengerWorld(World): # if we get in a position where this can have duplicates of items that aren't Power Seals # or Time shards, this will need to be redone. }] + + if self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: + total_seals = min(len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool), + self.multiworld.total_seals[self.player].value) + if total_seals < self.total_seals: + logging.warning(f"Not enough locations for total seals setting. Adjusting to {total_seals}") + self.total_seals = total_seals + self.required_seals = int(self.multiworld.percent_seals_required[self.player].value / 100 * self.total_seals) + + seals = [self.create_item("Power Seal") for _ in range(self.total_seals)] + for i in range(self.required_seals): + seals[i].classification = ItemClassification.progression_skip_balancing + itempool += seals + itempool += [self.create_filler() for _ in range(len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool))] @@ -122,7 +139,10 @@ class MessengerWorld(World): "music_box": self.multiworld.music_box[self.player].value, "required_seals": self.required_seals, "locations": locations, - "settings": {"Difficulty": "Basic" if not self.multiworld.shuffle_seals[self.player] else "Advanced"}, + "settings": { + "Difficulty": "Basic" if not self.multiworld.shuffle_seals[self.player] else "Advanced", + "Mega Shards": self.multiworld.shuffle_shards[self.player].value + }, "logic": self.multiworld.logic_level[self.player].current_key, } diff --git a/worlds/messenger/docs/en_The Messenger.md b/worlds/messenger/docs/en_The Messenger.md index e25be4b9..64b0dd73 100644 --- a/worlds/messenger/docs/en_The Messenger.md +++ b/worlds/messenger/docs/en_The Messenger.md @@ -32,7 +32,9 @@ You can find items wherever items can be picked up in the original game. This in * Quest Item pickups * Music Box notes * Phobekins +* Bosses * Power seals +* Mega Time Shards ## What are the item name groups? @@ -64,8 +66,6 @@ for it. The groups you can use for The Messenger are: * Ruxxtin Coffin cutscene will sometimes not play correctly, but will still reward the item * If you receive the Fairy Bottle while in Quillshroom Marsh, The De-curse Queen cutscene will not play. You can exit to Searing Crags and re-enter to get it to play correctly. -* If you defeat Barma'thazël, the cutscene afterward will not play correctly since that is what normally transitions - you to 2nd quest. The game will not kill you if you fall here, so you can teleport to HQ at any point after defeating him. * Sometimes upon teleporting back to HQ, Ninja will run left and enter a different portal than the one entered by the player. This may also cause a softlock. * Text entry menus don't accept controller input diff --git a/worlds/messenger/test/TestAccess.py b/worlds/messenger/test/TestAccess.py index 84b29406..87bf55f7 100644 --- a/worlds/messenger/test/TestAccess.py +++ b/worlds/messenger/test/TestAccess.py @@ -3,12 +3,17 @@ from ..Constants import NOTES, PHOBEKINS class AccessTest(MessengerTestBase): + options = { + "shuffle_shards": "true", + } def testTabi(self) -> None: """locations that hard require the Ninja Tabi""" locations = ["Pyro", "Key of Chaos", "Underworld Seal - Sharp and Windy Climb", "Underworld Seal - Spike Wall", "Underworld Seal - Fireball Wave", "Underworld Seal - Rising Fanta", "Sun Crest", "Moon Crest", - "Sunken Shrine Seal - Waterfall Paradise", "Sunken Shrine Seal - Tabi Gauntlet"] + "Sunken Shrine Seal - Waterfall Paradise", "Sunken Shrine Seal - Tabi Gauntlet", + "Mega Shard of the Moon", "Mega Shard of the Sun", "Under Entrance Mega Shard", + "Hot Tub Mega Shard", "Projectile Pit Mega Shard"] items = [["Ninja Tabi"]] self.assertAccessDependency(locations, items) @@ -17,7 +22,8 @@ class AccessTest(MessengerTestBase): locations = ["Ninja Village Seal - Tree House", "Key of Hope", "Howling Grotto Seal - Crushing Pits", "Glacial Peak Seal - Ice Climbers", "Tower of Time Seal - Time Waster Seal", "Tower of Time Seal - Arcane Orbs", "Underworld Seal - Rising Fanta", "Key of Symbiosis", - "Elemental Skylands Seal - Water", "Elemental Skylands Seal - Fire"] + "Elemental Skylands Seal - Water", "Elemental Skylands Seal - Fire", "Earth Mega Shard", + "Water Mega Shard"] items = [["Rope Dart"]] self.assertAccessDependency(locations, items) @@ -35,7 +41,11 @@ class AccessTest(MessengerTestBase): "Tower of Time Seal - Lantern Climb", "Tower of Time Seal - Arcane Orbs", "Underworld Seal - Sharp and Windy Climb", "Underworld Seal - Fireball Wave", "Elemental Skylands Seal - Air", "Forlorn Temple Seal - Rocket Maze", - "Forlorn Temple Seal - Rocket Sunset", "Astral Seed", "Astral Tea Leaves"] + "Forlorn Temple Seal - Rocket Sunset", "Astral Seed", "Astral Tea Leaves", + "Autumn Hills Mega Shard", "Hidden Entrance Mega Shard", "Sunny Day Mega Shard", + "Down Under Mega Shard", "Catacombs Mega Shard", "Above Entrance Mega Shard", + "Abandoned Mega Shard", "Time Loop Mega Shard", "Money Farm Room Mega Shard 1", + "Money Farm Room Mega Shard 2", "Leaf Golem", "Ruxxtin", "Emerald Golem"] items = [["Wingsuit"]] self.assertAccessDependency(locations, items) @@ -56,18 +66,26 @@ class AccessTest(MessengerTestBase): "Cloud Ruins Seal - Saw Pit", "Cloud Ruins Seal - Money Farm Room", "Tower of Time Seal - Lantern Climb", "Tower of Time Seal - Arcane Orbs", "Underworld Seal - Sharp and Windy Climb", "Underworld Seal - Fireball Wave", - "Elemental Skylands Seal - Air", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset", - "Power Thistle", "Key of Strength", "Glacial Peak Seal - Projectile Spike Pit", - "Glacial Peak Seal - Glacial Air Swag", "Fairy Bottle", "Riviere Turquoise Seal - Flower Power", - "Searing Crags Seal - Triple Ball Spinner", "Searing Crags Seal - Raining Rocks", - "Searing Crags Seal - Rhythm Rocks", "Astral Seed", "Astral Tea Leaves", "Rescue Phantom"] + "Elemental Skylands Seal - Air", "Forlorn Temple Seal - Rocket Maze", + "Forlorn Temple Seal - Rocket Sunset", "Power Thistle", "Key of Strength", + "Glacial Peak Seal - Projectile Spike Pit", "Glacial Peak Seal - Glacial Air Swag", + "Fairy Bottle", "Riviere Turquoise Seal - Flower Power", "Searing Crags Seal - Triple Ball Spinner", + "Searing Crags Seal - Raining Rocks", "Searing Crags Seal - Rhythm Rocks", "Astral Seed", + "Astral Tea Leaves", "Rescue Phantom", "Autumn Hills Mega Shard", "Hidden Entrance Mega Shard", + "Sunny Day Mega Shard", "Down Under Mega Shard", "Catacombs Mega Shard", + "Above Entrance Mega Shard", "Abandoned Mega Shard", "Time Loop Mega Shard", + "Searing Crags Mega Shard", "Glacial Peak Mega Shard", "Cloud Entrance Mega Shard", + "Time Warp Mega Shard", "Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2", + "Quick Restock Mega Shard 1", "Quick Restock Mega Shard 2", "Earth Mega Shard", "Water Mega Shard", + "Leaf Golem", "Ruxxtin", "Emerald Golem"] items = [["Wingsuit", "Rope Dart"]] self.assertAccessDependency(locations, items) def testAmulet(self) -> None: """Locations that require Ruxxtin's Amulet""" locations = ["Acro", "Cloud Ruins Seal - Ghost Pit", "Cloud Ruins Seal - Toothbrush Alley", - "Cloud Ruins Seal - Saw Pit", "Cloud Ruins Seal - Money Farm Room"] + "Cloud Ruins Seal - Saw Pit", "Cloud Ruins Seal - Money Farm Room", "Cloud Entrance Mega Shard", + "Time Warp Mega Shard", "Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2"] # Cloud Ruins requires Ruxxtin's Amulet items = [["Ruxxtin's Amulet"]] self.assertAccessDependency(locations, items) @@ -75,7 +93,7 @@ class AccessTest(MessengerTestBase): def testBottle(self) -> None: """Elemental Skylands and Corrupted Future require the Fairy Bottle""" locations = ["Key of Symbiosis", "Elemental Skylands Seal - Air", "Elemental Skylands Seal - Fire", - "Elemental Skylands Seal - Water", "Key of Courage"] + "Elemental Skylands Seal - Water", "Key of Courage", "Earth Mega Shard", "Water Mega Shard"] items = [["Fairy Bottle"]] self.assertAccessDependency(locations, items) diff --git a/worlds/messenger/test/TestLogic.py b/worlds/messenger/test/TestLogic.py index 2fd81110..8b3d5854 100644 --- a/worlds/messenger/test/TestLogic.py +++ b/worlds/messenger/test/TestLogic.py @@ -16,21 +16,21 @@ class HardLogicTest(MessengerTestBase): # ninja village "Candle", "Astral Seed", "Ninja Village Seal - Tree House", "Astral Tea Leaves", # autumn hills - "Climbing Claws", "Key of Hope", + "Climbing Claws", "Key of Hope", "Leaf Golem", "Autumn Hills Seal - Trip Saws", "Autumn Hills Seal - Double Swing Saws", "Autumn Hills Seal - Spike Ball Swing", "Autumn Hills Seal - Spike Ball Darts", # forlorn temple "Demon King Crown", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset", # catacombs - "Necro", "Ruxxtin's Amulet", + "Necro", "Ruxxtin's Amulet", "Ruxxtin", "Catacombs Seal - Triple Spike Crushers", "Catacombs Seal - Crusher Gauntlet", "Catacombs Seal - Dirty Pond", # bamboo creek "Claustro", "Bamboo Creek Seal - Spike Crushers and Doors", "Bamboo Creek Seal - Spike Ball Pits", "Bamboo Creek Seal - Spike Crushers and Doors v2", # howling grotto - "Howling Grotto Seal - Crushing Pits", "Howling Grotto Seal - Crushing Pits", + "Emerald Golem", "Howling Grotto Seal - Crushing Pits", "Howling Grotto Seal - Crushing Pits", # glacial peak "Glacial Peak Seal - Ice Climbers", # cloud ruins @@ -41,7 +41,7 @@ class HardLogicTest(MessengerTestBase): # riviere turquoise "Fairy Bottle", "Riviere Turquoise Seal - Flower Power", # elemental skylands - "Elemental Skylands Seal - Air", "Elemental Skylands Seal - Water", "Elemental Skylands Seal - Fire", + "Elemental Skylands Seal - Air", # phantom "Rescue Phantom", ] diff --git a/worlds/messenger/test/TestShopChest.py b/worlds/messenger/test/TestShopChest.py index 9289ec99..fea49de8 100644 --- a/worlds/messenger/test/TestShopChest.py +++ b/worlds/messenger/test/TestShopChest.py @@ -77,3 +77,33 @@ class ThirtyThirtySeals(MessengerTestBase): required_seals = [seal for seal in total_seals if seal.classification == ItemClassification.progression_skip_balancing] self.assertEqual(len(total_seals), 30) self.assertEqual(len(required_seals), 10) + + +class MaxSealsNoShards(MessengerTestBase): + options = { + "goal": "power_seal_hunt", + "total_seals": 85, + } + + def testSealsAmount(self) -> None: + """Should set total seals to 57 since shards aren't shuffled.""" + self.assertEqual(self.multiworld.total_seals[self.player], 85) + self.assertEqual(self.multiworld.worlds[self.player].total_seals, 57) + + +class MaxSealsWithShards(MessengerTestBase): + options = { + "goal": "power_seal_hunt", + "total_seals": 85, + "shuffle_shards": "true", + } + + def testSealsAmount(self) -> None: + """Should have 85 seals in the pool with all required and be a valid seed.""" + self.assertEqual(self.multiworld.total_seals[self.player], 85) + self.assertEqual(self.multiworld.worlds[self.player].total_seals, 85) + self.assertEqual(self.multiworld.worlds[self.player].required_seals, 85) + total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"] + required_seals = [seal for seal in total_seals if seal.classification == ItemClassification.progression_skip_balancing] + self.assertEqual(len(total_seals), 85) + self.assertEqual(len(required_seals), 85)