| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | Archipelago init file for The Witness | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import typing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-13 18:06:43 -06:00
										 |  |  | from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, ItemClassification | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \ | 
					
						
							|  |  |  |     get_priority_hint_items, make_hints, generate_joke_hints | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | from ..AutoWorld import World, WebWorld | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | from .player_logic import WitnessPlayerLogic | 
					
						
							|  |  |  | from .static_logic import StaticWitnessLogic | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | from .locations import WitnessPlayerLocations, StaticWitnessLocations | 
					
						
							|  |  |  | from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems | 
					
						
							|  |  |  | from .rules import set_rules | 
					
						
							|  |  |  | from .regions import WitnessRegions | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | from .Options import is_option_enabled, the_witness_options, get_option_value | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  | from .utils import best_junk_to_add_based_on_weights, get_audio_logs, make_warning_string | 
					
						
							| 
									
										
										
										
											2022-08-22 05:50:01 +02:00
										 |  |  | from logging import warning | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WitnessWebWorld(WebWorld): | 
					
						
							|  |  |  |     theme = "jungle" | 
					
						
							| 
									
										
										
										
											2022-05-11 13:05:53 -05:00
										 |  |  |     tutorials = [Tutorial( | 
					
						
							|  |  |  |         "Multiworld Setup Guide", | 
					
						
							|  |  |  |         "A guide to playing The Witness with Archipelago.", | 
					
						
							|  |  |  |         "English", | 
					
						
							|  |  |  |         "setup_en.md", | 
					
						
							|  |  |  |         "setup/en", | 
					
						
							|  |  |  |         ["NewSoupVi", "Jarno"] | 
					
						
							|  |  |  |     )] | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WitnessWorld(World): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     The Witness is an open-world puzzle game with dozens of locations | 
					
						
							|  |  |  |     to explore and over 500 puzzles. Play the popular puzzle randomizer | 
					
						
							|  |  |  |     by sigma144, with an added layer of progression randomization! | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     game = "The Witness" | 
					
						
							|  |  |  |     topology_present = False | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |     data_version = 13 | 
					
						
							| 
									
										
										
										
											2022-07-01 22:01:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     static_logic = StaticWitnessLogic() | 
					
						
							|  |  |  |     static_locat = StaticWitnessLocations() | 
					
						
							|  |  |  |     static_items = StaticWitnessItems() | 
					
						
							|  |  |  |     web = WitnessWebWorld() | 
					
						
							| 
									
										
										
										
											2022-08-15 16:46:59 -05:00
										 |  |  |     option_definitions = the_witness_options | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     item_name_to_id = { | 
					
						
							|  |  |  |         name: data.code for name, data in static_items.ALL_ITEM_TABLE.items() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID | 
					
						
							| 
									
										
										
										
											2022-07-23 12:42:14 +02:00
										 |  |  |     item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |     required_client_version = (0, 3, 9) | 
					
						
							| 
									
										
										
										
											2022-11-06 21:26:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     def _get_slot_data(self): | 
					
						
							|  |  |  |         return { | 
					
						
							| 
									
										
										
										
											2023-02-03 10:39:18 -08:00
										 |  |  |             'seed': self.multiworld.per_slot_randoms[self.player].randint(0, 1000000), | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |             'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  |             'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |             'item_id_to_door_hexes': self.static_items.ITEM_ID_TO_DOOR_HEX_ALL, | 
					
						
							|  |  |  |             'door_hexes_in_the_pool': self.items.DOORS, | 
					
						
							| 
									
										
										
										
											2022-08-22 05:50:01 +02:00
										 |  |  |             'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME, | 
					
						
							|  |  |  |             'disabled_panels': self.player_logic.COMPLETELY_DISABLED_CHECKS, | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  |             'log_ids_to_hints': self.log_ids_to_hints, | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |             '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}, | 
					
						
							| 
									
										
										
										
											2023-03-10 07:58:00 +01:00
										 |  |  |             'entity_to_name': self.static_logic.ENTITY_ID_TO_NAME, | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def generate_early(self): | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         self.items_by_name = dict() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         if not (is_option_enabled(self.multiworld, self.player, "shuffle_symbols") | 
					
						
							|  |  |  |                 or get_option_value(self.multiworld, self.player, "shuffle_doors") | 
					
						
							|  |  |  |                 or is_option_enabled(self.multiworld, self.player, "shuffle_lasers")): | 
					
						
							|  |  |  |             if self.multiworld.players == 1: | 
					
						
							| 
									
										
										
										
											2022-08-22 05:50:01 +02:00
										 |  |  |                 warning("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door" | 
					
						
							|  |  |  |                         " Shuffle or Laser Shuffle if that doesn't seem right.") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle," | 
					
						
							|  |  |  |                                 " Door Shuffle or Laser Shuffle.") | 
					
						
							| 
									
										
										
										
											2022-07-17 12:56:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         disabled_locations = self.multiworld.exclude_locations[self.player].value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.player_logic = WitnessPlayerLogic( | 
					
						
							|  |  |  |             self.multiworld, self.player, disabled_locations, self.multiworld.start_inventory[self.player].value | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.locat = WitnessPlayerLocations(self.multiworld, self.player, self.player_logic) | 
					
						
							|  |  |  |         self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         self.regio = WitnessRegions(self.locat) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         self.log_ids_to_hints = dict() | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  |         self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |     def create_regions(self): | 
					
						
							|  |  |  |         self.regio.create_regions(self.multiworld, self.player, self.player_logic) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_items(self): | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         # Generate item pool | 
					
						
							|  |  |  |         pool = [] | 
					
						
							|  |  |  |         for item in self.items.ITEM_TABLE: | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  |             for i in range(0, self.items.PROG_ITEM_AMOUNTS[item]): | 
					
						
							|  |  |  |                 if item in self.items.PROGRESSION_TABLE: | 
					
						
							|  |  |  |                     witness_item = self.create_item(item) | 
					
						
							|  |  |  |                     pool.append(witness_item) | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |                     self.items_by_name[item] = witness_item | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         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] | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |                 if item_obj in pool: | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |                     pool.remove(item_obj)  # remove one instance of this pre-collected item if it exists | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-17 12:56:22 +02:00
										 |  |  |         for item in self.player_logic.STARTING_INVENTORY: | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |             self.multiworld.push_precollected(self.items_by_name[item]) | 
					
						
							|  |  |  |             pool.remove(self.items_by_name[item]) | 
					
						
							| 
									
										
										
										
											2022-07-17 12:56:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  |         for item in self.items.EXTRA_AMOUNTS: | 
					
						
							|  |  |  |             for i in range(0, self.items.EXTRA_AMOUNTS[item]): | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |                 witness_item = self.create_item(item) | 
					
						
							|  |  |  |                 pool.append(witness_item) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Tie Event Items to Event Locations (e.g. Laser Activations) | 
					
						
							|  |  |  |         for event_location in self.locat.EVENT_LOCATION_TABLE: | 
					
						
							|  |  |  |             item_obj = self.create_item( | 
					
						
							|  |  |  |                 self.player_logic.EVENT_ITEM_PAIRS[event_location] | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             location_obj = self.multiworld.get_location(event_location, self.player) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |             location_obj.place_locked_item(item_obj) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |         # 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) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |         # Place two locked items: Good symbol on Tutorial Gate Open, and a Puzzle Skip on "Town Pet the Dog" | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         good_items_in_the_game = [] | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |         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"]]) | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for symbol in self.items.GOOD_ITEMS: | 
					
						
							|  |  |  |             item = self.items_by_name[symbol] | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |             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. | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |                 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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             item = self.items_by_name[random_good_item] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |             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 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_rules(self): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         set_rules(self.multiworld, self.player, self.player_logic, self.locat) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def fill_slot_data(self) -> dict: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         hint_amount = get_option_value(self.multiworld, self.player, "hint_amount") | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 17:35:59 +01:00
										 |  |  |         credits_hint = ( | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |             "This Randomizer is brought to you by", | 
					
						
							|  |  |  |             "NewSoupVi, Jarno, blastron,", | 
					
						
							|  |  |  |             "jbzdarkid, sigma144, IHNN, oddGarrett.", -1 | 
					
						
							| 
									
										
										
										
											2022-11-17 17:35:59 +01:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         audio_logs = get_audio_logs().copy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if hint_amount != 0: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             generated_hints = make_hints(self.multiworld, self.player, hint_amount) | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 10:39:18 -08:00
										 |  |  |             self.multiworld.per_slot_randoms[self.player].shuffle(audio_logs) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  |             duplicates = len(audio_logs) // hint_amount | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for _ in range(0, hint_amount): | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |                 hint = generated_hints.pop(0) | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 for _ in range(0, duplicates): | 
					
						
							|  |  |  |                     audio_log = audio_logs.pop() | 
					
						
							|  |  |  |                     self.log_ids_to_hints[int(audio_log, 16)] = hint | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if audio_logs: | 
					
						
							|  |  |  |             audio_log = audio_logs.pop() | 
					
						
							|  |  |  |             self.log_ids_to_hints[int(audio_log, 16)] = credits_hint | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         joke_hints = generate_joke_hints(self.multiworld, self.player, len(audio_logs)) | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         while audio_logs: | 
					
						
							|  |  |  |             audio_log = audio_logs.pop() | 
					
						
							|  |  |  |             self.log_ids_to_hints[int(audio_log, 16)] = joke_hints.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # generate hints done | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         slot_data = self._get_slot_data() | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for option_name in the_witness_options: | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  |             slot_data[option_name] = get_option_value( | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 self.multiworld, self.player, option_name | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return slot_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_item(self, name: str) -> Item: | 
					
						
							|  |  |  |         # this conditional is purely for unit tests, which need to be able to create an item before generate_early | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  |         if hasattr(self, 'items') and name in self.items.ITEM_TABLE: | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |             item = self.items.ITEM_TABLE[name] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             item = StaticWitnessItems.ALL_ITEM_TABLE[name] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  |         if item.trap: | 
					
						
							|  |  |  |             classification = ItemClassification.trap | 
					
						
							|  |  |  |         elif item.progression: | 
					
						
							|  |  |  |             classification = ItemClassification.progression | 
					
						
							|  |  |  |         elif item.never_exclude: | 
					
						
							|  |  |  |             classification = ItemClassification.useful | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             classification = ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         new_item = WitnessItem( | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  |             name, classification, item.code, player=self.player | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         return new_item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  |     def get_filler_item_name(self) -> str:  # Used by itemlinks | 
					
						
							|  |  |  |         item = best_junk_to_add_based_on_weights(self.items.JUNK_WEIGHTS, self.junk_items_created) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.junk_items_created[item] += 1 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  |         return item | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WitnessLocation(Location): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Archipelago Location for The Witness | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     game: str = "The Witness" | 
					
						
							|  |  |  |     check_hex: int = -1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, player: int, name: str, address: typing.Optional[int], parent, ch_hex: int = -1): | 
					
						
							|  |  |  |         super().__init__(player, name, address, parent) | 
					
						
							|  |  |  |         self.check_hex = ch_hex | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_region(world: MultiWorld, player: int, name: str, | 
					
						
							|  |  |  |                   locat: WitnessPlayerLocations, region_locations=None, exits=None): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Create an Archipelago Region for The Witness | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-13 18:06:43 -06:00
										 |  |  |     ret = Region(name, player, world) | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     if region_locations: | 
					
						
							|  |  |  |         for location in region_locations: | 
					
						
							|  |  |  |             loc_id = locat.CHECK_LOCATION_TABLE[location] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             check_hex = -1 | 
					
						
							|  |  |  |             if location in StaticWitnessLogic.CHECKS_BY_NAME: | 
					
						
							|  |  |  |                 check_hex = int( | 
					
						
							|  |  |  |                     StaticWitnessLogic.CHECKS_BY_NAME[location]["checkHex"], 0 | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             location = WitnessLocation( | 
					
						
							|  |  |  |                 player, location, loc_id, ret, check_hex | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ret.locations.append(location) | 
					
						
							|  |  |  |     if exits: | 
					
						
							|  |  |  |         for single_exit in exits: | 
					
						
							|  |  |  |             ret.exits.append(Entrance(player, single_exit, ret)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret |