From b413e5a9c5d5e5e6eff63a74fbb3ecdabf2cc66f Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sun, 2 Nov 2025 13:07:30 -0500 Subject: Prioritize hinted locations in tracker --- apworld/client/client.gd | 10 +++++++++ apworld/client/manager.gd | 7 +++++++ apworld/client/textclient.gd | 48 +++++++++++++++++++++++++++++++------------- apworld/context.py | 33 ++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 14 deletions(-) (limited to 'apworld') diff --git a/apworld/client/client.gd b/apworld/client/client.gd index 54d3d67..c149482 100644 --- a/apworld/client/client.gd +++ b/apworld/client/client.gd @@ -26,6 +26,7 @@ var _accessible_locations = [] var _accessible_worldports = [] var _goal_accessible = false var _latched_doors = [] +var _hinted_locations = [] signal could_not_connect signal connect_status @@ -41,6 +42,7 @@ signal checked_locations_updated signal ignored_locations_updated(locations) signal checked_worldports_updated signal keyboard_update_received +signal hinted_locations_updated func _init(): @@ -207,6 +209,14 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo ignored_locations_updated.emit(locs) + elif cmd == "UpdateHintedLocations": + for id in message["locations"]: + var iid = int(id) + if !_hinted_locations.has(iid): + _hinted_locations.append(iid) + + hinted_locations_updated.emit() + func connectToServer(server, un, pw): sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index b7bb5fd..830ebb8 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd @@ -146,6 +146,7 @@ func _ready(): client.accessible_locations_updated.connect(_on_accessible_locations_updated) client.checked_locations_updated.connect(_on_checked_locations_updated) client.ignored_locations_updated.connect(_on_ignored_locations_updated) + client.hinted_locations_updated.connect(_on_hinted_locations_updated) client.checked_worldports_updated.connect(_on_checked_worldports_updated) client.door_latched.connect(_on_door_latched) @@ -394,6 +395,12 @@ func _on_ignored_locations_updated(locations): textclient_node.update_locations() +func _on_hinted_locations_updated(): + var textclient_node = global.get_node("Textclient") + if textclient_node != null: + textclient_node.update_locations() + + func _on_door_latched(door_id): var gamedata = global.get_node("Gamedata") if gamedata.get_door_map_name(door_id) != global.map: diff --git a/apworld/client/textclient.gd b/apworld/client/textclient.gd index 8356f92..ce28a3a 100644 --- a/apworld/client/textclient.gd +++ b/apworld/client/textclient.gd @@ -17,6 +17,7 @@ var tracker_port_tree_item_by_id = {} var tracker_goal_tree_item = null var tracker_object_by_index = {} var tracker_object_by_ignored_index = {} +var tracker_ignored_group = null var worldports_tab var worldports_tree @@ -212,6 +213,7 @@ func update_locations(reset_locations = true): "type": kLocation, "id": location_id, "ignored": ap._ignored_locations.has(location_id), + "hint": ap.client._hinted_locations.has(location_id), } ) ) @@ -227,13 +229,12 @@ func update_locations(reset_locations = true): "type": kWorldport, "id": port_id, "ignored": false, + "hint": false, } ) ) - locations.sort_custom( - func(a, b): return a["name"] < b["name"] if a["ignored"] == b["ignored"] else !a["ignored"] - ) + locations.sort_custom(_cmp_tracker_objects) if ap.client._goal_accessible: var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ @@ -246,6 +247,7 @@ func update_locations(reset_locations = true): "name": location_name, "type": kGoal, "ignored": false, + "hint": false, } ) ) @@ -254,6 +256,8 @@ func update_locations(reset_locations = true): for location in locations: if count < 18 and not location["ignored"]: locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) + if location["hint"]: + locations_overlay.push_color(Color("#fafad2")) locations_overlay.append_text(location["name"]) locations_overlay.append_text(" ") if location["type"] == kLocation: @@ -262,6 +266,8 @@ func update_locations(reset_locations = true): locations_overlay.add_image(worldport_texture) elif location["type"] == kGoal: locations_overlay.add_image(goal_texture) + if location["hint"]: + locations_overlay.pop() locations_overlay.pop() count += 1 @@ -272,21 +278,20 @@ func update_locations(reset_locations = true): reset_tracker_tab() var root_ti = tracker_tree.create_item(null) - var ignored_loc_header = null for location in locations: var loc_row if location["ignored"]: - if ignored_loc_header == null: - ignored_loc_header = root_ti.create_child() - ignored_loc_header.set_text(1, "Ignored Locations") - ignored_loc_header.set_selectable(0, false) - ignored_loc_header.set_selectable(1, false) - ignored_loc_header.set_selectable(2, false) - ignored_loc_header.set_selectable(3, false) - - loc_row = ignored_loc_header.create_child() + if tracker_ignored_group == null: + tracker_ignored_group = root_ti.create_child() + tracker_ignored_group.set_text(1, "Ignored Locations") + tracker_ignored_group.set_selectable(0, false) + tracker_ignored_group.set_selectable(1, false) + tracker_ignored_group.set_selectable(2, false) + tracker_ignored_group.set_selectable(3, false) + + loc_row = tracker_ignored_group.create_child() else: loc_row = root_ti.create_child() @@ -294,6 +299,8 @@ func update_locations(reset_locations = true): loc_row.set_selectable(0, false) loc_row.set_text(1, location["name"]) loc_row.set_selectable(1, false) + if location["hint"]: + loc_row.set_custom_color(1, Color("#fafad2")) loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM) loc_row.set_text(2, "Show Path") loc_row.set_custom_as_button(2, true) @@ -346,6 +353,18 @@ func update_locations(reset_locations = true): if tracker_goal_tree_item != null and ap.client._goal_accessible: tracker_goal_tree_item.visible = true + if tracker_ignored_group != null: + tracker_ignored_group.visible = true + + +func _cmp_tracker_objects(a, b) -> bool: + if a["ignored"] != b["ignored"]: + return !a["ignored"] + elif a["hint"] != b["hint"]: + return a["hint"] + else: + return a["name"] < b["name"] + func update_locations_visibility(): var ap = global.get_node("Archipelago") @@ -372,7 +391,7 @@ func _on_tracker_button_clicked(): ap.client.getLogicalPath(type_str, tracker_object.get("id", null)) elif tracker_tree.get_edited_column() == 3: ap.toggle_ignored_location(tracker_object["id"]) - else: + elif edited_item.get_parent() == tracker_ignored_group: # This is the ignored locations group. if ( tracker_object_by_ignored_index.has(edited_index) @@ -485,4 +504,5 @@ func reset_tracker_tab(): tracker_goal_tree_item = null tracker_object_by_index.clear() tracker_object_by_ignored_index.clear() + tracker_ignored_group = null tracker_tree.clear() diff --git a/apworld/context.py b/apworld/context.py index e5ba3cf..86392f9 100644 --- a/apworld/context.py +++ b/apworld/context.py @@ -49,6 +49,7 @@ class Lingo2Manager: worldports: set[int] goaled: bool latches: set[int] + hinted_locations: set[int] def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): self.game_ctx = game_ctx @@ -67,6 +68,7 @@ class Lingo2Manager: self.worldports = set() self.goaled = False self.latches = set() + self.hinted_locations = set() def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: ret: dict[str, int] = {} @@ -99,6 +101,12 @@ class Lingo2Manager: return ret + def update_hinted_locations(self, new_locs: set[int]) -> set[int]: + ret = new_locs.difference(self.hinted_locations) + self.hinted_locations.update(new_locs) + + return ret + class Lingo2GameContext: server: Endpoint | None @@ -285,6 +293,17 @@ class Lingo2GameContext: async_start(self.send_msgs([msg]), name="set ignored locations") + def send_update_hinted_locations(self, hinted_locations): + if self.server is None: + return + + msg = { + "cmd": "UpdateHintedLocations", + "locations": hinted_locations, + } + + async_start(self.send_msgs([msg]), name="update hinted locations") + async def send_msgs(self, msgs: list[Any]) -> None: """ `msgs` JSON serializable """ if not self.server or not self.server.socket.open or self.server.socket.closed: @@ -299,6 +318,7 @@ class Lingo2ClientContext(CommonContext): items_handling = 0b111 slot_data: dict[str, Any] | None + hints_data_storage_key: str victory_data_storage_key: str def __init__(self, server_address: str | None = None, password: str | None = None): @@ -336,6 +356,7 @@ class Lingo2ClientContext(CommonContext): self.manager.tracker.set_checked_locations(self.checked_locations) self.manager.game_ctx.send_accessible_locations() + self.hints_data_storage_key = f"_read_hints_{self.team}_{self.slot}" 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"), @@ -463,6 +484,8 @@ class Lingo2ClientContext(CommonContext): for k, v in args["keys"].items(): if k == self.victory_data_storage_key: self.handle_status_update(v) + elif k == self.hints_data_storage_key: + self.update_hints() elif cmd == "SetReply": if args["key"] == self.get_datastorage_key("keyboard1"): self.handle_keyboard_update(1, args) @@ -482,6 +505,8 @@ class Lingo2ClientContext(CommonContext): self.manager.game_ctx.send_update_latches(updates) elif args["key"] == self.get_datastorage_key("ignored_locations"): self.manager.game_ctx.send_ignored_locations(args["value"]) + elif args["key"] == self.hints_data_storage_key: + self.update_hints() def get_datastorage_key(self, name: str): return f"Lingo2_{self.slot}_{name}" @@ -599,6 +624,14 @@ class Lingo2ClientContext(CommonContext): }] }]) + def update_hints(self): + hints = self.stored_data.get(self.hints_data_storage_key, []) + + hinted_locations = set(hint["location"] for hint in hints if hint["finding_player"] == self.slot) + updates = self.manager.update_hinted_locations(hinted_locations) + if len(updates) > 0: + self.manager.game_ctx.send_update_hinted_locations(updates) + async def pipe_loop(manager: Lingo2Manager): while not manager.client_ctx.exit_event.is_set(): -- cgit 1.4.1