Files
Grinch-AP/worlds/messenger/test/test_access.py
Aaron Wagener d20d09e682 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>
2024-03-11 23:23:41 +01:00

213 lines
12 KiB
Python

from . import MessengerTestBase
from ..constants import NOTES, PHOBEKINS
class AccessTest(MessengerTestBase):
options = {
"shuffle_shards": "true",
}
def test_tabi(self) -> None:
"""locations that hard require the Lightfoot Tabi"""
locations = [
"Searing Crags - Pyro", "Underworld - Key of Chaos", "Underworld Seal - Sharp and Windy Climb",
"Underworld Seal - Spike Wall", "Underworld Seal - Fireball Wave", "Underworld Seal - Rising Fanta",
"Sunken Shrine - Sun Crest", "Sunken Shrine - Moon Crest", "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 = [["Lightfoot Tabi"]]
self.assertAccessDependency(locations, items)
def test_dart(self) -> None:
"""locations that hard require the Rope Dart"""
locations = [
"Ninja Village Seal - Tree House",
"Autumn Hills - Key of Hope",
"Forlorn Temple - Demon King",
"Down Under Mega Shard",
"Howling Grotto Seal - Crushing Pits",
"Glacial Peak Seal - Ice Climbers",
"Tower of Time Seal - Time Waster",
"Tower of Time Seal - Lantern Climb",
"Tower of Time Seal - Arcane Orbs",
"Cloud Ruins Seal - Ghost Pit",
"Cloud Ruins Seal - Money Farm Room",
"Cloud Ruins Seal - Toothbrush Alley",
"Money Farm Room Mega Shard 1",
"Money Farm Room Mega Shard 2",
"Underworld Seal - Rising Fanta",
"Elemental Skylands - Key of Symbiosis",
"Elemental Skylands Seal - Water",
"Elemental Skylands Seal - Fire",
"Earth Mega Shard",
"Water Mega Shard",
"Rescue Phantom",
]
items = [["Rope Dart"]]
self.assertAccessDependency(locations, items)
def test_wingsuit(self) -> None:
"""locations that hard require the Wingsuit"""
locations = [
"Ninja Village - Candle", "Ninja Village Seal - Tree House", "Autumn Hills - Climbing Claws",
"Autumn Hills - Key of Hope", "Autumn Hills Seal - Trip Saws", "Autumn Hills Seal - Double Swing Saws",
"Autumn Hills Seal - Spike Ball Swing", "Autumn Hills Seal - Spike Ball Darts", "Catacombs - Necro",
"Catacombs - Ruxxtin's Amulet", "Catacombs Seal - Triple Spike Crushers",
"Catacombs Seal - Crusher Gauntlet", "Catacombs Seal - Dirty Pond", "Bamboo Creek - Claustro",
"Cloud Ruins - Acro", "Bamboo Creek Seal - Spike Crushers and Doors", "Bamboo Creek Seal - Spike Ball Pits",
"Bamboo Creek Seal - Spike Crushers and Doors v2", "Howling Grotto Seal - Crushing Pits",
"Howling Grotto Seal - Windy Saws and Balls", "Tower of Time Seal - Lantern Climb",
"Forlorn Temple - Demon King", "Cloud Ruins Seal - Ghost Pit", "Cloud Ruins Seal - Toothbrush Alley",
"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", "Elemental Skylands Seal - Water",
"Elemental Skylands Seal - Fire", "Elemental Skylands - Key of Symbiosis",
"Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset", "Ninja Village - Astral Seed",
"Searing Crags - 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", "Earth Mega Shard", "Water Mega Shard",
"Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2",
"Autumn Hills - Leaf Golem", "Catacombs - Ruxxtin", "Howling Grotto - Emerald Golem"
]
items = [["Wingsuit"]]
self.assertAccessDependency(locations, items)
def test_vertical(self) -> None:
"""locations that require either the Rope Dart or the Wingsuit"""
locations = [
"Ninja Village Seal - Tree House", "Howling Grotto Seal - Crushing Pits",
"Glacial Peak Seal - Ice Climbers", "Tower of Time Seal - Time Waster",
"Underworld Seal - Rising Fanta", "Elemental Skylands - Key of Symbiosis",
"Elemental Skylands Seal - Water", "Elemental Skylands Seal - Fire", "Ninja Village - Candle",
"Autumn Hills - Climbing Claws", "Autumn Hills - Key of Hope", "Autumn Hills Seal - Trip Saws",
"Autumn Hills Seal - Double Swing Saws", "Autumn Hills Seal - Spike Ball Swing",
"Autumn Hills Seal - Spike Ball Darts", "Catacombs - Necro", "Catacombs - Ruxxtin's Amulet",
"Catacombs Seal - Triple Spike Crushers", "Catacombs Seal - Crusher Gauntlet",
"Catacombs Seal - Dirty Pond", "Bamboo Creek - Claustro", "Cloud Ruins - Acro",
"Bamboo Creek Seal - Spike Crushers and Doors", "Bamboo Creek Seal - Spike Ball Pits",
"Bamboo Creek Seal - Spike Crushers and Doors v2", "Howling Grotto Seal - Crushing Pits",
"Howling Grotto Seal - Windy Saws and Balls", "Forlorn Temple - Demon King", "Cloud Ruins Seal - Ghost Pit",
"Cloud Ruins Seal - Toothbrush Alley", "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",
"Searing Crags - Power Thistle", "Searing Crags - Key of Strength",
"Glacial Peak Seal - Projectile Spike Pit", "Glacial Peak Seal - Glacial Air Swag",
"Riviere Turquoise - Butterfly Matriarch", "Riviere Turquoise Seal - Flower Power",
"Riviere Turquoise Seal - Launch of Faith",
"Searing Crags Seal - Triple Ball Spinner", "Searing Crags Seal - Raining Rocks",
"Searing Crags Seal - Rhythm Rocks", "Ninja Village - Astral Seed", "Searing Crags - 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",
"Autumn Hills - Leaf Golem", "Catacombs - Ruxxtin", "Howling Grotto - Emerald Golem"
]
items = [["Wingsuit", "Rope Dart"]]
self.assertAccessDependency(locations, items)
def test_amulet(self) -> None:
"""Locations that require Ruxxtin's Amulet"""
locations = [
"Cloud Ruins - Acro", "Cloud Ruins Seal - Ghost Pit", "Cloud Ruins Seal - Toothbrush Alley",
"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)
def test_firefly(self) -> None:
"""Elemental Skylands and Corrupted Future require the Magic Firefly"""
locations = [
"Elemental Skylands - Key of Symbiosis", "Elemental Skylands Seal - Air", "Elemental Skylands Seal - Fire",
"Elemental Skylands Seal - Water", "Corrupted Future - Key of Courage", "Earth Mega Shard",
"Water Mega Shard"
]
items = [["Magic Firefly"]]
self.assertAccessDependency(locations, items)
def test_crests(self) -> None:
"""Test Key of Love nonsense"""
locations = ["Sunken Shrine - Key of Love"]
items = [["Sun Crest", "Moon Crest"]]
self.assertAccessDependency(locations, items)
self.collect_all_but("Sun Crest")
self.assertEqual(self.can_reach_location("Sunken Shrine - Key of Love"), False)
self.remove(self.get_item_by_name("Moon Crest"))
self.collect_by_name("Sun Crest")
self.assertEqual(self.can_reach_location("Sunken Shrine - Key of Love"), False)
def test_thistle(self) -> None:
"""I'm a chuckster!"""
locations = ["Searing Crags - Key of Strength"]
items = [["Power Thistle"]]
self.assertAccessDependency(locations, items)
def test_crown(self) -> None:
"""Crocomire but not"""
locations = ["Corrupted Future - Key of Courage"]
items = [["Demon King Crown"]]
self.assertAccessDependency(locations, items)
def test_dboost(self) -> None:
"""
short for damage boosting, d-boosting is a technique in video games where the player intentionally or
unintentionally takes damage and uses the several following frames of invincibility to defeat or get past an
enemy or obstacle, most commonly used in platformers such as the Super Mario games
"""
locations = [
"Riviere Turquoise Seal - Bounces and Balls", "Searing Crags Seal - Triple Ball Spinner",
"Forlorn Temple - Demon King", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset",
"Sunny Day Mega Shard", "Down Under Mega Shard",
]
items = [["Path of Resilience", "Meditation", "Second Wind"]]
self.assertAccessDependency(locations, items)
def test_currents(self) -> None:
"""there's one of these but oh man look at it go"""
self.assertAccessDependency(["Elemental Skylands Seal - Water"], [["Currents Master"]])
def test_strike(self) -> None:
"""strike is pretty cool but it doesn't block much"""
locations = [
"Glacial Peak Seal - Projectile Spike Pit", "Elemental Skylands Seal - Fire",
]
items = [["Strike of the Ninja"]]
self.assertAccessDependency(locations, items)
def test_goal(self) -> None:
"""Test some different states to verify goal requires the correct items"""
self.collect_all_but([*NOTES, "Do the Thing!"])
self.assertEqual(self.can_reach_location("Rescue Phantom"), False)
self.collect_all_but(["Key of Love", "Do the Thing!"])
self.assertBeatable(False)
self.collect_by_name(["Key of Love"])
self.assertEqual(self.can_reach_location("Rescue Phantom"), True)
self.assertBeatable(True)
class ItemsAccessTest(MessengerTestBase):
options = {
"shuffle_seals": "false",
"accessibility": "items",
}
def test_self_locking_items(self) -> None:
"""Force items that can be self locked to ensure it's valid placement."""
location_lock_pairs = {
"Searing Crags - Key of Strength": ["Power Thistle"],
"Sunken Shrine - Key of Love": ["Sun Crest", "Moon Crest"],
"Corrupted Future - Key of Courage": ["Demon King Crown"],
}
self.collect_all_but([item for items in location_lock_pairs.values() for item in items])
for loc in location_lock_pairs:
for item_name in location_lock_pairs[loc]:
item = self.get_item_by_name(item_name)
with self.subTest("Fulfills Accessibility", location=loc, item=item_name):
self.assertTrue(self.multiworld.get_location(loc, self.player).can_fill(self.multiworld.state, item,
True))