diff options
| -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 { |
