Add The Witness (#467)

* Added The Witness


Co-authored-by: metzner <unconfigured@null.spigotmc.org>
Co-authored-by: Jarno Westhof <jarnowesthof@gmail.com>
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
NewSoupVi 2022-04-29 00:42:11 +02:00 committed by GitHub
parent 50eaf712a9
commit 3e8c821c02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2272 additions and 1 deletions

View File

@ -23,6 +23,7 @@ Currently, the following games are supported:
* ChecksFinder
* ArchipIDLE
* Hollow Knight
* The Witness
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled

View File

@ -0,0 +1,28 @@
# The Witness
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Puzzles are randomly generated using the popular [Sigma Rando](https://github.com/sigma144/witness-randomizer).
They are made to be similar to the original game, but with different solutions.
Ontop of that each puzzle symbol (Squares, Stars, Dots, etc.) is now an item.
Panels with puzzle symbols on them are now locked initially.
## What is a "check" in The Witness?
Solving the last panel in a row of panels or an important standalone panel will count as a check, and send out an item.
## What "items" can you unlock in The Witness?
Every puzzle symbol and many other puzzle mechanics are items.
This includes symbols such as "Dots", "Black/White Squares", "Colored Squares", "Stars", "Symmetry", "Shapers" (coll. "Tetris Pieces"), "Erasers" and many more.
## The Jungle, Orchard, Forest and Color House aren't randomized. What gives?
There are limitations to what can currently be randomized in The Witness.
There is an option to turn these non-randomized panels off, called "disable_non_randomized" in your yaml file. This will also slightly change the activation requirement of certain panels, detailed [here](https://github.com/sigma144/witness-randomizer/wiki/Activation-Triggers).

View File

@ -0,0 +1,26 @@
# The Witness Randomizer Setup
## Required Software
- [The Witness (Steam)](https://store.steampowered.com/app/210970/The_Witness/)
- [The Witness Archipalego Randomizer](https://github.com/JarnoWesthof/The-Witness-Randomizer-for-Archipelago)
- [ArchipelagoTextClient](https://github.com/ArchipelagoMW/Archipelago/releases)
## Joining a MultiWorld Game
This Randomizer can be very "moody" if you don't do everything in the correct order.
It is recommended to do every single one of these steps when you connect to a world.
1. Launch The Witness
2. Start a fresh save (unless you have absolutely no other choice)
3. Do not move
4. Launch [The Witness Archipalego Randomizer](https://github.com/JarnoWesthof/The-Witness-Randomizer-for-Archipelago)
5. Enter the Archipelago Adress, Slot Name and Password
6. Press "Randomize"
7. Wait for the randomization to fully finish before moving in-game
That's it! Have fun!
## ArchipelagoTextClient
Its recommended to have Archipelago's Text Client open on the side to keep track of what item you receive and send as The Witness has no in-game messages.

View File

@ -574,5 +574,24 @@
]
}
]
},
{
"gameTitle": "The Witness",
"tutorials": [
{
"name": "Multiworld Setup Guide",
"description": "A guide to playing The Witness with Archipelago.",
"files": [
{
"language": "English",
"filename": "The Witness/setup_en.md",
"link": "The Witness/setup/en",
"authors": [
"NewSoupVi", "Jarno"
]
}
]
}
]
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 374 KiB

View File

@ -71,9 +71,11 @@ flowchart LR
SM64[Super Mario 64 Ex]
V6[VVVVVV]
MT[Meritous]
TW[The Witness]
APCLIENTPP <--> SOE
APCLIENTPP <--> MT
APCLIENTPP <-- The Witness Randomizer --> TW
APCPP <--> SM64
APCPP <--> V6
end

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -0,0 +1,104 @@
Event Items:
Shadows Laser Activation - 0x00021,0x17D28,0x17C71
Bunker Laser Activation - 0x00061,0x17D01,0x17C42
Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9,0x17CA4
Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x00B8D,0x17CF7
Requirement Changes:
0x17CA4 - True - True
0x28B39 - 0x2896A - Reflection
0x17CAB - True - True
Region Changes:
Quarry (Quarry) - Outside Quarry - 0x17C09 - Quarry Mill - 0x275ED - Quarry Mill - 0x17CAC
Disabled Locations:
0x03505 (Tutorial Gate Close)
0x0C335 (Tutorial Pillar)
0x0C373 (Tutorial Patio Floor)
0x009B8 (Symmetry Island Scenery Outlines 1)
0x003E8 (Symmetry Island Scenery Outlines 2)
0x00A15 (Symmetry Island Scenery Outlines 3)
0x00B53 (Symmetry Island Scenery Outlines 4)
0x00B8D (Symmetry Island Scenery Outlines 5)
0x00143 (Orchard Apple Tree 1)
0x0003B (Orchard Apple Tree 2)
0x00055 (Orchard Apple Tree 3)
0x032F7 (Orchard Apple Tree 4)
0x032FF (Orchard Apple Tree 5)
0x168B5 (Shadows Lower Avoid 1)
0x198BD (Shadows Lower Avoid 2)
0x198BF (Shadows Lower Avoid 3)
0x19771 (Shadows Lower Avoid 4)
0x0A8DC (Shadows Lower Avoid 5)
0x0AC74 (Shadows Lower Avoid 6)
0x0AC7A (Shadows Lower Avoid 7)
0x0A8E0 (Shadows Lower Avoid 8)
0x386FA (Shadows Environmental Avoid 1)
0x1C33F (Shadows Environmental Avoid 2)
0x196E2 (Shadows Environmental Avoid 3)
0x1972A (Shadows Environmental Avoid 4)
0x19809 (Shadows Environmental Avoid 5)
0x19806 (Shadows Environmental Avoid 6)
0x196F8 (Shadows Environmental Avoid 7)
0x1972F (Shadows Environmental Avoid 8)
0x19797 (Shadows Follow 1)
0x1979A (Shadows Follow 2)
0x197E0 (Shadows Follow 3)
0x197E8 (Shadows Follow 4)
0x197E5 (Shadows Follow 5)
0x19650 (Shadows Laser)
0x00139 (Keep Hedge Maze 1)
0x019DC (Keep Hedge Maze 2)
0x019E7 (Keep Hedge Maze 3)
0x01A0F (Keep Hedge Maze 4)
0x0360E (Laser Hedges)
0x00290 (Monastery Rhombic Avoid 1)
0x00038 (Monastery Rhombic Avoid 2)
0x00037 (Monastery Rhombic Avoid 3)
0x193A7 (Monastery Branch Avoid 1)
0x193AA (Monastery Branch Avoid 2)
0x193AB (Monastery Branch Follow 1)
0x193A6 (Monastery Branch Follow 2)
0x17CA4 (Monastery Laser) - 0x193A6 - True
0x18590 (Tree Outlines) - True - Symmetry & Environment
0x28AE3 (Vines Shadows Follow) - 0x18590 - Shadows Follow & Environment
0x28938 (Four-way Apple Tree) - 0x28AE3 - Environment
0x079DF (Triple Environmental Puzzle) - 0x28938 - Shadows Avoid & Environment & Reflection
0x28B39 (Hexagonal Reflection) - 0x079DF & 0x2896A - Reflection
0x03553 (Theater Tutorial Video)
0x03552 (Theater Desert Video)
0x0354E (Theater Jungle Video)
0x03549 (Theater Challenge Video)
0x0354F (Theater Shipwreck Video)
0x03545 (Theater Mountain Video)
0x002C4 (Waves 1)
0x00767 (Waves 2)
0x002C6 (Waves 3)
0x0070E (Waves 4)
0x0070F (Waves 5)
0x0087D (Waves 6)
0x002C7 (Waves 7)
0x15ADD (River Rhombic Avoid Vault)
0x03702 (River Vault Box)
0x17C2E (Door to Bunker) - True - Squares & Black/White Squares
0x09F7D (Bunker Drawn Squares 1)
0x09FDC (Bunker Drawn Squares 2)
0x09FF7 (Bunker Drawn Squares 3)
0x09F82 (Bunker Drawn Squares 4)
0x09FF8 (Bunker Drawn Squares 5)
0x09D9F (Bunker Drawn Squares 6)
0x09DA1 (Bunker Drawn Squares 7)
0x09DA2 (Bunker Drawn Squares 8)
0x09DAF (Bunker Drawn Squares 9)
0x0A010 (Bunker Drawn Squares through Tinted Glass 1)
0x0A01B (Bunker Drawn Squares through Tinted Glass 2)
0x0A01F (Bunker Drawn Squares through Tinted Glass 3)
0x0A099 (Door to Bunker Proper)
0x34BC5 (Bunker Drop-Down Door Open)
0x34BC6 (Bunker Drop-Down Door Close)
0x17E63 (Bunker Drop-Down Door Squares 1)
0x17E67 (Bunker Drop-Down Door Squares 2)
0x09DE0 (Bunker Laser)
0x0A079 (Bunker Elevator Control)
0x0042D (Mountaintop River Shape)

71
worlds/witness/Options.py Normal file
View File

@ -0,0 +1,71 @@
from typing import Dict
from BaseClasses import MultiWorld
from Options import Toggle, DefaultOnToggle, Option
# class HardMode(Toggle):
# "Play the randomizer in hardmode"
# display_name = "Hard Mode"
# class UnlockSymbols(DefaultOnToggle):
# "All Puzzle symbols of a specific panel need to be unlocked before the panel can be used"
# display_name = "Unlock Symbols"
class DisableNonRandomizedPuzzles(DefaultOnToggle):
"""Disable puzzles that cannot be randomized.
Non randomized puzzles are Shadows, Monastery, and Greenhouse.
The lasers for those areas will be activated as you solve optional puzzles throughout the island."""
display_name = "Disable non randomized puzzles"
class ShuffleDiscardedPanels(Toggle):
"""Discarded Panels will have items on them.
Solving certain Discarded Panels may still be necessary!"""
display_name = "Shuffle Discarded Panels"
class ShuffleVaultBoxes(Toggle):
"""Vault Boxes will have items on them."""
display_name = "Shuffle Vault Boxes"
class ShuffleUncommonLocations(Toggle):
"""Adds the following checks to the pool:
Mountaintop River Shape, Tutorial Patio Floor, Theater Videos"""
display_name = "Shuffle Uncommon Locations"
class ShuffleHardLocations(Toggle):
"""Adds some harder locations into the game, e.g. Mountain Secret Area panels"""
display_name = "Shuffle Hard Locations"
class ChallengeVictoryCondition(Toggle):
"""The victory condition now becomes beating the Challenge area,
instead of the final elevator."""
display_name = "Victory on beating the Challenge"
the_witness_options: Dict[str, Option] = {
# "hard_mode": HardMode,
# "unlock_symbols": UnlockSymbols,
"disable_non_randomized_puzzles": DisableNonRandomizedPuzzles,
"shuffle_discarded_panels": ShuffleDiscardedPanels,
"shuffle_vault_boxes": ShuffleVaultBoxes,
"shuffle_uncommon": ShuffleUncommonLocations,
"shuffle_hard": ShuffleHardLocations,
"challenge_victory": ChallengeVictoryCondition
}
def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
return get_option_value(world, player, name) > 0
def get_option_value(world: MultiWorld, player: int, name: str) -> int:
option = getattr(world, name, None)
if option is None:
return 0
return int(option[player].value)

View File

@ -0,0 +1,21 @@
Progression:
0 - Dots
1 - Colored Dots
5 - Sound Dots
10 - Symmetry
20 - Triangles
30 - Eraser
40 - Shapers
41 - Rotated Shapers
50 - Negative Shapers
60 - Stars
61 - Stars + Same Colored Symbol
71 - Black/White Squares
72 - Colored Squares
Boosts:
500 - Speed Boost
Traps:
600 - Slowness Trap
610 - Power Surge Trap

View File

@ -0,0 +1,712 @@
First Hallway (First Hallway) - Entry - True:
0x00064 (Straight) - True - True
0x00182 (Bend) - 0x00064 - True
Tutorial (Tutorial) - First Hallway - 0x00182:
0x00293 (Front Center) - True - True
0x00295 (Center Left) - 0x00293 - True
0x002C2 (Front Left) - 0x00295 - True
0x0A3B5 (Back Left) - True - True
0x0A3B2 (Back Right) - True - True
0x03629 (Gate Open) - 0x002C2 & 0x0A3B5 & 0x0A3B2 - True
0x03505 (Gate Close) - 0x2FAF6 - True
0x0C335 (Pillar) - True - Triangles - True
0x0C373 (Patio Floor) - 0x0C335 - True
Outside Tutorial (Outside Tutorial) - Tutorial - 0x03629:
0x033D4 (Vault) - True - Dots & Squares & Black/White Squares
0x03481 (Vault Box) - 0x033D4 - True
0x0A171 (Optional Door 1) - 0x0A3B5 - Dots
0x17CFB (Discard) - 0x0A171 - Triangles
0x04CA4 (Optional Door 2) - 0x0A171 - Dots & Squares & Black/White Squares
0x0005D (Dots Introduction 1) - True - Dots
0x0005E (Dots Introduction 2) - 0x0005D - Dots
0x0005F (Dots Introduction 3) - 0x0005E - Dots
0x00060 (Dots Introduction 4) - 0x0005F - Dots
0x00061 (Dots Introduction 5) - 0x00060 - Dots
0x018AF (Squares Introduction 1) - True - Squares & Black/White Squares
0x0001B (Squares Introduction 2) - 0x018AF - Squares & Black/White Squares
0x012C9 (Squares Introduction 3) - 0x0001B - Squares & Black/White Squares
0x0001C (Squares Introduction 4) - 0x012C9 - Squares & Black/White Squares
0x0001D (Squares Introduction 5) - 0x0001C - Squares & Black/White Squares
0x0001E (Squares Introduction 6) - 0x0001D - Squares & Black/White Squares
0x0001F (Squares Introduction 7) - 0x0001E - Squares & Black/White Squares
0x00020 (Squares Introduction 8) - 0x0001F - Squares & Black/White Squares
0x00021 (Squares Introduction 9) - 0x00020 - Squares & Black/White Squares
Main Island () - Outside Tutorial - True:
Outside Glass Factory (Glass Factory) - Main Island - True:
0x01A54 (Entry Door) - True - Symmetry
0x3C12B (Discard) - True - Triangles
Inside Glass Factory (Glass Factory) - Outside Glass Factory - 0x01A54:
0x00086 (Vertical Symmetry 1) - True - Symmetry
0x00087 (Vertical Symmetry 2) - 0x00086 - Symmetry
0x00059 (Vertical Symmetry 3) - 0x00087 - Symmetry
0x00062 (Vertical Symmetry 4) - 0x00059 - Symmetry
0x0005C (Vertical Symmetry 5) - 0x00062 - Symmetry
0x0008D (Rotational Symmetry 1) - 0x0005C - Symmetry
0x00081 (Rotational Symmetry 2) - 0x0008D - Symmetry
0x00083 (Rotational Symmetry 3) - 0x00081 - Symmetry
0x00084 (Melting 1) - 0x00083 - Symmetry
0x00082 (Melting 2) - 0x00084 - Symmetry
0x0343A (Melting 3) - 0x00082 - Symmetry
0x17CC8 (Boat Spawn) - True - Boat
Outside Symmetry Island (Symmetry Island) - Main Island - True:
0x000B0 (Door to Symmetry Island Lower) - True - Dots
Symmetry Island Lower (Symmetry Island) - Outside Symmetry Island - 0x000B0:
0x00022 (Black Dots 1) - True - Symmetry & Dots
0x00023 (Black Dots 2) - 0x00022 - Symmetry & Dots
0x00024 (Black Dots 3) - 0x00023 - Symmetry & Dots
0x00025 (Black Dots 4) - 0x00024 - Symmetry & Dots
0x00026 (Black Dots 5) - 0x00025 - Symmetry & Dots
0x0007C (Colored Dots 1) - 0x00026 - Symmetry & Colored Dots
0x0007E (Colored Dots 2) - 0x0007C - Symmetry & Colored Dots
0x00075 (Colored Dots 3) - 0x0007E - Symmetry & Colored Dots
0x00073 (Colored Dots 4) - 0x00075 - Symmetry & Colored Dots
0x00077 (Colored Dots 5) - 0x00073 - Symmetry & Colored Dots
0x00079 (Colored Dots 6) - 0x00077 - Symmetry & Colored Dots
0x00065 (Fading Lines 1) - 0x00079 - Symmetry & Colored Dots
0x0006D (Fading Lines 2) - 0x00065 - Symmetry & Colored Dots
0x00072 (Fading Lines 3) - 0x0006D - Symmetry & Colored Dots
0x0006F (Fading Lines 4) - 0x00072 - Symmetry & Colored Dots
0x00070 (Fading Lines 5) - 0x0006F - Symmetry & Colored Dots
0x00071 (Fading Lines 6) - 0x00070 - Symmetry & Colored Dots
0x00076 (Fading Lines 7) - 0x00071 - Symmetry & Colored Dots
0x009B8 (Scenery Outlines 1) - True - Symmetry & Environment
0x003E8 (Scenery Outlines 2) - 0x009B8 - Symmetry & Environment
0x00A15 (Scenery Outlines 3) - 0x003E8 - Symmetry & Environment
0x00B53 (Scenery Outlines 4) - 0x00A15 - Symmetry & Environment
0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry & Environment
0x1C349 (Door to Symmetry Island Upper) - 0x00076 - Symmetry & Dots
Symmetry Island Upper (Symmetry Island) - Symmetry Island Lower - 0x1C349:
0x00A52 (Yellow 1) - True - Symmetry & Colored Dots
0x00A57 (Yellow 2) - 0x00A52 - Symmetry & Colored Dots
0x00A5B (Yellow 3) - 0x00A57 - Symmetry & Colored Dots
0x00A61 (Blue 1) - 0x00A52 - Symmetry & Colored Dots
0x00A64 (Blue 2) - 0x00A61 & 0x00A52 - Symmetry & Colored Dots
0x00A68 (Blue 3) - 0x00A64 & 0x00A57 - Symmetry & Colored Dots
0x0360D (Laser) - 0x00A68 - True
Orchard (Orchard) - Main Island - True:
0x00143 (Apple Tree 1) - True - Environment
0x0003B (Apple Tree 2) - 0x00143 - Environment
0x00055 (Apple Tree 3) - 0x0003B - Environment
0x032F7 (Apple Tree 4) - 0x00055 - Environment
0x032FF (Apple Tree 5) - 0x032F7 - Environment
Desert Outside (Desert) - Main Island - True:
0x0CC7B (Vault) - True - Dots & Shapers & Rotated Shapers & Negative Shapers
0x0339E (Vault Box) - 0x0CC7B - True
0x17CE7 (Discard) - True - Triangles
0x00698 (Sun Reflection 1) - True - Reflection
0x0048F (Sun Reflection 2) - 0x00698 - Reflection
0x09F92 (Sun Reflection 3) - 0x0048F & 0x09FA0 - Reflection
0x09FA0 (Reflection 3 Control) - 0x0048F - True
0x0A036 (Sun Reflection 4) - 0x09F92 - Reflection
0x09DA6 (Sun Reflection 5) - 0x09F92 - Reflection
0x0A049 (Sun Reflection 6) - 0x09F92 - Reflection
0x0A053 (Sun Reflection 7) - 0x0A036 & 0x09DA6 & 0x0A049 - Reflection
0x09F94 (Sun Reflection 8) - 0x0A053 & 0x09F86 - Reflection
0x09F86 (Reflection 8 Control) - 0x0A053 - True
0x0C339 (Door to Desert Flood Light Room) - 0x09F94 - True
Desert Floodlight Room (Desert) - Desert Outside - 0x0C339:
0x09FAA (Light Control) - True - True
0x00422 (Artificial Light Reflection 1) - 0x09FAA - Reflection
0x006E3 (Artificial Light Reflection 2) - 0x09FAA - Reflection
0x0A02D (Artificial Light Reflection 3) - 0x09FAA & 0x00422 & 0x006E3 - Reflection
Desert Pond Room (Desert) - Desert Floodlight Room - 0x0A02D:
0x00C72 (Pond Reflection 1) - True - Reflection
0x0129D (Pond Reflection 2) - 0x00C72 - Reflection
0x008BB (Pond Reflection 3) - 0x0129D - Reflection
0x0078D (Pond Reflection 4) - 0x008BB - Reflection
0x18313 (Pond Reflection 5) - 0x0078D - Reflection
0x0A249 (Door to Desert Water Levels Room) - 0x18313 - Reflection
Desert Water Levels Room (Desert) - Desert Pond Room - 0x0A249:
0x1C2DF (Reduce Water Level Far Left) - True - True
0x1831E (Reduce Water Level Far Right) - True - True
0x1C260 (Reduce Water Level Near Left) - True - True
0x1831C (Reduce Water Level Near Right) - True - True
0x1C2F3 (Raise Water Level Far Left) - True - True
0x1831D (Raise Water Level Far Right) - True - True
0x1C2B1 (Raise Water Level Near Left) - True - True
0x1831B (Raise Water Level Near Right) - True - True
0x04D18 (Flood Reflection 1) - True - Reflection
0x01205 (Flood Reflection 2) - 0x04D18 - Reflection
0x181AB (Flood Reflection 3) - 0x01205 - Reflection
0x0117A (Flood Reflection 4) - 0x181AB - Reflection
0x17ECA (Flood Reflection 5) - 0x0117A - Reflection
0x18076 (Flood Reflection 6) - 0x17ECA - Reflection
Desert Elevator Room (Desert) - Desert Water Levels Room - 0x18076:
0x17C31 (Final Transparent Reflection) - True - Reflection
0x012D7 (Final Reflection) - 0x17C31 & 0x0A015 - Reflection
0x012D7 (Final Reflection) - 0x17C31 & 0x0A015 - Reflection
0x0A015 (Final Reflection Control) - 0x17C31 - True
0x0A15C (Final Bent Reflection 1) - True - Reflection
0x09FFF (Final Bent Reflection 2) - 0x0A15C - Reflection
0x0A15F (Final Bent Reflection 3) - 0x09FFF - Reflection
0x03608 (Laser) - 0x012D7 & 0x0A15F - True
Outside Quarry (Quarry) - Main Island - True:
0x09E57 (Door to Quarry 1) - True - Squares & Black/White Squares
0x17C09 (Door to Quarry 2) - 0x09E57 - Shapers
0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser
Quarry (Quarry) - Outside Quarry - 0x17C09 - Quarry Mill - 0x275ED - Quarry Mill - 0x17CAC - Shadows Ledge - 0x198BF:
0x01E5A (Door to Mill Left) - True - Squares & Black/White Squares
0x01E59 (Door to Mill Right) - True - Dots
0x17CF0 (Discard) - True - Triangles
0x03612 (Laser) - 0x0A3D0 & 0x0367C - Eraser & Shapers
Quarry Mill (Quarry Mill) - Quarry - 0x01E59 & 0x01E5A:
0x275ED (Ground Floor Shortcut Door) - True - True
0x03678 (Lower Ramp Control) - True - Dots & Eraser
0x00E0C (Eraser and Dots 1) - 0x03678 - Dots & Eraser
0x01489 (Eraser and Dots 2) - 0x00E0C - Dots & Eraser
0x0148A (Eraser and Dots 3) - 0x01489 - Dots & Eraser
0x014D9 (Eraser and Dots 4) - 0x0148A - Dots & Eraser
0x014E7 (Eraser and Dots 5) - 0x014D9 - Dots & Eraser
0x014E8 (Eraser and Dots 6) - 0x014E7 - Dots & Eraser
0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser
0x03675 (Upper Ramp Control) - 0x03679 - Dots & Eraser
0x03676 (Upper Lift Control) - 0x03679 - Dots & Eraser
0x00557 (Eraser and Squares 1) - 0x03679 - Squares & Colored Squares & Eraser
0x005F1 (Eraser and Squares 2) - 0x00557 - Squares & Colored Squares & Eraser
0x00620 (Eraser and Squares 3) - 0x005F1 - Squares & Colored Squares & Eraser
0x009F5 (Eraser and Squares 4) - 0x00620 - Squares & Colored Squares & Eraser
0x0146C (Eraser and Squares 5) - 0x009F5 - Squares & Colored Squares & Eraser
0x3C12D (Eraser and Squares 6) - 0x0146C - Squares & Colored Squares & Eraser
0x03686 (Eraser and Squares 7) - 0x3C12D - Squares & Colored Squares & Eraser
0x014E9 (Eraser and Squares 8) - 0x03686 - Squares & Colored Squares & Eraser
0x03677 (Stair Control) - 0x014E8 - Squares & Colored Squares & Eraser
0x3C125 (Big Squares & Dots & and Eraser) - 0x0367C - Squares & Black/White Squares & Dots & Eraser
0x0367C (Small Squares & Dots & and Eraser) - 0x014E9 - Squares & Colored Squares & Dots & Eraser
0x17CAC (Door to Outside Quarry Stairs) - True - True
Quarry Boathouse (Quarry Boathouse) - Quarry - True:
0x034D4 (Intro Stars) - True - Stars
0x021D5 (Intro Shapers) - True - Shapers & Rotated Shapers
0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers
0x021B3 (Eraser and Shapers 1) - 0x03852 - Shapers & Eraser
0x021B4 (Eraser and Shapers 2) - 0x021B3 - Shapers & Eraser
0x021B0 (Eraser and Shapers 3) - 0x021B4 - Shapers & Eraser
0x021AF (Eraser and Shapers 4) - 0x021B0 - Shapers & Eraser
0x021AE (Eraser and Shapers 5) - 0x021AF - Shapers & Eraser & Broken Shapers
0x03858 (Ramp Horizontal Control) - 0x021AE - Shapers & Eraser
0x38663 (Shortcut Door) - 0x03858 - True
0x021B5 (Stars and Colored Eraser 1) - 0x03858 - Stars & Stars + Same Colored Symbol & Eraser
0x021B6 (Stars and Colored Eraser 2) - 0x021B5 - Stars & Stars + Same Colored Symbol & Eraser
0x021B7 (Stars and Colored Eraser 3) - 0x021B6 - Stars & Stars + Same Colored Symbol & Eraser
0x021BB (Stars and Colored Eraser 4) - 0x021B7 - Stars & Stars + Same Colored Symbol & Eraser
0x09DB5 (Stars and Colored Eraser 5) - 0x021BB - Stars & Stars + Same Colored Symbol & Eraser
0x09DB1 (Stars and Colored Eraser 6) - 0x09DB5 - Stars & Stars + Same Colored Symbol & Eraser
0x3C124 (Stars and Colored Eraser 7) - 0x09DB1 - Stars & Stars + Same Colored Symbol & Eraser
0x09DB3 (Stars & Eraser & and Shapers 1) - 0x3C124 - Stars & Eraser & Shapers
0x09DB4 (Stars & Eraser & and Shapers 2) - 0x09DB3 - Stars & Eraser & Shapers
0x275FA (Hook Control) - 0x03858 - Shapers & Eraser
0x17CA6 (Boat Spawn) - True - Boat
0x0A3CB (Stars & Eraser & and Shapers 3) - 0x09DB4 - Stars & Eraser & Shapers
0x0A3CC (Stars & Eraser & and Shapers 4) - 0x0A3CB - Stars & Eraser & Shapers
0x0A3D0 (Stars & Eraser & and Shapers 5) - 0x0A3CC - Stars & Eraser & Shapers
Shadows (Shadows) - Main Island - True - Keep Glass Plates - 0x09E49:
0x334DB (Door Timer Outside) - True - True
0x0AC74 (Lower Avoid 6) - 0x0A8DC - Shadows Avoid
0x0AC7A (Lower Avoid 7) - 0x0AC74 - Shadows Avoid
0x0A8E0 (Lower Avoid 8) - 0x0AC7A - Shadows Avoid
0x386FA (Environmental Avoid 1) - 0x0A8E0 - Shadows Avoid & Environment
0x1C33F (Environmental Avoid 2) - 0x386FA - Shadows Avoid & Environment
0x196E2 (Environmental Avoid 3) - 0x1C33F - Shadows Avoid & Environment
0x1972A (Environmental Avoid 4) - 0x196E2 - Shadows Avoid & Environment
0x19809 (Environmental Avoid 5) - 0x1972A - Shadows Avoid & Environment
0x19806 (Environmental Avoid 6) - 0x19809 - Shadows Avoid & Environment
0x196F8 (Environmental Avoid 7) - 0x19806 - Shadows Avoid & Environment
0x1972F (Environmental Avoid 8) - 0x196F8 - Shadows Avoid & Environment
0x19797 (Follow 1) - 0x0A8E0 - Shadows Follow
0x1979A (Follow 2) - 0x19797 - Shadows Follow
0x197E0 (Follow 3) - 0x1979A - Shadows Follow
0x197E8 (Follow 4) - 0x197E0 - Shadows Follow
0x197E5 (Follow 5) - 0x197E8 - Shadows Follow
0x19650 (Laser) - 0x197E5 & 0x196F8 - Shadows Avoid & Shadows Follow
Shadows Ledge (Shadows) - Shadows - 0x334DB | 0x334DC | 0x0A8DC:
0x334DC (Door Timer Inside) - True - True
0x168B5 (Lower Avoid 1) - True - Shadows Avoid
0x198BD (Lower Avoid 2) - 0x168B5 - Shadows Avoid
0x198BF (Lower Avoid 3) - 0x198BD & 0x334DC - Shadows Avoid
0x19771 (Lower Avoid 4) - 0x198BF - Shadows Avoid
0x0A8DC (Lower Avoid 5) - 0x19771 - Shadows Avoid
Keep (Keep) - Main Island - True:
Keep Hedges (Keep) - Keep - True:
0x00139 (Hedge Maze 1) - True - Environment
0x019DC (Hedge Maze 2) - 0x00139 - Environment
0x019E7 (Hedge Maze 3) - 0x019DC - Environment & Sound
0x01A0F (Hedge Maze 4) - 0x019E7 - Environment
Keep Glass Plates (Keep) - Keep - True - Keep Tower - 0x0361B:
0x0A3A8 (Reset Pressure Plates 1) - True - True
0x033EA (Pressure Plates 1) - 0x0A3A8 - Pressure Plates & Dots
0x0A3B9 (Reset Pressure Plates 2) - 0x033EA - True
0x01BE9 (Pressure Plates 2) - 0x033EA & 0x0A3B9 - Pressure Plates & Stars & Stars + Same Colored Symbol & Squares & Black/White Squares
0x0A3BB (Reset Pressure Plates 3) - 0x0A3A8 - True
0x01CD3 (Pressure Plates 3) - 0x0A3A8 & 0x0A3BB - Pressure Plates & Shapers & Squares & Black/White Squares & Colored Squares
0x0A3AD (Reset Pressure Plates 4) - 0x01CD3 - True
0x01D3F (Pressure Plates 4) - 0x01CD3 & 0x0A3AD - Pressure Plates & Shapers & Dots & Symmetry
0x17D27 (Discard) - 0x01CD3 - Triangles
0x09E49 (Shortcut to Shadows) - 0x01CD3 - True
Shipwreck (Shipwreck) - Keep Glass Plates - 0x033EA:
0x00AFB (Vault) - True - Symmetry & Sound & Sound Dots & Colored Dots
0x03535 (Vault Box) - 0x00AFB - True
0x17D28 (Discard) - True - Triangles
Keep Tower (Keep) - Keep Hedges - 0x01A0F - Keep Glass Plates - 0x01D3F:
0x0361B (Shortcut to Keep Glass Plates) - True - True
0x0360E (Laser Hedges) - 0x01A0F - Environment & Sound
0x03317 (Laser Pressure Plates) - 0x01D3F - Shapers & Squares & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots
Outside Monastery (Monastery) - Main Island - True:
0x03713 (Shortcut) - True - True
0x00B10 (Door Open Left) - True - True
0x00C92 (Door Open Right) - True - True
0x00290 (Rhombic Avoid 1) - 0x09D9B - Environment
0x00038 (Rhombic Avoid 2) - 0x09D9B & 0x00290 - Environment
0x00037 (Rhombic Avoid 3) - 0x09D9B & 0x00038 - Environment
0x17CA4 (Laser) - 0x193A6 - True
Inside Monastery (Monastery) - Outside Monastery - 0x00B10 & 0x00C92:
0x09D9B (Overhead Door Control) - True - Dots
0x193A7 (Branch Avoid 1) - 0x00037 - Environment
0x193AA (Branch Avoid 2) - 0x193A7 - Environment
0x193AB (Branch Follow 1) - 0x193AA - Environment
0x193A6 (Branch Follow 2) - 0x193AB - Environment
Monastery Garden (Monastery) - Outside Monastery - 0x00037 - Outside Jungle River - 0x17CAA:
Town (Town) - Main Island - True - Theater - 0x0A168 | 0x33AB2:
0x0A054 (Boat Summon) - True - Boat
0x0A0C8 (Cargo Box) - True - Squares & Black/White Squares & Shapers
0x17D01 (Cargo Box Discard) - 0x0A0C8 - Triangles
0x09F98 (Desert Laser Redirect) - True - True
0x18590 (Tree Outlines) - True - Symmetry & Environment
0x28AE3 (Vines Shadows Follow) - 0x18590 - Shadows Follow & Environment
0x28938 (Four-way Apple Tree) - 0x28AE3 - Environment
0x079DF (Triple Environmental Puzzle) - 0x28938 - Shadows Avoid & Environment & Reflection
0x28B39 (Hexagonal Reflection) - 0x079DF & 0x2896A - Reflection
0x28998 (Tinted Door to RGB House) - True - Stars & Rotated Shapers
0x28A0D (Door to Church) - 0x28998 - Stars & RGB & Environment
0x28A69 (Square Avoid) - 0x28A0D - Environment
0x28A79 (Maze Stair Control) - True - Environment
0x2896A (Maze Rooftop Bridge Control) - 0x28A79 - Shapers
0x17C71 (Rooftop Discard) - 0x2896A - Triangles
0x28AC7 (Symmetry Squares 1) - 0x2896A - Symmetry & Squares & Black/White Squares
0x28AC8 (Symmetry Squares 2) - 0x28AC7 - Symmetry & Squares & Black/White Squares
0x28ACA (Symmetry Squares 3 + Dots) - 0x28AC8 - Symmetry & Squares & Black/White Squares & Dots
0x28ACB (Symmetry Squares 4 + Dots) - 0x28ACA - Symmetry & Squares & Black/White Squares & Dots
0x28ACC (Symmetry Squares 5 + Dots) - 0x28ACB - Symmetry & Squares & Black/White Squares & Dots
0x2899C (Full Dot Grid Shapers 1) - True - Rotated Shapers & Dots
0x28A33 (Full Dot Grid Shapers 2) - 0x2899C - Shapers & Dots
0x28ABF (Full Dot Grid Shapers 3) - 0x28A33 - Shapers & Rotated Shapers & Dots
0x28AC0 (Full Dot Grid Shapers 4) - 0x28ABF - Rotated Shapers & Dots
0x28AC1 (Full Dot Grid Shapers 5) - 0x28AC0 - Rotated Shapers & Dots
0x28AD9 (Shapers & Dots & and Eraser) - 0x28AC1 - Rotated Shapers & Dots & Eraser
0x17F5F (Windmill Door) - True - Dots
RGB House (Town) - Town - 0x28998:
0x034E4 (Sound Room Left) - True - Sound & Sound Waves
0x034E3 (Sound Room Right) - True - Sound & Sound Dots
0x334D8 (RGB Control) - 0x034E4 & 0x034E3 - Rotated Shapers & RGB & Squares & Colored Squares
0x03C0C (RGB Squares) - 0x334D8 - RGB & Squares & Colored Squares & Black/White Squares
0x03C08 (RGB Stars) - 0x334D8 - RGB & Stars
Town Tower Top (Town) - Town - 0x28A69 & 0x28B39 & 0x28ACC & 0x28AD9:
0x032F5 (Laser) - True - True
Windmill Interior (Windmill) - Town - 0x17F5F:
0x17D02 (Turn Control) - True - Dots
0x17F89 (Door to Front of Theater) - True - Squares & Black/White Squares
Theater (Theater) - Windmill Interior - 0x17F89:
0x00815 (Video Input) - True - True
0x03553 (Tutorial Video) - 0x00815 & 0x03481 - True
0x03552 (Desert Video) - 0x00815 & 0x0339E - True
0x0354E (Jungle Video) - 0x00815 & 0x03702 - True
0x03549 (Challenge Video) - 0x00815 & 0x2FAF6 - True
0x0354F (Shipwreck Video) - 0x00815 & 0x03535 - True
0x03545 (Mountain Video) - 0x00815 & 0x03542 - True
0x0A168 (Door to Cargo Box Left) - True - Squares & Black/White Squares & Eraser
0x33AB2 (Door to Cargo Box Right) - True - Squares & Black/White Squares & Shapers
0x17CF7 (Discard) - True - Triangles
Jungle (Jungle) - Main Island - True:
0x17CDF (Shore Boat Spawn) - True - Boat
0x17F9B (Discard) - True - Triangles
0x002C4 (Waves 1) - True - Sound & Sound Waves
0x00767 (Waves 2) - 0x002C4 - Sound & Sound Waves
0x002C6 (Waves 3) - 0x00767 - Sound & Sound Waves
0x0070E (Waves 4) - 0x002C6 - Sound & Sound Waves
0x0070F (Waves 5) - 0x0070E - Sound & Sound Waves
0x0087D (Waves 6) - 0x0070F - Sound & Sound Waves
0x002C7 (Waves 7) - 0x0087D - Sound & Sound Waves
0x17CAB (Popup Wall Control) - 0x002C7 - True
0x0026D (Popup Wall 1) - 0x17CAB - Sound & Sound Dots
0x0026E (Popup Wall 2) - 0x0026D - Sound & Sound Dots
0x0026F (Popup Wall 3) - 0x0026E - Sound & Sound Dots
0x00C3F (Popup Wall 4) - 0x0026F - Sound & Sound Dots
0x00C41 (Popup Wall 5) - 0x00C3F - Sound & Sound Dots
0x014B2 (Popup Wall 6) - 0x00C41 - Sound & Sound Dots
0x03616 (Laser) - 0x014B2 - True
0x337FA (Shortcut to River) - True - True
Outside Jungle River (River) - Main Island - True - Jungle - 0x337FA:
0x17CAA (Rhombic Avoid to Monastery Garden) - True - Environment
0x15ADD (Rhombic Avoid Vault) - True - Environment
0x03702 (Vault Box) - 0x15ADD - True
Outside Bunker (Bunker) - Main Island - True - Inside Bunker - 0x0A079:
0x17C2E (Door to Bunker) - True - Squares & Black/White Squares
0x09DE0 (Laser) - 0x0A079 - True
Inside Bunker (Bunker) - Outside Bunker - 0x17C2E:
0x09F7D (Drawn Squares 1) - True - Squares & Colored Squares
0x09FDC (Drawn Squares 2) - 0x09F7D - Squares & Colored Squares & Black/White Squares
0x09FF7 (Drawn Squares 3) - 0x09FDC - Squares & Colored Squares & Black/White Squares
0x09F82 (Drawn Squares 4) - 0x09FF7 - Squares & Colored Squares & Black/White Squares
0x09FF8 (Drawn Squares 5) - 0x09F82 - Squares & Colored Squares & Black/White Squares
0x09D9F (Drawn Squares 6) - 0x09FF8 - Squares & Colored Squares & Black/White Squares
0x09DA1 (Drawn Squares 7) - 0x09D9F - Squares & Colored Squares
0x09DA2 (Drawn Squares 8) - 0x09DA1 - Squares & Colored Squares
0x09DAF (Drawn Squares 9) - 0x09DA2 - Squares & Colored Squares
0x0A099 (Door to Bunker Proper) - 0x09DAF - True
0x0A010 (Drawn Squares through Tinted Glass 1) - 0x0A099 - Squares & Colored Squares & RGB & Environment
0x0A01B (Drawn Squares through Tinted Glass 2) - 0x0A010 - Squares & Colored Squares & Black/White Squares & RGB & Environment
0x0A01F (Drawn Squares through Tinted Glass 3) - 0x0A01B - Squares & Colored Squares & Black/White Squares & RGB & Environment
0x34BC5 (Drop-Down Door Open) - 0x0A01F - True
0x34BC6 (Drop-Down Door Close) - 0x34BC5 - True
0x17E63 (Drop-Down Door Squares 1) - 0x0A01F & 0x34BC5 - Squares & Colored Squares & RGB & Environment
0x17E67 (Drop-Down Door Squares 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares & RGB
0x0A079 (Elevator Control) - 0x17E67 - Squares & Colored Squares & Black/White Squares & RGB
Outside Swamp (Swamp) - Main Island - True:
0x0056E (Entry Door) - True - Shapers
Swamp Entry Area (Swamp) - Outside Swamp - 0x0056E:
0x00469 (Seperatable Shapers 1) - True - Shapers
0x00472 (Seperatable Shapers 2) - 0x00469 - Shapers
0x00262 (Seperatable Shapers 3) - 0x00472 - Shapers
0x00474 (Seperatable Shapers 4) - 0x00262 - Shapers
0x00553 (Seperatable Shapers 5) - 0x00474 - Shapers
0x0056F (Seperatable Shapers 6) - 0x00553 - Shapers
0x00390 (Combinable Shapers 1) - 0x0056F - Shapers
0x010CA (Combinable Shapers 2) - 0x00390 - Shapers
0x00983 (Combinable Shapers 3) - 0x010CA - Shapers
0x00984 (Combinable Shapers 4) - 0x00983 - Shapers
0x00986 (Combinable Shapers 5) - 0x00984 - Shapers
0x00985 (Combinable Shapers 6) - 0x00986 - Shapers
0x00987 (Combinable Shapers 7) - 0x00985 - Shapers
0x181A9 (Combinable Shapers 8) - 0x00987 - Shapers
0x00609 (Slide Bridge) - 0x181A9 - Shapers
Swamp Near Platform (Swamp) - Swamp Entry Area - 0x00609 | 0x18488:
0x00999 (Broken Shapers 1) - 0x00990 - Broken Shapers
0x0099D (Broken Shapers 2) - 0x00999 - Broken Shapers
0x009A0 (Broken Shapers 3) - 0x0099D - Broken Shapers
0x009A1 (Broken Shapers 4) - 0x009A0 - Broken Shapers
0x00002 (Cyan Underwater Negative Shapers 1) - 0x00006 - Shapers & Negative Shapers
0x00004 (Cyan Underwater Negative Shapers 2) - 0x00002 - Shapers & Negative Shapers
0x00005 (Cyan Underwater Negative Shapers 3) - 0x00004 - Shapers & Negative Shapers
0x013E6 (Cyan Underwater Negative Shapers 4) - 0x00005 - Shapers & Negative Shapers
0x00596 (Cyan Underwater Negative Shapers 5) - 0x013E6 - Shapers & Negative Shapers
0x18488 (Cyan Underwater Sliding Bridge Control) - 0x00006 - Shapers
Swamp Platform (Swamp) - Swamp Near Platform - True:
0x00982 (Platform Shapers 1) - True - Shapers
0x0097F (Platform Shapers 2) - 0x00982 - Shapers
0x0098F (Platform Shapers 3) - 0x0097F - Shapers
0x00990 (Platform Shapers 4) - 0x0098F - Shapers
0x17C0D (Platform Shortcut Door Left) - True - Shapers
0x17C0E (Platform Shortcut Door Right) - True - Shapers
Swamp Rotating Bridge Near Side (Swamp) - Swamp Near Platform - 0x009A1:
0x00007 (Rotated Shapers 1) - 0x009A1 - Rotated Shapers
0x00008 (Rotated Shapers 2) - 0x00007 - Rotated Shapers & Shapers
0x00009 (Rotated Shapers 3) - 0x00008 - Rotated Shapers
0x0000A (Rotated Shapers 4) - 0x00009 - Rotated Shapers
0x00001 (Red Underwater Negative Shapers 1) - 0x00596 - Shapers & Negative Shapers
0x014D2 (Red Underwater Negative Shapers 2) - 0x00596 - Shapers & Negative Shapers
0x014D4 (Red Underwater Negative Shapers 3) - 0x00596 - Shapers & Negative Shapers
0x014D1 (Red Underwater Negative Shapers 4) - 0x00596 - Shapers & Negative Shapers
Swamp Near Boat (Swamp) - Swamp Rotating Bridge Near Side - 0x009A1 - Swamp Platform - 0x17C0D & 0x17C0E:
0x181F5 (Rotating Bridge) - True - Rotated Shapers, Shapers
0x09DB8 (Boat Spawn) - True - Boat
0x003B2 (More Rotated Shapers 1) - 0x0000A - Rotated Shapers
0x00A1E (More Rotated Shapers 2) - 0x003B2 - Rotated Shapers
0x00C2E (More Rotated Shapers 3) - 0x00A1E - Rotated Shapers
0x00E3A (More Rotated Shapers 4) - 0x00C2E - Rotated Shapers
0x009A6 (Underwater Back Optional) - 0x00E3A - Shapers
0x009AB (Blue Underwater Negative Shapers 1) - 0x00E3A - Shapers & Negative Shapers
0x009AD (Blue Underwater Negative Shapers 2) - 0x009AB - Shapers & Negative Shapers
0x009AE (Blue Underwater Negetive Shapers 3) - 0x009AD - Shapers & Negative Shapers
0x009AF (Blue Underwater Negative Shapers 4) - 0x009AE - Shapers & Negative Shapers
0x00006 (Blue Underwater Negative Shapers 5) - 0x009AF - Shapers & Negative Shapers & Broken Negative Shapers
0x17E2B (Long Bridge Control) - True - Rotated Shapers
Swamp Maze (Swamp) - Swamp Rotating Bridge Near Side - 0x00001 & 0x014D2 & 0x014D4 & 0x014D1 - Outside Swamp - 0x17C05 & 0x17C02:
0x17C04 (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers & Environment
0x03615 (Laser) - 0x17C04 - True
0x17C05 (Near Laser Shortcut Door Left) - True - Rotated Shapers
0x17C02 (Near Laser Shortcut Door Right) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers
Treehouse Entry Area (Treehouse):
0x17C95 (Boat Spawn) - True - Boat
0x0288C (First Door) - True - Stars
0x02886 (Second Door) - 0x0288C - Stars
0x17D72 (Yellow Bridge 1) - 0x02886 - Stars
0x17D8F (Yellow Bridge 2) - 0x17D72 - Stars
0x17D74 (Yellow Bridge 3) - 0x17D8F - Stars
0x17DAC (Yellow Bridge 4) - 0x17D74 - Stars
0x17D9E (Yellow Bridge 5) - 0x17DAC - Stars
0x17DB9 (Yellow Bridge 6) - 0x17D9E - Stars
0x17D9C (Yellow Bridge 7) - 0x17DB9 - Stars
0x17DC2 (Yellow Bridge 8) - 0x17D9C - Stars
0x17DC4 (Yellow Bridge 9) - 0x17DC2 - Stars
0x0A182 (Beyond Yellow Bridge Door) - 0x17DC4 - Stars
Treehouse Beyond Yellow Bridge (Treehouse) - Treehouse Entry Area - 0x0A182:
0x2700B (Laser House Door Timer Outside Control) - True - True
0x17DC8 (First Purple Bridge 1) - True - Stars & Dots
0x17DC7 (First Purple Bridge 2) - 0x17DC8 - Stars & Dots
0x17CE4 (First Purple Bridge 3) - 0x17DC7 - Stars & Dots
0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots
0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots
0x17D9B (Second Purple Bridge 1) - 0x17D6C - Stars & Squares & Black/White Squares
0x17D99 (Second Purple Bridge 2) - 0x17D9B - Stars & Squares & Black/White Squares
0x17DAA (Second Purple Bridge 3) - 0x17D99 - Stars & Squares & Black/White Squares
0x17D97 (Second Purple Bridge 4) - 0x17DAA - Stars & Squares & Black/White Squares & Colored Squares
0x17BDF (Second Purple Bridge 5) - 0x17D97 - Stars & Squares & Colored Squares
0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Squares & Colored Squares
0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Squares & Colored Squares
0x17E3C (Green Bridge 1) - True - Stars & Shapers
0x17E4D (Green Bridge 2) - 0x17E3C - Stars & Shapers
0x17E4F (Green Bridge 3) - 0x17E4D - Stars & Shapers & Rotated Shapers
0x17E52 (Green Bridge 4 & Directional) - 0x17E4F - Stars & Rotated Shapers & Environment
0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol
0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Colored Shapers & Negative Shapers & Colored Negative Shapers & Stars + Same Colored Symbol
0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Rotated Shapers
0x17FA9 (Green Bridge Discard) - 0x17E61 - Triangles
0x17DB3 (Left Orange Bridge 1) - 0x17DC6 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DC0 (Left Orange Bridge 4) - 0x17DB6 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DD7 (Left Orange Bridge 5) - 0x17DC0 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol
0x17DD9 (Left Orange Bridge 6) - 0x17DD7 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol
0x17DB8 (Left Orange Bridge 7) - 0x17DD9 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol
0x17DDC (Left Orange Bridge 8) - 0x17DB8 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x17DD1 (Left Orange Bridge 9 & Directional) - 0x17DDC - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Environment
0x17DDE (Left Orange Bridge 10) - 0x17DD1 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x17DE3 (Left Orange Bridge 11) - 0x17DDE - Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x17DEC (Left Orange Bridge 12) - 0x17DE3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DAE (Left Orange Bridge 13) - 0x17DEC - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17FA0 (Burned House Discard) - 0x17DDB - Triangles
0x17D88 (Right Orange Bridge 1) - True - Stars
0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars
0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars
0x17CE3 (Right Orange Bridge 4 & Directional) - 0x17D8C - Stars & Environment
0x17DCD (Right Orange Bridge 5) - 0x17CE3 - Stars
0x17DB2 (Right Orange Bridge 6) - 0x17DCD - Stars
0x17DCC (Right Orange Bridge 7) - 0x17DB2 - Stars
0x17DCA (Right Orange Bridge 8) - 0x17DCC - Stars
0x17D8E (Right Orange Bridge 9) - 0x17DCA - Stars
0x17DB7 (Right Orange Bridge 10 & Directional) - 0x17D8E - Stars
0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars
0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars
Treehouse Laser Room (Treehouse) - Treehouse Beyond Yellow Bridge - 0x2700B & 0x17DA2 & 0x17DDB:
0x03613 (Laser) - True - True
0x17CBC (Laser House Door Timer Inside Control) - True - True
Treehouse Bridge Platform (Treehouse) - Treehouse Beyond Yellow Bridge - 0x17DA2 - Main Island - 0x037FF:
0x037FF (Bridge Control) - True - Stars
Mountaintop (Mountaintop) - Main Island - True:
0x0042D (River Shape) - True - True
0x09F7F (Box Open) - 7 Lasers - True
0x17C34 (Trap Door Triple Exit) - 0x09F7F - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x17C42 (Discard) - True - Triangles
0x002A6 (Vault) - True - Symmetry & Colored Dots & Squares & Black/White Squares & Dots
0x03542 (Vault Box) - 0x002A6 - True
Inside Mountain Top Layer (Inside Mountain) - Mountaintop - 0x17C34:
0x09E39 (Light Bridge Controller) - True - Squares & Black/White Squares & Colored Squares & Eraser & Colored Eraser
Inside Mountain Top Layer Bridge (Inside Mountain) - Inside Mountain Top Layer - 0x09E39:
0x09E7A (Obscured Vision 1) - True - Obscured & Squares & Black/White Squares & Dots
0x09E71 (Obscured Vision 2) - 0x09E7A - Obscured & Squares & Black/White Squares & Dots
0x09E72 (Obscured Vision 3) - 0x09E71 - Obscured & Squares & Black/White Squares & Rotated Shapers & Dots
0x09E69 (Obscured Vision 4) - 0x09E72 - Obscured & Squares & Black/White Squares & Dots
0x09E7B (Obscured Vision 5) - 0x09E69 - Obscured & Squares & Black/White Squares & Dots
0x09E73 (Moving Background 1) - True - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x09E75 (Moving Background 2) - 0x09E73 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x09E78 (Moving Background 3) - 0x09E75 - Moving & Shapers
0x09E79 (Moving Background 4) - 0x09E78 - Moving & Shapers & Rotated Shapers
0x09E6C (Moving Background 5) - 0x09E79 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x09E6F (Moving Background 6) - 0x09E6C - Moving & Stars & Rotated Shapers & Shapers
0x09E6B (Moving Background 7) - 0x09E6F - Moving & Stars & Dots
0x33AF5 (Physically Obstructed 1) - True - Squares & Black/White Squares & Environment & Symmetry
0x33AF7 (Physically Obstructed 2) - 0x33AF5 - Squares & Black/White Squares & Stars & Environment
0x09F6E (Physically Obstructed 3) - 0x33AF7 - Symmetry & Dots & Environment
0x09EAD (Angled Inside Trash 1) - True - Squares & Black/White Squares & Shapers & Angled
0x09EAF (Angled Inside Trash 2) - 0x09EAD - Squares & Black/White Squares & Shapers & Angled
Inside Mountain Second Layer (Inside Mountain) - Inside Mountain Top Layer Bridge - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B:
0x09FD3 (Color Cycle 1) - True - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x09FD4 (Color Cycle 2) - 0x09FD3 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x09FD6 (Color Cycle 3) - 0x09FD4 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol
0x09FD7 (Color Cycle 4) - 0x09FD6 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Shapers & Colored Shapers
0x09FD8 (Color Cycle 5) - 0x09FD7 - Color Cycle & RGB & Squares & Colored Squares & Symmetry & Colored Dots
0x09E86 (Light Bridge Controller 2) - 0x09FD7 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Eraser & Two Lines
Inside Mountain Second Layer Beyond Bridge (Inside Mountain) - Inside Mountain Second Layer - 0x09E86:
0x09FCC (Same Solution 1) - True - Dots & Same Solution
0x09FCE (Same Solution 2) - 0x09FCC - Squares & Black/White Squares & Same Solution
0x09FCF (Same Solution 3) - 0x09FCE - Stars & Same Solution
0x09FD0 (Same Solution 4) - 0x09FCF - Rotated Shapers & Same Solution
0x09FD1 (Same Solution 5) - 0x09FD0 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Same Solution
0x09FD2 (Same Solution 6) - 0x09FD1 - Shapers & Same Solution
0x09ED8 (Light Bridge Controller 3) - 0x09FD2 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Eraser & Two Lines
Inside Mountain Second Layer Elevator (Inside Mountain) - Inside Mountain Second Layer - 0x09ED8 & 0x09E86:
0x09EEB (Elevator Control Panel) - True - Dots
0x17F93 (Elevator Discard) - True - Triangles
Inside Mountain Third Layer (Inside Mountain) - Inside Mountain Second Layer Elevator - 0x09EEB:
0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser
0x09F8E (Giant Puzzle Bottom Right) - True - Shapers & Eraser
0x09F01 (Giant Puzzle Top Right) - True - Rotated Shapers
0x09EFF (Giant Puzzle Top Left) - True - Shapers & Eraser
0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry
Inside Mountain Bottom Layer (Inside Mountain) - Inside Mountain Third Layer - 0x09FDA - Inside Mountain Path to Secret Area - 0x334E1:
0x17FA2 (Bottom Layer Discard) - 11 Lasers & 0x09F7F - Triangles & Environment
0x01983 (Door to Final Room Left) - True - Shapers & Stars
0x01987 (Door to Final Room Right) - True - Squares & Colored Squares
Inside Mountain Path to Secret Area (Inside Mountain) - Inside Mountain Bottom Layer - 0x17FA2:
0x00FF8 (Door to Secret Area) - True - Triangles & Black/White Squares & Squares
0x334E1 (Rock Control) - True - True
Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path to Secret Area - 0x00FF8 - Main Island - 0x021D7 - Main Island - 0x17CF2:
0x021D7 (Shortcut to Mountain) - True - Triangles & Stars & Stars + Same Colored Symbol & Colored Triangles
0x17CF2 (Shortcut to Swamp) - True - Triangles
0x335AB (Elevator Inside Control) - True - Dots & Squares & Black/White Squares
0x335AC (Elevator Upper Outside Control) - 0x335AB - Squares & Black/White Squares
0x3369D (Elevator Lower Outside Control) - 0x335AB - Squares & Black/White Squares & Dots
0x00190 (Dot Grid Triangles 1) - True - Dots & Triangles
0x00558 (Dot Grid Triangles 2) - 0x00190 - Dots & Triangles
0x00567 (Dot Grid Triangles 3) - 0x00558 - Dots & Triangles
0x006FE (Dot Grid Triangles 4) - 0x00567 - Dots & Triangles
0x01A0D (Symmetry Triangles) - True - Symmetry & Triangles
0x008B8 (Squares and Triangles) - True - Squares & Black/White Squares & Triangles
0x00973 (Stars and Triangles) - 0x008B8 - Stars & Triangles
0x0097B (Stars and Triangles of same color) - 0x00973 - Stars & Triangles & Stars and Triangles of same color & Stars + Same Colored Symbol
0x0097D (Stars & Squares and Triangles) - 0x0097B - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Triangles
0x0097E (Stars & Squares and Triangles 2) - 0x0097D - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Stars and Triangles of same color
0x00994 (Rotated Shapers and Triangles 1) - True - Rotated Shapers & Triangles
0x334D5 (Rotated Shapers and Triangles 2) - 0x00994 - Rotated Shapers & Triangles
0x00995 (Rotated Shapers and Triangles 3) - 0x334D5 - Rotated Shapers & Triangles
0x00996 (Shapers and Triangles 1) - 0x00995 - Shapers & Triangles
0x00998 (Shapers and Triangles 2) - 0x00996 - Shapers & Triangles
0x009A4 (Broken Shapers) - True - Shapers & Broken Shapers
0x018A0 (Symmetry Shapers) - True - Shapers & Symmetry
0x00A72 (Broken and Negative Shapers) - True - Shapers & Broken Shapers & Negative Shapers
0x32962 (Rotated Broken Shapers) - True - Rotated Shapers & Broken Rotated Shapers
0x32966 (Stars and Squares) - True - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol
0x01A31 (Rainbow Squares) - True - Color Cycle & RGB & Squares & Colored Squares
0x00B71 (Squares & Stars and Colored Eraser) - True - Colored Eraser & Squares & Colored Squares & Stars & Stars + Same Colored Symbol
0x09DD5 (Lone Pillar) - True - Pillar & Triangles
0x0A16E (Door to Challenge) - 0x09DD5 - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol
0x288EA (Wooden Beam Shapers) - True - Environment & Shapers
0x288FC (Wooden Beam Squares and Shapers) - True - Environment & Squares & Black/White Squares & Shapers & Rotated Shapers
0x289E7 (Wooden Beam Shapers and Squares) - True - Environment & Stars & Squares & Black/White Squares
0x288AA (Wooden Beam Shapers and Stars) - True - Environment & Stars & Shapers
0x17FB9 (Upstairs Dot Grid Negative Shapers) - True - Shapers & Dots & Negative Shapers
0x0A16B (Upstairs Dot Grid Squares) - True - Squares & Black/White Squares & Colored Squares & Dots
0x0A2CE (Upstairs Dot Grid Stars) - 0x0A16B - Stars & Dots
0x0A2D7 (Upstairs Dot Grid Triangles) - 0x0A2CE - Triangles & Dots
0x0A2DD (Upstairs Dot Grid Shapers) - 0x0A2D7 - Shapers & Dots
0x0A2EA (Upstairs Dot Grid Rotated Shapers) - 0x0A2DD - Rotated Shapers & Dots
0x0008F (Upstairs Invisible Dots 1) - True - Dots & Invisible Dots
0x0006B (Upstairs Invisible Dots 2) - 0x0008F - Dots & Invisible Dots
0x0008B (Upstairs Invisible Dots 3) - 0x0006B - Dots & Invisible Dots
0x0008C (Upstairs Invisible Dots 4) - 0x0008B - Dots & Invisible Dots
0x0008A (Upstairs Invisible Dots 5) - 0x0008C - Dots & Invisible Dots
0x00089 (Upstairs Invisible Dots 6) - 0x0008A - Dots & Invisible Dots
0x0006A (Upstairs Invisible Dots 7) - 0x00089 - Dots & Invisible Dots
0x0006C (Upstairs Invisible Dots 8) - 0x0006A - Dots & Invisible Dots
0x00027 (Upstairs Invisible Dot Symmetry 1) - True - Dots & Invisible Dots & Symmetry
0x00028 (Upstairs Invisible Dot Symmetry 2) - 0x00027 - Dots & Invisible Dots & Symmetry
0x00029 (Upstairs Invisible Dot Symmetry 3) - 0x00028 - Dots & Invisible Dots & Symmetry
Challenge (Challenge) - Inside Mountain Secret Area - 0x0A16E:
0x0A332 (Start Timer) - True - True
0x0088E (Small Basic) - 0x0A332 - True
0x00BAF (Big Basic) - 0x0088E - True
0x00BF3 (Square) - 0x00BAF - Squares & Black/White Squares
0x00C09 (Maze Map) - 0x00BF3 - Dots
0x00CDB (Stars and Dots) - 0x00C09 - Stars & Dots
0x0051F (Symmetry) - 0x00CDB - Symmetry & Colored Dots & Dots
0x00524 (Stars and Shapers) - 0x0051F - Stars & Shapers
0x00CD4 (Big Basic 2) - 0x00524 - True
0x00CB9 (Choice Squares Right) - 0x00CD4 - Squares & Black/White Squares
0x00CA1 (Choice Squares Middle) - 0x00CD4 - Squares & Black/White Squares
0x00C80 (Choice Squares Left) - 0x00CD4 - Squares & Black/White Squares
0x00C68 (Choice Squares 2 Right) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares
0x00C59 (Choice Squares 2 Middle) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares
0x00C22 (Choice Squares 2 Left) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares
0x034F4 (Maze Hidden 1) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles
0x034EC (Maze Hidden 2) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles
0x1C31A (Dots Pillar) - 0x034F4 & 0x034EC - Dots & Symmetry & Pillar
0x1C319 (Squares Pillar) - 0x034F4 & 0x034EC - Squares & Black/White Squares & Symmetry & Pillar
0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True
0x039B4 (Door to Theater Walkway) - True - Triangles
Theater Walkway (Theater Walkway) - Challenge - 0x039B4 - Theater - 0x27732 - Desert Elevator Room - 0x2773D & 0x03608 - Town - 0x09E85:
0x2FAF6 (Vault Box) - True - True
0x27732 (Door to Back of Theater) - True - True
0x2773D (Door to Desert Elevator Room) - True - True
0x09E85 (Door to Town) - True - Triangles
Final Room (Inside Mountain Final Room) - Inside Mountain Bottom Layer - 0x01983 & 0x01987:
0x0383A (Stars Pillar) - True - Stars & Pillar
0x09E56 (Stars and Dots Pillar) - 0x0383A - Stars & Dots & Pillar
0x09E5A (Dot Grid Pillar) - 0x09E56 - Dots & Pillar
0x33961 (Sparse Dots Pillar) - 0x09E5A - Dots & Pillar
0x0383D (Dot Maze Pillar) - True - Dots & Pillar
0x0383F (Squares Pillar) - 0x0383D - Squares & Black/White Squares & Pillar
0x03859 (Shapers Pillar) - 0x0383F - Shapers & Pillar
0x339BB (Squares and Stars) - 0x03859 - Squares & Black/White Squares & Stars & Pillar
Elevator (Inside Mountain Final Room) - Final Room - 0x339BB & 0x33961:
0x3D9A6 (Elevator Door Closer Left) - True - True
0x3D9A7 (Elevator Door Close Right) - True - True
0x3C113 (Elevator Door Open Left) - 0x3D9A6 | 0x3D9A7 - True
0x3C114 (Elevator Door Open Right) - 0x3D9A6 | 0x3D9A7 - True
0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
0x3D9A9 (Elevator Start) - 0x3D9AA | 0x3D9A8 - True
Boat (Boat) - Main Island - 0x17CDF | 0x17CC8 | 0x17CA6 | 0x09DB8 | 0x17C95 - Inside Glass Factory - 0x17CDF | 0x17CC8 | 0x17CA6 | 0x09DB8 | 0x17C95 - Quarry Boathouse - 0x17CDF | 0x17CC8 | 0x17CA6 | 0x09DB8 | 0x17C95 - Swamp Near Boat - 0x17CDF | 0x17CC8 | 0x17CA6 | 0x09DB8 | 0x17C95 - Treehouse Entry Area - 0x17CDF | 0x17CC8 | 0x17CA6 | 0x09DB8 | 0x17C95:

165
worlds/witness/__init__.py Normal file
View File

@ -0,0 +1,165 @@
"""
Archipelago init file for The Witness
"""
import typing
from BaseClasses import Region, RegionType, Location, MultiWorld, Item, Entrance
from ..AutoWorld import World, WebWorld
from .player_logic import StaticWitnessLogic, WitnessPlayerLogic
from .locations import WitnessPlayerLocations, StaticWitnessLocations
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems
from .rules import set_rules
from .regions import WitnessRegions
from .Options import is_option_enabled, the_witness_options
class WitnessWebWorld(WebWorld):
theme = "jungle"
class WitnessWorld(World):
"""
The Witness is an open-world puzzle game with dozens of locations
to explore and over 500 puzzles. Play the popular puzzle randomizer
by sigma144, with an added layer of progression randomization!
"""
game = "The Witness"
topology_present = False
static_logic = StaticWitnessLogic()
static_locat = StaticWitnessLocations()
static_items = StaticWitnessItems()
web = WitnessWebWorld()
options = the_witness_options
item_name_to_id = {
name: data.code for name, data in static_items.ALL_ITEM_TABLE.items()
}
location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID
def _get_slot_data(self):
return {
'seed': self.world.random.randint(0, 1000000),
'victory_location': int(self.player_logic.VICTORY_LOCATION, 16),
'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID
}
def generate_early(self):
self.player_logic = WitnessPlayerLogic(self.world, self.player)
self.locat = WitnessPlayerLocations(self.world, self.player, self.player_logic)
self.items = WitnessPlayerItems(self.locat, self.world, self.player, self.player_logic)
self.regio = WitnessRegions(self.locat)
def generate_basic(self):
# Generate item pool
pool = []
items_by_name = dict()
for item in self.items.ITEM_TABLE:
witness_item = self.create_item(item)
if item not in self.items.EVENT_ITEM_TABLE:
pool.append(witness_item)
items_by_name[item] = witness_item
# Put good item on first check
random_good_item = self.world.random.choice(self.items.GOOD_ITEMS)
first_check = self.world.get_location(
"Tutorial Gate Open", self.player
)
first_check.place_locked_item(items_by_name[random_good_item])
pool.remove(items_by_name[random_good_item])
# Put in junk items to fill the rest
junk_pool = self.items.JUNK_WEIGHTS.copy()
junk_pool = self.world.random.choices(
list(junk_pool.keys()), weights=list(junk_pool.values()),
k=len(self.locat.CHECK_LOCATION_TABLE) - len(pool) - len(self.locat.EVENT_LOCATION_TABLE) - 1
)
pool += [self.create_item(junk) for junk in junk_pool]
# Tie Event Items to Event Locations (e.g. Laser Activations)
for event_location in self.locat.EVENT_LOCATION_TABLE:
item_obj = self.create_item(
self.player_logic.EVENT_ITEM_PAIRS[event_location]
)
location_obj = self.world.get_location(event_location, self.player)
location_obj.place_locked_item(item_obj)
self.world.itempool += pool
def create_regions(self):
self.regio.create_regions(self.world, self.player, self.player_logic)
def set_rules(self):
set_rules(self.world, self.player, self.player_logic, self.locat)
def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data()
slot_data["hard_mode"] = False
for option_name in the_witness_options:
slot_data[option_name] = is_option_enabled(
self.world, self.player, option_name
)
return slot_data
def create_item(self, name: str) -> Item:
# this conditional is purely for unit tests, which need to be able to create an item before generate_early
if hasattr(self, 'items'):
item = self.items.ITEM_TABLE[name]
else:
item = StaticWitnessItems.ALL_ITEM_TABLE[name]
new_item = WitnessItem(
name, item.progression, item.code, player=self.player
)
new_item.trap = item.trap
return new_item
def get_filler_item_name(self) -> str: # Used ny itemlinks
junk_pool = self.items.JUNK_WEIGHTS.copy()
return self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()))[0]
class WitnessLocation(Location):
"""
Archipelago Location for The Witness
"""
game: str = "The Witness"
check_hex: int = -1
def __init__(self, player: int, name: str, address: typing.Optional[int], parent, ch_hex: int = -1):
super().__init__(player, name, address, parent)
self.check_hex = ch_hex
def create_region(world: MultiWorld, player: int, name: str,
locat: WitnessPlayerLocations, region_locations=None, exits=None):
"""
Create an Archipelago Region for The Witness
"""
ret = Region(name, RegionType.Generic, name, player)
ret.world = world
if region_locations:
for location in region_locations:
loc_id = locat.CHECK_LOCATION_TABLE[location]
check_hex = -1
if location in StaticWitnessLogic.CHECKS_BY_NAME:
check_hex = int(
StaticWitnessLogic.CHECKS_BY_NAME[location]["checkHex"], 0
)
location = WitnessLocation(
player, location, loc_id, ret, check_hex
)
ret.locations.append(location)
if exits:
for single_exit in exits:
ret.exits.append(Entrance(player, single_exit, ret))
return ret

98
worlds/witness/items.py Normal file
View File

@ -0,0 +1,98 @@
"""
Defines progression, junk and event items for The Witness
"""
import copy
from typing import Dict, NamedTuple, Optional
from BaseClasses import Item, MultiWorld
from . import StaticWitnessLogic, WitnessPlayerLocations, WitnessPlayerLogic
from .Options import is_option_enabled
class ItemData(NamedTuple):
"""
ItemData for an item in The Witness
"""
code: Optional[int]
progression: bool
event: bool = False
trap: bool = False
class WitnessItem(Item):
"""
Item from the game The Witness
"""
game: str = "The Witness"
class StaticWitnessItems:
"""
Class that handles Witness items independent of world settings
"""
ALL_ITEM_TABLE: Dict[str, ItemData] = {}
JUNK_WEIGHTS = {
"Speed Boost": 1,
"Slowness": 0.8,
"Power Surge": 0.2,
}
def __init__(self):
item_tab = dict()
for item in StaticWitnessLogic.ALL_ITEMS:
if item[0] == "11 Lasers" or item == "7 Lasers":
continue
item_tab[item[0]] = ItemData(158000 + item[1], True, False)
for item in StaticWitnessLogic.ALL_TRAPS:
item_tab[item[0]] = ItemData(
158000 + item[1], False, False, True
)
for item in StaticWitnessLogic.ALL_BOOSTS:
item_tab[item[0]] = ItemData(158000 + item[1], False, False)
item_tab = dict(sorted(
item_tab.items(),
key=lambda single_item: single_item[1].code
if isinstance(single_item[1].code, int) else 0)
)
for key, item in item_tab.items():
self.ALL_ITEM_TABLE[key] = item
class WitnessPlayerItems:
"""
Class that defines Items for a single world
"""
def __init__(self, locat: WitnessPlayerLocations, world: MultiWorld, player: int, player_logic: WitnessPlayerLogic):
"""Adds event items after logic changes due to options"""
self.EVENT_ITEM_TABLE = dict()
self.ITEM_TABLE = copy.copy(StaticWitnessItems.ALL_ITEM_TABLE)
self.GOOD_ITEMS = [
"Dots", "Black/White Squares", "Stars",
"Shapers", "Symmetry"
]
if is_option_enabled(world, player, "shuffle_discarded_panels"):
self.GOOD_ITEMS.append("Triangles")
if not is_option_enabled(world, player, "disable_non_randomized_puzzles"):
self.GOOD_ITEMS.append("Colored Squares")
for event_location in locat.EVENT_LOCATION_TABLE:
location = player_logic.EVENT_ITEM_PAIRS[event_location]
self.EVENT_ITEM_TABLE[location] = ItemData(None, True, True)
self.ITEM_TABLE[location] = ItemData(None, True, True)
self.JUNK_WEIGHTS = {
key: value for (key, value)
in StaticWitnessItems.JUNK_WEIGHTS.items()
if key in self.ITEM_TABLE.keys()
}

281
worlds/witness/locations.py Normal file
View File

@ -0,0 +1,281 @@
"""
Defines constants for different types of locations in the game
"""
from .Options import is_option_enabled
from .player_logic import StaticWitnessLogic, WitnessPlayerLogic
class StaticWitnessLocations:
"""
Witness Location Constants that stay consistent across worlds
"""
ID_START = 158000
TYPE_OFFSETS = {
"General": 0,
"Discard": 600,
"Vault": 650,
"Laser": 700,
}
GENERAL_LOCATIONS = {
"Tutorial Gate Open",
"Outside Tutorial Vault Box",
"Outside Tutorial Discard",
"Outside Tutorial Dots Introduction 5",
"Outside Tutorial Squares Introduction 9",
"Glass Factory Discard",
"Glass Factory Vertical Symmetry 5",
"Glass Factory Rotational Symmetry 3",
"Glass Factory Melting 3",
"Symmetry Island Black Dots 5",
"Symmetry Island Colored Dots 6",
"Symmetry Island Fading Lines 7",
"Symmetry Island Scenery Outlines 5",
"Symmetry Island Laser",
"Orchard Apple Tree 5",
"Desert Vault Box",
"Desert Discard",
"Desert Sun Reflection 8",
"Desert Artificial Light Reflection 3",
"Desert Pond Reflection 5",
"Desert Flood Reflection 6",
"Desert Laser",
"Quarry Mill Eraser and Dots 6",
"Quarry Mill Eraser and Squares 8",
"Quarry Mill Small Squares & Dots & and Eraser",
"Quarry Mill Big Squares & Dots & and Eraser",
"Quarry Boathouse Intro Shapers",
"Quarry Boathouse Eraser and Shapers 5",
"Quarry Boathouse Stars & Eraser & and Shapers 2",
"Quarry Boathouse Stars & Eraser & and Shapers 5",
"Quarry Discard",
"Quarry Laser",
"Shadows Lower Avoid 8",
"Shadows Environmental Avoid 8",
"Shadows Follow 5",
"Shadows Laser",
"Keep Hedge Maze 4",
"Keep Pressure Plates 4",
"Keep Discard",
"Keep Laser Hedges",
"Keep Laser Pressure Plates",
"Shipwreck Vault Box",
"Shipwreck Discard",
"Monastery Rhombic Avoid 3",
"Monastery Branch Follow 2",
"Monastery Laser",
"Town Cargo Box Discard",
"Town Hexagonal Reflection",
"Town Square Avoid",
"Town Rooftop Discard",
"Town Symmetry Squares 5 + Dots",
"Town Full Dot Grid Shapers 5",
"Town Shapers & Dots & and Eraser",
"Town Laser",
"Theater Discard",
"Jungle Discard",
"Jungle Waves 3",
"Jungle Waves 7",
"Jungle Popup Wall 6",
"Jungle Laser",
"River Vault Box",
"Bunker Drawn Squares 5",
"Bunker Drawn Squares 9",
"Bunker Drawn Squares through Tinted Glass 3",
"Bunker Drop-Down Door Squares 2",
"Bunker Laser",
"Swamp Seperatable Shapers 6",
"Swamp Combinable Shapers 8",
"Swamp Broken Shapers 4",
"Swamp Cyan Underwater Negative Shapers 5",
"Swamp Platform Shapers 4",
"Swamp Rotated Shapers 4",
"Swamp Red Underwater Negative Shapers 4",
"Swamp More Rotated Shapers 4",
"Swamp Blue Underwater Negative Shapers 5",
"Swamp Laser",
"Treehouse Yellow Bridge 9",
"Treehouse First Purple Bridge 5",
"Treehouse Second Purple Bridge 7",
"Treehouse Green Bridge 7",
"Treehouse Green Bridge Discard",
"Treehouse Left Orange Bridge 15",
"Treehouse Burned House Discard",
"Treehouse Right Orange Bridge 12",
"Treehouse Laser",
"Mountaintop Trap Door Triple Exit",
"Mountaintop Discard",
"Mountaintop Vault Box",
"Inside Mountain Obscured Vision 5",
"Inside Mountain Moving Background 7",
"Inside Mountain Physically Obstructed 3",
"Inside Mountain Angled Inside Trash 2",
"Inside Mountain Color Cycle 5",
"Inside Mountain Same Solution 6",
"Inside Mountain Elevator Discard",
"Inside Mountain Giant Puzzle",
}
UNCOMMON_LOCATIONS = {
"Mountaintop River Shape",
"Tutorial Patio Floor",
"Theater Tutorial Video",
"Theater Desert Video",
"Theater Jungle Video",
"Theater Shipwreck Video",
"Theater Mountain Video",
"Town RGB Squares",
"Town RGB Stars",
"Swamp Underwater Back Optional",
}
HARD_LOCATIONS = {
"Tutorial Gate Close",
"Inside Mountain Secret Area Dot Grid Triangles 4",
"Inside Mountain Secret Area Symmetry Triangles",
"Inside Mountain Secret Area Stars & Squares and Triangles 2",
"Inside Mountain Secret Area Shapers and Triangles 2",
"Inside Mountain Secret Area Symmetry Shapers",
"Inside Mountain Secret Area Broken and Negative Shapers",
"Inside Mountain Secret Area Broken Shapers",
"Inside Mountain Secret Area Rainbow Squares",
"Inside Mountain Secret Area Squares & Stars and Colored Eraser",
"Inside Mountain Secret Area Rotated Broken Shapers",
"Inside Mountain Secret Area Stars and Squares",
"Inside Mountain Secret Area Lone Pillar",
"Inside Mountain Secret Area Wooden Beam Shapers",
"Inside Mountain Secret Area Wooden Beam Squares and Shapers",
"Inside Mountain Secret Area Wooden Beam Shapers and Squares",
"Inside Mountain Secret Area Wooden Beam Shapers and Stars",
"Inside Mountain Secret Area Upstairs Invisible Dots 8",
"Inside Mountain Secret Area Upstairs Invisible Dot Symmetry 3",
"Inside Mountain Secret Area Upstairs Dot Grid Shapers",
"Inside Mountain Secret Area Upstairs Dot Grid Rotated Shapers",
"Challenge Vault Box",
"Theater Walkway Vault Box",
"Inside Mountain Bottom Layer Discard",
"Theater Challenge Video",
}
ALL_LOCATIONS_TO_ID = dict()
@staticmethod
def get_id(chex):
"""
Calculates the location ID for any given location
"""
panel_offset = StaticWitnessLogic.CHECKS_BY_HEX[chex]["idOffset"]
type_offset = StaticWitnessLocations.TYPE_OFFSETS[
StaticWitnessLogic.CHECKS_BY_HEX[chex]["panelType"]
]
return StaticWitnessLocations.ID_START + panel_offset + type_offset
@staticmethod
def get_event_name(panel_hex):
"""
Returns the event name of any given panel.
Currently this is always "Panelname Solved"
"""
return StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]["checkName"] + " Solved"
def __init__(self):
all_loc_to_id = {
panel_obj["checkName"]: self.get_id(chex)
for chex, panel_obj in StaticWitnessLogic.CHECKS_BY_HEX.items()
}
all_loc_to_id = dict(
sorted(all_loc_to_id.items(), key=lambda loc: loc[1])
)
for key, item in all_loc_to_id.items():
self.ALL_LOCATIONS_TO_ID[key] = item
class WitnessPlayerLocations:
"""
Class that defines locations for a single player
"""
def __init__(self, world, player, player_logic: WitnessPlayerLogic):
self.PANEL_TYPES_TO_SHUFFLE = {"General", "Laser"}
self.CHECK_LOCATIONS = (
StaticWitnessLocations.GENERAL_LOCATIONS
)
"""Defines locations AFTER logic changes due to options"""
if is_option_enabled(world, player, "shuffle_discarded_panels"):
self.PANEL_TYPES_TO_SHUFFLE.add("Discard")
if is_option_enabled(world, player, "shuffle_vault_boxes"):
self.PANEL_TYPES_TO_SHUFFLE.add("Vault")
if is_option_enabled(world, player, "shuffle_uncommon"):
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.UNCOMMON_LOCATIONS
if is_option_enabled(world, player, "shuffle_hard"):
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.HARD_LOCATIONS
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | player_logic.ADDED_CHECKS
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS - {
StaticWitnessLogic.CHECKS_BY_HEX[check_hex]["checkName"]
for check_hex in player_logic.COMPLETELY_DISABLED_CHECKS
}
self.CHECK_PANELHEX_TO_ID = {
StaticWitnessLogic.CHECKS_BY_NAME[ch]["checkHex"]: StaticWitnessLocations.ALL_LOCATIONS_TO_ID[ch]
for ch in self.CHECK_LOCATIONS
}
self.CHECK_PANELHEX_TO_ID = dict(
sorted(self.CHECK_PANELHEX_TO_ID.items(), key=lambda item: item[1])
)
event_locations = {
p for p in player_logic.NECESSARY_EVENT_PANELS
if StaticWitnessLogic.CHECKS_BY_HEX[p]["checkName"]
not in self.CHECK_LOCATIONS
or p in player_logic.ALWAYS_EVENT_HEX_CODES
}
self.EVENT_LOCATION_TABLE = {
StaticWitnessLocations.get_event_name(panel_hex): None
for panel_hex in event_locations
}
check_dict = {
location: StaticWitnessLocations.get_id(StaticWitnessLogic.CHECKS_BY_NAME[location]["checkHex"])
for location in self.CHECK_LOCATIONS
if StaticWitnessLogic.CHECKS_BY_NAME[location]["panelType"] in self.PANEL_TYPES_TO_SHUFFLE
}
self.CHECK_LOCATION_TABLE = {**self.EVENT_LOCATION_TABLE, **check_dict}

View File

@ -0,0 +1,287 @@
"""
Parses the WitnessLogic.txt logic file into useful data structures.
This is the heart of the randomization.
In WitnessLogic.txt we have regions defined with their connections:
Region Name (Short name) - Connected Region 1 - Connection Requirement 1 - Connected Region 2...
And then panels in that region with the hex code used in the game
previous panels that are required to turn them on, as well as the symbols they require:
0x##### (Panel Name) - Required Panels - Required Items
On __init__, the base logic is read and all panels are given Location IDs.
When the world has parsed its options, a second function is called to finalize the logic.
"""
import copy
from BaseClasses import MultiWorld
from .static_logic import StaticWitnessLogic
from .utils import define_new_region, get_disable_unrandomized_list, parse_lambda
from .Options import is_option_enabled
class WitnessPlayerLogic:
"""WITNESS LOGIC CLASS"""
def reduce_req_within_region(self, panel_hex):
"""
Panels in this game often only turn on when other panels are solved.
Those other panels may have different item requirements.
It would be slow to recursively check solvability each time.
This is why we reduce the item dependencies within the region.
Panels outside of the same region will still be checked manually.
"""
if self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"] == frozenset({frozenset()}):
return self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["items"]
all_options = set()
these_items = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["items"]
these_panels = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"]
check_obj = StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]
for option in these_panels:
dependent_items_for_option = frozenset({frozenset()})
for option_panel in option:
new_items = set()
dep_obj = StaticWitnessLogic.CHECKS_BY_HEX.get(option_panel)
if option_panel in {"7 Lasers", "11 Lasers"}:
new_items = frozenset({frozenset([option_panel])})
# If a panel turns on when a panel in a different region turns on,
# the latter panel will be an "event panel", unless it ends up being
# a location itself. This prevents generation failures.
elif dep_obj["region"]["name"] != check_obj["region"]["name"]:
new_items = frozenset({frozenset([option_panel])})
self.EVENT_PANELS_FROM_PANELS.add(option_panel)
else:
new_items = self.reduce_req_within_region(option_panel)
updated_items = set()
for items_option in dependent_items_for_option:
for items_option2 in new_items:
updated_items.add(items_option.union(items_option2))
dependent_items_for_option = updated_items
for items_option in these_items:
for dependentItem in dependent_items_for_option:
all_options.add(items_option.union(dependentItem))
return frozenset(all_options)
def make_single_adjustment(self, adj_type, line):
"""Makes a single logic adjustment based on additional logic file"""
if adj_type == "Event Items":
line_split = line.split(" - ")
hex_set = line_split[1].split(",")
for hex_code in hex_set:
self.ALWAYS_EVENT_NAMES_BY_HEX[hex_code] = line_split[0]
"""
Should probably do this differently...
Events right now depend on a panel.
That seems bad.
"""
to_remove = set()
for hex_code, event_name in self.ALWAYS_EVENT_NAMES_BY_HEX.items():
if hex_code not in hex_set and event_name == line_split[0]:
to_remove.add(hex_code)
for remove in to_remove:
del self.ALWAYS_EVENT_NAMES_BY_HEX[remove]
return
if adj_type == "Requirement Changes":
line_split = line.split(" - ")
required_items = parse_lambda(line_split[2])
items_actually_in_the_game = {item[0] for item in StaticWitnessLogic.ALL_ITEMS}
required_items = frozenset(
subset.intersection(items_actually_in_the_game)
for subset in required_items
)
requirement = {
"panels": parse_lambda(line_split[1]),
"items": required_items
}
self.DEPENDENT_REQUIREMENTS_BY_HEX[line_split[0]] = requirement
return
if adj_type == "Disabled Locations":
self.COMPLETELY_DISABLED_CHECKS.add(line[:7])
return
if adj_type == "Region Changes":
new_region_and_options = define_new_region(line + ":")
self.CONNECTIONS_BY_REGION_NAME[new_region_and_options[0]["name"]] = new_region_and_options[1]
return
if adj_type == "Added Locations":
self.ADDED_CHECKS.add(line)
def make_options_adjustments(self, world, player):
"""Makes logic adjustments based on options"""
adjustment_linesets_in_order = []
if is_option_enabled(world, player, "challenge_victory"):
self.VICTORY_LOCATION = "0x0356B"
else:
self.VICTORY_LOCATION = "0x3D9A9"
self.COMPLETELY_DISABLED_CHECKS.add(
self.VICTORY_LOCATION
)
if is_option_enabled(world, player, "disable_non_randomized_puzzles"):
adjustment_linesets_in_order.append(get_disable_unrandomized_list())
for adjustment_lineset in adjustment_linesets_in_order:
current_adjustment_type = None
for line in adjustment_lineset:
if len(line) == 0:
continue
if line[-1] == ":":
current_adjustment_type = line[:-1]
continue
self.make_single_adjustment(current_adjustment_type, line)
def make_dependency_reduced_checklist(self):
"""
Turns dependent check set into semi-independent check set
"""
for check_hex in self.DEPENDENT_REQUIREMENTS_BY_HEX.keys():
indep_requirement = self.reduce_req_within_region(check_hex)
self.REQUIREMENTS_BY_HEX[check_hex] = indep_requirement
def make_event_item_pair(self, panel):
"""
Makes a pair of an event panel and its event item
"""
name = StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + " Solved"
pair = (name, self.EVENT_ITEM_NAMES[panel])
return pair
def make_event_panel_lists(self):
"""
Special event panel data structures
"""
for region_conn in self.CONNECTIONS_BY_REGION_NAME.values():
for region_and_option in region_conn:
for panelset in region_and_option[1]:
for panel in panelset:
self.EVENT_PANELS_FROM_REGIONS.add(panel)
self.ALWAYS_EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory"
self.ORIGINAL_EVENT_PANELS.update(self.EVENT_PANELS_FROM_PANELS)
self.ORIGINAL_EVENT_PANELS.update(self.EVENT_PANELS_FROM_REGIONS)
self.NECESSARY_EVENT_PANELS.update(self.EVENT_PANELS_FROM_PANELS)
for panel in self.EVENT_PANELS_FROM_REGIONS:
for region_name, region in StaticWitnessLogic.ALL_REGIONS_BY_NAME.items():
for connection in self.CONNECTIONS_BY_REGION_NAME[region_name]:
connected_r = connection[0]
if connected_r not in StaticWitnessLogic.ALL_REGIONS_BY_NAME:
continue
if region_name == "Boat" or connected_r == "Boat":
continue
connected_r = StaticWitnessLogic.ALL_REGIONS_BY_NAME[connected_r]
if not any([panel in option for option in connection[1]]):
continue
if panel not in region["panels"] | connected_r["panels"]:
self.NECESSARY_EVENT_PANELS.add(panel)
for always_hex, always_item in self.ALWAYS_EVENT_NAMES_BY_HEX.items():
self.ALWAYS_EVENT_HEX_CODES.add(always_hex)
self.NECESSARY_EVENT_PANELS.add(always_hex)
self.EVENT_ITEM_NAMES[always_hex] = always_item
for panel in self.NECESSARY_EVENT_PANELS:
pair = self.make_event_item_pair(panel)
self.EVENT_ITEM_PAIRS[pair[0]] = pair[1]
def __init__(self, world: MultiWorld, player: int):
self.EVENT_PANELS_FROM_PANELS = set()
self.EVENT_PANELS_FROM_REGIONS = set()
self.CONNECTIONS_BY_REGION_NAME = copy.copy(StaticWitnessLogic.STATIC_CONNECTIONS_BY_REGION_NAME)
self.DEPENDENT_REQUIREMENTS_BY_HEX = copy.copy(StaticWitnessLogic.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX)
self.REQUIREMENTS_BY_HEX = dict()
# Determining which panels need to be events is a difficult process.
# At the end, we will have EVENT_ITEM_PAIRS for all the necessary ones.
self.ORIGINAL_EVENT_PANELS = set()
self.NECESSARY_EVENT_PANELS = set()
self.EVENT_ITEM_PAIRS = dict()
self.ALWAYS_EVENT_HEX_CODES = set()
self.COMPLETELY_DISABLED_CHECKS = set()
self.ADDED_CHECKS = set()
self.VICTORY_LOCATION = "0x0356B"
self.EVENT_ITEM_NAMES = {
"0x01A0F": "Keep Laser Panel (Hedge Mazes) Activates",
"0x09D9B": "Monastery Overhead Doors Open",
"0x193A6": "Monastery Laser Panel Activates",
"0x00037": "Monastery Branch Panels Activate",
"0x0A079": "Access to Bunker Laser",
"0x0A3B5": "Door to Tutorial Discard Opens",
"0x01D3F": "Keep Laser Panel (Pressure Plates) Activates",
"0x09F7F": "Mountain Access",
"0x0367C": "Quarry Laser Mill Requirement Met",
"0x009A1": "Swamp Rotating Bridge Near Side",
"0x00006": "Swamp Cyan Water Drains",
"0x00990": "Swamp Broken Shapers 1 Activates",
"0x0A8DC": "Lower Avoid 6 Activates",
"0x0000A": "Swamp More Rotated Shapers 1 Access",
"0x09ED8": "Inside Mountain Second Layer Both Light Bridges Solved",
"0x0A3D0": "Quarry Laser Boathouse Requirement Met",
"0x00596": "Swamp Red Water Drains",
"0x28B39": "Town Tower 4th Door Opens"
}
self.ALWAYS_EVENT_NAMES_BY_HEX = {
"0x0360D": "Symmetry Laser Activation",
"0x03608": "Desert Laser Activation",
"0x09F98": "Desert Laser Redirection",
"0x03612": "Quarry Laser Activation",
"0x19650": "Shadows Laser Activation",
"0x0360E": "Keep Laser Hedges Activation",
"0x03317": "Keep Laser Pressure Plates Activation",
"0x17CA4": "Monastery Laser Activation",
"0x032F5": "Town Laser Activation",
"0x03616": "Jungle Laser Activation",
"0x09DE0": "Bunker Laser Activation",
"0x03615": "Swamp Laser Activation",
"0x03613": "Treehouse Laser Activation",
"0x03535": "Shipwreck Video Pattern Knowledge",
"0x03542": "Mountain Video Pattern Knowledge",
"0x0339E": "Desert Video Pattern Knowledge",
"0x03481": "Tutorial Video Pattern Knowledge",
"0x03702": "Jungle Video Pattern Knowledge",
"0x2FAF6": "Theater Walkway Video Pattern Knowledge",
}
self.make_options_adjustments(world, player)
self.make_dependency_reduced_checklist()
self.make_event_panel_lists()

89
worlds/witness/regions.py Normal file
View File

@ -0,0 +1,89 @@
"""
Defines Region for The Witness, assigns locations to them,
and connects them with the proper requirements
"""
from BaseClasses import MultiWorld, Entrance
from . import StaticWitnessLogic
from .locations import WitnessPlayerLocations
from .player_logic import WitnessPlayerLogic
class WitnessRegions:
"""Class that defines Witness Regions"""
locat = None
logic = None
def make_lambda(self, panel_hex_to_solve_set, world, player, player_logic):
"""
Lambdas are made in a for loop, so the values have to be captured
This function is for that purpose
"""
return lambda state: state._witness_can_solve_panels(
panel_hex_to_solve_set, world, player, player_logic, self.locat
)
def connect(self, world: MultiWorld, player: int, source: str, target: str, player_logic: WitnessPlayerLogic,
panel_hex_to_solve_set=None):
"""
connect two regions and set the corresponding requirement
"""
source_region = world.get_region(source, player)
target_region = world.get_region(target, player)
connection = Entrance(
player,
source + " to " + target + " via " + str(panel_hex_to_solve_set),
source_region
)
connection.access_rule = self.make_lambda(panel_hex_to_solve_set, world, player, player_logic)
source_region.exits.append(connection)
connection.connect(target_region)
def create_regions(self, world, player: int, player_logic: WitnessPlayerLogic):
"""
Creates all the regions for The Witness
"""
from . import create_region
world.regions += [
create_region(world, player, 'Menu', self.locat, None, ["The Splashscreen?"]),
]
all_locations = set()
for region_name, region in StaticWitnessLogic.ALL_REGIONS_BY_NAME.items():
locations_for_this_region = [
StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] for panel in region["panels"]
if StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] in self.locat.CHECK_LOCATION_TABLE
]
locations_for_this_region += [
StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + " Solved" for panel in region["panels"]
if StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + " Solved" in self.locat.EVENT_LOCATION_TABLE
]
all_locations = all_locations | set(locations_for_this_region)
world.regions += [
create_region(world, player, region_name, self.locat,locations_for_this_region)
]
for region_name, region in StaticWitnessLogic.ALL_REGIONS_BY_NAME.items():
for connection in player_logic.CONNECTIONS_BY_REGION_NAME[region_name]:
if connection[0] == "Entry":
continue
self.connect(world, player, region_name,
connection[0], player_logic, connection[1])
self.connect(world, player, connection[0],
region_name, player_logic, connection[1])
world.get_entrance("The Splashscreen?", player).connect(
world.get_region('First Hallway', player)
)
def __init__(self, locat: WitnessPlayerLocations):
self.locat = locat

