mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

* Add panel hunt options * Make sure all panels are either solvable or disabled in panel hunt * Pick huntable panels * Discards in disable non randomized * Set up panel hunt requirement * Panel hunt functional * Make it so an event can have multiple names * Panel hunt with events * Add hunt entities to slot data * ruff * add to hint data, no client sneding yet * encode panel hunt amount in compact hint data * Remove print statement * my b * consistent * meh * additions for lcient * Nah * Victory panels ineligible for panel hunt * Panel Hunt Postgame option * cleanup * Add data generation file * pull out set * always disable gate ep in panel hunt * Disallow certain challenge panels from being panel hunt panels * Make panelhuntpostgame its own function, so it can be called even if normal postgame is enabled * disallow PP resets from panel hunt * Disable challenge timer and elevetor start respectively in disable hunt postgame * Fix panelhunt postgame * lol * When you test that the bug is fixed but not that the non-bug is not unfixed * Prevent Obelisks from being panel hunt panels * Make picking panels for panel hunt a bit more sophisticated, if less random * Better function maybe ig * Ok maybe that was a bit too much * Give advanced players some control over panel hunt * lint * correct the logic for amount to pick * decided the jingle thing was dumb, I'll figure sth out client side. Same area discouragement is now a configurable factor, and the logic has been significantly rewritten * comment * Make the option visible * Safety * Change assert slightly * We do a little logging * number tweak & we do a lil logging * we do a little more logging * Ruff * Panel Hunt Option Group * Idk how that got here * Update worlds/witness/options.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update worlds/witness/__init__.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * remove merge error * Update worlds/witness/player_logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * True * Don't have underwater sliding bridge when you have above water sliding bridge * These are not actually connected lol * get rid of unnecessary variable * Refactor compact hint function again * lint * Pull out Entity Hunt Picking into its own class, split it into many functions. Kept a lot of the comments tho * forgot to actually add the new file * some more refactoring & docstrings * consistent naming * flip elif change * Comment about naming * Make static eligible panels a constant I can refer back to * slight formatting change * pull out options-based eligibility into its own function * better text and stuff * lint * this is not necessary * capitalisation * Fix same area discouragement 0 * Simplify data file generation * Simplify data file generation * prevent div 0 * Add Vault Boxes -> Vault Panels to replacements * Update options.py * Update worlds/witness/entity_hunt.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update entity_hunt.py * Fix some events not working * assert * remove now unused function * lint * Lasers Activate, Lasers don't Solve * lint * oops * mypy * lint * Add simple panel hunt unit test * Add Panel Hunt Tests * Add more Panel Hunt Tests * Disallow Box Short for normal panel hunt --------- Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
145 lines
5.6 KiB
Python
145 lines
5.6 KiB
Python
"""
|
|
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, 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.static_logic import StaticWitnessLogicObj
|
|
from .data.utils import WitnessRule, optimize_witness_rule
|
|
from .locations import WitnessPlayerLocations
|
|
from .player_logic import WitnessPlayerLogic
|
|
|
|
if TYPE_CHECKING:
|
|
from . import WitnessWorld
|
|
|
|
|
|
class WitnessPlayerRegions:
|
|
"""Class that defines Witness Regions"""
|
|
|
|
def __init__(self, player_locations: WitnessPlayerLocations, world: "WitnessWorld") -> None:
|
|
difficulty = world.options.puzzle_randomization
|
|
|
|
self.reference_logic: StaticWitnessLogicObj
|
|
if difficulty == "sigma_normal":
|
|
self.reference_logic = static_witness_logic.sigma_normal
|
|
elif difficulty == "sigma_expert":
|
|
self.reference_logic = static_witness_logic.sigma_expert
|
|
else:
|
|
self.reference_logic = static_witness_logic.vanilla
|
|
|
|
self.player_locations = player_locations
|
|
self.two_way_entrance_register: Dict[Tuple[str, str], List[Entrance]] = defaultdict(lambda: [])
|
|
self.created_region_names: Set[str] = set()
|
|
|
|
@staticmethod
|
|
def make_lambda(item_requirement: WitnessRule, world: "WitnessWorld") -> CollectionRule:
|
|
from .rules import _meets_item_requirements
|
|
|
|
"""
|
|
Lambdas are made in a for loop, so the values have to be captured
|
|
This function is for that purpose
|
|
"""
|
|
|
|
return _meets_item_requirements(item_requirement, world)
|
|
|
|
def connect_if_possible(self, world: "WitnessWorld", source: str, target: str, req: WitnessRule,
|
|
regions_by_name: Dict[str, Region]) -> None:
|
|
"""
|
|
connect two regions and set the corresponding requirement
|
|
"""
|
|
|
|
# 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})
|
|
|
|
# Dissolve any "True" or "TrueOneWay"
|
|
real_requirement = frozenset({option - {"True", "TrueOneWay"} for option in real_requirement})
|
|
|
|
# If there is no way to actually use this connection, don't even bother making it.
|
|
if not real_requirement:
|
|
return
|
|
|
|
# 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]
|
|
|
|
connection_name = source + " to " + target
|
|
|
|
connection = Entrance(
|
|
world.player,
|
|
connection_name,
|
|
source_region
|
|
)
|
|
|
|
connection.access_rule = self.make_lambda(final_requirement, world)
|
|
|
|
source_region.exits.append(connection)
|
|
connection.connect(target_region)
|
|
|
|
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 = {
|
|
single_unlock for option in final_requirement for single_unlock in option
|
|
if single_unlock in self.reference_logic.ALL_REGIONS_BY_NAME
|
|
}
|
|
|
|
for dependent_region in mentioned_regions:
|
|
world.multiworld.register_indirect_condition(regions_by_name[dependent_region], connection)
|
|
|
|
def create_regions(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic) -> None:
|
|
"""
|
|
Creates all the regions for The Witness
|
|
"""
|
|
from . import create_region
|
|
|
|
all_locations: Set[str] = set()
|
|
regions_by_name: Dict[str, Region] = {}
|
|
|
|
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
|
|
}
|
|
|
|
event_locations_per_region = defaultdict(list)
|
|
|
|
for event_location, event_item_and_entity in player_logic.EVENT_ITEM_PAIRS.items():
|
|
region = static_witness_logic.ENTITIES_BY_HEX[event_item_and_entity[1]]["region"]
|
|
if region is None:
|
|
region_name = "Entry"
|
|
else:
|
|
region_name = region["name"]
|
|
event_locations_per_region[region_name].append(event_location)
|
|
|
|
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["entities"]
|
|
if self.reference_logic.ENTITIES_BY_HEX[panel]["checkName"]
|
|
in self.player_locations.CHECK_LOCATION_TABLE
|
|
]
|
|
|
|
locations_for_this_region += event_locations_per_region[region_name]
|
|
|
|
all_locations = all_locations | set(locations_for_this_region)
|
|
|
|
new_region = create_region(world, region_name, self.player_locations, locations_for_this_region)
|
|
|
|
regions_by_name[region_name] = new_region
|
|
|
|
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)
|