The Witness - 0.3.3 features and fixes (#617)

New option: "Early Secret Area" (Opens a door to the Challenge Area from the start of the game)
New option: Victory Conditions "Mountaintop Box Short" and "Mountaintop Box Long"
New options: Number of Lasers of Mountain, Number of Lasers for Challenge
New option & item: Add some number of "Puzzle Skips", which let you skip one puzzle in the game

Many logic fixes
This commit is contained in:
NewSoupVi 2022-06-16 03:04:45 +02:00 committed by GitHub
parent 0a63bd0fc6
commit 7d79cff66f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 410 additions and 125 deletions

View File

@ -1,5 +1,6 @@
Event Items:
Shadows Laser Activation - 0x00021,0x17D28,0x17C71
Keep Laser Activation - 0x03317
Bunker Laser Activation - 0x00061,0x17D01,0x17C42
Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9,0x17CA4
Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x00B8D,0x17CF7
@ -26,7 +27,7 @@ Disabled Locations:
0x00055 (Orchard Apple Tree 3)
0x032F7 (Orchard Apple Tree 4)
0x032FF (Orchard Apple Tree 5)
0x168B5 (Shadows Lower Avoid 1)
0x198B5 (Shadows Lower Avoid 1)
0x198BD (Shadows Lower Avoid 2)
0x198BF (Shadows Lower Avoid 3)
0x19771 (Shadows Lower Avoid 4)
@ -53,6 +54,8 @@ Disabled Locations:
0x019E7 (Keep Hedge Maze 3)
0x01A0F (Keep Hedge Maze 4)
0x0360E (Laser Hedges)
0x00B10 (Monastery Door Open Left)
0x00C92 (Monastery Door Open Right)
0x00290 (Monastery Rhombic Avoid 1)
0x00038 (Monastery Rhombic Avoid 2)
0x00037 (Monastery Rhombic Avoid 3)

View File

@ -0,0 +1,30 @@
100 - 0x01A54 - None - Glass Factory Entry Door
105 - 0x000B0 - 0x0343A - Door to Symmetry Island Lower
107 - 0x1C349 - 0x00076 - Door to Symmetry Island Upper
110 - 0x0C339 - 0x09F94 - Door to Desert Flood Light Room
111 - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B - None - Desert Flood Room Flood Controls
120 - 0x03678 - None - Quarry Mill Ramp Control
122 - 0x03679 - 0x014E8 - Quarry Mill Elevator Control
125 - 0x03852 - 0x034D4,0x021D5 - Quarry Boathouse Ramp Height Control
127 - 0x03858 - 0x021AE - Quarry Boathouse Ramp Horizontal Control
131 - 0x334DB,0x334DC - None - Shadows Door Timer
150 - 0x00B10 - None - Monastery Entry Door Left
151 - 0x00C92 - None - Monastery Entry Door Right
162 - 0x28998 - None - Town Door to RGB House
163 - 0x28A0D - 0x28998 - Town Door to Church
166 - 0x28A79 - None - Town Maze Panel (Drop-Down Staircase)
169 - 0x17F5F - None - Windmill Door
200 - 0x0288C - None - Treehouse First & Second Door
202 - 0x0A182 - None - Treehouse Third Door
205 - 0x2700B - None - Treehouse Laser House Door Timer
208 - 0x17CBC - None - Treehouse Shortcut Drop-Down Bridge
175 - 0x17CAB - 0x002C7 - Jungle Popup Wall
180 - 0x17C2E - None - Bunker Entry Door
183 - 0x0A099 - 0x09DAF - Inside Bunker Door to Bunker Proper
186 - 0x0A079 - None - Bunker Elevator Control
190 - 0x0056E - None - Swamp Entry Door
192 - 0x00609,0x18488 - 0x181A9 - Swamp Sliding Bridge
195 - 0x181F5 - None - Swamp Rotating Bridge
197 - 0x17C0A - None - Swamp Maze Control
300 - 0x0042D - None - Mountaintop River Shape Panel (Shortcut to Secret Area)
310 - 0x17CDF,0x17CC8,0x17CA6,0x09DB8,0x17C95,0x0A054 - None - Boat

View File

@ -0,0 +1,5 @@
Event Items:
Shortcut to Secret Area Opens - 0x0042D
Region Changes:
Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path to Secret Area - 0x00FF8 - Main Island - 0x021D7 | 0x0042D - Main Island - 0x17CF2

View File

@ -1,6 +1,6 @@
from typing import Dict
from typing import Dict, Union
from BaseClasses import MultiWorld
from Options import Toggle, DefaultOnToggle, Option, Range
from Options import Toggle, DefaultOnToggle, Option, Range, Choice
# class HardMode(Toggle):
@ -18,6 +18,23 @@ class DisableNonRandomizedPuzzles(DefaultOnToggle):
display_name = "Disable non randomized puzzles"
class EarlySecretArea(Toggle):
"""The Mountainside shortcut to the Mountain Secret Area is open from the start.
(Otherwise known as "UTM", "Caves" or the "Challenge Area")"""
display_name = "Early Secret Area"
class ShuffleSymbols(DefaultOnToggle):
"""You will need to unlock puzzle symbols as items to be able to solve the panels that contain those symbols."""
display_name = "Shuffle Symbols"
class ShuffleDoors(Toggle):
"""Many doors around the island will have their panels turned off initially.
You will need to find the items that power the panels to open those doors."""
display_name = "Shuffle Doors"
class ShuffleDiscardedPanels(Toggle):
"""Discarded Panels will have items on them.
Solving certain Discarded Panels may still be necessary!"""
@ -40,10 +57,31 @@ class ShuffleHardLocations(Toggle):
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"
class VictoryCondition(Choice):
"""Change the victory condition from the original game's ending (elevator) to beating the Challenge
or solving the mountaintop box, either using the short solution
(7 lasers or whatever you've changed it to) or the long solution (11 lasers or whatever you've changed it to)."""
display_name = "Victory Condition"
option_elevator = 0
option_challenge = 1
option_mountain_box_short = 2
option_mountain_box_long = 3
class MountainLasers(Range):
"""Sets the amount of beams required to enter the final area."""
display_name = "Required Lasers for Mountain Entry"
range_start = 1
range_end = 7
default = 7
class ChallengeLasers(Range):
"""Sets the amount of beams required to enter the secret area through the Mountain Bottom Layer Discard."""
display_name = "Required Lasers for Challenge"
range_start = 1
range_end = 11
default = 11
class TrapPercentage(Range):
@ -54,16 +92,30 @@ class TrapPercentage(Range):
default = 20
the_witness_options: Dict[str, Option] = {
class PuzzleSkipAmount(Range):
"""Adds this number of Puzzle Skips into the pool, if there is room. Puzzle Skips let you skip one panel.
Works on most panels in the game - The only big exception is The Challenge."""
display_name = "Puzzle Skips"
range_start = 0
range_end = 20
default = 5
the_witness_options: Dict[str, type] = {
# "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,
"trap_percentage": TrapPercentage
"victory_condition": VictoryCondition,
"trap_percentage": TrapPercentage,
"early_secret_area": EarlySecretArea,
# "shuffle_symbols": ShuffleSymbols,
# "shuffle_doors": ShuffleDoors,
"mountain_lasers": MountainLasers,
"challenge_lasers": ChallengeLasers,
"puzzle_skip_amount": PuzzleSkipAmount,
}
@ -71,10 +123,12 @@ 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:
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[bool, int]:
option = getattr(world, name, None)
if option is None:
return 0
return int(option[player].value)
if issubclass(the_witness_options[name], Toggle) or issubclass(the_witness_options[name], DefaultOnToggle):
return bool(option[player].value)
return option[player].value

View File

@ -13,6 +13,10 @@ Progression:
71 - Black/White Squares
72 - Colored Squares
Usefuls:
101 - Functioning Brain
510 - Puzzle Skip
Boosts:
500 - Speed Boost

View File

@ -11,7 +11,7 @@ Tutorial (Tutorial) - First Hallway - 0x00182:
0x03629 (Gate Open) - 0x002C2 & 0x0A3B5 & 0x0A3B2 - True
0x03505 (Gate Close) - 0x2FAF6 - True
0x0C335 (Pillar) - True - Triangles - True
0x0C373 (Patio Floor) - 0x0C335 - True
0x0C373 (Patio Floor) - 0x0C335 - Dots
Outside Tutorial (Outside Tutorial) - Tutorial - 0x03629:
0x033D4 (Vault) - True - Dots & Squares & Black/White Squares
@ -52,10 +52,10 @@ Inside Glass Factory (Glass Factory) - Outside Glass Factory - 0x01A54:
0x00084 (Melting 1) - 0x00083 - Symmetry
0x00082 (Melting 2) - 0x00084 - Symmetry
0x0343A (Melting 3) - 0x00082 - Symmetry
0x17CC8 (Boat Spawn) - True - Boat
0x17CC8 (Boat Spawn) - 0x0005C - Boat
Outside Symmetry Island (Symmetry Island) - Main Island - True:
0x000B0 (Door to Symmetry Island Lower) - True - Dots
0x000B0 (Door to Symmetry Island Lower) - 0x0343A - Dots
Symmetry Island Lower (Symmetry Island) - Outside Symmetry Island - 0x000B0:
0x00022 (Black Dots 1) - True - Symmetry & Dots
@ -138,17 +138,16 @@ Desert Water Levels Room (Desert) - Desert Pond Room - 0x0A249:
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
0x04D18 (Flood Reflection 1) - 0x1C260 & 0x1831C - Reflection
0x01205 (Flood Reflection 2) - 0x04D18 & 0x1C260 & 0x1831C - Reflection
0x181AB (Flood Reflection 3) - 0x01205 & 0x1C260 & 0x1831C - Reflection
0x0117A (Flood Reflection 4) - 0x181AB & 0x1C260 & 0x1831C - Reflection
0x17ECA (Flood Reflection 5) - 0x0117A & 0x1C260 & 0x1831C - Reflection
0x18076 (Flood Reflection 6) - 0x17ECA & 0x1C260 & 0x1831C - 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
@ -187,8 +186,8 @@ Quarry Mill (Quarry Mill) - Quarry - 0x01E59 & 0x01E5A:
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
0x3C125 (Big Squares & Dots & Eraser) - 0x0367C - Squares & Black/White Squares & Dots & Eraser
0x0367C (Small Squares & Dots & Eraser) - 0x014E9 - Squares & Colored Squares & Dots & Eraser
0x17CAC (Door to Outside Quarry Stairs) - True - True
Quarry Boathouse (Quarry Boathouse) - Quarry - True:
@ -209,13 +208,13 @@ Quarry Boathouse (Quarry Boathouse) - Quarry - True:
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
0x09DB3 (Stars & Eraser & Shapers 1) - 0x3C124 - Stars & Eraser & Shapers
0x09DB4 (Stars & Eraser & 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
0x0A3CB (Stars & Eraser & Shapers 3) - 0x09DB4 - Stars & Eraser & Shapers
0x0A3CC (Stars & Eraser & Shapers 4) - 0x0A3CB - Stars & Eraser & Shapers
0x0A3D0 (Stars & Eraser & Shapers 5) - 0x0A3CC - Stars & Eraser & Shapers
Shadows (Shadows) - Main Island - True - Keep Glass Plates - 0x09E49:
0x334DB (Door Timer Outside) - True - True
@ -239,8 +238,8 @@ Shadows (Shadows) - Main Island - True - Keep Glass Plates - 0x09E49:
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
0x198B5 (Lower Avoid 1) - True - Shadows Avoid
0x198BD (Lower Avoid 2) - 0x198B5 - Shadows Avoid
0x198BF (Lower Avoid 3) - 0x198BD & 0x334DC - Shadows Avoid
0x19771 (Lower Avoid 4) - 0x198BF - Shadows Avoid
0x0A8DC (Lower Avoid 5) - 0x19771 - Shadows Avoid
@ -319,7 +318,7 @@ Town (Town) - Main Island - True - Theater - 0x0A168 | 0x33AB2:
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
0x28AD9 (Shapers & Dots & Eraser) - 0x28AC1 - Rotated Shapers & Dots & Eraser
0x17F5F (Windmill Door) - True - Dots
RGB House (Town) - Town - 0x28998:
@ -415,7 +414,7 @@ Swamp Entry Area (Swamp) - Outside Swamp - 0x0056E:
0x00985 (Combinable Shapers 6) - 0x00986 - Shapers
0x00987 (Combinable Shapers 7) - 0x00985 - Shapers
0x181A9 (Combinable Shapers 8) - 0x00987 - Shapers
0x00609 (Slide Bridge) - 0x181A9 - Shapers
0x00609 (Sliding Bridge) - 0x181A9 - Shapers
Swamp Near Platform (Swamp) - Swamp Entry Area - 0x00609 | 0x18488:
0x00999 (Broken Shapers 1) - 0x00990 - Broken Shapers
@ -447,24 +446,25 @@ Swamp Rotating Bridge Near Side (Swamp) - Swamp Near Platform - 0x009A1:
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
Swamp Near Boat (Swamp) - Swamp Rotating Bridge Near Side - 0x0000A - 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
0x009A6 (Underwater Back Optional) - 0x00E3A & 0x181F5 - 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
0x17E2B (Long Bridge Control) - True - Rotated Shapers & 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
0x17C0A (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers & Environment
0x17E07 (Maze Control Other Side) - True - Shapers & Negative Shapers & Rotated Shapers & Environment
0x03615 (Laser) - 0x17C0A & 0x17E07 - True
0x17C05 (Near Laser Shortcut Door Left) - True - Rotated Shapers
0x17C02 (Near Laser Shortcut Door Right) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers
@ -543,7 +543,8 @@ Treehouse Bridge Platform (Treehouse) - Treehouse Beyond Yellow Bridge - 0x17DA2
Mountaintop (Mountaintop) - Main Island - True:
0x0042D (River Shape) - True - True
0x09F7F (Box Open) - 7 Lasers - True
0x09F7F (Box Short) - 7 Lasers - True
0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - 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
@ -577,7 +578,7 @@ Inside Mountain Second Layer (Inside Mountain) - Inside Mountain Top Layer Bridg
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
0x09E86 (Light Bridge Controller 2) - 0x09FD8 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & 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
@ -586,7 +587,7 @@ Inside Mountain Second Layer Beyond Bridge (Inside Mountain) - Inside Mountain S
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
0x09ED8 (Light Bridge Controller 3) - 0x09FD2 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Rotated Shapers & Eraser & Two Lines
Inside Mountain Second Layer Elevator (Inside Mountain) - Inside Mountain Second Layer - 0x09ED8 & 0x09E86:
0x09EEB (Elevator Control Panel) - True - Dots
@ -600,10 +601,9 @@ Inside Mountain Third Layer (Inside Mountain) - Inside Mountain Second Layer Ele
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
0x17FA2 (Bottom Layer Discard) - 0xFFF00 - Triangles & Environment
0x01983 (Door to Final Room Left) - True - Shapers & Stars
0x01987 (Door to Final Room Right) - True - Squares & Colored Squares
0x01987 (Door to Final Room Right) - True - Squares & Colored Squares & Dots
Inside Mountain Path to Secret Area (Inside Mountain) - Inside Mountain Bottom Layer - 0x17FA2:
0x00FF8 (Door to Secret Area) - True - Triangles & Black/White Squares & Squares
@ -636,17 +636,17 @@ Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path
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
0x00B71 (Squares & Stars and Colored Eraser) - True - Colored Eraser & Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Eraser
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
0x289E7 (Wooden Beam Stars 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
0x0A16B (Upstairs Dot Grid Gap Dots) - True - Dots
0x0A2CE (Upstairs Dot Grid Stars) - 0x0A16B - Stars & Dots
0x0A2D7 (Upstairs Dot Grid Triangles) - 0x0A2CE - Triangles & Dots
0x0A2D7 (Upstairs Dot Grid Stars & Squares) - 0x0A2CE - Dots & Black/White Squares & Stars + Same Colored Symbol & Stars
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
@ -662,7 +662,7 @@ Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path
0x00029 (Upstairs Invisible Dot Symmetry 3) - 0x00028 - Dots & Invisible Dots & Symmetry
Challenge (Challenge) - Inside Mountain Secret Area - 0x0A16E:
0x0A332 (Start Timer) - True - True
0x0A332 (Start Timer) - 11 Lasers - True
0x0088E (Small Basic) - 0x0A332 - True
0x00BAF (Big Basic) - 0x0088E - True
0x00BF3 (Square) - 0x00BAF - Squares & Black/White Squares
@ -694,11 +694,11 @@ Final Room (Inside Mountain Final Room) - Inside Mountain Bottom Layer - 0x01983
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
0x33961 (Sparse Dots Pillar) - 0x09E5A - Dots & Symmetry & 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
0x339BB (Squares and Stars) - 0x03859 - Squares & Black/White Squares & Stars & Symmetry & Pillar
Elevator (Inside Mountain Final Room) - Final Room - 0x339BB & 0x33961:
0x3D9A6 (Elevator Door Closer Left) - True - True
@ -709,4 +709,4 @@ Elevator (Inside Mountain Final Room) - Final Room - 0x339BB & 0x33961:
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:
Boat (Boat) - Main Island - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054 - Inside Glass Factory - 0x17CDF & 0x0005C | 0x17CC8 & 0x0005C | 0x17CA6 & 0x0005C | 0x09DB8 & 0x0005C | 0x17C95 & 0x0005C | 0x0A054 & 0x0005C - Quarry Boathouse - 0x17CA6 - Swamp Near Boat - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054 - Treehouse Entry Area - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054:

View File

@ -11,7 +11,7 @@ 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
from .Options import is_option_enabled, the_witness_options, get_option_value
from .utils import best_junk_to_add_based_on_weights
@ -50,7 +50,9 @@ class WitnessWorld(World):
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
'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID,
'doorhex_to_id': self.player_logic.DOOR_DICT_FOR_CLIENT,
'door_connections_to_sever': self.player_logic.DOOR_CONNECTIONS_TO_SEVER
}
def generate_early(self):
@ -67,20 +69,36 @@ class WitnessWorld(World):
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:
if item in self.items.PROGRESSION_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])
less_junk = 0
# Put good item on first check if symbol shuffle is on
# symbols = is_option_enabled(self.world, self.player, "shuffle_symbols")
symbols = True
if symbols:
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])
less_junk = 1
for item in self.items.EXTRA_AMOUNTS:
witness_item = self.create_item(item)
for i in range(0, self.items.EXTRA_AMOUNTS[item]):
if len(pool) < len(self.locat.CHECK_LOCATION_TABLE) - len(self.locat.EVENT_LOCATION_TABLE) - less_junk:
pool.append(witness_item)
# Put in junk items to fill the rest
junk_size = len(self.locat.CHECK_LOCATION_TABLE) - len(pool) - len(self.locat.EVENT_LOCATION_TABLE) - 1
junk_size = len(self.locat.CHECK_LOCATION_TABLE) - len(pool) - len(self.locat.EVENT_LOCATION_TABLE) - less_junk
print(junk_size)
for i in range(0, junk_size):
pool.append(self.create_item(self.get_filler_item_name()))
@ -107,7 +125,7 @@ class WitnessWorld(World):
slot_data["hard_mode"] = False
for option_name in the_witness_options:
slot_data[option_name] = is_option_enabled(
slot_data[option_name] = get_option_value(
self.world, self.player, option_name
)
@ -124,6 +142,8 @@ class WitnessWorld(World):
name, item.progression, item.code, player=self.player
)
new_item.trap = item.trap
if item.never_exclude:
new_item.never_exclude = True
return new_item
def get_filler_item_name(self) -> str: # Used by itemlinks

