about summary refs log tree commit diff stats
path: root/apworld/tracker.py
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/tracker.py')
-rw-r--r--apworld/tracker.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/apworld/tracker.py b/apworld/tracker.py new file mode 100644 index 0000000..cf2dbe1 --- /dev/null +++ b/apworld/tracker.py
@@ -0,0 +1,106 @@
1from typing import TYPE_CHECKING
2
3from BaseClasses import MultiWorld, CollectionState, ItemClassification
4from NetUtils import NetworkItem
5from . import Lingo2World, Lingo2Item
6from .regions import connect_ports_from_ut
7from .options import Lingo2Options, ShuffleLetters
8
9if TYPE_CHECKING:
10 from .context import Lingo2Manager
11
12PLAYER_NUM = 1
13
14
15class Tracker:
16 manager: "Lingo2Manager"
17
18 multiworld: MultiWorld
19 world: Lingo2World
20
21 collected_items: dict[int, int]
22 checked_locations: set[int]
23 accessible_locations: set[int]
24 accessible_worldports: set[int]
25
26 state: CollectionState
27
28 def __init__(self, manager: "Lingo2Manager"):
29 self.manager = manager
30 self.collected_items = {}
31 self.checked_locations = set()
32 self.accessible_locations = set()
33 self.accessible_worldports = set()
34
35 def setup_slot(self, slot_data):
36 Lingo2World.for_tracker = True
37
38 self.multiworld = MultiWorld(players=PLAYER_NUM)
39 self.world = Lingo2World(self.multiworld, PLAYER_NUM)
40 self.multiworld.worlds[1] = self.world
41 self.world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default))
42 for k, t in Lingo2Options.type_hints.items()})
43
44 self.world.generate_early()
45 self.world.create_regions()
46
47 if self.world.options.shuffle_worldports:
48 port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()}
49 connect_ports_from_ut(port_pairings, self.world)
50
51 self.refresh_state()
52
53 def set_checked_locations(self, checked_locations: set[int]):
54 self.checked_locations = checked_locations.copy()
55
56 def set_collected_items(self, network_items: list[NetworkItem]):
57 self.collected_items = {}
58
59 for item in network_items:
60 self.collected_items[item.item] = self.collected_items.get(item.item, 0) + 1
61
62 self.refresh_state()
63
64 def refresh_state(self):
65 self.state = CollectionState(self.multiworld)
66
67 for item_id, item_amount in self.collected_items.items():
68 for i in range(item_amount):
69 self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id),
70 ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True)
71
72 for k, v in self.manager.keyboard.items():
73 # Unless all level 1 letters are pre-unlocked, H1 I1 N1 and T1 act differently between the generator and
74 # game. The generator considers them to be unlocked, which means they are not included in logic
75 # requirements, and only one item/event is needed to unlock their level 2 forms. The game considers them to
76 # be vanilla, which means you still have to pick them up in the Starting Room in order for them to appear on
77 # your keyboard. This also means that whether or not you have the level 1 forms should be synced to the
78 # multiworld. The tracker specifically should collect one fewer item for these letters in this scenario.
79 tv = v
80 if k in "hint" and self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla,
81 ShuffleLetters.option_progressive]:
82 tv = max(0, v - 1)
83
84 if tv > 0:
85 for i in range(tv):
86 self.state.collect(Lingo2Item(k.upper(), ItemClassification.progression, None, PLAYER_NUM),
87 prevent_sweep=True)
88
89 for port_id in self.manager.worldports:
90 self.state.collect(Lingo2Item(f"Worldport {port_id} Entered", ItemClassification.progression, None,
91 PLAYER_NUM), prevent_sweep=True)
92
93 self.state.sweep_for_advancements()
94
95 self.accessible_locations = set()
96 self.accessible_worldports = set()
97
98 for region in self.state.reachable_regions[PLAYER_NUM]:
99 for location in region.locations:
100 if location.access_rule(self.state):
101 if location.address is not None:
102 if location.address not in self.checked_locations:
103 self.accessible_locations.add(location.address)
104 elif hasattr(location, "port_id"):
105 if location.port_id not in self.manager.worldports:
106 self.accessible_worldports.add(location.port_id)