From 99191f3aa87b2362516971c1fdd64d21b16f87b7 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 29 Sep 2025 12:31:01 -0400 Subject: Show when goal is reachable in tracker --- apworld/client/assets/goal.png | Bin 0 -> 215 bytes apworld/client/client.gd | 4 ++++ apworld/client/gamedata.gd | 5 ++++- apworld/client/manager.gd | 16 ++++++++++++++++ apworld/client/player.gd | 22 +++------------------- apworld/client/textclient.gd | 15 +++++++++++++++ apworld/context.py | 23 +++++++++++++++++++++-- apworld/locations.py | 1 + apworld/regions.py | 3 +++ apworld/tracker.py | 6 ++++++ 10 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 apworld/client/assets/goal.png diff --git a/apworld/client/assets/goal.png b/apworld/client/assets/goal.png new file mode 100644 index 0000000..bd1650d Binary files /dev/null and b/apworld/client/assets/goal.png differ diff --git a/apworld/client/client.gd b/apworld/client/client.gd index a23e85a..62d7fd8 100644 --- a/apworld/client/client.gd +++ b/apworld/client/client.gd @@ -24,6 +24,7 @@ var _received_items = {} var _slot_data = {} var _accessible_locations = [] var _accessible_worldports = [] +var _goal_accessible = false signal could_not_connect signal connect_status @@ -61,6 +62,7 @@ func _reset_state(): _checked_worldports = [] _accessible_locations = [] _accessible_worldports = [] + _goal_accessible = false func disconnect_from_ap(): @@ -174,6 +176,8 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo for port_id in message["worldports"]: _accessible_worldports.append(int(port_id)) + _goal_accessible = bool(message.get("goal", false)) + accessible_locations_updated.emit() elif cmd == "UpdateKeyboard": diff --git a/apworld/client/gamedata.gd b/apworld/client/gamedata.gd index 1424721..334d42a 100644 --- a/apworld/client/gamedata.gd +++ b/apworld/client/gamedata.gd @@ -14,6 +14,7 @@ var letter_id_by_ap_id = {} var symbol_item_ids = [] var anti_trap_ids = {} var location_name_by_id = {} +var ending_display_name_by_name = {} var kSYMBOL_ITEMS @@ -103,7 +104,9 @@ func load(data_bytes): location_name_by_id[mastery.get_ap_id()] = _get_mastery_location_name(mastery) for ending in objects.get_endings(): - location_name_by_id[ending.get_ap_id()] = _get_ending_location_name(ending) + var location_name = _get_ending_location_name(ending) + location_name_by_id[ending.get_ap_id()] = location_name + ending_display_name_by_name[ending.get_name()] = location_name for keyholder in objects.get_keyholders(): if keyholder.has_key(): diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 5b731d2..b4fef1c 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd @@ -45,6 +45,22 @@ const kCYAN_DOOR_BEHAVIOR_H2 = 0 const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 const kCYAN_DOOR_BEHAVIOR_ITEM = 2 +const kEndingNameByVictoryValue = { + 0: "GRAY", + 1: "PURPLE", + 2: "MINT", + 3: "BLACK", + 4: "BLUE", + 5: "CYAN", + 6: "RED", + 7: "PLUM", + 8: "ORANGE", + 9: "GOLD", + 10: "YELLOW", + 11: "GREEN", + 12: "WHITE", +} + var apworld_version = [0, 0] var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 var daedalus_roof_access = false diff --git a/apworld/client/player.gd b/apworld/client/player.gd index 366c3b0..5417a48 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd @@ -1,21 +1,5 @@ extends "res://scripts/nodes/player.gd" -const kEndingNameByVictoryValue = { - 0: "GRAY", - 1: "PURPLE", - 2: "MINT", - 3: "BLACK", - 4: "BLUE", - 5: "CYAN", - 6: "RED", - 7: "PLUM", - 8: "ORANGE", - 9: "GOLD", - 10: "YELLOW", - 11: "GREEN", - 12: "WHITE", -} - signal evaluate_solvability var compass @@ -138,7 +122,7 @@ func _ready(): get_parent().add_child.call_deferred(locationListener) - if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): + if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): var victoryListener = ap.SCRIPT_victoryListener.new() victoryListener.name = "victoryListener" victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) @@ -216,9 +200,9 @@ func _ready(): var sign2 = sign_prefab.instantiate() sign2.position = Vector3(-7, 4, -15.01) - sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?") + sign2.text = "%s ending" % ap.kEndingNameByVictoryValue.get(ap.victory_condition, "?") - var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower() + var sign2_color = ap.kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower() if sign2_color == "white": sign2_color = "silver" diff --git a/apworld/client/textclient.gd b/apworld/client/textclient.gd index 0c4e675..530eddb 100644 --- a/apworld/client/textclient.gd +++ b/apworld/client/textclient.gd @@ -10,6 +10,7 @@ var is_open = false var locations_overlay var location_texture var worldport_texture +var goal_texture var worldports_tab var worldports_tree @@ -116,6 +117,10 @@ func _ready(): worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png")) worldport_texture = ImageTexture.create_from_image(worldport_image) + var goal_image = Image.new() + goal_image.load_png_from_buffer(runtime.read_path("assets/goal.png")) + goal_texture = ImageTexture.create_from_image(goal_image) + func _input(event): if global.loaded and event is InputEventKey and event.pressed: @@ -179,6 +184,7 @@ func update_locations(): const kLocation = 0 const kWorldport = 1 + const kGoal = 2 var location_names = [] var type_by_name = {} @@ -196,6 +202,13 @@ func update_locations(): location_names.sort() + if ap.client._goal_accessible: + var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ + ap.victory_condition + ]] + location_names.push_front(location_name) + type_by_name[location_name] = kGoal + var count = 0 for location_name in location_names: tracker_label.append_text("[p]%s[/p]" % location_name) @@ -207,6 +220,8 @@ func update_locations(): locations_overlay.add_image(location_texture) elif type_by_name[location_name] == kWorldport: locations_overlay.add_image(worldport_texture) + elif type_by_name[location_name] == kGoal: + locations_overlay.add_image(goal_texture) locations_overlay.pop() count += 1 diff --git a/apworld/context.py b/apworld/context.py index 4b78517..0e1a125 100644 --- a/apworld/context.py +++ b/apworld/context.py @@ -10,7 +10,7 @@ import Utils import settings from BaseClasses import ItemClassification from CommonClient import CommonContext, server_loop, gui_enabled, logger, get_base_parser, handle_url_arg -from NetUtils import Endpoint, decode, encode +from NetUtils import Endpoint, decode, encode, ClientStatus from Utils import async_start from . import Lingo2World from .tracker import Tracker @@ -36,6 +36,7 @@ class Lingo2Manager: keyboard: dict[str, int] worldports: set[int] + goaled: bool def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): self.game_ctx = game_ctx @@ -53,6 +54,7 @@ class Lingo2Manager: self.keyboard[k] = 0 self.worldports = set() + self.goaled = False def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: ret: dict[str, int] = {} @@ -177,6 +179,9 @@ class Lingo2GameContext: if len(self.manager.tracker.accessible_worldports) > 0: msg["worldports"] = list(self.manager.tracker.accessible_worldports) + if self.manager.tracker.goal_accessible and not self.manager.goaled: + msg["goal"] = True + async_start(self.send_msgs([msg]), name="accessible locations") def send_update_locations(self, locations): @@ -226,6 +231,7 @@ class Lingo2ClientContext(CommonContext): items_handling = 0b111 slot_data: dict[str, Any] | None + victory_data_storage_key: str def __init__(self, server_address: str | None = None, password: str | None = None): super().__init__(server_address, password) @@ -253,7 +259,10 @@ class Lingo2ClientContext(CommonContext): self.manager.tracker.set_checked_locations(self.checked_locations) self.manager.game_ctx.send_accessible_locations() - self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2")) + self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}" + + self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"), + self.victory_data_storage_key) msg_batch = [{ "cmd": "Set", "key": self.get_datastorage_key("keyboard1"), @@ -360,6 +369,9 @@ class Lingo2ClientContext(CommonContext): }) self.manager.game_ctx.send_location_info(locations) + elif cmd == "Received": + if args["key"] == self.victory_data_storage_key: + self.handle_status_update(args["value"]) elif cmd == "SetReply": if args["key"] == self.get_datastorage_key("keyboard1"): self.handle_keyboard_update(1, args) @@ -369,6 +381,8 @@ class Lingo2ClientContext(CommonContext): updates = self.manager.update_worldports(set(args["value"])) if len(updates) > 0: self.manager.game_ctx.send_update_worldports(updates) + elif args["key"] == self.victory_data_storage_key: + self.handle_status_update(args["value"]) def get_datastorage_key(self, name: str): return f"Lingo2_{self.slot}_{name}" @@ -445,6 +459,11 @@ class Lingo2ClientContext(CommonContext): }] }]) + def handle_status_update(self, value: int): + self.manager.goaled = (value == ClientStatus.CLIENT_GOAL) + self.manager.tracker.refresh_state() + self.manager.game_ctx.send_accessible_locations() + async def pipe_loop(manager: Lingo2Manager): while not manager.client_ctx.exit_event.is_set(): diff --git a/apworld/locations.py b/apworld/locations.py index a502931..3d619dc 100644 --- a/apworld/locations.py +++ b/apworld/locations.py @@ -5,3 +5,4 @@ class Lingo2Location(Location): game: str = "Lingo 2" port_id: int + goal: bool diff --git a/apworld/regions.py b/apworld/regions.py index 9f44682..64302f6 100644 --- a/apworld/regions.py +++ b/apworld/regions.py @@ -28,6 +28,9 @@ def create_locations(room, new_region: Region, world: "Lingo2World", regions: di for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items(): new_location = Lingo2Location(world.player, event_name, None, new_region) + if world.for_tracker and item_name == "Victory": + new_location.goal = True + event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player) new_location.place_locked_item(event_item) new_region.locations.append(new_location) diff --git a/apworld/tracker.py b/apworld/tracker.py index cf2dbe1..7239b65 100644 --- a/apworld/tracker.py +++ b/apworld/tracker.py @@ -22,6 +22,7 @@ class Tracker: checked_locations: set[int] accessible_locations: set[int] accessible_worldports: set[int] + goal_accessible: bool state: CollectionState @@ -31,6 +32,7 @@ class Tracker: self.checked_locations = set() self.accessible_locations = set() self.accessible_worldports = set() + self.goal_accessible = False def setup_slot(self, slot_data): Lingo2World.for_tracker = True @@ -94,6 +96,7 @@ class Tracker: self.accessible_locations = set() self.accessible_worldports = set() + self.goal_accessible = False for region in self.state.reachable_regions[PLAYER_NUM]: for location in region.locations: @@ -104,3 +107,6 @@ class Tracker: elif hasattr(location, "port_id"): if location.port_id not in self.manager.worldports: self.accessible_worldports.add(location.port_id) + elif hasattr(location, "goal") and location.goal: + if not self.manager.goaled: + self.goal_accessible = True -- cgit 1.4.1