mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
The Witness: The Secret Feature (#4370)
* Secret Feature * Fixes * Fixes and unit tests * renaming some variables * Fix the thing * unit test for elevator egg * Docstring * reword * Fix duplicate locations I think? * Remove debug thing * Add the tests back lol * Make it so that you can exclude an egg to disable it * Improve hint text for easter eggs * Update worlds/witness/options.py Co-authored-by: Scipio Wright <scipiowright@gmail.com> * Update worlds/witness/player_logic.py Co-authored-by: Scipio Wright <scipiowright@gmail.com> * Update worlds/witness/options.py Co-authored-by: Scipio Wright <scipiowright@gmail.com> * Update worlds/witness/player_logic.py Co-authored-by: Scipio Wright <scipiowright@gmail.com> * Update worlds/witness/rules.py Co-authored-by: Scipio Wright <scipiowright@gmail.com> * Update test_easter_egg_shuffle.py * This was actually not necessary, since this is the Egg requirements, nothing to do with location names * Move one of them * Improve logic * Lol * Moar * Adjust unit tests * option docstring adjustment * Recommend door shuffle * Don't overlap IDs * Option description idk * Change the way the difficulties work to reward playing higher modes * Fix merge * add some stuff to generate_data_file (this file is not imported during gen, don't review it :D) * oop * space * This can be earlier than I thought, apparently. * buffer * Comment * Make sure the option is VERY visible * Some mypy stuff * apparently ruff wants this * . * durinig * Update options.py * Explain the additional effects of each difficulty * Fix logic of flood room secret * Add Southern Peninsula Area * oop --------- Co-authored-by: Scipio Wright <scipiowright@gmail.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import dataclasses
|
||||
from logging import error, warning
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
|
||||
from BaseClasses import CollectionState, Entrance, Location, Region, Tutorial
|
||||
from BaseClasses import CollectionState, Entrance, Location, LocationProgressType, Region, Tutorial
|
||||
|
||||
from Options import OptionError, PerGameCommonOptions, Toggle
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
@@ -380,6 +380,10 @@ class WitnessWorld(World):
|
||||
if isinstance(item_name, dict):
|
||||
item_name = next(iter(item_name))
|
||||
|
||||
# Easter Egg events with arbitrary sizes
|
||||
if item_name.startswith("+") and "Easter Egg" in item_name:
|
||||
return WitnessItem.make_egg_event(item_name, self.player)
|
||||
|
||||
# this conditional is purely for unit tests, which need to be able to create an item before generate_early
|
||||
item_data: ItemData
|
||||
if hasattr(self, "player_items") and self.player_items and item_name in self.player_items.item_data:
|
||||
@@ -389,6 +393,18 @@ class WitnessWorld(World):
|
||||
|
||||
return WitnessItem(item_name, item_data.classification, item_data.ap_code, player=self.player)
|
||||
|
||||
def collect(self, state: "CollectionState", item: WitnessItem) -> bool:
|
||||
changed = super().collect(state, item)
|
||||
if changed and item.eggs:
|
||||
state.prog_items[self.player]["Egg"] += item.eggs
|
||||
return changed
|
||||
|
||||
def remove(self, state: "CollectionState", item: WitnessItem) -> bool:
|
||||
changed = super().remove(state, item)
|
||||
if changed and item.eggs:
|
||||
state.prog_items[self.player]["Egg"] -= item.eggs
|
||||
return changed
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return "Speed Boost"
|
||||
|
||||
@@ -398,11 +414,9 @@ class WitnessLocation(Location):
|
||||
Archipelago Location for The Witness
|
||||
"""
|
||||
game: str = "The Witness"
|
||||
entity_hex: int = -1
|
||||
|
||||
def __init__(self, player: int, name: str, address: Optional[int], parent: Region, ch_hex: int = -1) -> None:
|
||||
def __init__(self, player: int, name: str, address: Optional[int], parent: Region) -> None:
|
||||
super().__init__(player, name, address, parent)
|
||||
self.entity_hex = ch_hex
|
||||
|
||||
|
||||
def create_region(world: WitnessWorld, name: str, player_locations: WitnessPlayerLocations,
|
||||
@@ -416,14 +430,13 @@ def create_region(world: WitnessWorld, name: str, player_locations: WitnessPlaye
|
||||
for location in region_locations:
|
||||
loc_id = player_locations.CHECK_LOCATION_TABLE[location]
|
||||
|
||||
entity_hex = -1
|
||||
location_obj = WitnessLocation(world.player, location, loc_id, ret)
|
||||
|
||||
if location in static_witness_logic.ENTITIES_BY_NAME:
|
||||
entity_hex = int(
|
||||
static_witness_logic.ENTITIES_BY_NAME[location]["entity_hex"], 0
|
||||
)
|
||||
location_obj = WitnessLocation(
|
||||
world.player, location, loc_id, ret, entity_hex
|
||||
)
|
||||
entity_hex = static_witness_logic.ENTITIES_BY_NAME[location]["entity_hex"]
|
||||
|
||||
if entity_hex in world.player_logic.EXCLUDED_ENTITIES:
|
||||
location_obj.progress_type = LocationProgressType.EXCLUDED
|
||||
|
||||
ret.locations.append(location_obj)
|
||||
if exits:
|
||||
|
@@ -206,7 +206,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
|
||||
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
|
||||
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
|
||||
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316 - Desert Flood Room Underwater - 0x1C260:
|
||||
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
|
||||
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
|
||||
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
|
||||
@@ -224,6 +224,8 @@ Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Flood Room Underwater (Desert):
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
@@ -501,7 +503,7 @@ Laser - 0x17C65 (Laser) - 0x17CA4
|
||||
159121 - 0x03BE3 (Garden Right EP) - True - True
|
||||
159122 - 0x0A409 (Wall EP) - True - True
|
||||
|
||||
Inside Monastery (Monastery):
|
||||
Inside Monastery (Monastery) - Monastery North Shutters - 0x09D9B:
|
||||
158213 - 0x09D9B (Shutters Control) - True - Dots
|
||||
158214 - 0x193A7 (Inside 1) - 0x00037 - True
|
||||
158215 - 0x193AA (Inside 2) - 0x193A7 - True
|
||||
@@ -513,6 +515,8 @@ Inside Monastery (Monastery):
|
||||
|
||||
Monastery Garden (Monastery):
|
||||
|
||||
Monastery North Shutters (Monastery):
|
||||
|
||||
==Town==
|
||||
|
||||
Town Obelisk (Town) - Entry - True:
|
||||
@@ -637,9 +641,13 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
|
||||
159556 - 0x33A2A (Door EP) - 0x03553 - True
|
||||
159558 - 0x33B06 (Church EP) - 0x0354E - True
|
||||
|
||||
==Southern Peninsula==
|
||||
|
||||
Southern Peninsula (Southern Peninsula) - Main Island - True:
|
||||
|
||||
==Jungle==
|
||||
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF - Jungle Under Popup Wall - 0x1475B:
|
||||
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
|
||||
158609 - 0x17F9B (Discard) - True - Triangles
|
||||
158252 - 0x002C4 (First Row 1) - True - True
|
||||
@@ -670,6 +678,8 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
|
||||
159350 - 0x035CB (Bamboo CCW EP) - True - True
|
||||
159351 - 0x035CF (Bamboo CW EP) - True - True
|
||||
|
||||
Jungle Under Popup Wall (Jungle):
|
||||
|
||||
Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
|
||||
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
|
||||
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
|
||||
@@ -712,9 +722,11 @@ Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D:
|
||||
158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Colored Squares & Black/White Squares
|
||||
Door - 0x0A08D (Elevator Room Entry) - 0x17E67
|
||||
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay - Bunker Under Elevator - 0x0A079 | Bunker Green Room | Bunker Cyan Room | Bunker Laser Platform:
|
||||
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
|
||||
|
||||
Bunker Under Elevator (Bunker):
|
||||
|
||||
Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
|
||||
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
|
||||
|
||||
@@ -1005,7 +1017,7 @@ Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
|
||||
Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
|
||||
158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Colored Squares & Eraser
|
||||
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay - Mountain Floor 1 Trash Pillar - TrueOneWay - Mountain Floor 1 Back Section - TrueOneWay:
|
||||
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots
|
||||
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots
|
||||
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers & Dots
|
||||
@@ -1018,11 +1030,15 @@ Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneW
|
||||
158418 - 0x09E6C (Left Row 5) - 0x09E79 - Stars & Black/White Squares & Stars + Same Colored Symbol
|
||||
158419 - 0x09E6F (Left Row 6) - 0x09E6C - Stars & Rotated Shapers & Shapers
|
||||
158420 - 0x09E6B (Left Row 7) - 0x09E6F - Stars & Dots
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shaper
|
||||
|
||||
Mountain Floor 1 Trash Pillar (Mountain Floor 1):
|
||||
|
||||
Mountain Floor 1 Back Section (Mountain Floor 1):
|
||||
158421 - 0x33AF5 (Back Row 1) - True - Black/White Squares & Symmetry
|
||||
158422 - 0x33AF7 (Back Row 2) - 0x33AF5 - Black/White Squares & Stars
|
||||
158423 - 0x09F6E (Back Row 3) - 0x33AF7 - Symmetry & Dots
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers
|
||||
|
||||
Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
|
||||
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
|
||||
@@ -1098,14 +1114,16 @@ Elevator (Mountain Bottom Floor):
|
||||
Mountain Pink Bridge EP (Mountain Floor 2):
|
||||
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
|
||||
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D - Caves Entry Door - TrueOneWay:
|
||||
158447 - 0x00FF8 (Caves Entry Panel) - True - Triangles & Black/White Squares
|
||||
Door - 0x2D77D (Caves Entry) - 0x00FF8
|
||||
158448 - 0x334E1 (Rock Control) - True - True
|
||||
|
||||
==Caves==
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
|
||||
Caves Entry Door (Caves):
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5 - Caves Entry Door - TrueOneWay:
|
||||
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares
|
||||
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares
|
||||
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots
|
||||
@@ -1219,3 +1237,7 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
|
||||
159521 - 0x33879 (Tutorial Reflection EP) - True - True
|
||||
159522 - 0x03C19 (Tutorial Moss EP) - True - True
|
||||
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
|
||||
|
||||
==Easter Eggs==
|
||||
|
||||
Easter Eggs (Easter Eggs) - Entry - True:
|
||||
|
@@ -206,7 +206,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
|
||||
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
|
||||
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
|
||||
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316 - Desert Flood Room Underwater - 0x1C260:
|
||||
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
|
||||
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
|
||||
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
|
||||
@@ -224,6 +224,8 @@ Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Flood Room Underwater (Desert):
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
@@ -501,7 +503,7 @@ Laser - 0x17C65 (Laser) - 0x17CA4
|
||||
159121 - 0x03BE3 (Garden Right EP) - True - True
|
||||
159122 - 0x0A409 (Wall EP) - True - True
|
||||
|
||||
Inside Monastery (Monastery):
|
||||
Inside Monastery (Monastery) - Monastery North Shutters - 0x09D9B:
|
||||
158213 - 0x09D9B (Shutters Control) - True - Dots
|
||||
158214 - 0x193A7 (Inside 1) - 0x00037 - True
|
||||
158215 - 0x193AA (Inside 2) - 0x193A7 - True
|
||||
@@ -513,6 +515,8 @@ Inside Monastery (Monastery):
|
||||
|
||||
Monastery Garden (Monastery):
|
||||
|
||||
Monastery North Shutters (Monastery):
|
||||
|
||||
==Town==
|
||||
|
||||
Town Obelisk (Town) - Entry - True:
|
||||
@@ -637,9 +641,13 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
|
||||
159556 - 0x33A2A (Door EP) - 0x03553 - True
|
||||
159558 - 0x33B06 (Church EP) - 0x0354E - True
|
||||
|
||||
==Southern Peninsula==
|
||||
|
||||
Southern Peninsula (Southern Peninsula) - Main Island - True:
|
||||
|
||||
==Jungle==
|
||||
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF - Jungle Under Popup Wall - 0x1475B:
|
||||
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
|
||||
158609 - 0x17F9B (Discard) - True - Arrows
|
||||
158252 - 0x002C4 (First Row 1) - True - True
|
||||
@@ -670,6 +678,8 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
|
||||
159350 - 0x035CB (Bamboo CCW EP) - True - True
|
||||
159351 - 0x035CF (Bamboo CW EP) - True - True
|
||||
|
||||
Jungle Under Popup Wall (Jungle):
|
||||
|
||||
Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
|
||||
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
|
||||
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
|
||||
@@ -712,9 +722,11 @@ Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D:
|
||||
158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares
|
||||
Door - 0x0A08D (Elevator Room Entry) - 0x17E67
|
||||
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay - Bunker Under Elevator - 0x0A079 | Bunker Green Room | Bunker Cyan Room | Bunker Laser Platform:
|
||||
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
|
||||
|
||||
Bunker Under Elevator (Bunker):
|
||||
|
||||
Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
|
||||
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
|
||||
|
||||
@@ -1005,7 +1017,7 @@ Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
|
||||
Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
|
||||
158408 - 0x09E39 (Light Bridge Controller) - True - Eraser & Triangles
|
||||
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay - Mountain Floor 1 Trash Pillar - TrueOneWay - Mountain Floor 1 Back Section - TrueOneWay:
|
||||
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots & Stars & Stars + Same Colored Symbol
|
||||
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Triangles
|
||||
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers & Stars & Stars + Same Colored Symbol
|
||||
@@ -1018,11 +1030,15 @@ Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneW
|
||||
158418 - 0x09E6C (Left Row 5) - 0x09E79 - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol
|
||||
158419 - 0x09E6F (Left Row 6) - 0x09E6C - Symmetry & Stars & Colored Squares & Black/White Squares & Stars + Same Colored Symbol & Symmetry & Eraser
|
||||
158420 - 0x09E6B (Left Row 7) - 0x09E6F - Symmetry & Dots & Full Dots & Triangles
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Rotated Shapers & Stars
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Rotated Shapers & Triangles
|
||||
|
||||
Mountain Floor 1 Trash Pillar (Mountain Floor 1):
|
||||
|
||||
Mountain Floor 1 Back Section (Mountain Floor 1):
|
||||
158421 - 0x33AF5 (Back Row 1) - True - Symmetry & Black/White Squares & Triangles
|
||||
158422 - 0x33AF7 (Back Row 2) - 0x33AF5 - Symmetry & Stars & Triangles & Stars + Same Colored Symbol
|
||||
158423 - 0x09F6E (Back Row 3) - 0x33AF7 - Symmetry & Stars & Shapers & Stars + Same Colored Symbol
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Rotated Shapers & Stars
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Rotated Shapers & Triangles
|
||||
|
||||
Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
|
||||
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
|
||||
@@ -1098,14 +1114,16 @@ Elevator (Mountain Bottom Floor):
|
||||
Mountain Pink Bridge EP (Mountain Floor 2):
|
||||
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
|
||||
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D - Caves Entry Door - TrueOneWay:
|
||||
158447 - 0x00FF8 (Caves Entry Panel) - True - Arrows & Black/White Squares
|
||||
Door - 0x2D77D (Caves Entry) - 0x00FF8
|
||||
158448 - 0x334E1 (Rock Control) - True - True
|
||||
|
||||
==Caves==
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
|
||||
Caves Entry Door (Caves):
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5 - Caves Entry Door - TrueOneWay:
|
||||
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Squares & Black/White Squares
|
||||
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Squares & Black/White Squares
|
||||
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Squares & Black/White Squares & Dots
|
||||
@@ -1219,3 +1237,7 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
|
||||
159521 - 0x33879 (Tutorial Reflection EP) - True - True
|
||||
159522 - 0x03C19 (Tutorial Moss EP) - True - True
|
||||
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
|
||||
|
||||
==Easter Eggs==
|
||||
|
||||
Easter Eggs (Easter Eggs) - Entry - True:
|
||||
|
@@ -206,7 +206,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
|
||||
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
|
||||
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
|
||||
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316 - Desert Flood Room Underwater - 0x1C260:
|
||||
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
|
||||
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
|
||||
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
|
||||
@@ -224,6 +224,8 @@ Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Flood Room Underwater (Desert):
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
@@ -501,7 +503,7 @@ Laser - 0x17C65 (Laser) - 0x17CA4
|
||||
159121 - 0x03BE3 (Garden Right EP) - True - True
|
||||
159122 - 0x0A409 (Wall EP) - True - True
|
||||
|
||||
Inside Monastery (Monastery):
|
||||
Inside Monastery (Monastery) - Monastery North Shutters - 0x09D9B:
|
||||
158213 - 0x09D9B (Shutters Control) - True - Dots
|
||||
158214 - 0x193A7 (Inside 1) - 0x00037 - True
|
||||
158215 - 0x193AA (Inside 2) - 0x193A7 - True
|
||||
@@ -513,6 +515,8 @@ Inside Monastery (Monastery):
|
||||
|
||||
Monastery Garden (Monastery):
|
||||
|
||||
Monastery North Shutters (Monastery):
|
||||
|
||||
==Town==
|
||||
|
||||
Town Obelisk (Town) - Entry - True:
|
||||
@@ -637,9 +641,13 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
|
||||
159556 - 0x33A2A (Door EP) - 0x03553 - True
|
||||
159558 - 0x33B06 (Church EP) - 0x0354E - True
|
||||
|
||||
==Southern Peninsula==
|
||||
|
||||
Southern Peninsula (Southern Peninsula) - Main Island - True:
|
||||
|
||||
==Jungle==
|
||||
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF - Jungle Under Popup Wall - 0x1475B:
|
||||
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
|
||||
158609 - 0x17F9B (Discard) - True - Triangles
|
||||
158252 - 0x002C4 (First Row 1) - True - True
|
||||
@@ -670,6 +678,8 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
|
||||
159350 - 0x035CB (Bamboo CCW EP) - True - True
|
||||
159351 - 0x035CF (Bamboo CW EP) - True - True
|
||||
|
||||
Jungle Under Popup Wall (Jungle):
|
||||
|
||||
Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
|
||||
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
|
||||
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
|
||||
@@ -712,9 +722,11 @@ Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D:
|
||||
158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Colored Squares & Black/White Squares
|
||||
Door - 0x0A08D (Elevator Room Entry) - 0x17E67
|
||||
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay - Bunker Under Elevator - 0x0A079 | Bunker Green Room | Bunker Cyan Room | Bunker Laser Platform:
|
||||
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
|
||||
|
||||
Bunker Under Elevator (Bunker):
|
||||
|
||||
Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
|
||||
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
|
||||
|
||||
@@ -1005,7 +1017,7 @@ Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
|
||||
Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
|
||||
158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Rotated Shapers
|
||||
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay - Mountain Floor 1 Trash Pillar - TrueOneWay - Mountain Floor 1 Back Section - TrueOneWay:
|
||||
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots
|
||||
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots
|
||||
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers
|
||||
@@ -1018,11 +1030,15 @@ Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneW
|
||||
158418 - 0x09E6C (Left Row 5) - 0x09E79 - Stars & Black/White Squares
|
||||
158419 - 0x09E6F (Left Row 6) - 0x09E6C - Shapers & Dots
|
||||
158420 - 0x09E6B (Left Row 7) - 0x09E6F - Dots
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers
|
||||
|
||||
Mountain Floor 1 Trash Pillar (Mountain Floor 1):
|
||||
|
||||
Mountain Floor 1 Back Section (Mountain Floor 1):
|
||||
158421 - 0x33AF5 (Back Row 1) - True - Black/White Squares & Symmetry
|
||||
158422 - 0x33AF7 (Back Row 2) - 0x33AF5 - Black/White Squares
|
||||
158423 - 0x09F6E (Back Row 3) - 0x33AF7 - Symmetry & Dots
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers
|
||||
|
||||
Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
|
||||
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
|
||||
@@ -1098,14 +1114,16 @@ Elevator (Mountain Bottom Floor):
|
||||
Mountain Pink Bridge EP (Mountain Floor 2):
|
||||
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
|
||||
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D - Caves Entry Door - TrueOneWay:
|
||||
158447 - 0x00FF8 (Caves Entry Panel) - True - Black/White Squares
|
||||
Door - 0x2D77D (Caves Entry) - 0x00FF8
|
||||
158448 - 0x334E1 (Rock Control) - True - True
|
||||
|
||||
==Caves==
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
|
||||
Caves Entry Door (Caves):
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5 - Caves Entry Door - TrueOneWay:
|
||||
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares
|
||||
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares
|
||||
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots
|
||||
@@ -1219,3 +1237,7 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
|
||||
159521 - 0x33879 (Tutorial Reflection EP) - True - True
|
||||
159522 - 0x03C19 (Tutorial Moss EP) - True - True
|
||||
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
|
||||
|
||||
==Easter Eggs==
|
||||
|
||||
Easter Eggs (Easter Eggs) - Entry - True:
|
||||
|
@@ -206,7 +206,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
|
||||
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
|
||||
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
|
||||
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316 - Desert Flood Room Underwater - 0x1C260:
|
||||
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
|
||||
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
|
||||
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
|
||||
@@ -224,6 +224,8 @@ Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
|
||||
Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Flood Room Underwater (Desert):
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
@@ -501,7 +503,7 @@ Laser - 0x17C65 (Laser) - 0x17CA4
|
||||
159121 - 0x03BE3 (Garden Right EP) - True - True
|
||||
159122 - 0x0A409 (Wall EP) - True - True
|
||||
|
||||
Inside Monastery (Monastery):
|
||||
Inside Monastery (Monastery) - Monastery North Shutters - 0x09D9B:
|
||||
158213 - 0x09D9B (Shutters Control) - True - Dots
|
||||
158214 - 0x193A7 (Inside 1) - 0x00037 - True
|
||||
158215 - 0x193AA (Inside 2) - 0x193A7 - True
|
||||
@@ -513,6 +515,8 @@ Inside Monastery (Monastery):
|
||||
|
||||
Monastery Garden (Monastery):
|
||||
|
||||
Monastery North Shutters (Monastery):
|
||||
|
||||
==Town==
|
||||
|
||||
Town Obelisk (Town) - Entry - True:
|
||||
@@ -637,9 +641,13 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
|
||||
159556 - 0x33A2A (Door EP) - 0x03553 - True
|
||||
159558 - 0x33B06 (Church EP) - 0x0354E - True
|
||||
|
||||
==Southern Peninsula==
|
||||
|
||||
Southern Peninsula (Southern Peninsula) - Main Island - True:
|
||||
|
||||
==Jungle==
|
||||
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
|
||||
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF - Jungle Under Popup Wall - 0x1475B:
|
||||
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
|
||||
158609 - 0x17F9B (Discard) - True - Arrows & Triangles
|
||||
158252 - 0x002C4 (First Row 1) - True - True
|
||||
@@ -670,6 +678,8 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
|
||||
159350 - 0x035CB (Bamboo CCW EP) - True - True
|
||||
159351 - 0x035CF (Bamboo CW EP) - True - True
|
||||
|
||||
Jungle Under Popup Wall (Jungle):
|
||||
|
||||
Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
|
||||
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
|
||||
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
|
||||
@@ -712,9 +722,11 @@ Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D:
|
||||
158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Colored Squares & Black/White Squares
|
||||
Door - 0x0A08D (Elevator Room Entry) - 0x17E67
|
||||
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
|
||||
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay - Bunker Under Elevator - 0x0A079 | Bunker Green Room | Bunker Cyan Room | Bunker Laser Platform:
|
||||
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
|
||||
|
||||
Bunker Under Elevator (Bunker):
|
||||
|
||||
Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
|
||||
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
|
||||
|
||||
@@ -1005,7 +1017,7 @@ Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
|
||||
Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
|
||||
158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Colored Squares & Eraser
|
||||
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
|
||||
Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay - Mountain Floor 1 Trash Pillar - TrueOneWay - Mountain Floor 1 Back Section - TrueOneWay:
|
||||
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots
|
||||
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots & Stars & Stars + Same Colored Symbol
|
||||
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers & Stars & Stars + Same Colored Symbol
|
||||
@@ -1018,11 +1030,15 @@ Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneW
|
||||
158418 - 0x09E6C (Left Row 5) - 0x09E79 - Arrows & Black/White Squares & Stars & Stars + Same Colored Symbol
|
||||
158419 - 0x09E6F (Left Row 6) - 0x09E6C - Arrows & Dots & Full Dots
|
||||
158420 - 0x09E6B (Left Row 7) - 0x09E6F - Arrows & Dots & Full Dots
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Triangles & Arrows
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Triangles & Arrows
|
||||
|
||||
Mountain Floor 1 Trash Pillar (Mountain Floor 1):
|
||||
|
||||
Mountain Floor 1 Back Section (Mountain Floor 1):
|
||||
158421 - 0x33AF5 (Back Row 1) - True - Symmetry & Triangles
|
||||
158422 - 0x33AF7 (Back Row 2) - 0x33AF5 - Triangles
|
||||
158423 - 0x09F6E (Back Row 3) - 0x33AF7 - Symmetry & Triangles
|
||||
158424 - 0x09EAD (Trash Pillar 1) - True - Triangles & Arrows
|
||||
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Triangles & Arrows
|
||||
|
||||
Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
|
||||
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
|
||||
@@ -1098,14 +1114,16 @@ Elevator (Mountain Bottom Floor):
|
||||
Mountain Pink Bridge EP (Mountain Floor 2):
|
||||
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
|
||||
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
|
||||
Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D - Caves Entry Door - TrueOneWay:
|
||||
158447 - 0x00FF8 (Caves Entry Panel) - True - Black/White Squares & Arrows & Triangles
|
||||
Door - 0x2D77D (Caves Entry) - 0x00FF8
|
||||
158448 - 0x334E1 (Rock Control) - True - True
|
||||
|
||||
==Caves==
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
|
||||
Caves Entry Door (Caves):
|
||||
|
||||
Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5 - Caves Entry Door - TrueOneWay:
|
||||
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares
|
||||
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares
|
||||
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots
|
||||
@@ -1219,3 +1237,7 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
|
||||
159521 - 0x33879 (Tutorial Reflection EP) - True - True
|
||||
159522 - 0x03C19 (Tutorial Moss EP) - True - True
|
||||
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
|
||||
|
||||
==Easter Eggs==
|
||||
|
||||
Easter Eggs (Easter Eggs) - Entry - True:
|
||||
|
75
worlds/witness/data/settings/easter_eggs.py
Normal file
75
worlds/witness/data/settings/easter_eggs.py
Normal file
@@ -0,0 +1,75 @@
|
||||
MAXIMUM_EASTER_EGG_CHECKS = 50
|
||||
|
||||
EASTER_EGGS = {
|
||||
"Tutorial": 1,
|
||||
"Outside Tutorial": 4,
|
||||
"Outside Tutorial Path To Outpost": 1,
|
||||
"Outside Tutorial Outpost": 1,
|
||||
"Orchard Beyond First Gate": 1,
|
||||
"Orchard End": 1,
|
||||
"Inside Glass Factory": 2,
|
||||
"Symmetry Island Lower": 2,
|
||||
"Symmetry Island Upper": 1,
|
||||
"Desert Outside": 6,
|
||||
"Desert Vault": 1,
|
||||
"Desert Pond Room": 2,
|
||||
"Desert Flood Room Underwater": 1,
|
||||
"Desert Elevator Room": 1,
|
||||
"Outside Quarry": 2,
|
||||
"Quarry": 5,
|
||||
"Quarry Stoneworks Upper Floor": 2,
|
||||
"Quarry Boathouse": 2,
|
||||
"Shadows": 2,
|
||||
"Shadows Ledge": 1,
|
||||
"Shadows Laser Room": 1,
|
||||
"Keep": 1,
|
||||
"Keep 3rd Maze": 3,
|
||||
"Keep 2nd Pressure Plate": 2,
|
||||
"Keep 3rd Pressure Plate": 1,
|
||||
"Keep 4th Pressure Plate": 1,
|
||||
"Keep Tower": 2,
|
||||
"Shipwreck": 5,
|
||||
"Inside Monastery": 1,
|
||||
"Monastery North Shutters": 1,
|
||||
"Monastery Garden": 1,
|
||||
"Town": 5,
|
||||
"Town Wooden Rooftop": 1,
|
||||
"Town RGB House": 2,
|
||||
"Town Tower Top": 1,
|
||||
"Windmill Interior": 2,
|
||||
"Theater": 1,
|
||||
"Southern Peninsula": 5,
|
||||
"Jungle": 2,
|
||||
"Jungle Under Popup Wall": 1,
|
||||
"Jungle Vault": 1,
|
||||
"Outside Bunker": 3,
|
||||
"Bunker Glass Room": 1,
|
||||
"Bunker Under Elevator": 1,
|
||||
"Bunker Green Room": 1,
|
||||
"Outside Swamp": 2,
|
||||
"Swamp Entry Area": 1,
|
||||
"Swamp Platform": 1,
|
||||
"Swamp Cyan Underwater": 1,
|
||||
"Swamp Near Boat": 1,
|
||||
"Swamp Laser Area": 1,
|
||||
"Treehouse Beach": 1,
|
||||
"Treehouse Yellow Bridge": 1,
|
||||
"Treehouse Junction": 2,
|
||||
"Treehouse Second Purple Bridge": 1,
|
||||
"Treehouse Green Bridge Left House": 1,
|
||||
"Treehouse Laser Room Back Platform": 1,
|
||||
"Treehouse Burned House": 1,
|
||||
"Treehouse Drawbridge Platform": 1,
|
||||
"Mountainside": 4,
|
||||
"Mountaintop": 1,
|
||||
"Mountain Floor 1 Trash Pillar": 1,
|
||||
"Mountain Floor 1 Back Section": 1,
|
||||
"Mountain Floor 2": 1,
|
||||
"Mountain Bottom Floor Pillars Room": 1,
|
||||
"Caves Entry Door": 1,
|
||||
"Caves": 2,
|
||||
"Caves Path to Challenge": 1,
|
||||
"Challenge": 2,
|
||||
"Tunnels": 2,
|
||||
"The Ocean": 2,
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
from collections import defaultdict
|
||||
from collections import Counter, defaultdict
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from Utils import cache_argsless
|
||||
@@ -11,6 +11,7 @@ from .item_definition_classes import (
|
||||
ProgressiveItemDefinition,
|
||||
WeightedItemDefinition,
|
||||
)
|
||||
from .settings.easter_eggs import EASTER_EGGS
|
||||
from .utils import (
|
||||
WitnessRule,
|
||||
define_new_region,
|
||||
@@ -49,6 +50,70 @@ class StaticWitnessLogicObj:
|
||||
self.reverse_connections()
|
||||
self.combine_connections()
|
||||
|
||||
def add_easter_eggs(self) -> None:
|
||||
egg_counter = 0
|
||||
area_counts: Dict[str, int] = Counter()
|
||||
for region_name, entity_amount in EASTER_EGGS.items():
|
||||
region_object = self.ALL_REGIONS_BY_NAME[region_name]
|
||||
correct_area = region_object["area"]
|
||||
|
||||
for _ in range(entity_amount):
|
||||
location_id = 160200 + egg_counter
|
||||
entity_hex = hex(0xEE000 + egg_counter)
|
||||
egg_counter += 1
|
||||
|
||||
area_counts[correct_area["name"]] += 1
|
||||
full_entity_name = f"{correct_area['name']} Easter Egg {area_counts[correct_area['name']]}"
|
||||
|
||||
self.ENTITIES_BY_HEX[entity_hex] = {
|
||||
"checkName": full_entity_name,
|
||||
"entity_hex": entity_hex,
|
||||
"region": region_object,
|
||||
"id": int(location_id),
|
||||
"entityType": "Easter Egg",
|
||||
"locationType": "Easter Egg",
|
||||
"area": correct_area,
|
||||
"order": len(self.ENTITIES_BY_HEX),
|
||||
}
|
||||
|
||||
self.ENTITIES_BY_NAME[self.ENTITIES_BY_HEX[entity_hex]["checkName"]] = self.ENTITIES_BY_HEX[entity_hex]
|
||||
|
||||
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[entity_hex] = {
|
||||
"entities": frozenset({frozenset({})})
|
||||
}
|
||||
region_object["entities"].append(entity_hex)
|
||||
region_object["physical_entities"].append(entity_hex)
|
||||
|
||||
easter_egg_region = self.ALL_REGIONS_BY_NAME["Easter Eggs"]
|
||||
easter_egg_area = easter_egg_region["area"]
|
||||
for i in range(sum(EASTER_EGGS.values())):
|
||||
location_id = 160000 + i
|
||||
entity_hex = hex(0xEE200 + i)
|
||||
|
||||
if i == 0:
|
||||
continue
|
||||
|
||||
full_entity_name = f"{i + 1} Easter Eggs Collected"
|
||||
|
||||
self.ENTITIES_BY_HEX[entity_hex] = {
|
||||
"checkName": full_entity_name,
|
||||
"entity_hex": entity_hex,
|
||||
"region": easter_egg_region,
|
||||
"id": int(location_id),
|
||||
"entityType": "Easter Egg Total",
|
||||
"locationType": "Easter Egg Total",
|
||||
"area": easter_egg_area,
|
||||
"order": len(self.ENTITIES_BY_HEX),
|
||||
}
|
||||
|
||||
self.ENTITIES_BY_NAME[self.ENTITIES_BY_HEX[entity_hex]["checkName"]] = self.ENTITIES_BY_HEX[entity_hex]
|
||||
|
||||
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[entity_hex] = {
|
||||
"entities": frozenset({frozenset({})})
|
||||
}
|
||||
easter_egg_region["entities"].append(entity_hex)
|
||||
easter_egg_region["physical_entities"].append(entity_hex)
|
||||
|
||||
def read_logic_file(self, lines: List[str]) -> None:
|
||||
"""
|
||||
Reads the logic file and does the initial population of data structures
|
||||
@@ -66,7 +131,7 @@ class StaticWitnessLogicObj:
|
||||
continue
|
||||
|
||||
if line[-1] == ":":
|
||||
new_region_and_connections = define_new_region(line)
|
||||
new_region_and_connections = define_new_region(line, current_area)
|
||||
current_region = new_region_and_connections[0]
|
||||
region_name = current_region["name"]
|
||||
self.ALL_REGIONS_BY_NAME[region_name] = current_region
|
||||
@@ -198,6 +263,8 @@ class StaticWitnessLogicObj:
|
||||
current_region["entities"].append(entity_hex)
|
||||
current_region["physical_entities"].append(entity_hex)
|
||||
|
||||
self.add_easter_eggs()
|
||||
|
||||
def reverse_connection(self, source_region: str, connection: Tuple[str, Set[WitnessRule]]) -> None:
|
||||
target = connection[0]
|
||||
traversal_options = connection[1]
|
||||
|
@@ -1,3 +1,4 @@
|
||||
from datetime import date
|
||||
from math import floor
|
||||
from pkgutil import get_data
|
||||
from random import Random
|
||||
@@ -61,7 +62,7 @@ def build_weighted_int_list(inputs: Collection[float], total: int) -> List[int]:
|
||||
return rounded_output
|
||||
|
||||
|
||||
def define_new_region(region_string: str) -> Tuple[Dict[str, Any], Set[Tuple[str, WitnessRule]]]:
|
||||
def define_new_region(region_string: str, area: dict[str, Any]) -> Tuple[Dict[str, Any], Set[Tuple[str, WitnessRule]]]:
|
||||
"""
|
||||
Returns a region object by parsing a line in the logic file
|
||||
"""
|
||||
@@ -91,6 +92,7 @@ def define_new_region(region_string: str) -> Tuple[Dict[str, Any], Set[Tuple[str
|
||||
"shortName": region_name_simple,
|
||||
"entities": [],
|
||||
"physical_entities": [],
|
||||
"area": area,
|
||||
}
|
||||
return region_obj, options
|
||||
|
||||
@@ -264,3 +266,15 @@ def logical_and_witness_rules(witness_rules: Iterable[WitnessRule]) -> WitnessRu
|
||||
|
||||
def logical_or_witness_rules(witness_rules: Iterable[WitnessRule]) -> WitnessRule:
|
||||
return optimize_witness_rule(frozenset.union(*witness_rules))
|
||||
|
||||
|
||||
def is_easter_time() -> bool:
|
||||
# dateutils would have been nice here, because it has an easter() function.
|
||||
# But adding it as a requirement seems heavier than necessary.
|
||||
# Thus, we just take a range from the earliest to latest possible easter dates.
|
||||
|
||||
today = date.today()
|
||||
earliest_easter_day = date(today.year, 3, 20) # Earliest possible is 3/22 + 2 day buffer for Good Friday
|
||||
last_easter_day = date(today.year, 4, 26) # Latest possible is 4/25 + 1 day buffer for Easter Monday
|
||||
|
||||
return earliest_easter_day <= today <= last_easter_day
|
||||
|
@@ -43,3 +43,12 @@ if __name__ == "__main__":
|
||||
)
|
||||
)
|
||||
datafile.write("\n};\n\n")
|
||||
|
||||
datafile.write("inline std::map<int, std::string> entityToName = {")
|
||||
datafile.write(
|
||||
"\n".join(
|
||||
"\t{ " + entity_hex + ', "' + entity_object["checkName"] + '" },'
|
||||
for entity_hex, entity_object in static_witness_logic.ENTITIES_BY_HEX.items()
|
||||
)
|
||||
)
|
||||
datafile.write("\n};\n\n")
|
||||
|
@@ -241,7 +241,10 @@ def word_direct_hint(world: "WitnessWorld", hint: WitnessLocationHint) -> Witnes
|
||||
area = chosen_group
|
||||
|
||||
# local locations should only ever return a location group, as Witness defines groups for every location.
|
||||
hint_text = f"{item_name} can be found in the {area} area."
|
||||
if area == "Easter Eggs":
|
||||
hint_text = f"{item_name} can be found by collecting Easter Eggs."
|
||||
else:
|
||||
hint_text = f"{item_name} can be found in the {area} area."
|
||||
else:
|
||||
player_name = world.multiworld.get_player_name(hint.location.player)
|
||||
|
||||
@@ -505,10 +508,13 @@ def word_area_hint(world: "WitnessWorld", hinted_area: str, area_items: List[Ite
|
||||
|
||||
area_progression_word = "Both" if total_progression == 2 else "All"
|
||||
|
||||
hint_string = f"In the {hinted_area} area, you will find "
|
||||
if hinted_area == "Easter Eggs":
|
||||
hint_string = "Through collecting Easter Eggs, you will find "
|
||||
else:
|
||||
hint_string = f"In the {hinted_area} area, you will find "
|
||||
|
||||
hunt_panels = None
|
||||
if world.options.victory_condition == "panel_hunt":
|
||||
if world.options.victory_condition == "panel_hunt" and hinted_area != "Easter Eggs":
|
||||
hunt_panels = sum(
|
||||
static_witness_logic.ENTITIES_BY_HEX[hunt_entity]["area"]["name"] == hinted_area
|
||||
for hunt_entity in world.player_logic.HUNT_ENTITIES
|
||||
|
@@ -19,7 +19,7 @@ class WitnessPlayerLocations:
|
||||
def __init__(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic) -> None:
|
||||
"""Defines locations AFTER logic changes due to options"""
|
||||
|
||||
self.PANEL_TYPES_TO_SHUFFLE = {"General", "Good Boi"}
|
||||
self.PANEL_TYPES_TO_SHUFFLE = {"General", "Good Boi", "Easter Egg Total"}
|
||||
self.CHECK_LOCATIONS = static_witness_locations.GENERAL_LOCATIONS.copy()
|
||||
|
||||
if world.options.shuffle_discarded_panels:
|
||||
|
@@ -1,4 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Tuple
|
||||
|
||||
from schema import And, Schema
|
||||
|
||||
@@ -18,6 +20,7 @@ from Options import (
|
||||
|
||||
from .data import static_logic as static_witness_logic
|
||||
from .data.item_definition_classes import ItemCategory, WeightedItemDefinition
|
||||
from .data.utils import is_easter_time
|
||||
from .entity_hunt import ALL_HUNTABLE_PANELS
|
||||
|
||||
|
||||
@@ -142,6 +145,53 @@ class ShuffleEnvironmentalPuzzles(Choice):
|
||||
option_obelisk_sides = 2
|
||||
|
||||
|
||||
class EasterEggHunt(Choice):
|
||||
"""
|
||||
Adds up to 120 Easter Eggs to the game, placed by NewSoupVi, Exempt-Medic, hatkirby, Scipio, and Rever.
|
||||
These can be collected by simply clicking on them.
|
||||
|
||||
The difficulty options differ by how many Eggs you need to collect for each check and how many are logically required for each check.
|
||||
|
||||
- "Easy": 3 / 8
|
||||
- "Normal": 3 / 6
|
||||
- "Hard": 4 / 6
|
||||
- "Very Hard": 4 / 5
|
||||
- "Extreme": 4 / 4 (You are expected to collect every Easter Egg)
|
||||
|
||||
Checks that require more Eggs than logically available still exist, but are excluded.
|
||||
For example, on "Easy", the "63 Eggs Collected" check can physically be obtained, but would logically require 125 Easter Eggs, which is impossible. Thus, it is excluded.
|
||||
|
||||
On "Easy", "Normal", and "Hard", you will start with an "Egg Radar" that you can activate using the Puzzle Skip key.
|
||||
On every difficulty except "Extreme", there will be a message when you've collected all Easter Eggs in an area.
|
||||
On "Easy", there will be an additional message after every Easter Egg telling you how many Easter Eggs are remaining in the area.
|
||||
|
||||
It is recommended that you play this mode together with Door Shuffle. Without it, more than half of the Easter Eggs will be in sphere 1.
|
||||
"""
|
||||
|
||||
visibility = Visibility.all if is_easter_time() else Visibility.none
|
||||
|
||||
display_name = "Easter Egg Hunt"
|
||||
option_off = 0
|
||||
# Number represents the amount of eggs needed per check
|
||||
option_easy = 1
|
||||
option_normal = 2
|
||||
option_hard = 3
|
||||
option_very_hard = 4
|
||||
option_extreme = 5
|
||||
default = 2 if is_easter_time() else 0
|
||||
|
||||
def get_step_and_logical_step(self) -> Tuple[int, int]:
|
||||
if self == "easy":
|
||||
return 3, 8
|
||||
if self == "normal":
|
||||
return 3, 6
|
||||
if self == "hard":
|
||||
return 4, 6
|
||||
if self == "very_hard":
|
||||
return 4, 5
|
||||
return 4, 4
|
||||
|
||||
|
||||
class ShuffleDog(Choice):
|
||||
"""
|
||||
Adds petting the dog statue in Town into the location pool.
|
||||
@@ -504,6 +554,7 @@ class TheWitnessOptions(PerGameCommonOptions):
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
puzzle_randomization_seed: PuzzleRandomizationSeed
|
||||
shuffle_dog: ShuffleDog
|
||||
easter_egg_hunt: EasterEggHunt
|
||||
|
||||
|
||||
witness_option_groups = [
|
||||
@@ -561,3 +612,13 @@ witness_option_groups = [
|
||||
ShuffleDog,
|
||||
])
|
||||
]
|
||||
|
||||
# Make sure that Easter Egg Hunt is VERY visible during easter time (when it's enabled by default)
|
||||
if is_easter_time():
|
||||
easter_special_option_group = OptionGroup("EASTER SPECIAL", [
|
||||
EasterEggHunt,
|
||||
])
|
||||
witness_option_groups = [easter_special_option_group, *witness_option_groups]
|
||||
else:
|
||||
silly_options_group = next(group for group in witness_option_groups if group.name == "Silly Options")
|
||||
silly_options_group.options.append(EasterEggHunt)
|
||||
|
@@ -31,6 +31,13 @@ class WitnessItem(Item):
|
||||
Item from the game The Witness
|
||||
"""
|
||||
game: str = "The Witness"
|
||||
eggs: int = 0
|
||||
|
||||
@classmethod
|
||||
def make_egg_event(cls, item_name: str, player: int):
|
||||
ret = cls(item_name, ItemClassification.progression, None, player)
|
||||
ret.eggs = int(item_name[1:].split(" ", 1)[0])
|
||||
return ret
|
||||
|
||||
|
||||
class WitnessPlayerItems:
|
||||
|
@@ -24,7 +24,6 @@ from .data.item_definition_classes import DoorItemDefinition, ItemCategory, Prog
|
||||
from .data.static_logic import StaticWitnessLogicObj
|
||||
from .data.utils import (
|
||||
WitnessRule,
|
||||
define_new_region,
|
||||
get_boat,
|
||||
get_caves_except_path_to_challenge_exclusion_list,
|
||||
get_complex_additional_panels,
|
||||
@@ -119,6 +118,8 @@ class WitnessPlayerLogic:
|
||||
self.PRE_PICKED_HUNT_ENTITIES: Set[str] = set()
|
||||
self.HUNT_ENTITIES: Set[str] = set()
|
||||
|
||||
self.AVAILABLE_EASTER_EGGS: Set[str] = set()
|
||||
self.AVAILABLE_EASTER_EGGS_PER_REGION: Dict[str, int] = {}
|
||||
self.ALWAYS_EVENT_NAMES_BY_HEX = {
|
||||
"0x00509": "+1 Laser",
|
||||
"0x012FB": "+1 Laser (Unredirected)",
|
||||
@@ -154,6 +155,9 @@ class WitnessPlayerLogic:
|
||||
picker = EntityHuntPicker(self, world, self.PRE_PICKED_HUNT_ENTITIES)
|
||||
self.HUNT_ENTITIES = picker.pick_panel_hunt_panels(world.options.panel_hunt_total.value)
|
||||
|
||||
if world.options.easter_egg_hunt:
|
||||
self.finalize_easter_eggs(world)
|
||||
|
||||
# Finalize which items actually exist in the MultiWorld and which get grouped into progressive items.
|
||||
self.finalize_items()
|
||||
|
||||
@@ -241,6 +245,8 @@ class WitnessPlayerLogic:
|
||||
if option_entity in {"7 Lasers", "11 Lasers", "7 Lasers + Redirect", "11 Lasers + Redirect",
|
||||
"PP2 Weirdness", "Theater to Tunnels", "Entity Hunt"}:
|
||||
new_items = frozenset({frozenset([option_entity])})
|
||||
elif "Eggs" in option_entity:
|
||||
new_items = frozenset({frozenset([option_entity])})
|
||||
elif option_entity in self.DISABLE_EVERYTHING_BEHIND:
|
||||
new_items = frozenset()
|
||||
else:
|
||||
@@ -387,13 +393,6 @@ class WitnessPlayerLogic:
|
||||
|
||||
return
|
||||
|
||||
if adj_type == "Region Changes":
|
||||
new_region_and_options = define_new_region(line + ":")
|
||||
|
||||
self.CONNECTIONS_BY_REGION_NAME_THEORETICAL[new_region_and_options[0]["name"]] = new_region_and_options[1]
|
||||
|
||||
return
|
||||
|
||||
if adj_type == "New Connections":
|
||||
line_split = line.split(" - ")
|
||||
source_region = line_split[0]
|
||||
@@ -533,6 +532,55 @@ class WitnessPlayerLogic:
|
||||
|
||||
return postgame_adjustments
|
||||
|
||||
def set_easter_egg_requirements(self, world: "WitnessWorld") -> None:
|
||||
eggs_per_check, logically_required_eggs_per_check = world.options.easter_egg_hunt.get_step_and_logical_step()
|
||||
|
||||
for entity_hex, entity_obj in static_witness_logic.ENTITIES_BY_HEX.items():
|
||||
if entity_obj["entityType"] != "Easter Egg Total":
|
||||
continue
|
||||
|
||||
direct_egg_count = int(entity_obj["checkName"].split(" ")[0])
|
||||
|
||||
if direct_egg_count % eggs_per_check:
|
||||
self.COMPLETELY_DISABLED_ENTITIES.add(entity_hex)
|
||||
|
||||
requirement = direct_egg_count // eggs_per_check * logically_required_eggs_per_check
|
||||
self.DEPENDENT_REQUIREMENTS_BY_HEX[entity_hex] = {
|
||||
"entities": frozenset({frozenset({f"{requirement} Eggs"})})
|
||||
}
|
||||
|
||||
def finalize_easter_eggs(self, world: "WitnessWorld") -> None:
|
||||
self.AVAILABLE_EASTER_EGGS = {
|
||||
entity_hex for entity_hex, entity_obj in static_witness_logic.ENTITIES_BY_HEX.items()
|
||||
if entity_obj["entityType"] == "Easter Egg" and self.solvability_guaranteed(entity_hex)
|
||||
}
|
||||
max_eggs = len(self.AVAILABLE_EASTER_EGGS)
|
||||
|
||||
self.AVAILABLE_EASTER_EGGS_PER_REGION = defaultdict(int)
|
||||
for entity_hex in self.AVAILABLE_EASTER_EGGS:
|
||||
region_name = static_witness_logic.ENTITIES_BY_HEX[entity_hex]["region"]["name"]
|
||||
self.AVAILABLE_EASTER_EGGS_PER_REGION[region_name] += 1
|
||||
|
||||
eggs_per_check, logically_required_eggs_per_check = world.options.easter_egg_hunt.get_step_and_logical_step()
|
||||
|
||||
for entity_hex, entity_obj in static_witness_logic.ENTITIES_BY_HEX.items():
|
||||
if entity_obj["entityType"] != "Easter Egg Total":
|
||||
continue
|
||||
if entity_hex in self.COMPLETELY_DISABLED_ENTITIES:
|
||||
continue
|
||||
|
||||
direct_egg_count = int(entity_obj["checkName"].split(" ", 1)[0])
|
||||
logically_required_egg_count = direct_egg_count // eggs_per_check * logically_required_eggs_per_check
|
||||
if direct_egg_count > max_eggs:
|
||||
self.COMPLETELY_DISABLED_ENTITIES.add(entity_hex)
|
||||
continue
|
||||
|
||||
self.ADDED_CHECKS.add(entity_obj["checkName"])
|
||||
if logically_required_egg_count > max_eggs:
|
||||
# Exclude and set logic to require every egg
|
||||
self.EXCLUDED_ENTITIES.add(entity_hex)
|
||||
self.REQUIREMENTS_BY_HEX[entity_hex] = frozenset({frozenset({f"{max_eggs} Eggs"})})
|
||||
|
||||
def make_options_adjustments(self, world: "WitnessWorld") -> None:
|
||||
"""Makes logic adjustments based on options"""
|
||||
adjustment_linesets_in_order = []
|
||||
@@ -641,6 +689,8 @@ class WitnessPlayerLogic:
|
||||
adjustment_linesets_in_order.append([
|
||||
"New Connections:",
|
||||
"Outside Bunker - Bunker Elevator - TrueOneWay",
|
||||
"Bunker Elevator Section - Bunker Under Elevator - "
|
||||
"0x0A079 | Bunker Green Room | Bunker Cyan Room | Bunker Laser Platform | Outside Bunker",
|
||||
])
|
||||
if "Swamp Long Bridge" in world.options.elevators_come_to_you:
|
||||
adjustment_linesets_in_order.append([
|
||||
@@ -655,6 +705,14 @@ class WitnessPlayerLogic:
|
||||
# "New Connections:"
|
||||
# "Town Red Rooftop - Town Maze Rooftop - TrueOneWay"
|
||||
|
||||
if world.options.easter_egg_hunt:
|
||||
self.set_easter_egg_requirements(world)
|
||||
else:
|
||||
self.COMPLETELY_DISABLED_ENTITIES.update({
|
||||
entity_hex for entity_hex, entity_obj in static_witness_logic.ENTITIES_BY_HEX.items()
|
||||
if "Easter Egg" in entity_obj["entityType"]
|
||||
})
|
||||
|
||||
if world.options.victory_condition == "panel_hunt":
|
||||
adjustment_linesets_in_order.append(get_entity_hunt())
|
||||
|
||||
@@ -691,6 +749,9 @@ class WitnessPlayerLogic:
|
||||
if loc_obj["entityType"] == "EP":
|
||||
self.COMPLETELY_DISABLED_ENTITIES.add(loc_obj["entity_hex"])
|
||||
|
||||
if loc_obj["entityType"] == "Easter Egg":
|
||||
self.COMPLETELY_DISABLED_ENTITIES.add(loc_obj["entity_hex"])
|
||||
|
||||
elif loc_obj["entityType"] == "Panel":
|
||||
self.EXCLUDED_ENTITIES.add(loc_obj["entity_hex"])
|
||||
|
||||
@@ -937,6 +998,7 @@ class WitnessPlayerLogic:
|
||||
doors = world.options.shuffle_doors
|
||||
shortbox_req = world.options.mountain_lasers
|
||||
longbox_req = world.options.challenge_lasers
|
||||
eggs_exist = world.options.easter_egg_hunt
|
||||
|
||||
swamp_bridge_comes_to_you = "Swamp Long Bridge" in world.options.elevators_come_to_you
|
||||
quarry_elevator_comes_to_you = "Quarry Elevator" in world.options.elevators_come_to_you
|
||||
@@ -953,17 +1015,17 @@ class WitnessPlayerLogic:
|
||||
# It is easier to think about when these items *are* required, so we make that dict first
|
||||
# If the entity is disabled anyway, we don't need to consider that case
|
||||
is_item_required_dict = {
|
||||
"0x03750": eps_shuffled, # Monastery Garden Entry Door
|
||||
"0x03750": eps_shuffled or eggs_exist, # Monastery Garden Entry Door
|
||||
"0x275FA": eps_shuffled, # Boathouse Hook Control
|
||||
"0x17D02": eps_shuffled, # Windmill Turn Control
|
||||
"0x0368A": symbols_shuffled or door_panels, # Quarry Stoneworks Stairs Door
|
||||
"0x3865F": symbols_shuffled or door_panels or eps_shuffled, # Quarry Boathouse 2nd Barrier
|
||||
"0x17CC4": quarry_elevator_comes_to_you or eps_shuffled, # Quarry Elevator Panel
|
||||
"0x17E2B": swamp_bridge_comes_to_you and boat_shuffled or eps_shuffled, # Swamp Long Bridge
|
||||
"0x0CF2A": False, # Jungle Monastery Garden Shortcut
|
||||
"0x0CF2A": eggs_exist, # Jungle Monastery Garden Shortcut
|
||||
"0x0364E": False, # Monastery Laser Shortcut Door
|
||||
"0x03713": remote_doors, # Monastery Laser Shortcut Panel
|
||||
"0x03313": False, # Orchard Second Gate
|
||||
"0x03313": eggs_exist, # Orchard Second Gate
|
||||
"0x337FA": remote_doors, # Jungle Bamboo Laser Shortcut Panel
|
||||
"0x3873B": False, # Jungle Bamboo Laser Shortcut Door
|
||||
"0x335AB": False, # Caves Elevator Controls
|
||||
@@ -1026,4 +1088,9 @@ class WitnessPlayerLogic:
|
||||
entity_name = entity_obj["checkName"]
|
||||
self.EVENT_ITEM_PAIRS[entity_name + " (Panel Hunt)"] = ("+1 Panel Hunt", entity_hex)
|
||||
|
||||
for region_name, easter_egg_count in self.AVAILABLE_EASTER_EGGS_PER_REGION.items():
|
||||
plural = "s" if easter_egg_count != 1 else ""
|
||||
event_name = f"+{easter_egg_count} Easter Egg{plural}"
|
||||
self.EVENT_ITEM_PAIRS[f"{region_name} Easter Egg{plural}"] = (event_name, region_name)
|
||||
|
||||
return
|
||||
|
@@ -117,12 +117,17 @@ class WitnessPlayerRegions:
|
||||
event_locations_per_region = defaultdict(dict)
|
||||
|
||||
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"
|
||||
entity_or_region = event_item_and_entity[1]
|
||||
if entity_or_region in static_witness_logic.ALL_REGIONS_BY_NAME:
|
||||
region_name = entity_or_region
|
||||
order = -1
|
||||
else:
|
||||
region_name = region["name"]
|
||||
order = self.reference_logic.ENTITIES_BY_HEX[event_item_and_entity[1]]["order"]
|
||||
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"]
|
||||
order = self.reference_logic.ENTITIES_BY_HEX[entity_or_region]["order"]
|
||||
event_locations_per_region[region_name][event_location] = order
|
||||
|
||||
for region_name, region in regions_to_create.items():
|
||||
|
@@ -2,7 +2,7 @@ line-length = 120
|
||||
|
||||
[lint]
|
||||
select = ["C", "E", "F", "R", "W", "I", "N", "Q", "UP", "RUF", "ISC", "T20"]
|
||||
ignore = ["C9", "RUF012", "RUF100"]
|
||||
ignore = ["C9", "RUF012", "RUF021", "RUF100", "UP006", "UP035"]
|
||||
|
||||
[lint.per-file-ignores]
|
||||
# The way options definitions work right now, I am forced to break line length requirements.
|
||||
|
@@ -196,6 +196,8 @@ def _has_item(item: str, world: "WitnessWorld",
|
||||
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 "Eggs" in item:
|
||||
return SimpleItemRepresentation("Egg", int(item.split(" ")[0]))
|
||||
if item == "PP2 Weirdness":
|
||||
return lambda state: _can_do_expert_pp2(state, world)
|
||||
if item == "Theater to Tunnels":
|
||||
@@ -303,6 +305,11 @@ def make_lambda(entity_hex: str, world: "WitnessWorld") -> Optional[CollectionRu
|
||||
return _meets_item_requirements(entity_req, world)
|
||||
|
||||
|
||||
def make_region_lambda(region_name: str, world: "WitnessWorld") -> CollectionRule:
|
||||
region = world.get_region(region_name)
|
||||
return lambda state: region.can_reach(state)
|
||||
|
||||
|
||||
def set_rules(world: "WitnessWorld") -> None:
|
||||
"""
|
||||
Sets all rules for all locations
|
||||
@@ -312,8 +319,12 @@ def set_rules(world: "WitnessWorld") -> None:
|
||||
real_location = location
|
||||
|
||||
if location in world.player_locations.EVENT_LOCATION_TABLE:
|
||||
entity_hex = world.player_logic.EVENT_ITEM_PAIRS[location][1]
|
||||
real_location = static_witness_logic.ENTITIES_BY_HEX[entity_hex]["checkName"]
|
||||
entity_hex_or_region_name = world.player_logic.EVENT_ITEM_PAIRS[location][1]
|
||||
if entity_hex_or_region_name in static_witness_logic.ALL_REGIONS_BY_NAME:
|
||||
set_rule(world.get_location(location), make_region_lambda(entity_hex_or_region_name, world))
|
||||
continue
|
||||
|
||||
real_location = static_witness_logic.ENTITIES_BY_HEX[entity_hex_or_region_name]["checkName"]
|
||||
|
||||
associated_entity = world.player_logic.REFERENCE_LOGIC.ENTITIES_BY_NAME[real_location]
|
||||
entity_hex = associated_entity["entity_hex"]
|
||||
|
155
worlds/witness/test/test_easter_egg_shuffle.py
Normal file
155
worlds/witness/test/test_easter_egg_shuffle.py
Normal file
@@ -0,0 +1,155 @@
|
||||
from typing import cast
|
||||
|
||||
from BaseClasses import LocationProgressType
|
||||
|
||||
from .. import WitnessWorld
|
||||
from ..test import WitnessMultiworldTestBase
|
||||
|
||||
|
||||
class TestEasterEggShuffle(WitnessMultiworldTestBase):
|
||||
options_per_world = [
|
||||
{
|
||||
"easter_egg_hunt": "off",
|
||||
},
|
||||
{
|
||||
"easter_egg_hunt": "easy",
|
||||
},
|
||||
{
|
||||
"easter_egg_hunt": "normal",
|
||||
},
|
||||
{
|
||||
"easter_egg_hunt": "hard",
|
||||
},
|
||||
{
|
||||
"easter_egg_hunt": "very_hard",
|
||||
},
|
||||
{
|
||||
"easter_egg_hunt": "extreme",
|
||||
},
|
||||
]
|
||||
|
||||
def test_easter_egg_hunt(self) -> None:
|
||||
with self.subTest("Test that player without Easter Egg Hunt has no easter egg related locations"):
|
||||
egg_locations = {location for location in self.multiworld.get_locations(1) if "Egg" in location.name}
|
||||
self.assertFalse(egg_locations)
|
||||
|
||||
for player, eggs_per_check, logical_eggs_per_check in zip([2, 3, 4, 5, 6], [3, 3, 4, 4, 4], [8, 6, 6, 5, 4]):
|
||||
world = cast(WitnessWorld, self.multiworld.worlds[player])
|
||||
option_name = world.options.easter_egg_hunt
|
||||
|
||||
with self.subTest(f"Test that {option_name} Egg Hunt player starts with 0 eggs"):
|
||||
self.assertEqual(self.multiworld.state.count("Egg", player), 0)
|
||||
|
||||
with self.subTest(f"Test that the correct Egg Collection locations exist for {option_name} player"):
|
||||
first_egg_location = f"{eggs_per_check} Easter Eggs Collected"
|
||||
one_less_location = f"{eggs_per_check - 1} Easter Eggs Collected"
|
||||
one_more_location = f"{eggs_per_check + 1} Easter Eggs Collected"
|
||||
self.assert_location_exists(first_egg_location, player)
|
||||
self.assert_location_does_not_exist(one_less_location, player, strict_check=False)
|
||||
self.assert_location_does_not_exist(one_more_location, player, strict_check=False)
|
||||
|
||||
one_too_few = logical_eggs_per_check - 1
|
||||
with self.subTest(f'Test that "+{one_too_few} Easter Eggs" item adds 4 easter eggs'):
|
||||
item = world.create_item(f"+{one_too_few} Easter Eggs")
|
||||
self.multiworld.state.collect(item, prevent_sweep=True)
|
||||
self.assertEqual(self.multiworld.state.count("Egg", player), one_too_few)
|
||||
|
||||
with self.subTest(
|
||||
f"Test that {one_too_few} Easter Eggs are not enough for {option_name} player's first location"
|
||||
):
|
||||
self.assertFalse(self.multiworld.state.can_reach_location(first_egg_location, player))
|
||||
|
||||
with self.subTest(
|
||||
f"Test that {logical_eggs_per_check} Easter Eggs are enough for {option_name} player's first location"
|
||||
):
|
||||
item = world.create_item("+1 Easter Egg")
|
||||
self.multiworld.state.collect(item, prevent_sweep=True)
|
||||
self.assertTrue(self.multiworld.state.can_reach_location(first_egg_location, player))
|
||||
|
||||
|
||||
class TestEggRestrictions(WitnessMultiworldTestBase):
|
||||
options_per_world = [
|
||||
{
|
||||
"shuffle_postgame": False,
|
||||
},
|
||||
{
|
||||
"shuffle_postgame": True,
|
||||
},
|
||||
{
|
||||
"shuffle_postgame": True,
|
||||
"exclude_locations": frozenset({"Bunker Easter Egg 3"}),
|
||||
}
|
||||
]
|
||||
|
||||
common_options = {
|
||||
"victory_condition": "mountain_box_short",
|
||||
"shuffle_doors": "off",
|
||||
"easter_egg_hunt": "very_hard",
|
||||
"shuffle_vault_boxes": True,
|
||||
}
|
||||
|
||||
def test_egg_restrictions(self) -> None:
|
||||
with self.subTest("Test that locations beyond 108 Easter Eggs don't exist for a seed without Mountain"):
|
||||
self.assert_location_exists("108 Easter Eggs Collected", 1)
|
||||
self.assert_location_does_not_exist("112 Easter Eggs Collected", 1)
|
||||
|
||||
with self.subTest(
|
||||
"Test that locations beyond 86 Easter Eggs, which would logically require more than 108 Eggs, are excluded"
|
||||
):
|
||||
egg_84_location = self.multiworld.get_location("84 Easter Eggs Collected", 1)
|
||||
egg_88_location = self.multiworld.get_location("88 Easter Eggs Collected", 1)
|
||||
|
||||
self.assertNotEqual(egg_84_location.progress_type, LocationProgressType.EXCLUDED)
|
||||
self.assertEqual(egg_88_location.progress_type, LocationProgressType.EXCLUDED)
|
||||
|
||||
with self.subTest("Test that in a seed with the whole game included, the 120 egg location exists"):
|
||||
self.assert_location_exists("120 Easter Eggs Collected", 2)
|
||||
|
||||
with self.subTest(
|
||||
"Test that locations beyond 96 Easter Eggs, which would logically require more than 120 Eggs, are excluded"
|
||||
):
|
||||
egg_96_location = self.multiworld.get_location("96 Easter Eggs Collected", 2)
|
||||
egg_100_location = self.multiworld.get_location("100 Easter Eggs Collected", 2)
|
||||
|
||||
self.assertNotEqual(egg_96_location.progress_type, LocationProgressType.EXCLUDED)
|
||||
self.assertEqual(egg_100_location.progress_type, LocationProgressType.EXCLUDED)
|
||||
|
||||
with self.subTest("Test that you can exclude and egg to disable it"):
|
||||
self.assert_location_exists("116 Easter Eggs Collected", 3)
|
||||
self.assert_location_does_not_exist("120 Easter Eggs Collected", 3)
|
||||
|
||||
|
||||
class TestBunkerElevatorEgg(WitnessMultiworldTestBase):
|
||||
options_per_world = [
|
||||
{
|
||||
"elevators_come_to_you": frozenset()
|
||||
},
|
||||
{
|
||||
"elevators_come_to_you": frozenset({"Bunker Elevator"})
|
||||
},
|
||||
]
|
||||
|
||||
common_options = {
|
||||
"easter_egg_hunt": "normal",
|
||||
"shuffle_doors": "panels",
|
||||
"shuffle_symbols": False,
|
||||
}
|
||||
|
||||
def test_bunker_elevator_egg(self) -> None:
|
||||
items_to_reach_bunker_elevator = [
|
||||
"Bunker Entry (Panel)",
|
||||
"Bunker Tinted Glass Door (Panel)",
|
||||
"Bunker Drop-Down Door Controls (Panel)"
|
||||
]
|
||||
|
||||
with self.subTest("Test that normally, the egg behind the elevator needs Elevator Control"):
|
||||
self.assertFalse(self.multiworld.state.can_reach_location("Bunker Under Elevator Easter Egg", 1))
|
||||
self.collect_by_name(items_to_reach_bunker_elevator, 1)
|
||||
self.assertFalse(self.multiworld.state.can_reach_location("Bunker Under Elevator Easter Egg", 1))
|
||||
self.collect_by_name(["Bunker Elevator Control (Panel)"], 1)
|
||||
self.assertTrue(self.multiworld.state.can_reach_location("Bunker Under Elevator Easter Egg", 1))
|
||||
|
||||
with self.subTest("Test that with auto-elevators, the egg behind the elevator doesn't need Elevator Control"):
|
||||
self.assertFalse(self.multiworld.state.can_reach_location("Bunker Under Elevator Easter Egg", 2))
|
||||
self.collect_by_name(items_to_reach_bunker_elevator, 2)
|
||||
self.assertTrue(self.multiworld.state.can_reach_location("Bunker Under Elevator Easter Egg", 2))
|
Reference in New Issue
Block a user