171
worlds/witness/rules.py Normal file
View File

@ -0,0 +1,171 @@
"""
Defines the rules by which locations can be accessed,
depending on the items received
"""
# pylint: disable=E1101
from BaseClasses import MultiWorld
from .player_logic import WitnessPlayerLogic
from .Options import is_option_enabled
from .locations import WitnessPlayerLocations
from . import StaticWitnessLogic
from ..AutoWorld import LogicMixin
from ..generic.Rules import set_rule
class WitnessLogic(LogicMixin):
"""
Logic macros that get reused
"""
def _witness_has_lasers(self, world, player: int, amount: int) -> bool:
lasers = 0
lasers += int(self.has("Symmetry Laser Activation", player))
lasers += int(self.has("Desert Laser Activation", player)
and self.has("Desert Laser Redirection", player))
lasers += int(self.has("Town Laser Activation", player))
lasers += int(self.has("Monastery Laser Activation", player))
lasers += int(self.has("Keep Laser Pressure Plates Activation", player) and (
is_option_enabled(world, player, "disable_non_randomized_puzzles")
or self.has("Keep Laser Hedges Activation", player)
))
lasers += int(self.has("Quarry Laser Activation", player))
lasers += int(self.has("Treehouse Laser Activation", player))
lasers += int(self.has("Jungle Laser Activation", player))
lasers += int(self.has("Bunker Laser Activation", player))
lasers += int(self.has("Swamp Laser Activation", player))
lasers += int(self.has("Shadows Laser Activation", player))
return lasers >= amount
def _witness_can_solve_panel(self, panel, world, player, player_logic: WitnessPlayerLogic, locat):
"""
Determines whether a panel can be solved
"""
panel_obj = StaticWitnessLogic.CHECKS_BY_HEX[panel]
check_name = panel_obj["checkName"]
if (check_name + " Solved" in locat.EVENT_LOCATION_TABLE
and not self.has(player_logic.EVENT_ITEM_PAIRS[check_name + " Solved"], player)):
return False
if panel not in player_logic.ORIGINAL_EVENT_PANELS and not self.can_reach(check_name, "Location", player):
return False
if (panel in player_logic.ORIGINAL_EVENT_PANELS
and check_name + " Solved" not in locat.EVENT_LOCATION_TABLE
and not self._witness_safe_manual_panel_check(panel, world, player, player_logic, locat)):
return False
return True
def _witness_meets_item_requirements(self, panel, world, player, player_logic: WitnessPlayerLogic, locat):
"""
Checks whether item and panel requirements are met for
a panel
"""
panel_req = player_logic.REQUIREMENTS_BY_HEX[panel]
for option in panel_req:
if len(option) == 0:
return True
valid_option = True
for item in option:
if item == "7 Lasers":
if not self._witness_has_lasers(world, player, 7):
valid_option = False
break
elif item == "11 Lasers":
if not self._witness_has_lasers(world, player, 11):
valid_option = False
break
elif item in player_logic.NECESSARY_EVENT_PANELS:
if StaticWitnessLogic.CHECKS_BY_HEX[item]["checkName"] + " Solved" in locat.EVENT_LOCATION_TABLE:
valid_option = self.has(player_logic.EVENT_ITEM_NAMES[item], player)
else:
valid_option = self.can_reach(
StaticWitnessLogic.CHECKS_BY_HEX[item]["checkName"], "Location", player
)
if not valid_option:
break
elif not self.has(item, player):
valid_option = False
break
if valid_option:
return True
return False
def _witness_safe_manual_panel_check(self, panel, world, player, player_logic: WitnessPlayerLogic, locat):
"""
nested can_reach can cause problems, but only if the region being
checked is neither of the two original regions from the first
can_reach.
A nested can_reach is okay here because the only panels this
function is called on are panels that exist on either side of all
connections they are required for.
The spoiler log looks so much nicer this way,
it gets rid of a bunch of event items, only leaving a couple. :)
"""
region = StaticWitnessLogic.CHECKS_BY_HEX[panel]["region"]["name"]
return (
self._witness_meets_item_requirements(panel, world, player, player_logic, locat)
and self.can_reach(region, "Region", player)
)
def _witness_can_solve_panels(self, panel_hex_to_solve_set, world, player, player_logic: WitnessPlayerLogic, locat):
"""
Checks whether a set of panels can be solved.
"""
for option in panel_hex_to_solve_set:
if len(option) == 0:
return True
valid_option = True
for panel in option:
if not self._witness_can_solve_panel(panel, world, player, player_logic, locat):
valid_option = False
break
if valid_option:
return True
return False
def make_lambda(check_hex, world, player, player_logic, locat):
"""
Lambdas are created in a for loop so values need to be captured
"""
return lambda state: state._witness_meets_item_requirements(
check_hex, world, player, player_logic, locat
)
def set_rules(world: MultiWorld, player: int, player_logic: WitnessPlayerLogic, locat: WitnessPlayerLocations):
"""
Sets all rules for all locations
"""
for location in locat.CHECK_LOCATION_TABLE:
real_location = location
if location in locat.EVENT_LOCATION_TABLE:
real_location = location[:-7]
panel = StaticWitnessLogic.CHECKS_BY_NAME[real_location]
check_hex = panel["checkHex"]
rule = make_lambda(check_hex, world, player, player_logic, locat)
set_rule(world.get_location(location, player), rule)
world.completion_condition[player] = \
lambda state: state.has('Victory', player)

