| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | Defines progression, junk and event items for The Witness | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import copy | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | from dataclasses import dataclass | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  | from typing import Optional, Dict, List, Set, TYPE_CHECKING | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | from BaseClasses import Item, MultiWorld, ItemClassification | 
					
						
							|  |  |  | from .locations import ID_START, WitnessPlayerLocations | 
					
						
							|  |  |  | from .player_logic import WitnessPlayerLogic | 
					
						
							|  |  |  | from .static_logic import ItemDefinition, DoorItemDefinition, ProgressiveItemDefinition, ItemCategory, \ | 
					
						
							|  |  |  |     StaticWitnessLogic, WeightedItemDefinition | 
					
						
							|  |  |  | from .utils import build_weighted_int_list | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import WitnessWorld | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | NUM_ENERGY_UPGRADES = 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @dataclass() | 
					
						
							|  |  |  | class ItemData: | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     ItemData for an item in The Witness | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |     ap_code: Optional[int] | 
					
						
							|  |  |  |     definition: ItemDefinition | 
					
						
							|  |  |  |     classification: ItemClassification | 
					
						
							|  |  |  |     local_only: bool = False | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WitnessItem(Item): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Item from the game The Witness | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     game: str = "The Witness" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StaticWitnessItems: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Class that handles Witness items independent of world settings | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     item_data: Dict[str, ItemData] = {} | 
					
						
							|  |  |  |     item_groups: Dict[str, List[str]] = {} | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |     # Useful items that are treated specially at generation time and should not be automatically added to the player's | 
					
						
							|  |  |  |     #   item list during get_progression_items. | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     special_usefuls: List[str] = ["Puzzle Skip"] | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         for item_name, definition in StaticWitnessLogic.all_items.items(): | 
					
						
							|  |  |  |             ap_item_code = definition.local_code + ID_START | 
					
						
							|  |  |  |             classification: ItemClassification = ItemClassification.filler | 
					
						
							|  |  |  |             local_only: bool = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if definition.category is ItemCategory.SYMBOL: | 
					
						
							|  |  |  |                 classification = ItemClassification.progression | 
					
						
							|  |  |  |                 StaticWitnessItems.item_groups.setdefault("Symbols", []).append(item_name) | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.DOOR: | 
					
						
							|  |  |  |                 classification = ItemClassification.progression | 
					
						
							|  |  |  |                 StaticWitnessItems.item_groups.setdefault("Doors", []).append(item_name) | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.LASER: | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |                 classification = ItemClassification.progression_skip_balancing | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |                 StaticWitnessItems.item_groups.setdefault("Lasers", []).append(item_name) | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.USEFUL: | 
					
						
							|  |  |  |                 classification = ItemClassification.useful | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.FILLER: | 
					
						
							|  |  |  |                 if item_name in ["Energy Fill (Small)"]: | 
					
						
							|  |  |  |                     local_only = True | 
					
						
							|  |  |  |                 classification = ItemClassification.filler | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.TRAP: | 
					
						
							|  |  |  |                 classification = ItemClassification.trap | 
					
						
							|  |  |  |             elif definition.category is ItemCategory.JOKE: | 
					
						
							|  |  |  |                 classification = ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             StaticWitnessItems.item_data[item_name] = ItemData(ap_item_code, definition, | 
					
						
							|  |  |  |                                                                classification, local_only) | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_item_to_door_mappings() -> Dict[int, List[int]]: | 
					
						
							|  |  |  |         output: Dict[int, List[int]] = {} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         for item_name, item_data in {name: data for name, data in StaticWitnessItems.item_data.items() | 
					
						
							|  |  |  |                                      if isinstance(data.definition, DoorItemDefinition)}.items(): | 
					
						
							|  |  |  |             item = StaticWitnessItems.item_data[item_name] | 
					
						
							|  |  |  |             output[item.ap_code] = [int(hex_string, 16) for hex_string in item_data.definition.panel_id_hexes] | 
					
						
							|  |  |  |         return output | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class WitnessPlayerItems: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Class that defines Items for a single world | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |     def __init__(self, world: "WitnessWorld", logic: WitnessPlayerLogic, locat: WitnessPlayerLocations): | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |         """Adds event items after logic changes due to options""" | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         self._world: "WitnessWorld" = world | 
					
						
							|  |  |  |         self._multiworld: MultiWorld = world.multiworld | 
					
						
							|  |  |  |         self._player_id: int = world.player | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         self._logic: WitnessPlayerLogic = logic | 
					
						
							|  |  |  |         self._locations: WitnessPlayerLocations = locat | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Duplicate the static item data, then make any player-specific adjustments to classification. | 
					
						
							| 
									
										
										
										
											2023-07-25 05:54:23 +02:00
										 |  |  |         self.item_data: Dict[str, ItemData] = copy.deepcopy(StaticWitnessItems.item_data) | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Remove all progression items that aren't actually in the game. | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         self.item_data = { | 
					
						
							|  |  |  |             name: data for (name, data) in self.item_data.items() | 
					
						
							|  |  |  |             if data.classification not in | 
					
						
							|  |  |  |             {ItemClassification.progression, ItemClassification.progression_skip_balancing} | 
					
						
							|  |  |  |             or name in logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Adjust item classifications based on game settings. | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         eps_shuffled = self._world.options.shuffle_EPs | 
					
						
							|  |  |  |         come_to_you = self._world.options.elevators_come_to_you | 
					
						
							| 
									
										
										
										
											2023-12-10 20:35:46 +01:00
										 |  |  |         difficulty = self._world.options.puzzle_randomization | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         for item_name, item_data in self.item_data.items(): | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |             if not eps_shuffled and item_name in {"Monastery Garden Entry (Door)", | 
					
						
							|  |  |  |                                                   "Monastery Shortcuts", | 
					
						
							|  |  |  |                                                   "Quarry Boathouse Hook Control (Panel)", | 
					
						
							|  |  |  |                                                   "Windmill Turn Control (Panel)"}: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |                 # Downgrade doors that only gate progress in EP shuffle. | 
					
						
							|  |  |  |                 item_data.classification = ItemClassification.useful | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |             elif not come_to_you and not eps_shuffled and item_name in {"Quarry Elevator Control (Panel)", | 
					
						
							|  |  |  |                                                                         "Swamp Long Bridge (Panel)"}: | 
					
						
							|  |  |  |                 # These Bridges/Elevators are not logical access because they may leave you stuck. | 
					
						
							|  |  |  |                 item_data.classification = ItemClassification.useful | 
					
						
							|  |  |  |             elif item_name in {"River Monastery Garden Shortcut (Door)", | 
					
						
							|  |  |  |                                "Monastery Laser Shortcut (Door)", | 
					
						
							|  |  |  |                                "Orchard Second Gate (Door)", | 
					
						
							|  |  |  |                                "Jungle Bamboo Laser Shortcut (Door)", | 
					
						
							|  |  |  |                                "Caves Elevator Controls (Panel)"}: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |                 # Downgrade doors that don't gate progress. | 
					
						
							|  |  |  |                 item_data.classification = ItemClassification.useful | 
					
						
							| 
									
										
										
										
											2023-12-10 20:35:46 +01:00
										 |  |  |             elif item_name == "Keep Pressure Plates 2 Exit (Door)" and not (difficulty == "none" and eps_shuffled): | 
					
						
							|  |  |  |                 # PP2EP requires the door in vanilla puzzles, otherwise it's unnecessary | 
					
						
							|  |  |  |                 item_data.classification = ItemClassification.useful | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Build the mandatory item list. | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |         self._mandatory_items: Dict[str, int] = {} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Add progression items to the mandatory item list. | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         progression_dict = { | 
					
						
							|  |  |  |             name: data for (name, data) in self.item_data.items() | 
					
						
							|  |  |  |             if data.classification in {ItemClassification.progression, ItemClassification.progression_skip_balancing} | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_data in progression_dict.items(): | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |             if isinstance(item_data.definition, ProgressiveItemDefinition): | 
					
						
							|  |  |  |                 num_progression = len(self._logic.MULTI_LISTS[item_name]) | 
					
						
							|  |  |  |                 self._mandatory_items[item_name] = num_progression | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |                 self._mandatory_items[item_name] = 1 | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         # Add setting-specific useful items to the mandatory item list. | 
					
						
							|  |  |  |         for item_name, item_data in {name: data for (name, data) in self.item_data.items() | 
					
						
							|  |  |  |                                      if data.classification == ItemClassification.useful}.items(): | 
					
						
							|  |  |  |             if item_name in StaticWitnessItems.special_usefuls: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             elif item_name == "Energy Capacity": | 
					
						
							|  |  |  |                 self._mandatory_items[item_name] = NUM_ENERGY_UPGRADES | 
					
						
							|  |  |  |             elif isinstance(item_data.classification, ProgressiveItemDefinition): | 
					
						
							|  |  |  |                 self._mandatory_items[item_name] = len(item_data.mappings) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self._mandatory_items[item_name] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add event items to the item definition list for later lookup. | 
					
						
							|  |  |  |         for event_location in self._locations.EVENT_LOCATION_TABLE: | 
					
						
							|  |  |  |             location_name = logic.EVENT_ITEM_PAIRS[event_location] | 
					
						
							|  |  |  |             self.item_data[location_name] = ItemData(None, ItemDefinition(0, ItemCategory.EVENT), | 
					
						
							|  |  |  |                                                      ItemClassification.progression, False) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_mandatory_items(self) -> Dict[str, int]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns the list of items that must be in the pool for the game to successfully generate. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-09-10 14:29:42 -07:00
										 |  |  |         return self._mandatory_items.copy() | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_filler_items(self, quantity: int) -> Dict[str, int]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Generates a list of filler items of the given length. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if quantity <= 0: | 
					
						
							|  |  |  |             return {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |         output: Dict[str, int] = {} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         remaining_quantity = quantity | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add joke items. | 
					
						
							|  |  |  |         output.update({name: 1 for (name, data) in self.item_data.items() | 
					
						
							|  |  |  |                        if data.definition.category is ItemCategory.JOKE}) | 
					
						
							|  |  |  |         remaining_quantity -= len(output) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Read trap configuration data. | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         trap_weight = self._world.options.trap_percentage / 100 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         filler_weight = 1 - trap_weight | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add filler items to the list. | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |         filler_items: Dict[str, float] | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         filler_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1 | 
					
						
							|  |  |  |                         for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.FILLER} | 
					
						
							|  |  |  |         filler_items = {name: base_weight * filler_weight / sum(filler_items.values()) | 
					
						
							|  |  |  |                         for name, base_weight in filler_items.items() if base_weight > 0} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add trap items. | 
					
						
							|  |  |  |         if trap_weight > 0: | 
					
						
							|  |  |  |             trap_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1 | 
					
						
							|  |  |  |                           for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.TRAP} | 
					
						
							|  |  |  |             filler_items.update({name: base_weight * trap_weight / sum(trap_items.values()) | 
					
						
							|  |  |  |                                  for name, base_weight in trap_items.items() if base_weight > 0}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Get the actual number of each item by scaling the float weight values to match the target quantity. | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |         int_weights: List[int] = build_weighted_int_list(filler_items.values(), remaining_quantity) | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         output.update(zip(filler_items.keys(), int_weights)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return output | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_early_items(self) -> List[str]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns items that are ideal for placing on extremely early checks, like the tutorial gate. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |         output: Set[str] = set() | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         if self._world.options.shuffle_symbols: | 
					
						
							|  |  |  |             if self._world.options.shuffle_doors: | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                 output = {"Dots", "Black/White Squares", "Symmetry"} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                 output = {"Dots", "Black/White Squares", "Symmetry", "Shapers", "Stars"} | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |             if self._world.options.shuffle_discarded_panels: | 
					
						
							|  |  |  |                 if self._world.options.puzzle_randomization == 1: | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                     output.add("Arrows") | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                     output.add("Triangles") | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Replace progressive items with their parents. | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |             output = {StaticWitnessLogic.get_parent_progressive_item(item) for item in output} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Remove items that are mentioned in any plando options. (Hopefully, in the future, plando will get resolved | 
					
						
							|  |  |  |         #   before create_items so that we'll be able to check placed items instead of just removing all items mentioned | 
					
						
							|  |  |  |         #   regardless of whether or not they actually wind up being manually placed. | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  |         for plando_setting in self._multiworld.plando_items[self._player_id]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |             if plando_setting.get("from_pool", True): | 
					
						
							| 
									
										
										
										
											2023-08-16 07:03:41 -07:00
										 |  |  |                 for item_setting_key in [key for key in ["item", "items"] if key in plando_setting]: | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                     if type(plando_setting[item_setting_key]) is str: | 
					
						
							| 
									
										
										
										
											2023-08-16 07:03:41 -07:00
										 |  |  |                         output -= {plando_setting[item_setting_key]} | 
					
						
							| 
									
										
										
										
											2023-07-28 09:39:56 +02:00
										 |  |  |                     elif type(plando_setting[item_setting_key]) is dict: | 
					
						
							|  |  |  |                         output -= {item for item, weight in plando_setting[item_setting_key].items() if weight} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |                     else: | 
					
						
							|  |  |  |                         # Assume this is some other kind of iterable. | 
					
						
							| 
									
										
										
										
											2023-08-16 07:03:41 -07:00
										 |  |  |                         for inner_item in plando_setting[item_setting_key]: | 
					
						
							|  |  |  |                             if type(inner_item) is str: | 
					
						
							|  |  |  |                                 output -= {inner_item} | 
					
						
							|  |  |  |                             elif type(inner_item) is dict: | 
					
						
							|  |  |  |                                 output -= {item for item, weight in inner_item.items() if weight} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Sort the output for consistency across versions if the implementation changes but the logic does not. | 
					
						
							| 
									
										
										
										
											2023-08-16 07:03:41 -07:00
										 |  |  |         return sorted(list(output)) | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_door_ids_in_pool(self) -> List[int]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns the total set of all door IDs that are controlled by items in the pool. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |         output: List[int] = [] | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         for item_name, item_data in {name: data for name, data in self.item_data.items() | 
					
						
							|  |  |  |                                      if isinstance(data.definition, DoorItemDefinition)}.items(): | 
					
						
							|  |  |  |             output += [int(hex_string, 16) for hex_string in item_data.definition.panel_id_hexes] | 
					
						
							| 
									
										
										
										
											2023-11-24 06:27:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         return output | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_symbol_ids_not_in_pool(self) -> List[int]: | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns the item IDs of symbol items that were defined in the configuration file but are not in the pool. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return [data.ap_code for name, data in StaticWitnessItems.item_data.items() | 
					
						
							|  |  |  |                 if name not in self.item_data.keys() and data.definition.category is ItemCategory.SYMBOL] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 02:10:48 +02:00
										 |  |  |     def get_progressive_item_ids_in_pool(self) -> Dict[int, List[int]]: | 
					
						
							|  |  |  |         output: Dict[int, List[int]] = {} | 
					
						
							| 
									
										
										
										
											2023-07-18 20:02:57 -07:00
										 |  |  |         for item_name, quantity in {name: quantity for name, quantity in self._mandatory_items.items()}.items(): | 
					
						
							|  |  |  |             item = self.item_data[item_name] | 
					
						
							|  |  |  |             if isinstance(item.definition, ProgressiveItemDefinition): | 
					
						
							|  |  |  |                 # Note: we need to reference the static table here rather than the player-specific one because the child | 
					
						
							|  |  |  |                 #   items were removed from the pool when we pruned out all progression items not in the settings. | 
					
						
							|  |  |  |                 output[item.ap_code] = [StaticWitnessItems.item_data[child_item].ap_code | 
					
						
							|  |  |  |                                         for child_item in item.definition.child_item_names] | 
					
						
							|  |  |  |         return output |