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
This commit is contained in:
parent
c626618221
commit
eef8f7af1a
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue