From a17b7e1cbfb43141af21263f838f9593836a9c52 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 14 Aug 2025 12:30:12 -0400 Subject: Fixed some issues with door logic If a door is shuffled, the door's item should be used in connections and in requirements specified by panels, ports, paintings, and other doors. However, the original requirements still need to be used for locations. --- apworld/player_logic.py | 30 +++++++++++++++++++++++++----- apworld/regions.py | 6 +++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 7209ee5..c2850d5 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py @@ -63,6 +63,7 @@ class Lingo2PlayerLogic: world: "Lingo2World" locations_by_room: dict[int, list[PlayerLocation]] + item_by_door: dict[int, str] panel_reqs: dict[int, AccessRequirements] proxy_reqs: dict[int, dict[str, AccessRequirements]] @@ -73,19 +74,25 @@ class Lingo2PlayerLogic: def __init__(self, world: "Lingo2World"): self.world = world self.locations_by_room = {} + self.item_by_door = {} self.panel_reqs = dict() self.proxy_reqs = dict() self.door_reqs = dict() self.real_items = list() + # We iterate through the doors in two parts because it is essential that we determine which doors are shuffled + # before we calculate any access requirements. + for door in world.static_logic.objects.doors: + if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.ITEM_ONLY] and self.world.options.shuffle_doors: + door_item_name = self.world.static_logic.get_door_item_name(door.id) + self.item_by_door[door.id] = door_item_name + self.real_items.append(door_item_name) + for door in world.static_logic.objects.doors: if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY]: self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id, self.get_door_reqs(door.id))) - if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.ITEM_ONLY] and self.world.options.shuffle_doors: - self.real_items.append(self.world.static_logic.get_door_item_name(door.id)) - for letter in world.static_logic.objects.letters: self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, AccessRequirements())) @@ -127,7 +134,7 @@ class Lingo2PlayerLogic: reqs.symbols.add(symbol) if panel.HasField("required_door"): - door_reqs = self.get_door_reqs(panel.required_door) + door_reqs = self.get_door_open_reqs(panel.required_door) reqs.merge(door_reqs) if panel.HasField("required_room"): @@ -135,6 +142,8 @@ class Lingo2PlayerLogic: return reqs + # This gets/calculates the requirements described by the door object. This is most notably used as the requirements + # for clearing a location, or opening a door when the door is not shuffled. def get_door_reqs(self, door_id: int) -> AccessRequirements: if door_id not in self.door_reqs: self.door_reqs[door_id] = self.calculate_door_reqs(door_id) @@ -161,7 +170,18 @@ class Lingo2PlayerLogic: reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) for sub_door_id in door.doors: - sub_reqs = self.get_door_reqs(sub_door_id) + sub_reqs = self.get_door_open_reqs(sub_door_id) reqs.merge(sub_reqs) return reqs + + # This gets the requirements to open a door within the world. When a door is shuffled, this means having the item + # that acts as the door's key. + def get_door_open_reqs(self, door_id: int) -> AccessRequirements: + if door_id in self.item_by_door: + reqs = AccessRequirements() + reqs.items.add(self.item_by_door.get(door_id)) + + return reqs + else: + return self.get_door_reqs(door_id) diff --git a/apworld/regions.py b/apworld/regions.py index 2a850ef..14fcaac 100644 --- a/apworld/regions.py +++ b/apworld/regions.py @@ -41,7 +41,7 @@ def create_regions(world: "Lingo2World"): reqs = AccessRequirements() if connection.HasField("required_door"): - reqs.merge(world.player_logic.get_door_reqs(connection.required_door)) + reqs.merge(world.player_logic.get_door_open_reqs(connection.required_door)) door = world.static_logic.objects.doors[connection.required_door] wmap = world.static_logic.objects.maps[door.map_id] @@ -52,14 +52,14 @@ def create_regions(world: "Lingo2World"): connection_name = f"{connection_name} (via port {port.name})" if port.HasField("required_door"): - reqs.merge(world.player_logic.get_door_reqs(port.required_door)) + reqs.merge(world.player_logic.get_door_open_reqs(port.required_door)) if connection.HasField("painting"): painting = world.static_logic.objects.paintings[connection.painting] connection_name = f"{connection_name} (via painting {painting.name})" if painting.HasField("required_door"): - reqs.merge(world.player_logic.get_door_reqs(painting.required_door)) + reqs.merge(world.player_logic.get_door_open_reqs(painting.required_door)) if connection.HasField("panel"): proxy = connection.panel -- cgit 1.4.1