The Witness: Panel Hunt Mode (#3265)

* 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>
This commit is contained in:
NewSoupVi
2024-08-20 01:16:35 +02:00
committed by GitHub
parent c010c8c938
commit f253dffc07
16 changed files with 751 additions and 117 deletions

View File

@@ -10,7 +10,6 @@ from worlds.generic.Rules import CollectionRule, set_rule
from .data import static_logic as static_witness_logic
from .data.utils import WitnessRule
from .locations import WitnessPlayerLocations
from .player_logic import WitnessPlayerLogic
if TYPE_CHECKING:
@@ -31,42 +30,37 @@ laser_hexes = [
]
def _has_laser(laser_hex: str, world: "WitnessWorld", player: int, redirect_required: bool) -> CollectionRule:
if laser_hex == "0x012FB" and redirect_required:
return lambda state: (
_can_solve_panel(laser_hex, world, world.player, world.player_logic, world.player_locations)(state)
and state.has("Desert Laser Redirection", player)
)
def _can_do_panel_hunt(world: "WitnessWorld") -> CollectionRule:
required = world.panel_hunt_required_count
player = world.player
return lambda state: state.has("+1 Panel Hunt", player, required)
return _can_solve_panel(laser_hex, world, world.player, world.player_logic, world.player_locations)
def _has_laser(laser_hex: str, world: "WitnessWorld", redirect_required: bool) -> CollectionRule:
player = world.player
laser_name = static_witness_logic.ENTITIES_BY_HEX[laser_hex]["checkName"]
# Workaround for intentional naming inconsistency
if laser_name == "Symmetry Island Laser":
laser_name = "Symmetry Laser"
if laser_hex == "0x012FB" and redirect_required:
return lambda state: state.has_all([f"+1 Laser ({laser_name})", "Desert Laser Redirection"], player)
return lambda state: state.has(f"+1 Laser ({laser_name})", player)
def _has_lasers(amount: int, world: "WitnessWorld", redirect_required: bool) -> CollectionRule:
laser_lambdas = []
for laser_hex in laser_hexes:
has_laser_lambda = _has_laser(laser_hex, world, world.player, redirect_required)
has_laser_lambda = _has_laser(laser_hex, world, redirect_required)
laser_lambdas.append(has_laser_lambda)
return lambda state: sum(laser_lambda(state) for laser_lambda in laser_lambdas) >= amount
def _can_solve_panel(panel: str, world: "WitnessWorld", player: int, player_logic: WitnessPlayerLogic,
player_locations: WitnessPlayerLocations) -> CollectionRule:
"""
Determines whether a panel can be solved
"""
panel_obj = player_logic.REFERENCE_LOGIC.ENTITIES_BY_HEX[panel]
entity_name = panel_obj["checkName"]
if entity_name + " Solved" in player_locations.EVENT_LOCATION_TABLE:
return lambda state: state.has(player_logic.EVENT_ITEM_PAIRS[entity_name + " Solved"], player)
return make_lambda(panel, world)
def _can_do_expert_pp2(state: CollectionState, world: "WitnessWorld") -> bool:
"""
For Expert PP2, you need a way to access PP2 from the front, and a separate way from the back.
@@ -202,8 +196,9 @@ def _can_do_theater_to_tunnels(state: CollectionState, world: "WitnessWorld") ->
)
def _has_item(item: str, world: "WitnessWorld", player: int,
player_logic: WitnessPlayerLogic, player_locations: WitnessPlayerLocations) -> CollectionRule:
def _has_item(item: str, world: "WitnessWorld", player: int, player_logic: WitnessPlayerLogic) -> CollectionRule:
assert item not in static_witness_logic.ENTITIES_BY_HEX, "Requirements can no longer contain entity hexes directly."
if item in player_logic.REFERENCE_LOGIC.ALL_REGIONS_BY_NAME:
region = world.get_region(item)
return region.can_reach
@@ -219,12 +214,13 @@ def _has_item(item: str, world: "WitnessWorld", player: int,
if item == "11 Lasers + Redirect":
laser_req = world.options.challenge_lasers.value
return _has_lasers(laser_req, world, True)
if item == "Entity Hunt":
# Right now, panel hunt is the only type of entity hunt. This may need to be changed later
return _can_do_panel_hunt(world)
if item == "PP2 Weirdness":
return lambda state: _can_do_expert_pp2(state, world)
if item == "Theater to Tunnels":
return lambda state: _can_do_theater_to_tunnels(state, world)
if item in player_logic.USED_EVENT_NAMES_BY_HEX:
return _can_solve_panel(item, world, player, player_logic, player_locations)
prog_item = static_witness_logic.get_parent_progressive_item(item)
return lambda state: state.has(prog_item, player, player_logic.MULTI_AMOUNTS[item])
@@ -237,7 +233,7 @@ def _meets_item_requirements(requirements: WitnessRule, world: "WitnessWorld") -
"""
lambda_conversion = [
[_has_item(item, world, world.player, world.player_logic, world.player_locations) for item in subset]
[_has_item(item, world, world.player, world.player_logic) for item in subset]
for subset in requirements
]
@@ -265,7 +261,8 @@ def set_rules(world: "WitnessWorld") -> None:
real_location = location
if location in world.player_locations.EVENT_LOCATION_TABLE:
real_location = location[:-7]
entity_hex = world.player_logic.EVENT_ITEM_PAIRS[location][1]
real_location = static_witness_logic.ENTITIES_BY_HEX[entity_hex]["checkName"]
associated_entity = world.player_logic.REFERENCE_LOGIC.ENTITIES_BY_NAME[real_location]
entity_hex = associated_entity["entity_hex"]