diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-08 12:42:31 -0400 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-08 12:42:31 -0400 |
commit | 6af543ba049e3ba880b113907cd5222b205b8c05 (patch) | |
tree | 58a17c86870db0e67e30e20b7349aee69fba4e82 | |
parent | 215b576cc816f6f378d057b330ef01f02539602a (diff) | |
download | lingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.tar.gz lingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.tar.bz2 lingo2-archipelago-6af543ba049e3ba880b113907cd5222b205b8c05.zip |
Add cyan door behavior option
-rw-r--r-- | apworld/__init__.py | 1 | ||||
-rw-r--r-- | apworld/options.py | 22 | ||||
-rw-r--r-- | apworld/player_logic.py | 36 | ||||
-rw-r--r-- | data/door_groups.txtpb | 52 | ||||
-rw-r--r-- | data/ids.yaml | 1 | ||||
-rw-r--r-- | data/maps/the_darkroom/doors.txtpb | 4 | ||||
-rw-r--r-- | data/maps/the_graveyard/doors.txtpb | 2 | ||||
-rw-r--r-- | data/maps/the_parthenon/doors.txtpb | 7 | ||||
-rw-r--r-- | data/maps/the_unkempt/doors.txtpb | 2 | ||||
-rw-r--r-- | data/maps/the_unkempt/rooms/Right Area.txtpb | 3 | ||||
-rw-r--r-- | data/maps/the_unyielding/doors.txtpb | 3 | ||||
-rw-r--r-- | proto/data.proto | 3 |
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 | ||
51 | class 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 | |||
51 | class DaedalusRoofAccess(Toggle): | 72 | class 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 | |||
3 | from .generated import data_pb2 as data_pb2 | 3 | from .generated import data_pb2 as data_pb2 |
4 | from typing import TYPE_CHECKING, NamedTuple | 4 | from typing import TYPE_CHECKING, NamedTuple |
5 | 5 | ||
6 | from .options import VictoryCondition, ShuffleLetters | 6 | from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior |
7 | 7 | ||
8 | if TYPE_CHECKING: | 8 | if 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 | } |
85 | door_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 @@ | |||
2 | doors { | 2 | doors { |
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 | } |
9 | doors { | 9 | doors { |
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 { | |||
19 | doors { | 19 | doors { |
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 @@ | |||
1 | doors { | 1 | doors { |
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 | } |
6 | doors { | 13 | doors { |
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 { | |||
21 | doors { | 21 | doors { |
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 | } |
26 | doors { | 27 | doors { |
@@ -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 { | |||
499 | doors { | 499 | doors { |
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 | ||
46 | enum AxisDirection { | 49 | enum AxisDirection { |