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:
parent
0a63bd0fc6
commit
7d79cff66f
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -13,6 +13,10 @@ Progression:
|
|||
71 - Black/White Squares
|
||||
72 - Colored Squares
|
||||
|
||||
Usefuls:
|
||||
101 - Functioning Brain
|
||||
510 - Puzzle Skip
|
||||
|
||||
Boosts:
|
||||
500 - Speed Boost
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue