From 9b5c160f60a4e6c679b0c1afca82d9a58523eaab Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 30 Jan 2026 10:50:01 -0500 Subject: Worked on daed-only mode --- apworld/__init__.py | 1 + apworld/client/main.gd | 10 ++++++-- apworld/client/manager.gd | 2 ++ apworld/client/rteMenu.gd | 12 ++++++++++ apworld/options.py | 11 +++++++++ apworld/player_logic.py | 60 +++++++++++++++++++++++++++++++++++++---------- apworld/regions.py | 10 ++++---- 7 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 apworld/client/rteMenu.gd (limited to 'apworld') diff --git a/apworld/__init__.py b/apworld/__init__.py index 3d2f075..27ed95a 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py @@ -132,6 +132,7 @@ class Lingo2World(World): def fill_slot_data(self): slot_options = [ "cyan_door_behavior", + "daedalus_only", "daedalus_roof_access", "enable_gift_maps", "enable_icarus", diff --git a/apworld/client/main.gd b/apworld/client/main.gd index c90d6e7..8cac24c 100644 --- a/apworld/client/main.gd +++ b/apworld/client/main.gd @@ -48,6 +48,7 @@ func _ready(): installScriptExtension(runtime.load_script("panel.gd")) installScriptExtension(runtime.load_script("pauseMenu.gd")) installScriptExtension(runtime.load_script("player.gd")) + installScriptExtension(runtime.load_script("rteMenu.gd")) installScriptExtension(runtime.load_script("saver.gd")) installScriptExtension(runtime.load_script("teleport.gd")) installScriptExtension(runtime.load_script("teleportListener.gd")) @@ -229,7 +230,11 @@ func startGame(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) global.user = ap.getSaveFileName() global.universe = "lingo" - global.map = "the_entry" + + if ap.daedalus_only: + global.map = "daedalus" + else: + global.map = "the_entry" unlocks.resetCollectables() unlocks.resetData() @@ -263,6 +268,7 @@ func startGame(): clearResourceCache("res://objects/nodes/teleport.tscn") clearResourceCache("res://objects/nodes/worldport.tscn") clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") + clearResourceCache("res://objects/scenes/menus/rte_inner.tscn") var paintings_dir = DirAccess.open("res://objects/meshes/paintings") if paintings_dir: @@ -273,7 +279,7 @@ func startGame(): clearResourceCache("res://objects/meshes/paintings/" + file_name) file_name = paintings_dir.get_next() - switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn") + switcher.switch_map.call_deferred("res://objects/scenes/%s.tscn" % global.map) func connectionUnsuccessful(error_message): diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 8c981f9..381a779 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd @@ -64,6 +64,7 @@ const kEndingNameByVictoryValue = { var apworld_version = [0, 0, 0] var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 +var daedalus_only = false var daedalus_roof_access = false var enable_gift_maps = [] var enable_icarus = false @@ -463,6 +464,7 @@ func _client_connected(slot_data): # Read slot data. cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) + daedalus_only = bool(slot_data.get("daedalus_only", false)) daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) enable_gift_maps = slot_data.get("enable_gift_maps", []) enable_icarus = bool(slot_data.get("enable_icarus", false)) diff --git a/apworld/client/rteMenu.gd b/apworld/client/rteMenu.gd new file mode 100644 index 0000000..5882d77 --- /dev/null +++ b/apworld/client/rteMenu.gd @@ -0,0 +1,12 @@ +extends "res://scripts/ui/rteMenu.gd" + + +func _readier(): + var ap = global.get_node("Archipelago") + if ap.daedalus_only: + get_node("rte_the_entry").hide() + get_node("rte_daedalus").show() + + switcher.preload_map("res://objects/scenes/daedalus.tscn") + else: + super()._readier() diff --git a/apworld/options.py b/apworld/options.py index f687434..1323635 100644 --- a/apworld/options.py +++ b/apworld/options.py @@ -121,6 +121,16 @@ class EnableGiftMaps(OptionSet): valid_keys = ["The Advanced", "The Charismatic", "The Crystalline", "The Fuzzy", "The Stellar"] +class DaedalusOnly(Toggle): + """ + If enabled, all maps besides Daedalus, The Gold, and The Tenacious will be disabled. This overrides any other + map-based option, such as Enable Icarus. The player will start in Daedalus. The ending must be set to Orange or + Gold. Worldport shuffle must be enabled. Letter shuffle cannot be set to a vanilla value. Cyan Door Behavior cannot + be set to Collect H2. + """ + display_name = "Daedalus Only" + + 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 @@ -221,6 +231,7 @@ class Lingo2Options(PerGameCommonOptions): cyan_door_behavior: CyanDoorBehavior enable_icarus: EnableIcarus enable_gift_maps: EnableGiftMaps + daedalus_only: DaedalusOnly daedalus_roof_access: DaedalusRoofAccess strict_purple_ending: StrictPurpleEnding strict_cyan_ending: StrictCyanEnding diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 3ee8f38..aea7698 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py @@ -203,6 +203,8 @@ class Lingo2PlayerLogic: world: "Lingo2World" shuffled_maps: set[int] + shuffled_rooms: set[int] + shuffled_doors: set[int] locations_by_room: dict[int, list[PlayerLocation]] event_loc_item_by_room: dict[int, dict[str, str]] @@ -220,6 +222,8 @@ class Lingo2PlayerLogic: def __init__(self, world: "Lingo2World"): self.world = world + self.shuffled_rooms = set() + self.shuffled_doors = set() self.locations_by_room = {} self.event_loc_item_by_room = {} self.item_by_door = {} @@ -229,7 +233,10 @@ class Lingo2PlayerLogic: self.real_items = list() self.double_letter_amount = dict() - def should_shuffle_map(game_map) -> bool: + def should_shuffle_map(game_map) -> bool | set[int]: + if world.options.daedalus_only: + return game_map.daedalus_only_mode == data_pb2.DaedalusOnlyMode.DAED_ONLY_ALLOW + if game_map.type == data_pb2.MapType.NORMAL_MAP: return True elif game_map.type == data_pb2.MapType.ICARUS: @@ -251,6 +258,14 @@ class Lingo2PlayerLogic: self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps if should_shuffle_map(game_map)) + if world.options.daedalus_only: + for game_map in world.static_logic.objects.maps: + if game_map.daedalus_only_mode == data_pb2.DaedalusOnlyMode.DAED_ONLY_PARTIAL: + self.shuffled_rooms.update(set(room.id for room in world.static_logic.objects.rooms + if room.map_id == game_map.id and room.daedalus_only_allow)) + self.shuffled_doors.update(set(door.id for door in world.static_logic.objects.doors + if door.map_id == game_map.id and door.daedalus_only_allow)) + maximum_masteries = 13 + len(world.options.enable_gift_maps.value) if world.options.enable_icarus: maximum_masteries += 1 @@ -265,7 +280,7 @@ class Lingo2PlayerLogic: for progressive in world.static_logic.objects.progressives: for i in range(0, len(progressive.doors)): door = world.static_logic.objects.doors[progressive.doors[i]] - if door.map_id not in self.shuffled_maps: + if not self.should_shuffle_door(door.id): continue self.item_by_door[progressive.doors[i]] = (progressive.name, i + 1) @@ -279,13 +294,15 @@ class Lingo2PlayerLogic: if not self.world.options.shuffle_control_center_colors or self.world.options.shuffle_worldports: continue elif door_group.type == data_pb2.DoorGroupType.SHUFFLE_GROUP: - if not self.world.options.shuffle_doors: + if door_group.name == "Lavender Cubes" and self.world.options.daedalus_only: + # Always shuffle this if we're in Daed-only mode. + pass + elif not self.world.options.shuffle_doors: continue else: continue - shuffleable_doors = [door_id for door_id in door_group.doors - if world.static_logic.objects.doors[door_id].map_id in self.shuffled_maps] + shuffleable_doors = [door_id for door_id in door_group.doors if self.should_shuffle_door(door_id)] if len(shuffleable_doors) > 0: for door in shuffleable_doors: @@ -296,7 +313,7 @@ class Lingo2PlayerLogic: # 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.map_id not in self.shuffled_maps: + if not self.should_shuffle_door(door.id): continue if door.type in [data_pb2.DoorType.EVENT, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: @@ -328,8 +345,7 @@ class Lingo2PlayerLogic: continue shuffleable_doors = [door_id for door_id in door_group.doors - if world.static_logic.objects.doors[door_id].map_id in self.shuffled_maps - and door_id not in self.item_by_door] + if self.should_shuffle_door(door_id) and door_id not in self.item_by_door] if len(shuffleable_doors) > 0: for door in shuffleable_doors: @@ -338,7 +354,7 @@ class Lingo2PlayerLogic: self.real_items.append(door_group.name) for door in world.static_logic.objects.doors: - if door.map_id not in self.shuffled_maps: + if not self.should_shuffle_door(door.id): continue if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.LOCATION_ONLY, data_pb2.DoorType.GRAVESTONE]: @@ -346,7 +362,7 @@ class Lingo2PlayerLogic: self.get_door_reqs(door.id))) for letter in world.static_logic.objects.letters: - if world.static_logic.get_room_object_map_id(letter) not in self.shuffled_maps: + if not self.should_shuffle_room(letter.room_id): continue self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, @@ -368,7 +384,7 @@ class Lingo2PlayerLogic: self.double_letter_amount[letter.key.upper()] = self.double_letter_amount.get(letter.key.upper(), 0) + 1 for mastery in world.static_logic.objects.masteries: - if world.static_logic.get_room_object_map_id(mastery) not in self.shuffled_maps: + if not self.should_shuffle_room(mastery.room_id): continue self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, @@ -379,7 +395,7 @@ class Lingo2PlayerLogic: self.event_loc_item_by_room.setdefault(mastery.room_id, {})[event_name] = "Mastery" for ending in world.static_logic.objects.endings: - if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: + if not self.should_shuffle_room(ending.room_id): continue # Don't create a location for your selected ending. Also don't create a location for White Ending if it's @@ -401,7 +417,7 @@ class Lingo2PlayerLogic: if self.world.options.keyholder_sanity: for keyholder in world.static_logic.objects.keyholders: if keyholder.HasField("key"): - if world.static_logic.get_room_object_map_id(keyholder) not in self.shuffled_maps: + if not self.should_shuffle_room(keyholder.room_id): continue reqs = AccessRequirements() @@ -606,3 +622,21 @@ class Lingo2PlayerLogic: if any(l.isnumeric() for l in solution): reqs.items.add("Numbers") + + def should_shuffle_room(self, room_id: int) -> bool: + if room_id in self.shuffled_rooms: + return True + + room = self.world.static_logic.objects.rooms[room_id] + game_map = self.world.static_logic.objects.maps[room.map_id] + + return game_map.id in self.shuffled_maps + + def should_shuffle_door(self, door_id: int) -> bool: + if door_id in self.shuffled_doors: + return True + + door = self.world.static_logic.objects.doors[door_id] + game_map = self.world.static_logic.objects.maps[door.map_id] + + return game_map.id in self.shuffled_maps diff --git a/apworld/regions.py b/apworld/regions.py index 1118603..2f9b571 100644 --- a/apworld/regions.py +++ b/apworld/regions.py @@ -62,7 +62,7 @@ def create_regions(world: "Lingo2World"): # locations. This allows us to reference the actual region objects in the access rules for the locations, which is # faster than having to look them up during access checking. for room in world.static_logic.objects.rooms: - if room.map_id not in world.player_logic.shuffled_maps: + if not world.player_logic.should_shuffle_room(room.id): continue region = create_region(room, world) @@ -72,7 +72,10 @@ def create_regions(world: "Lingo2World"): for (region, room) in region_and_room: create_locations(room, region, world, regions) - regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") + if world.options.daedalus_only: + regions["Menu"].connect(regions["Daedalus - Starting Room"], "Start Game") + else: + regions["Menu"].connect(regions["The Entry - Starting Room"], "Start Game") for connection in world.static_logic.objects.connections: if connection.roof_access and not world.options.daedalus_roof_access: @@ -160,8 +163,7 @@ def shuffle_entrances(world: "Lingo2World"): port_id_by_name: dict[str, int] = {} shuffleable_ports = [port for port in world.static_logic.objects.ports - if not port.no_shuffle - and world.static_logic.get_room_object_map_id(port) in world.player_logic.shuffled_maps] + if not port.no_shuffle and world.player_logic.should_shuffle_room(port.room_id)] if len(shuffleable_ports) % 2 == 1: # We have an odd number of shuffleable ports! Pick a port from a room that has more than one, and make it a -- cgit 1.4.1