Skip to content

Commit

Permalink
The Witness: Hint distribution changes, added locations, misc fixes (A…
Browse files Browse the repository at this point in the history
…rchipelagoMW#1785)

Changes:

* Hints should feel a lot less same-y now ("Priority hints" are no longer always hints in disguise)
* Keep Hedge Mazes 1-3 and Pressure Plates 1-3 are added as locations in all settings
* Desert Final Room Hexagonal & Desert Final Room Bent 3 are added as locations
* Entries in exclude_locations that are referring to panels are now sent through slot data. This means they can be pre-skipped on the client side.

Fixes:

* Logic error in the Stoneworks that led to more restrictive seeds than necessary
* Logic error for Theater Flowers EP that led to more restrictive seeds than necessary
* Fixed crash in plando when "item" is a dict with weights
* Spoiler log locations were in random order per region, now they are consistent
  • Loading branch information
NewSoupVi authored Jun 20, 2023
1 parent afe9e12 commit 845502a
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 60 deletions.
2 changes: 1 addition & 1 deletion worlds/witness/WitnessLogic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Flo
158141 - 0x014E9 (Upper Row 8) - 0x03686 - Colored Squares & Eraser
158142 - 0x03677 (Stair Control) - True - Colored Squares & Eraser
Door - 0x0368A (Stairs) - 0x03677
158143 - 0x3C125 (Control Room Left) - 0x0367C - Black/White Squares & Dots & Eraser
158143 - 0x3C125 (Control Room Left) - 0x014E9 - Black/White Squares & Dots & Eraser
158144 - 0x0367C (Control Room Right) - 0x014E9 - Colored Squares & Dots & Eraser
159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True
159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True
Expand Down
2 changes: 1 addition & 1 deletion worlds/witness/WitnessLogicExpert.txt
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Flo
158141 - 0x014E9 (Upper Row 8) - 0x03686 - Squares & Colored Squares & Eraser & Stars & Stars + Same Colored Symbol
158142 - 0x03677 (Stair Control) - True - Squares & Colored Squares & Eraser
Door - 0x0368A (Stairs) - 0x03677
158143 - 0x3C125 (Control Room Left) - 0x0367C - Squares & Black/White Squares & Dots & Full Dots & Eraser
158143 - 0x3C125 (Control Room Left) - 0x014E9 - Squares & Black/White Squares & Dots & Full Dots & Eraser
158144 - 0x0367C (Control Room Right) - 0x014E9 - Squares & Colored Squares & Triangles & Eraser & Stars & Stars + Same Colored Symbol
159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True
159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True
Expand Down
4 changes: 2 additions & 2 deletions worlds/witness/WitnessLogicVanilla.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Tutorial (Tutorial) - Outside Tutorial - 0x03629:
158006 - 0x0A3B2 (Back Right) - True - True
158007 - 0x03629 (Gate Open) - 0x002C2 & 0x0A3B5 & 0x0A3B2 - True
158008 - 0x03505 (Gate Close) - 0x2FAF6 & 0x03629 - True
158009 - 0x0C335 (Pillar) - True - Triangles - True
158009 - 0x0C335 (Pillar) - True - Triangles
158010 - 0x0C373 (Patio Floor) - 0x0C335 - Dots
159512 - 0x33530 (Cloud EP) - True - True
159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True
Expand Down Expand Up @@ -264,7 +264,7 @@ Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Flo
158141 - 0x014E9 (Upper Row 8) - 0x03686 - Colored Squares & Eraser
158142 - 0x03677 (Stair Control) - True - Colored Squares & Eraser
Door - 0x0368A (Stairs) - 0x03677
158143 - 0x3C125 (Control Room Left) - 0x0367C - Black/White Squares & Dots & Eraser
158143 - 0x3C125 (Control Room Left) - 0x014E9 - Black/White Squares & Dots & Eraser
158144 - 0x0367C (Control Room Right) - 0x014E9 - Colored Squares & Dots & Eraser
159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True
159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True
Expand Down
21 changes: 13 additions & 8 deletions worlds/witness/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ def _get_slot_data(self):
'item_id_to_door_hexes': self.static_items.ITEM_ID_TO_DOOR_HEX_ALL,
'door_hexes_in_the_pool': self.items.DOORS,
'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME,
'disabled_panels': self.player_logic.COMPLETELY_DISABLED_CHECKS,
'disabled_panels': list(self.player_logic.COMPLETELY_DISABLED_CHECKS),
'log_ids_to_hints': self.log_ids_to_hints,
'progressive_item_lists': self.items.MULTI_LISTS_BY_CODE,
'obelisk_side_id_to_EPs': self.static_logic.OBELISK_SIDE_ID_TO_EP_HEXES,
'precompleted_puzzles': {int(h, 16) for h in self.player_logic.PRECOMPLETED_LOCATIONS},
'precompleted_puzzles': [int(h, 16) for h in self.player_logic.EXCLUDED_LOCATIONS],
'entity_to_name': self.static_logic.ENTITY_ID_TO_NAME,
}

