diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-27 17:14:40 -0400 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-27 17:14:40 -0400 |
commit | b0f474bee1c8e1111f7542bf4985136d9aedf340 (patch) | |
tree | ef2aa34bad532ffb2a45d90893dbcd4c378a0dfb /apworld/tracker.py | |
parent | feb89a44ddf5f93bc476ca29cd02257aea47dc06 (diff) | |
download | lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.tar.gz lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.tar.bz2 lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.zip |
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.
Diffstat (limited to 'apworld/tracker.py')
-rw-r--r-- | apworld/tracker.py | 50 |
1 files changed, 39 insertions, 11 deletions
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 @@ | |||
1 | from typing import TYPE_CHECKING | ||
2 | |||
1 | from BaseClasses import MultiWorld, CollectionState, ItemClassification | 3 | from BaseClasses import MultiWorld, CollectionState, ItemClassification |
2 | from NetUtils import NetworkItem | 4 | from NetUtils import NetworkItem |
3 | from . import Lingo2World, Lingo2Item | 5 | from . import Lingo2World, Lingo2Item |
4 | from .regions import connect_ports_from_ut | 6 | from .regions import connect_ports_from_ut |
5 | from .options import Lingo2Options | 7 | from .options import Lingo2Options, ShuffleLetters |
8 | |||
9 | if TYPE_CHECKING: | ||
10 | from .context import Lingo2Manager | ||
6 | 11 | ||
7 | PLAYER_NUM = 1 | 12 | PLAYER_NUM = 1 |
8 | 13 | ||
9 | 14 | ||
10 | class Tracker: | 15 | class Tracker: |
16 | manager: "Lingo2Manager" | ||
17 | |||
11 | multiworld: MultiWorld | 18 | multiworld: MultiWorld |
19 | world: Lingo2World | ||
12 | 20 | ||
13 | collected_items: dict[int, int] | 21 | collected_items: dict[int, int] |
14 | checked_locations: set[int] | 22 | checked_locations: set[int] |
@@ -16,26 +24,29 @@ class Tracker: | |||
16 | 24 | ||
17 | state: CollectionState | 25 | state: CollectionState |
18 | 26 | ||
19 | def __init__(self): | 27 | def __init__(self, manager: "Lingo2Manager"): |
28 | self.manager = manager | ||
20 | self.collected_items = {} | 29 | self.collected_items = {} |
21 | self.checked_locations = set() | 30 | self.checked_locations = set() |
22 | self.accessible_locations = set() | 31 | self.accessible_locations = set() |
23 | 32 | ||
24 | def setup_slot(self, slot_data): | 33 | def setup_slot(self, slot_data): |
34 | Lingo2World.for_tracker = True | ||
35 | |||
25 | self.multiworld = MultiWorld(players=PLAYER_NUM) | 36 | self.multiworld = MultiWorld(players=PLAYER_NUM) |
26 | world = Lingo2World(self.multiworld, PLAYER_NUM) | 37 | self.world = Lingo2World(self.multiworld, PLAYER_NUM) |
27 | self.multiworld.worlds[1] = world | 38 | self.multiworld.worlds[1] = self.world |
28 | world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) | 39 | self.world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) |
29 | for k, t in Lingo2Options.type_hints.items()}) | 40 | for k, t in Lingo2Options.type_hints.items()}) |
30 | 41 | ||
31 | world.generate_early() | 42 | self.world.generate_early() |
32 | world.create_regions() | 43 | self.world.create_regions() |
33 | 44 | ||
34 | if world.options.shuffle_worldports: | 45 | if self.world.options.shuffle_worldports: |
35 | port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()} | 46 | port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()} |
36 | connect_ports_from_ut(port_pairings, world) | 47 | connect_ports_from_ut(port_pairings, self.world) |
37 | 48 | ||
38 | self.state = CollectionState(self.multiworld) | 49 | self.refresh_state() |
39 | 50 | ||
40 | def set_checked_locations(self, checked_locations: set[int]): | 51 | def set_checked_locations(self, checked_locations: set[int]): |
41 | self.checked_locations = checked_locations.copy() | 52 | self.checked_locations = checked_locations.copy() |
@@ -56,6 +67,23 @@ class Tracker: | |||
56 | self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id), | 67 | self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id), |
57 | ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True) | 68 | ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True) |
58 | 69 | ||
70 | for k, v in self.manager.keyboard.items(): | ||
71 | # Unless all level 1 letters are pre-unlocked, H1 I1 N1 and T1 act differently between the generator and | ||
72 | # game. The generator considers them to be unlocked, which means they are not included in logic | ||
73 | # requirements, and only one item/event is needed to unlock their level 2 forms. The game considers them to | ||
74 | # be vanilla, which means you still have to pick them up in the Starting Room in order for them to appear on | ||
75 | # your keyboard. This also means that whether or not you have the level 1 forms should be synced to the | ||
76 | # multiworld. The tracker specifically should collect one fewer item for these letters in this scenario. | ||
77 | tv = v | ||
78 | if k in "hint" and self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla, | ||
79 | ShuffleLetters.option_progressive]: | ||
80 | tv = max(0, v - 1) | ||
81 | |||
82 | if tv > 0: | ||
83 | for i in range(tv): | ||
84 | self.state.collect(Lingo2Item(k.upper(), ItemClassification.progression, None, PLAYER_NUM), | ||
85 | prevent_sweep=True) | ||
86 | |||
59 | self.state.sweep_for_advancements() | 87 | self.state.sweep_for_advancements() |
60 | 88 | ||
61 | self.accessible_locations = set() | 89 | self.accessible_locations = set() |