View File

@ -0,0 +1,138 @@
import os
from .utils import define_new_region, parse_lambda
class StaticWitnessLogic:
ALL_ITEMS = set()
ALL_TRAPS = set()
ALL_BOOSTS = set()
EVENT_PANELS_FROM_REGIONS = set()
# All regions with a list of panels in them and the connections to other regions, before logic adjustments
ALL_REGIONS_BY_NAME = dict()
STATIC_CONNECTIONS_BY_REGION_NAME = dict()
CHECKS_BY_HEX = dict()
CHECKS_BY_NAME = dict()
STATIC_DEPENDENT_REQUIREMENTS_BY_HEX = dict()
def parse_items(self):
"""
Parses currently defined items from WitnessItems.txt
"""
path = os.path.join(os.path.dirname(__file__), "WitnessItems.txt")
with open(path, "r", encoding="utf-8") as file:
current_set = self.ALL_ITEMS
for line in file.readlines():
line = line.strip()
if line == "Progression:":
current_set = self.ALL_ITEMS
continue
if line == "Boosts:":
current_set = self.ALL_BOOSTS
continue
if line == "Traps:":
current_set = self.ALL_TRAPS
continue
if line == "":
continue
line_split = line.split(" - ")
current_set.add((line_split[1], int(line_split[0])))
def read_logic_file(self):
"""
Reads the logic file and does the initial population of data structures
"""
path = os.path.join(os.path.dirname(__file__), "WitnessLogic.txt")
with open(path, "r", encoding="utf-8") as file:
current_region = ""
discard_ids = 0
normal_panel_ids = 0
vault_ids = 0
laser_ids = 0
for line in file.readlines():
line = line.strip()
if line == "":
continue
if line[0] != "0":
new_region_and_connections = define_new_region(line)
current_region = new_region_and_connections[0]
region_name = current_region["name"]
self.ALL_REGIONS_BY_NAME[region_name] = current_region
self.STATIC_CONNECTIONS_BY_REGION_NAME[region_name] = new_region_and_connections[1]
continue
line_split = line.split(" - ")
check_name_full = line_split.pop(0)
check_hex = check_name_full[0:7]
check_name = check_name_full[9:-1]
required_panel_lambda = line_split.pop(0)
required_item_lambda = line_split.pop(0)
laser_names = {
"Laser",
"Laser Hedges",
"Laser Pressure Plates",
"Desert Laser Redirect"
}
is_vault_or_video = "Vault" in check_name or "Video" in check_name
if "Discard" in check_name:
location_type = "Discard"
location_id = discard_ids
discard_ids += 1
elif is_vault_or_video or check_name == "Tutorial Gate Close":
location_type = "Vault"
location_id = vault_ids
vault_ids += 1
elif check_name in laser_names:
location_type = "Laser"
location_id = laser_ids
laser_ids += 1
else:
location_type = "General"
location_id = normal_panel_ids
normal_panel_ids += 1
required_items = parse_lambda(required_item_lambda)
items_actually_in_the_game = {item[0] for item in self.ALL_ITEMS}
required_items = frozenset(
subset.intersection(items_actually_in_the_game)
for subset in required_items
)
requirement = {
"panels": parse_lambda(required_panel_lambda),
"items": required_items
}
self.CHECKS_BY_HEX[check_hex] = {
"checkName": current_region["shortName"] + " " + check_name,
"checkHex": check_hex,
"region": current_region,
"idOffset": location_id,
"panelType": location_type
}
self.CHECKS_BY_NAME[self.CHECKS_BY_HEX[check_hex]["checkName"]] = self.CHECKS_BY_HEX[check_hex]
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[check_hex] = requirement
current_region["panels"].add(check_hex)
def __init__(self):
self.parse_items()
self.read_logic_file()

