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:
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user