mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
The Messenger: content update (#2823)
* map option objects to a `World.options` dict * convert RoR2 to options dict system for testing * add temp behavior for lttp with notes * copy/paste bad * convert `set_default_common_options` to a namespace property * reorganize test call order * have fill_restrictive use the new options system * update world api * update soe tests * fix world api * core: auto initialize a dataclass on the World class with the option results * core: auto initialize a dataclass on the World class with the option results: small tying improvement * add `as_dict` method to the options dataclass * fix namespace issues with tests * have current option updates use `.value` instead of changing the option * update ror2 to use the new options system again * revert the junk pool dict since it's cased differently * fix begin_with_loop typo * write new and old options to spoiler * change factorio option behavior back * fix comparisons * move common and per_game_common options to new system * core: automatically create missing options_dataclass from legacy option_definitions * remove spoiler special casing and add back the Factorio option changing but in new system * give ArchipIDLE the default options_dataclass so its options get generated and spoilered properly * reimplement `inspect.get_annotations` * move option info generation for webhost to new system * need to include Common and PerGame common since __annotations__ doesn't include super * use get_type_hints for the options dictionary * typing.get_type_hints returns the bases too. * forgot to sweep through generate * sweep through all the tests * swap to a metaclass property * move remaining usages from get_type_hints to metaclass property * move remaining usages from __annotations__ to metaclass property * move remaining usages from legacy dictionaries to metaclass property * remove legacy dictionaries * cache the metaclass property * clarify inheritance in world api * move the messenger to new options system * add an assert for my dumb * update the doc * rename o to options * missed a spot * update new messenger options * comment spacing Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> * fix tests * fix missing import * make the documentation definition more accurate * use options system for loc creation * type cast MessengerWorld * fix typo and use quotes for cast * LTTP: set random seed in tests * ArchipIdle: remove change here as it's default on AutoWorld * Stardew: Need to set state because `set_default_common_options` used to * The Messenger: update shop rando and helpers to new system; optimize imports * Add a kwarg to `as_dict` to do the casing for you * RoR2: use new kwarg for less code * RoR2: revert some accidental reverts * The Messenger: remove an unnecessary variable * remove TypeVar that isn't used * CommonOptions not abstract * Docs: fix mistake in options api.md Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> * create options for item link worlds * revert accidental doc removals * Item Links: set default options on group * Messenger: Limited Movement option first draft * The Messenger: add automated setup through the launcher * drop tomllib * don't uselessly import launcher * The Messenger: fix missing goal requirement for power seal hunt * make hard mode goal harder * make fire seal a bit more lenient * have limited movement force minimal accessibility * add an early meditation option * clean up precollected notes tests a bit * add linux support * add steam deck support * await monokickstart * minor styling cleanup * more minor styling cleanup * Initial implementation of Generic ER * Move ERType to Entrance.Type, fix typing imports * updates based on testing (read: flailing) * Updates from feedback * Various bug fixes in ERCollectionState * Use deque instead of queue.Queue * Allow partial entrances in collection state earlier, doc improvements * Prevent early loops in region graph, improve reusability of ER stage code * Typos, grammar, PEP8, and style "fixes" * use RuntimeError instead of bare Exceptions * return tuples from connect since it's slightly faster for our purposes * move the shuffle to the beginning of find_pairing * do er_state placements within pairing lookups to remove code duplication * requested adjustments * Add some temporary performance logging * Use CollectionState to track available exits and placed regions * remove seal shuffle option * some cleanup stuff * portal rando progress * pre-emptive region creation * seals need to be in the datapackage * put mega shards in old order * fix typos and make it actually work * fix more missed connections and add portal events * fix all the portal rando code * finish initial logic implementation * remove/comment out debug stuff * does not actually support plando yet * typos and fix a crash when 3 available portals was selected * finish initial logic for all connections and remove/rename as necessary * fix typos and add some more leniency * move item classification determination to its own method rather than split between two spots * super complicated solution for handling installing the alpha builds * fix logic bugs and add a test * implement logic to shuffle the cutscene portals even though it's probably not possible * just use the one list * fix some issues with the mod checking/downloading * Core: have webhost slot name links go through the launcher so that components can use them * add uri support to the launcher component function * generate output file under specific conditions * cleanup connections.py * set topology_present to true when portals are shuffled * add requirement for ghost pit loc since it's pretty hard without movement * bring hard logic back * misc cleanup * fix asset grabbing of latest version * implement ER * just use the entrances for the spoiler instead of manipulating the cache * remove test defaults * remove excessive comprehension * cleanup and cater data for the client * add elemental skylands to the shuffle pools * initial attempts at hint text * use network items for offline seeds * change around the offline seed data again * move er after portal shuffle and ensure a minimal sphere 1 * Add a method to automatically disconnect entrances in a coupled-compliant way Update docs and cleanup todos * Make find_placeable_exits deterministic by sorting blocked_connections set * add more ER transitions * fix spoiler output of portal warps * add path to hint_data * rename entrance to tot to be a bit clearer * cleanup imports and update description for hard logic * cleanup for PR to main * missed a spot * cleanup monokickstart * add location_name_groups * update docs for new setup * client can reconnect on its own now, no need for a button. * fix mod download link grabbing the wrong assets * cleanup mod pulling a bit and display version it's trying to update to * plando support * comment out broken steam deck support * supports plando * satisfy flake for currently unused file * fix the items accessibility test * review comments * add searing crags portal to starting portals when disabled like option says * address sliver comments * rip out currently unused transition shuffle * add aerobatics warrior requirement to fire seal --------- Co-authored-by: el-u <109771707+el-u@users.noreply.github.com> Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> Co-authored-by: Doug Hoskisson <beauxq@yahoo.com> Co-authored-by: Sean Dempsey <dempsey.sean@outlook.com> Co-authored-by: qwint <qwint.42@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from typing import Dict, TYPE_CHECKING
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.generic.Rules import add_rule, allow_self_locking_items, CollectionRule
|
||||
from worlds.generic.Rules import CollectionRule, add_rule, allow_self_locking_items
|
||||
from .constants import NOTES, PHOBEKINS
|
||||
from .options import MessengerAccessibility
|
||||
|
||||
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
|
||||
class MessengerRules:
|
||||
player: int
|
||||
world: "MessengerWorld"
|
||||
connection_rules: Dict[str, CollectionRule]
|
||||
region_rules: Dict[str, CollectionRule]
|
||||
location_rules: Dict[str, CollectionRule]
|
||||
maximum_price: int
|
||||
@@ -27,83 +28,286 @@ class MessengerRules:
|
||||
self.maximum_price = min(maximum_price, world.total_shards)
|
||||
self.required_seals = max(1, world.required_seals)
|
||||
|
||||
self.region_rules = {
|
||||
"Ninja Village": self.has_wingsuit,
|
||||
"Autumn Hills": self.has_wingsuit,
|
||||
"Catacombs": self.has_wingsuit,
|
||||
"Bamboo Creek": self.has_wingsuit,
|
||||
"Searing Crags Upper": self.has_vertical,
|
||||
"Cloud Ruins": lambda state: self.has_vertical(state) and state.has("Ruxxtin's Amulet", self.player),
|
||||
"Cloud Ruins Right": lambda state: self.has_wingsuit(state) and
|
||||
(self.has_dart(state) or self.can_dboost(state)),
|
||||
"Underworld": self.has_tabi,
|
||||
"Riviere Turquoise": lambda state: self.has_dart(state) or
|
||||
(self.has_wingsuit(state) and self.can_destroy_projectiles(state)),
|
||||
"Forlorn Temple": lambda state: state.has_all({"Wingsuit", *PHOBEKINS}, self.player) and self.can_dboost(state),
|
||||
"Glacial Peak": self.has_vertical,
|
||||
"Elemental Skylands": lambda state: state.has("Magic Firefly", self.player) and self.has_wingsuit(state),
|
||||
"Music Box": lambda state: (state.has_all(NOTES, self.player)
|
||||
or self.has_enough_seals(state)) and self.has_dart(state),
|
||||
"The Craftsman's Corner": lambda state: state.has("Money Wrench", self.player) and self.can_shop(state),
|
||||
# dict of connection names and requirements to traverse the exit
|
||||
self.connection_rules = {
|
||||
# from ToTHQ
|
||||
"Artificer's Portal":
|
||||
lambda state: state.has_all({"Demon King Crown", "Magic Firefly"}, self.player),
|
||||
"Shrink Down":
|
||||
lambda state: state.has_all(NOTES, self.player) or self.has_enough_seals(state),
|
||||
# the shop
|
||||
"Money Sink":
|
||||
lambda state: state.has("Money Wrench", self.player) and self.can_shop(state),
|
||||
# Autumn Hills
|
||||
"Autumn Hills - Portal -> Autumn Hills - Dimension Climb Shop":
|
||||
lambda state: self.has_wingsuit(state) and self.has_dart(state),
|
||||
"Autumn Hills - Dimension Climb Shop -> Autumn Hills - Portal":
|
||||
self.has_vertical,
|
||||
"Autumn Hills - Climbing Claws Shop -> Autumn Hills - Hope Path Shop":
|
||||
self.has_dart,
|
||||
"Autumn Hills - Climbing Claws Shop -> Autumn Hills - Key of Hope Checkpoint":
|
||||
self.false, # hard logic only
|
||||
"Autumn Hills - Hope Path Shop -> Autumn Hills - Hope Latch Checkpoint":
|
||||
self.has_dart,
|
||||
"Autumn Hills - Hope Path Shop -> Autumn Hills - Climbing Claws Shop":
|
||||
lambda state: self.has_dart(state) and self.can_dboost(state),
|
||||
"Autumn Hills - Hope Path Shop -> Autumn Hills - Lakeside Checkpoint":
|
||||
lambda state: self.has_dart(state) and self.can_dboost(state),
|
||||
"Autumn Hills - Hope Latch Checkpoint -> Autumn Hills - Hope Path Shop":
|
||||
self.can_dboost,
|
||||
"Autumn Hills - Hope Latch Checkpoint -> Autumn Hills - Key of Hope Checkpoint":
|
||||
lambda state: self.has_dart(state) and self.has_wingsuit(state),
|
||||
# Forlorn Temple
|
||||
"Forlorn Temple - Outside Shop -> Forlorn Temple - Entrance Shop":
|
||||
lambda state: state.has_all(PHOBEKINS, self.player),
|
||||
"Forlorn Temple - Entrance Shop -> Forlorn Temple - Outside Shop":
|
||||
lambda state: state.has_all(PHOBEKINS, self.player),
|
||||
"Forlorn Temple - Entrance Shop -> Forlorn Temple - Sunny Day Checkpoint":
|
||||
lambda state: self.has_vertical(state) and self.can_dboost(state),
|
||||
"Forlorn Temple - Sunny Day Checkpoint -> Forlorn Temple - Rocket Maze Checkpoint":
|
||||
self.has_vertical,
|
||||
"Forlorn Temple - Rocket Sunset Shop -> Forlorn Temple - Descent Shop":
|
||||
lambda state: self.has_dart(state) and (self.can_dboost(state) or self.has_wingsuit(state)),
|
||||
"Forlorn Temple - Saw Gauntlet Shop -> Forlorn Temple - Demon King Shop":
|
||||
self.has_vertical,
|
||||
"Forlorn Temple - Demon King Shop -> Forlorn Temple - Saw Gauntlet Shop":
|
||||
self.has_vertical,
|
||||
# Howling Grotto
|
||||
"Howling Grotto - Portal -> Howling Grotto - Crushing Pits Shop":
|
||||
self.has_wingsuit,
|
||||
"Howling Grotto - Wingsuit Shop -> Howling Grotto - Left":
|
||||
self.has_wingsuit,
|
||||
"Howling Grotto - Wingsuit Shop -> Howling Grotto - Lost Woods Checkpoint":
|
||||
self.has_wingsuit,
|
||||
"Howling Grotto - Lost Woods Checkpoint -> Howling Grotto - Bottom":
|
||||
lambda state: state.has("Seashell", self.player),
|
||||
"Howling Grotto - Crushing Pits Shop -> Howling Grotto - Portal":
|
||||
lambda state: self.has_wingsuit(state) or self.can_dboost(state),
|
||||
"Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Emerald Golem Shop":
|
||||
self.has_wingsuit,
|
||||
"Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Crushing Pits Shop":
|
||||
lambda state: (self.has_wingsuit(state) or self.can_dboost(
|
||||
state
|
||||
) or self.can_destroy_projectiles(state))
|
||||
and state.multiworld.get_region(
|
||||
"Howling Grotto - Emerald Golem Shop", self.player
|
||||
).can_reach(state),
|
||||
"Howling Grotto - Emerald Golem Shop -> Howling Grotto - Right":
|
||||
self.has_wingsuit,
|
||||
# Searing Crags
|
||||
"Searing Crags - Rope Dart Shop -> Searing Crags - Triple Ball Spinner Checkpoint":
|
||||
self.has_vertical,
|
||||
"Searing Crags - Portal -> Searing Crags - Right":
|
||||
self.has_tabi,
|
||||
"Searing Crags - Portal -> Searing Crags - Before Final Climb Shop":
|
||||
self.has_wingsuit,
|
||||
"Searing Crags - Portal -> Searing Crags - Colossuses Shop":
|
||||
self.has_wingsuit,
|
||||
"Searing Crags - Bottom -> Searing Crags - Portal":
|
||||
self.has_wingsuit,
|
||||
"Searing Crags - Right -> Searing Crags - Portal":
|
||||
lambda state: self.has_tabi(state) and self.has_wingsuit(state),
|
||||
"Searing Crags - Colossuses Shop -> Searing Crags - Key of Strength Shop":
|
||||
lambda state: state.has("Power Thistle", self.player)
|
||||
and (self.has_dart(state)
|
||||
or (self.has_wingsuit(state)
|
||||
and self.can_destroy_projectiles(state))),
|
||||
"Searing Crags - Falling Rocks Shop -> Searing Crags - Searing Mega Shard Shop":
|
||||
self.has_dart,
|
||||
"Searing Crags - Searing Mega Shard Shop -> Searing Crags - Before Final Climb Shop":
|
||||
lambda state: self.has_dart(state) or self.can_destroy_projectiles(state),
|
||||
"Searing Crags - Searing Mega Shard Shop -> Searing Crags - Falling Rocks Shop":
|
||||
self.has_dart,
|
||||
"Searing Crags - Searing Mega Shard Shop -> Searing Crags - Key of Strength Shop":
|
||||
self.false,
|
||||
"Searing Crags - Before Final Climb Shop -> Searing Crags - Colossuses Shop":
|
||||
self.has_dart,
|
||||
# Glacial Peak
|
||||
"Glacial Peak - Portal -> Glacial Peak - Tower Entrance Shop":
|
||||
self.has_vertical,
|
||||
"Glacial Peak - Left -> Elemental Skylands - Air Shmup":
|
||||
lambda state: state.has("Magic Firefly", self.player)
|
||||
and state.multiworld.get_location("Quillshroom Marsh - Queen of Quills", self.player)
|
||||
.can_reach(state),
|
||||
"Glacial Peak - Tower Entrance Shop -> Glacial Peak - Top":
|
||||
lambda state: state.has("Ruxxtin's Amulet", self.player),
|
||||
"Glacial Peak - Projectile Spike Pit Checkpoint -> Glacial Peak - Left":
|
||||
lambda state: self.has_dart(state) or (self.can_dboost(state) and self.has_wingsuit(state)),
|
||||
# Tower of Time
|
||||
"Tower of Time - Left -> Tower of Time - Final Chance Shop":
|
||||
self.has_dart,
|
||||
"Tower of Time - Second Checkpoint -> Tower of Time - Third Checkpoint":
|
||||
lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
|
||||
"Tower of Time - Third Checkpoint -> Tower of Time - Fourth Checkpoint":
|
||||
lambda state: self.has_wingsuit(state) or self.can_dboost(state),
|
||||
"Tower of Time - Fourth Checkpoint -> Tower of Time - Fifth Checkpoint":
|
||||
lambda state: self.has_wingsuit(state) and self.has_dart(state),
|
||||
"Tower of Time - Fifth Checkpoint -> Tower of Time - Sixth Checkpoint":
|
||||
self.has_wingsuit,
|
||||
# Cloud Ruins
|
||||
"Cloud Ruins - Cloud Entrance Shop -> Cloud Ruins - Spike Float Checkpoint":
|
||||
self.has_wingsuit,
|
||||
"Cloud Ruins - Spike Float Checkpoint -> Cloud Ruins - Cloud Entrance Shop":
|
||||
lambda state: self.has_vertical(state) or self.can_dboost(state),
|
||||
"Cloud Ruins - Spike Float Checkpoint -> Cloud Ruins - Pillar Glide Shop":
|
||||
lambda state: self.has_vertical(state) or self.can_dboost(state),
|
||||
"Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Spike Float Checkpoint":
|
||||
lambda state: self.has_vertical(state) and self.can_double_dboost(state),
|
||||
"Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Ghost Pit Checkpoint":
|
||||
lambda state: self.has_dart(state) and self.has_wingsuit(state),
|
||||
"Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Crushers' Descent Shop":
|
||||
lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
|
||||
"Cloud Ruins - Toothbrush Alley Checkpoint -> Cloud Ruins - Seeing Spikes Shop":
|
||||
self.has_vertical,
|
||||
"Cloud Ruins - Seeing Spikes Shop -> Cloud Ruins - Sliding Spikes Shop":
|
||||
self.has_wingsuit,
|
||||
"Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Seeing Spikes Shop":
|
||||
self.has_wingsuit,
|
||||
"Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Saw Pit Checkpoint":
|
||||
self.has_vertical,
|
||||
"Cloud Ruins - Final Flight Shop -> Cloud Ruins - Manfred's Shop":
|
||||
lambda state: self.has_wingsuit(state) and self.has_dart(state),
|
||||
"Cloud Ruins - Manfred's Shop -> Cloud Ruins - Final Flight Shop":
|
||||
lambda state: self.has_wingsuit(state) and self.can_dboost(state),
|
||||
# Underworld
|
||||
"Underworld - Left -> Underworld - Left Shop":
|
||||
self.has_tabi,
|
||||
"Underworld - Left Shop -> Underworld - Left":
|
||||
self.has_tabi,
|
||||
"Underworld - Hot Dip Checkpoint -> Underworld - Lava Run Checkpoint":
|
||||
self.has_tabi,
|
||||
"Underworld - Fireball Wave Shop -> Underworld - Long Climb Shop":
|
||||
lambda state: self.can_destroy_projectiles(state) or self.has_tabi(state) or self.has_vertical(state),
|
||||
"Underworld - Long Climb Shop -> Underworld - Hot Tub Checkpoint":
|
||||
lambda state: self.has_tabi(state)
|
||||
and (self.can_destroy_projectiles(state)
|
||||
or self.has_wingsuit(state))
|
||||
or (self.has_wingsuit(state)
|
||||
and (self.has_dart(state)
|
||||
or self.can_dboost(state)
|
||||
or self.can_destroy_projectiles(state))),
|
||||
"Underworld - Hot Tub Checkpoint -> Underworld - Long Climb Shop":
|
||||
lambda state: self.has_tabi(state)
|
||||
or self.can_destroy_projectiles(state)
|
||||
or (self.has_dart(state) and self.has_wingsuit(state)),
|
||||
# Dark Cave
|
||||
"Dark Cave - Right -> Dark Cave - Left":
|
||||
lambda state: state.has("Candle", self.player) and self.has_dart(state),
|
||||
# Riviere Turquoise
|
||||
"Riviere Turquoise - Waterfall Shop -> Riviere Turquoise - Flower Flight Checkpoint":
|
||||
lambda state: self.has_dart(state) or (
|
||||
self.has_wingsuit(state) and self.can_destroy_projectiles(state)),
|
||||
"Riviere Turquoise - Launch of Faith Shop -> Riviere Turquoise - Flower Flight Checkpoint":
|
||||
lambda state: self.has_dart(state) and self.can_dboost(state),
|
||||
"Riviere Turquoise - Flower Flight Checkpoint -> Riviere Turquoise - Waterfall Shop":
|
||||
lambda state: False,
|
||||
# Elemental Skylands
|
||||
"Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Seal Checkpoint":
|
||||
self.has_wingsuit,
|
||||
"Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Generator Shop":
|
||||
self.has_wingsuit,
|
||||
# Sunken Shrine
|
||||
"Sunken Shrine - Portal -> Sunken Shrine - Sun Path Shop":
|
||||
self.has_tabi,
|
||||
"Sunken Shrine - Portal -> Sunken Shrine - Moon Path Shop":
|
||||
self.has_tabi,
|
||||
"Sunken Shrine - Moon Path Shop -> Sunken Shrine - Waterfall Paradise Checkpoint":
|
||||
self.has_tabi,
|
||||
"Sunken Shrine - Waterfall Paradise Checkpoint -> Sunken Shrine - Moon Path Shop":
|
||||
self.has_tabi,
|
||||
"Sunken Shrine - Tabi Gauntlet Shop -> Sunken Shrine - Sun Path Shop":
|
||||
lambda state: self.can_dboost(state) or self.has_dart(state),
|
||||
}
|
||||
|
||||
self.location_rules = {
|
||||
# ninja village
|
||||
"Ninja Village Seal - Tree House": self.has_dart,
|
||||
"Ninja Village Seal - Tree House":
|
||||
self.has_dart,
|
||||
"Ninja Village - Candle":
|
||||
lambda state: state.multiworld.get_location("Searing Crags - Astral Tea Leaves", self.player).can_reach(
|
||||
state),
|
||||
# autumn hills
|
||||
"Autumn Hills - Key of Hope": self.has_dart,
|
||||
"Autumn Hills Seal - Spike Ball Darts": self.is_aerobatic,
|
||||
"Autumn Hills Seal - Spike Ball Darts":
|
||||
self.is_aerobatic,
|
||||
"Autumn Hills Seal - Trip Saws":
|
||||
self.has_wingsuit,
|
||||
# forlorn temple
|
||||
"Forlorn Temple Seal - Rocket Maze":
|
||||
self.has_vertical,
|
||||
# bamboo creek
|
||||
"Bamboo Creek - Claustro": lambda state: self.has_dart(state) or self.can_dboost(state),
|
||||
"Bamboo Creek - Claustro":
|
||||
lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
|
||||
"Above Entrance Mega Shard":
|
||||
lambda state: self.has_dart(state) or self.can_dboost(state),
|
||||
"Bamboo Creek Seal - Spike Ball Pits":
|
||||
self.has_wingsuit,
|
||||
# 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),
|
||||
"Howling Grotto - Emerald Golem": self.has_wingsuit,
|
||||
"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),
|
||||
"Howling Grotto - Emerald Golem":
|
||||
self.has_wingsuit,
|
||||
# searing crags
|
||||
"Searing Crags Seal - Triple Ball Spinner": self.has_vertical,
|
||||
"Searing Crags - Astral Tea Leaves":
|
||||
lambda state: state.can_reach("Ninja Village - Astral Seed", "Location", self.player),
|
||||
"Searing Crags - Key of Strength": lambda state: state.has("Power Thistle", self.player)
|
||||
and (self.has_dart(state)
|
||||
or (self.has_wingsuit(state)
|
||||
and self.can_destroy_projectiles(state))),
|
||||
lambda state: state.multiworld.get_location("Ninja Village - Astral Seed", self.player).can_reach(state),
|
||||
"Searing Crags Seal - Triple Ball Spinner":
|
||||
self.can_dboost,
|
||||
"Searing Crags - Pyro":
|
||||
self.has_tabi,
|
||||
# glacial peak
|
||||
"Glacial Peak Seal - Ice Climbers": self.has_dart,
|
||||
"Glacial Peak Seal - Projectile Spike Pit": self.can_destroy_projectiles,
|
||||
# cloud ruins
|
||||
"Cloud Ruins Seal - Ghost Pit": self.has_dart,
|
||||
"Glacial Peak Seal - Ice Climbers":
|
||||
self.has_dart,
|
||||
"Glacial Peak Seal - Projectile Spike Pit":
|
||||
self.can_destroy_projectiles,
|
||||
# tower of time
|
||||
"Tower of Time Seal - Time Waster": self.has_dart,
|
||||
"Tower of Time Seal - Lantern Climb": lambda state: self.has_wingsuit(state) and self.has_dart(state),
|
||||
"Tower of Time Seal - Arcane Orbs": lambda state: self.has_wingsuit(state) and self.has_dart(state),
|
||||
"Tower of Time Seal - Time Waster":
|
||||
self.has_dart,
|
||||
# cloud ruins
|
||||
"Time Warp Mega Shard":
|
||||
lambda state: self.has_vertical(state) or self.can_dboost(state),
|
||||
"Cloud Ruins Seal - Ghost Pit":
|
||||
self.has_vertical,
|
||||
"Cloud Ruins Seal - Toothbrush Alley":
|
||||
self.has_dart,
|
||||
"Cloud Ruins Seal - Saw Pit":
|
||||
self.has_vertical,
|
||||
# underworld
|
||||
"Underworld Seal - Sharp and Windy Climb": self.has_wingsuit,
|
||||
"Underworld Seal - Fireball Wave": self.is_aerobatic,
|
||||
"Underworld Seal - Rising Fanta": self.has_dart,
|
||||
"Underworld Seal - Sharp and Windy Climb":
|
||||
self.has_wingsuit,
|
||||
"Underworld Seal - Fireball Wave":
|
||||
self.is_aerobatic,
|
||||
"Underworld Seal - Rising Fanta":
|
||||
self.has_dart,
|
||||
"Hot Tub Mega Shard":
|
||||
lambda state: self.has_tabi(state) or self.has_dart(state),
|
||||
# sunken shrine
|
||||
"Sunken Shrine - Sun Crest": self.has_tabi,
|
||||
"Sunken Shrine - Moon Crest": self.has_tabi,
|
||||
"Sunken Shrine - 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,
|
||||
"Sunken Shrine - 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 Sun":
|
||||
self.has_tabi,
|
||||
# riviere turquoise
|
||||
"Riviere Turquoise Seal - Bounces and Balls": self.can_dboost,
|
||||
"Riviere Turquoise Seal - Launch of Faith": lambda state: self.can_dboost(state) or self.has_dart(state),
|
||||
"Riviere Turquoise Seal - Bounces and Balls":
|
||||
self.can_dboost,
|
||||
"Riviere Turquoise Seal - Launch of Faith":
|
||||
lambda state: self.has_vertical(state),
|
||||
# elemental skylands
|
||||
"Elemental Skylands - Key of Symbiosis": self.has_dart,
|
||||
"Elemental Skylands Seal - Air": self.has_wingsuit,
|
||||
"Elemental Skylands Seal - Water": lambda state: self.has_dart(state) and
|
||||
state.has("Currents Master", self.player),
|
||||
"Elemental Skylands Seal - Fire": lambda state: self.has_dart(state) and self.can_destroy_projectiles(state),
|
||||
"Earth Mega Shard": self.has_dart,
|
||||
"Water Mega Shard": self.has_dart,
|
||||
# corrupted future
|
||||
"Corrupted Future - Key of Courage": lambda state: state.has_all({"Demon King Crown", "Magic Firefly"},
|
||||
self.player),
|
||||
# tower hq
|
||||
"Money Wrench": self.can_shop,
|
||||
"Elemental Skylands - Key of Symbiosis":
|
||||
self.has_dart,
|
||||
"Elemental Skylands Seal - Air":
|
||||
self.has_wingsuit,
|
||||
"Elemental Skylands Seal - Water":
|
||||
lambda state: self.has_dart(state) and state.has("Currents Master", self.player),
|
||||
"Elemental Skylands Seal - Fire":
|
||||
lambda state: self.has_dart(state) and self.can_destroy_projectiles(state) and self.is_aerobatic(state),
|
||||
"Earth Mega Shard":
|
||||
self.has_dart,
|
||||
"Water Mega Shard":
|
||||
self.has_dart,
|
||||
}
|
||||
|
||||
def has_wingsuit(self, state: CollectionState) -> bool:
|
||||
@@ -128,6 +332,9 @@ class MessengerRules:
|
||||
return state.has_any({"Path of Resilience", "Meditation"}, self.player) and \
|
||||
state.has("Second Wind", self.player)
|
||||
|
||||
def can_double_dboost(self, state: CollectionState) -> bool:
|
||||
return state.has_all({"Path of Resilience", "Meditation", "Second Wind"}, self.player)
|
||||
|
||||
def is_aerobatic(self, state: CollectionState) -> bool:
|
||||
return self.has_wingsuit(state) and state.has("Aerobatics Warrior", self.player)
|
||||
|
||||
@@ -135,87 +342,147 @@ class MessengerRules:
|
||||
"""I know this is stupid, but it's easier to read in the dicts."""
|
||||
return True
|
||||
|
||||
def false(self, state: CollectionState) -> bool:
|
||||
"""It's a bit easier to just always create the connections that are only possible in hard or higher logic."""
|
||||
return False
|
||||
|
||||
def can_shop(self, state: CollectionState) -> bool:
|
||||
return state.has("Shards", self.player, self.maximum_price)
|
||||
|
||||
def set_messenger_rules(self) -> None:
|
||||
multiworld = self.world.multiworld
|
||||
|
||||
for region in multiworld.get_regions(self.player):
|
||||
if region.name in self.region_rules:
|
||||
for entrance in region.entrances:
|
||||
entrance.access_rule = self.region_rules[region.name]
|
||||
for loc in region.locations:
|
||||
if loc.name in self.location_rules:
|
||||
loc.access_rule = self.location_rules[loc.name]
|
||||
for entrance_name, rule in self.connection_rules.items():
|
||||
entrance = multiworld.get_entrance(entrance_name, self.player)
|
||||
entrance.access_rule = rule
|
||||
for loc in multiworld.get_locations(self.player):
|
||||
if loc.name in self.location_rules:
|
||||
loc.access_rule = self.location_rules[loc.name]
|
||||
|
||||
multiworld.completion_condition[self.player] = lambda state: state.has("Rescue Phantom", self.player)
|
||||
if multiworld.accessibility[self.player]: # not locations accessibility
|
||||
if self.world.options.music_box and not self.world.options.limited_movement:
|
||||
add_rule(multiworld.get_entrance("Shrink Down", self.player), self.has_dart)
|
||||
multiworld.completion_condition[self.player] = lambda state: state.has("Do the Thing!", self.player)
|
||||
if self.world.options.accessibility: # not locations accessibility
|
||||
set_self_locking_items(self.world, self.player)
|
||||
|
||||
|
||||
class MessengerHardRules(MessengerRules):
|
||||
extra_rules: Dict[str, CollectionRule]
|
||||
|
||||
def __init__(self, world: "MessengerWorld") -> None:
|
||||
super().__init__(world)
|
||||
|
||||
self.region_rules.update({
|
||||
"Ninja Village": self.has_vertical,
|
||||
"Autumn Hills": self.has_vertical,
|
||||
"Catacombs": self.has_vertical,
|
||||
"Bamboo Creek": self.has_vertical,
|
||||
"Riviere Turquoise": self.true,
|
||||
"Forlorn Temple": lambda state: self.has_vertical(state) and state.has_all(PHOBEKINS, self.player),
|
||||
"Searing Crags Upper": lambda state: self.can_destroy_projectiles(state) or self.has_windmill(state)
|
||||
or self.has_vertical(state),
|
||||
"Glacial Peak": lambda state: self.can_destroy_projectiles(state) or self.has_windmill(state)
|
||||
or self.has_vertical(state),
|
||||
"Elemental Skylands": lambda state: state.has("Magic Firefly", self.player) or
|
||||
self.has_windmill(state) or
|
||||
self.has_dart(state),
|
||||
})
|
||||
self.connection_rules.update(
|
||||
{
|
||||
# Autumn Hills
|
||||
"Autumn Hills - Portal -> Autumn Hills - Dimension Climb Shop":
|
||||
self.has_dart,
|
||||
"Autumn Hills - Climbing Claws Shop -> Autumn Hills - Key of Hope Checkpoint":
|
||||
self.true, # super easy normal clip - also possible with moderately difficult cloud stepping
|
||||
# Howling Grotto
|
||||
"Howling Grotto - Portal -> Howling Grotto - Crushing Pits Shop":
|
||||
self.true,
|
||||
"Howling Grotto - Lost Woods Checkpoint -> Howling Grotto - Bottom":
|
||||
self.true, # just memorize the pattern :)
|
||||
"Howling Grotto - Crushing Pits Shop -> Howling Grotto - Portal":
|
||||
self.true,
|
||||
"Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Emerald Golem Shop":
|
||||
lambda state: self.has_wingsuit(state) or # there's a very easy normal clip here but it's 16-bit only
|
||||
"Howling Grotto - Breezy Crushers Checkpoint" in self.world.spoiler_portal_mapping.values(),
|
||||
# Searing Crags
|
||||
"Searing Crags - Rope Dart Shop -> Searing Crags - Triple Ball Spinner Checkpoint":
|
||||
lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
|
||||
# it's doable without anything but one jump is pretty hard and time warping is no longer reliable
|
||||
"Searing Crags - Falling Rocks Shop -> Searing Crags - Searing Mega Shard Shop":
|
||||
lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
|
||||
"Searing Crags - Searing Mega Shard Shop -> Searing Crags - Falling Rocks Shop":
|
||||
lambda state: self.has_dart(state) or
|
||||
(self.can_destroy_projectiles(state) and
|
||||
(self.has_wingsuit(state) or self.can_dboost(state))),
|
||||
"Searing Crags - Searing Mega Shard Shop -> Searing Crags - Key of Strength Shop":
|
||||
lambda state: self.can_leash(state) or self.has_windmill(state),
|
||||
"Searing Crags - Before Final Climb Shop -> Searing Crags - Colossuses Shop":
|
||||
self.true,
|
||||
# Glacial Peak
|
||||
"Glacial Peak - Left -> Elemental Skylands - Air Shmup":
|
||||
lambda state: self.has_windmill(state) or
|
||||
(state.has("Magic Firefly", self.player) and
|
||||
state.multiworld.get_location(
|
||||
"Quillshroom Marsh - Queen of Quills", self.player).can_reach(state)) or
|
||||
(self.has_dart(state) and self.can_dboost(state)),
|
||||
"Glacial Peak - Projectile Spike Pit Checkpoint -> Glacial Peak - Left":
|
||||
lambda state: self.has_vertical(state) or self.has_windmill(state),
|
||||
# Cloud Ruins
|
||||
"Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Saw Pit Checkpoint":
|
||||
self.true,
|
||||
# Elemental Skylands
|
||||
"Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Generator Shop":
|
||||
self.true,
|
||||
# Riviere Turquoise
|
||||
"Riviere Turquoise - Waterfall Shop -> Riviere Turquoise - Flower Flight Checkpoint":
|
||||
self.true,
|
||||
"Riviere Turquoise - Launch of Faith Shop -> Riviere Turquoise - Flower Flight Checkpoint":
|
||||
self.can_dboost,
|
||||
"Riviere Turquoise - Flower Flight Checkpoint -> Riviere Turquoise - Waterfall Shop":
|
||||
self.can_double_dboost,
|
||||
}
|
||||
)
|
||||
|
||||
self.location_rules.update({
|
||||
"Howling Grotto Seal - Windy Saws and Balls": self.true,
|
||||
"Searing Crags Seal - Triple Ball Spinner": self.true,
|
||||
"Searing Crags Seal - Raining Rocks": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
|
||||
"Searing Crags Seal - Rhythm Rocks": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
|
||||
"Searing Crags - Power Thistle": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
|
||||
"Glacial Peak Seal - Ice Climbers": lambda state: self.has_vertical(state) or self.can_dboost(state),
|
||||
"Glacial Peak Seal - Projectile Spike Pit": self.true,
|
||||
"Glacial Peak Seal - Glacial Air Swag": lambda state: self.has_windmill(state) or self.has_vertical(state),
|
||||
"Glacial Peak Mega Shard": lambda state: self.has_windmill(state) or self.has_vertical(state),
|
||||
"Cloud Ruins Seal - Ghost Pit": self.true,
|
||||
"Bamboo Creek - Claustro": self.has_wingsuit,
|
||||
"Tower of Time Seal - Lantern Climb": self.has_wingsuit,
|
||||
"Elemental Skylands Seal - Water": lambda state: self.has_dart(state) or self.can_dboost(state)
|
||||
or self.has_windmill(state),
|
||||
"Elemental Skylands Seal - Fire": lambda state: (self.has_dart(state) or self.can_dboost(state)
|
||||
or self.has_windmill(state)) and
|
||||
self.can_destroy_projectiles(state),
|
||||
"Earth Mega Shard": lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
"Water Mega Shard": lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
})
|
||||
|
||||
self.extra_rules = {
|
||||
"Searing Crags - Key of Strength": lambda state: self.has_dart(state) or self.has_windmill(state),
|
||||
"Elemental Skylands - Key of Symbiosis": lambda state: self.has_windmill(state) or self.can_dboost(state),
|
||||
"Autumn Hills Seal - Spike Ball Darts": lambda state: self.has_dart(state) or self.has_windmill(state),
|
||||
"Underworld Seal - Fireball Wave": self.has_windmill,
|
||||
}
|
||||
self.location_rules.update(
|
||||
{
|
||||
"Autumn Hills Seal - Spike Ball Darts":
|
||||
lambda state: self.has_vertical(state) and self.has_windmill(state) or self.is_aerobatic(state),
|
||||
"Bamboo Creek - Claustro":
|
||||
self.has_wingsuit,
|
||||
"Bamboo Creek Seal - Spike Ball Pits":
|
||||
self.true,
|
||||
"Howling Grotto Seal - Windy Saws and Balls":
|
||||
self.true,
|
||||
"Searing Crags Seal - Triple Ball Spinner":
|
||||
self.true,
|
||||
"Glacial Peak Seal - Ice Climbers":
|
||||
lambda state: self.has_vertical(state) or self.can_dboost(state),
|
||||
"Glacial Peak Seal - Projectile Spike Pit":
|
||||
lambda state: self.can_dboost(state) or self.can_destroy_projectiles(state),
|
||||
"Glacial Peak Seal - Glacial Air Swag":
|
||||
lambda state: self.has_windmill(state) or self.has_vertical(state),
|
||||
"Glacial Peak Mega Shard":
|
||||
lambda state: self.has_windmill(state) or self.has_vertical(state),
|
||||
"Cloud Ruins Seal - Ghost Pit":
|
||||
self.true,
|
||||
"Cloud Ruins Seal - Toothbrush Alley":
|
||||
self.true,
|
||||
"Cloud Ruins Seal - Saw Pit":
|
||||
self.true,
|
||||
"Underworld Seal - Fireball Wave":
|
||||
lambda state: self.is_aerobatic(state) or self.has_windmill(state),
|
||||
"Riviere Turquoise Seal - Bounces and Balls":
|
||||
self.true,
|
||||
"Riviere Turquoise Seal - Launch of Faith":
|
||||
lambda state: self.can_dboost(state) or self.has_vertical(state),
|
||||
"Elemental Skylands - Key of Symbiosis":
|
||||
lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
"Elemental Skylands Seal - Water":
|
||||
lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
"Elemental Skylands Seal - Fire":
|
||||
lambda state: (self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state))
|
||||
and self.can_destroy_projectiles(state),
|
||||
"Earth Mega Shard":
|
||||
lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
"Water Mega Shard":
|
||||
lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
|
||||
}
|
||||
)
|
||||
|
||||
def has_windmill(self, state: CollectionState) -> bool:
|
||||
return state.has("Windmill Shuriken", self.player)
|
||||
|
||||
def set_messenger_rules(self) -> None:
|
||||
super().set_messenger_rules()
|
||||
for loc, rule in self.extra_rules.items():
|
||||
if not self.world.options.shuffle_seals and "Seal" in loc:
|
||||
continue
|
||||
if not self.world.options.shuffle_shards and "Shard" in loc:
|
||||
continue
|
||||
add_rule(self.world.multiworld.get_location(loc, self.player), rule, "or")
|
||||
def can_dboost(self, state: CollectionState) -> bool:
|
||||
return state.has("Second Wind", self.player) # who really needs meditation
|
||||
|
||||
def can_destroy_projectiles(self, state: CollectionState) -> bool:
|
||||
return super().can_destroy_projectiles(state) or self.has_windmill(state)
|
||||
|
||||
def can_leash(self, state: CollectionState) -> bool:
|
||||
return self.has_dart(state) and self.can_dboost(state)
|
||||
|
||||
|
||||
class MessengerOOBRules(MessengerRules):
|
||||
@@ -226,7 +493,9 @@ class MessengerOOBRules(MessengerRules):
|
||||
self.required_seals = max(1, world.required_seals)
|
||||
self.region_rules = {
|
||||
"Elemental Skylands":
|
||||
lambda state: state.has_any({"Windmill Shuriken", "Wingsuit", "Rope Dart", "Magic Firefly"}, self.player),
|
||||
lambda state: state.has_any(
|
||||
{"Windmill Shuriken", "Wingsuit", "Rope Dart", "Magic Firefly"}, self.player
|
||||
),
|
||||
"Music Box": lambda state: state.has_all(set(NOTES), self.player) or self.has_enough_seals(state),
|
||||
}
|
||||
|
||||
@@ -240,8 +509,10 @@ class MessengerOOBRules(MessengerRules):
|
||||
lambda state: state.has_all({"Demon King Crown", "Magic Firefly"}, self.player),
|
||||
"Autumn Hills Seal - Spike Ball Darts": self.has_dart,
|
||||
"Ninja Village Seal - Tree House": self.has_dart,
|
||||
"Underworld Seal - Fireball Wave": lambda state: state.has_any({"Wingsuit", "Windmill Shuriken"},
|
||||
self.player),
|
||||
"Underworld Seal - Fireball Wave": lambda state: state.has_any(
|
||||
{"Wingsuit", "Windmill Shuriken"},
|
||||
self.player
|
||||
),
|
||||
"Tower of Time Seal - Time Waster": self.has_dart,
|
||||
}
|
||||
|
||||
@@ -251,18 +522,8 @@ class MessengerOOBRules(MessengerRules):
|
||||
|
||||
|
||||
def set_self_locking_items(world: "MessengerWorld", player: int) -> None:
|
||||
multiworld = world.multiworld
|
||||
|
||||
# do the ones for seal shuffle on and off first
|
||||
allow_self_locking_items(multiworld.get_location("Searing Crags - Key of Strength", player), "Power Thistle")
|
||||
allow_self_locking_items(multiworld.get_location("Sunken Shrine - Key of Love", player), "Sun Crest", "Moon Crest")
|
||||
allow_self_locking_items(multiworld.get_location("Corrupted Future - Key of Courage", player), "Demon King Crown")
|
||||
|
||||
# add these locations when seals are shuffled
|
||||
if world.options.shuffle_seals:
|
||||
allow_self_locking_items(multiworld.get_location("Elemental Skylands Seal - Water", player), "Currents Master")
|
||||
# add these locations when seals and shards aren't shuffled
|
||||
elif not world.options.shuffle_shards:
|
||||
for entrance in multiworld.get_region("Cloud Ruins", player).entrances:
|
||||
entrance.access_rule = lambda state: state.has("Wingsuit", player) or state.has("Rope Dart", player)
|
||||
allow_self_locking_items(multiworld.get_region("Forlorn Temple", player), *PHOBEKINS)
|
||||
# locations where these placements are always valid
|
||||
allow_self_locking_items(world.get_location("Searing Crags - Key of Strength"), "Power Thistle")
|
||||
allow_self_locking_items(world.get_location("Sunken Shrine - Key of Love"), "Sun Crest", "Moon Crest")
|
||||
allow_self_locking_items(world.get_location("Corrupted Future - Key of Courage"), "Demon King Crown")
|
||||
allow_self_locking_items(world.get_location("Elemental Skylands Seal - Water"), "Currents Master")
|
||||
|
||||
Reference in New Issue
Block a user