| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  | import collections | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | from dataclasses import dataclass | 
					
						
							|  |  |  | from typing import Any, Callable, Dict, List, Optional, Set, Tuple | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | from uuid import UUID | 
					
						
							| 
									
										
										
										
											2020-06-22 20:25:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from flask import render_template | 
					
						
							|  |  |  | from werkzeug.exceptions import abort | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-12 00:30:43 +01:00
										 |  |  | from MultiServer import Context, get_saving_second | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | from NetUtils import ClientStatus, Hint, NetworkItem, NetworkSlot, SlotType | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  | from Utils import restricted_loads, KeyedDefaultDict | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  | from . import app, cache | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | from .models import GameDataPackage, Room | 
					
						
							| 
									
										
										
										
											2020-06-22 20:25:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | # Multisave is currently updated, at most, every minute. | 
					
						
							|  |  |  | TRACKER_CACHE_TIMEOUT_IN_SECONDS = 60 | 
					
						
							| 
									
										
										
										
											2021-02-23 00:05:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | _multidata_cache = {} | 
					
						
							|  |  |  | _multiworld_trackers: Dict[str, Callable] = {} | 
					
						
							|  |  |  | _player_trackers: Dict[str, Callable] = {} | 
					
						
							| 
									
										
										
										
											2021-02-23 00:05:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | TeamPlayer = Tuple[int, int] | 
					
						
							|  |  |  | ItemMetadata = Tuple[int, int, int] | 
					
						
							| 
									
										
										
										
											2020-06-24 16:06:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | def _cache_results(func: Callable) -> Callable: | 
					
						
							|  |  |  |     """Stores the results of any computationally expensive methods after the initial call in TrackerData.
 | 
					
						
							|  |  |  |     If called again, returns the cached result instead, as results will not change for the lifetime of TrackerData. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     def method_wrapper(self: "TrackerData", *args): | 
					
						
							|  |  |  |         cache_key = f"{func.__name__}{''.join(f'_[{arg.__repr__()}]' for arg in args)}" | 
					
						
							|  |  |  |         if cache_key in self._tracker_cache: | 
					
						
							|  |  |  |             return self._tracker_cache[cache_key] | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         result = func(self, *args) | 
					
						
							|  |  |  |         self._tracker_cache[cache_key] = result | 
					
						
							|  |  |  |         return result | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     return method_wrapper | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @dataclass | 
					
						
							|  |  |  | class TrackerData: | 
					
						
							|  |  |  |     """A helper dataclass that is instantiated each time an HTTP request comes in for tracker data.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Provides helper methods to lazily load necessary data that each tracker require and caches any results so any | 
					
						
							|  |  |  |     subsequent helper method calls do not need to recompute results during the lifetime of this instance. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     room: Room | 
					
						
							|  |  |  |     _multidata: Dict[str, Any] | 
					
						
							|  |  |  |     _multisave: Dict[str, Any] | 
					
						
							|  |  |  |     _tracker_cache: Dict[str, Any] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, room: Room): | 
					
						
							|  |  |  |         """Initialize a new RoomMultidata object for the current room.""" | 
					
						
							|  |  |  |         self.room = room | 
					
						
							|  |  |  |         self._multidata = Context.decompress(room.seed.multidata) | 
					
						
							|  |  |  |         self._multisave = restricted_loads(room.multisave) if room.multisave else {} | 
					
						
							|  |  |  |         self._tracker_cache = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.item_name_to_id: Dict[str, Dict[str, int]] = {} | 
					
						
							|  |  |  |         self.location_name_to_id: Dict[str, Dict[str, int]] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Generate inverse lookup tables from data package, useful for trackers. | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         self.item_id_to_name: Dict[str, Dict[int, str]] = KeyedDefaultDict(lambda game_name: { | 
					
						
							|  |  |  |             game_name: KeyedDefaultDict(lambda code: f"Unknown Game {game_name} - Item (ID: {code})") | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         self.location_id_to_name: Dict[str, Dict[int, str]] = KeyedDefaultDict(lambda game_name: { | 
					
						
							|  |  |  |             game_name: KeyedDefaultDict(lambda code: f"Unknown Game {game_name} - Location (ID: {code})") | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         for game, game_package in self._multidata["datapackage"].items(): | 
					
						
							|  |  |  |             game_package = restricted_loads(GameDataPackage.get(checksum=game_package["checksum"]).data) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             self.item_id_to_name[game] = KeyedDefaultDict(lambda code: f"Unknown Item (ID: {code})", { | 
					
						
							|  |  |  |                 id: name for name, id in game_package["item_name_to_id"].items()}) | 
					
						
							|  |  |  |             self.location_id_to_name[game] = KeyedDefaultDict(lambda code: f"Unknown Location (ID: {code})", { | 
					
						
							|  |  |  |                 id: name for name, id in game_package["location_name_to_id"].items()}) | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Normal lookup tables as well. | 
					
						
							|  |  |  |             self.item_name_to_id[game] = game_package["item_name_to_id"] | 
					
						
							|  |  |  |             self.location_name_to_id[game] = game_package["item_name_to_id"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_seed_name(self) -> str: | 
					
						
							|  |  |  |         """Retrieves the seed name.""" | 
					
						
							|  |  |  |         return self._multidata["seed_name"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_slot_data(self, team: int, player: int) -> Dict[str, Any]: | 
					
						
							|  |  |  |         """Retrieves the slot data for a given player.""" | 
					
						
							|  |  |  |         return self._multidata["slot_data"][player] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_slot_info(self, team: int, player: int) -> NetworkSlot: | 
					
						
							|  |  |  |         """Retrieves the NetworkSlot data for a given player.""" | 
					
						
							|  |  |  |         return self._multidata["slot_info"][player] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_name(self, team: int, player: int) -> str: | 
					
						
							|  |  |  |         """Retrieves the slot name for a given player.""" | 
					
						
							|  |  |  |         return self.get_slot_info(team, player).name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_game(self, team: int, player: int) -> str: | 
					
						
							|  |  |  |         """Retrieves the game for a given player.""" | 
					
						
							|  |  |  |         return self.get_slot_info(team, player).game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_locations(self, team: int, player: int) -> Dict[int, ItemMetadata]: | 
					
						
							|  |  |  |         """Retrieves all locations with their containing item's metadata for a given player.""" | 
					
						
							|  |  |  |         return self._multidata["locations"][player] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_starting_inventory(self, team: int, player: int) -> List[int]: | 
					
						
							|  |  |  |         """Retrieves a list of all item codes a given slot starts with.""" | 
					
						
							|  |  |  |         return self._multidata["precollected_items"][player] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_checked_locations(self, team: int, player: int) -> Set[int]: | 
					
						
							|  |  |  |         """Retrieves the set of all locations marked complete by this player.""" | 
					
						
							|  |  |  |         return self._multisave.get("location_checks", {}).get((team, player), set()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_player_missing_locations(self, team: int, player: int) -> Set[int]: | 
					
						
							|  |  |  |         """Retrieves the set of all locations not marked complete by this player.""" | 
					
						
							|  |  |  |         return set(self.get_player_locations(team, player)) - self.get_player_checked_locations(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_received_items(self, team: int, player: int) -> List[NetworkItem]: | 
					
						
							|  |  |  |         """Returns all items received to this player in order of received.""" | 
					
						
							|  |  |  |         return self._multisave.get("received_items", {}).get((team, player, True), []) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |     def get_player_inventory_counts(self, team: int, player: int) -> collections.Counter: | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         """Retrieves a dictionary of all items received by their id and their received count.""" | 
					
						
							|  |  |  |         items = self.get_player_received_items(team, player) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         inventory = collections.Counter() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         for item in items: | 
					
						
							|  |  |  |             inventory[item.item] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return inventory | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_player_hints(self, team: int, player: int) -> Set[Hint]: | 
					
						
							|  |  |  |         """Retrieves a set of all hints relevant for a particular player.""" | 
					
						
							|  |  |  |         return self._multisave.get("hints", {}).get((team, player), set()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_player_last_activity(self, team: int, player: int) -> Optional[datetime.timedelta]: | 
					
						
							|  |  |  |         """Retrieves the relative timedelta for when a particular player was last active.
 | 
					
						
							|  |  |  |         Returns None if no activity was ever recorded. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self.get_room_last_activity().get((team, player), None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_client_status(self, team: int, player: int) -> ClientStatus: | 
					
						
							|  |  |  |         """Retrieves the ClientStatus of a particular player.""" | 
					
						
							|  |  |  |         return self._multisave.get("client_game_state", {}).get((team, player), ClientStatus.CLIENT_UNKNOWN) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_player_alias(self, team: int, player: int) -> Optional[str]: | 
					
						
							|  |  |  |         """Returns the alias of a particular player, if any.""" | 
					
						
							|  |  |  |         return self._multisave.get("name_aliases", {}).get((team, player), None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_team_completed_worlds_count(self) -> Dict[int, int]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of number of completed worlds per team.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             team: sum( | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |                 self.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL for player in players | 
					
						
							|  |  |  |             ) for team, players in self.get_all_players().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_team_hints(self) -> Dict[int, Set[Hint]]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all hints per team.""" | 
					
						
							|  |  |  |         hints = {} | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         for team, players in self.get_all_slots().items(): | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             hints[team] = set() | 
					
						
							|  |  |  |             for player in players: | 
					
						
							|  |  |  |                 hints[team] |= self.get_player_hints(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return hints | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_team_locations_total_count(self) -> Dict[int, int]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of total player locations each team has.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             team: sum(len(self.get_player_locations(team, player)) for player in players) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_players().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_team_locations_checked_count(self) -> Dict[int, int]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of checked player locations each team has.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             team: sum(len(self.get_player_checked_locations(team, player)) for player in players) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_players().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     # TODO: Change this method to properly build for each team once teams are properly implemented, as they don't | 
					
						
							|  |  |  |     #       currently exist in multidata to easily look up, so these are all assuming only 1 team: Team #0 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |     def get_all_slots(self) -> Dict[int, List[int]]: | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         """Retrieves a dictionary of all players ids on each team.""" | 
					
						
							|  |  |  |         return { | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             0: [ | 
					
						
							|  |  |  |                 player for player, slot_info in self._multidata["slot_info"].items() | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # TODO: Change this method to properly build for each team once teams are properly implemented, as they don't | 
					
						
							|  |  |  |     #       currently exist in multidata to easily look up, so these are all assuming only 1 team: Team #0 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_all_players(self) -> Dict[int, List[int]]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all player slot-type players ids on each team.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             0: [ | 
					
						
							|  |  |  |                 player for player, slot_info in self._multidata["slot_info"].items() | 
					
						
							|  |  |  |                 if self.get_slot_info(0, player).type == SlotType.player | 
					
						
							|  |  |  |             ] | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-24 01:30:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_saving_second(self) -> int: | 
					
						
							|  |  |  |         """Retrieves the saving second value for this seed.
 | 
					
						
							| 
									
										
										
										
											2020-06-28 09:28:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         Useful for knowing when the multisave gets updated so trackers can attempt to update. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return get_saving_second(self.get_seed_name()) | 
					
						
							| 
									
										
										
										
											2021-09-23 13:52:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_locations(self) -> Dict[TeamPlayer, Dict[int, ItemMetadata]]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all locations and their associated item metadata per player.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             (team, player): self.get_player_locations(team, player) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_players().items() for player in players | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-28 09:28:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_games(self) -> Dict[TeamPlayer, str]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of games for each player.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             (team, player): self.get_player_game(team, player) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_slots().items() for player in players | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-23 13:52:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_locations_complete(self) -> Dict[TeamPlayer, int]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all locations complete per player.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             (team, player): len(self.get_player_checked_locations(team, player)) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_players().items() for player in players | 
					
						
							| 
									
										
										
										
											2023-07-21 02:50:01 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_client_statuses(self) -> Dict[TeamPlayer, ClientStatus]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all ClientStatus values per player.""" | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             (team, player): self.get_player_client_status(team, player) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in self.get_all_players().items() for player in players | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_long_player_names(self) -> Dict[TeamPlayer, str]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of names with aliases for each player.""" | 
					
						
							|  |  |  |         long_player_names = {} | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         for team, players in self.get_all_slots().items(): | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             for player in players: | 
					
						
							|  |  |  |                 alias = self.get_player_alias(team, player) | 
					
						
							|  |  |  |                 if alias: | 
					
						
							|  |  |  |                     long_player_names[team, player] = f"{alias} ({self.get_player_name(team, player)})" | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     long_player_names[team, player] = self.get_player_name(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return long_player_names | 
					
						
							| 
									
										
										
										
											2020-06-28 09:28:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_last_activity(self) -> Dict[TeamPlayer, datetime.timedelta]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of all players and the timedelta from now to their last activity.
 | 
					
						
							|  |  |  |         Does not include players who have no activity recorded. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         last_activity: Dict[TeamPlayer, datetime.timedelta] = {} | 
					
						
							|  |  |  |         now = datetime.datetime.utcnow() | 
					
						
							|  |  |  |         for (team, player), timestamp in self._multisave.get("client_activity_timers", []): | 
					
						
							|  |  |  |             last_activity[team, player] = now - datetime.datetime.utcfromtimestamp(timestamp) | 
					
						
							| 
									
										
										
										
											2020-06-28 09:28:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         return last_activity | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_cache_results | 
					
						
							|  |  |  |     def get_room_videos(self) -> Dict[TeamPlayer, Tuple[str, str]]: | 
					
						
							|  |  |  |         """Retrieves a dictionary of any players who have video streaming enabled and their feeds.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Only supported platforms are Twitch and YouTube. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         video_feeds = {} | 
					
						
							|  |  |  |         for (team, player), video_data in self._multisave.get("video", []): | 
					
						
							|  |  |  |             video_feeds[team, player] = video_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return video_feeds | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route("/tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>") | 
					
						
							|  |  |  | def get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, generic: bool = False) -> str: | 
					
						
							|  |  |  |     key = f"{tracker}_{tracked_team}_{tracked_player}_{generic}" | 
					
						
							| 
									
										
										
										
											2022-12-12 00:30:43 +01:00
										 |  |  |     tracker_page = cache.get(key) | 
					
						
							|  |  |  |     if tracker_page: | 
					
						
							|  |  |  |         return tracker_page | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     timeout, tracker_page = get_timeout_and_tracker(tracker, tracked_team, tracked_player, generic) | 
					
						
							| 
									
										
										
										
											2022-12-12 00:30:43 +01:00
										 |  |  |     cache.set(key, tracker_page, timeout) | 
					
						
							|  |  |  |     return tracker_page | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | @app.route("/generic_tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>") | 
					
						
							|  |  |  | def get_generic_game_tracker(tracker: UUID, tracked_team: int, tracked_player: int) -> str: | 
					
						
							|  |  |  |     return get_player_tracker(tracker, tracked_team, tracked_player, True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 00:05:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | @app.route("/tracker/<suuid:tracker>", defaults={"game": "Generic"}) | 
					
						
							|  |  |  | @app.route("/tracker/<suuid:tracker>/<game>") | 
					
						
							|  |  |  | @cache.memoize(timeout=TRACKER_CACHE_TIMEOUT_IN_SECONDS) | 
					
						
							|  |  |  | def get_multiworld_tracker(tracker: UUID, game: str): | 
					
						
							|  |  |  |     # Room must exist. | 
					
						
							|  |  |  |     room = Room.get(tracker=tracker) | 
					
						
							| 
									
										
										
										
											2021-02-21 00:38:53 -05:00
										 |  |  |     if not room: | 
					
						
							|  |  |  |         abort(404) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     tracker_data = TrackerData(room) | 
					
						
							|  |  |  |     enabled_trackers = list(get_enabled_multiworld_trackers(room).keys()) | 
					
						
							|  |  |  |     if game not in _multiworld_trackers: | 
					
						
							|  |  |  |         return render_generic_multiworld_tracker(tracker_data, enabled_trackers) | 
					
						
							| 
									
										
										
										
											2022-12-12 00:30:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     return _multiworld_trackers[game](tracker_data, enabled_trackers) | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | def get_timeout_and_tracker(tracker: UUID, tracked_team: int, tracked_player: int, generic: bool) -> Tuple[int, str]: | 
					
						
							|  |  |  |     # Room must exist. | 
					
						
							|  |  |  |     room = Room.get(tracker=tracker) | 
					
						
							|  |  |  |     if not room: | 
					
						
							|  |  |  |         abort(404) | 
					
						
							| 
									
										
										
										
											2021-12-03 02:41:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     tracker_data = TrackerData(room) | 
					
						
							| 
									
										
										
										
											2021-12-03 02:41:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     # Load and render the game-specific player tracker, or fallback to generic tracker if none exists. | 
					
						
							|  |  |  |     game_specific_tracker = _player_trackers.get(tracker_data.get_player_game(tracked_team, tracked_player), None) | 
					
						
							|  |  |  |     if game_specific_tracker and not generic: | 
					
						
							|  |  |  |         tracker = game_specific_tracker(tracker_data, tracked_team, tracked_player) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         tracker = render_generic_tracker(tracker_data, tracked_team, tracked_player) | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     return (tracker_data.get_room_saving_second() - datetime.datetime.now().second) % 60 or 60, tracker | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | def get_enabled_multiworld_trackers(room: Room) -> Dict[str, Callable]: | 
					
						
							|  |  |  |     # Render the multitracker for any games that exist in the current room if they are defined. | 
					
						
							|  |  |  |     enabled_trackers = {} | 
					
						
							|  |  |  |     for game_name, endpoint in _multiworld_trackers.items(): | 
					
						
							|  |  |  |         if any(slot.game == game_name for slot in room.seed.slots): | 
					
						
							|  |  |  |             enabled_trackers[game_name] = endpoint | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     # We resort the tracker to have Generic first, then lexicographically each enabled game. | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "Generic": render_generic_multiworld_tracker, | 
					
						
							|  |  |  |         **{key: enabled_trackers[key] for key in sorted(enabled_trackers.keys())}, | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | def render_generic_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |     game = tracker_data.get_player_game(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Add received index to all received items, excluding starting inventory. | 
					
						
							|  |  |  |     received_items_in_order = {} | 
					
						
							|  |  |  |     for received_index, network_item in enumerate(tracker_data.get_player_received_items(team, player), start=1): | 
					
						
							|  |  |  |         received_items_in_order[network_item.item] = received_index | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return render_template( | 
					
						
							|  |  |  |         template_name_or_list="genericTracker.html", | 
					
						
							|  |  |  |         game_specific_tracker=game in _player_trackers, | 
					
						
							|  |  |  |         room=tracker_data.room, | 
					
						
							|  |  |  |         team=team, | 
					
						
							|  |  |  |         player=player, | 
					
						
							|  |  |  |         player_name=tracker_data.get_room_long_player_names()[team, player], | 
					
						
							|  |  |  |         inventory=tracker_data.get_player_inventory_counts(team, player), | 
					
						
							|  |  |  |         locations=tracker_data.get_player_locations(team, player), | 
					
						
							|  |  |  |         checked_locations=tracker_data.get_player_checked_locations(team, player), | 
					
						
							|  |  |  |         received_items=received_items_in_order, | 
					
						
							|  |  |  |         saving_second=tracker_data.get_room_saving_second(), | 
					
						
							|  |  |  |         game=game, | 
					
						
							|  |  |  |         games=tracker_data.get_room_games(), | 
					
						
							|  |  |  |         player_names_with_alias=tracker_data.get_room_long_player_names(), | 
					
						
							|  |  |  |         location_id_to_name=tracker_data.location_id_to_name, | 
					
						
							|  |  |  |         item_id_to_name=tracker_data.item_id_to_name, | 
					
						
							|  |  |  |         hints=tracker_data.get_player_hints(team, player), | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | def render_generic_multiworld_tracker(tracker_data: TrackerData, enabled_trackers: List[str]) -> str: | 
					
						
							|  |  |  |     return render_template( | 
					
						
							|  |  |  |         "multitracker.html", | 
					
						
							|  |  |  |         enabled_trackers=enabled_trackers, | 
					
						
							|  |  |  |         current_tracker="Generic", | 
					
						
							|  |  |  |         room=tracker_data.room, | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         all_slots=tracker_data.get_all_slots(), | 
					
						
							|  |  |  |         room_players=tracker_data.get_all_players(), | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         locations=tracker_data.get_room_locations(), | 
					
						
							|  |  |  |         locations_complete=tracker_data.get_room_locations_complete(), | 
					
						
							|  |  |  |         total_team_locations=tracker_data.get_team_locations_total_count(), | 
					
						
							|  |  |  |         total_team_locations_complete=tracker_data.get_team_locations_checked_count(), | 
					
						
							|  |  |  |         player_names_with_alias=tracker_data.get_room_long_player_names(), | 
					
						
							|  |  |  |         completed_worlds=tracker_data.get_team_completed_worlds_count(), | 
					
						
							|  |  |  |         games=tracker_data.get_room_games(), | 
					
						
							|  |  |  |         states=tracker_data.get_room_client_statuses(), | 
					
						
							|  |  |  |         hints=tracker_data.get_team_hints(), | 
					
						
							|  |  |  |         activity_timers=tracker_data.get_room_last_activity(), | 
					
						
							|  |  |  |         videos=tracker_data.get_room_videos(), | 
					
						
							|  |  |  |         item_id_to_name=tracker_data.item_id_to_name, | 
					
						
							|  |  |  |         location_id_to_name=tracker_data.location_id_to_name, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | # TODO: This is a temporary solution until a proper Tracker API can be implemented for tracker templates and data to | 
					
						
							|  |  |  | #       live in their respective world folders. | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | from worlds import network_data_package | 
					
						
							| 
									
										
										
										
											2021-11-25 21:04:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 14:04:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | if "Factorio" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_Factorio_multiworld_tracker(tracker_data: TrackerData, enabled_trackers: List[str]): | 
					
						
							|  |  |  |         inventories: Dict[TeamPlayer, Dict[int, int]] = { | 
					
						
							|  |  |  |             (team, player): { | 
					
						
							|  |  |  |                 tracker_data.item_id_to_name["Factorio"][item_id]: count | 
					
						
							|  |  |  |                 for item_id, count in tracker_data.get_player_inventory_counts(team, player).items() | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             } for team, players in tracker_data.get_all_slots().items() for player in players | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             if tracker_data.get_player_game(team, player) == "Factorio" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-24 00:35:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         return render_template( | 
					
						
							|  |  |  |             "multitracker__Factorio.html", | 
					
						
							|  |  |  |             enabled_trackers=enabled_trackers, | 
					
						
							|  |  |  |             current_tracker="Factorio", | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             all_slots=tracker_data.get_all_slots(), | 
					
						
							|  |  |  |             room_players=tracker_data.get_all_players(), | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             locations=tracker_data.get_room_locations(), | 
					
						
							|  |  |  |             locations_complete=tracker_data.get_room_locations_complete(), | 
					
						
							|  |  |  |             total_team_locations=tracker_data.get_team_locations_total_count(), | 
					
						
							|  |  |  |             total_team_locations_complete=tracker_data.get_team_locations_checked_count(), | 
					
						
							|  |  |  |             player_names_with_alias=tracker_data.get_room_long_player_names(), | 
					
						
							|  |  |  |             completed_worlds=tracker_data.get_team_completed_worlds_count(), | 
					
						
							|  |  |  |             games=tracker_data.get_room_games(), | 
					
						
							|  |  |  |             states=tracker_data.get_room_client_statuses(), | 
					
						
							|  |  |  |             hints=tracker_data.get_team_hints(), | 
					
						
							|  |  |  |             activity_timers=tracker_data.get_room_last_activity(), | 
					
						
							|  |  |  |             videos=tracker_data.get_room_videos(), | 
					
						
							|  |  |  |             item_id_to_name=tracker_data.item_id_to_name, | 
					
						
							|  |  |  |             location_id_to_name=tracker_data.location_id_to_name, | 
					
						
							|  |  |  |             inventories=inventories, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-10-24 00:35:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _multiworld_trackers["Factorio"] = render_Factorio_multiworld_tracker | 
					
						
							| 
									
										
										
										
											2021-12-14 11:04:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | if "A Link to the Past" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_ALinkToThePast_multiworld_tracker(tracker_data: TrackerData, enabled_trackers: List[str]): | 
					
						
							|  |  |  |         # Helper objects. | 
					
						
							|  |  |  |         alttp_id_lookup = tracker_data.item_name_to_id["A Link to the Past"] | 
					
						
							| 
									
										
										
										
											2021-12-14 11:04:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         multi_items = { | 
					
						
							|  |  |  |             alttp_id_lookup[name] | 
					
						
							|  |  |  |             for name in ("Progressive Sword", "Progressive Bow", "Bottle", "Progressive Glove", "Triforce Piece") | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         links = { | 
					
						
							|  |  |  |             "Bow":                   "Progressive Bow", | 
					
						
							|  |  |  |             "Silver Arrows":         "Progressive Bow", | 
					
						
							|  |  |  |             "Silver Bow":            "Progressive Bow", | 
					
						
							|  |  |  |             "Progressive Bow (Alt)": "Progressive Bow", | 
					
						
							|  |  |  |             "Bottle (Red Potion)":   "Bottle", | 
					
						
							|  |  |  |             "Bottle (Green Potion)": "Bottle", | 
					
						
							|  |  |  |             "Bottle (Blue Potion)":  "Bottle", | 
					
						
							|  |  |  |             "Bottle (Fairy)":        "Bottle", | 
					
						
							|  |  |  |             "Bottle (Bee)":          "Bottle", | 
					
						
							|  |  |  |             "Bottle (Good Bee)":     "Bottle", | 
					
						
							|  |  |  |             "Fighter Sword":         "Progressive Sword", | 
					
						
							|  |  |  |             "Master Sword":          "Progressive Sword", | 
					
						
							|  |  |  |             "Tempered Sword":        "Progressive Sword", | 
					
						
							|  |  |  |             "Golden Sword":          "Progressive Sword", | 
					
						
							|  |  |  |             "Power Glove":           "Progressive Glove", | 
					
						
							|  |  |  |             "Titans Mitts":          "Progressive Glove", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         links = {alttp_id_lookup[key]: alttp_id_lookup[value] for key, value in links.items()} | 
					
						
							|  |  |  |         levels = { | 
					
						
							|  |  |  |             "Fighter Sword":   1, | 
					
						
							|  |  |  |             "Master Sword":    2, | 
					
						
							|  |  |  |             "Tempered Sword":  3, | 
					
						
							|  |  |  |             "Golden Sword":    4, | 
					
						
							|  |  |  |             "Power Glove":     1, | 
					
						
							|  |  |  |             "Titans Mitts":    2, | 
					
						
							|  |  |  |             "Bow":             1, | 
					
						
							|  |  |  |             "Silver Bow":      2, | 
					
						
							|  |  |  |             "Triforce Piece": 90, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         tracking_names = [ | 
					
						
							|  |  |  |             "Progressive Sword", "Progressive Bow", "Book of Mudora", "Hammer", "Hookshot", "Magic Mirror", "Flute", | 
					
						
							|  |  |  |             "Pegasus Boots", "Progressive Glove", "Flippers", "Moon Pearl", "Blue Boomerang", "Red Boomerang", | 
					
						
							|  |  |  |             "Bug Catching Net", "Cape", "Shovel", "Lamp", "Mushroom", "Magic Powder", "Cane of Somaria", | 
					
						
							|  |  |  |             "Cane of Byrna", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake", "Bottle", "Triforce Piece", "Triforce", | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         default_locations = { | 
					
						
							|  |  |  |             "Light World": { | 
					
						
							|  |  |  |                 1572864, 1572865, 60034, 1572867, 1572868, 60037, 1572869, 1572866, 60040, 59788, 60046, 60175, | 
					
						
							|  |  |  |                 1572880, 60049, 60178, 1572883, 60052, 60181, 1572885, 60055, 60184, 191256, 60058, 60187, 1572884, | 
					
						
							|  |  |  |                 1572886, 1572887, 1572906, 60202, 60205, 59824, 166320, 1010170, 60208, 60211, 60214, 60217, 59836, | 
					
						
							|  |  |  |                 60220, 60223, 59839, 1573184, 60226, 975299, 1573188, 1573189, 188229, 60229, 60232, 1573193, | 
					
						
							|  |  |  |                 1573194, 60235, 1573187, 59845, 59854, 211407, 60238, 59857, 1573185, 1573186, 1572882, 212328, | 
					
						
							|  |  |  |                 59881, 59761, 59890, 59770, 193020, 212605 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Dark World": { | 
					
						
							|  |  |  |                 59776, 59779, 975237, 1572870, 60043, 1572881, 60190, 60193, 60196, 60199, 60840, 1573190, 209095, | 
					
						
							|  |  |  |                 1573192, 1573191, 60241, 60244, 60247, 60250, 59884, 59887, 60019, 60022, 60028, 60031 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Desert Palace": {1573216, 59842, 59851, 59791, 1573201, 59830}, | 
					
						
							|  |  |  |             "Eastern Palace": {1573200, 59827, 59893, 59767, 59833, 59773}, | 
					
						
							|  |  |  |             "Hyrule Castle": {60256, 60259, 60169, 60172, 59758, 59764, 60025, 60253}, | 
					
						
							|  |  |  |             "Agahnims Tower": {60082, 60085}, | 
					
						
							|  |  |  |             "Tower of Hera": {1573218, 59878, 59821, 1573202, 59896, 59899}, | 
					
						
							|  |  |  |             "Swamp Palace": {60064, 60067, 60070, 59782, 59785, 60073, 60076, 60079, 1573204, 60061}, | 
					
						
							|  |  |  |             "Thieves Town": {59905, 59908, 59911, 59914, 59917, 59920, 59923, 1573206}, | 
					
						
							|  |  |  |             "Skull Woods": {59809, 59902, 59848, 59794, 1573205, 59800, 59803, 59806}, | 
					
						
							|  |  |  |             "Ice Palace": {59872, 59875, 59812, 59818, 59860, 59797, 1573207, 59869}, | 
					
						
							|  |  |  |             "Misery Mire": {60001, 60004, 60007, 60010, 60013, 1573208, 59866, 59998}, | 
					
						
							|  |  |  |             "Turtle Rock": {59938, 59941, 59944, 1573209, 59947, 59950, 59953, 59956, 59926, 59929, 59932, 59935}, | 
					
						
							|  |  |  |             "Palace of Darkness": { | 
					
						
							|  |  |  |                 59968, 59971, 59974, 59977, 59980, 59983, 59986, 1573203, 59989, 59959, 59992, 59962, 59995, | 
					
						
							|  |  |  |                 59965 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Ganons Tower": { | 
					
						
							|  |  |  |                 60160, 60163, 60166, 60088, 60091, 60094, 60097, 60100, 60103, 60106, 60109, 60112, 60115, 60118, | 
					
						
							|  |  |  |                 60121, 60124, 60127, 1573217, 60130, 60133, 60136, 60139, 60142, 60145, 60148, 60151, 60157 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Total": set() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         key_only_locations = { | 
					
						
							|  |  |  |             "Light World": set(), | 
					
						
							|  |  |  |             "Dark World": set(), | 
					
						
							|  |  |  |             "Desert Palace": {0x140031, 0x14002b, 0x140061, 0x140028}, | 
					
						
							|  |  |  |             "Eastern Palace": {0x14005b, 0x140049}, | 
					
						
							|  |  |  |             "Hyrule Castle": {0x140037, 0x140034, 0x14000d, 0x14003d}, | 
					
						
							|  |  |  |             "Agahnims Tower": {0x140061, 0x140052}, | 
					
						
							|  |  |  |             "Tower of Hera": set(), | 
					
						
							|  |  |  |             "Swamp Palace": {0x140019, 0x140016, 0x140013, 0x140010, 0x14000a}, | 
					
						
							|  |  |  |             "Thieves Town": {0x14005e, 0x14004f}, | 
					
						
							|  |  |  |             "Skull Woods": {0x14002e, 0x14001c}, | 
					
						
							|  |  |  |             "Ice Palace": {0x140004, 0x140022, 0x140025, 0x140046}, | 
					
						
							|  |  |  |             "Misery Mire": {0x140055, 0x14004c, 0x140064}, | 
					
						
							|  |  |  |             "Turtle Rock": {0x140058, 0x140007}, | 
					
						
							|  |  |  |             "Palace of Darkness": set(), | 
					
						
							|  |  |  |             "Ganons Tower": {0x140040, 0x140043, 0x14003a, 0x14001f}, | 
					
						
							|  |  |  |             "Total": set() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         location_to_area = {} | 
					
						
							|  |  |  |         for area, locations in default_locations.items(): | 
					
						
							|  |  |  |             for location in locations: | 
					
						
							|  |  |  |                 location_to_area[location] = area | 
					
						
							|  |  |  |         for area, locations in key_only_locations.items(): | 
					
						
							|  |  |  |             for location in locations: | 
					
						
							|  |  |  |                 location_to_area[location] = area | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         checks_in_area = {area: len(checks) for area, checks in default_locations.items()} | 
					
						
							|  |  |  |         checks_in_area["Total"] = 216 | 
					
						
							|  |  |  |         ordered_areas = ( | 
					
						
							|  |  |  |             "Light World", "Dark World", "Hyrule Castle", "Agahnims Tower", "Eastern Palace", "Desert Palace", | 
					
						
							|  |  |  |             "Tower of Hera", "Palace of Darkness", "Swamp Palace", "Skull Woods", "Thieves Town", "Ice Palace", | 
					
						
							|  |  |  |             "Misery Mire", "Turtle Rock", "Ganons Tower", "Total" | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-12-14 11:04:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         player_checks_in_area = { | 
					
						
							|  |  |  |             (team, player): { | 
					
						
							|  |  |  |                 area_name: len(tracker_data._multidata["checks_in_area"][player][area_name]) | 
					
						
							|  |  |  |                 if area_name != "Total" else tracker_data._multidata["checks_in_area"][player]["Total"] | 
					
						
							|  |  |  |                 for area_name in ordered_areas | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in tracker_data.get_all_slots().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             for player in players | 
					
						
							|  |  |  |             if tracker_data.get_slot_info(team, player).type != SlotType.group and | 
					
						
							|  |  |  |             tracker_data.get_slot_info(team, player).game == "A Link to the Past" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-12-11 19:43:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         tracking_ids = [] | 
					
						
							|  |  |  |         for item in tracking_names: | 
					
						
							|  |  |  |             tracking_ids.append(alttp_id_lookup[item]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Can't wait to get this into the apworld. Oof. | 
					
						
							|  |  |  |         from worlds.alttp import Items | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         small_key_ids = {} | 
					
						
							|  |  |  |         big_key_ids = {} | 
					
						
							|  |  |  |         ids_small_key = {} | 
					
						
							|  |  |  |         ids_big_key = {} | 
					
						
							|  |  |  |         for item_name, data in Items.item_table.items(): | 
					
						
							|  |  |  |             if "Key" in item_name: | 
					
						
							|  |  |  |                 area = item_name.split("(")[1][:-1] | 
					
						
							|  |  |  |                 if "Small" in item_name: | 
					
						
							|  |  |  |                     small_key_ids[area] = data[2] | 
					
						
							|  |  |  |                     ids_small_key[data[2]] = area | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     big_key_ids[area] = data[2] | 
					
						
							|  |  |  |                     ids_big_key[data[2]] = area | 
					
						
							| 
									
										
										
										
											2022-12-11 19:43:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         def _get_location_table(checks_table: dict) -> dict: | 
					
						
							|  |  |  |             loc_to_area = {} | 
					
						
							|  |  |  |             for area, locations in checks_table.items(): | 
					
						
							|  |  |  |                 if area == "Total": | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 for location in locations: | 
					
						
							|  |  |  |                     loc_to_area[location] = area | 
					
						
							|  |  |  |             return loc_to_area | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         player_location_to_area = { | 
					
						
							|  |  |  |             (team, player): _get_location_table(tracker_data._multidata["checks_in_area"][player]) | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in tracker_data.get_all_slots().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             for player in players | 
					
						
							|  |  |  |             if tracker_data.get_slot_info(team, player).type != SlotType.group and | 
					
						
							|  |  |  |             tracker_data.get_slot_info(team, player).game == "A Link to the Past" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-12-11 19:43:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         checks_done: Dict[TeamPlayer, Dict[str: int]] = { | 
					
						
							|  |  |  |             (team, player): {location_name: 0 for location_name in default_locations} | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             for team, players in tracker_data.get_all_slots().items() | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             for player in players | 
					
						
							|  |  |  |             if tracker_data.get_slot_info(team, player).type != SlotType.group and | 
					
						
							|  |  |  |             tracker_data.get_slot_info(team, player).game == "A Link to the Past" | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-05 13:17:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         inventories: Dict[TeamPlayer, Dict[int, int]] = {} | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |         player_big_key_locations = {(player): set() for player in tracker_data.get_all_slots()[0]} | 
					
						
							|  |  |  |         player_small_key_locations = {player: set() for player in tracker_data.get_all_slots()[0]} | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         group_big_key_locations = set() | 
					
						
							|  |  |  |         group_key_locations = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (team, player), locations in checks_done.items(): | 
					
						
							|  |  |  |             # Check if game complete. | 
					
						
							|  |  |  |             if tracker_data.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL: | 
					
						
							|  |  |  |                 inventories[team, player][106] = 1  # Triforce | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Count number of locations checked. | 
					
						
							|  |  |  |             for location in tracker_data.get_player_checked_locations(team, player): | 
					
						
							|  |  |  |                 checks_done[team, player][player_location_to_area[team, player][location]] += 1 | 
					
						
							|  |  |  |                 checks_done[team, player]["Total"] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Count keys. | 
					
						
							|  |  |  |             for location, (item, receiving, _) in tracker_data.get_player_locations(team, player).items(): | 
					
						
							|  |  |  |                 if item in ids_big_key: | 
					
						
							|  |  |  |                     player_big_key_locations[receiving].add(ids_big_key[item]) | 
					
						
							|  |  |  |                 elif item in ids_small_key: | 
					
						
							|  |  |  |                     player_small_key_locations[receiving].add(ids_small_key[item]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Iterate over received items and build inventory/key counts. | 
					
						
							|  |  |  |             inventories[team, player] = collections.Counter() | 
					
						
							|  |  |  |             for network_item in tracker_data.get_player_received_items(team, player): | 
					
						
							|  |  |  |                 target_item = links.get(network_item.item, network_item.item) | 
					
						
							|  |  |  |                 if network_item.item in levels:  # non-progressive | 
					
						
							|  |  |  |                     inventories[team, player][target_item] = (max(inventories[team, player][target_item], levels[network_item.item])) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     inventories[team, player][target_item] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             group_key_locations |= player_small_key_locations[player] | 
					
						
							|  |  |  |             group_big_key_locations |= player_big_key_locations[player] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "multitracker__ALinkToThePast.html", | 
					
						
							|  |  |  |             enabled_trackers=enabled_trackers, | 
					
						
							|  |  |  |             current_tracker="A Link to the Past", | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							| 
									
										
										
										
											2024-01-04 08:29:42 -06:00
										 |  |  |             all_slots=tracker_data.get_all_slots(), | 
					
						
							|  |  |  |             room_players=tracker_data.get_all_players(), | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             locations=tracker_data.get_room_locations(), | 
					
						
							|  |  |  |             locations_complete=tracker_data.get_room_locations_complete(), | 
					
						
							|  |  |  |             total_team_locations=tracker_data.get_team_locations_total_count(), | 
					
						
							|  |  |  |             total_team_locations_complete=tracker_data.get_team_locations_checked_count(), | 
					
						
							|  |  |  |             player_names_with_alias=tracker_data.get_room_long_player_names(), | 
					
						
							|  |  |  |             completed_worlds=tracker_data.get_team_completed_worlds_count(), | 
					
						
							|  |  |  |             games=tracker_data.get_room_games(), | 
					
						
							|  |  |  |             states=tracker_data.get_room_client_statuses(), | 
					
						
							|  |  |  |             hints=tracker_data.get_team_hints(), | 
					
						
							|  |  |  |             activity_timers=tracker_data.get_room_last_activity(), | 
					
						
							|  |  |  |             videos=tracker_data.get_room_videos(), | 
					
						
							|  |  |  |             item_id_to_name=tracker_data.item_id_to_name, | 
					
						
							|  |  |  |             location_id_to_name=tracker_data.location_id_to_name, | 
					
						
							|  |  |  |             inventories=inventories, | 
					
						
							|  |  |  |             tracking_names=tracking_names, | 
					
						
							|  |  |  |             tracking_ids=tracking_ids, | 
					
						
							|  |  |  |             multi_items=multi_items, | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             ordered_areas=ordered_areas, | 
					
						
							|  |  |  |             checks_in_area=player_checks_in_area, | 
					
						
							|  |  |  |             key_locations=group_key_locations, | 
					
						
							|  |  |  |             big_key_locations=group_big_key_locations, | 
					
						
							|  |  |  |             small_key_ids=small_key_ids, | 
					
						
							|  |  |  |             big_key_ids=big_key_ids, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-05 13:17:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     def render_ALinkToThePast_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         # Helper objects. | 
					
						
							|  |  |  |         alttp_id_lookup = tracker_data.item_name_to_id["A Link to the Past"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         links = { | 
					
						
							|  |  |  |             "Bow":                   "Progressive Bow", | 
					
						
							|  |  |  |             "Silver Arrows":         "Progressive Bow", | 
					
						
							|  |  |  |             "Silver Bow":            "Progressive Bow", | 
					
						
							|  |  |  |             "Progressive Bow (Alt)": "Progressive Bow", | 
					
						
							|  |  |  |             "Bottle (Red Potion)":   "Bottle", | 
					
						
							|  |  |  |             "Bottle (Green Potion)": "Bottle", | 
					
						
							|  |  |  |             "Bottle (Blue Potion)":  "Bottle", | 
					
						
							|  |  |  |             "Bottle (Fairy)":        "Bottle", | 
					
						
							|  |  |  |             "Bottle (Bee)":          "Bottle", | 
					
						
							|  |  |  |             "Bottle (Good Bee)":     "Bottle", | 
					
						
							|  |  |  |             "Fighter Sword":         "Progressive Sword", | 
					
						
							|  |  |  |             "Master Sword":          "Progressive Sword", | 
					
						
							|  |  |  |             "Tempered Sword":        "Progressive Sword", | 
					
						
							|  |  |  |             "Golden Sword":          "Progressive Sword", | 
					
						
							|  |  |  |             "Power Glove":           "Progressive Glove", | 
					
						
							|  |  |  |             "Titans Mitts":          "Progressive Glove", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         links = {alttp_id_lookup[key]: alttp_id_lookup[value] for key, value in links.items()} | 
					
						
							|  |  |  |         levels = { | 
					
						
							|  |  |  |             "Fighter Sword":   1, | 
					
						
							|  |  |  |             "Master Sword":    2, | 
					
						
							|  |  |  |             "Tempered Sword":  3, | 
					
						
							|  |  |  |             "Golden Sword":    4, | 
					
						
							|  |  |  |             "Power Glove":     1, | 
					
						
							|  |  |  |             "Titans Mitts":    2, | 
					
						
							|  |  |  |             "Bow":             1, | 
					
						
							|  |  |  |             "Silver Bow":      2, | 
					
						
							|  |  |  |             "Triforce Piece": 90, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         tracking_names = [ | 
					
						
							|  |  |  |             "Progressive Sword", "Progressive Bow", "Book of Mudora", "Hammer", "Hookshot", "Magic Mirror", "Flute", | 
					
						
							|  |  |  |             "Pegasus Boots", "Progressive Glove", "Flippers", "Moon Pearl", "Blue Boomerang", "Red Boomerang", | 
					
						
							|  |  |  |             "Bug Catching Net", "Cape", "Shovel", "Lamp", "Mushroom", "Magic Powder", "Cane of Somaria", | 
					
						
							|  |  |  |             "Cane of Byrna", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake", "Bottle", "Triforce Piece", "Triforce", | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         default_locations = { | 
					
						
							|  |  |  |             "Light World": { | 
					
						
							|  |  |  |                 1572864, 1572865, 60034, 1572867, 1572868, 60037, 1572869, 1572866, 60040, 59788, 60046, 60175, | 
					
						
							|  |  |  |                 1572880, 60049, 60178, 1572883, 60052, 60181, 1572885, 60055, 60184, 191256, 60058, 60187, 1572884, | 
					
						
							|  |  |  |                 1572886, 1572887, 1572906, 60202, 60205, 59824, 166320, 1010170, 60208, 60211, 60214, 60217, 59836, | 
					
						
							|  |  |  |                 60220, 60223, 59839, 1573184, 60226, 975299, 1573188, 1573189, 188229, 60229, 60232, 1573193, | 
					
						
							|  |  |  |                 1573194, 60235, 1573187, 59845, 59854, 211407, 60238, 59857, 1573185, 1573186, 1572882, 212328, | 
					
						
							|  |  |  |                 59881, 59761, 59890, 59770, 193020, 212605 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Dark World": { | 
					
						
							|  |  |  |                 59776, 59779, 975237, 1572870, 60043, 1572881, 60190, 60193, 60196, 60199, 60840, 1573190, 209095, | 
					
						
							|  |  |  |                 1573192, 1573191, 60241, 60244, 60247, 60250, 59884, 59887, 60019, 60022, 60028, 60031 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Desert Palace": {1573216, 59842, 59851, 59791, 1573201, 59830}, | 
					
						
							|  |  |  |             "Eastern Palace": {1573200, 59827, 59893, 59767, 59833, 59773}, | 
					
						
							|  |  |  |             "Hyrule Castle": {60256, 60259, 60169, 60172, 59758, 59764, 60025, 60253}, | 
					
						
							|  |  |  |             "Agahnims Tower": {60082, 60085}, | 
					
						
							|  |  |  |             "Tower of Hera": {1573218, 59878, 59821, 1573202, 59896, 59899}, | 
					
						
							|  |  |  |             "Swamp Palace": {60064, 60067, 60070, 59782, 59785, 60073, 60076, 60079, 1573204, 60061}, | 
					
						
							|  |  |  |             "Thieves Town": {59905, 59908, 59911, 59914, 59917, 59920, 59923, 1573206}, | 
					
						
							|  |  |  |             "Skull Woods": {59809, 59902, 59848, 59794, 1573205, 59800, 59803, 59806}, | 
					
						
							|  |  |  |             "Ice Palace": {59872, 59875, 59812, 59818, 59860, 59797, 1573207, 59869}, | 
					
						
							|  |  |  |             "Misery Mire": {60001, 60004, 60007, 60010, 60013, 1573208, 59866, 59998}, | 
					
						
							|  |  |  |             "Turtle Rock": {59938, 59941, 59944, 1573209, 59947, 59950, 59953, 59956, 59926, 59929, 59932, 59935}, | 
					
						
							|  |  |  |             "Palace of Darkness": { | 
					
						
							|  |  |  |                 59968, 59971, 59974, 59977, 59980, 59983, 59986, 1573203, 59989, 59959, 59992, 59962, 59995, | 
					
						
							|  |  |  |                 59965 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Ganons Tower": { | 
					
						
							|  |  |  |                 60160, 60163, 60166, 60088, 60091, 60094, 60097, 60100, 60103, 60106, 60109, 60112, 60115, 60118, | 
					
						
							|  |  |  |                 60121, 60124, 60127, 1573217, 60130, 60133, 60136, 60139, 60142, 60145, 60148, 60151, 60157 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Total": set() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         key_only_locations = { | 
					
						
							|  |  |  |             "Light World": set(), | 
					
						
							|  |  |  |             "Dark World": set(), | 
					
						
							|  |  |  |             "Desert Palace": {0x140031, 0x14002b, 0x140061, 0x140028}, | 
					
						
							|  |  |  |             "Eastern Palace": {0x14005b, 0x140049}, | 
					
						
							|  |  |  |             "Hyrule Castle": {0x140037, 0x140034, 0x14000d, 0x14003d}, | 
					
						
							|  |  |  |             "Agahnims Tower": {0x140061, 0x140052}, | 
					
						
							|  |  |  |             "Tower of Hera": set(), | 
					
						
							|  |  |  |             "Swamp Palace": {0x140019, 0x140016, 0x140013, 0x140010, 0x14000a}, | 
					
						
							|  |  |  |             "Thieves Town": {0x14005e, 0x14004f}, | 
					
						
							|  |  |  |             "Skull Woods": {0x14002e, 0x14001c}, | 
					
						
							|  |  |  |             "Ice Palace": {0x140004, 0x140022, 0x140025, 0x140046}, | 
					
						
							|  |  |  |             "Misery Mire": {0x140055, 0x14004c, 0x140064}, | 
					
						
							|  |  |  |             "Turtle Rock": {0x140058, 0x140007}, | 
					
						
							|  |  |  |             "Palace of Darkness": set(), | 
					
						
							|  |  |  |             "Ganons Tower": {0x140040, 0x140043, 0x14003a, 0x14001f}, | 
					
						
							|  |  |  |             "Total": set() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         location_to_area = {} | 
					
						
							|  |  |  |         for area, locations in default_locations.items(): | 
					
						
							|  |  |  |             for checked_location in locations: | 
					
						
							|  |  |  |                 location_to_area[checked_location] = area | 
					
						
							|  |  |  |         for area, locations in key_only_locations.items(): | 
					
						
							|  |  |  |             for checked_location in locations: | 
					
						
							|  |  |  |                 location_to_area[checked_location] = area | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         checks_in_area = {area: len(checks) for area, checks in default_locations.items()} | 
					
						
							|  |  |  |         checks_in_area["Total"] = 216 | 
					
						
							|  |  |  |         ordered_areas = ( | 
					
						
							|  |  |  |             "Light World", "Dark World", "Hyrule Castle", "Agahnims Tower", "Eastern Palace", "Desert Palace", | 
					
						
							|  |  |  |             "Tower of Hera", "Palace of Darkness", "Swamp Palace", "Skull Woods", "Thieves Town", "Ice Palace", | 
					
						
							|  |  |  |             "Misery Mire", "Turtle Rock", "Ganons Tower", "Total" | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-05 13:17:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         tracking_ids = [] | 
					
						
							|  |  |  |         for item in tracking_names: | 
					
						
							|  |  |  |             tracking_ids.append(alttp_id_lookup[item]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Can't wait to get this into the apworld. Oof. | 
					
						
							|  |  |  |         from worlds.alttp import Items | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         small_key_ids = {} | 
					
						
							|  |  |  |         big_key_ids = {} | 
					
						
							|  |  |  |         ids_small_key = {} | 
					
						
							|  |  |  |         ids_big_key = {} | 
					
						
							|  |  |  |         for item_name, data in Items.item_table.items(): | 
					
						
							|  |  |  |             if "Key" in item_name: | 
					
						
							|  |  |  |                 area = item_name.split("(")[1][:-1] | 
					
						
							|  |  |  |                 if "Small" in item_name: | 
					
						
							|  |  |  |                     small_key_ids[area] = data[2] | 
					
						
							|  |  |  |                     ids_small_key[data[2]] = area | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     big_key_ids[area] = data[2] | 
					
						
							|  |  |  |                     ids_big_key[data[2]] = area | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inventory = collections.Counter() | 
					
						
							|  |  |  |         checks_done = {loc_name: 0 for loc_name in default_locations} | 
					
						
							|  |  |  |         player_big_key_locations = set() | 
					
						
							|  |  |  |         player_small_key_locations = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         player_locations = tracker_data.get_player_locations(team, player) | 
					
						
							|  |  |  |         for checked_location in tracker_data.get_player_checked_locations(team, player): | 
					
						
							|  |  |  |             if checked_location in player_locations: | 
					
						
							|  |  |  |                 area_name = location_to_area.get(checked_location, None) | 
					
						
							|  |  |  |                 if area_name: | 
					
						
							|  |  |  |                     checks_done[area_name] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 checks_done["Total"] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for received_item in tracker_data.get_player_received_items(team, player): | 
					
						
							|  |  |  |             target_item = links.get(received_item.item, received_item.item) | 
					
						
							|  |  |  |             if received_item.item in levels:  # non-progressive | 
					
						
							|  |  |  |                 inventory[target_item] = max(inventory[target_item], levels[received_item.item]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 inventory[target_item] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for location, (item_id, _, _) in player_locations.items(): | 
					
						
							|  |  |  |             if item_id in ids_big_key: | 
					
						
							|  |  |  |                 player_big_key_locations.add(ids_big_key[item_id]) | 
					
						
							|  |  |  |             elif item_id in ids_small_key: | 
					
						
							|  |  |  |                 player_small_key_locations.add(ids_small_key[item_id]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note the presence of the triforce item | 
					
						
							|  |  |  |         if tracker_data.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL: | 
					
						
							|  |  |  |             inventory[106] = 1  # Triforce | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Progressive items need special handling for icons and class | 
					
						
							|  |  |  |         progressive_items = { | 
					
						
							|  |  |  |             "Progressive Sword":  94, | 
					
						
							|  |  |  |             "Progressive Glove":  97, | 
					
						
							|  |  |  |             "Progressive Bow":    100, | 
					
						
							|  |  |  |             "Progressive Mail":   96, | 
					
						
							|  |  |  |             "Progressive Shield": 95, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         progressive_names = { | 
					
						
							|  |  |  |             "Progressive Sword":  [None, "Fighter Sword", "Master Sword", "Tempered Sword", "Golden Sword"], | 
					
						
							|  |  |  |             "Progressive Glove":  [None, "Power Glove", "Titan Mitts"], | 
					
						
							|  |  |  |             "Progressive Bow":    [None, "Bow", "Silver Bow"], | 
					
						
							|  |  |  |             "Progressive Mail":   ["Green Mail", "Blue Mail", "Red Mail"], | 
					
						
							|  |  |  |             "Progressive Shield": [None, "Blue Shield", "Red Shield", "Mirror Shield"] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         # Determine which icon to use | 
					
						
							|  |  |  |         display_data = {} | 
					
						
							|  |  |  |         for item_name, item_id in progressive_items.items(): | 
					
						
							|  |  |  |             level = min(inventory[item_id], len(progressive_names[item_name]) - 1) | 
					
						
							|  |  |  |             display_name = progressive_names[item_name][level] | 
					
						
							|  |  |  |             acquired = True | 
					
						
							|  |  |  |             if not display_name: | 
					
						
							|  |  |  |                 acquired = False | 
					
						
							|  |  |  |                 display_name = progressive_names[item_name][level + 1] | 
					
						
							|  |  |  |             base_name = item_name.split(maxsplit=1)[1].lower() | 
					
						
							|  |  |  |             display_data[base_name + "_acquired"] = acquired | 
					
						
							|  |  |  |             display_data[base_name + "_icon"] = display_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # The single player tracker doesn't care about overworld, underworld, and total checks. Maybe it should? | 
					
						
							|  |  |  |         sp_areas = ordered_areas[2:15] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             template_name_or_list="tracker__ALinkToThePast.html", | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             acquired_items={tracker_data.item_id_to_name["A Link to the Past"][id] for id in inventory}, | 
					
						
							|  |  |  |             sp_areas=sp_areas, | 
					
						
							|  |  |  |             small_key_ids=small_key_ids, | 
					
						
							|  |  |  |             key_locations=player_small_key_locations, | 
					
						
							|  |  |  |             big_key_ids=big_key_ids, | 
					
						
							|  |  |  |             big_key_locations=player_big_key_locations, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _multiworld_trackers["A Link to the Past"] = render_ALinkToThePast_multiworld_tracker | 
					
						
							|  |  |  |     _player_trackers["A Link to the Past"] = render_ALinkToThePast_tracker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if "Minecraft" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_Minecraft_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Wooden Pickaxe":     "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d2/Wooden_Pickaxe_JE3_BE3.png", | 
					
						
							|  |  |  |             "Stone Pickaxe":      "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c4/Stone_Pickaxe_JE2_BE2.png", | 
					
						
							|  |  |  |             "Iron Pickaxe":       "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d1/Iron_Pickaxe_JE3_BE2.png", | 
					
						
							|  |  |  |             "Diamond Pickaxe":    "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e7/Diamond_Pickaxe_JE3_BE3.png", | 
					
						
							|  |  |  |             "Wooden Sword":       "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d5/Wooden_Sword_JE2_BE2.png", | 
					
						
							|  |  |  |             "Stone Sword":        "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b1/Stone_Sword_JE2_BE2.png", | 
					
						
							|  |  |  |             "Iron Sword":         "https://static.wikia.nocookie.net/minecraft_gamepedia/images/8/8e/Iron_Sword_JE2_BE2.png", | 
					
						
							|  |  |  |             "Diamond Sword":      "https://static.wikia.nocookie.net/minecraft_gamepedia/images/4/44/Diamond_Sword_JE3_BE3.png", | 
					
						
							|  |  |  |             "Leather Tunic":      "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b7/Leather_Tunic_JE4_BE2.png", | 
					
						
							|  |  |  |             "Iron Chestplate":    "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Iron_Chestplate_JE2_BE2.png", | 
					
						
							|  |  |  |             "Diamond Chestplate": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e0/Diamond_Chestplate_JE3_BE2.png", | 
					
						
							|  |  |  |             "Iron Ingot":         "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Iron_Ingot_JE3_BE2.png", | 
					
						
							|  |  |  |             "Block of Iron":      "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7e/Block_of_Iron_JE4_BE3.png", | 
					
						
							|  |  |  |             "Brewing Stand":      "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b3/Brewing_Stand_%28empty%29_JE10.png", | 
					
						
							|  |  |  |             "Ender Pearl":        "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/f6/Ender_Pearl_JE3_BE2.png", | 
					
						
							|  |  |  |             "Bucket":             "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Bucket_JE2_BE2.png", | 
					
						
							|  |  |  |             "Bow":                "https://static.wikia.nocookie.net/minecraft_gamepedia/images/a/ab/Bow_%28Pull_2%29_JE1_BE1.png", | 
					
						
							|  |  |  |             "Shield":             "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c6/Shield_JE2_BE1.png", | 
					
						
							|  |  |  |             "Red Bed":            "https://static.wikia.nocookie.net/minecraft_gamepedia/images/6/6a/Red_Bed_%28N%29.png", | 
					
						
							|  |  |  |             "Netherite Scrap":    "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/33/Netherite_Scrap_JE2_BE1.png", | 
					
						
							|  |  |  |             "Flint and Steel":    "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/94/Flint_and_Steel_JE4_BE2.png", | 
					
						
							|  |  |  |             "Enchanting Table":   "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Enchanting_Table.gif", | 
					
						
							|  |  |  |             "Fishing Rod":        "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7f/Fishing_Rod_JE2_BE2.png", | 
					
						
							|  |  |  |             "Campfire":           "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/91/Campfire_JE2_BE2.gif", | 
					
						
							|  |  |  |             "Water Bottle":       "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/75/Water_Bottle_JE2_BE2.png", | 
					
						
							|  |  |  |             "Spyglass":           "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c1/Spyglass_JE2_BE1.png", | 
					
						
							|  |  |  |             "Dragon Egg Shard":   "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/38/Dragon_Egg_JE4.png", | 
					
						
							|  |  |  |             "Lead":               "https://static.wikia.nocookie.net/minecraft_gamepedia/images/1/1f/Lead_JE2_BE2.png", | 
					
						
							|  |  |  |             "Saddle":             "https://i.imgur.com/2QtDyR0.png", | 
					
						
							|  |  |  |             "Channeling Book":    "https://i.imgur.com/J3WsYZw.png", | 
					
						
							|  |  |  |             "Silk Touch Book":    "https://i.imgur.com/iqERxHQ.png", | 
					
						
							|  |  |  |             "Piercing IV Book":   "https://i.imgur.com/OzJptGz.png", | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-04-04 03:18:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         minecraft_location_ids = { | 
					
						
							|  |  |  |             "Story":       [42073, 42023, 42027, 42039, 42002, 42009, 42010, 42070, | 
					
						
							|  |  |  |                             42041, 42049, 42004, 42031, 42025, 42029, 42051, 42077], | 
					
						
							|  |  |  |             "Nether":      [42017, 42044, 42069, 42058, 42034, 42060, 42066, 42076, 42064, 42071, 42021, | 
					
						
							|  |  |  |                             42062, 42008, 42061, 42033, 42011, 42006, 42019, 42000, 42040, 42001, 42015, 42104, 42014], | 
					
						
							|  |  |  |             "The End":     [42052, 42005, 42012, 42032, 42030, 42042, 42018, 42038, 42046], | 
					
						
							|  |  |  |             "Adventure":   [42047, 42050, 42096, 42097, 42098, 42059, 42055, 42072, 42003, 42109, 42035, 42016, 42020, | 
					
						
							|  |  |  |                             42048, 42054, 42068, 42043, 42106, 42074, 42075, 42024, 42026, 42037, 42045, 42056, 42105, | 
					
						
							|  |  |  |                             42099, 42103, 42110, 42100], | 
					
						
							|  |  |  |             "Husbandry":   [42065, 42067, 42078, 42022, 42113, 42107, 42007, 42079, 42013, 42028, 42036, 42108, 42111, | 
					
						
							|  |  |  |                             42112, | 
					
						
							|  |  |  |                             42057, 42063, 42053, 42102, 42101, 42092, 42093, 42094, 42095], | 
					
						
							|  |  |  |             "Archipelago": [42080, 42081, 42082, 42083, 42084, 42085, 42086, 42087, 42088, 42089, 42090, 42091], | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-03 23:04:00 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         display_data = {} | 
					
						
							| 
									
										
										
										
											2023-08-29 14:58:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         # Determine display for progressive items | 
					
						
							|  |  |  |         progressive_items = { | 
					
						
							|  |  |  |             "Progressive Tools":             45013, | 
					
						
							|  |  |  |             "Progressive Weapons":           45012, | 
					
						
							|  |  |  |             "Progressive Armor":             45014, | 
					
						
							|  |  |  |             "Progressive Resource Crafting": 45001 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         progressive_names = { | 
					
						
							|  |  |  |             "Progressive Tools":             ["Wooden Pickaxe", "Stone Pickaxe", "Iron Pickaxe", "Diamond Pickaxe"], | 
					
						
							|  |  |  |             "Progressive Weapons":           ["Wooden Sword", "Stone Sword", "Iron Sword", "Diamond Sword"], | 
					
						
							|  |  |  |             "Progressive Armor":             ["Leather Tunic", "Iron Chestplate", "Diamond Chestplate"], | 
					
						
							|  |  |  |             "Progressive Resource Crafting": ["Iron Ingot", "Iron Ingot", "Block of Iron"] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  |         for item_name, item_id in progressive_items.items(): | 
					
						
							|  |  |  |             level = min(inventory[item_id], len(progressive_names[item_name]) - 1) | 
					
						
							|  |  |  |             display_name = progressive_names[item_name][level] | 
					
						
							|  |  |  |             base_name = item_name.split(maxsplit=1)[1].lower().replace(" ", "_") | 
					
						
							|  |  |  |             display_data[base_name + "_url"] = icons[display_name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Multi-items | 
					
						
							|  |  |  |         multi_items = { | 
					
						
							|  |  |  |             "3 Ender Pearls":    45029, | 
					
						
							|  |  |  |             "8 Netherite Scrap": 45015, | 
					
						
							|  |  |  |             "Dragon Egg Shard":  45043 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_id in multi_items.items(): | 
					
						
							|  |  |  |             base_name = item_name.split()[-1].lower() | 
					
						
							|  |  |  |             count = inventory[item_id] | 
					
						
							|  |  |  |             if count >= 0: | 
					
						
							|  |  |  |                 display_data[base_name + "_count"] = count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Turn location IDs into advancement tab counts | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player) | 
					
						
							|  |  |  |         lookup_name = lambda id: tracker_data.location_id_to_name["Minecraft"][id] | 
					
						
							|  |  |  |         location_info = {tab_name: {lookup_name(id): (id in checked_locations) for id in tab_locations} | 
					
						
							|  |  |  |                          for tab_name, tab_locations in minecraft_location_ids.items()} | 
					
						
							|  |  |  |         checks_done = {tab_name: len([id for id in tab_locations if id in checked_locations]) | 
					
						
							|  |  |  |                        for tab_name, tab_locations in minecraft_location_ids.items()} | 
					
						
							|  |  |  |         checks_done["Total"] = len(checked_locations) | 
					
						
							|  |  |  |         checks_in_area = {tab_name: len(tab_locations) for tab_name, tab_locations in minecraft_location_ids.items()} | 
					
						
							|  |  |  |         checks_in_area["Total"] = sum(checks_in_area.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["Minecraft"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__Minecraft.html", | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             saving_second=tracker_data.get_room_saving_second(), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             location_info=location_info, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							| 
									
										
										
										
											2023-10-31 14:20:07 -07:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _player_trackers["Minecraft"] = render_Minecraft_tracker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if "Ocarina of Time" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_OcarinaOfTime_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Fairy Ocarina":          "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/97/OoT_Fairy_Ocarina_Icon.png", | 
					
						
							|  |  |  |             "Ocarina of Time":        "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/4/4e/OoT_Ocarina_of_Time_Icon.png", | 
					
						
							|  |  |  |             "Slingshot":              "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/32/OoT_Fairy_Slingshot_Icon.png", | 
					
						
							|  |  |  |             "Boomerang":              "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/d/d5/OoT_Boomerang_Icon.png", | 
					
						
							|  |  |  |             "Bottle":                 "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/f/fc/OoT_Bottle_Icon.png", | 
					
						
							|  |  |  |             "Rutos Letter":           "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/OoT_Letter_Icon.png", | 
					
						
							|  |  |  |             "Bombs":                  "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/1/11/OoT_Bomb_Icon.png", | 
					
						
							|  |  |  |             "Bombchus":               "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/36/OoT_Bombchu_Icon.png", | 
					
						
							|  |  |  |             "Lens of Truth":          "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/0/05/OoT_Lens_of_Truth_Icon.png", | 
					
						
							|  |  |  |             "Bow":                    "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/9a/OoT_Fairy_Bow_Icon.png", | 
					
						
							|  |  |  |             "Hookshot":               "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/7/77/OoT_Hookshot_Icon.png", | 
					
						
							|  |  |  |             "Longshot":               "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/a/a4/OoT_Longshot_Icon.png", | 
					
						
							|  |  |  |             "Megaton Hammer":         "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/93/OoT_Megaton_Hammer_Icon.png", | 
					
						
							|  |  |  |             "Fire Arrows":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/1/1e/OoT_Fire_Arrow_Icon.png", | 
					
						
							|  |  |  |             "Ice Arrows":             "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/3c/OoT_Ice_Arrow_Icon.png", | 
					
						
							|  |  |  |             "Light Arrows":           "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/7/76/OoT_Light_Arrow_Icon.png", | 
					
						
							|  |  |  |             "Dins Fire":              r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/d/da/OoT_Din%27s_Fire_Icon.png", | 
					
						
							|  |  |  |             "Farores Wind":           r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/7/7a/OoT_Farore%27s_Wind_Icon.png", | 
					
						
							|  |  |  |             "Nayrus Love":            r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/b/be/OoT_Nayru%27s_Love_Icon.png", | 
					
						
							|  |  |  |             "Kokiri Sword":           "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/5/53/OoT_Kokiri_Sword_Icon.png", | 
					
						
							|  |  |  |             "Biggoron Sword":         r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/2e/OoT_Giant%27s_Knife_Icon.png", | 
					
						
							|  |  |  |             "Mirror Shield":          "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/b/b0/OoT_Mirror_Shield_Icon_2.png", | 
					
						
							|  |  |  |             "Goron Bracelet":         r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/b/b7/OoT_Goron%27s_Bracelet_Icon.png", | 
					
						
							|  |  |  |             "Silver Gauntlets":       "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/b/b9/OoT_Silver_Gauntlets_Icon.png", | 
					
						
							|  |  |  |             "Golden Gauntlets":       "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/6/6a/OoT_Golden_Gauntlets_Icon.png", | 
					
						
							|  |  |  |             "Goron Tunic":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/1/1c/OoT_Goron_Tunic_Icon.png", | 
					
						
							|  |  |  |             "Zora Tunic":             "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/2c/OoT_Zora_Tunic_Icon.png", | 
					
						
							|  |  |  |             "Silver Scale":           "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/4/4e/OoT_Silver_Scale_Icon.png", | 
					
						
							|  |  |  |             "Gold Scale":             "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/95/OoT_Golden_Scale_Icon.png", | 
					
						
							|  |  |  |             "Iron Boots":             "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/34/OoT_Iron_Boots_Icon.png", | 
					
						
							|  |  |  |             "Hover Boots":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/22/OoT_Hover_Boots_Icon.png", | 
					
						
							|  |  |  |             "Adults Wallet":          r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/f/f9/OoT_Adult%27s_Wallet_Icon.png", | 
					
						
							|  |  |  |             "Giants Wallet":          r"https://static.wikia.nocookie.net/zelda_gamepedia_en/images/8/87/OoT_Giant%27s_Wallet_Icon.png", | 
					
						
							|  |  |  |             "Small Magic":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/9f/OoT3D_Magic_Jar_Icon.png", | 
					
						
							|  |  |  |             "Large Magic":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/3e/OoT3D_Large_Magic_Jar_Icon.png", | 
					
						
							|  |  |  |             "Gerudo Membership Card": "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/4/4e/OoT_Gerudo_Token_Icon.png", | 
					
						
							|  |  |  |             "Gold Skulltula Token":   "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/4/47/OoT_Token_Icon.png", | 
					
						
							|  |  |  |             "Triforce Piece":         "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/0/0b/SS_Triforce_Piece_Icon.png", | 
					
						
							|  |  |  |             "Triforce":               "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/6/68/ALttP_Triforce_Title_Sprite.png", | 
					
						
							|  |  |  |             "Zeldas Lullaby":         "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Eponas Song":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Sarias Song":            "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Suns Song":              "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Song of Time":           "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Song of Storms":         "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/2/21/Grey_Note.png", | 
					
						
							|  |  |  |             "Minuet of Forest":       "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/e/e4/Green_Note.png", | 
					
						
							|  |  |  |             "Bolero of Fire":         "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/f/f0/Red_Note.png", | 
					
						
							|  |  |  |             "Serenade of Water":      "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/0/0f/Blue_Note.png", | 
					
						
							|  |  |  |             "Requiem of Spirit":      "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/a/a4/Orange_Note.png", | 
					
						
							|  |  |  |             "Nocturne of Shadow":     "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/97/Purple_Note.png", | 
					
						
							|  |  |  |             "Prelude of Light":       "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/90/Yellow_Note.png", | 
					
						
							|  |  |  |             "Small Key":              "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/e/e5/OoT_Small_Key_Icon.png", | 
					
						
							|  |  |  |             "Boss Key":               "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/4/40/OoT_Boss_Key_Icon.png", | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         display_data = {} | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         # Determine display for progressive items | 
					
						
							|  |  |  |         progressive_items = { | 
					
						
							|  |  |  |             "Progressive Hookshot":         66128, | 
					
						
							|  |  |  |             "Progressive Strength Upgrade": 66129, | 
					
						
							|  |  |  |             "Progressive Wallet":           66133, | 
					
						
							|  |  |  |             "Progressive Scale":            66134, | 
					
						
							|  |  |  |             "Magic Meter":                  66138, | 
					
						
							|  |  |  |             "Ocarina":                      66139, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         progressive_names = { | 
					
						
							|  |  |  |             "Progressive Hookshot":         ["Hookshot", "Hookshot", "Longshot"], | 
					
						
							|  |  |  |             "Progressive Strength Upgrade": ["Goron Bracelet", "Goron Bracelet", "Silver Gauntlets", | 
					
						
							|  |  |  |                                              "Golden Gauntlets"], | 
					
						
							|  |  |  |             "Progressive Wallet":           ["Adults Wallet", "Adults Wallet", "Giants Wallet", "Giants Wallet"], | 
					
						
							|  |  |  |             "Progressive Scale":            ["Silver Scale", "Silver Scale", "Gold Scale"], | 
					
						
							|  |  |  |             "Magic Meter":                  ["Small Magic", "Small Magic", "Large Magic"], | 
					
						
							|  |  |  |             "Ocarina":                      ["Fairy Ocarina", "Fairy Ocarina", "Ocarina of Time"] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-10 00:33:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  |         for item_name, item_id in progressive_items.items(): | 
					
						
							|  |  |  |             level = min(inventory[item_id], len(progressive_names[item_name]) - 1) | 
					
						
							|  |  |  |             display_name = progressive_names[item_name][level] | 
					
						
							|  |  |  |             if item_name.startswith("Progressive"): | 
					
						
							|  |  |  |                 base_name = item_name.split(maxsplit=1)[1].lower().replace(" ", "_") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 base_name = item_name.lower().replace(" ", "_") | 
					
						
							|  |  |  |             display_data[base_name + "_url"] = icons[display_name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if base_name == "hookshot": | 
					
						
							|  |  |  |                 display_data["hookshot_length"] = {0: "", 1: "H", 2: "L"}.get(level) | 
					
						
							|  |  |  |             if base_name == "wallet": | 
					
						
							|  |  |  |                 display_data["wallet_size"] = {0: "99", 1: "200", 2: "500", 3: "999"}.get(level) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Determine display for bottles. Show letter if it's obtained, determine bottle count | 
					
						
							|  |  |  |         bottle_ids = [66015, 66020, 66021, 66140, 66141, 66142, 66143, 66144, 66145, 66146, 66147, 66148] | 
					
						
							|  |  |  |         display_data["bottle_count"] = min(sum(map(lambda item_id: inventory[item_id], bottle_ids)), 4) | 
					
						
							|  |  |  |         display_data["bottle_url"] = icons["Rutos Letter"] if inventory[66021] > 0 else icons["Bottle"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Determine bombchu display | 
					
						
							|  |  |  |         display_data["has_bombchus"] = any(map(lambda item_id: inventory[item_id] > 0, [66003, 66106, 66107, 66137])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Multi-items | 
					
						
							|  |  |  |         multi_items = { | 
					
						
							|  |  |  |             "Gold Skulltula Token": 66091, | 
					
						
							|  |  |  |             "Triforce Piece":       66202, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_id in multi_items.items(): | 
					
						
							|  |  |  |             base_name = item_name.split()[-1].lower() | 
					
						
							|  |  |  |             display_data[base_name + "_count"] = inventory[item_id] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Gather dungeon locations | 
					
						
							|  |  |  |         area_id_ranges = { | 
					
						
							|  |  |  |             "Overworld":              ((67000, 67263), (67269, 67280), (67747, 68024), (68054, 68062)), | 
					
						
							|  |  |  |             "Deku Tree":              ((67281, 67303), (68063, 68077)), | 
					
						
							|  |  |  |             "Dodongo's Cavern":       ((67304, 67334), (68078, 68160)), | 
					
						
							|  |  |  |             "Jabu Jabu's Belly":      ((67335, 67359), (68161, 68188)), | 
					
						
							|  |  |  |             "Bottom of the Well":     ((67360, 67384), (68189, 68230)), | 
					
						
							|  |  |  |             "Forest Temple":          ((67385, 67420), (68231, 68281)), | 
					
						
							|  |  |  |             "Fire Temple":            ((67421, 67457), (68282, 68350)), | 
					
						
							|  |  |  |             "Water Temple":           ((67458, 67484), (68351, 68483)), | 
					
						
							|  |  |  |             "Shadow Temple":          ((67485, 67532), (68484, 68565)), | 
					
						
							|  |  |  |             "Spirit Temple":          ((67533, 67582), (68566, 68625)), | 
					
						
							|  |  |  |             "Ice Cavern":             ((67583, 67596), (68626, 68649)), | 
					
						
							|  |  |  |             "Gerudo Training Ground": ((67597, 67635), (68650, 68656)), | 
					
						
							|  |  |  |             "Thieves' Hideout":       ((67264, 67268), (68025, 68053)), | 
					
						
							|  |  |  |             "Ganon's Castle":         ((67636, 67673), (68657, 68705)), | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-10 00:33:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         def lookup_and_trim(id, area): | 
					
						
							|  |  |  |             full_name = tracker_data.location_id_to_name["Ocarina of Time"][id] | 
					
						
							|  |  |  |             if "Ganons Tower" in full_name: | 
					
						
							|  |  |  |                 return full_name | 
					
						
							|  |  |  |             if area not in ["Overworld", "Thieves' Hideout"]: | 
					
						
							|  |  |  |                 # trim dungeon name. leaves an extra space that doesn't display, or trims fully for DC/Jabu/GC | 
					
						
							|  |  |  |                 return full_name[len(area):] | 
					
						
							|  |  |  |             return full_name | 
					
						
							| 
									
										
										
										
											2023-09-10 00:33:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         locations = tracker_data.get_player_locations(team, player) | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player).intersection(set(locations)) | 
					
						
							|  |  |  |         location_info = {} | 
					
						
							|  |  |  |         checks_done = {} | 
					
						
							|  |  |  |         checks_in_area = {} | 
					
						
							|  |  |  |         for area, ranges in area_id_ranges.items(): | 
					
						
							|  |  |  |             location_info[area] = {} | 
					
						
							|  |  |  |             checks_done[area] = 0 | 
					
						
							|  |  |  |             checks_in_area[area] = 0 | 
					
						
							|  |  |  |             for r in ranges: | 
					
						
							|  |  |  |                 min_id, max_id = r | 
					
						
							|  |  |  |                 for id in range(min_id, max_id + 1): | 
					
						
							|  |  |  |                     if id in locations: | 
					
						
							|  |  |  |                         checked = id in checked_locations | 
					
						
							|  |  |  |                         location_info[area][lookup_and_trim(id, area)] = checked | 
					
						
							|  |  |  |                         checks_in_area[area] += 1 | 
					
						
							|  |  |  |                         checks_done[area] += checked | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         checks_done["Total"] = sum(checks_done.values()) | 
					
						
							|  |  |  |         checks_in_area["Total"] = sum(checks_in_area.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Give skulltulas on non-tracked locations | 
					
						
							|  |  |  |         non_tracked_locations = tracker_data.get_player_checked_locations(team, player).difference(set(locations)) | 
					
						
							|  |  |  |         for id in non_tracked_locations: | 
					
						
							|  |  |  |             if "GS" in lookup_and_trim(id, ""): | 
					
						
							|  |  |  |                 display_data["token_count"] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         oot_y = "✔" | 
					
						
							|  |  |  |         oot_x = "✕" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Gather small and boss key info | 
					
						
							|  |  |  |         small_key_counts = { | 
					
						
							|  |  |  |             "Forest Temple":          oot_y if inventory[66203] else inventory[66175], | 
					
						
							|  |  |  |             "Fire Temple":            oot_y if inventory[66204] else inventory[66176], | 
					
						
							|  |  |  |             "Water Temple":           oot_y if inventory[66205] else inventory[66177], | 
					
						
							|  |  |  |             "Spirit Temple":          oot_y if inventory[66206] else inventory[66178], | 
					
						
							|  |  |  |             "Shadow Temple":          oot_y if inventory[66207] else inventory[66179], | 
					
						
							|  |  |  |             "Bottom of the Well":     oot_y if inventory[66208] else inventory[66180], | 
					
						
							|  |  |  |             "Gerudo Training Ground": oot_y if inventory[66209] else inventory[66181], | 
					
						
							|  |  |  |             "Thieves' Hideout":       oot_y if inventory[66210] else inventory[66182], | 
					
						
							|  |  |  |             "Ganon's Castle":         oot_y if inventory[66211] else inventory[66183], | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         boss_key_counts = { | 
					
						
							|  |  |  |             "Forest Temple":  oot_y if inventory[66149] else oot_x, | 
					
						
							|  |  |  |             "Fire Temple":    oot_y if inventory[66150] else oot_x, | 
					
						
							|  |  |  |             "Water Temple":   oot_y if inventory[66151] else oot_x, | 
					
						
							|  |  |  |             "Spirit Temple":  oot_y if inventory[66152] else oot_x, | 
					
						
							|  |  |  |             "Shadow Temple":  oot_y if inventory[66153] else oot_x, | 
					
						
							|  |  |  |             "Ganon's Castle": oot_y if inventory[66154] else oot_x, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["Ocarina of Time"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__OcarinaOfTime.html", | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info, | 
					
						
							|  |  |  |             small_key_counts=small_key_counts, | 
					
						
							|  |  |  |             boss_key_counts=boss_key_counts, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _player_trackers["Ocarina of Time"] = render_OcarinaOfTime_tracker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if "Timespinner" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_Timespinner_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Timespinner Wheel":   "https://timespinnerwiki.com/mediawiki/images/7/76/Timespinner_Wheel.png", | 
					
						
							|  |  |  |             "Timespinner Spindle": "https://timespinnerwiki.com/mediawiki/images/1/1a/Timespinner_Spindle.png", | 
					
						
							|  |  |  |             "Timespinner Gear 1":  "https://timespinnerwiki.com/mediawiki/images/3/3c/Timespinner_Gear_1.png", | 
					
						
							|  |  |  |             "Timespinner Gear 2":  "https://timespinnerwiki.com/mediawiki/images/e/e9/Timespinner_Gear_2.png", | 
					
						
							|  |  |  |             "Timespinner Gear 3":  "https://timespinnerwiki.com/mediawiki/images/2/22/Timespinner_Gear_3.png", | 
					
						
							|  |  |  |             "Talaria Attachment":  "https://timespinnerwiki.com/mediawiki/images/6/61/Talaria_Attachment.png", | 
					
						
							|  |  |  |             "Succubus Hairpin":    "https://timespinnerwiki.com/mediawiki/images/4/49/Succubus_Hairpin.png", | 
					
						
							|  |  |  |             "Lightwall":           "https://timespinnerwiki.com/mediawiki/images/0/03/Lightwall.png", | 
					
						
							|  |  |  |             "Celestial Sash":      "https://timespinnerwiki.com/mediawiki/images/f/f1/Celestial_Sash.png", | 
					
						
							|  |  |  |             "Twin Pyramid Key":    "https://timespinnerwiki.com/mediawiki/images/4/49/Twin_Pyramid_Key.png", | 
					
						
							|  |  |  |             "Security Keycard D":  "https://timespinnerwiki.com/mediawiki/images/1/1b/Security_Keycard_D.png", | 
					
						
							|  |  |  |             "Security Keycard C":  "https://timespinnerwiki.com/mediawiki/images/e/e5/Security_Keycard_C.png", | 
					
						
							|  |  |  |             "Security Keycard B":  "https://timespinnerwiki.com/mediawiki/images/f/f6/Security_Keycard_B.png", | 
					
						
							|  |  |  |             "Security Keycard A":  "https://timespinnerwiki.com/mediawiki/images/b/b9/Security_Keycard_A.png", | 
					
						
							|  |  |  |             "Library Keycard V":   "https://timespinnerwiki.com/mediawiki/images/5/50/Library_Keycard_V.png", | 
					
						
							|  |  |  |             "Tablet":              "https://timespinnerwiki.com/mediawiki/images/a/a0/Tablet.png", | 
					
						
							|  |  |  |             "Elevator Keycard":    "https://timespinnerwiki.com/mediawiki/images/5/55/Elevator_Keycard.png", | 
					
						
							|  |  |  |             "Oculus Ring":         "https://timespinnerwiki.com/mediawiki/images/8/8d/Oculus_Ring.png", | 
					
						
							|  |  |  |             "Water Mask":          "https://timespinnerwiki.com/mediawiki/images/0/04/Water_Mask.png", | 
					
						
							|  |  |  |             "Gas Mask":            "https://timespinnerwiki.com/mediawiki/images/2/2e/Gas_Mask.png", | 
					
						
							|  |  |  |             "Djinn Inferno":       "https://timespinnerwiki.com/mediawiki/images/f/f6/Djinn_Inferno.png", | 
					
						
							|  |  |  |             "Pyro Ring":           "https://timespinnerwiki.com/mediawiki/images/2/2c/Pyro_Ring.png", | 
					
						
							|  |  |  |             "Infernal Flames":     "https://timespinnerwiki.com/mediawiki/images/1/1f/Infernal_Flames.png", | 
					
						
							|  |  |  |             "Fire Orb":            "https://timespinnerwiki.com/mediawiki/images/3/3e/Fire_Orb.png", | 
					
						
							|  |  |  |             "Royal Ring":          "https://timespinnerwiki.com/mediawiki/images/f/f3/Royal_Ring.png", | 
					
						
							|  |  |  |             "Plasma Geyser":       "https://timespinnerwiki.com/mediawiki/images/1/12/Plasma_Geyser.png", | 
					
						
							|  |  |  |             "Plasma Orb":          "https://timespinnerwiki.com/mediawiki/images/4/44/Plasma_Orb.png", | 
					
						
							|  |  |  |             "Kobo":                "https://timespinnerwiki.com/mediawiki/images/c/c6/Familiar_Kobo.png", | 
					
						
							|  |  |  |             "Merchant Crow":       "https://timespinnerwiki.com/mediawiki/images/4/4e/Familiar_Crow.png", | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         timespinner_location_ids = { | 
					
						
							|  |  |  |             "Present":         [ | 
					
						
							|  |  |  |                 1337000, 1337001, 1337002, 1337003, 1337004, 1337005, 1337006, 1337007, 1337008, 1337009, | 
					
						
							|  |  |  |                 1337010, 1337011, 1337012, 1337013, 1337014, 1337015, 1337016, 1337017, 1337018, 1337019, | 
					
						
							|  |  |  |                 1337020, 1337021, 1337022, 1337023, 1337024, 1337025, 1337026, 1337027, 1337028, 1337029, | 
					
						
							|  |  |  |                 1337030, 1337031, 1337032, 1337033, 1337034, 1337035, 1337036, 1337037, 1337038, 1337039, | 
					
						
							|  |  |  |                 1337040, 1337041, 1337042, 1337043, 1337044, 1337045, 1337046, 1337047, 1337048, 1337049, | 
					
						
							|  |  |  |                 1337050, 1337051, 1337052, 1337053, 1337054, 1337055, 1337056, 1337057, 1337058, 1337059, | 
					
						
							|  |  |  |                 1337060, 1337061, 1337062, 1337063, 1337064, 1337065, 1337066, 1337067, 1337068, 1337069, | 
					
						
							|  |  |  |                 1337070, 1337071, 1337072, 1337073, 1337074, 1337075, 1337076, 1337077, 1337078, 1337079, | 
					
						
							|  |  |  |                 1337080, 1337081, 1337082, 1337083, 1337084, 1337085], | 
					
						
							|  |  |  |             "Past":            [ | 
					
						
							|  |  |  |                 1337086, 1337087, 1337088, 1337089, | 
					
						
							|  |  |  |                 1337090, 1337091, 1337092, 1337093, 1337094, 1337095, 1337096, 1337097, 1337098, 1337099, | 
					
						
							|  |  |  |                 1337100, 1337101, 1337102, 1337103, 1337104, 1337105, 1337106, 1337107, 1337108, 1337109, | 
					
						
							|  |  |  |                 1337110, 1337111, 1337112, 1337113, 1337114, 1337115, 1337116, 1337117, 1337118, 1337119, | 
					
						
							|  |  |  |                 1337120, 1337121, 1337122, 1337123, 1337124, 1337125, 1337126, 1337127, 1337128, 1337129, | 
					
						
							|  |  |  |                 1337130, 1337131, 1337132, 1337133, 1337134, 1337135, 1337136, 1337137, 1337138, 1337139, | 
					
						
							|  |  |  |                 1337140, 1337141, 1337142, 1337143, 1337144, 1337145, 1337146, 1337147, 1337148, 1337149, | 
					
						
							|  |  |  |                 1337150, 1337151, 1337152, 1337153, 1337154, 1337155, | 
					
						
							|  |  |  |                 1337171, 1337172, 1337173, 1337174, 1337175], | 
					
						
							|  |  |  |             "Ancient Pyramid": [ | 
					
						
							|  |  |  |                 1337236, | 
					
						
							|  |  |  |                 1337246, 1337247, 1337248, 1337249] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         slot_data = tracker_data.get_slot_data(team, player) | 
					
						
							|  |  |  |         if (slot_data["DownloadableItems"]): | 
					
						
							|  |  |  |             timespinner_location_ids["Present"] += [ | 
					
						
							|  |  |  |                 1337156, 1337157, 1337159, | 
					
						
							|  |  |  |                 1337160, 1337161, 1337162, 1337163, 1337164, 1337165, 1337166, 1337167, 1337168, 1337169, | 
					
						
							|  |  |  |                 1337170] | 
					
						
							|  |  |  |         if (slot_data["Cantoran"]): | 
					
						
							|  |  |  |             timespinner_location_ids["Past"].append(1337176) | 
					
						
							|  |  |  |         if (slot_data["LoreChecks"]): | 
					
						
							|  |  |  |             timespinner_location_ids["Present"] += [ | 
					
						
							|  |  |  |                 1337177, 1337178, 1337179, | 
					
						
							|  |  |  |                 1337180, 1337181, 1337182, 1337183, 1337184, 1337185, 1337186, 1337187] | 
					
						
							|  |  |  |             timespinner_location_ids["Past"] += [ | 
					
						
							|  |  |  |                 1337188, 1337189, | 
					
						
							|  |  |  |                 1337190, 1337191, 1337192, 1337193, 1337194, 1337195, 1337196, 1337197, 1337198] | 
					
						
							|  |  |  |         if (slot_data["GyreArchives"]): | 
					
						
							|  |  |  |             timespinner_location_ids["Ancient Pyramid"] += [ | 
					
						
							|  |  |  |                 1337237, 1337238, 1337239, | 
					
						
							|  |  |  |                 1337240, 1337241, 1337242, 1337243, 1337244, 1337245] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         display_data = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Turn location IDs into advancement tab counts | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player) | 
					
						
							|  |  |  |         lookup_name = lambda id: tracker_data.location_id_to_name["Timespinner"][id] | 
					
						
							|  |  |  |         location_info = {tab_name: {lookup_name(id): (id in checked_locations) for id in tab_locations} | 
					
						
							|  |  |  |                          for tab_name, tab_locations in timespinner_location_ids.items()} | 
					
						
							|  |  |  |         checks_done = {tab_name: len([id for id in tab_locations if id in checked_locations]) | 
					
						
							|  |  |  |                        for tab_name, tab_locations in timespinner_location_ids.items()} | 
					
						
							|  |  |  |         checks_done["Total"] = len(checked_locations) | 
					
						
							|  |  |  |         checks_in_area = {tab_name: len(tab_locations) for tab_name, tab_locations in timespinner_location_ids.items()} | 
					
						
							|  |  |  |         checks_in_area["Total"] = sum(checks_in_area.values()) | 
					
						
							|  |  |  |         options = {k for k, v in slot_data.items() if v} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["Timespinner"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__Timespinner.html", | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             location_info=location_info, | 
					
						
							|  |  |  |             options=options, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _player_trackers["Timespinner"] = render_Timespinner_tracker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if "Super Metroid" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_SuperMetroid_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Energy Tank":    "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/ETank.png", | 
					
						
							|  |  |  |             "Missile":        "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Missile.png", | 
					
						
							|  |  |  |             "Super Missile":  "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Super.png", | 
					
						
							|  |  |  |             "Power Bomb":     "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/PowerBomb.png", | 
					
						
							|  |  |  |             "Bomb":           "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Bomb.png", | 
					
						
							|  |  |  |             "Charge Beam":    "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Charge.png", | 
					
						
							|  |  |  |             "Ice Beam":       "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Ice.png", | 
					
						
							|  |  |  |             "Hi-Jump Boots":  "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/HiJump.png", | 
					
						
							|  |  |  |             "Speed Booster":  "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/SpeedBooster.png", | 
					
						
							|  |  |  |             "Wave Beam":      "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Wave.png", | 
					
						
							|  |  |  |             "Spazer":         "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Spazer.png", | 
					
						
							|  |  |  |             "Spring Ball":    "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/SpringBall.png", | 
					
						
							|  |  |  |             "Varia Suit":     "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Varia.png", | 
					
						
							|  |  |  |             "Plasma Beam":    "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Plasma.png", | 
					
						
							|  |  |  |             "Grappling Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Grapple.png", | 
					
						
							|  |  |  |             "Morph Ball":     "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Morph.png", | 
					
						
							|  |  |  |             "Reserve Tank":   "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Reserve.png", | 
					
						
							|  |  |  |             "Gravity Suit":   "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/Gravity.png", | 
					
						
							|  |  |  |             "X-Ray Scope":    "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/XRayScope.png", | 
					
						
							|  |  |  |             "Space Jump":     "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/SpaceJump.png", | 
					
						
							|  |  |  |             "Screw Attack":   "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/ScrewAttack.png", | 
					
						
							|  |  |  |             "Nothing":        "", | 
					
						
							|  |  |  |             "No Energy":      "", | 
					
						
							|  |  |  |             "Kraid":          "", | 
					
						
							|  |  |  |             "Phantoon":       "", | 
					
						
							|  |  |  |             "Draygon":        "", | 
					
						
							|  |  |  |             "Ridley":         "", | 
					
						
							|  |  |  |             "Mother Brain":   "", | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         multi_items = { | 
					
						
							|  |  |  |             "Energy Tank":   83000, | 
					
						
							|  |  |  |             "Missile":       83001, | 
					
						
							|  |  |  |             "Super Missile": 83002, | 
					
						
							|  |  |  |             "Power Bomb":    83003, | 
					
						
							|  |  |  |             "Reserve Tank":  83020, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:38:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         supermetroid_location_ids = { | 
					
						
							|  |  |  |             'Crateria/Blue Brinstar': [82005, 82007, 82008, 82026, 82029, | 
					
						
							|  |  |  |                                        82000, 82004, 82006, 82009, 82010, | 
					
						
							|  |  |  |                                        82011, 82012, 82027, 82028, 82034, | 
					
						
							|  |  |  |                                        82036, 82037], | 
					
						
							|  |  |  |             'Green/Pink Brinstar':    [82017, 82023, 82030, 82033, 82035, | 
					
						
							|  |  |  |                                        82013, 82014, 82015, 82016, 82018, | 
					
						
							|  |  |  |                                        82019, 82021, 82022, 82024, 82025, | 
					
						
							|  |  |  |                                        82031], | 
					
						
							|  |  |  |             'Red Brinstar':           [82038, 82042, 82039, 82040, 82041], | 
					
						
							|  |  |  |             'Kraid':                  [82043, 82048, 82044], | 
					
						
							|  |  |  |             'Norfair':                [82050, 82053, 82061, 82066, 82068, | 
					
						
							|  |  |  |                                        82049, 82051, 82054, 82055, 82056, | 
					
						
							|  |  |  |                                        82062, 82063, 82064, 82065, 82067], | 
					
						
							|  |  |  |             'Lower Norfair':          [82078, 82079, 82080, 82070, 82071, | 
					
						
							|  |  |  |                                        82073, 82074, 82075, 82076, 82077], | 
					
						
							|  |  |  |             'Crocomire':              [82052, 82060, 82057, 82058, 82059], | 
					
						
							|  |  |  |             'Wrecked Ship':           [82129, 82132, 82134, 82135, 82001, | 
					
						
							|  |  |  |                                        82002, 82003, 82128, 82130, 82131, | 
					
						
							|  |  |  |                                        82133], | 
					
						
							|  |  |  |             'West Maridia':           [82138, 82136, 82137, 82139, 82140, | 
					
						
							|  |  |  |                                        82141, 82142], | 
					
						
							|  |  |  |             'East Maridia':           [82143, 82145, 82150, 82152, 82154, | 
					
						
							|  |  |  |                                        82144, 82146, 82147, 82148, 82149, | 
					
						
							|  |  |  |                                        82151], | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         display_data = {} | 
					
						
							|  |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for item_name, item_id in multi_items.items(): | 
					
						
							|  |  |  |             base_name = item_name.split()[0].lower() | 
					
						
							|  |  |  |             display_data[base_name + "_count"] = inventory[item_id] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Turn location IDs into advancement tab counts | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player) | 
					
						
							|  |  |  |         lookup_name = lambda id: tracker_data.location_id_to_name["Super Metroid"][id] | 
					
						
							|  |  |  |         location_info = {tab_name: {lookup_name(id): (id in checked_locations) for id in tab_locations} | 
					
						
							|  |  |  |                          for tab_name, tab_locations in supermetroid_location_ids.items()} | 
					
						
							|  |  |  |         checks_done = {tab_name: len([id for id in tab_locations if id in checked_locations]) | 
					
						
							|  |  |  |                        for tab_name, tab_locations in supermetroid_location_ids.items()} | 
					
						
							|  |  |  |         checks_done['Total'] = len(checked_locations) | 
					
						
							|  |  |  |         checks_in_area = {tab_name: len(tab_locations) for tab_name, tab_locations in supermetroid_location_ids.items()} | 
					
						
							|  |  |  |         checks_in_area['Total'] = sum(checks_in_area.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["Super Metroid"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__SuperMetroid.html", | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             location_info=location_info, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _player_trackers["Super Metroid"] = render_SuperMetroid_tracker | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  | if "ChecksFinder" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_ChecksFinder_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Checks Available": "https://0rganics.org/archipelago/cf/spr_tiles_3.png", | 
					
						
							|  |  |  |             "Map Width":        "https://0rganics.org/archipelago/cf/spr_tiles_4.png", | 
					
						
							|  |  |  |             "Map Height":       "https://0rganics.org/archipelago/cf/spr_tiles_5.png", | 
					
						
							|  |  |  |             "Map Bombs":        "https://0rganics.org/archipelago/cf/spr_tiles_6.png", | 
					
						
							| 
									
										
										
										
											2023-03-08 22:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |             "Nothing":          "", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         checksfinder_location_ids = { | 
					
						
							|  |  |  |             "Tile 1":  81000, | 
					
						
							|  |  |  |             "Tile 2":  81001, | 
					
						
							|  |  |  |             "Tile 3":  81002, | 
					
						
							|  |  |  |             "Tile 4":  81003, | 
					
						
							|  |  |  |             "Tile 5":  81004, | 
					
						
							|  |  |  |             "Tile 6":  81005, | 
					
						
							|  |  |  |             "Tile 7":  81006, | 
					
						
							|  |  |  |             "Tile 8":  81007, | 
					
						
							|  |  |  |             "Tile 9":  81008, | 
					
						
							|  |  |  |             "Tile 10": 81009, | 
					
						
							|  |  |  |             "Tile 11": 81010, | 
					
						
							|  |  |  |             "Tile 12": 81011, | 
					
						
							|  |  |  |             "Tile 13": 81012, | 
					
						
							|  |  |  |             "Tile 14": 81013, | 
					
						
							|  |  |  |             "Tile 15": 81014, | 
					
						
							|  |  |  |             "Tile 16": 81015, | 
					
						
							|  |  |  |             "Tile 17": 81016, | 
					
						
							|  |  |  |             "Tile 18": 81017, | 
					
						
							|  |  |  |             "Tile 19": 81018, | 
					
						
							|  |  |  |             "Tile 20": 81019, | 
					
						
							|  |  |  |             "Tile 21": 81020, | 
					
						
							|  |  |  |             "Tile 22": 81021, | 
					
						
							|  |  |  |             "Tile 23": 81022, | 
					
						
							|  |  |  |             "Tile 24": 81023, | 
					
						
							|  |  |  |             "Tile 25": 81024, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         display_data = {} | 
					
						
							|  |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  |         locations = tracker_data.get_player_locations(team, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Multi-items | 
					
						
							|  |  |  |         multi_items = { | 
					
						
							|  |  |  |             "Map Width":  80000, | 
					
						
							|  |  |  |             "Map Height": 80001, | 
					
						
							|  |  |  |             "Map Bombs":  80002 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_id in multi_items.items(): | 
					
						
							|  |  |  |             base_name = item_name.split()[-1].lower() | 
					
						
							|  |  |  |             count = inventory[item_id] | 
					
						
							|  |  |  |             display_data[base_name + "_count"] = count | 
					
						
							|  |  |  |             display_data[base_name + "_display"] = count + 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Get location info | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player) | 
					
						
							|  |  |  |         lookup_name = lambda id: tracker_data.location_id_to_name["ChecksFinder"][id] | 
					
						
							|  |  |  |         location_info = {tile_name: {lookup_name(tile_location): (tile_location in checked_locations)} for | 
					
						
							|  |  |  |                          tile_name, tile_location in checksfinder_location_ids.items() if | 
					
						
							|  |  |  |                          tile_location in set(locations)} | 
					
						
							|  |  |  |         checks_done = {tile_name: len([tile_location]) for tile_name, tile_location in checksfinder_location_ids.items() | 
					
						
							|  |  |  |                        if tile_location in checked_locations and tile_location in set(locations)} | 
					
						
							|  |  |  |         checks_done['Total'] = len(checked_locations) | 
					
						
							|  |  |  |         checks_in_area = checks_done | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Calculate checks available | 
					
						
							|  |  |  |         display_data["checks_unlocked"] = min( | 
					
						
							|  |  |  |             display_data["width_count"] + display_data["height_count"] + display_data["bombs_count"] + 5, 25) | 
					
						
							|  |  |  |         display_data["checks_available"] = max(display_data["checks_unlocked"] - len(checked_locations), 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["ChecksFinder"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__ChecksFinder.html", | 
					
						
							|  |  |  |             inventory=inventory, icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             location_info=location_info, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							| 
									
										
										
										
											2023-10-31 14:20:07 -07:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |     _player_trackers["ChecksFinder"] = render_ChecksFinder_tracker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if "Starcraft 2 Wings of Liberty" in network_data_package["games"]: | 
					
						
							|  |  |  |     def render_Starcraft2WingsOfLiberty_tracker(tracker_data: TrackerData, team: int, player: int) -> str: | 
					
						
							|  |  |  |         SC2WOL_LOC_ID_OFFSET = 1000 | 
					
						
							|  |  |  |         SC2WOL_ITEM_ID_OFFSET = 1000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         icons = { | 
					
						
							|  |  |  |             "Starting Minerals":                           "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/icons/icon-mineral-protoss.png", | 
					
						
							|  |  |  |             "Starting Vespene":                            "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/icons/icon-gas-terran.png", | 
					
						
							|  |  |  |             "Starting Supply":                             "https://static.wikia.nocookie.net/starcraft/images/d/d3/TerranSupply_SC2_Icon1.gif", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Infantry Weapons Level 1":                    "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryweaponslevel1.png", | 
					
						
							|  |  |  |             "Infantry Weapons Level 2":                    "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryweaponslevel2.png", | 
					
						
							|  |  |  |             "Infantry Weapons Level 3":                    "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryweaponslevel3.png", | 
					
						
							|  |  |  |             "Infantry Armor Level 1":                      "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryarmorlevel1.png", | 
					
						
							|  |  |  |             "Infantry Armor Level 2":                      "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryarmorlevel2.png", | 
					
						
							|  |  |  |             "Infantry Armor Level 3":                      "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-infantryarmorlevel3.png", | 
					
						
							|  |  |  |             "Vehicle Weapons Level 1":                     "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleweaponslevel1.png", | 
					
						
							|  |  |  |             "Vehicle Weapons Level 2":                     "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleweaponslevel2.png", | 
					
						
							|  |  |  |             "Vehicle Weapons Level 3":                     "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleweaponslevel3.png", | 
					
						
							|  |  |  |             "Vehicle Armor Level 1":                       "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleplatinglevel1.png", | 
					
						
							|  |  |  |             "Vehicle Armor Level 2":                       "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleplatinglevel2.png", | 
					
						
							|  |  |  |             "Vehicle Armor Level 3":                       "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-vehicleplatinglevel3.png", | 
					
						
							|  |  |  |             "Ship Weapons Level 1":                        "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipweaponslevel1.png", | 
					
						
							|  |  |  |             "Ship Weapons Level 2":                        "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipweaponslevel2.png", | 
					
						
							|  |  |  |             "Ship Weapons Level 3":                        "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipweaponslevel3.png", | 
					
						
							|  |  |  |             "Ship Armor Level 1":                          "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipplatinglevel1.png", | 
					
						
							|  |  |  |             "Ship Armor Level 2":                          "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipplatinglevel2.png", | 
					
						
							|  |  |  |             "Ship Armor Level 3":                          "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/upgrades/btn-upgrade-terran-shipplatinglevel3.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Bunker":                                      "https://static.wikia.nocookie.net/starcraft/images/c/c5/Bunker_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Missile Turret":                              "https://static.wikia.nocookie.net/starcraft/images/5/5f/MissileTurret_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Sensor Tower":                                "https://static.wikia.nocookie.net/starcraft/images/d/d2/SensorTower_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Projectile Accelerator (Bunker)":             "https://0rganics.org/archipelago/sc2wol/ProjectileAccelerator.png", | 
					
						
							|  |  |  |             "Neosteel Bunker (Bunker)":                    "https://0rganics.org/archipelago/sc2wol/NeosteelBunker.png", | 
					
						
							|  |  |  |             "Titanium Housing (Missile Turret)":           "https://0rganics.org/archipelago/sc2wol/TitaniumHousing.png", | 
					
						
							|  |  |  |             "Hellstorm Batteries (Missile Turret)":        "https://0rganics.org/archipelago/sc2wol/HellstormBatteries.png", | 
					
						
							|  |  |  |             "Advanced Construction (SCV)":                 "https://0rganics.org/archipelago/sc2wol/AdvancedConstruction.png", | 
					
						
							|  |  |  |             "Dual-Fusion Welders (SCV)":                   "https://0rganics.org/archipelago/sc2wol/Dual-FusionWelders.png", | 
					
						
							|  |  |  |             "Fire-Suppression System (Building)":          "https://0rganics.org/archipelago/sc2wol/Fire-SuppressionSystem.png", | 
					
						
							|  |  |  |             "Orbital Command (Building)":                  "https://0rganics.org/archipelago/sc2wol/OrbitalCommandCampaign.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Marine":                                      "https://static.wikia.nocookie.net/starcraft/images/4/47/Marine_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Medic":                                       "https://static.wikia.nocookie.net/starcraft/images/7/74/Medic_SC2_Rend1.jpg", | 
					
						
							|  |  |  |             "Firebat":                                     "https://static.wikia.nocookie.net/starcraft/images/3/3c/Firebat_SC2_Rend1.jpg", | 
					
						
							|  |  |  |             "Marauder":                                    "https://static.wikia.nocookie.net/starcraft/images/b/ba/Marauder_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Reaper":                                      "https://static.wikia.nocookie.net/starcraft/images/7/7d/Reaper_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Stimpack (Marine)":                           "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", | 
					
						
							|  |  |  |             "Super Stimpack (Marine)":                     "/static/static/icons/sc2/superstimpack.png", | 
					
						
							|  |  |  |             "Combat Shield (Marine)":                      "https://0rganics.org/archipelago/sc2wol/CombatShieldCampaign.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Marine)":             "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Magrail Munitions (Marine)":                  "/static/static/icons/sc2/magrailmunitions.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Marine)":                "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Advanced Medic Facilities (Medic)":           "https://0rganics.org/archipelago/sc2wol/AdvancedMedicFacilities.png", | 
					
						
							|  |  |  |             "Stabilizer Medpacks (Medic)":                 "https://0rganics.org/archipelago/sc2wol/StabilizerMedpacks.png", | 
					
						
							|  |  |  |             "Restoration (Medic)":                         "/static/static/icons/sc2/restoration.png", | 
					
						
							|  |  |  |             "Optical Flare (Medic)":                       "/static/static/icons/sc2/opticalflare.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Medic)":                 "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Incinerator Gauntlets (Firebat)":             "https://0rganics.org/archipelago/sc2wol/IncineratorGauntlets.png", | 
					
						
							|  |  |  |             "Juggernaut Plating (Firebat)":                "https://0rganics.org/archipelago/sc2wol/JuggernautPlating.png", | 
					
						
							|  |  |  |             "Stimpack (Firebat)":                          "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", | 
					
						
							|  |  |  |             "Super Stimpack (Firebat)":                    "/static/static/icons/sc2/superstimpack.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Firebat)":               "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Concussive Shells (Marauder)":                "https://0rganics.org/archipelago/sc2wol/ConcussiveShellsCampaign.png", | 
					
						
							|  |  |  |             "Kinetic Foam (Marauder)":                     "https://0rganics.org/archipelago/sc2wol/KineticFoam.png", | 
					
						
							|  |  |  |             "Stimpack (Marauder)":                         "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", | 
					
						
							|  |  |  |             "Super Stimpack (Marauder)":                   "/static/static/icons/sc2/superstimpack.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Marauder)":           "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Magrail Munitions (Marauder)":                "/static/static/icons/sc2/magrailmunitions.png", | 
					
						
							|  |  |  |             "Internal Tech Module (Marauder)":             "/static/static/icons/sc2/internalizedtechmodule.png", | 
					
						
							|  |  |  |             "U-238 Rounds (Reaper)":                       "https://0rganics.org/archipelago/sc2wol/U-238Rounds.png", | 
					
						
							|  |  |  |             "G-4 Clusterbomb (Reaper)":                    "https://0rganics.org/archipelago/sc2wol/G-4Clusterbomb.png", | 
					
						
							|  |  |  |             "Stimpack (Reaper)":                           "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", | 
					
						
							|  |  |  |             "Super Stimpack (Reaper)":                     "/static/static/icons/sc2/superstimpack.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Reaper)":             "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Advanced Cloaking Field (Reaper)":            "/static/static/icons/sc2/terran-cloak-color.png", | 
					
						
							|  |  |  |             "Spider Mines (Reaper)":                       "/static/static/icons/sc2/spidermine.png", | 
					
						
							|  |  |  |             "Combat Drugs (Reaper)":                       "/static/static/icons/sc2/reapercombatdrugs.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Hellion":                                     "https://static.wikia.nocookie.net/starcraft/images/5/56/Hellion_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Vulture":                                     "https://static.wikia.nocookie.net/starcraft/images/d/da/Vulture_WoL.jpg", | 
					
						
							|  |  |  |             "Goliath":                                     "https://static.wikia.nocookie.net/starcraft/images/e/eb/Goliath_WoL.jpg", | 
					
						
							|  |  |  |             "Diamondback":                                 "https://static.wikia.nocookie.net/starcraft/images/a/a6/Diamondback_WoL.jpg", | 
					
						
							|  |  |  |             "Siege Tank":                                  "https://static.wikia.nocookie.net/starcraft/images/5/57/SiegeTank_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Twin-Linked Flamethrower (Hellion)":          "https://0rganics.org/archipelago/sc2wol/Twin-LinkedFlamethrower.png", | 
					
						
							|  |  |  |             "Thermite Filaments (Hellion)":                "https://0rganics.org/archipelago/sc2wol/ThermiteFilaments.png", | 
					
						
							|  |  |  |             "Hellbat Aspect (Hellion)":                    "/static/static/icons/sc2/hellionbattlemode.png", | 
					
						
							|  |  |  |             "Smart Servos (Hellion)":                      "/static/static/icons/sc2/transformationservos.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Hellion)":               "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Jump Jets (Hellion)":                         "/static/static/icons/sc2/jumpjets.png", | 
					
						
							|  |  |  |             "Stimpack (Hellion)":                          "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", | 
					
						
							|  |  |  |             "Super Stimpack (Hellion)":                    "/static/static/icons/sc2/superstimpack.png", | 
					
						
							|  |  |  |             "Cerberus Mine (Spider Mine)":                 "https://0rganics.org/archipelago/sc2wol/CerberusMine.png", | 
					
						
							|  |  |  |             "High Explosive Munition (Spider Mine)":       "/static/static/icons/sc2/high-explosive-spidermine.png", | 
					
						
							|  |  |  |             "Replenishable Magazine (Vulture)":            "https://0rganics.org/archipelago/sc2wol/ReplenishableMagazine.png", | 
					
						
							|  |  |  |             "Ion Thrusters (Vulture)":                     "/static/static/icons/sc2/emergencythrusters.png", | 
					
						
							|  |  |  |             "Auto Launchers (Vulture)":                    "/static/static/icons/sc2/jotunboosters.png", | 
					
						
							|  |  |  |             "Multi-Lock Weapons System (Goliath)":         "https://0rganics.org/archipelago/sc2wol/Multi-LockWeaponsSystem.png", | 
					
						
							|  |  |  |             "Ares-Class Targeting System (Goliath)":       "https://0rganics.org/archipelago/sc2wol/Ares-ClassTargetingSystem.png", | 
					
						
							|  |  |  |             "Jump Jets (Goliath)":                         "/static/static/icons/sc2/jumpjets.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Goliath)":               "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Tri-Lithium Power Cell (Diamondback)":        "https://0rganics.org/archipelago/sc2wol/Tri-LithiumPowerCell.png", | 
					
						
							|  |  |  |             "Shaped Hull (Diamondback)":                   "https://0rganics.org/archipelago/sc2wol/ShapedHull.png", | 
					
						
							|  |  |  |             "Hyperfluxor (Diamondback)":                   "/static/static/icons/sc2/hyperfluxor.png", | 
					
						
							|  |  |  |             "Burst Capacitors (Diamondback)":              "/static/static/icons/sc2/burstcapacitors.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Diamondback)":           "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Maelstrom Rounds (Siege Tank)":               "https://0rganics.org/archipelago/sc2wol/MaelstromRounds.png", | 
					
						
							|  |  |  |             "Shaped Blast (Siege Tank)":                   "https://0rganics.org/archipelago/sc2wol/ShapedBlast.png", | 
					
						
							|  |  |  |             "Jump Jets (Siege Tank)":                      "/static/static/icons/sc2/jumpjets.png", | 
					
						
							|  |  |  |             "Spider Mines (Siege Tank)":                   "/static/static/icons/sc2/siegetank-spidermines.png", | 
					
						
							|  |  |  |             "Smart Servos (Siege Tank)":                   "/static/static/icons/sc2/transformationservos.png", | 
					
						
							|  |  |  |             "Graduating Range (Siege Tank)":               "/static/static/icons/sc2/siegetankrange.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Siege Tank)":         "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Advanced Siege Tech (Siege Tank)":            "/static/static/icons/sc2/improvedsiegemode.png", | 
					
						
							|  |  |  |             "Internal Tech Module (Siege Tank)":           "/static/static/icons/sc2/internalizedtechmodule.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Medivac":                                     "https://static.wikia.nocookie.net/starcraft/images/d/db/Medivac_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Wraith":                                      "https://static.wikia.nocookie.net/starcraft/images/7/75/Wraith_WoL.jpg", | 
					
						
							|  |  |  |             "Viking":                                      "https://static.wikia.nocookie.net/starcraft/images/2/2a/Viking_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Banshee":                                     "https://static.wikia.nocookie.net/starcraft/images/3/32/Banshee_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Battlecruiser":                               "https://static.wikia.nocookie.net/starcraft/images/f/f5/Battlecruiser_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Rapid Deployment Tube (Medivac)":             "https://0rganics.org/archipelago/sc2wol/RapidDeploymentTube.png", | 
					
						
							|  |  |  |             "Advanced Healing AI (Medivac)":               "https://0rganics.org/archipelago/sc2wol/AdvancedHealingAI.png", | 
					
						
							|  |  |  |             "Expanded Hull (Medivac)":                     "/static/static/icons/sc2/neosteelfortifiedarmor.png", | 
					
						
							|  |  |  |             "Afterburners (Medivac)":                      "/static/static/icons/sc2/medivacemergencythrusters.png", | 
					
						
							|  |  |  |             "Tomahawk Power Cells (Wraith)":               "https://0rganics.org/archipelago/sc2wol/TomahawkPowerCells.png", | 
					
						
							|  |  |  |             "Displacement Field (Wraith)":                 "https://0rganics.org/archipelago/sc2wol/DisplacementField.png", | 
					
						
							|  |  |  |             "Advanced Laser Technology (Wraith)":          "/static/static/icons/sc2/improvedburstlaser.png", | 
					
						
							|  |  |  |             "Ripwave Missiles (Viking)":                   "https://0rganics.org/archipelago/sc2wol/RipwaveMissiles.png", | 
					
						
							|  |  |  |             "Phobos-Class Weapons System (Viking)":        "https://0rganics.org/archipelago/sc2wol/Phobos-ClassWeaponsSystem.png", | 
					
						
							|  |  |  |             "Smart Servos (Viking)":                       "/static/static/icons/sc2/transformationservos.png", | 
					
						
							|  |  |  |             "Magrail Munitions (Viking)":                  "/static/static/icons/sc2/magrailmunitions.png", | 
					
						
							|  |  |  |             "Cross-Spectrum Dampeners (Banshee)":          "/static/static/icons/sc2/crossspectrumdampeners.png", | 
					
						
							|  |  |  |             "Advanced Cross-Spectrum Dampeners (Banshee)": "https://0rganics.org/archipelago/sc2wol/Cross-SpectrumDampeners.png", | 
					
						
							|  |  |  |             "Shockwave Missile Battery (Banshee)":         "https://0rganics.org/archipelago/sc2wol/ShockwaveMissileBattery.png", | 
					
						
							|  |  |  |             "Hyperflight Rotors (Banshee)":                "/static/static/icons/sc2/hyperflightrotors.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Banshee)":            "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Internal Tech Module (Banshee)":              "/static/static/icons/sc2/internalizedtechmodule.png", | 
					
						
							|  |  |  |             "Missile Pods (Battlecruiser)":                "https://0rganics.org/archipelago/sc2wol/MissilePods.png", | 
					
						
							|  |  |  |             "Defensive Matrix (Battlecruiser)":            "https://0rganics.org/archipelago/sc2wol/DefensiveMatrix.png", | 
					
						
							|  |  |  |             "Tactical Jump (Battlecruiser)":               "/static/static/icons/sc2/warpjump.png", | 
					
						
							|  |  |  |             "Cloak (Battlecruiser)":                       "/static/static/icons/sc2/terran-cloak-color.png", | 
					
						
							|  |  |  |             "ATX Laser Battery (Battlecruiser)":           "/static/static/icons/sc2/specialordance.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Battlecruiser)":         "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Internal Tech Module (Battlecruiser)":        "/static/static/icons/sc2/internalizedtechmodule.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Ghost":                                       "https://static.wikia.nocookie.net/starcraft/images/6/6e/Ghost_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Spectre":                                     "https://static.wikia.nocookie.net/starcraft/images/0/0d/Spectre_WoL.jpg", | 
					
						
							|  |  |  |             "Thor":                                        "https://static.wikia.nocookie.net/starcraft/images/e/ef/Thor_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Widow Mine":                                  "/static/static/icons/sc2/widowmine.png", | 
					
						
							|  |  |  |             "Cyclone":                                     "/static/static/icons/sc2/cyclone.png", | 
					
						
							|  |  |  |             "Liberator":                                   "/static/static/icons/sc2/liberator.png", | 
					
						
							|  |  |  |             "Valkyrie":                                    "/static/static/icons/sc2/valkyrie.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Ocular Implants (Ghost)":                     "https://0rganics.org/archipelago/sc2wol/OcularImplants.png", | 
					
						
							|  |  |  |             "Crius Suit (Ghost)":                          "https://0rganics.org/archipelago/sc2wol/CriusSuit.png", | 
					
						
							|  |  |  |             "EMP Rounds (Ghost)":                          "/static/static/icons/sc2/terran-emp-color.png", | 
					
						
							|  |  |  |             "Lockdown (Ghost)":                            "/static/static/icons/sc2/lockdown.png", | 
					
						
							|  |  |  |             "Psionic Lash (Spectre)":                      "https://0rganics.org/archipelago/sc2wol/PsionicLash.png", | 
					
						
							|  |  |  |             "Nyx-Class Cloaking Module (Spectre)":         "https://0rganics.org/archipelago/sc2wol/Nyx-ClassCloakingModule.png", | 
					
						
							|  |  |  |             "Impaler Rounds (Spectre)":                    "/static/static/icons/sc2/impalerrounds.png", | 
					
						
							|  |  |  |             "330mm Barrage Cannon (Thor)":                 "https://0rganics.org/archipelago/sc2wol/330mmBarrageCannon.png", | 
					
						
							|  |  |  |             "Immortality Protocol (Thor)":                 "https://0rganics.org/archipelago/sc2wol/ImmortalityProtocol.png", | 
					
						
							|  |  |  |             "High Impact Payload (Thor)":                  "/static/static/icons/sc2/thorsiegemode.png", | 
					
						
							|  |  |  |             "Smart Servos (Thor)":                         "/static/static/icons/sc2/transformationservos.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Optimized Logistics (Predator)":              "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Drilling Claws (Widow Mine)":                 "/static/static/icons/sc2/drillingclaws.png", | 
					
						
							|  |  |  |             "Concealment (Widow Mine)":                    "/static/static/icons/sc2/widowminehidden.png", | 
					
						
							|  |  |  |             "Black Market Launchers (Widow Mine)":         "/static/static/icons/sc2/widowmine-attackrange.png", | 
					
						
							|  |  |  |             "Executioner Missiles (Widow Mine)":           "/static/static/icons/sc2/widowmine-deathblossom.png", | 
					
						
							|  |  |  |             "Mag-Field Accelerators (Cyclone)":            "/static/static/icons/sc2/magfieldaccelerator.png", | 
					
						
							|  |  |  |             "Mag-Field Launchers (Cyclone)":               "/static/static/icons/sc2/cyclonerangeupgrade.png", | 
					
						
							|  |  |  |             "Targeting Optics (Cyclone)":                  "/static/static/icons/sc2/targetingoptics.png", | 
					
						
							|  |  |  |             "Rapid Fire Launchers (Cyclone)":              "/static/static/icons/sc2/ripwavemissiles.png", | 
					
						
							|  |  |  |             "Bio Mechanical Repair Drone (Raven)":         "/static/static/icons/sc2/biomechanicaldrone.png", | 
					
						
							|  |  |  |             "Spider Mines (Raven)":                        "/static/static/icons/sc2/siegetank-spidermines.png", | 
					
						
							|  |  |  |             "Railgun Turret (Raven)":                      "/static/static/icons/sc2/autoturretblackops.png", | 
					
						
							|  |  |  |             "Hunter-Seeker Weapon (Raven)":                "/static/static/icons/sc2/specialordance.png", | 
					
						
							|  |  |  |             "Interference Matrix (Raven)":                 "/static/static/icons/sc2/interferencematrix.png", | 
					
						
							|  |  |  |             "Anti-Armor Missile (Raven)":                  "/static/static/icons/sc2/shreddermissile.png", | 
					
						
							|  |  |  |             "Internal Tech Module (Raven)":                "/static/static/icons/sc2/internalizedtechmodule.png", | 
					
						
							|  |  |  |             "EMP Shockwave (Science Vessel)":              "/static/static/icons/sc2/staticempblast.png", | 
					
						
							|  |  |  |             "Defensive Matrix (Science Vessel)":           "https://0rganics.org/archipelago/sc2wol/DefensiveMatrix.png", | 
					
						
							|  |  |  |             "Advanced Ballistics (Liberator)":             "/static/static/icons/sc2/advanceballistics.png", | 
					
						
							|  |  |  |             "Raid Artillery (Liberator)":                  "/static/static/icons/sc2/terrandefendermodestructureattack.png", | 
					
						
							|  |  |  |             "Cloak (Liberator)":                           "/static/static/icons/sc2/terran-cloak-color.png", | 
					
						
							|  |  |  |             "Laser Targeting System (Liberator)":          "/static/static/icons/sc2/lasertargetingsystem.png", | 
					
						
							|  |  |  |             "Optimized Logistics (Liberator)":             "/static/static/icons/sc2/optimizedlogistics.png", | 
					
						
							|  |  |  |             "Enhanced Cluster Launchers (Valkyrie)":       "https://0rganics.org/archipelago/sc2wol/HellstormBatteries.png", | 
					
						
							|  |  |  |             "Shaped Hull (Valkyrie)":                      "https://0rganics.org/archipelago/sc2wol/ShapedHull.png", | 
					
						
							|  |  |  |             "Burst Lasers (Valkyrie)":                     "/static/static/icons/sc2/improvedburstlaser.png", | 
					
						
							|  |  |  |             "Afterburners (Valkyrie)":                     "/static/static/icons/sc2/medivacemergencythrusters.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "War Pigs":                                    "https://static.wikia.nocookie.net/starcraft/images/e/ed/WarPigs_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Devil Dogs":                                  "https://static.wikia.nocookie.net/starcraft/images/3/33/DevilDogs_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Hammer Securities":                           "https://static.wikia.nocookie.net/starcraft/images/3/3b/HammerSecurity_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Spartan Company":                             "https://static.wikia.nocookie.net/starcraft/images/b/be/SpartanCompany_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Siege Breakers":                              "https://static.wikia.nocookie.net/starcraft/images/3/31/SiegeBreakers_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Hel's Angel":                                 "https://static.wikia.nocookie.net/starcraft/images/6/63/HelsAngels_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Dusk Wings":                                  "https://static.wikia.nocookie.net/starcraft/images/5/52/DuskWings_SC2_Icon1.jpg", | 
					
						
							|  |  |  |             "Jackson's Revenge":                           "https://static.wikia.nocookie.net/starcraft/images/9/95/JacksonsRevenge_SC2_Icon1.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Ultra-Capacitors":                            "https://static.wikia.nocookie.net/starcraft/images/2/23/SC2_Lab_Ultra_Capacitors_Icon.png", | 
					
						
							|  |  |  |             "Vanadium Plating":                            "https://static.wikia.nocookie.net/starcraft/images/6/67/SC2_Lab_VanPlating_Icon.png", | 
					
						
							|  |  |  |             "Orbital Depots":                              "https://static.wikia.nocookie.net/starcraft/images/0/01/SC2_Lab_Orbital_Depot_Icon.png", | 
					
						
							|  |  |  |             "Micro-Filtering":                             "https://static.wikia.nocookie.net/starcraft/images/2/20/SC2_Lab_MicroFilter_Icon.png", | 
					
						
							|  |  |  |             "Automated Refinery":                          "https://static.wikia.nocookie.net/starcraft/images/7/71/SC2_Lab_Auto_Refinery_Icon.png", | 
					
						
							|  |  |  |             "Command Center Reactor":                      "https://static.wikia.nocookie.net/starcraft/images/e/ef/SC2_Lab_CC_Reactor_Icon.png", | 
					
						
							|  |  |  |             "Raven":                                       "https://static.wikia.nocookie.net/starcraft/images/1/19/SC2_Lab_Raven_Icon.png", | 
					
						
							|  |  |  |             "Science Vessel":                              "https://static.wikia.nocookie.net/starcraft/images/c/c3/SC2_Lab_SciVes_Icon.png", | 
					
						
							|  |  |  |             "Tech Reactor":                                "https://static.wikia.nocookie.net/starcraft/images/c/c5/SC2_Lab_Tech_Reactor_Icon.png", | 
					
						
							|  |  |  |             "Orbital Strike":                              "https://static.wikia.nocookie.net/starcraft/images/d/df/SC2_Lab_Orb_Strike_Icon.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Shrike Turret (Bunker)":                      "https://static.wikia.nocookie.net/starcraft/images/4/44/SC2_Lab_Shrike_Turret_Icon.png", | 
					
						
							|  |  |  |             "Fortified Bunker (Bunker)":                   "https://static.wikia.nocookie.net/starcraft/images/4/4f/SC2_Lab_FortBunker_Icon.png", | 
					
						
							|  |  |  |             "Planetary Fortress":                          "https://static.wikia.nocookie.net/starcraft/images/0/0b/SC2_Lab_PlanetFortress_Icon.png", | 
					
						
							|  |  |  |             "Perdition Turret":                            "https://static.wikia.nocookie.net/starcraft/images/a/af/SC2_Lab_PerdTurret_Icon.png", | 
					
						
							|  |  |  |             "Predator":                                    "https://static.wikia.nocookie.net/starcraft/images/8/83/SC2_Lab_Predator_Icon.png", | 
					
						
							|  |  |  |             "Hercules":                                    "https://static.wikia.nocookie.net/starcraft/images/4/40/SC2_Lab_Hercules_Icon.png", | 
					
						
							|  |  |  |             "Cellular Reactor":                            "https://static.wikia.nocookie.net/starcraft/images/d/d8/SC2_Lab_CellReactor_Icon.png", | 
					
						
							|  |  |  |             "Regenerative Bio-Steel Level 1":              "/static/static/icons/sc2/SC2_Lab_BioSteel_L1.png", | 
					
						
							|  |  |  |             "Regenerative Bio-Steel Level 2":              "/static/static/icons/sc2/SC2_Lab_BioSteel_L2.png", | 
					
						
							|  |  |  |             "Hive Mind Emulator":                          "https://static.wikia.nocookie.net/starcraft/images/b/bc/SC2_Lab_Hive_Emulator_Icon.png", | 
					
						
							|  |  |  |             "Psi Disrupter":                               "https://static.wikia.nocookie.net/starcraft/images/c/cf/SC2_Lab_Psi_Disruptor_Icon.png", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Zealot":                                      "https://static.wikia.nocookie.net/starcraft/images/6/6e/Icon_Protoss_Zealot.jpg", | 
					
						
							|  |  |  |             "Stalker":                                     "https://static.wikia.nocookie.net/starcraft/images/0/0d/Icon_Protoss_Stalker.jpg", | 
					
						
							|  |  |  |             "High Templar":                                "https://static.wikia.nocookie.net/starcraft/images/a/a0/Icon_Protoss_High_Templar.jpg", | 
					
						
							|  |  |  |             "Dark Templar":                                "https://static.wikia.nocookie.net/starcraft/images/9/90/Icon_Protoss_Dark_Templar.jpg", | 
					
						
							|  |  |  |             "Immortal":                                    "https://static.wikia.nocookie.net/starcraft/images/c/c1/Icon_Protoss_Immortal.jpg", | 
					
						
							|  |  |  |             "Colossus":                                    "https://static.wikia.nocookie.net/starcraft/images/4/40/Icon_Protoss_Colossus.jpg", | 
					
						
							|  |  |  |             "Phoenix":                                     "https://static.wikia.nocookie.net/starcraft/images/b/b1/Icon_Protoss_Phoenix.jpg", | 
					
						
							|  |  |  |             "Void Ray":                                    "https://static.wikia.nocookie.net/starcraft/images/1/1d/VoidRay_SC2_Rend1.jpg", | 
					
						
							|  |  |  |             "Carrier":                                     "https://static.wikia.nocookie.net/starcraft/images/2/2c/Icon_Protoss_Carrier.jpg", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "Nothing":                                     "", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         sc2wol_location_ids = { | 
					
						
							|  |  |  |             "Liberation Day":          range(SC2WOL_LOC_ID_OFFSET + 100, SC2WOL_LOC_ID_OFFSET + 200), | 
					
						
							|  |  |  |             "The Outlaws":             range(SC2WOL_LOC_ID_OFFSET + 200, SC2WOL_LOC_ID_OFFSET + 300), | 
					
						
							|  |  |  |             "Zero Hour":               range(SC2WOL_LOC_ID_OFFSET + 300, SC2WOL_LOC_ID_OFFSET + 400), | 
					
						
							|  |  |  |             "Evacuation":              range(SC2WOL_LOC_ID_OFFSET + 400, SC2WOL_LOC_ID_OFFSET + 500), | 
					
						
							|  |  |  |             "Outbreak":                range(SC2WOL_LOC_ID_OFFSET + 500, SC2WOL_LOC_ID_OFFSET + 600), | 
					
						
							|  |  |  |             "Safe Haven":              range(SC2WOL_LOC_ID_OFFSET + 600, SC2WOL_LOC_ID_OFFSET + 700), | 
					
						
							|  |  |  |             "Haven's Fall":            range(SC2WOL_LOC_ID_OFFSET + 700, SC2WOL_LOC_ID_OFFSET + 800), | 
					
						
							|  |  |  |             "Smash and Grab":          range(SC2WOL_LOC_ID_OFFSET + 800, SC2WOL_LOC_ID_OFFSET + 900), | 
					
						
							|  |  |  |             "The Dig":                 range(SC2WOL_LOC_ID_OFFSET + 900, SC2WOL_LOC_ID_OFFSET + 1000), | 
					
						
							|  |  |  |             "The Moebius Factor":      range(SC2WOL_LOC_ID_OFFSET + 1000, SC2WOL_LOC_ID_OFFSET + 1100), | 
					
						
							|  |  |  |             "Supernova":               range(SC2WOL_LOC_ID_OFFSET + 1100, SC2WOL_LOC_ID_OFFSET + 1200), | 
					
						
							|  |  |  |             "Maw of the Void":         range(SC2WOL_LOC_ID_OFFSET + 1200, SC2WOL_LOC_ID_OFFSET + 1300), | 
					
						
							|  |  |  |             "Devil's Playground":      range(SC2WOL_LOC_ID_OFFSET + 1300, SC2WOL_LOC_ID_OFFSET + 1400), | 
					
						
							|  |  |  |             "Welcome to the Jungle":   range(SC2WOL_LOC_ID_OFFSET + 1400, SC2WOL_LOC_ID_OFFSET + 1500), | 
					
						
							|  |  |  |             "Breakout":                range(SC2WOL_LOC_ID_OFFSET + 1500, SC2WOL_LOC_ID_OFFSET + 1600), | 
					
						
							|  |  |  |             "Ghost of a Chance":       range(SC2WOL_LOC_ID_OFFSET + 1600, SC2WOL_LOC_ID_OFFSET + 1700), | 
					
						
							|  |  |  |             "The Great Train Robbery": range(SC2WOL_LOC_ID_OFFSET + 1700, SC2WOL_LOC_ID_OFFSET + 1800), | 
					
						
							|  |  |  |             "Cutthroat":               range(SC2WOL_LOC_ID_OFFSET + 1800, SC2WOL_LOC_ID_OFFSET + 1900), | 
					
						
							|  |  |  |             "Engine of Destruction":   range(SC2WOL_LOC_ID_OFFSET + 1900, SC2WOL_LOC_ID_OFFSET + 2000), | 
					
						
							|  |  |  |             "Media Blitz":             range(SC2WOL_LOC_ID_OFFSET + 2000, SC2WOL_LOC_ID_OFFSET + 2100), | 
					
						
							|  |  |  |             "Piercing the Shroud":     range(SC2WOL_LOC_ID_OFFSET + 2100, SC2WOL_LOC_ID_OFFSET + 2200), | 
					
						
							|  |  |  |             "Whispers of Doom":        range(SC2WOL_LOC_ID_OFFSET + 2200, SC2WOL_LOC_ID_OFFSET + 2300), | 
					
						
							|  |  |  |             "A Sinister Turn":         range(SC2WOL_LOC_ID_OFFSET + 2300, SC2WOL_LOC_ID_OFFSET + 2400), | 
					
						
							|  |  |  |             "Echoes of the Future":    range(SC2WOL_LOC_ID_OFFSET + 2400, SC2WOL_LOC_ID_OFFSET + 2500), | 
					
						
							|  |  |  |             "In Utter Darkness":       range(SC2WOL_LOC_ID_OFFSET + 2500, SC2WOL_LOC_ID_OFFSET + 2600), | 
					
						
							|  |  |  |             "Gates of Hell":           range(SC2WOL_LOC_ID_OFFSET + 2600, SC2WOL_LOC_ID_OFFSET + 2700), | 
					
						
							|  |  |  |             "Belly of the Beast":      range(SC2WOL_LOC_ID_OFFSET + 2700, SC2WOL_LOC_ID_OFFSET + 2800), | 
					
						
							|  |  |  |             "Shatter the Sky":         range(SC2WOL_LOC_ID_OFFSET + 2800, SC2WOL_LOC_ID_OFFSET + 2900), | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         display_data = {} | 
					
						
							| 
									
										
										
										
											2022-01-19 19:35:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:29:35 -06:00
										 |  |  |         # Grouped Items | 
					
						
							|  |  |  |         grouped_item_ids = { | 
					
						
							|  |  |  |             "Progressive Weapon Upgrade":       107 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Armor Upgrade":        108 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Infantry Upgrade":     109 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Vehicle Upgrade":      110 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Ship Upgrade":         111 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Weapon/Armor Upgrade": 112 + SC2WOL_ITEM_ID_OFFSET | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         grouped_item_replacements = { | 
					
						
							|  |  |  |             "Progressive Weapon Upgrade":   ["Progressive Infantry Weapon", "Progressive Vehicle Weapon", | 
					
						
							|  |  |  |                                              "Progressive Ship Weapon"], | 
					
						
							|  |  |  |             "Progressive Armor Upgrade":    ["Progressive Infantry Armor", "Progressive Vehicle Armor", | 
					
						
							|  |  |  |                                              "Progressive Ship Armor"], | 
					
						
							|  |  |  |             "Progressive Infantry Upgrade": ["Progressive Infantry Weapon", "Progressive Infantry Armor"], | 
					
						
							|  |  |  |             "Progressive Vehicle Upgrade":  ["Progressive Vehicle Weapon", "Progressive Vehicle Armor"], | 
					
						
							|  |  |  |             "Progressive Ship Upgrade":     ["Progressive Ship Weapon", "Progressive Ship Armor"] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         grouped_item_replacements["Progressive Weapon/Armor Upgrade"] = grouped_item_replacements[ | 
					
						
							|  |  |  |                                                                             "Progressive Weapon Upgrade"] + \ | 
					
						
							|  |  |  |                                                                         grouped_item_replacements[ | 
					
						
							|  |  |  |                                                                             "Progressive Armor Upgrade"] | 
					
						
							|  |  |  |         replacement_item_ids = { | 
					
						
							|  |  |  |             "Progressive Infantry Weapon": 100 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Infantry Armor":  102 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Vehicle Weapon":  103 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Vehicle Armor":   104 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Ship Weapon":     105 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Ship Armor":      106 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inventory = tracker_data.get_player_inventory_counts(team, player) | 
					
						
							|  |  |  |         for grouped_item_name, grouped_item_id in grouped_item_ids.items(): | 
					
						
							|  |  |  |             count: int = inventory[grouped_item_id] | 
					
						
							|  |  |  |             if count > 0: | 
					
						
							|  |  |  |                 for replacement_item in grouped_item_replacements[grouped_item_name]: | 
					
						
							|  |  |  |                     replacement_id: int = replacement_item_ids[replacement_item] | 
					
						
							|  |  |  |                     inventory[replacement_id] = count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Determine display for progressive items | 
					
						
							|  |  |  |         progressive_items = { | 
					
						
							|  |  |  |             "Progressive Infantry Weapon":                    100 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Infantry Armor":                     102 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Vehicle Weapon":                     103 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Vehicle Armor":                      104 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Ship Weapon":                        105 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Ship Armor":                         106 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Stimpack (Marine)":                  208 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Stimpack (Firebat)":                 226 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Stimpack (Marauder)":                228 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Stimpack (Reaper)":                  250 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Stimpack (Hellion)":                 259 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive High Impact Payload (Thor)":         361 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Cross-Spectrum Dampeners (Banshee)": 316 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "Progressive Regenerative Bio-Steel":             617 + SC2WOL_ITEM_ID_OFFSET | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         progressive_names = { | 
					
						
							|  |  |  |             "Progressive Infantry Weapon":                    ["Infantry Weapons Level 1", "Infantry Weapons Level 1", | 
					
						
							|  |  |  |                                                                "Infantry Weapons Level 2", "Infantry Weapons Level 3"], | 
					
						
							|  |  |  |             "Progressive Infantry Armor":                     ["Infantry Armor Level 1", "Infantry Armor Level 1", | 
					
						
							|  |  |  |                                                                "Infantry Armor Level 2", "Infantry Armor Level 3"], | 
					
						
							|  |  |  |             "Progressive Vehicle Weapon":                     ["Vehicle Weapons Level 1", "Vehicle Weapons Level 1", | 
					
						
							|  |  |  |                                                                "Vehicle Weapons Level 2", "Vehicle Weapons Level 3"], | 
					
						
							|  |  |  |             "Progressive Vehicle Armor":                      ["Vehicle Armor Level 1", "Vehicle Armor Level 1", | 
					
						
							|  |  |  |                                                                "Vehicle Armor Level 2", "Vehicle Armor Level 3"], | 
					
						
							|  |  |  |             "Progressive Ship Weapon":                        ["Ship Weapons Level 1", "Ship Weapons Level 1", | 
					
						
							|  |  |  |                                                                "Ship Weapons Level 2", "Ship Weapons Level 3"], | 
					
						
							|  |  |  |             "Progressive Ship Armor":                         ["Ship Armor Level 1", "Ship Armor Level 1", | 
					
						
							|  |  |  |                                                                "Ship Armor Level 2", "Ship Armor Level 3"], | 
					
						
							|  |  |  |             "Progressive Stimpack (Marine)":                  ["Stimpack (Marine)", "Stimpack (Marine)", | 
					
						
							|  |  |  |                                                                "Super Stimpack (Marine)"], | 
					
						
							|  |  |  |             "Progressive Stimpack (Firebat)":                 ["Stimpack (Firebat)", "Stimpack (Firebat)", | 
					
						
							|  |  |  |                                                                "Super Stimpack (Firebat)"], | 
					
						
							|  |  |  |             "Progressive Stimpack (Marauder)":                ["Stimpack (Marauder)", "Stimpack (Marauder)", | 
					
						
							|  |  |  |                                                                "Super Stimpack (Marauder)"], | 
					
						
							|  |  |  |             "Progressive Stimpack (Reaper)":                  ["Stimpack (Reaper)", "Stimpack (Reaper)", | 
					
						
							|  |  |  |                                                                "Super Stimpack (Reaper)"], | 
					
						
							|  |  |  |             "Progressive Stimpack (Hellion)":                 ["Stimpack (Hellion)", "Stimpack (Hellion)", | 
					
						
							|  |  |  |                                                                "Super Stimpack (Hellion)"], | 
					
						
							|  |  |  |             "Progressive High Impact Payload (Thor)":         ["High Impact Payload (Thor)", | 
					
						
							|  |  |  |                                                                "High Impact Payload (Thor)", "Smart Servos (Thor)"], | 
					
						
							|  |  |  |             "Progressive Cross-Spectrum Dampeners (Banshee)": ["Cross-Spectrum Dampeners (Banshee)", | 
					
						
							|  |  |  |                                                                "Cross-Spectrum Dampeners (Banshee)", | 
					
						
							|  |  |  |                                                                "Advanced Cross-Spectrum Dampeners (Banshee)"], | 
					
						
							|  |  |  |             "Progressive Regenerative Bio-Steel":             ["Regenerative Bio-Steel Level 1", | 
					
						
							|  |  |  |                                                                "Regenerative Bio-Steel Level 1", | 
					
						
							|  |  |  |                                                                "Regenerative Bio-Steel Level 2"] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_id in progressive_items.items(): | 
					
						
							|  |  |  |             level = min(inventory[item_id], len(progressive_names[item_name]) - 1) | 
					
						
							|  |  |  |             display_name = progressive_names[item_name][level] | 
					
						
							|  |  |  |             base_name = (item_name.split(maxsplit=1)[1].lower() | 
					
						
							|  |  |  |                          .replace(' ', '_') | 
					
						
							|  |  |  |                          .replace("-", "") | 
					
						
							|  |  |  |                          .replace("(", "") | 
					
						
							|  |  |  |                          .replace(")", "")) | 
					
						
							|  |  |  |             display_data[base_name + "_level"] = level | 
					
						
							|  |  |  |             display_data[base_name + "_url"] = icons[display_name] | 
					
						
							|  |  |  |             display_data[base_name + "_name"] = display_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Multi-items | 
					
						
							|  |  |  |         multi_items = { | 
					
						
							|  |  |  |             "+15 Starting Minerals": 800 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "+15 Starting Vespene":  801 + SC2WOL_ITEM_ID_OFFSET, | 
					
						
							|  |  |  |             "+2 Starting Supply":    802 + SC2WOL_ITEM_ID_OFFSET | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for item_name, item_id in multi_items.items(): | 
					
						
							|  |  |  |             base_name = item_name.split()[-1].lower() | 
					
						
							|  |  |  |             count = inventory[item_id] | 
					
						
							|  |  |  |             if base_name == "supply": | 
					
						
							|  |  |  |                 count = count * 2 | 
					
						
							|  |  |  |                 display_data[base_name + "_count"] = count | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 count = count * 15 | 
					
						
							|  |  |  |                 display_data[base_name + "_count"] = count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Victory condition | 
					
						
							|  |  |  |         game_state = tracker_data.get_player_client_status(team, player) | 
					
						
							|  |  |  |         display_data["game_finished"] = game_state == 30 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Turn location IDs into mission objective counts | 
					
						
							|  |  |  |         locations = tracker_data.get_player_locations(team, player) | 
					
						
							|  |  |  |         checked_locations = tracker_data.get_player_checked_locations(team, player) | 
					
						
							|  |  |  |         lookup_name = lambda id: tracker_data.location_id_to_name["Starcraft 2 Wings of Liberty"][id] | 
					
						
							|  |  |  |         location_info = {mission_name: {lookup_name(id): (id in checked_locations) for id in mission_locations if | 
					
						
							|  |  |  |                                         id in set(locations)} for mission_name, mission_locations in | 
					
						
							|  |  |  |                          sc2wol_location_ids.items()} | 
					
						
							|  |  |  |         checks_done = {mission_name: len( | 
					
						
							|  |  |  |             [id for id in mission_locations if id in checked_locations and id in set(locations)]) for | 
					
						
							|  |  |  |                        mission_name, mission_locations in sc2wol_location_ids.items()} | 
					
						
							|  |  |  |         checks_done['Total'] = len(checked_locations) | 
					
						
							|  |  |  |         checks_in_area = {mission_name: len([id for id in mission_locations if id in set(locations)]) for | 
					
						
							|  |  |  |                           mission_name, mission_locations in sc2wol_location_ids.items()} | 
					
						
							|  |  |  |         checks_in_area['Total'] = sum(checks_in_area.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lookup_any_item_id_to_name = tracker_data.item_id_to_name["Starcraft 2 Wings of Liberty"] | 
					
						
							|  |  |  |         return render_template( | 
					
						
							|  |  |  |             "tracker__Starcraft2WingsOfLiberty.html", | 
					
						
							|  |  |  |             inventory=inventory, | 
					
						
							|  |  |  |             icons=icons, | 
					
						
							|  |  |  |             acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0}, | 
					
						
							|  |  |  |             player=player, | 
					
						
							|  |  |  |             team=team, | 
					
						
							|  |  |  |             room=tracker_data.room, | 
					
						
							|  |  |  |             player_name=tracker_data.get_player_name(team, player), | 
					
						
							|  |  |  |             checks_done=checks_done, | 
					
						
							|  |  |  |             checks_in_area=checks_in_area, | 
					
						
							|  |  |  |             location_info=location_info, | 
					
						
							|  |  |  |             **display_data, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _player_trackers["Starcraft 2 Wings of Liberty"] = render_Starcraft2WingsOfLiberty_tracker |