From 6af543ba049e3ba880b113907cd5222b205b8c05 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 8 Sep 2025 12:42:31 -0400 Subject: Add cyan door behavior option --- apworld/__init__.py | 1 + apworld/options.py | 22 ++++++++++++++++++++++ apworld/player_logic.py | 36 ++++++++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 10 deletions(-) (limited to 'apworld') 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): def fill_slot_data(self): slot_options = [ + "cyan_door_behavior", "daedalus_roof_access", "keyholder_sanity", "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): display_name = "Keyholder Sanity" +class CyanDoorBehavior(Choice): + """ + Cyan-colored doors usually only open upon unlocking double letters. Some panels also only appear upon unlocking + double letters. This option determines how these unlocks should behave. + + - **Collect H2**: In the base game, H2 is the first double letter you are intended to collect, so cyan doors only + open when you collect the H2 pickup in The Repetitive. Collecting the actual pickup is still required even with + remote letter shuffle enabled. + - **Any Double Letter**: Cyan doors will open when you have unlocked any cyan letter on your keyboard. In letter + shuffle, this means receiving a cyan letter, not picking up a cyan letter collectable. + - **Item**: Cyan doors will be grouped together in a single item. + + Note that some cyan doors are impacted by door shuffle (e.g. the entrance to The Tower). When door shuffle is + enabled, these doors won't be affected by the value of this option. + """ + display_name = "Cyan Door Behavior" + option_collect_h2 = 0 + option_any_double_letter = 1 + option_item = 2 + + class DaedalusRoofAccess(Toggle): """ 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): shuffle_control_center_colors: ShuffleControlCenterColors shuffle_letters: ShuffleLetters keyholder_sanity: KeyholderSanity + cyan_door_behavior: CyanDoorBehavior daedalus_roof_access: DaedalusRoofAccess 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 from .generated import data_pb2 as data_pb2 from typing import TYPE_CHECKING, NamedTuple -from .options import VictoryCondition, ShuffleLetters +from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior if TYPE_CHECKING: from . import Lingo2World @@ -124,11 +124,13 @@ class Lingo2PlayerLogic: self.real_items.append(progressive.name) for door_group in world.static_logic.objects.door_groups: - if door_group.type == data_pb2.DoorGroupType.CONNECTOR and not self.world.options.shuffle_doors: - continue - - if (door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR and - not self.world.options.shuffle_control_center_colors): + if door_group.type == data_pb2.DoorGroupType.CONNECTOR: + if not self.world.options.shuffle_doors: + continue + elif door_group.type == data_pb2.DoorGroupType.COLOR_CONNECTOR: + if not self.world.options.shuffle_control_center_colors: + continue + else: continue for door in door_group.doors: @@ -157,6 +159,19 @@ class Lingo2PlayerLogic: self.item_by_door[door.id] = (door_item_name, 1) self.real_items.append(door_item_name) + # We handle cyan_door_behavior = Item after door shuffle, because cyan doors that are impacted by door shuffle + # should be exempt from cyan_door_behavior. + if world.options.cyan_door_behavior == CyanDoorBehavior.option_item: + for door_group in world.static_logic.objects.door_groups: + if door_group.type != data_pb2.DoorGroupType.CYAN_DOORS: + continue + + for door in door_group.doors: + if not door in self.item_by_door: + self.item_by_door[door] = (door_group.name, 1) + + self.real_items.append(door_group.name) + for door in world.static_logic.objects.doors: if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, @@ -295,12 +310,13 @@ class Lingo2PlayerLogic: self.add_solution_reqs(reqs, door.control_center_color) if door.double_letters: - if self.world.options.shuffle_letters in [ShuffleLetters.option_vanilla, - ShuffleLetters.option_vanilla_cyan]: + if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2: reqs.rooms.add("The Repetitive - Main Room") - elif self.world.options.shuffle_letters in [ShuffleLetters.option_progressive, - ShuffleLetters.option_item_cyan]: + elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_any_double_letter: reqs.cyans = True + elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item: + # There shouldn't be any locations that are cyan doors. + pass for keyholder_uses in door.keyholders: key_name = keyholder_uses.key.upper() -- cgit 1.4.1