summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-09-02 14:09:35 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-09-02 14:09:35 -0400
commit7f82beb120e222ace6c258fc3982b5988f9ae070 (patch)
tree6a9fdf65badf1ed5e7d26b9a07bf0b0e840ca10e
parent7f5f14ddb5a67e1ccfdc7aa3d68d829473d0b745 (diff)
downloadlingo2-archipelago-7f82beb120e222ace6c258fc3982b5988f9ae070.tar.gz
lingo2-archipelago-7f82beb120e222ace6c258fc3982b5988f9ae070.tar.bz2
lingo2-archipelago-7f82beb120e222ace6c258fc3982b5988f9ae070.zip
Added keyholder sanity
-rw-r--r--apworld/__init__.py4
-rw-r--r--apworld/options.py10
-rw-r--r--apworld/player_logic.py9
-rw-r--r--apworld/static_logic.py22
-rw-r--r--data/ids.yaml60
-rw-r--r--data/maps/control_center/rooms/Main Area.txtpb4
-rw-r--r--data/maps/daedalus/rooms/C Keyholder.txtpb1
-rw-r--r--data/maps/daedalus/rooms/D Keyholder.txtpb1
-rw-r--r--data/maps/daedalus/rooms/F Keyholder.txtpb1
-rw-r--r--data/maps/daedalus/rooms/Number Paintings Area.txtpb1
-rw-r--r--data/maps/daedalus/rooms/Outside House.txtpb1
-rw-r--r--data/maps/four_rooms/rooms/Keyholder Room.txtpb1
-rw-r--r--data/maps/the_congruent/rooms/T Keyholder.txtpb1
-rw-r--r--data/maps/the_extravagant/rooms/X Plus.txtpb1
-rw-r--r--data/maps/the_gallery/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_great/rooms/North Landscape.txtpb1
-rw-r--r--data/maps/the_hive/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_jubilant/rooms/Side Area.txtpb1
-rw-r--r--data/maps/the_nuanced/rooms/Main Room.txtpb1
-rw-r--r--data/maps/the_parthenon/rooms/U Keyholder.txtpb1
-rw-r--r--data/maps/the_partial/rooms/Obverse Side.txtpb1
-rw-r--r--data/maps/the_quiet/rooms/Keyholder Room.txtpb1
-rw-r--r--data/maps/the_shop/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_talented/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_tenacious/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_unkempt/rooms/Main Area.txtpb1
-rw-r--r--data/maps/the_unkempt/rooms/V Keyholder.txtpb1
-rw-r--r--data/maps/the_unkempt/rooms/W Keyholder.txtpb1
-rw-r--r--proto/data.proto2
-rw-r--r--proto/human.proto8
-rw-r--r--tools/assign_ids/main.cpp23
-rw-r--r--tools/datapacker/main.cpp12
-rw-r--r--tools/util/ids_yaml_format.cpp15
33 files changed, 183 insertions, 8 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index 4e5777a..1f1e6fe 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -65,7 +65,9 @@ class Lingo2World(World):
65 65
66 def fill_slot_data(self): 66 def fill_slot_data(self):
67 slot_options = [ 67 slot_options = [
68 "victory_condition", "shuffle_doors", 68 "keyholder_sanity",
69 "shuffle_doors",
70 "victory_condition",
69 ] 71 ]
70 72
71 slot_data = { 73 slot_data = {
diff --git a/apworld/options.py b/apworld/options.py index d984beb..dacbc46 100644 --- a/apworld/options.py +++ b/apworld/options.py
@@ -8,6 +8,15 @@ class ShuffleDoors(Toggle):
8 display_name = "Shuffle Doors" 8 display_name = "Shuffle Doors"
9 9
10 10
11class KeyholderSanity(Toggle):
12 """
13 If enabled, 26 locations will be created for placing each key into its respective Green Ending keyholder.
14
15 NOTE: This does not apply to the two disappearing keyholders in The Congruent, as they are not part of Green Ending.
16 """
17 display_name = "Keyholder Sanity"
18
19
11class VictoryCondition(Choice): 20class VictoryCondition(Choice):
12 """Victory condition.""" 21 """Victory condition."""
13 display_name = "Victory Condition" 22 display_name = "Victory Condition"
@@ -29,4 +38,5 @@ class VictoryCondition(Choice):
29@dataclass 38@dataclass
30class Lingo2Options(PerGameCommonOptions): 39class Lingo2Options(PerGameCommonOptions):
31 shuffle_doors: ShuffleDoors 40 shuffle_doors: ShuffleDoors
41 keyholder_sanity: KeyholderSanity
32 victory_condition: VictoryCondition 42 victory_condition: VictoryCondition
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index e08f644..dc1bdf0 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -166,6 +166,15 @@ class Lingo2PlayerLogic:
166 166
167 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = item_name 167 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = item_name
168 168
169 if self.world.options.keyholder_sanity:
170 for keyholder in world.static_logic.objects.keyholders:
171 if keyholder.HasField("key"):
172 reqs = AccessRequirements()
173 reqs.letters[keyholder.key.upper()] = 1
174
175 self.locations_by_room.setdefault(keyholder.room_id, []).append(PlayerLocation(keyholder.ap_id,
176 reqs))
177
169 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: 178 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements:
170 if answer is None: 179 if answer is None:
171 if panel_id not in self.panel_reqs: 180 if panel_id not in self.panel_reqs:
diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 0613474..a945bc0 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py
@@ -44,6 +44,11 @@ class Lingo2StaticLogic:
44 for progressive in self.objects.progressives: 44 for progressive in self.objects.progressives:
45 self.item_id_to_name[progressive.ap_id] = progressive.name 45 self.item_id_to_name[progressive.ap_id] = progressive.name
46 46
47 for keyholder in self.objects.keyholders:
48 if keyholder.HasField("key"):
49 location_name = f"{self.get_room_object_location_prefix(keyholder)} - {keyholder.key.upper()} Keyholder"
50 self.location_id_to_name[keyholder.ap_id] = location_name
51
47 self.item_id_to_name[self.objects.special_ids["Nothing"]] = "Nothing" 52 self.item_id_to_name[self.objects.special_ids["Nothing"]] = "Nothing"
48 53
49 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} 54 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()}
@@ -57,13 +62,7 @@ class Lingo2StaticLogic:
57 return self.get_door_item_name(door_id) 62 return self.get_door_item_name(door_id)
58 63
59 def get_door_location_name(self, door: data_pb2.Door) -> str: 64 def get_door_location_name(self, door: data_pb2.Door) -> str:
60 game_map = self.objects.maps[door.map_id] 65 map_part = self.get_room_object_location_prefix(door)
61 room = self.objects.rooms[door.room_id]
62
63 if room.HasField("panel_display_name"):
64 map_part = f"{game_map.display_name} ({room.panel_display_name})"
65 else:
66 map_part = game_map.display_name
67 66
68 if door.HasField("location_name"): 67 if door.HasField("location_name"):
69 return f"{map_part} - {door.location_name}" 68 return f"{map_part} - {door.location_name}"
@@ -129,3 +128,12 @@ class Lingo2StaticLogic:
129 128
130 def get_room_object_map_name(self, obj) -> str: 129 def get_room_object_map_name(self, obj) -> str:
131 return self.get_map_object_map_name(self.objects.rooms[obj.room_id]) 130 return self.get_map_object_map_name(self.objects.rooms[obj.room_id])
131
132 def get_room_object_location_prefix(self, obj) -> str:
133 room = self.objects.rooms[obj.room_id]
134 game_map = self.objects.maps[room.map_id]
135
136 if room.HasField("panel_display_name"):
137 return f"{game_map.display_name} ({room.panel_display_name})"
138 else:
139 return game_map.display_name
diff --git a/data/ids.yaml b/data/ids.yaml index 3aff6ea..b66d6a9 100644 --- a/data/ids.yaml +++ b/data/ids.yaml
@@ -20,6 +20,11 @@ maps:
20 panels: 20 panels:
21 COLOR: 2726 21 COLOR: 2726
22 Letters: 2727 22 Letters: 2727
23 keyholders:
24 1: 2760
25 2: 2761
26 3: 2762
27 4: 2763
23 Partial Entrance: 28 Partial Entrance:
24 panels: 29 panels:
25 PARTIAL: 2729 30 PARTIAL: 2729
@@ -155,6 +160,9 @@ maps:
155 Brown Smiley: 160 Brown Smiley:
156 panels: 161 panels:
157 OTHERS: 1667 162 OTHERS: 1667
163 C Keyholder:
164 keyholders:
165 C: 2755
158 Castle: 166 Castle:
159 panels: 167 panels:
160 FIVE (Blue): 1673 168 FIVE (Blue): 1673
@@ -265,6 +273,9 @@ maps:
265 SUMMER: 1754 273 SUMMER: 1754
266 WORD: 1753 274 WORD: 1753
267 WORDWORD: 1761 275 WORDWORD: 1761
276 D Keyholder:
277 keyholders:
278 D: 2759
268 Dark Light Exit: 279 Dark Light Exit:
269 panels: 280 panels:
270 GASKET: 1763 281 GASKET: 1763
@@ -287,6 +298,9 @@ maps:
287 Eye Painting: 298 Eye Painting:
288 panels: 299 panels:
289 REVILED: 1777 300 REVILED: 1777
301 F Keyholder:
302 keyholders:
303 F: 2756
290 F2 Room: 304 F2 Room:
291 panels: 305 panels:
292 CAST: 1782 306 CAST: 1782
@@ -480,6 +494,8 @@ maps:
480 panels: 494 panels:
481 GOING: 1934 495 GOING: 1934
482 TURN: 1935 496 TURN: 1935
497 keyholders:
498 G: 2757
483 Nursery: 499 Nursery:
484 panels: 500 panels:
485 "?": 1937 501 "?": 1937
@@ -547,6 +563,8 @@ maps:
547 WALLS: 1986 563 WALLS: 1986
548 WHISPER: 1978 564 WHISPER: 1978
549 WING: 1979 565 WING: 1979
566 keyholders:
567 H: 2758
550 Outside Magic Room: 568 Outside Magic Room:
551 panels: 569 panels:
552 WIZARD: 1988 570 WIZARD: 1988
@@ -1160,6 +1178,9 @@ maps:
1160 SWAY: 24 1178 SWAY: 24
1161 TERROR: 20 1179 TERROR: 20
1162 TURN: 22 1180 TURN: 22
1181 Keyholder Room:
1182 keyholders:
1183 A: 2773
1163 Synonyms Room: 1184 Synonyms Room:
1164 panels: 1185 panels:
1165 ADORE: 26 1186 ADORE: 26
@@ -1452,6 +1473,9 @@ maps:
1452 panels: 1473 panels:
1453 CIVIL: 216 1474 CIVIL: 216
1454 CRABS: 217 1475 CRABS: 217
1476 T Keyholder:
1477 keyholders:
1478 T: 2754
1455 doors: 1479 doors:
1456 C Keyholder Blocker: 176 1480 C Keyholder Blocker: 176
1457 C2 Door: 177 1481 C2 Door: 177
@@ -1760,6 +1784,8 @@ maps:
1760 X Plus: 1784 X Plus:
1761 panels: 1785 panels:
1762 ROSE: 405 1786 ROSE: 405
1787 keyholders:
1788 M: 2766
1763 X Plus Middle Leg: 1789 X Plus Middle Leg:
1764 panels: 1790 panels:
1765 COLONY: 403 1791 COLONY: 403
@@ -1789,6 +1815,9 @@ maps:
1789 Daedalus Extension: 1815 Daedalus Extension:
1790 panels: 1816 panels:
1791 WHERE: 433 1817 WHERE: 433
1818 Main Area:
1819 keyholders:
1820 P: 2765
1792 doors: 1821 doors:
1793 Ancient Painting: 428 1822 Ancient Painting: 428
1794 Between Painting: 414 1823 Between Painting: 414
@@ -1973,6 +2002,8 @@ maps:
1973 LAUGH FINISHED: 573 2002 LAUGH FINISHED: 573
1974 PLANTS: 570 2003 PLANTS: 570
1975 WEATHER: 568 2004 WEATHER: 568
2005 keyholders:
2006 X: 2770
1976 Outside Jail: 2007 Outside Jail:
1977 panels: 2008 panels:
1978 GUT: 575 2009 GUT: 575
@@ -2120,6 +2151,8 @@ maps:
2120 WAS: 631 2151 WAS: 631
2121 WINGS: 662 2152 WINGS: 662
2122 YELL: 636 2153 YELL: 636
2154 keyholders:
2155 B: 2769
2123 Mastery Room: 2156 Mastery Room:
2124 masteries: 2157 masteries:
2125 MASTERY: 666 2158 MASTERY: 666
@@ -2186,6 +2219,8 @@ maps:
2186 FLASHBACK: 705 2219 FLASHBACK: 705
2187 PUSH: 703 2220 PUSH: 703
2188 PUSHBACK: 702 2221 PUSHBACK: 702
2222 keyholders:
2223 J: 2772
2189 doors: 2224 doors:
2190 Side Door: 687 2225 Side Door: 687
2191 the_keen: 2226 the_keen:
@@ -2297,6 +2332,8 @@ maps:
2297 NAY: 774 2332 NAY: 774
2298 NIGH: 781 2333 NIGH: 781
2299 TORE: 787 2334 TORE: 787
2335 keyholders:
2336 S: 2767
2300 doors: 2337 doors:
2301 Left Room Puzzles: 763 2338 Left Room Puzzles: 763
2302 Main Room Door: 2750 2339 Main Room Door: 2750
@@ -2404,6 +2441,9 @@ maps:
2404 CLEOPATRA: 859 2441 CLEOPATRA: 859
2405 NAPOLEON: 860 2442 NAPOLEON: 860
2406 XERXES: 857 2443 XERXES: 857
2444 U Keyholder:
2445 keyholders:
2446 U: 2777
2407 doors: 2447 doors:
2408 K2 Door: 852 2448 K2 Door: 852
2409 the_partial: 2449 the_partial:
@@ -2427,6 +2467,8 @@ maps:
2427 TON: 878 2467 TON: 878
2428 TURN: 875 2468 TURN: 875
2429 UP: 870 2469 UP: 870
2470 keyholders:
2471 L: 2771
2430 Reverse Side: 2472 Reverse Side:
2431 panels: 2473 panels:
2432 BRO: 884 2474 BRO: 884
@@ -2545,6 +2587,9 @@ maps:
2545 Turtle Entrance: 891 2587 Turtle Entrance: 891
2546 the_quiet: 2588 the_quiet:
2547 rooms: 2589 rooms:
2590 Keyholder Room:
2591 keyholders:
2592 Q: 2778
2548 Main Area: 2593 Main Area:
2549 panels: 2594 panels:
2550 BEE: 979 2595 BEE: 979
@@ -2791,6 +2836,8 @@ maps:
2791 STIM: 1148 2836 STIM: 1148
2792 STONE: 1142 2837 STONE: 1142
2793 TADPOLES: 1159 2838 TADPOLES: 1159
2839 keyholders:
2840 N: 2779
2794 doors: 2841 doors:
2795 Books Puzzles: 1136 2842 Books Puzzles: 1136
2796 Games Puzzles: 1137 2843 Games Puzzles: 1137
@@ -3145,6 +3192,8 @@ maps:
3145 SWINE (Brown): 2446 3192 SWINE (Brown): 2446
3146 WIFE (Black): 2440 3193 WIFE (Black): 2440
3147 WIFE (Brown): 2447 3194 WIFE (Brown): 2447
3195 keyholders:
3196 Y: 2764
3148 doors: 3197 doors:
3149 Black Side Panels: 2427 3198 Black Side Panels: 2427
3150 Brown Side Panels: 2428 3199 Brown Side Panels: 2428
@@ -3157,6 +3206,9 @@ maps:
3157 Control Center Entrance: 3206 Control Center Entrance:
3158 panels: 3207 panels:
3159 ZERO: 2455 3208 ZERO: 2455
3209 Main Area:
3210 keyholders:
3211 K: 2768
3160 Mastery: 3212 Mastery:
3161 masteries: 3213 masteries:
3162 MASTERY: 2456 3214 MASTERY: 2456
@@ -3361,6 +3413,8 @@ maps:
3361 WAYS: 2621 3413 WAYS: 2621
3362 WHILE: 2613 3414 WHILE: 2613
3363 ZOO: 2615 3415 ZOO: 2615
3416 keyholders:
3417 I: 2775
3364 Middle Room: 3418 Middle Room:
3365 panels: 3419 panels:
3366 FELLOW: 2624 3420 FELLOW: 2624
@@ -3408,6 +3462,12 @@ maps:
3408 UNINTERESTED: 2650 3462 UNINTERESTED: 2650
3409 UNIRONIC: 2656 3463 UNIRONIC: 2656
3410 UNLUCKY: 2654 3464 UNLUCKY: 2654
3465 V Keyholder:
3466 keyholders:
3467 V: 2776
3468 W Keyholder:
3469 keyholders:
3470 W: 2774
3411 doors: 3471 doors:
3412 Cog Rhino Hug Rug: 2586 3472 Cog Rhino Hug Rug: 2586
3413 Control Center Orange Door: 2582 3473 Control Center Orange Door: 2582
diff --git a/data/maps/control_center/rooms/Main Area.txtpb b/data/maps/control_center/rooms/Main Area.txtpb index 44b0f79..bf81e26 100644 --- a/data/maps/control_center/rooms/Main Area.txtpb +++ b/data/maps/control_center/rooms/Main Area.txtpb
@@ -30,18 +30,22 @@ panels {
30keyholders { 30keyholders {
31 name: "1" 31 name: "1"
32 path: "Components/KeyHolders/keyHolder" 32 path: "Components/KeyHolders/keyHolder"
33 key: "z"
33} 34}
34keyholders { 35keyholders {
35 name: "2" 36 name: "2"
36 path: "Components/KeyHolders/keyHolder2" 37 path: "Components/KeyHolders/keyHolder2"
38 key: "e"
37} 39}
38keyholders { 40keyholders {
39 name: "3" 41 name: "3"
40 path: "Components/KeyHolders/keyHolder3" 42 path: "Components/KeyHolders/keyHolder3"
43 key: "r"
41} 44}
42keyholders { 45keyholders {
43 name: "4" 46 name: "4"
44 path: "Components/KeyHolders/keyHolder4" 47 path: "Components/KeyHolders/keyHolder4"
48 key: "o"
45} 49}
46ports { 50ports {
47 name: "RIGHT" 51 name: "RIGHT"
diff --git a/data/maps/daedalus/rooms/C Keyholder.txtpb b/data/maps/daedalus/rooms/C Keyholder.txtpb index cc8548c..ef10a90 100644 --- a/data/maps/daedalus/rooms/C Keyholder.txtpb +++ b/data/maps/daedalus/rooms/C Keyholder.txtpb
@@ -3,4 +3,5 @@ panel_display_name: "North Area"
3keyholders { 3keyholders {
4 name: "C" 4 name: "C"
5 path: "Components/KeyHolders/keyHolderC" 5 path: "Components/KeyHolders/keyHolderC"
6 key: "c"
6} 7}
diff --git a/data/maps/daedalus/rooms/D Keyholder.txtpb b/data/maps/daedalus/rooms/D Keyholder.txtpb index 2521ab2..a5852be 100644 --- a/data/maps/daedalus/rooms/D Keyholder.txtpb +++ b/data/maps/daedalus/rooms/D Keyholder.txtpb
@@ -3,4 +3,5 @@ panel_display_name: "Plum Room"
3keyholders { 3keyholders {
4 name: "D" 4 name: "D"
5 path: "Components/KeyHolders/keyHolderD" 5 path: "Components/KeyHolders/keyHolderD"
6 key: "d"
6} 7}
diff --git a/data/maps/daedalus/rooms/F Keyholder.txtpb b/data/maps/daedalus/rooms/F Keyholder.txtpb index 662f76d..b424c6a 100644 --- a/data/maps/daedalus/rooms/F Keyholder.txtpb +++ b/data/maps/daedalus/rooms/F Keyholder.txtpb
@@ -3,4 +3,5 @@ panel_display_name: "West Area"
3keyholders { 3keyholders {
4 name: "F" 4 name: "F"
5 path: "Components/KeyHolders/keyHolderF" 5 path: "Components/KeyHolders/keyHolderF"
6 key: "f"
6} 7}
diff --git a/data/maps/daedalus/rooms/Number Paintings Area.txtpb b/data/maps/daedalus/rooms/Number Paintings Area.txtpb index 15c8875..c89bfcf 100644 --- a/data/maps/daedalus/rooms/Number Paintings Area.txtpb +++ b/data/maps/daedalus/rooms/Number Paintings Area.txtpb
@@ -17,6 +17,7 @@ panels {
17keyholders { 17keyholders {
18 name: "G" 18 name: "G"
19 path: "Components/KeyHolders/keyHolderG" 19 path: "Components/KeyHolders/keyHolderG"
20 key: "g"
20} 21}
21paintings { 22paintings {
22 name: "WON" 23 name: "WON"
diff --git a/data/maps/daedalus/rooms/Outside House.txtpb b/data/maps/daedalus/rooms/Outside House.txtpb index fd3f5f0..fed9dda 100644 --- a/data/maps/daedalus/rooms/Outside House.txtpb +++ b/data/maps/daedalus/rooms/Outside House.txtpb
@@ -75,6 +75,7 @@ panels {
75keyholders { 75keyholders {
76 name: "H" 76 name: "H"
77 path: "Components/KeyHolders/keyHolderH" 77 path: "Components/KeyHolders/keyHolderH"
78 key: "h"
78} 79}
79paintings { 80paintings {
80 name: "CASTLE2" 81 name: "CASTLE2"
diff --git a/data/maps/four_rooms/rooms/Keyholder Room.txtpb b/data/maps/four_rooms/rooms/Keyholder Room.txtpb index e7c7fa6..13c3dce 100644 --- a/data/maps/four_rooms/rooms/Keyholder Room.txtpb +++ b/data/maps/four_rooms/rooms/Keyholder Room.txtpb
@@ -2,4 +2,5 @@ name: "Keyholder Room"
2keyholders { 2keyholders {
3 name: "A" 3 name: "A"
4 path: "Components/KeyHolders/keyHolderA" 4 path: "Components/KeyHolders/keyHolderA"
5 key: "a"
5} 6}
diff --git a/data/maps/the_congruent/rooms/T Keyholder.txtpb b/data/maps/the_congruent/rooms/T Keyholder.txtpb index 360b030..143ea53 100644 --- a/data/maps/the_congruent/rooms/T Keyholder.txtpb +++ b/data/maps/the_congruent/rooms/T Keyholder.txtpb
@@ -2,4 +2,5 @@ name: "T Keyholder"
2keyholders { 2keyholders {
3 name: "T" 3 name: "T"
4 path: "Components/KeyHolders/keyHolderT" 4 path: "Components/KeyHolders/keyHolderT"
5 key: "t"
5} 6}
diff --git a/data/maps/the_extravagant/rooms/X Plus.txtpb b/data/maps/the_extravagant/rooms/X Plus.txtpb index 89b6da7..a1c4b9d 100644 --- a/data/maps/the_extravagant/rooms/X Plus.txtpb +++ b/data/maps/the_extravagant/rooms/X Plus.txtpb
@@ -23,4 +23,5 @@ paintings {
23keyholders { 23keyholders {
24 name: "M" 24 name: "M"
25 path: "Components/KeyHolders/keyHolderM" 25 path: "Components/KeyHolders/keyHolderM"
26 key: "m"
26} 27}
diff --git a/data/maps/the_gallery/rooms/Main Area.txtpb b/data/maps/the_gallery/rooms/Main Area.txtpb index 5ba6b25..bc1606d 100644 --- a/data/maps/the_gallery/rooms/Main Area.txtpb +++ b/data/maps/the_gallery/rooms/Main Area.txtpb
@@ -2,6 +2,7 @@ name: "Main Area"
2keyholders { 2keyholders {
3 name: "P" 3 name: "P"
4 path: "Components/KeyHolders/keyHolderP" 4 path: "Components/KeyHolders/keyHolderP"
5 key: "p"
5} 6}
6paintings { 7paintings {
7 name: "OWL" 8 name: "OWL"
diff --git a/data/maps/the_great/rooms/North Landscape.txtpb b/data/maps/the_great/rooms/North Landscape.txtpb index f0fde77..fb11c42 100644 --- a/data/maps/the_great/rooms/North Landscape.txtpb +++ b/data/maps/the_great/rooms/North Landscape.txtpb
@@ -52,6 +52,7 @@ panels {
52keyholders { 52keyholders {
53 name: "X" 53 name: "X"
54 path: "Components/KeyHolders/keyHolderX" 54 path: "Components/KeyHolders/keyHolderX"
55 key: "x"
55} 56}
56ports { 57ports {
57 name: "INVISIBLE" 58 name: "INVISIBLE"
diff --git a/data/maps/the_hive/rooms/Main Area.txtpb b/data/maps/the_hive/rooms/Main Area.txtpb index 0f73682..013390a 100644 --- a/data/maps/the_hive/rooms/Main Area.txtpb +++ b/data/maps/the_hive/rooms/Main Area.txtpb
@@ -268,6 +268,7 @@ panels {
268keyholders { 268keyholders {
269 name: "B" 269 name: "B"
270 path: "Components/KeyHolders/keyHolderB" 270 path: "Components/KeyHolders/keyHolderB"
271 key: "b"
271} 272}
272ports { 273ports {
273 name: "DAED1" 274 name: "DAED1"
diff --git a/data/maps/the_jubilant/rooms/Side Area.txtpb b/data/maps/the_jubilant/rooms/Side Area.txtpb index e924762..807f044 100644 --- a/data/maps/the_jubilant/rooms/Side Area.txtpb +++ b/data/maps/the_jubilant/rooms/Side Area.txtpb
@@ -38,4 +38,5 @@ panels {
38keyholders { 38keyholders {
39 name: "J" 39 name: "J"
40 path: "Components/KeyHolders/keyHolderJ" 40 path: "Components/KeyHolders/keyHolderJ"
41 key: "j"
41} 42}
diff --git a/data/maps/the_nuanced/rooms/Main Room.txtpb b/data/maps/the_nuanced/rooms/Main Room.txtpb index 8da3b5f..da89bd8 100644 --- a/data/maps/the_nuanced/rooms/Main Room.txtpb +++ b/data/maps/the_nuanced/rooms/Main Room.txtpb
@@ -112,4 +112,5 @@ ports {
112keyholders { 112keyholders {
113 name: "S" 113 name: "S"
114 path: "Components/KeyHolders/keyHolderS" 114 path: "Components/KeyHolders/keyHolderS"
115 key: "s"
115} 116}
diff --git a/data/maps/the_parthenon/rooms/U Keyholder.txtpb b/data/maps/the_parthenon/rooms/U Keyholder.txtpb index 8248df8..0a5c31b 100644 --- a/data/maps/the_parthenon/rooms/U Keyholder.txtpb +++ b/data/maps/the_parthenon/rooms/U Keyholder.txtpb
@@ -2,4 +2,5 @@ name: "U Keyholder"
2keyholders { 2keyholders {
3 name: "U" 3 name: "U"
4 path: "Components/KeyHolders/keyHolderU" 4 path: "Components/KeyHolders/keyHolderU"
5 key: "u"
5} 6}
diff --git a/data/maps/the_partial/rooms/Obverse Side.txtpb b/data/maps/the_partial/rooms/Obverse Side.txtpb index 75cd9bb..c0ce04b 100644 --- a/data/maps/the_partial/rooms/Obverse Side.txtpb +++ b/data/maps/the_partial/rooms/Obverse Side.txtpb
@@ -106,6 +106,7 @@ keyholders {
106 # This is one of the ones that's misnamed within the game. 106 # This is one of the ones that's misnamed within the game.
107 name: "L" 107 name: "L"
108 path: "Components/KeyHolders/keyHolderI" 108 path: "Components/KeyHolders/keyHolderI"
109 key: "l"
109} 110}
110paintings { 111paintings {
111 name: "F" 112 name: "F"
diff --git a/data/maps/the_quiet/rooms/Keyholder Room.txtpb b/data/maps/the_quiet/rooms/Keyholder Room.txtpb index d0f2677..d3cab73 100644 --- a/data/maps/the_quiet/rooms/Keyholder Room.txtpb +++ b/data/maps/the_quiet/rooms/Keyholder Room.txtpb
@@ -2,4 +2,5 @@ name: "Keyholder Room"
2keyholders { 2keyholders {
3 name: "Q" 3 name: "Q"
4 path: "Components/KeyHolders/keyHolderQ" 4 path: "Components/KeyHolders/keyHolderQ"
5 key: "q"
5} 6}
diff --git a/data/maps/the_shop/rooms/Main Area.txtpb b/data/maps/the_shop/rooms/Main Area.txtpb index d45e0f0..db93fe1 100644 --- a/data/maps/the_shop/rooms/Main Area.txtpb +++ b/data/maps/the_shop/rooms/Main Area.txtpb
@@ -160,4 +160,5 @@ ports {
160keyholders { 160keyholders {
161 name: "N" 161 name: "N"
162 path: "Components/KeyHolders/keyHolderN" 162 path: "Components/KeyHolders/keyHolderN"
163 key: "n"
163} 164}
diff --git a/data/maps/the_talented/rooms/Main Area.txtpb b/data/maps/the_talented/rooms/Main Area.txtpb index cc3e222..f99be48 100644 --- a/data/maps/the_talented/rooms/Main Area.txtpb +++ b/data/maps/the_talented/rooms/Main Area.txtpb
@@ -107,6 +107,7 @@ panels {
107keyholders { 107keyholders {
108 name: "Y" 108 name: "Y"
109 path: "Components/KeyHolders/keyHolderY" 109 path: "Components/KeyHolders/keyHolderY"
110 key: "y"
110} 111}
111ports { 112ports {
112 name: "GREAT" 113 name: "GREAT"
diff --git a/data/maps/the_tenacious/rooms/Main Area.txtpb b/data/maps/the_tenacious/rooms/Main Area.txtpb index 8190827..18356e7 100644 --- a/data/maps/the_tenacious/rooms/Main Area.txtpb +++ b/data/maps/the_tenacious/rooms/Main Area.txtpb
@@ -2,4 +2,5 @@ name: "Main Area"
2keyholders { 2keyholders {
3 name: "K" 3 name: "K"
4 path: "Components/KeyHolders/keyHolderK" 4 path: "Components/KeyHolders/keyHolderK"
5 key: "k"
5} 6}
diff --git a/data/maps/the_unkempt/rooms/Main Area.txtpb b/data/maps/the_unkempt/rooms/Main Area.txtpb index ed3ce21..b5d29c4 100644 --- a/data/maps/the_unkempt/rooms/Main Area.txtpb +++ b/data/maps/the_unkempt/rooms/Main Area.txtpb
@@ -212,6 +212,7 @@ panels {
212keyholders { 212keyholders {
213 name: "I" 213 name: "I"
214 path: "Components/KeyHolders/keyHolderL" 214 path: "Components/KeyHolders/keyHolderL"
215 key: "i"
215} 216}
216ports { 217ports {
217 name: "GREAT" 218 name: "GREAT"
diff --git a/data/maps/the_unkempt/rooms/V Keyholder.txtpb b/data/maps/the_unkempt/rooms/V Keyholder.txtpb index 0906b2e..8a4941d 100644 --- a/data/maps/the_unkempt/rooms/V Keyholder.txtpb +++ b/data/maps/the_unkempt/rooms/V Keyholder.txtpb
@@ -2,4 +2,5 @@ name: "V Keyholder"
2keyholders { 2keyholders {
3 name: "V" 3 name: "V"
4 path: "Components/KeyHolders/keyHolderV" 4 path: "Components/KeyHolders/keyHolderV"
5 key: "v"
5} 6}
diff --git a/data/maps/the_unkempt/rooms/W Keyholder.txtpb b/data/maps/the_unkempt/rooms/W Keyholder.txtpb index ae367b2..e16f997 100644 --- a/data/maps/the_unkempt/rooms/W Keyholder.txtpb +++ b/data/maps/the_unkempt/rooms/W Keyholder.txtpb
@@ -2,4 +2,5 @@ name: "W Keyholder"
2keyholders { 2keyholders {
3 name: "W" 3 name: "W"
4 path: "Components/KeyHolders/keyHolderW" 4 path: "Components/KeyHolders/keyHolderW"
5 key: "w"
5} 6}
diff --git a/proto/data.proto b/proto/data.proto index b627e83..9cdf5fd 100644 --- a/proto/data.proto +++ b/proto/data.proto
@@ -162,10 +162,12 @@ message Port {
162 162
163message KeyholderData { 163message KeyholderData {
164 optional uint64 id = 1; 164 optional uint64 id = 1;
165 optional uint64 ap_id = 6;
165 optional uint64 room_id = 2; 166 optional uint64 room_id = 2;
166 167
167 optional string name = 3; 168 optional string name = 3;
168 optional string path = 4; 169 optional string path = 4;
170 optional string key = 5;
169} 171}
170 172
171message Letter { 173message Letter {
diff --git a/proto/human.proto b/proto/human.proto index 8d882da..e0378cc 100644 --- a/proto/human.proto +++ b/proto/human.proto
@@ -142,6 +142,13 @@ message HumanPort {
142message HumanKeyholder { 142message HumanKeyholder {
143 optional string name = 1; 143 optional string name = 1;
144 optional string path = 2; 144 optional string path = 2;
145
146 // If this is set, the keyholder will become a location when keyholder shuffle
147 // is enabled. This value specifies the key that is required to clear the
148 // location. It should be the same as the key needed for Green Ending. The
149 // only cases when this shouldn't be set is the two disappearing keyholders in
150 // The Congruent.
151 optional string key = 3;
145} 152}
146 153
147message HumanLetter { 154message HumanLetter {
@@ -196,6 +203,7 @@ message IdMappings {
196 message RoomIds { 203 message RoomIds {
197 map<string, uint64> panels = 1; 204 map<string, uint64> panels = 1;
198 map<string, uint64> masteries = 2; 205 map<string, uint64> masteries = 2;
206 map<string, uint64> keyholders = 3;
199 } 207 }
200 208
201 message MapIds { 209 message MapIds {
diff --git a/tools/assign_ids/main.cpp b/tools/assign_ids/main.cpp index 3a2f347..6eb41e3 100644 --- a/tools/assign_ids/main.cpp +++ b/tools/assign_ids/main.cpp
@@ -183,6 +183,29 @@ class AssignIds {
183 endings[h_ending.name()] = next_id_++; 183 endings[h_ending.name()] = next_id_++;
184 } 184 }
185 } 185 }
186
187 for (const HumanKeyholder& h_keyholder : h_room.keyholders()) {
188 if (!h_keyholder.has_key()) {
189 continue;
190 }
191
192 if (!id_mappings_.maps().contains(current_map_name) ||
193 !id_mappings_.maps()
194 .at(current_map_name)
195 .rooms()
196 .contains(h_room.name()) ||
197 !id_mappings_.maps()
198 .at(current_map_name)
199 .rooms()
200 .at(h_room.name())
201 .keyholders()
202 .contains(h_keyholder.name())) {
203 auto& maps = *id_mappings_.mutable_maps();
204 auto& rooms = *maps[current_map_name].mutable_rooms();
205 auto& keyholders = *rooms[h_room.name()].mutable_keyholders();
206 keyholders[h_keyholder.name()] = next_id_++;
207 }
208 }
186 } 209 }
187 210
188 void ProcessProgressivesFile(std::filesystem::path path) { 211 void ProcessProgressivesFile(std::filesystem::path path) {
diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index 5ed82cc..d7e0b69 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp
@@ -293,6 +293,10 @@ class DataPacker {
293 293
294 keyholder.set_path(h_keyholder.path()); 294 keyholder.set_path(h_keyholder.path());
295 295
296 if (h_keyholder.has_key()) {
297 keyholder.set_key(h_keyholder.key());
298 }
299
296 return keyholder_id; 300 return keyholder_id;
297 } 301 }
298 302
@@ -592,6 +596,14 @@ class DataPacker {
592 .mutable_masteries(mastery_id) 596 .mutable_masteries(mastery_id)
593 ->set_ap_id(ap_id); 597 ->set_ap_id(ap_id);
594 } 598 }
599
600 for (const auto& [keyholder_name, ap_id] : room.keyholders()) {
601 uint64_t keyholder_id = container_.FindOrAddKeyholder(
602 map_name, room_name, keyholder_name, std::nullopt, std::nullopt);
603 container_.all_objects()
604 .mutable_keyholders(keyholder_id)
605 ->set_ap_id(ap_id);
606 }
595 } 607 }
596 } 608 }
597 609
diff --git a/tools/util/ids_yaml_format.cpp b/tools/util/ids_yaml_format.cpp index ae62073..67c21d6 100644 --- a/tools/util/ids_yaml_format.cpp +++ b/tools/util/ids_yaml_format.cpp
@@ -56,6 +56,14 @@ IdMappings ReadIdsFromYaml(const std::string& filename) {
56 mastery_it.second.as<uint64_t>(); 56 mastery_it.second.as<uint64_t>();
57 } 57 }
58 } 58 }
59
60 if (room_it.second["keyholders"]) {
61 for (const auto& keyholder_it : room_it.second["keyholders"]) {
62 (*room_ids.mutable_keyholders())[keyholder_it.first
63 .as<std::string>()] =
64 keyholder_it.second.as<uint64_t>();
65 }
66 }
59 } 67 }
60 } 68 }
61 69
@@ -124,6 +132,13 @@ void WriteIdsAsYaml(const IdMappings& ids, const std::string& filename) {
124 mastery_id; 132 mastery_id;
125 }); 133 });
126 134
135 OperateOnSortedMap(room_ids.keyholders(),
136 [&room_node](const std::string& keyholder_name,
137 uint64_t keyholder_id) {
138 room_node["keyholders"][keyholder_name] =
139 keyholder_id;
140 });
141
127 map_node["rooms"][room_name] = std::move(room_node); 142 map_node["rooms"][room_name] = std::move(room_node);
128 }); 143 });
129 144