from typing import TYPE_CHECKING from BaseClasses import MultiWorld, CollectionState, ItemClassification from NetUtils import NetworkItem from . import Lingo2World, Lingo2Item from .regions import connect_ports_from_ut from .options import Lingo2Options, ShuffleLetters if TYPE_CHECKING: from .context import Lingo2Manager PLAYER_NUM = 1 class Tracker: manager: "Lingo2Manager" multiworld: MultiWorld world: Lingo2World collected_items: dict[int, int] checked_locations: set[int] accessible_locations: set[int] accessible_worldports: set[int] state: CollectionState def __init__(self, manager: "Lingo2Manager"): self.manager = manager self.collected_items = {} self.checked_locations = set() self.accessible_locations = set() self.accessible_worldports = set() def setup_slot(self, slot_data): Lingo2World.for_tracker = True self.multiworld = MultiWorld(players=PLAYER_NUM) self.world = Lingo2World(self.multiworld, PLAYER_NUM) self.multiworld.worlds[1] = self.world self.world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) for k, t in Lingo2Options.type_hints.items()}) self.world.generate_early() self.world.create_regions() if self.world.options.shuffle_worldports: port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()} connect_ports_from_ut(port_pairings, self.world) self.refresh_state() def set_checked_locations(self, checked_locations: set[int]): self.checked_locations = checked_locations.copy() def set_collected_items(self, network_items: list[NetworkItem]): self.collected_items = {} for item in network_items: self.collected_items[item.item] = self.collected_items.get(item.item, 0) + 1 self.refresh_state() def refresh_state(self): self.state = CollectionState(self.multiworld) for item_id, item_amount in self.collected_items.items(): for i in range(item_amount): self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id), ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True) for k, v in self.manager.keyboard.items(): # Unless all level 1 letters are pre-unlocked, H1 I1 N1 and T1 act differently between the generator and # game. The generator considers them to be unlocked, which means they are not included in logic # requirements, and only one item/event is needed to unlock their level 2 forms. The game considers them to # be vanilla, which means you still have to pick them up in the Starting Room in order for them to appear on # your keyboard. This also means that whether or not you have the level 1 forms should be synced to the # multiworld. The tracker specifically should collect one fewer item for these letters in this scenario. tv = v if k in "hint" and self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla, ShuffleLetters.option_progressive]: tv = max(0, v - 1) if tv > 0: for i in range(tv): self.state.collect(Lingo2Item(k.upper(), ItemClassification.progression, None, PLAYER_NUM), prevent_sweep=True) for port_id in self.manager.worldports: self.state.collect(Lingo2Item(f"Worldport {port_id} Entered", ItemClassification.progression, None, PLAYER_NUM), prevent_sweep=True) self.state.sweep_for_advancements() self.accessible_locations = set() self.accessible_worldports = set() for region in self.state.reachable_regions[PLAYER_NUM]: for location in region.locations: if location.access_rule(self.state): if location.address is not None: if location.address not in self.checked_locations: self.accessible_locations.add(location.address) elif hasattr(location, "port_id"): if location.port_id not in self.manager.worldports: self.accessible_worldports.add(location.port_id)