The Witness: Automatic Postgame & Disabled Panels Calculation (#2698)

* Refactor postgame code to be more readable

* Change all references to options to strings

* oops

* Fix some outdated code related to yaml-disabled EPs

* Small fixes to short/longbox stuff (thanks Medic)

* comment

* fix duplicate

* Removed triplicate lmfao

* Better comment

* added another 'unfun' postgame consideration

* comment

* more option strings

* oops

* Remove an unnecessary comparison

* another string missed

* New classification changes (Credit: Exempt-Medic)

* Don't need to pass world

* Comments

* Replace it with another magic system because why not at this point :DDDDDD

* oops

* Oops

* Another was missed

* Make events conditions. Disable_Non_Randomized will no longer just 'have all events'

* What the fuck? Has this just always been broken?

* Don't have boolean function with 'not' in the name

* Another useful classification

* slight code refactor

* Funny haha booleans

* This would create a really bad merge error

* I can't believe this actually kind of works

* And here's the punchline. + some bugfixes

* Comment dat code

* Comments galore

* LMAO OOPS

* so nice I did it twice

* debug x2

* Careful

* Add more comments

* That comment is a bit unnecessary now

* Fix overriding region connections

* Correct a comment

* Correct again

* Rename variable

* Idk I guess this is in this branch now

* More tweaking of postgame & comments

* This is commit just exists to fix that grammar error

* I think I can just fucking delete this now???

* Forgot to reset something here

* Delete dead codepath

* Obelisk Keys were getting yote erroneously

* More comments

* Fix duplicate connections

* Oopsington III

* performance improvements & cleanup

* More rules cleanup and performance improvements

* Oh cool I can do this huh

* Okay but this is even more swag tho

* Lazy eval

* remove some implicit checks

* Is this too magical yet

* more guard magic

* Maaaaaaaagiccccccccc

* Laaaaaaaaaaaaaaaazzzzzzyyyyyyyyyyy

* Make it docstring

* Newline bc I like that better

* this is a little spooky lol

* lol

* Wait

* spoO

* Better variable name and comment

* Improved comment again

* better API

* oops I deleted a deepcopy

* lol help

* Help???

* player_regionsns lmao

* Add some comments

* Make doors disabled properly again. I hope this works

* Don't disable lasers

* Omega oops

* Make Floor 2 Exit not exist

* Make a fix that's warps compatible

* I think this was an oversight, I tested a seed and it seems to have the same result

* This is definitely less Violet than before

* Does this feel more violet lol

* Exception if a laser gets disabled, cleanup

* Ruff

* >:(

* consistent utils import

* Make autopostgame more reviewable (hopefully)

* more reviewability

* WitnessRule

* replace another instance of it

* lint

* style

* comment

* found the bug

* Move comment

* Get rid of cache and ugly allow_victory

* comments and lint
This commit is contained in:
NewSoupVi
2024-06-01 23:11:28 +02:00
committed by GitHub
parent da33d1576a
commit e49b1f9fbb
19 changed files with 643 additions and 518 deletions

View File

@@ -3,13 +3,14 @@ Defines Region for The Witness, assigns locations to them,
and connects them with the proper requirements
"""
from collections import defaultdict
from typing import TYPE_CHECKING, Dict, FrozenSet, List, Tuple
from typing import TYPE_CHECKING, Dict, List, Set, Tuple
from BaseClasses import Entrance, Region
from worlds.generic.Rules import CollectionRule
from .data import static_logic as static_witness_logic
from .data.utils import WitnessRule, optimize_witness_rule
from .locations import WitnessPlayerLocations, static_witness_locations
from .player_logic import WitnessPlayerLogic
@@ -24,7 +25,7 @@ class WitnessPlayerRegions:
logic = None
@staticmethod
def make_lambda(item_requirement: FrozenSet[FrozenSet[str]], world: "WitnessWorld") -> CollectionRule:
def make_lambda(item_requirement: WitnessRule, world: "WitnessWorld") -> CollectionRule:
from .rules import _meets_item_requirements
"""
@@ -34,8 +35,8 @@ class WitnessPlayerRegions:
return _meets_item_requirements(item_requirement, world)
def connect_if_possible(self, world: "WitnessWorld", source: str, target: str, req: FrozenSet[FrozenSet[str]],
regions_by_name: Dict[str, Region], backwards: bool = False):
def connect_if_possible(self, world: "WitnessWorld", source: str, target: str, req: WitnessRule,
regions_by_name: Dict[str, Region]):
"""
connect two regions and set the corresponding requirement
"""
@@ -43,10 +44,6 @@ class WitnessPlayerRegions:
# Remove any possibilities where being in the target region would be required anyway.
real_requirement = frozenset({option for option in req if target not in option})
# There are some connections that should only be done one way. If this is a backwards connection, check for that
if backwards:
real_requirement = frozenset({option for option in real_requirement if "TrueOneWay" not in option})
# Dissolve any "True" or "TrueOneWay"
real_requirement = frozenset({option - {"True", "TrueOneWay"} for option in real_requirement})
@@ -56,12 +53,12 @@ class WitnessPlayerRegions:
# We don't need to check for the accessibility of the source region.
final_requirement = frozenset({option - frozenset({source}) for option in real_requirement})
final_requirement = optimize_witness_rule(final_requirement)
source_region = regions_by_name[source]
target_region = regions_by_name[target]
backwards = " Backwards" if backwards else ""
connection_name = source + " to " + target + backwards
connection_name = source + " to " + target
connection = Entrance(
world.player,
@@ -74,7 +71,8 @@ class WitnessPlayerRegions:
source_region.exits.append(connection)
connection.connect(target_region)
self.created_entrances[source, target].append(connection)
self.two_way_entrance_register[source, target].append(connection)
self.two_way_entrance_register[target, source].append(connection)
# Register any necessary indirect connections
mentioned_regions = {
@@ -94,14 +92,19 @@ class WitnessPlayerRegions:
all_locations = set()
regions_by_name = dict()
for region_name, region in self.reference_logic.ALL_REGIONS_BY_NAME.items():
regions_to_create = {
k: v for k, v in self.reference_logic.ALL_REGIONS_BY_NAME.items()
if k not in player_logic.UNREACHABLE_REGIONS
}
for region_name, region in regions_to_create.items():
locations_for_this_region = [
self.reference_logic.ENTITIES_BY_HEX[panel]["checkName"] for panel in region["panels"]
self.reference_logic.ENTITIES_BY_HEX[panel]["checkName"] for panel in region["entities"]
if self.reference_logic.ENTITIES_BY_HEX[panel]["checkName"]
in self.player_locations.CHECK_LOCATION_TABLE
]
locations_for_this_region += [
static_witness_locations.get_event_name(panel) for panel in region["panels"]
static_witness_locations.get_event_name(panel) for panel in region["entities"]
if static_witness_locations.get_event_name(panel) in self.player_locations.EVENT_LOCATION_TABLE
]
@@ -111,31 +114,13 @@ class WitnessPlayerRegions:
regions_by_name[region_name] = new_region
for region_name, region in self.reference_logic.ALL_REGIONS_BY_NAME.items():
self.created_region_names = set(regions_by_name)
world.multiworld.regions += regions_by_name.values()
for region_name, region in regions_to_create.items():
for connection in player_logic.CONNECTIONS_BY_REGION_NAME[region_name]:
self.connect_if_possible(world, region_name, connection[0], connection[1], regions_by_name)
self.connect_if_possible(world, connection[0], region_name, connection[1], regions_by_name, True)
# find regions that are completely disconnected from the start node and remove them
regions_to_check = {"Menu"}
reachable_regions = {"Menu"}
while regions_to_check:
next_region = regions_to_check.pop()
region_obj = regions_by_name[next_region]
for exit in region_obj.exits:
target = exit.connected_region
if target.name in reachable_regions:
continue
regions_to_check.add(target.name)
reachable_regions.add(target.name)
self.created_regions = {k: v for k, v in regions_by_name.items() if k in reachable_regions}
world.multiworld.regions += self.created_regions.values()
def __init__(self, player_locations: WitnessPlayerLocations, world: "WitnessWorld") -> None:
difficulty = world.options.puzzle_randomization
@@ -148,5 +133,5 @@ class WitnessPlayerRegions:
self.reference_logic = static_witness_logic.vanilla
self.player_locations = player_locations
self.created_entrances: Dict[Tuple[str, str], List[Entrance]] = defaultdict(lambda: [])
self.created_regions: Dict[str, Region] = dict()
self.two_way_entrance_register: Dict[Tuple[str, str], List[Entrance]] = defaultdict(lambda: [])
self.created_region_names: Set[str] = set()