The Witness: v4 Content Update (#1338)

## New Features:

- EP Shuffle (Individual or Obelisk Sides, with varying difficulty levels)
- Ability to play without Puzzle Randomization (I.e. vanilla + AP layer)
- Pet the Dog to get a Puzzle Skip :) (No, really.)

## Changes:

- Starting inventory behavior improved (Consider starting items like doors and lasers logically even if they aren't part of the mode)
- Audio Log hint system improved (On low hint counts, you will no longer get the same locations hinted every time, i.e. always hints are shuffled)

## Fixes:

- Many fixes to symbol requirements
- Fixes to "shuffle_postgame" (What checks are evaluated as "postgame" in specific modes)
- Logically irrelevant doors are now "useful" instead of "progression"
This commit is contained in:
NewSoupVi
2023-02-01 21:18:07 +01:00
committed by GitHub
parent 3cef39a387
commit 4de7ebd8b0
22 changed files with 2557 additions and 297 deletions

View File

@@ -17,7 +17,7 @@ joke_hints = [
("Have you tried ChecksFinder?", "If 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 Donkey Kong Country 3?", "A legendary game", "from a golden age of platformers!"),
("Have you tried Factorio?", "Alone in an unknown world.", "Sound familiar?"),
("Have you tried Factorio?", "Alone in an unknown multiworld.", "Sound familiar?"),
("Have you tried Final Fantasy?", "Experience a classic game", "improved to fit modern standards!"),
("Have you tried Hollow Knight?", "Another independent hit", "revolutionising a genre!"),
("Have you tried A Link to the Past?", "The Archipelago game", "that started it all!"),
@@ -95,17 +95,18 @@ joke_hints = [
]
def get_always_hint_items(world: MultiWorld, player: int):
def get_always_hint_items(multiworld: MultiWorld, player: int):
priority = [
"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(world, player, "puzzle_randomization")
discards = is_option_enabled(world, player, "shuffle_discards")
difficulty = get_option_value(multiworld, player, "puzzle_randomization")
discards = is_option_enabled(multiworld, player, "shuffle_discarded_panels")
if discards:
if difficulty == 1:
@@ -116,16 +117,19 @@ def get_always_hint_items(world: MultiWorld, player: int):
return priority
def get_always_hint_locations(world: MultiWorld, player: int):
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",
"Shipwreck Couch EP",
"Mountainside Cloud Cycle EP",
}
def get_priority_hint_items(world: MultiWorld, player: int):
def get_priority_hint_items(multiworld: MultiWorld, player: int):
priority = {
"Negative Shapers",
"Sound Dots",
@@ -135,7 +139,7 @@ def get_priority_hint_items(world: MultiWorld, player: int):
"Swamp Laser Shortcut (Door)",
}
if is_option_enabled(world, player, "shuffle_lasers"):
if is_option_enabled(multiworld, player, "shuffle_lasers"):
lasers = {
"Symmetry Laser",
"Desert Laser",
@@ -150,18 +154,18 @@ def get_priority_hint_items(world: MultiWorld, player: int):
"Shadows Laser",
}
if get_option_value(world, player, "doors") >= 2:
if get_option_value(multiworld, player, "doors") >= 2:
priority.add("Desert Laser")
lasers.remove("Desert Laser")
priority.update(world.random.sample(lasers, 2))
priority.update(multiworld.slot_seeds[player].sample(lasers, 2))
else:
priority.update(world.random.sample(lasers, 3))
priority.update(multiworld.slot_seeds[player].sample(lasers, 3))
return priority
def get_priority_hint_locations(world: MultiWorld, player: int):
def get_priority_hint_locations(multiworld: MultiWorld, player: int):
return {
"Town RGB Room Left",
"Town RGB Room Right",
@@ -171,75 +175,85 @@ def get_priority_hint_locations(world: MultiWorld, player: int):
"Desert Vault Box",
"Mountainside Vault Box",
"Mountainside Discard",
"Tunnels Theater Flowers EP",
"Boat Shipwreck Green EP",
}
def make_hint_from_item(world: MultiWorld, player: int, item: str):
location_obj = world.find_item(item, player).item.location
def make_hint_from_item(multiworld: MultiWorld, player: int, item: str):
location_obj = multiworld.find_item(item, player).item.location
location_name = location_obj.name
if location_obj.player != player:
location_name += " (" + world.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
def make_hint_from_location(world: MultiWorld, player: int, location: str):
location_obj = world.get_location(location, player)
item_obj = world.get_location(location, player).item
def make_hint_from_location(multiworld: MultiWorld, player: int, location: str):
location_obj = multiworld.get_location(location, player)
item_obj = multiworld.get_location(location, player).item
item_name = item_obj.name
if item_obj.player != player:
item_name += " (" + world.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
def make_hints(world: MultiWorld, player: int, hint_amount: int):
def make_hints(multiworld: MultiWorld, player: int, hint_amount: int):
hints = list()
prog_items_in_this_world = {
item.name for item in world.get_items()
item.name for item in multiworld.get_items()
if item.player == player and item.code and item.advancement
}
loc_in_this_world = {
location.name for location in world.get_locations()
if location.player == player and not location.event
location.name for location in multiworld.get_locations()
if location.player == player and location.address
}
always_locations = [
location for location in get_always_hint_locations(world, player)
location for location in get_always_hint_locations(multiworld, player)
if location in loc_in_this_world
]
always_items = [
item for item in get_always_hint_items(world, player)
item for item in get_always_hint_items(multiworld, player)
if item in prog_items_in_this_world
]
priority_locations = [
location for location in get_priority_hint_locations(world, player)
location for location in get_priority_hint_locations(multiworld, player)
if location in loc_in_this_world
]
priority_items = [
item for item in get_priority_hint_items(world, player)
item for item in get_priority_hint_items(multiworld, player)
if item in prog_items_in_this_world
]
always_hint_pairs = dict()
for item in always_items:
hint_pair = make_hint_from_item(world, player, item)
hint_pair = make_hint_from_item(multiworld, player, item)
if hint_pair[2] == 158007: # Tutorial Gate Open
continue
always_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2])
for location in always_locations:
hint_pair = make_hint_from_location(world, player, location)
hint_pair = make_hint_from_location(multiworld, player, location)
always_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2])
priority_hint_pairs = dict()
for item in priority_items:
hint_pair = make_hint_from_item(world, player, item)
hint_pair = make_hint_from_item(multiworld, player, item)
if hint_pair[2] == 158007: # Tutorial Gate Open
continue
priority_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2])
for location in priority_locations:
hint_pair = make_hint_from_location(world, player, location)
hint_pair = make_hint_from_location(multiworld, player, location)
priority_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2])
for loc, item in always_hint_pairs.items():
@@ -248,17 +262,19 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int):
else:
hints.append((loc, "contains", item[0], item[2]))
next_random_hint_is_item = world.random.randint(0, 2)
multiworld.slot_seeds[player].shuffle(hints) # shuffle always hint order in case of low hint amount
next_random_hint_is_item = multiworld.slot_seeds[player].randint(0, 2)
prog_items_in_this_world = sorted(list(prog_items_in_this_world))
locations_in_this_world = sorted(list(loc_in_this_world))
world.random.shuffle(prog_items_in_this_world)
world.random.shuffle(locations_in_this_world)
multiworld.slot_seeds[player].shuffle(prog_items_in_this_world)
multiworld.slot_seeds[player].shuffle(locations_in_this_world)
while len(hints) < hint_amount:
if priority_hint_pairs:
loc = world.random.choice(list(priority_hint_pairs.keys()))
loc = multiworld.slot_seeds[player].choice(list(priority_hint_pairs.keys()))
item = priority_hint_pairs[loc]
del priority_hint_pairs[loc]
@@ -273,10 +289,10 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int):
next_random_hint_is_item = not next_random_hint_is_item
continue
hint = make_hint_from_item(world, 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]))
else:
hint = make_hint_from_location(world, 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]))
next_random_hint_is_item = not next_random_hint_is_item
@@ -284,5 +300,5 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int):
return hints
def generate_joke_hints(world: MultiWorld, amount: int):
return [(x, y, z, -1) for (x, y, z) in world.random.sample(joke_hints, amount)]
def generate_joke_hints(multiworld: MultiWorld, player: int, amount: int):
return [(x, y, z, -1) for (x, y, z) in multiworld.slot_seeds[player].sample(joke_hints, amount)]