58
worlds/witness/utils.py Normal file
View File

@ -0,0 +1,58 @@
import os
from Utils import cache_argsless
def define_new_region(region_string):
"""
Returns a region object by parsing a line in the logic file
"""
region_string = region_string[:-1]
line_split = region_string.split(" - ")
region_name_full = line_split.pop(0)
region_name_split = region_name_full.split(" (")
region_name = region_name_split[0]
region_name_simple = region_name_split[1][:-1]
options = set()
for _ in range(len(line_split) // 2):
connected_region = line_split.pop(0)
corresponding_lambda = line_split.pop(0)
options.add(
(connected_region, parse_lambda(corresponding_lambda))
)
region_obj = {
"name": region_name,
"shortName": region_name_simple,
"panels": set()
}
return region_obj, options
def parse_lambda(lambda_string):
"""
Turns a lambda String literal like this: a | b & c
into a set of sets like this: {{a}, {b, c}}
The lambda has to be in DNF.
"""
if lambda_string == "True":
return frozenset([frozenset()])
split_ands = set(lambda_string.split(" | "))
lambda_set = frozenset({frozenset(a.split(" & ")) for a in split_ands})
return lambda_set
@cache_argsless
def get_disable_unrandomized_list():
adjustment_file = "Disable_Unrandomized.txt"
path = os.path.join(os.path.dirname(__file__), adjustment_file)
with open(path) as f:
return [line.strip() for line in f.readlines()]