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.py65
1 files changed, 52 insertions, 13 deletions
diff --git a/apworld/tracker.py b/apworld/tracker.py index 721e9b3..cf2dbe1 100644 --- a/apworld/tracker.py +++ b/apworld/tracker.py
@@ -1,41 +1,54 @@
1from typing import TYPE_CHECKING
2
1from BaseClasses import MultiWorld, CollectionState, ItemClassification 3from BaseClasses import MultiWorld, CollectionState, ItemClassification
2from NetUtils import NetworkItem 4from NetUtils import NetworkItem
3from . import Lingo2World, Lingo2Item 5from . import Lingo2World, Lingo2Item
4from .regions import connect_ports_from_ut 6from .regions import connect_ports_from_ut
5from .options import Lingo2Options 7from .options import Lingo2Options, ShuffleLetters
8
9if TYPE_CHECKING:
10 from .context import Lingo2Manager
6 11
7PLAYER_NUM = 1 12PLAYER_NUM = 1
8 13
9 14
10class Tracker: 15class 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]
15 accessible_locations: set[int] 23 accessible_locations: set[int]
24 accessible_worldports: set[int]
16 25
17 state: CollectionState 26 state: CollectionState
18 27
19 def __init__(self): 28 def __init__(self, manager: "Lingo2Manager"):
29 self.manager = manager
20 self.collected_items = {} 30 self.collected_items = {}
21 self.checked_locations = set() 31 self.checked_locations = set()
22 self.accessible_locations = set() 32 self.accessible_locations = set()
33 self.accessible_worldports = set()
23 34
24 def setup_slot(self, slot_data): 35 def setup_slot(self, slot_data):
36 Lingo2World.for_tracker = True
37
25 self.multiworld = MultiWorld(players=PLAYER_NUM) 38 self.multiworld = MultiWorld(players=PLAYER_NUM)
26 world = Lingo2World(self.multiworld, PLAYER_NUM) 39 self.world = Lingo2World(self.multiworld, PLAYER_NUM)
27 self.multiworld.worlds[1] = world 40 self.multiworld.worlds[1] = self.world
28 world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default)) 41 self.world.options = Lingo2Options(**{k: t(slot_data.get(k, t.default))
29 for k, t in Lingo2Options.type_hints.items()}) 42 for k, t in Lingo2Options.type_hints.items()})
30 43
31 world.generate_early() 44 self.world.generate_early()
32 world.create_regions() 45 self.world.create_regions()
33 46
34 if world.options.shuffle_worldports: 47 if self.world.options.shuffle_worldports:
35 port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()} 48 port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()}
36 connect_ports_from_ut(port_pairings, world) 49 connect_ports_from_ut(port_pairings, self.world)
37 50
38 self.state = CollectionState(self.multiworld) 51 self.refresh_state()
39 52
40 def set_checked_locations(self, checked_locations: set[int]): 53 def set_checked_locations(self, checked_locations: set[int]):
41 self.checked_locations = checked_locations.copy() 54 self.checked_locations = checked_locations.copy()
@@ -56,12 +69,38 @@ class Tracker:
56 self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id), 69 self.state.collect(Lingo2Item(Lingo2World.static_logic.item_id_to_name.get(item_id),
57 ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True) 70 ItemClassification.progression, item_id, PLAYER_NUM), prevent_sweep=True)
58 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
59 self.state.sweep_for_advancements() 93 self.state.sweep_for_advancements()
60 94
61 self.accessible_locations = set() 95 self.accessible_locations = set()
96 self.accessible_worldports = set()
62 97
63 for region in self.state.reachable_regions[PLAYER_NUM]: 98 for region in self.state.reachable_regions[PLAYER_NUM]:
64 for location in region.locations: 99 for location in region.locations:
65 if location.address not in self.checked_locations and location.access_rule(self.state): 100 if location.access_rule(self.state):
66 if location.address is not None: 101 if location.address is not None:
67 self.accessible_locations.add(location.address) 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)