about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-09-08 12:42:31 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-09-08 12:42:31 -0400
commit6af543ba049e3ba880b113907cd5222b205b8c05 (patch)
tree58a17c86870db0e67e30e20b7349aee69fba4e82
parent215b576cc816f6f378d057b330ef01f02539602a (diff)
downloadlingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.tar.gz
lingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.tar.bz2
lingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.zip
Add cyan door behavior option
-rw-r--r--apworld/__init__.py1
-rw-r--r--apworld/options.py22
-rw-r--r--apworld/player_logic.py36
-rw-r--r--data/door_groups.txtpb52
-rw-r--r--data/ids.yaml1
-rw-r--r--data/maps/the_darkroom/doors.txtpb4
-rw-r--r--data/maps/the_graveyard/doors.txtpb2
-rw-r--r--data/maps/the_parthenon/doors.txtpb7
-rw-r--r--data/maps/the_unkempt/doors.txtpb2
-rw-r--r--data/maps/the_unkempt/rooms/Right Area.txtpb3
-rw-r--r--data/maps/the_unyielding/doors.txtpb3
-rw-r--r--proto/data.proto3
12 files changed, 122 insertions, 14 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index d6a3acb..c45e8b3 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -66,6 +66,7 @@ class Lingo2World(World):
66 66
67 def fill_slot_data(self): 67 def fill_slot_data(self):
68 slot_options = [ 68 slot_options = [
69 "cyan_door_behavior",
69 "daedalus_roof_access", 70 "daedalus_roof_access",
70 "keyholder_sanity", 71 "keyholder_sanity",
71 "shuffle_control_center_colors", 72 "shuffle_control_center_colors",
diff --git a/apworld/options.py b/apworld/options.py index dbf09e7..2197b0f 100644 --- a/apworld/options.py +++ b/apworld/options.py
@@ -48,6 +48,27 @@ class KeyholderSanity(Toggle):
48 display_name = "Keyholder Sanity" 48 display_name = "Keyholder Sanity"
49 49
50 50
51class CyanDoorBehavior(Choice):
52 """
53 Cyan-colored doors usually only open upon unlocking double letters. Some panels also only appear upon unlocking
54 double letters. This option determines how these unlocks should behave.
55
56 - **Collect H2**: In the base game, H2 is the first double letter you are intended to collect, so cyan doors only
57 open when you collect the H2 pickup in The Repetitive. Collecting the actual pickup is still required even with
58 remote letter shuffle enabled.
59 - **Any Double Letter**: Cyan doors will open when you have unlocked any cyan letter on your keyboard. In letter
60 shuffle, this means receiving a cyan letter, not picking up a cyan letter collectable.
61 - **Item**: Cyan doors will be grouped together in a single item.
62
63 Note that some cyan doors are impacted by door shuffle (e.g. the entrance to The Tower). When door shuffle is
64 enabled, these doors won't be affected by the value of this option.
65 """
66 display_name = "Cyan Door Behavior"
67 option_collect_h2 = 0
68 option_any_double_letter = 1
69 option_item = 2
70
71
51class DaedalusRoofAccess(Toggle): 72class DaedalusRoofAccess(Toggle):
52 """ 73 """
53 If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus 74 If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus
@@ -82,5 +103,6 @@ class Lingo2Options(PerGameCommonOptions):
82 shuffle_control_center_colors: ShuffleControlCenterColors 103 shuffle_control_center_colors: ShuffleControlCenterColors
83 shuffle_letters: ShuffleLetters 104 shuffle_letters: ShuffleLetters
84 keyholder_sanity: KeyholderSanity 105 keyholder_sanity: KeyholderSanity
106 cyan_door_behavior: CyanDoorBehavior
85 daedalus_roof_access: DaedalusRoofAccess 107 daedalus_roof_access: DaedalusRoofAccess
86 victory_condition: VictoryCondition 108 victory_condition: VictoryCondition
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index ce9a4e5..317d13b 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -3,7 +3,7 @@ from enum import IntEnum, auto
3from .generated import data_pb2 as data_pb2 3from .generated import data_pb2 as data_pb2
4from typing import TYPE_CHECKING, NamedTuple 4from typing import TYPE_CHECKING, NamedTuple
5 5
6from .options import VictoryCondition, ShuffleLetters 6from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior
7 7
8if TYPE_CHECKING: 8if TYPE_CHECKING:
9 from . import Lingo2World 9 from . import Lingo2World
@@ -124,11 +124,13 @@ class Lingo2PlayerLogic:
124 self.real_items.append(progressive.name) 124 self.real_items.append(progressive.name)
125 125
126 for door_group in world.static_logic.objects.door_groups: 126 for door_group in world.static_logic.objects.door_groups:
127 if door_group.type == data_pb2.DoorGroupType.CONNECTOR and not self.world.options.shuffle_doors: 127 if door_group.type == data_pb2.DoorGroupType.CONNECTOR:
128 continue 128 if not self.world.options.shuffle_doors:
129 129 continue
130 if (door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR and 130 elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR:
131 not self.world.options.shuffle_control_center_colors): 131 if not self.world.options.shuffle_control_center_colors:
132 continue
133 else:
132 continue 134 continue
133 135
134 for door in door_group.doors: 136 for door in door_group.doors:
@@ -157,6 +159,19 @@ class Lingo2PlayerLogic:
157 self.item_by_door[door.id] = (door_item_name, 1) 159 self.item_by_door[door.id] = (door_item_name, 1)
158 self.real_items.append(door_item_name) 160 self.real_items.append(door_item_name)
159 161
162 # We handle cyan_door_behavior = Item after door shuffle, because cyan doors that are impacted by door shuffle
163 # should be exempt from cyan_door_behavior.
164 if world.options.cyan_door_behavior == CyanDoorBehavior.option_item:
165 for door_group in world.static_logic.objects.door_groups:
166 if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS:
167 continue
168
169 for door in door_group.doors:
170 if not door in self.item_by_door:
171 self.item_by_door[door] = (door_group.name, 1)
172
173 self.real_items.append(door_group.name)
174
160 for door in world.static_logic.objects.doors: 175 for door in world.static_logic.objects.doors:
161 if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: 176 if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]:
162 self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, 177 self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id,
@@ -295,12 +310,13 @@ class Lingo2PlayerLogic:
295 self.add_solution_reqs(reqs, door.control_center_color) 310 self.add_solution_reqs(reqs, door.control_center_color)
296 311
297 if door.double_letters: 312 if door.double_letters:
298 if self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla, 313 if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2:
299 ShuffleLetters.option_vanilla_cyan]:
300 reqs.rooms.add("The Repetitive - Main Room") 314 reqs.rooms.add("The Repetitive - Main Room")
301 elif self.world.options.shuffle_letters in [ShuffleLetters.option_progressive, 315 elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_any_double_letter:
302 ShuffleLetters.option_item_cyan]:
303 reqs.cyans = True 316 reqs.cyans = True
317 elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item:
318 # There shouldn't be any locations that are cyan doors.
319 pass
304 320
305 for keyholder_uses in door.keyholders: 321 for keyholder_uses in door.keyholders:
306 key_name = keyholder_uses.key.upper() 322 key_name = keyholder_uses.key.upper()
diff --git a/data/door_groups.txtpb b/data/door_groups.txtpb index ca8ce54..bc8cdf6 100644 --- a/data/door_groups.txtpb +++ b/data/door_groups.txtpb
@@ -82,3 +82,55 @@ door_groups {
82 name: "Digital Entrance" 82 name: "Digital Entrance"
83 } 83 }
84} 84}
85door_groups {
86 name: "Cyan Doors"
87 type: CYAN_DOORS
88 doors {
89 map: "daedalus"
90 name: "Eye Painting"
91 }
92 doors {
93 map: "the_bearer"
94 name: "Butterfly Entrance"
95 }
96 doors {
97 map: "the_darkroom"
98 name: "Double Letter Panel Blockers"
99 }
100 doors {
101 map: "the_entry"
102 name: "Starting Room West Wall North Door"
103 }
104 doors {
105 map: "the_entry"
106 name: "Flipped Pyramid Area Entrance"
107 }
108 doors {
109 map: "the_entry"
110 name: "Near D Room Painting"
111 }
112 doors {
113 map: "the_graveyard"
114 name: "Double Letters"
115 }
116 doors {
117 map: "the_great"
118 name: "Tower Entrance"
119 }
120 doors {
121 map: "the_parthenon"
122 name: "Double Letters"
123 }
124 doors {
125 map: "the_unkempt"
126 name: "Cyan Doors"
127 }
128 doors {
129 map: "the_unkempt"
130 name: "Control Center Orange Door"
131 }
132 doors {
133 map: "the_unyielding"
134 name: "Cyan Doors"
135 }
136}
diff --git a/data/ids.yaml b/data/ids.yaml index bd6cbc1..4e2cd66 100644 --- a/data/ids.yaml +++ b/data/ids.yaml
@@ -3846,5 +3846,6 @@ door_groups:
3846 Control Center Orange Doors: 2786 3846 Control Center Orange Doors: 2786
3847 Control Center Purple Doors: 2785 3847 Control Center Purple Doors: 2785
3848 Control Center White Doors: 2784 3848 Control Center White Doors: 2784
3849 Cyan Doors: 2789
3849 The Entry - Repetitive Entrance: 2782 3850 The Entry - Repetitive Entrance: 2782
3850 The Repetitive - Plaza Entrance: 2783 3851 The Repetitive - Plaza Entrance: 2783
diff --git a/data/maps/the_darkroom/doors.txtpb b/data/maps/the_darkroom/doors.txtpb index d7094ae..047c7d0 100644 --- a/data/maps/the_darkroom/doors.txtpb +++ b/data/maps/the_darkroom/doors.txtpb
@@ -2,8 +2,8 @@
2doors { 2doors {
3 name: "Double Letter Panel Blockers" 3 name: "Double Letter Panel Blockers"
4 type: EVENT 4 type: EVENT
5 #receivers: "Panels/Room 1/panel_3/visibilityListener" 5 receivers: "Panels/Room 1/panel_3/visibilityListener"
6 #receivers: "Panels/Room 2/panel_3/visibilityListener" 6 receivers: "Panels/Room 2/panel_3/visibilityListener"
7 double_letters: true 7 double_letters: true
8} 8}
9doors { 9doors {
diff --git a/data/maps/the_graveyard/doors.txtpb b/data/maps/the_graveyard/doors.txtpb index 5e5e929..a10d8f6 100644 --- a/data/maps/the_graveyard/doors.txtpb +++ b/data/maps/the_graveyard/doors.txtpb
@@ -19,5 +19,7 @@ doors {
19doors { 19doors {
20 name: "Double Letters" 20 name: "Double Letters"
21 type: EVENT 21 type: EVENT
22 receivers: "Panels/panel_3/teleportListener"
23 receivers: "Components/Paintings/omrt/teleportListener"
22 double_letters: true 24 double_letters: true
23} 25}
diff --git a/data/maps/the_parthenon/doors.txtpb b/data/maps/the_parthenon/doors.txtpb index bb57d12..5187aea 100644 --- a/data/maps/the_parthenon/doors.txtpb +++ b/data/maps/the_parthenon/doors.txtpb
@@ -1,6 +1,13 @@
1doors { 1doors {
2 name: "Double Letters" 2 name: "Double Letters"
3 type: EVENT 3 type: EVENT
4 receivers: "Components/Doors/entry_11"
5 receivers: "Components/Doors/entry_5"
6 receivers: "Components/Doors/entry_6"
7 receivers: "Components/Doors/entry_7"
8 receivers: "Components/Doors/entry_8"
9 receivers: "Components/Doors/entry_9"
10 receivers: "Components/Doors/entry_10"
4 double_letters: true 11 double_letters: true
5} 12}
6doors { 13doors {
diff --git a/data/maps/the_unkempt/doors.txtpb b/data/maps/the_unkempt/doors.txtpb index 9a13c82..2349913 100644 --- a/data/maps/the_unkempt/doors.txtpb +++ b/data/maps/the_unkempt/doors.txtpb
@@ -21,6 +21,7 @@ doors {
21doors { 21doors {
22 name: "Cyan Doors" 22 name: "Cyan Doors"
23 type: EVENT 23 type: EVENT
24 receivers: "Components/Doors/entry_12"
24 double_letters: true 25 double_letters: true
25} 26}
26doors { 27doors {
@@ -67,6 +68,7 @@ doors {
67 type: CONTROL_CENTER_COLOR 68 type: CONTROL_CENTER_COLOR
68 receivers: "Components/Doors/entry_6" 69 receivers: "Components/Doors/entry_6"
69 receivers: "Components/Doors/entry_13" 70 receivers: "Components/Doors/entry_13"
71 receivers: "Panels/Assorted/panel_1/teleportListener"
70 control_center_color: "orange" 72 control_center_color: "orange"
71 double_letters: true 73 double_letters: true
72} 74}
diff --git a/data/maps/the_unkempt/rooms/Right Area.txtpb b/data/maps/the_unkempt/rooms/Right Area.txtpb index 1475fb0..03d7cea 100644 --- a/data/maps/the_unkempt/rooms/Right Area.txtpb +++ b/data/maps/the_unkempt/rooms/Right Area.txtpb
@@ -159,6 +159,5 @@ panels {
159 clue: "color" 159 clue: "color"
160 answer: "orange" 160 answer: "orange"
161 symbols: EXAMPLE 161 symbols: EXAMPLE
162 # TODO: This is hidden in-game until double letters are unlocked AND "orange" 162 required_door { name: "Control Center Orange Door" }
163 # is entered in the control center.
164} 163}
diff --git a/data/maps/the_unyielding/doors.txtpb b/data/maps/the_unyielding/doors.txtpb index b9d0d77..a3c3999 100644 --- a/data/maps/the_unyielding/doors.txtpb +++ b/data/maps/the_unyielding/doors.txtpb
@@ -499,5 +499,8 @@ doors {
499doors { 499doors {
500 name: "Cyan Doors" 500 name: "Cyan Doors"
501 type: EVENT 501 type: EVENT
502 receivers: "Components/Doors/entry_4"
503 receivers: "Panels/Miscellaneous/entry_2/teleportListener"
504 receivers: "Panels/Miscellaneous/entry_3/teleportListener"
502 double_letters: true 505 double_letters: true
503} 506}
diff --git a/proto/data.proto b/proto/data.proto index 84d14ef..24b98fe 100644 --- a/proto/data.proto +++ b/proto/data.proto
@@ -41,6 +41,9 @@ enum DoorGroupType {
41 // connections are not shuffled, but are not items at all when control center 41 // connections are not shuffled, but are not items at all when control center
42 // colors are not shuffled. 42 // colors are not shuffled.
43 COLOR_CONNECTOR = 2; 43 COLOR_CONNECTOR = 2;
44
45 // Groups with this type become an item if cyan door behavior is set to item.
46 CYAN_DOORS = 3;
44} 47}
45 48
46enum AxisDirection { 49enum AxisDirection {