about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--apworld/client/client.gd10
-rw-r--r--apworld/client/manager.gd7
-rw-r--r--apworld/client/textclient.gd48
-rw-r--r--apworld/context.py33
4 files changed, 84 insertions, 14 deletions
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 = []
26var _accessible_worldports = [] 26var _accessible_worldports = []
27var _goal_accessible = false 27var _goal_accessible = false
28var _latched_doors = [] 28var _latched_doors = []
29var _hinted_locations = []
29 30
30signal could_not_connect 31signal could_not_connect
31signal connect_status 32signal connect_status
@@ -41,6 +42,7 @@ signal checked_locations_updated
41signal ignored_locations_updated(locations) 42signal ignored_locations_updated(locations)
42signal checked_worldports_updated 43signal checked_worldports_updated
43signal keyboard_update_received 44signal keyboard_update_received
45signal hinted_locations_updated
44 46
45 47
46func _init(): 48func _init():
@@ -207,6 +209,14 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
207 209
208 ignored_locations_updated.emit(locs) 210 ignored_locations_updated.emit(locs)
209 211
212 elif cmd == "UpdateHintedLocations":
213 for id in message["locations"]:
214 var iid = int(id)
215 if !_hinted_locations.has(iid):
216 _hinted_locations.append(iid)
217
218 hinted_locations_updated.emit()
219
210 220
211func connectToServer(server, un, pw): 221func connectToServer(server, un, pw):
212 sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) 222 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():
146 client.accessible_locations_updated.connect(_on_accessible_locations_updated) 146 client.accessible_locations_updated.connect(_on_accessible_locations_updated)
147 client.checked_locations_updated.connect(_on_checked_locations_updated) 147 client.checked_locations_updated.connect(_on_checked_locations_updated)
148 client.ignored_locations_updated.connect(_on_ignored_locations_updated) 148 client.ignored_locations_updated.connect(_on_ignored_locations_updated)
149 client.hinted_locations_updated.connect(_on_hinted_locations_updated)
149 client.checked_worldports_updated.connect(_on_checked_worldports_updated) 150 client.checked_worldports_updated.connect(_on_checked_worldports_updated)
150 client.door_latched.connect(_on_door_latched) 151 client.door_latched.connect(_on_door_latched)
151 152
@@ -394,6 +395,12 @@ func _on_ignored_locations_updated(locations):
394 textclient_node.update_locations() 395 textclient_node.update_locations()
395 396
396 397
398func _on_hinted_locations_updated():
399 var textclient_node = global.get_node("Textclient")
400 if textclient_node != null:
401 textclient_node.update_locations()
402
403
397func _on_door_latched(door_id): 404func _on_door_latched(door_id):
398 var gamedata = global.get_node("Gamedata") 405 var gamedata = global.get_node("Gamedata")
399 if gamedata.get_door_map_name(door_id) != global.map: 406 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 = {}
17var tracker_goal_tree_item = null 17var tracker_goal_tree_item = null
18var tracker_object_by_index = {} 18var tracker_object_by_index = {}
19var tracker_object_by_ignored_index = {} 19var tracker_object_by_ignored_index = {}
20var tracker_ignored_group = null
20 21
21var worldports_tab 22var worldports_tab
22var worldports_tree 23var worldports_tree
@@ -212,6 +213,7 @@ func update_locations(reset_locations = true):
212 "type": kLocation, 213 "type": kLocation,
213 "id": location_id, 214 "id": location_id,
214 "ignored": ap._ignored_locations.has(location_id), 215 "ignored": ap._ignored_locations.has(location_id),
216 "hint": ap.client._hinted_locations.has(location_id),
215 } 217 }
216 ) 218 )
217 ) 219 )
@@ -227,13 +229,12 @@ func update_locations(reset_locations = true):
227 "type": kWorldport, 229 "type": kWorldport,
228 "id": port_id, 230 "id": port_id,
229 "ignored": false, 231 "ignored": false,
232 "hint": false,
230 } 233 }
231 ) 234 )
232 ) 235 )
233 236
234 locations.sort_custom( 237 locations.sort_custom(_cmp_tracker_objects)
235 func(a, b): return a["name"] < b["name"] if a["ignored"] == b["ignored"] else !a["ignored"]
236 )
237 238
238 if ap.client._goal_accessible: 239 if ap.client._goal_accessible:
239 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ 240 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
@@ -246,6 +247,7 @@ func update_locations(reset_locations = true):
246 "name": location_name, 247 "name": location_name,
247 "type": kGoal, 248 "type": kGoal,
248 "ignored": false, 249 "ignored": false,
250 "hint": false,
249 } 251 }
250 ) 252 )
251 ) 253 )
@@ -254,6 +256,8 @@ func update_locations(reset_locations = true):
254 for location in locations: 256 for location in locations:
255 if count < 18 and not location["ignored"]: 257 if count < 18 and not location["ignored"]:
256 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) 258 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT)
259 if location["hint"]:
260 locations_overlay.push_color(Color("#fafad2"))
257 locations_overlay.append_text(location["name"]) 261 locations_overlay.append_text(location["name"])
258 locations_overlay.append_text(" ") 262 locations_overlay.append_text(" ")
259 if location["type"] == kLocation: 263 if location["type"] == kLocation:
@@ -262,6 +266,8 @@ func update_locations(reset_locations = true):
262 locations_overlay.add_image(worldport_texture) 266 locations_overlay.add_image(worldport_texture)
263 elif location["type"] == kGoal: 267 elif location["type"] == kGoal:
264 locations_overlay.add_image(goal_texture) 268 locations_overlay.add_image(goal_texture)
269 if location["hint"]:
270 locations_overlay.pop()
265 locations_overlay.pop() 271 locations_overlay.pop()
266 count += 1 272 count += 1
267 273
@@ -272,21 +278,20 @@ func update_locations(reset_locations = true):
272 reset_tracker_tab() 278 reset_tracker_tab()
273 279
274 var root_ti = tracker_tree.create_item(null) 280 var root_ti = tracker_tree.create_item(null)
275 var ignored_loc_header = null
276 281
277 for location in locations: 282 for location in locations:
278 var loc_row 283 var loc_row
279 284
280 if location["ignored"]: 285 if location["ignored"]:
281 if ignored_loc_header == null: 286 if tracker_ignored_group == null:
282 ignored_loc_header = root_ti.create_child() 287 tracker_ignored_group = root_ti.create_child()
283 ignored_loc_header.set_text(1, "Ignored Locations") 288 tracker_ignored_group.set_text(1, "Ignored Locations")
284 ignored_loc_header.set_selectable(0, false) 289 tracker_ignored_group.set_selectable(0, false)
285 ignored_loc_header.set_selectable(1, false) 290 tracker_ignored_group.set_selectable(1, false)
286 ignored_loc_header.set_selectable(2, false) 291 tracker_ignored_group.set_selectable(2, false)
287 ignored_loc_header.set_selectable(3, false) 292 tracker_ignored_group.set_selectable(3, false)
288 293
289 loc_row = ignored_loc_header.create_child() 294 loc_row = tracker_ignored_group.create_child()
290 else: 295 else:
291 loc_row = root_ti.create_child() 296 loc_row = root_ti.create_child()
292 297
@@ -294,6 +299,8 @@ func update_locations(reset_locations = true):
294 loc_row.set_selectable(0, false) 299 loc_row.set_selectable(0, false)
295 loc_row.set_text(1, location["name"]) 300 loc_row.set_text(1, location["name"])
296 loc_row.set_selectable(1, false) 301 loc_row.set_selectable(1, false)
302 if location["hint"]:
303 loc_row.set_custom_color(1, Color("#fafad2"))
297 loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM) 304 loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM)
298 loc_row.set_text(2, "Show Path") 305 loc_row.set_text(2, "Show Path")
299 loc_row.set_custom_as_button(2, true) 306 loc_row.set_custom_as_button(2, true)
@@ -346,6 +353,18 @@ func update_locations(reset_locations = true):
346 if tracker_goal_tree_item != null and ap.client._goal_accessible: 353 if tracker_goal_tree_item != null and ap.client._goal_accessible:
347 tracker_goal_tree_item.visible = true 354 tracker_goal_tree_item.visible = true
348 355
356 if tracker_ignored_group != null:
357 tracker_ignored_group.visible = true
358
359
360func _cmp_tracker_objects(a, b) -> bool:
361 if a["ignored"] != b["ignored"]:
362 return !a["ignored"]
363 elif a["hint"] != b["hint"]:
364 return a["hint"]
365 else:
366 return a["name"] < b["name"]
367
349 368
350func update_locations_visibility(): 369func update_locations_visibility():
351 var ap = global.get_node("Archipelago") 370 var ap = global.get_node("Archipelago")
@@ -372,7 +391,7 @@ func _on_tracker_button_clicked():
372 ap.client.getLogicalPath(type_str, tracker_object.get("id", null)) 391 ap.client.getLogicalPath(type_str, tracker_object.get("id", null))
373 elif tracker_tree.get_edited_column() == 3: 392 elif tracker_tree.get_edited_column() == 3:
374 ap.toggle_ignored_location(tracker_object["id"]) 393 ap.toggle_ignored_location(tracker_object["id"])
375 else: 394 elif edited_item.get_parent() == tracker_ignored_group:
376 # This is the ignored locations group. 395 # This is the ignored locations group.
377 if ( 396 if (
378 tracker_object_by_ignored_index.has(edited_index) 397 tracker_object_by_ignored_index.has(edited_index)
@@ -485,4 +504,5 @@ func reset_tracker_tab():
485 tracker_goal_tree_item = null 504 tracker_goal_tree_item = null
486 tracker_object_by_index.clear() 505 tracker_object_by_index.clear()
487 tracker_object_by_ignored_index.clear() 506 tracker_object_by_ignored_index.clear()
507 tracker_ignored_group = null
488 tracker_tree.clear() 508 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:
49 worldports: set[int] 49 worldports: set[int]
50 goaled: bool 50 goaled: bool
51 latches: set[int] 51 latches: set[int]
52 hinted_locations: set[int]
52 53
53 def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): 54 def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"):
54 self.game_ctx = game_ctx 55 self.game_ctx = game_ctx
@@ -67,6 +68,7 @@ class Lingo2Manager:
67 self.worldports = set() 68 self.worldports = set()
68 self.goaled = False 69 self.goaled = False
69 self.latches = set() 70 self.latches = set()
71 self.hinted_locations = set()
70 72
71 def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: 73 def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]:
72 ret: dict[str, int] = {} 74 ret: dict[str, int] = {}
@@ -99,6 +101,12 @@ class Lingo2Manager:
99 101
100 return ret 102 return ret
101 103
104 def update_hinted_locations(self, new_locs: set[int]) -> set[int]:
105 ret = new_locs.difference(self.hinted_locations)
106 self.hinted_locations.update(new_locs)
107
108 return ret
109
102 110
103class Lingo2GameContext: 111class Lingo2GameContext:
104 server: Endpoint | None 112 server: Endpoint | None
@@ -285,6 +293,17 @@ class Lingo2GameContext:
285 293
286 async_start(self.send_msgs([msg]), name="set ignored locations") 294 async_start(self.send_msgs([msg]), name="set ignored locations")
287 295
296 def send_update_hinted_locations(self, hinted_locations):
297 if self.server is None:
298 return
299
300 msg = {
301 "cmd": "UpdateHintedLocations",
302 "locations": hinted_locations,
303 }
304
305 async_start(self.send_msgs([msg]), name="update hinted locations")
306
288 async def send_msgs(self, msgs: list[Any]) -> None: 307 async def send_msgs(self, msgs: list[Any]) -> None:
289 """ `msgs` JSON serializable """ 308 """ `msgs` JSON serializable """
290 if not self.server or not self.server.socket.open or self.server.socket.closed: 309 if not self.server or not self.server.socket.open or self.server.socket.closed:
@@ -299,6 +318,7 @@ class Lingo2ClientContext(CommonContext):
299 items_handling = 0b111 318 items_handling = 0b111
300 319
301 slot_data: dict[str, Any] | None 320 slot_data: dict[str, Any] | None
321 hints_data_storage_key: str
302 victory_data_storage_key: str 322 victory_data_storage_key: str
303 323
304 def __init__(self, server_address: str | None = None, password: str | None = None): 324 def __init__(self, server_address: str | None = None, password: str | None = None):
@@ -336,6 +356,7 @@ class Lingo2ClientContext(CommonContext):
336 self.manager.tracker.set_checked_locations(self.checked_locations) 356 self.manager.tracker.set_checked_locations(self.checked_locations)
337 self.manager.game_ctx.send_accessible_locations() 357 self.manager.game_ctx.send_accessible_locations()
338 358
359 self.hints_data_storage_key = f"_read_hints_{self.team}_{self.slot}"
339 self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}" 360 self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}"
340 361
341 self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"), 362 self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"),
@@ -463,6 +484,8 @@ class Lingo2ClientContext(CommonContext):
463 for k, v in args["keys"].items(): 484 for k, v in args["keys"].items():
464 if k == self.victory_data_storage_key: 485 if k == self.victory_data_storage_key:
465 self.handle_status_update(v) 486 self.handle_status_update(v)
487 elif k == self.hints_data_storage_key:
488 self.update_hints()
466 elif cmd == "SetReply": 489 elif cmd == "SetReply":
467 if args["key"] == self.get_datastorage_key("keyboard1"): 490 if args["key"] == self.get_datastorage_key("keyboard1"):
468 self.handle_keyboard_update(1, args) 491 self.handle_keyboard_update(1, args)
@@ -482,6 +505,8 @@ class Lingo2ClientContext(CommonContext):
482 self.manager.game_ctx.send_update_latches(updates) 505 self.manager.game_ctx.send_update_latches(updates)
483 elif args["key"] == self.get_datastorage_key("ignored_locations"): 506 elif args["key"] == self.get_datastorage_key("ignored_locations"):
484 self.manager.game_ctx.send_ignored_locations(args["value"]) 507 self.manager.game_ctx.send_ignored_locations(args["value"])
508 elif args["key"] == self.hints_data_storage_key:
509 self.update_hints()
485 510
486 def get_datastorage_key(self, name: str): 511 def get_datastorage_key(self, name: str):
487 return f"Lingo2_{self.slot}_{name}" 512 return f"Lingo2_{self.slot}_{name}"
@@ -599,6 +624,14 @@ class Lingo2ClientContext(CommonContext):
599 }] 624 }]
600 }]) 625 }])
601 626
627 def update_hints(self):
628 hints = self.stored_data.get(self.hints_data_storage_key, [])
629
630 hinted_locations = set(hint["location"] for hint in hints if hint["finding_player"] == self.slot)
631 updates = self.manager.update_hinted_locations(hinted_locations)
632 if len(updates) > 0:
633 self.manager.game_ctx.send_update_hinted_locations(updates)
634
602 635
603async def pipe_loop(manager: Lingo2Manager): 636async def pipe_loop(manager: Lingo2Manager):
604 while not manager.client_ctx.exit_event.is_set(): 637 while not manager.client_ctx.exit_event.is_set():