From b0f474bee1c8e1111f7542bf4985136d9aedf340 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 27 Sep 2025 17:14:40 -0400 Subject: Treat local letters as items for tracker Local letters are now synced with datastorage, so they transfer to other computers like regular items would, and the tracker also now waits until you collect local letters before showing what they give you in logic. --- apworld/tracker.py | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) (limited to 'apworld/tracker.py') diff --git a/apworld/tracker.py b/apworld/tracker.py index 721e9b3..2c3d0f3 100644 --- a/apworld/tracker.py +++ b/apworld/tracker.py @@ -1,14 +1,22 @@ +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 +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] @@ -16,26 +24,29 @@ class Tracker: state: CollectionState - def __init__(self): + def __init__(self, manager: "Lingo2Manager"): + self.manager = manager self.collected_items = {} self.checked_locations = set() self.accessible_locations = set() def setup_slot(self, slot_data): + Lingo2World.for_tracker = True + self.multiworld = MultiWorld(players=PLAYER_NUM) - world = Lingo2World(self.multiworld, PLAYER_NUM) - self.multiworld.worlds[1] = world - world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) - for k, t in Lingo2Options.type_hints.items()}) + 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()}) - world.generate_early() - world.create_regions() + self.world.generate_early() + self.world.create_regions() - if world.options.shuffle_worldports: + 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, world) + connect_ports_from_ut(port_pairings, self.world) - self.state = CollectionState(self.multiworld) + self.refresh_state() def set_checked_locations(self, checked_locations: set[int]): self.checked_locations = checked_locations.copy() @@ -56,6 +67,23 @@ class Tracker: 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) + self.state.sweep_for_advancements() self.accessible_locations = set() -- cgit 1.4.1