Witness: Bugfixes in response to beta tests (#1473)

* Make all Keep Pressure Plates logically required for the Laser Panel

* Added more Tutorial checks

* Added the remaining two Shipwreck Boat EPs to the exclude list for normal

* Improved itempool filling system, added warning if usefuls had to be eaten

* Moved creation of said warning string to utils

* Fixed logic bug causing broken seeds on Mountain Floor 2

* Hints system change

* Expert Logic Fix

* Fixed typo

* Better wording

* Added missing games to junk hints

* Made sure Entrance names are unique

* Fixed missing Obelisk Side

* Disable Non Randomized + EP Shuffle fix

* Fixed disable_non_randomized precompleted EPs being 'disabled' instead of 'precompleted'

* Fixed if/elif error

* Tutorial Gate Open local symbol item becomes local_early_item in expert instead

* Bump required client version. There is a beta client that sends 0.3.9.

* Removed print statement, oops

* Fixed itempool manipulation in pre_fill

* Replaced string concats with fstrings

* Improved make_warning_string function signature

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>

* Improved performance on removing multiple items from multiworld itempool

* Comment

* Fixed errors with the code

* Made removal from itempool not fail unit test for multiple references

* Moved all item creation to create_items, got rid of itempool modifying system

* Colored Squares is no longer a good item, that's outdated

* Removed double if

* React to from_pool: false by removing a junk item

* Fixed warning if only Fnc Brain was removed

* Make use of string truthiness instead

* Made reading of plandoed items safer

---------

Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
NewSoupVi 2023-03-03 00:08:24 +01:00 committed by GitHub
parent 0cf8206660
commit 805f33c39e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 292 additions and 183 deletions

View File

@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F:
158206 - 0x0361B (Tower Shortcut Panel) - True - True 158206 - 0x0361B (Tower Shortcut Panel) - True - True
Door - 0x04F8F (Tower Shortcut) - 0x0361B Door - 0x04F8F (Tower Shortcut) - 0x0361B
158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True 158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots 158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots
Laser - 0x014BB (Laser) - 0x0360E | 0x03317 Laser - 0x014BB (Laser) - 0x0360E | 0x03317
159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True 159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
@ -909,7 +909,7 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry & Colored Dots 158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry & Colored Dots
Door - 0x09FFB (Staircase Near) - 0x09FD8 Door - 0x09FFB (Staircase Near) - 0x09FD8
Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - TrueOneWay: Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD: Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True:
159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True 159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True 159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True 159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True 159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True 159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True

View File

@ -270,7 +270,7 @@ Door - 0x0368A (Stairs) - 0x03677
159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True 159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True
Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B: Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B:
158146 - 0x034D4 (Intro Left) - True - Stars & Eraser 158146 - 0x034D4 (Intro Left) - True - Stars & Stars + Same Colored Symbol + Eraser
158147 - 0x021D5 (Intro Right) - True - Shapers & Eraser 158147 - 0x021D5 (Intro Right) - True - Shapers & Eraser
158148 - 0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers 158148 - 0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers
158166 - 0x17CA6 (Boat Spawn) - True - Boat 158166 - 0x17CA6 (Boat Spawn) - True - Boat
@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F:
158206 - 0x0361B (Tower Shortcut Panel) - True - True 158206 - 0x0361B (Tower Shortcut Panel) - True - True
Door - 0x04F8F (Tower Shortcut) - 0x0361B Door - 0x04F8F (Tower Shortcut) - 0x0361B
158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True 158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01BE9 - Shapers & Rotated Shapers & Triangles & Stars & Stars + Same Colored Symbol & Colored Squares & Black/White Squares 158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Rotated Shapers & Triangles & Stars & Stars + Same Colored Symbol & Colored Squares & Black/White Squares
Laser - 0x014BB (Laser) - 0x0360E | 0x03317 Laser - 0x014BB (Laser) - 0x0360E | 0x03317
159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True 159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
@ -909,7 +909,7 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Stars & Stars + Same Colored Symbol & Rotated Shapers & Eraser 158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Stars & Stars + Same Colored Symbol & Rotated Shapers & Eraser
Door - 0x09FFB (Staircase Near) - 0x09FD8 Door - 0x09FFB (Staircase Near) - 0x09FD8
Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - TrueOneWay: Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD: Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True:
159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True 159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True 159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True 159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True 159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True 159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True

View File

@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F:
158206 - 0x0361B (Tower Shortcut Panel) - True - True 158206 - 0x0361B (Tower Shortcut Panel) - True - True
Door - 0x04F8F (Tower Shortcut) - 0x0361B Door - 0x04F8F (Tower Shortcut) - 0x0361B
158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True 158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Black/White Squares & Rotated Shapers 158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Black/White Squares & Rotated Shapers
Laser - 0x014BB (Laser) - 0x0360E | 0x03317 Laser - 0x014BB (Laser) - 0x0360E | 0x03317
159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True 159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
@ -909,7 +909,7 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry 158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry
Door - 0x09FFB (Staircase Near) - 0x09FD8 Door - 0x09FFB (Staircase Near) - 0x09FD8
Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - TrueOneWay: Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD: Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True:
159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True 159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True 159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True 159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True 159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True 159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True

View File

@ -14,7 +14,7 @@ from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems
from .rules import set_rules from .rules import set_rules
from .regions import WitnessRegions from .regions import WitnessRegions
from .Options import is_option_enabled, the_witness_options, get_option_value from .Options import is_option_enabled, the_witness_options, get_option_value
from .utils import best_junk_to_add_based_on_weights, get_audio_logs from .utils import best_junk_to_add_based_on_weights, get_audio_logs, make_warning_string
from logging import warning from logging import warning
@ -38,7 +38,7 @@ class WitnessWorld(World):
""" """
game = "The Witness" game = "The Witness"
topology_present = False topology_present = False
data_version = 12 data_version = 13
static_logic = StaticWitnessLogic() static_logic = StaticWitnessLogic()
static_locat = StaticWitnessLocations() static_locat = StaticWitnessLocations()
@ -52,7 +52,7 @@ class WitnessWorld(World):
location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID
item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS
required_client_version = (0, 3, 8) required_client_version = (0, 3, 9)
def _get_slot_data(self): def _get_slot_data(self):
return { return {
@ -93,13 +93,13 @@ class WitnessWorld(World):
self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic) self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic)
self.regio = WitnessRegions(self.locat) self.regio = WitnessRegions(self.locat)
def create_regions(self):
self.regio.create_regions(self.multiworld, self.player, self.player_logic)
def generate_basic(self):
self.log_ids_to_hints = dict() self.log_ids_to_hints = dict()
self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()} self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()}
def create_regions(self):
self.regio.create_regions(self.multiworld, self.player, self.player_logic)
def create_items(self):
# Generate item pool # Generate item pool
pool = [] pool = []
for item in self.items.ITEM_TABLE: for item in self.items.ITEM_TABLE:
@ -109,22 +109,12 @@ class WitnessWorld(World):
pool.append(witness_item) pool.append(witness_item)
self.items_by_name[item] = witness_item self.items_by_name[item] = witness_item
less_junk = 0
dog_check = self.multiworld.get_location(
"Town Pet the Dog", self.player
)
dog_check.place_locked_item(self.create_item("Puzzle Skip"))
less_junk += 1
for precol_item in self.multiworld.precollected_items[self.player]: for precol_item in self.multiworld.precollected_items[self.player]:
if precol_item.name in self.items_by_name: # if item is in the pool, remove 1 instance. if precol_item.name in self.items_by_name: # if item is in the pool, remove 1 instance.
item_obj = self.items_by_name[precol_item.name] item_obj = self.items_by_name[precol_item.name]
if item_obj in pool: if item_obj in pool:
pool.remove(item_obj) # remove one instance of this pre-collected item if it exists pool.remove(item_obj) # remove one instance of this pre-collected item if it exists
for item in self.player_logic.STARTING_INVENTORY: for item in self.player_logic.STARTING_INVENTORY:
self.multiworld.push_precollected(self.items_by_name[item]) self.multiworld.push_precollected(self.items_by_name[item])
@ -132,15 +122,8 @@ class WitnessWorld(World):
for item in self.items.EXTRA_AMOUNTS: for item in self.items.EXTRA_AMOUNTS:
for i in range(0, self.items.EXTRA_AMOUNTS[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: witness_item = self.create_item(item)
witness_item = self.create_item(item) pool.append(witness_item)
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) - less_junk
for i in range(0, junk_size):
pool.append(self.create_item(self.get_filler_item_name()))
# Tie Event Items to Event Locations (e.g. Laser Activations) # Tie Event Items to Event Locations (e.g. Laser Activations)
for event_location in self.locat.EVENT_LOCATION_TABLE: for event_location in self.locat.EVENT_LOCATION_TABLE:
@ -150,27 +133,108 @@ class WitnessWorld(World):
location_obj = self.multiworld.get_location(event_location, self.player) location_obj = self.multiworld.get_location(event_location, self.player)
location_obj.place_locked_item(item_obj) location_obj.place_locked_item(item_obj)
self.multiworld.itempool += pool # Find out how much empty space there is for junk items. -1 for the "Town Pet the Dog" check
itempool_difference = len(self.locat.CHECK_LOCATION_TABLE) - len(self.locat.EVENT_LOCATION_TABLE) - 1
itempool_difference -= len(pool)
def pre_fill(self): # Place two locked items: Good symbol on Tutorial Gate Open, and a Puzzle Skip on "Town Pet the Dog"
# Put good item on first check if there are any of the designated "good items" in the pool
good_items_in_the_game = [] good_items_in_the_game = []
plandoed_items = set()
for v in self.multiworld.plando_items[self.player]:
if v.get("from_pool", True):
plandoed_items.update({self.items_by_name[i] for i in v.get("items", dict()).keys()
if i in self.items_by_name})
if "item" in v and v["item"] in self.items_by_name:
plandoed_items.add(self.items_by_name[v["item"]])
for symbol in self.items.GOOD_ITEMS: for symbol in self.items.GOOD_ITEMS:
item = self.items_by_name[symbol] item = self.items_by_name[symbol]
if item in self.multiworld.itempool: # Only do this if the item is still in item pool (e.g. after plando) if item in pool and item not in plandoed_items:
# for now, any item that is mentioned in any plando option, even if it's a list of items, is ineligible.
# Hopefully, in the future, plando gets resolved before create_items.
# I could also partially resolve lists myself, but this could introduce errors if not done carefully.
good_items_in_the_game.append(symbol) good_items_in_the_game.append(symbol)
if good_items_in_the_game: if good_items_in_the_game:
random_good_item = self.multiworld.random.choice(good_items_in_the_game) random_good_item = self.multiworld.random.choice(good_items_in_the_game)
first_check = self.multiworld.get_location(
"Tutorial Gate Open", self.player
)
item = self.items_by_name[random_good_item] item = self.items_by_name[random_good_item]
first_check.place_locked_item(item) if get_option_value(self.multiworld, self.player, "puzzle_randomization") == 1:
self.multiworld.itempool.remove(item) self.multiworld.local_early_items[self.player][random_good_item] = 1
else:
first_check = self.multiworld.get_location(
"Tutorial Gate Open", self.player
)
first_check.place_locked_item(item)
pool.remove(item)
dog_check = self.multiworld.get_location(
"Town Pet the Dog", self.player
)
dog_check.place_locked_item(self.create_item("Puzzle Skip"))
# Fill rest of item pool with junk if there is room
if itempool_difference > 0:
for i in range(0, itempool_difference):
self.multiworld.itempool.append(self.create_item(self.get_filler_item_name()))
# Remove junk, Functioning Brain, useful items (non-door), useful door items in that order until there is room
if itempool_difference < 0:
junk = [
item for item in pool
if item.classification in {ItemClassification.filler, ItemClassification.trap}
and item.name != "Functioning Brain"
]
f_brain = [item for item in pool if item.name == "Functioning Brain"]
usefuls = [
item for item in pool
if item.classification == ItemClassification.useful
and item.name not in StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT
]
removable_doors = [
item for item in pool
if item.classification == ItemClassification.useful
and item.name in StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT
]
self.multiworld.per_slot_randoms[self.player].shuffle(junk)
self.multiworld.per_slot_randoms[self.player].shuffle(usefuls)
self.multiworld.per_slot_randoms[self.player].shuffle(removable_doors)
removed_junk = False
removed_usefuls = False
removed_doors = False
for i in range(itempool_difference, 0):
if junk:
pool.remove(junk.pop())
removed_junk = True
elif f_brain:
pool.remove(f_brain.pop())
elif usefuls:
pool.remove(usefuls.pop())
removed_usefuls = True
elif removable_doors:
pool.remove(removable_doors.pop())
removed_doors = True
warn = make_warning_string(
removed_junk, removed_usefuls, removed_doors, not junk, not usefuls, not removable_doors
)
if warn:
warning(f"This Witness world has too few locations to place all its items."
f" In order to make space, {warn} had to be removed.")
# Finally, add the generated pool to the overall itempool
self.multiworld.itempool += pool
def set_rules(self): def set_rules(self):
set_rules(self.multiworld, self.player, self.player_logic, self.locat) set_rules(self.multiworld, self.player, self.player_logic, self.locat)

View File

@ -2,96 +2,96 @@ from BaseClasses import MultiWorld
from .Options import is_option_enabled, get_option_value from .Options import is_option_enabled, get_option_value
joke_hints = [ joke_hints = [
("Quaternions", "break", "my brain"), "Quaternions break my brain",
("Eclipse", "has nothing", "but you should do it anyway"), "Eclipse has nothing, but you should do it anyway.",
("", "Beep", ""), "Beep",
("Putting in custom subtitles", "shouldn't have been", "as hard as it was..."), "Putting in custom subtitles shouldn't have been as hard as it was...",
("BK mode", "is right", "around the corner"), "BK mode is right around the corner.",
("", "You can do it!", ""), "You can do it!",
("", "I believe in you!", ""), "I believe in you!",
("The person playing", "is", "cute <3"), "The person playing is cute. <3",
("dash dot, dash dash dash", "dash, dot dot dot dot, dot dot", "dash dot, dash dash dot"), "dash dot, dash dash dash, dash, dot dot dot dot, dot dot, dash dot, dash dash dot",
("When you think about it,", "there are actually a lot of", "bubbles in a stream"), "When you think about it, there are actually a lot of bubbles in a stream.",
("Never gonna give you up", "Never gonna let you down", "Never gonna run around and desert you"), "Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you",
("Thanks to", "the Archipelago developers", "for making this possible."), "Thanks to the Archipelago developers for making this possible.",
("Have you tried ChecksFinder?", "If you like puzzles,", "you might enjoy it!"), "Have you tried ChecksFinder?\nIf you like puzzles, you might enjoy it!",
("Have you tried Dark Souls III?", "A tough game like this", "feels better when friends are helping you!"), "Have you tried Dark Souls III?\nA tough game like this feels better when friends are helping you!",
("Have you tried Donkey Kong Country 3?", "A legendary game", "from a golden age of platformers!"), "Have you tried Donkey Kong Country 3?\nA legendary game from a golden age of platformers!",
("Have you tried Factorio?", "Alone in an unknown multiworld.", "Sound familiar?"), "Have you tried Factorio?\nAlone in an unknown multiworld. Sound familiar?",
("Have you tried Final Fantasy?", "Experience a classic game", "improved to fit modern standards!"), "Have you tried Final Fantasy?\nExperience a classic game improved to fit modern standards!",
("Have you tried Hollow Knight?", "Another independent hit", "revolutionising a genre!"), "Have you tried Hollow Knight?\nAnother independent hit revolutionising a genre!",
("Have you tried A Link to the Past?", "The Archipelago game", "that started it all!"), "Have you tried A Link to the Past?\nThe Archipelago game that started it all!",
("Have you tried Meritous?", "You should know that obscure games", "are often groundbreaking!"), "Have you tried Meritous?\nYou should know that obscure games are often groundbreaking!",
("Have you tried Ocarine of Time?", "One of the biggest randomizers,", "big inspiration for this one's features!"), "Have you tried Ocarina of Time?\nOne of the biggest randomizers, big inspiration for this one's features!",
("Have you tried Raft?", "Haven't you always wanted to explore", "the ocean surrounding this island?"), "Have you tried Raft?\nHaven't you always wanted to explore the ocean surrounding this island?",
("Have you tried Risk of Rain 2?", "I haven't either.", "But I hear it's incredible!"), "Have you tried Risk of Rain 2?\nI haven't either. But I hear it's incredible!",
("Have you tried Rogue Legacy?", "After solving so many puzzles", "it's the perfect way to rest your brain."), "Have you tried Rogue Legacy?\nAfter solving so many puzzles it's the perfect way to rest your brain.",
("Have you tried Secret of Evermore?", "I haven't either", "But I hear it's great!"), "Have you tried Secret of Evermore?\nI haven't either But I hear it's great!",
("Have you tried Slay the Spire?", "Experience the thrill of combat", "without needing fast fingers!"), "Have you tried Slay the Spire?\nExperience the thrill of combat without needing fast fingers!",
("Have you tried SMZ3?", "Why play one incredible game", "when you can play 2 at once?"), "Have you tried SMZ3?\nWhy play one incredible game when you can play 2 at once?",
("Have you tried Starcraft 2?", "Use strategy and management", "to crush your enemies!"), "Have you tried Starcraft 2?\nUse strategy and management to crush your enemies!",
("Have you tried Super Mario 64?", "3-dimensional games like this", "owe everything to that game."), "Have you tried Super Mario 64?\n3-dimensional games like this owe everything to that game.",
("Have you tried Super Metroid?", "A classic game", "that started a whole genre."), "Have you tried Super Metroid?\nA classic game, yet still one of the best in the genre.",
("Have you tried Timespinner?", "Everyone who plays it", "ends up loving it!"), "Have you tried Timespinner?\nEveryone who plays it ends up loving it!",
("Have you tried VVVVVV?", "Experience the essence of gaming", "distilled into its purest form!"), "Have you tried VVVVVV?\nExperience the essence of gaming distilled into its purest form!",
("Have you tried The Witness?", "Oh. I guess you already have.", " Thanks for playing!"), "Have you tried The Witness?\nOh. I guess you already have. Thanks for playing!",
("Have you tried Super Mario World?", "I don't think I need to tell you", "that it is beloved by many."), "Have you tried Super Mario World?\nI don't think I need to tell you that it is beloved by many.",
("Have you tried Overcooked 2?", "When you're done relaxing with puzzles,", "Have you tried Overcooked 2?\nWhen you're done relaxing with puzzles, use your energy to yell at your friends.",
"use your energy to yell at your friends."), "Have you tried Zillion?\nMe neither. But it looks fun. So, let's try something new together?",
("Have you tried Zillion?", "Me neither. But it looks fun.", "So, let's try something new together?"), "Have you tried Hylics 2?\nStop motion might just be the epitome of unique art styles.",
("Have you tried Hylics 2?", "Stop motion might just be", "the epitome of unique art styles."), "Have you tried Pokemon Red&Blue?\nA cute pet collecting game that fascinated an entire generation.",
("Have you tried Pokemon Red&Blue?", "A cute pet collecting game", "that fascinated an entire generation."), "Have you tried Lufia II?\nRoguelites are not just a 2010s phenomenon, turns out.",
("Waiting to get your items?", "Try BK Sudoku!", "Make progress even while stuck."), "Have you tried Minecraft?\nI have recently learned this is a question that needs to be asked.",
("One day I was fascinated", "by the subject of", "generation of waves by wind"), "Have you tried Subnautica?\nIf you like this game's lonely atmosphere, I would suggest you try it.",
("I don't like sandwiches", "Why would you think I like sandwiches?", "Have you ever seen me with a sandwich?"),
("Where are you right now?", "I'm at soup!", "What do you mean you're at soup?"), "Have you tried Sonic Adventure 2?\nIf the silence on this island is getting to you, "
("Remember to ask", "in the Archipelago Discord", "what the Functioning Brain does."), "there aren't many games more energetic.",
("", "Don't use your puzzle skips", "you might need them later"),
("", "For an extra challenge", "Try playing blindfolded"), "Waiting to get your items?\nTry BK Sudoku! Make progress even while stuck.",
("Go to the top of the mountain", "and see if you can see", "your house"), "One day I was fascinated by the subject of generation of waves by wind.",
("Yellow = Red + Green", "Cyan = Green + Blue", "Magenta = Red + Blue"), "I don't like sandwiches. Why would you think I like sandwiches? Have you ever seen me with a sandwich?",
("", "Maybe that panel really is unsolvable", ""), "Where are you right now?\nI'm at soup!\nWhat do you mean you're at soup?",
("", "Did you make sure it was plugged in?", ""), "Remember to ask in the Archipelago Discord what the Functioning Brain does.",
("", "Do not look into laser with remaining eye", ""), "Don't use your puzzle skips, you might need them later.",
("", "Try pressing Space to jump", ""), "For an extra challenge, try playing blindfolded.",
("The Witness is a Doom clone.", "Just replace the demons", "with puzzles"), "Go to the top of the mountain and see if you can see your house.",
("", "Test Hint please ignore", ""), "Yellow = Red + Green\nCyan = Green + Blue\nMagenta = Red + Blue",
("Shapers can never be placed", "outside the panel boundaries", "even if subtracted."), "Maybe that panel really is unsolvable.",
("", "The Keep laser panels use", "the same trick on both sides!"), "Did you make sure it was plugged in?",
("Can't get past a door? Try going around.", "Can't go around? Try building a", "nether portal."), "Do not look into laser with remaining eye.",
("", "We've been trying to reach you", "about your car's extended warranty"), "Try pressing Space to jump.",
("I hate this game. I hate this game.", "I hate this game.", "-chess player Bobby Fischer"), "The Witness is a Doom clone.\nJust replace the demons with puzzles",
("Dear Mario,", "Please come to the castle.", "I've baked a cake for you!"), "Test Hint please ignore",
("Have you tried waking up?", "", "Yeah, me neither."), "Shapers can never be placed outside the panel boundaries, even if subtracted.",
("Why do they call it The Witness,", "when wit game the player view", "play of with the game."), "The Keep laser panels use the same trick on both sides!",
("", "THE WIND FISH IN NAME ONLY", "FOR IT IS NEITHER"), "Can't get past a door? Try going around. Can't go around? Try building a nether portal.",
("Like this game? Try The Wit.nes,", "Understand, INSIGHT, Taiji", "What the Witness?, and Tametsi."), "We've been trying to reach you about your car's extended warranty.",
("", "In a race", "It's survival of the Witnesst"), "I hate this game. I hate this game. I hate this game.\n- Chess player Bobby Fischer",
("", "This hint has been removed", "We apologize for your inconvenience."), "Dear Mario,\nPlease come to the castle. I've baked a cake for you!",
("", "O-----------", ""), "Have you tried waking up?\nYeah, me neither.",
("Circle is draw", "Square is separate", "Line is win"), "Why do they call it The Witness, when wit game the player view play of with the game.",
("Circle is draw", "Star is pair", "Line is win"), "THE WIND FISH IN NAME ONLY, FOR IT IS NEITHER",
("Circle is draw", "Circle is copy", "Line is win"), "Like this game?\nTry The Wit.nes, Understand, INSIGHT, Taiji What the Witness?, and Tametsi.",
("Circle is draw", "Dot is eat", "Line is win"), "In a race, It's survival of the Witnesst.",
("Circle is start", "Walk is draw", "Line is win"), "This hint has been removed. We apologize for your inconvenience.",
("Circle is start", "Line is win", "Witness is you"), "O-----------",
("Can't find any items?", "Consider a relaxing boat trip", "around the island"), "Circle is draw\nSquare is separate\nLine is win",
("", "Don't forget to like, comment, and subscribe", ""), "Circle is draw\nStar is pair\nLine is win",
("Ah crap, gimme a second.", "[papers rustling]", "Sorry, nothing."), "Circle is draw\nCircle is copy\nLine is win",
("", "Trying to get a hint?", "Too bad."), "Circle is draw\nDot is eat\nLine is win",
("", "Here's a hint:", "Get good at the game."), "Circle is start\nWalk is draw\nLine is win",
("", "I'm still not entirely sure", "what we're witnessing here."), "Circle is start\nLine is win\nWitness is you",
("Have you found a red page yet?", "No?", "Then have you found a blue page?"), "Can't find any items?\nConsider a relaxing boat trip around the island!",
( "Don't forget to like, comment, and subscribe.",
"And here we see the Witness player,", "Ah crap, gimme a second.\n[papers rustling]\nSorry, nothing.",
"seeking answers where there are none-", "Trying to get a hint? Too bad.",
"Did someone turn on the loudspeaker?" "Here's a hint: Get good at the game.",
), "I'm still not entirely sure what we're witnessing here.",
( "Have you found a red page yet? No? Then have you found a blue page?",
"Hints suggested by:", "And here we see the Witness player, seeking answers where there are none-\nDid someone turn on the loudspeaker?",
"IHNN, Beaker, MrPokemon11, Ember, TheM8, NewSoupVi,",
"KF, Yoshi348, Berserker, BowlinJim, oddGarrett, Pink Switch." "Hints suggested by:\nIHNN, Beaker, MrPokemon11, Ember, TheM8, NewSoupVi,"
), "KF, Yoshi348, Berserker, BowlinJim, oddGarrett, Pink Switch.",
] ]
@ -186,7 +186,7 @@ def make_hint_from_item(multiworld: MultiWorld, player: int, item: str):
if location_obj.player != player: if location_obj.player != player:
location_name += " (" + multiworld.get_player_name(location_obj.player) + ")" location_name += " (" + multiworld.get_player_name(location_obj.player) + ")"
return location_name, item, location_obj.address if(location_obj.player == player) else -1 return location_name, item, location_obj.address if (location_obj.player == player) else -1
def make_hint_from_location(multiworld: MultiWorld, player: int, location: str): def make_hint_from_location(multiworld: MultiWorld, player: int, location: str):
@ -196,7 +196,7 @@ def make_hint_from_location(multiworld: MultiWorld, player: int, location: str):
if item_obj.player != player: if item_obj.player != player:
item_name += " (" + multiworld.get_player_name(item_obj.player) + ")" item_name += " (" + multiworld.get_player_name(item_obj.player) + ")"
return location, item_name, location_obj.address if(location_obj.player == player) else -1 return location, item_name, location_obj.address if (location_obj.player == player) else -1
def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
@ -258,9 +258,9 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
for loc, item in always_hint_pairs.items(): for loc, item in always_hint_pairs.items():
if item[1]: if item[1]:
hints.append((item[0], "can be found at", loc, item[2])) hints.append((f"{item[0]} can be found at {loc}.", item[2]))
else: else:
hints.append((loc, "contains", item[0], item[2])) hints.append((f"{loc} contains {item[0]}.", item[2]))
multiworld.per_slot_randoms[player].shuffle(hints) # shuffle always hint order in case of low hint amount multiworld.per_slot_randoms[player].shuffle(hints) # shuffle always hint order in case of low hint amount
@ -279,9 +279,9 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
del priority_hint_pairs[loc] del priority_hint_pairs[loc]
if item[1]: if item[1]:
hints.append((item[0], "can be found at", loc, item[2])) hints.append((f"{item[0]} can be found at {loc}.", item[2]))
else: else:
hints.append((loc, "contains", item[0], item[2])) hints.append((f"{loc} contains {item[0]}.", item[2]))
continue continue
if next_random_hint_is_item: if next_random_hint_is_item:
@ -290,10 +290,10 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
continue continue
hint = make_hint_from_item(multiworld, player, prog_items_in_this_world.pop()) hint = make_hint_from_item(multiworld, player, prog_items_in_this_world.pop())
hints.append((hint[1], "can be found at", hint[0], hint[2])) hints.append((f"{hint[1]} can be found at {hint[0]}.", hint[2]))
else: else:
hint = make_hint_from_location(multiworld, player, locations_in_this_world.pop()) hint = make_hint_from_location(multiworld, player, locations_in_this_world.pop())
hints.append((hint[0], "contains", hint[1], hint[2])) hints.append((f"{hint[0]} contains {hint[1]}.", hint[2]))
next_random_hint_is_item = not next_random_hint_is_item next_random_hint_is_item = not next_random_hint_is_item
@ -301,4 +301,4 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
def generate_joke_hints(multiworld: MultiWorld, player: int, amount: int): def generate_joke_hints(multiworld: MultiWorld, player: int, amount: int):
return [(x, y, z, -1) for (x, y, z) in multiworld.per_slot_randoms[player].sample(joke_hints, amount)] return [(x, -1) for x in multiworld.per_slot_randoms[player].sample(joke_hints, amount)]

View File

@ -205,13 +205,10 @@ class WitnessPlayerItems:
] ]
if is_option_enabled(multiworld, player, "shuffle_discarded_panels"): if is_option_enabled(multiworld, player, "shuffle_discarded_panels"):
if is_option_enabled(multiworld, player, "shuffle_discarded_panels"): if get_option_value(multiworld, player, "puzzle_randomization") == 1:
if get_option_value(multiworld, player, "puzzle_randomization") == 1: self.GOOD_ITEMS.append("Arrows")
self.GOOD_ITEMS.append("Arrows") else:
else: self.GOOD_ITEMS.append("Triangles")
self.GOOD_ITEMS.append("Triangles")
if not is_option_enabled(multiworld, player, "disable_non_randomized_puzzles"):
self.GOOD_ITEMS.append("Colored Squares")
self.GOOD_ITEMS = [ self.GOOD_ITEMS = [
StaticWitnessLogic.ITEMS_TO_PROGRESSIVE.get(item, item) for item in self.GOOD_ITEMS StaticWitnessLogic.ITEMS_TO_PROGRESSIVE.get(item, item) for item in self.GOOD_ITEMS

View File

@ -13,13 +13,10 @@ class StaticWitnessLocations:
""" """
ID_START = 158000 ID_START = 158000
EXTRA_LOCATIONS = { GENERAL_LOCATIONS = {
"Tutorial Front Left", "Tutorial Front Left",
"Tutorial Back Left", "Tutorial Back Left",
"Tutorial Back Right", "Tutorial Back Right",
}
GENERAL_LOCATIONS = {
"Tutorial Gate Open", "Tutorial Gate Open",
"Outside Tutorial Vault Box", "Outside Tutorial Vault Box",
@ -302,6 +299,7 @@ class StaticWitnessLocations:
"Quarry Obelisk Side 2", "Quarry Obelisk Side 2",
"Quarry Obelisk Side 3", "Quarry Obelisk Side 3",
"Quarry Obelisk Side 4", "Quarry Obelisk Side 4",
"Quarry Obelisk Side 5",
"Town Obelisk Side 1", "Town Obelisk Side 1",
"Town Obelisk Side 2", "Town Obelisk Side 2",
"Town Obelisk Side 3", "Town Obelisk Side 3",
@ -338,6 +336,7 @@ class StaticWitnessLocations:
"Quarry Obelisk Side 2", "Quarry Obelisk Side 2",
"Quarry Obelisk Side 3", "Quarry Obelisk Side 3",
"Quarry Obelisk Side 4", "Quarry Obelisk Side 4",
"Quarry Obelisk Side 5",
"Town Obelisk Side 1", "Town Obelisk Side 1",
"Town Obelisk Side 2", "Town Obelisk Side 2",
"Town Obelisk Side 3", "Town Obelisk Side 3",

View File

@ -433,7 +433,6 @@ class WitnessPlayerLogic:
self.ADDED_CHECKS = set() self.ADDED_CHECKS = set()
self.VICTORY_LOCATION = "0x0356B" self.VICTORY_LOCATION = "0x0356B"
self.EVENT_ITEM_NAMES = { self.EVENT_ITEM_NAMES = {
"0x01A0F": "Keep Laser Panel (Hedge Mazes) Activates",
"0x09D9B": "Monastery Shutters Open", "0x09D9B": "Monastery Shutters Open",
"0x193A6": "Monastery Laser Panel Activates", "0x193A6": "Monastery Laser Panel Activates",
"0x00037": "Monastery Branch Panels Activate", "0x00037": "Monastery Branch Panels Activate",
@ -442,8 +441,11 @@ class WitnessPlayerLogic:
"0x00139": "Keep Hedges 1 Knowledge", "0x00139": "Keep Hedges 1 Knowledge",
"0x019DC": "Keep Hedges 2 Knowledge", "0x019DC": "Keep Hedges 2 Knowledge",
"0x019E7": "Keep Hedges 3 Knowledge", "0x019E7": "Keep Hedges 3 Knowledge",
"0x01D3F": "Keep Laser Panel (Pressure Plates) Activates", "0x01A0F": "Keep Hedges 4 Knowledge",
"0x01BE9": "Keep Laser Panel (Pressure Plates) Activates - Expert", "0x033EA": "Pressure Plates 1 Knowledge",
"0x01BE9": "Pressure Plates 2 Knowledge",
"0x01CD3": "Pressure Plates 3 Knowledge",
"0x01D3F": "Pressure Plates 4 Knowledge",
"0x09F7F": "Mountain Access", "0x09F7F": "Mountain Access",
"0x0367C": "Quarry Laser Stoneworks Requirement Met", "0x0367C": "Quarry Laser Stoneworks Requirement Met",
"0x009A1": "Swamp Between Bridges Far 1 Activates", "0x009A1": "Swamp Between Bridges Far 1 Activates",
@ -492,11 +494,9 @@ class WitnessPlayerLogic:
"0x17D02": "Windmill Blades Spinning", "0x17D02": "Windmill Blades Spinning",
"0x0A0C9": "Cargo Box EP completable", "0x0A0C9": "Cargo Box EP completable",
"0x09E39": "Pink Light Bridge Extended", "0x09E39": "Pink Light Bridge Extended",
"0x01CD3": "Pressure Plates 3 EP available",
"0x17CC4": "Rails EP available", "0x17CC4": "Rails EP available",
"0x2896A": "Bridge Underside EP available", "0x2896A": "Bridge Underside EP available",
"0x00064": "First Tunnel EP visible", "0x00064": "First Tunnel EP visible",
"0x033EA": "Pressure Plates 1 EP available",
"0x03553": "Tutorial Video EPs availble", "0x03553": "Tutorial Video EPs availble",
"0x17C79": "Bunker Door EP available", "0x17C79": "Bunker Door EP available",
"0x275FF": "Stoneworks Light EPs available", "0x275FF": "Stoneworks Light EPs available",

View File

@ -27,20 +27,18 @@ class WitnessRegions:
) )
def connect(self, world: MultiWorld, player: int, source: str, target: str, player_logic: WitnessPlayerLogic, def connect(self, world: MultiWorld, player: int, source: str, target: str, player_logic: WitnessPlayerLogic,
panel_hex_to_solve_set=frozenset({frozenset()})): panel_hex_to_solve_set=frozenset({frozenset()}), backwards: bool = False):
""" """
connect two regions and set the corresponding requirement connect two regions and set the corresponding requirement
""" """
source_region = world.get_region(source, player) source_region = world.get_region(source, player)
target_region = world.get_region(target, player) target_region = world.get_region(target, player)
#print(source_region) backwards = " Backwards" if backwards else ""
#print(target_region)
#print("---")
connection = Entrance( connection = Entrance(
player, player,
source + " to " + target, source + " to " + target + backwards,
source_region source_region
) )
@ -92,10 +90,18 @@ class WitnessRegions:
self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()})) self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()}))
continue continue
backwards_connections = set()
for subset in connection[1]: for subset in connection[1]:
if all({panel in player_logic.DOOR_ITEMS_BY_ID for panel in subset}): if all({panel in player_logic.DOOR_ITEMS_BY_ID for panel in subset}):
if all({reference_logic.CHECKS_BY_HEX[panel]["id"] is None for panel in subset}): if all({reference_logic.CHECKS_BY_HEX[panel]["id"] is None for panel in subset}):
self.connect(world, player, connection[0], region_name, player_logic, frozenset({subset})) backwards_connections.add(subset)
if backwards_connections:
self.connect(
world, player, connection[0], region_name, player_logic,
frozenset(backwards_connections), True
)
self.connect(world, player, region_name, connection[0], player_logic, connection[1]) self.connect(world, player, region_name, connection[0], player_logic, connection[1])

