From 805f33c39ef565c174a3cb7aae513d92920472b3 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Fri, 3 Mar 2023 00:08:24 +0100 Subject: [PATCH] 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> --- worlds/witness/WitnessLogic.txt | 9 +- worlds/witness/WitnessLogicExpert.txt | 11 +- worlds/witness/WitnessLogicVanilla.txt | 9 +- worlds/witness/__init__.py | 136 ++++++++---- worlds/witness/hints.py | 198 +++++++++--------- worlds/witness/items.py | 11 +- worlds/witness/locations.py | 7 +- worlds/witness/player_logic.py | 10 +- worlds/witness/regions.py | 18 +- .../witness/settings/Disable_Unrandomized.txt | 26 +-- .../witness/settings/EP_Shuffle/EP_Easy.txt | 4 +- worlds/witness/utils.py | 36 ++++ 12 files changed, 292 insertions(+), 183 deletions(-) diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index 308cba38..2b3c25f2 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F: 158206 - 0x0361B (Tower Shortcut Panel) - True - True Door - 0x04F8F (Tower Shortcut) - 0x0361B 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 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - 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 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: Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 @@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True: 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True -159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True -159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - 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 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 diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index f100a309..b396e535 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -270,7 +270,7 @@ Door - 0x0368A (Stairs) - 0x03677 159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True 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 158148 - 0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers 158166 - 0x17CA6 (Boat Spawn) - True - Boat @@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F: 158206 - 0x0361B (Tower Shortcut Panel) - True - True Door - 0x04F8F (Tower Shortcut) - 0x0361B 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 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - 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 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: Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 @@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True: 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True -159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True -159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - 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 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 diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt index 294950c3..f0e84e3e 100644 --- a/worlds/witness/WitnessLogicVanilla.txt +++ b/worlds/witness/WitnessLogicVanilla.txt @@ -411,7 +411,7 @@ Keep Tower (Keep) - Keep - 0x04F8F: 158206 - 0x0361B (Tower Shortcut Panel) - True - True Door - 0x04F8F (Tower Shortcut) - 0x0361B 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 159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - 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 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: Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 @@ -1113,8 +1113,9 @@ Obelisks (EPs) - Entry - True: 159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True 159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True 159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True -159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True -159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - 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 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 diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index 71bebb6e..f6fae6bd 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -14,7 +14,7 @@ from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems from .rules import set_rules from .regions import WitnessRegions 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 @@ -38,7 +38,7 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - data_version = 12 + data_version = 13 static_logic = StaticWitnessLogic() static_locat = StaticWitnessLocations() @@ -52,7 +52,7 @@ class WitnessWorld(World): location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS - required_client_version = (0, 3, 8) + required_client_version = (0, 3, 9) def _get_slot_data(self): return { @@ -93,13 +93,13 @@ class WitnessWorld(World): self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic) 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.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 pool = [] for item in self.items.ITEM_TABLE: @@ -109,22 +109,12 @@ class WitnessWorld(World): pool.append(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]: 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] 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: self.multiworld.push_precollected(self.items_by_name[item]) @@ -132,15 +122,8 @@ class WitnessWorld(World): for item in self.items.EXTRA_AMOUNTS: 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) - 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())) + witness_item = self.create_item(item) + pool.append(witness_item) # Tie Event Items to Event Locations (e.g. Laser Activations) 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.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): - # Put good item on first check if there are any of the designated "good items" in the pool + # Place two locked items: Good symbol on Tutorial Gate Open, and a Puzzle Skip on "Town Pet the Dog" 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: 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) if 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] - first_check.place_locked_item(item) - self.multiworld.itempool.remove(item) + if get_option_value(self.multiworld, self.player, "puzzle_randomization") == 1: + 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): set_rules(self.multiworld, self.player, self.player_logic, self.locat) diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index baa6dd45..4c36c482 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -2,96 +2,96 @@ from BaseClasses import MultiWorld from .Options import is_option_enabled, get_option_value joke_hints = [ - ("Quaternions", "break", "my brain"), - ("Eclipse", "has nothing", "but you should do it anyway"), - ("", "Beep", ""), - ("Putting in custom subtitles", "shouldn't have been", "as hard as it was..."), - ("BK mode", "is right", "around the corner"), - ("", "You can do it!", ""), - ("", "I believe in you!", ""), - ("The person playing", "is", "cute <3"), - ("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"), - ("Never gonna give you up", "Never gonna let you down", "Never gonna run around and desert you"), - ("Thanks to", "the Archipelago developers", "for making this possible."), - ("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 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!"), - ("Have you tried Meritous?", "You 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 Raft?", "Haven'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 Rogue Legacy?", "After 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 Slay the Spire?", "Experience 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 Starcraft 2?", "Use 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 Metroid?", "A classic game", "that started a whole genre."), - ("Have you tried Timespinner?", "Everyone who plays it", "ends up loving it!"), - ("Have you tried VVVVVV?", "Experience 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 Super Mario World?", "I 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,", - "use your energy to yell at your friends."), - ("Have you tried Zillion?", "Me neither. But it looks fun.", "So, let's try something new together?"), - ("Have you tried Hylics 2?", "Stop motion might just be", "the epitome of unique art styles."), - ("Have you tried Pokemon Red&Blue?", "A cute pet collecting game", "that fascinated an entire generation."), - ("Waiting to get your items?", "Try BK Sudoku!", "Make progress even while stuck."), - ("One day I was fascinated", "by the subject of", "generation of waves by wind"), - ("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?"), - ("Remember to ask", "in the Archipelago Discord", "what the Functioning Brain does."), - ("", "Don't use your puzzle skips", "you might need them later"), - ("", "For an extra challenge", "Try playing blindfolded"), - ("Go to the top of the mountain", "and see if you can see", "your house"), - ("Yellow = Red + Green", "Cyan = Green + Blue", "Magenta = Red + Blue"), - ("", "Maybe that panel really is unsolvable", ""), - ("", "Did you make sure it was plugged in?", ""), - ("", "Do not look into laser with remaining eye", ""), - ("", "Try pressing Space to jump", ""), - ("The Witness is a Doom clone.", "Just replace the demons", "with puzzles"), - ("", "Test Hint please ignore", ""), - ("Shapers can never be placed", "outside the panel boundaries", "even if subtracted."), - ("", "The Keep laser panels use", "the same trick on both sides!"), - ("Can't get past a door? Try going around.", "Can't go around? Try building a", "nether portal."), - ("", "We've been trying to reach you", "about your car's extended warranty"), - ("I hate this game. I hate this game.", "I hate this game.", "-chess player Bobby Fischer"), - ("Dear Mario,", "Please come to the castle.", "I've baked a cake for you!"), - ("Have you tried waking up?", "", "Yeah, me neither."), - ("Why do they call it The Witness,", "when wit game the player view", "play of with the game."), - ("", "THE WIND FISH IN NAME ONLY", "FOR IT IS NEITHER"), - ("Like this game? Try The Wit.nes,", "Understand, INSIGHT, Taiji", "What the Witness?, and Tametsi."), - ("", "In a race", "It's survival of the Witnesst"), - ("", "This hint has been removed", "We apologize for your inconvenience."), - ("", "O-----------", ""), - ("Circle is draw", "Square is separate", "Line is win"), - ("Circle is draw", "Star is pair", "Line is win"), - ("Circle is draw", "Circle is copy", "Line is win"), - ("Circle is draw", "Dot is eat", "Line is win"), - ("Circle is start", "Walk is draw", "Line is win"), - ("Circle is start", "Line is win", "Witness is you"), - ("Can't find any items?", "Consider a relaxing boat trip", "around the island"), - ("", "Don't forget to like, comment, and subscribe", ""), - ("Ah crap, gimme a second.", "[papers rustling]", "Sorry, nothing."), - ("", "Trying to get a hint?", "Too bad."), - ("", "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?"), - ( - "And here we see the Witness player,", - "seeking answers where there are none-", - "Did someone turn on the loudspeaker?" - ), - ( - "Hints suggested by:", - "IHNN, Beaker, MrPokemon11, Ember, TheM8, NewSoupVi,", - "KF, Yoshi348, Berserker, BowlinJim, oddGarrett, Pink Switch." - ), + "Quaternions break my brain", + "Eclipse has nothing, but you should do it anyway.", + "Beep", + "Putting in custom subtitles shouldn't have been as hard as it was...", + "BK mode is right around the corner.", + "You can do it!", + "I believe in you!", + "The person playing is cute. <3", + "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.", + "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.", + "Have you tried ChecksFinder?\nIf you like puzzles, you might enjoy it!", + "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?\nA legendary game from a golden age of platformers!", + "Have you tried Factorio?\nAlone in an unknown multiworld. Sound familiar?", + "Have you tried Final Fantasy?\nExperience a classic game improved to fit modern standards!", + "Have you tried Hollow Knight?\nAnother independent hit revolutionising a genre!", + "Have you tried A Link to the Past?\nThe Archipelago game that started it all!", + "Have you tried Meritous?\nYou should know that obscure games are often groundbreaking!", + "Have you tried Ocarina of Time?\nOne of the biggest randomizers, big inspiration for this one's features!", + "Have you tried Raft?\nHaven't you always wanted to explore the ocean surrounding this island?", + "Have you tried Risk of Rain 2?\nI haven't either. But I hear it's incredible!", + "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?\nI haven't either But I hear it's great!", + "Have you tried Slay the Spire?\nExperience the thrill of combat without needing fast fingers!", + "Have you tried SMZ3?\nWhy play one incredible game when you can play 2 at once?", + "Have you tried Starcraft 2?\nUse strategy and management to crush your enemies!", + "Have you tried Super Mario 64?\n3-dimensional games like this owe everything to that game.", + "Have you tried Super Metroid?\nA classic game, yet still one of the best in the genre.", + "Have you tried Timespinner?\nEveryone who plays it ends up loving it!", + "Have you tried VVVVVV?\nExperience the essence of gaming distilled into its purest form!", + "Have you tried The Witness?\nOh. I guess you already have. Thanks for playing!", + "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?\nWhen you're done relaxing with puzzles, 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 Hylics 2?\nStop 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 Lufia II?\nRoguelites are not just a 2010s phenomenon, turns out.", + "Have you tried Minecraft?\nI have recently learned this is a question that needs to be asked.", + "Have you tried Subnautica?\nIf you like this game's lonely atmosphere, I would suggest you try it.", + + "Have you tried Sonic Adventure 2?\nIf the silence on this island is getting to you, " + "there aren't many games more energetic.", + + "Waiting to get your items?\nTry BK Sudoku! Make progress even while stuck.", + "One day I was fascinated by the subject of generation of waves by wind.", + "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?\nI'm at soup!\nWhat do you mean you're at soup?", + "Remember to ask in the Archipelago Discord what the Functioning Brain does.", + "Don't use your puzzle skips, you might need them later.", + "For an extra challenge, try playing blindfolded.", + "Go to the top of the mountain and see if you can see your house.", + "Yellow = Red + Green\nCyan = Green + Blue\nMagenta = Red + Blue", + "Maybe that panel really is unsolvable.", + "Did you make sure it was plugged in?", + "Do not look into laser with remaining eye.", + "Try pressing Space to jump.", + "The Witness is a Doom clone.\nJust replace the demons with puzzles", + "Test Hint please ignore", + "Shapers can never be placed outside the panel boundaries, even if subtracted.", + "The Keep laser panels use the same trick on both sides!", + "Can't get past a door? Try going around. Can't go around? Try building a nether portal.", + "We've been trying to reach you about your car's extended warranty.", + "I hate this game. I hate this game. I hate this game.\n- Chess player Bobby Fischer", + "Dear Mario,\nPlease come to the castle. I've baked a cake for you!", + "Have you tried waking up?\nYeah, me neither.", + "Why do they call it The Witness, when wit game the player view play of with the game.", + "THE WIND FISH IN NAME ONLY, FOR IT IS NEITHER", + "Like this game?\nTry The Wit.nes, Understand, INSIGHT, Taiji What the Witness?, and Tametsi.", + "In a race, It's survival of the Witnesst.", + "This hint has been removed. We apologize for your inconvenience.", + "O-----------", + "Circle is draw\nSquare is separate\nLine is win", + "Circle is draw\nStar is pair\nLine is win", + "Circle is draw\nCircle is copy\nLine is win", + "Circle is draw\nDot is eat\nLine is win", + "Circle is start\nWalk is draw\nLine is win", + "Circle is start\nLine is win\nWitness is you", + "Can't find any items?\nConsider a relaxing boat trip around the island!", + "Don't forget to like, comment, and subscribe.", + "Ah crap, gimme a second.\n[papers rustling]\nSorry, nothing.", + "Trying to get a hint? Too bad.", + "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?", + "And here we see the Witness player, seeking answers where there are none-\nDid someone turn on the loudspeaker?", + + "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: 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): @@ -196,7 +196,7 @@ def make_hint_from_location(multiworld: MultiWorld, player: int, location: str): if item_obj.player != 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): @@ -258,9 +258,9 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): for loc, item in always_hint_pairs.items(): 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: - 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 @@ -279,9 +279,9 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): del priority_hint_pairs[loc] 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: - hints.append((loc, "contains", item[0], item[2])) + hints.append((f"{loc} contains {item[0]}.", item[2])) continue if next_random_hint_is_item: @@ -290,10 +290,10 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): continue 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: 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 @@ -301,4 +301,4 @@ def make_hints(multiworld: MultiWorld, player: int, hint_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)] diff --git a/worlds/witness/items.py b/worlds/witness/items.py index 914c1af2..4beb3b02 100644 --- a/worlds/witness/items.py +++ b/worlds/witness/items.py @@ -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 get_option_value(multiworld, player, "puzzle_randomization") == 1: - self.GOOD_ITEMS.append("Arrows") - else: - self.GOOD_ITEMS.append("Triangles") - if not is_option_enabled(multiworld, player, "disable_non_randomized_puzzles"): - self.GOOD_ITEMS.append("Colored Squares") + if get_option_value(multiworld, player, "puzzle_randomization") == 1: + self.GOOD_ITEMS.append("Arrows") + else: + self.GOOD_ITEMS.append("Triangles") self.GOOD_ITEMS = [ StaticWitnessLogic.ITEMS_TO_PROGRESSIVE.get(item, item) for item in self.GOOD_ITEMS diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index 37f61646..ccdeda1b 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -13,13 +13,10 @@ class StaticWitnessLocations: """ ID_START = 158000 - EXTRA_LOCATIONS = { + GENERAL_LOCATIONS = { "Tutorial Front Left", "Tutorial Back Left", "Tutorial Back Right", - } - - GENERAL_LOCATIONS = { "Tutorial Gate Open", "Outside Tutorial Vault Box", @@ -302,6 +299,7 @@ class StaticWitnessLocations: "Quarry Obelisk Side 2", "Quarry Obelisk Side 3", "Quarry Obelisk Side 4", + "Quarry Obelisk Side 5", "Town Obelisk Side 1", "Town Obelisk Side 2", "Town Obelisk Side 3", @@ -338,6 +336,7 @@ class StaticWitnessLocations: "Quarry Obelisk Side 2", "Quarry Obelisk Side 3", "Quarry Obelisk Side 4", + "Quarry Obelisk Side 5", "Town Obelisk Side 1", "Town Obelisk Side 2", "Town Obelisk Side 3", diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index 34b53b62..eb5b7934 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -433,7 +433,6 @@ class WitnessPlayerLogic: self.ADDED_CHECKS = set() self.VICTORY_LOCATION = "0x0356B" self.EVENT_ITEM_NAMES = { - "0x01A0F": "Keep Laser Panel (Hedge Mazes) Activates", "0x09D9B": "Monastery Shutters Open", "0x193A6": "Monastery Laser Panel Activates", "0x00037": "Monastery Branch Panels Activate", @@ -442,8 +441,11 @@ class WitnessPlayerLogic: "0x00139": "Keep Hedges 1 Knowledge", "0x019DC": "Keep Hedges 2 Knowledge", "0x019E7": "Keep Hedges 3 Knowledge", - "0x01D3F": "Keep Laser Panel (Pressure Plates) Activates", - "0x01BE9": "Keep Laser Panel (Pressure Plates) Activates - Expert", + "0x01A0F": "Keep Hedges 4 Knowledge", + "0x033EA": "Pressure Plates 1 Knowledge", + "0x01BE9": "Pressure Plates 2 Knowledge", + "0x01CD3": "Pressure Plates 3 Knowledge", + "0x01D3F": "Pressure Plates 4 Knowledge", "0x09F7F": "Mountain Access", "0x0367C": "Quarry Laser Stoneworks Requirement Met", "0x009A1": "Swamp Between Bridges Far 1 Activates", @@ -492,11 +494,9 @@ class WitnessPlayerLogic: "0x17D02": "Windmill Blades Spinning", "0x0A0C9": "Cargo Box EP completable", "0x09E39": "Pink Light Bridge Extended", - "0x01CD3": "Pressure Plates 3 EP available", "0x17CC4": "Rails EP available", "0x2896A": "Bridge Underside EP available", "0x00064": "First Tunnel EP visible", - "0x033EA": "Pressure Plates 1 EP available", "0x03553": "Tutorial Video EPs availble", "0x17C79": "Bunker Door EP available", "0x275FF": "Stoneworks Light EPs available", diff --git a/worlds/witness/regions.py b/worlds/witness/regions.py index 69b0317d..0e15cafe 100644 --- a/worlds/witness/regions.py +++ b/worlds/witness/regions.py @@ -27,20 +27,18 @@ class WitnessRegions: ) 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 """ source_region = world.get_region(source, player) target_region = world.get_region(target, player) - #print(source_region) - #print(target_region) - #print("---") + backwards = " Backwards" if backwards else "" connection = Entrance( player, - source + " to " + target, + source + " to " + target + backwards, source_region ) @@ -92,10 +90,18 @@ class WitnessRegions: self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()})) continue + backwards_connections = set() + for subset in connection[1]: 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}): - 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]) diff --git a/worlds/witness/settings/Disable_Unrandomized.txt b/worlds/witness/settings/Disable_Unrandomized.txt index 8f6034cc..dbe9caa5 100644 --- a/worlds/witness/settings/Disable_Unrandomized.txt +++ b/worlds/witness/settings/Disable_Unrandomized.txt @@ -114,15 +114,17 @@ Disabled Locations: 0x17CAA (River Garden Entry Panel) -0x034A7 (Left Shutter EP) -0x034AD (Middle Shutter EP) -0x034AF (Right Shutter EP) -0x339B6 (Eclipse EP) - 0x03549 - True -0x33A29 (Window EP) - 0x03553 - True -0x33A2A (Door EP) - 0x03553 - True -0x33B06 (Church EP) - 0x0354E - True -0x3352F (Gate EP) -0x33600 (Patio Flowers EP) -0x035F5 (Tinted Door EP) -0x000D3 (Green Room Flowers EP) -0x33A20 (Theater Flowers EP) \ No newline at end of file +Precompleted Locations: +0x034A7 +0x034AD +0x034AF +0x339B6 +0x33A29 +0x33A2A +0x33B06 +0x3352F +0x33600 +0x035F5 +0x000D3 +0x33A20 +0x03BE2 \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_Easy.txt b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt index 3e168b58..93905516 100644 --- a/worlds/witness/settings/EP_Shuffle/EP_Easy.txt +++ b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt @@ -9,4 +9,6 @@ Precompleted Locations: 0x33857 0x33879 0x016B2 -0x036CE \ No newline at end of file +0x036CE +0x03B25 +0x28B2A \ No newline at end of file diff --git a/worlds/witness/utils.py b/worlds/witness/utils.py index dcb335ed..7182545c 100644 --- a/worlds/witness/utils.py +++ b/worlds/witness/utils.py @@ -3,6 +3,42 @@ from Utils import cache_argsless from itertools import accumulate from typing import * 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]):