View File

@ -6,7 +6,7 @@ from typing import Dict, NamedTuple, Optional
from BaseClasses import Item, MultiWorld
from . import StaticWitnessLogic, WitnessPlayerLocations, WitnessPlayerLogic
from .Options import get_option_value, is_option_enabled
from .Options import get_option_value, is_option_enabled, the_witness_options
from fractions import Fraction
@ -18,6 +18,7 @@ class ItemData(NamedTuple):
progression: bool
event: bool = False
trap: bool = False
never_exclude: bool = False
class WitnessItem(Item):
@ -50,7 +51,7 @@ class StaticWitnessItems:
def __init__(self):
item_tab = dict()
for item in StaticWitnessLogic.ALL_ITEMS:
for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS.union(StaticWitnessLogic.ALL_DOOR_ITEMS):
if item[0] == "11 Lasers" or item == "7 Lasers":
continue
@ -64,6 +65,9 @@ class StaticWitnessItems:
for item in StaticWitnessLogic.ALL_BOOSTS:
item_tab[item[0]] = ItemData(158000 + item[1], False, False)
for item in StaticWitnessLogic.ALL_USEFULS:
item_tab[item[0]] = ItemData(158000 + item[1], False, False, False, True)
item_tab = dict(sorted(
item_tab.items(),
key=lambda single_item: single_item[1].code
@ -83,11 +87,35 @@ class WitnessPlayerItems:
"""Adds event items after logic changes due to options"""
self.EVENT_ITEM_TABLE = dict()
self.ITEM_TABLE = copy.copy(StaticWitnessItems.ALL_ITEM_TABLE)
self.PROGRESSION_TABLE = dict()
self.GOOD_ITEMS = [
"Dots", "Black/White Squares", "Stars",
"Shapers", "Symmetry"
]
self.EXTRA_AMOUNTS = {
"Functioning Brain": 1,
"Puzzle Skip": get_option_value(world, player, "puzzle_skip_amount")
}
for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS.union(StaticWitnessLogic.ALL_DOOR_ITEMS):
if item not in player_logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME:
del self.ITEM_TABLE[item[0]]
else:
self.PROGRESSION_TABLE[item[0]] = self.ITEM_TABLE[item[0]]
symbols = is_option_enabled(world, player, "shuffle_symbols")
if "shuffle_symbols" not in the_witness_options.keys():
symbols = True
doors = is_option_enabled(world, player, "shuffle_doors")
if doors and symbols:
self.GOOD_ITEMS = [
"Dots", "Black/White Squares", "Symmetry"
]
elif symbols:
self.GOOD_ITEMS = [
"Dots", "Black/White Squares", "Stars",
"Shapers", "Symmetry"
]
if is_option_enabled(world, player, "shuffle_discarded_panels"):
self.GOOD_ITEMS.append("Triangles")

View File

@ -19,6 +19,12 @@ class StaticWitnessLocations:
"Laser": 700,
}
EXTRA_LOCATIONS = {
"Tutorial Front Left",
"Tutorial Back Left",
"Tutorial Back Right",
}
GENERAL_LOCATIONS = {
"Tutorial Gate Open",
@ -50,11 +56,12 @@ class StaticWitnessLocations:
"Quarry Mill Eraser and Dots 6",
"Quarry Mill Eraser and Squares 8",
"Quarry Mill Small Squares & Dots & and Eraser",
"Quarry Mill Small Squares & Dots & Eraser",
"Quarry Boathouse Intro Shapers",
"Quarry Boathouse Intro Stars",
"Quarry Boathouse Eraser and Shapers 5",
"Quarry Boathouse Stars & Eraser & and Shapers 2",
"Quarry Boathouse Stars & Eraser & and Shapers 5",
"Quarry Boathouse Stars & Eraser & Shapers 2",
"Quarry Boathouse Stars & Eraser & Shapers 5",
"Quarry Discard",
"Quarry Laser",
@ -82,7 +89,7 @@ class StaticWitnessLocations:
"Town Rooftop Discard",
"Town Symmetry Squares 5 + Dots",
"Town Full Dot Grid Shapers 5",
"Town Shapers & Dots & and Eraser",
"Town Shapers & Dots & Eraser",
"Town Laser",
"Theater Discard",
@ -138,7 +145,7 @@ class StaticWitnessLocations:
UNCOMMON_LOCATIONS = {
"Mountaintop River Shape",
"Tutorial Patio Floor",
"Quarry Mill Big Squares & Dots & and Eraser",
"Quarry Mill Big Squares & Dots & Eraser",
"Theater Tutorial Video",
"Theater Desert Video",
"Theater Jungle Video",
@ -165,11 +172,11 @@ class StaticWitnessLocations:
"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 Stars 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 Negative Shapers",
"Inside Mountain Secret Area Upstairs Dot Grid Rotated Shapers",
"Challenge Vault Box",
@ -241,6 +248,13 @@ class WitnessPlayerLocations:
if is_option_enabled(world, player, "shuffle_hard"):
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.HARD_LOCATIONS
if is_option_enabled(world, player, "shuffle_symbols") and is_option_enabled(world, player, "shuffle_doors"):
if is_option_enabled(world, player, "disable_non_randomized_puzzles"):
# This particular combination of logic settings leads to logic so restrictive that generation can fail
# Hence, we add some extra sphere 0 locations
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.EXTRA_LOCATIONS
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | player_logic.ADDED_CHECKS
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS - {
@ -259,9 +273,6 @@ class WitnessPlayerLocations:
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 = {

View File

@ -18,13 +18,22 @@ When the world has parsed its options, a second function is called to finalize t
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
from .utils import define_new_region, get_disable_unrandomized_list, parse_lambda, get_early_utm_list
from .Options import is_option_enabled, get_option_value, the_witness_options
class WitnessPlayerLogic:
"""WITNESS LOGIC CLASS"""
def update_door_dict(self, panel_hex):
item_id = StaticWitnessLogic.ALL_DOOR_ITEM_IDS_BY_HEX.get(panel_hex)
if item_id is None:
return
self.DOOR_DICT_FOR_CLIENT[panel_hex] = item_id
self.DOOR_CONNECTIONS_TO_SEVER.update(StaticWitnessLogic.CONNECTIONS_TO_SEVER_BY_DOOR_HEX[panel_hex])
def reduce_req_within_region(self, panel_hex):
"""
Panels in this game often only turn on when other panels are solved.
@ -34,13 +43,27 @@ class WitnessPlayerLogic:
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"]
these_items = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["items"]
real_items = {item[0] for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME}
these_items = frozenset({
subset.intersection(real_items)
for subset in these_items
})
these_panels = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"]
if StaticWitnessLogic.DOOR_NAMES_BY_HEX.get(panel_hex) in real_items:
self.update_door_dict(panel_hex)
these_panels = frozenset({frozenset()})
if these_panels == frozenset({frozenset()}):
return these_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:
@ -57,6 +80,9 @@ class WitnessPlayerLogic:
elif dep_obj["region"]["name"] != check_obj["region"]["name"]:
new_items = frozenset({frozenset([option_panel])})
self.EVENT_PANELS_FROM_PANELS.add(option_panel)
elif option_panel in self.ALWAYS_EVENT_NAMES_BY_HEX.keys():
new_items = frozenset({frozenset([option_panel])})
self.EVENT_PANELS_FROM_PANELS.add(option_panel)
else:
new_items = self.reduce_req_within_region(option_panel)
@ -105,7 +131,7 @@ class WitnessPlayerLogic:
line_split = line.split(" - ")
required_items = parse_lambda(line_split[2])
items_actually_in_the_game = {item[0] for item in StaticWitnessLogic.ALL_ITEMS}
items_actually_in_the_game = {item[0] for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS}
required_items = frozenset(
subset.intersection(items_actually_in_the_game)
for subset in required_items
@ -121,7 +147,14 @@ class WitnessPlayerLogic:
return
if adj_type == "Disabled Locations":
self.COMPLETELY_DISABLED_CHECKS.add(line[:7])
panel_hex = line[:7]
self.COMPLETELY_DISABLED_CHECKS.add(panel_hex)
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = {
item for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME
if item[0] != StaticWitnessLogic.DOOR_NAMES_BY_HEX.get(panel_hex)
}
return
@ -139,10 +172,14 @@ class WitnessPlayerLogic:
"""Makes logic adjustments based on options"""
adjustment_linesets_in_order = []
if is_option_enabled(world, player, "challenge_victory"):
self.VICTORY_LOCATION = "0x0356B"
else:
if get_option_value(world, player, "victory_condition") == 0:
self.VICTORY_LOCATION = "0x3D9A9"
elif get_option_value(world, player, "victory_condition") == 1:
self.VICTORY_LOCATION = "0x0356B"
elif get_option_value(world, player, "victory_condition") == 2:
self.VICTORY_LOCATION = "0x09F7F"
elif get_option_value(world, player, "victory_condition") == 3:
self.VICTORY_LOCATION = "0xFFF00"
self.COMPLETELY_DISABLED_CHECKS.add(
self.VICTORY_LOCATION
@ -151,6 +188,20 @@ class WitnessPlayerLogic:
if is_option_enabled(world, player, "disable_non_randomized_puzzles"):
adjustment_linesets_in_order.append(get_disable_unrandomized_list())
if is_option_enabled(world, player, "shuffle_symbols") or "shuffle_symbols" not in the_witness_options.keys():
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.update(StaticWitnessLogic.ALL_SYMBOL_ITEMS)
if is_option_enabled(world, player, "shuffle_doors"):
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.update(StaticWitnessLogic.ALL_DOOR_ITEMS)
if is_option_enabled(world, player, "early_secret_area"):
adjustment_linesets_in_order.append(get_early_utm_list())
else:
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = {
item for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME
if item[0] != "Mountaintop River Shape Power On"
}
for adjustment_lineset in adjustment_linesets_in_order:
current_adjustment_type = None
@ -182,6 +233,17 @@ class WitnessPlayerLogic:
pair = (name, self.EVENT_ITEM_NAMES[panel])
return pair
def _regions_are_adjacent(self, region1, region2):
for connection in self.CONNECTIONS_BY_REGION_NAME[region1]:
if connection[0] == region2:
return True
for connection in self.CONNECTIONS_BY_REGION_NAME[region2]:
if connection[0] == region1:
return True
return False
def make_event_panel_lists(self):
"""
Special event panel data structures
@ -197,7 +259,6 @@ class WitnessPlayerLogic:
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():
@ -213,6 +274,15 @@ class WitnessPlayerLogic:
if panel not in region["panels"] | connected_r["panels"]:
self.NECESSARY_EVENT_PANELS.add(panel)
for event_panel in self.EVENT_PANELS_FROM_PANELS:
for panel, panel_req in self.REQUIREMENTS_BY_HEX.items():
if any([event_panel in item_set for item_set in panel_req]):
region1 = StaticWitnessLogic.CHECKS_BY_HEX[panel]["region"]["name"]
region2 = StaticWitnessLogic.CHECKS_BY_HEX[event_panel]["region"]["name"]
if not self._regions_are_adjacent(region1, region2):
self.NECESSARY_EVENT_PANELS.add(event_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)
@ -226,6 +296,10 @@ class WitnessPlayerLogic:
self.EVENT_PANELS_FROM_PANELS = set()
self.EVENT_PANELS_FROM_REGIONS = set()
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = set()
self.DOOR_DICT_FOR_CLIENT = dict()
self.DOOR_CONNECTIONS_TO_SEVER = 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()
@ -257,7 +331,9 @@ class WitnessPlayerLogic:
"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"
"0x28B39": "Town Tower 4th Door Opens",
"0x0343A": "Door to Symmetry Island Powers On",
"0xFFF00": "Inside Mountain Bottom Layer Discard Turns On"
}
self.ALWAYS_EVENT_NAMES_BY_HEX = {
@ -266,8 +342,8 @@ class WitnessPlayerLogic:
"0x09F98": "Desert Laser Redirection",
"0x03612": "Quarry Laser Activation",
"0x19650": "Shadows Laser Activation",
"0x0360E": "Keep Laser Hedges Activation",
"0x03317": "Keep Laser Pressure Plates Activation",
"0x0360E": "Keep Laser Activation",
"0x03317": "Keep Laser Activation",
"0x17CA4": "Monastery Laser Activation",
"0x032F5": "Town Laser Activation",
"0x03616": "Jungle Laser Activation",
@ -280,6 +356,8 @@ class WitnessPlayerLogic:
"0x03481": "Tutorial Video Pattern Knowledge",
"0x03702": "Jungle Video Pattern Knowledge",
"0x2FAF6": "Theater Walkway Video Pattern Knowledge",
"0x09F7F": "Mountaintop Trap Door Turns On",
"0x17C34": "Mountain Access",
}
self.make_options_adjustments(world, player)

View File

@ -7,7 +7,7 @@ depending on the items received
from BaseClasses import MultiWorld
from .player_logic import WitnessPlayerLogic
from .Options import is_option_enabled
from .Options import is_option_enabled, get_option_value
from .locations import WitnessPlayerLocations
from . import StaticWitnessLogic
from ..AutoWorld import LogicMixin
@ -27,10 +27,7 @@ class WitnessLogic(LogicMixin):
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("Keep Laser 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))
@ -57,7 +54,6 @@ class WitnessLogic(LogicMixin):
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):
@ -76,22 +72,15 @@ class WitnessLogic(LogicMixin):
for item in option:
if item == "7 Lasers":
if not self._witness_has_lasers(world, player, 7):
if not self._witness_has_lasers(world, player, get_option_value(world, player, "mountain_lasers")):
valid_option = False
break
elif item == "11 Lasers":
if not self._witness_has_lasers(world, player, 11):
if not self._witness_has_lasers(world, player, get_option_value(world, player, "challenge_lasers")):
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 item in player_logic.ORIGINAL_EVENT_PANELS:
valid_option = self._witness_can_solve_panel(item, world, player, player_logic, locat)
elif not self.has(item, player):
valid_option = False
break

View File

@ -4,9 +4,14 @@ from .utils import define_new_region, parse_lambda
class StaticWitnessLogic:
ALL_ITEMS = set()
ALL_SYMBOL_ITEMS = set()
ALL_USEFULS = set()
ALL_TRAPS = set()
ALL_BOOSTS = set()
ALL_DOOR_ITEM_IDS_BY_HEX = dict()
DOOR_NAMES_BY_HEX = dict()
ALL_DOOR_ITEMS = set()
CONNECTIONS_TO_SEVER_BY_DOOR_HEX = dict()
EVENT_PANELS_FROM_REGIONS = set()
@ -25,13 +30,13 @@ class StaticWitnessLogic:
path = os.path.join(os.path.dirname(__file__), "WitnessItems.txt")
with open(path, "r", encoding="utf-8") as file:
current_set = self.ALL_ITEMS
current_set = self.ALL_SYMBOL_ITEMS
for line in file.readlines():
line = line.strip()
if line == "Progression:":
current_set = self.ALL_ITEMS
current_set = self.ALL_SYMBOL_ITEMS
continue
if line == "Boosts:":
current_set = self.ALL_BOOSTS
@ -39,12 +44,34 @@ class StaticWitnessLogic:
if line == "Traps:":
current_set = self.ALL_TRAPS
continue
if line == "Usefuls:":
current_set = self.ALL_USEFULS
continue
if line == "":
continue
line_split = line.split(" - ")
current_set.add((line_split[1], int(line_split[0])))
path = os.path.join(os.path.dirname(__file__), "Door_Shuffle.txt")
with open(path, "r", encoding="utf-8") as file:
for line in file.readlines():
line = line.strip()
line_split = line.split(" - ")
hex_set_split = line_split[1].split(",")
sever_list = line_split[2].split(",")
sever_set = {sever_panel for sever_panel in sever_list if sever_panel != "None"}
for door_hex in hex_set_split:
self.ALL_DOOR_ITEM_IDS_BY_HEX[door_hex] = int(line_split[0])
self.CONNECTIONS_TO_SEVER_BY_DOOR_HEX[door_hex] = sever_set
if len(line_split) > 3:
self.DOOR_NAMES_BY_HEX[door_hex] = line_split[3]
def read_logic_file(self):
"""
@ -52,7 +79,7 @@ class StaticWitnessLogic:
"""
path = os.path.join(os.path.dirname(__file__), "WitnessLogic.txt")
with open(path, "r", encoding="utf-8") as file:
current_region = ""
current_region = dict()
discard_ids = 0
normal_panel_ids = 0
@ -105,16 +132,44 @@ class StaticWitnessLogic:
laser_ids += 1
else:
location_type = "General"
location_id = normal_panel_ids
normal_panel_ids += 1
if check_hex == "0x012D7": # Compatibility
normal_panel_ids += 1
if check_hex == "0x17E07": # Compatibility
location_id = 112
elif check_hex == "0xFFF00":
location_id = 800
else:
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(
items_actually_in_the_game = {item[0] for item in self.ALL_SYMBOL_ITEMS}
required_items = set(
subset.intersection(items_actually_in_the_game)
for subset in required_items
)
doors_in_the_game = self.ALL_DOOR_ITEM_IDS_BY_HEX.keys()
if check_hex in doors_in_the_game:
door_name = current_region["shortName"] + " " + check_name + " Power On"
if check_hex in self.DOOR_NAMES_BY_HEX.keys():
door_name = self.DOOR_NAMES_BY_HEX[check_hex]
required_items = set(
subset.union(frozenset({door_name}))
for subset in required_items
)
self.ALL_DOOR_ITEMS.add(
(door_name, self.ALL_DOOR_ITEM_IDS_BY_HEX[check_hex])
)
required_items = frozenset(required_items)
requirement = {
"panels": parse_lambda(required_panel_lambda),
"items": required_items

View File

@ -89,10 +89,18 @@ def parse_lambda(lambda_string):
return lambda_set
@cache_argsless
def get_disable_unrandomized_list():
adjustment_file = "Disable_Unrandomized.txt"
def get_adjustment_file(adjustment_file):
path = os.path.join(os.path.dirname(__file__), adjustment_file)
with open(path) as f:
return [line.strip() for line in f.readlines()]
@cache_argsless
def get_disable_unrandomized_list():
return get_adjustment_file("Disable_Unrandomized.txt")
@cache_argsless
def get_early_utm_list():
return get_adjustment_file("Early_UTM.txt")