View File

@ -114,15 +114,17 @@ Disabled Locations:
0x17CAA (River Garden Entry Panel) 0x17CAA (River Garden Entry Panel)
0x034A7 (Left Shutter EP) Precompleted Locations:
0x034AD (Middle Shutter EP) 0x034A7
0x034AF (Right Shutter EP) 0x034AD
0x339B6 (Eclipse EP) - 0x03549 - True 0x034AF
0x33A29 (Window EP) - 0x03553 - True 0x339B6
0x33A2A (Door EP) - 0x03553 - True 0x33A29
0x33B06 (Church EP) - 0x0354E - True 0x33A2A
0x3352F (Gate EP) 0x33B06
0x33600 (Patio Flowers EP) 0x3352F
0x035F5 (Tinted Door EP) 0x33600
0x000D3 (Green Room Flowers EP) 0x035F5
0x33A20 (Theater Flowers EP) 0x000D3
0x33A20
0x03BE2

View File

@ -10,3 +10,5 @@ Precompleted Locations:
0x33879 0x33879
0x016B2 0x016B2
0x036CE 0x036CE
0x03B25
0x28B2A

View File

@ -3,6 +3,42 @@ from Utils import cache_argsless
from itertools import accumulate from itertools import accumulate
from typing import * from typing import *
from fractions import Fraction from fractions import Fraction
from collections import Counter
def make_warning_string(any_j: bool, any_u: bool, any_d: bool, all_j: bool, all_u: bool, all_d: bool) -> str:
warning_string = ""
if any_j:
if all_j:
warning_string += "all "
else:
warning_string += "some "
warning_string += "junk"
if any_u or any_d:
if warning_string:
warning_string += " and "
if all_u:
warning_string += "all "
else:
warning_string += "some "
warning_string += "usefuls"
if any_d:
warning_string += ", including "
if all_d:
warning_string += "all "
else:
warning_string += "some "
warning_string += "non-essential door items"
return warning_string
def best_junk_to_add_based_on_weights(weights: Dict[Any, Fraction], created_junk: Dict[Any, int]): def best_junk_to_add_based_on_weights(weights: Dict[Any, Fraction], created_junk: Dict[Any, int]):