Expand Down Expand Up @@ -143,14 +143,19 @@ def create_items(self):

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 item_key in {"item", "items"}:
if item_key in v:
if type(v[item_key]) is str:
plandoed_items.add(v[item_key])
elif type(v[item_key]) is dict:
plandoed_items.update(item for item, weight in v[item_key].items() if weight)
else:
# Other type of iterable
plandoed_items.update(v[item_key])

for symbol in self.items.GOOD_ITEMS:
item = self.items_by_name[symbol]
if item in pool and item not in plandoed_items:
if item in pool and symbol 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.
Expand Down Expand Up @@ -255,7 +260,7 @@ def fill_slot_data(self) -> dict:

self.multiworld.per_slot_randoms[self.player].shuffle(audio_logs)

duplicates = len(audio_logs) // hint_amount
duplicates = min(3, len(audio_logs) // hint_amount)

for _ in range(0, hint_amount):
hint = generated_hints.pop(0)
Expand Down
53 changes: 30 additions & 23 deletions worlds/witness/hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,31 +96,30 @@


def get_always_hint_items(multiworld: MultiWorld, player: int):
priority = [
always = [
"Boat",
"Mountain Bottom Floor Final Room Entry (Door)",
"Caves Mountain Shortcut (Door)",
"Caves Swamp Shortcut (Door)",
"Caves Exits to Main Island",
"Progressive Dots",
]

difficulty = get_option_value(multiworld, player, "puzzle_randomization")
discards = is_option_enabled(multiworld, player, "shuffle_discarded_panels")
wincon = get_option_value(multiworld, player, "victory_condition")

if discards:
if difficulty == 1:
priority.append("Arrows")
always.append("Arrows")
else:
priority.append("Triangles")
always.append("Triangles")

return priority
if wincon == 0:
always.append("Mountain Bottom Floor Final Room Entry (Door)")

return always


def get_always_hint_locations(multiworld: MultiWorld, player: int):
return {
"Swamp Purple Underwater",
"Shipwreck Vault Box",
"Challenge Vault Box",
"Mountain Bottom Floor Discard",
"Theater Eclipse EP",
Expand All @@ -131,6 +130,8 @@ def get_always_hint_locations(multiworld: MultiWorld, player: int):

def get_priority_hint_items(multiworld: MultiWorld, player: int):
priority = {
"Caves Mountain Shortcut (Door)",
"Caves Swamp Shortcut (Door)",
"Negative Shapers",
"Sound Dots",
"Colored Dots",
Expand All @@ -157,16 +158,18 @@ def get_priority_hint_items(multiworld: MultiWorld, player: int):
if get_option_value(multiworld, player, "doors") >= 2:
priority.add("Desert Laser")
lasers.remove("Desert Laser")
priority.update(multiworld.per_slot_randoms[player].sample(lasers, 2))
priority.update(multiworld.per_slot_randoms[player].sample(lasers, 5))

else:
priority.update(multiworld.per_slot_randoms[player].sample(lasers, 3))
priority.update(multiworld.per_slot_randoms[player].sample(lasers, 6))

return priority


def get_priority_hint_locations(multiworld: MultiWorld, player: int):
return {
"Swamp Purple Underwater",
"Shipwreck Vault Box",
"Town RGB Room Left",
"Town RGB Room Right",
"Treehouse Green Bridge 7",
Expand Down Expand Up @@ -264,26 +267,30 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):

multiworld.per_slot_randoms[player].shuffle(hints) # shuffle always hint order in case of low hint amount

next_random_hint_is_item = multiworld.per_slot_randoms[player].randint(0, 2)
remaining_hints = hint_amount - len(hints)
priority_hint_amount = int(max(0.0, min(len(priority_hint_pairs) / 2, remaining_hints / 2)))

prog_items_in_this_world = sorted(list(prog_items_in_this_world))
locations_in_this_world = sorted(list(loc_in_this_world))

multiworld.per_slot_randoms[player].shuffle(prog_items_in_this_world)
multiworld.per_slot_randoms[player].shuffle(locations_in_this_world)

while len(hints) < hint_amount:
if priority_hint_pairs:
loc = multiworld.per_slot_randoms[player].choice(list(priority_hint_pairs.keys()))
item = priority_hint_pairs[loc]
del priority_hint_pairs[loc]

if item[1]:
hints.append((f"{item[0]} can be found at {loc}.", item[2]))
else:
hints.append((f"{loc} contains {item[0]}.", item[2]))
continue
priority_hint_list = list(priority_hint_pairs.items())
multiworld.per_slot_randoms[player].shuffle(priority_hint_list)
for _ in range(0, priority_hint_amount):
next_priority_hint = priority_hint_list.pop()
loc = next_priority_hint[0]
item = next_priority_hint[1]

if item[1]:
hints.append((f"{item[0]} can be found at {loc}.", item[2]))
else:
hints.append((f"{loc} contains {item[0]}.", item[2]))

next_random_hint_is_item = multiworld.per_slot_randoms[player].randint(0, 2)

while len(hints) < hint_amount:
if next_random_hint_is_item:
if not prog_items_in_this_world:
next_random_hint_is_item = not next_random_hint_is_item
Expand Down
8 changes: 4 additions & 4 deletions worlds/witness/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ def __init__(self, locat: WitnessPlayerLocations, multiworld: MultiWorld, player
self.PROGRESSION_TABLE = dict()

self.ITEM_ID_TO_DOOR_HEX = dict()
self.DOORS = set()
self.DOORS = list()

self.PROG_ITEM_AMOUNTS = defaultdict(lambda: 1)

self.SYMBOLS_NOT_IN_THE_GAME = set()
self.SYMBOLS_NOT_IN_THE_GAME = list()

self.EXTRA_AMOUNTS = {
"Functioning Brain": 1,
Expand All @@ -162,7 +162,7 @@ def __init__(self, locat: WitnessPlayerLocations, multiworld: MultiWorld, player
if item[0] not in logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME:
del self.ITEM_TABLE[item[0]]
if item in StaticWitnessLogic.ALL_SYMBOL_ITEMS:
self.SYMBOLS_NOT_IN_THE_GAME.add(StaticWitnessItems.ALL_ITEM_TABLE[item[0]].code)
self.SYMBOLS_NOT_IN_THE_GAME.append(StaticWitnessItems.ALL_ITEM_TABLE[item[0]].code)
else:
if item[0] in StaticWitnessLogic.PROGRESSIVE_TO_ITEMS:
self.PROG_ITEM_AMOUNTS[item[0]] = len(logic.MULTI_LISTS[item[0]])
Expand All @@ -178,7 +178,7 @@ def __init__(self, locat: WitnessPlayerLocations, multiworld: MultiWorld, player
for entity_hex, items in logic.DOOR_ITEMS_BY_ID.items():
entity_hex_int = int(entity_hex, 16)

self.DOORS.add(entity_hex_int)
self.DOORS.append(entity_hex_int)

for item in items:
item_id = StaticWitnessItems.ALL_ITEM_TABLE[item].code
Expand Down
8 changes: 8 additions & 0 deletions worlds/witness/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class StaticWitnessLocations:
"Desert Light Room 3",
"Desert Pond Room 5",
"Desert Flood Room 6",
"Desert Final Hexagonal",
"Desert Final Bent 3",
"Desert Laser Panel",

"Quarry Stoneworks Lower Row 6",
Expand All @@ -61,7 +63,13 @@ class StaticWitnessLocations:
"Shadows Near 5",
"Shadows Laser Panel",

"Keep Hedge Maze 1",
"Keep Hedge Maze 2",
"Keep Hedge Maze 3",
"Keep Hedge Maze 4",
"Keep Pressure Plates 1",
"Keep Pressure Plates 2",
"Keep Pressure Plates 3",
"Keep Pressure Plates 4",
"Keep Discard",
"Keep Laser Panel Hedges",
Expand Down
21 changes: 11 additions & 10 deletions worlds/witness/player_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,21 +296,21 @@ def make_options_adjustments(self, world, player):
elif get_option_value(world, player, "shuffle_EPs") == 1: # Individual EPs
adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_obelisks()[1:])

else: # Obelisk Sides
yaml_disabled_eps = []
yaml_disabled_eps = []

for yaml_disabled_location in self.YAML_DISABLED_LOCATIONS:
if yaml_disabled_location not in StaticWitnessLogic.CHECKS_BY_NAME:
continue

loc_obj = StaticWitnessLogic.CHECKS_BY_NAME[yaml_disabled_location]
for yaml_disabled_location in self.YAML_DISABLED_LOCATIONS:
if yaml_disabled_location not in StaticWitnessLogic.CHECKS_BY_NAME:
continue

if loc_obj["panelType"] != "EP":
continue
loc_obj = StaticWitnessLogic.CHECKS_BY_NAME[yaml_disabled_location]

if loc_obj["panelType"] == "EP" and get_option_value(world, player, "shuffle_EPs") == 2:
yaml_disabled_eps.append(loc_obj["checkHex"])

adjustment_linesets_in_order.append(["Precompleted Locations:"] + yaml_disabled_eps)
if loc_obj["panelType"] in {"EP", "General"}:
self.EXCLUDED_LOCATIONS.add(loc_obj["checkHex"])

adjustment_linesets_in_order.append(["Precompleted Locations:"] + yaml_disabled_eps)

for adjustment_lineset in adjustment_linesets_in_order:
current_adjustment_type = None
Expand Down Expand Up @@ -430,6 +430,7 @@ def __init__(self, world: MultiWorld, player: int, disabled_locations: Set[str],
self.ALWAYS_EVENT_HEX_CODES = set()
self.COMPLETELY_DISABLED_CHECKS = set()
self.PRECOMPLETED_LOCATIONS = set()
self.EXCLUDED_LOCATIONS = set()
self.ADDED_CHECKS = set()
self.VICTORY_LOCATION = "0x0356B"
self.EVENT_ITEM_NAMES = {
Expand Down
17 changes: 11 additions & 6 deletions worlds/witness/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,19 @@ def _witness_meets_item_requirements(self, panel, world, player, player_logic: W
and self.can_reach("Windmill Interior to Theater", "Entrance", player)
)

exit_to_town = self.can_reach("Theater to Town", "Entrance", player)
entrance_to_town = (
self.can_reach("Town to Windmill Interior", "Entrance", player)
and self.can_reach("Windmill Interior to Theater", "Entrance", player)
theater_from_town = (
self.can_reach("Town to Windmill Interior", "Entrance", player)
and self.can_reach("Windmill Interior to Theater", "Entrance", player)
or self.can_reach("Theater to Town", "Entrance", player)
)

tunnels_from_town = (
self.can_reach("Tunnels to Windmill Interior", "Entrance", player)
and self.can_reach("Town to Windmill Interior", "Entrance", player)
or self.can_reach("Tunnels to Town", "Entrance", player)
)
tunnels_to_town = self.can_reach("Tunnels to Town", "Entrance", player)

if not (direct_access or (exit_to_town or entrance_to_town) and tunnels_to_town):
if not (direct_access or theater_from_town and tunnels_from_town):
valid_option = False
break

Expand Down
2 changes: 0 additions & 2 deletions worlds/witness/settings/Disable_Unrandomized.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ Disabled Locations:
0x0070F (Second Row 2)
0x0087D (Second Row 3)
0x002C7 (Second Row 4)
0x15ADD (River Outside Vault)
0x03702 (River Vault Box)
0x17CAA (Monastery Shortcut Panel)
0x0C2A4 (Bunker Entry)
0x17C79 (Tinted Glass Door)
Expand Down
4 changes: 2 additions & 2 deletions worlds/witness/static_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def read_logic_file(self, file_path="WitnessLogic.txt"):
"panels": parse_lambda(required_panel_lambda)
}

current_region["panels"].add(check_hex)
current_region["panels"].append(check_hex)
continue

required_item_lambda = line_split.pop(0)
Expand Down Expand Up @@ -117,7 +117,7 @@ def read_logic_file(self, file_path="WitnessLogic.txt"):
self.CHECKS_BY_NAME[self.CHECKS_BY_HEX[check_hex]["checkName"]] = self.CHECKS_BY_HEX[check_hex]
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[check_hex] = requirement

current_region["panels"].add(check_hex)
current_region["panels"].append(check_hex)

def __init__(self, file_path="WitnessLogic.txt"):
# All regions with a list of panels in them and the connections to other regions, before logic adjustments
Expand Down
2 changes: 1 addition & 1 deletion worlds/witness/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def define_new_region(region_string):
region_obj = {
"name": region_name,
"shortName": region_name_simple,
"panels": set()
"panels": list()
}
return region_obj, options

Expand Down

0 comments on commit 845502a

Please sign in to comment.