about summary refs log tree commit diff stats
path: root/apworld/client/textclient.gd
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/client/textclient.gd')
-rw-r--r--apworld/client/textclient.gd438
1 files changed, 438 insertions, 0 deletions
diff --git a/apworld/client/textclient.gd b/apworld/client/textclient.gd new file mode 100644 index 0000000..f785a03 --- /dev/null +++ b/apworld/client/textclient.gd
@@ -0,0 +1,438 @@
1extends CanvasLayer
2
3var tabs
4var panel
5var label
6var entry
7var is_open = false
8
9var locations_overlay
10var location_texture
11var worldport_texture
12var goal_texture
13
14var tracker_tree
15var tracker_loc_tree_item_by_id = {}
16var tracker_port_tree_item_by_id = {}
17var tracker_goal_tree_item = null
18var tracker_object_by_index = {}
19
20var worldports_tab
21var worldports_tree
22var port_tree_item_by_map = {}
23var port_tree_item_by_map_port = {}
24
25const kLocation = 0
26const kWorldport = 1
27const kGoal = 2
28
29
30func _ready():
31 process_mode = ProcessMode.PROCESS_MODE_ALWAYS
32 layer = 2
33
34 locations_overlay = RichTextLabel.new()
35 locations_overlay.name = "LocationsOverlay"
36 locations_overlay.offset_top = 220
37 locations_overlay.offset_bottom = 720
38 locations_overlay.offset_left = 20
39 locations_overlay.anchor_right = 1.0
40 locations_overlay.offset_right = -10
41 locations_overlay.scroll_active = false
42 locations_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
43 locations_overlay.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
44 add_child(locations_overlay)
45 update_locations_visibility()
46
47 tabs = TabContainer.new()
48 tabs.name = "Tabs"
49 tabs.offset_left = 100
50 tabs.offset_right = 1820
51 tabs.offset_top = 100
52 tabs.offset_bottom = 980
53 tabs.visible = false
54 tabs.theme = preload("res://assets/themes/baseUI.tres")
55 tabs.add_theme_font_size_override("font_size", 36)
56 add_child(tabs)
57
58 panel = MarginContainer.new()
59 panel.name = "Text Client"
60 panel.add_theme_constant_override("margin_top", 60)
61 panel.add_theme_constant_override("margin_left", 60)
62 panel.add_theme_constant_override("margin_right", 60)
63 panel.add_theme_constant_override("margin_bottom", 60)
64 tabs.add_child(panel)
65
66 label = RichTextLabel.new()
67 label.set_name("Label")
68 label.scroll_following = true
69 label.selection_enabled = true
70 label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
71 label.size_flags_vertical = Control.SIZE_EXPAND_FILL
72 label.push_font(preload("res://assets/fonts/Lingo2.ttf"))
73 label.push_font_size(30)
74
75 var entry_style = StyleBoxFlat.new()
76 entry_style.bg_color = Color(0.9, 0.9, 0.9, 1)
77
78 entry = LineEdit.new()
79 entry.set_name("Entry")
80 entry.add_theme_font_override("font", preload("res://assets/fonts/Lingo2.ttf"))
81 entry.add_theme_font_size_override("font_size", 36)
82 entry.add_theme_color_override("font_color", Color(0, 0, 0, 1))
83 entry.add_theme_color_override("cursor_color", Color(0, 0, 0, 1))
84 entry.add_theme_stylebox_override("focus", entry_style)
85 entry.text_submitted.connect(text_entered)
86
87 var tc_arranger = VBoxContainer.new()
88 tc_arranger.add_child(label)
89 tc_arranger.add_child(entry)
90 tc_arranger.add_theme_constant_override("separation", 40)
91 panel.add_child(tc_arranger)
92
93 var tracker_margins = MarginContainer.new()
94 tracker_margins.name = "Locations"
95 tracker_margins.add_theme_constant_override("margin_top", 60)
96 tracker_margins.add_theme_constant_override("margin_left", 60)
97 tracker_margins.add_theme_constant_override("margin_right", 60)
98 tracker_margins.add_theme_constant_override("margin_bottom", 60)
99 tabs.add_child(tracker_margins)
100
101 tracker_tree = Tree.new()
102 tracker_tree.columns = 3
103 tracker_tree.hide_root = true
104 tracker_tree.add_theme_font_size_override("font_size", 24)
105 tracker_tree.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8, 1))
106 tracker_tree.add_theme_constant_override("v_separation", 1)
107 tracker_tree.item_edited.connect(_on_tracker_button_clicked)
108 tracker_tree.set_column_expand(0, false)
109 tracker_tree.set_column_expand(1, true)
110 tracker_tree.set_column_expand(2, false)
111 tracker_tree.set_column_custom_minimum_width(2, 200)
112 tracker_margins.add_child(tracker_tree)
113
114 worldports_tab = MarginContainer.new()
115 worldports_tab.name = "Worldports"
116 worldports_tab.add_theme_constant_override("margin_top", 60)
117 worldports_tab.add_theme_constant_override("margin_left", 60)
118 worldports_tab.add_theme_constant_override("margin_right", 60)
119 worldports_tab.add_theme_constant_override("margin_bottom", 60)
120 tabs.add_child(worldports_tab)
121 tabs.set_tab_hidden(2, true)
122
123 worldports_tree = Tree.new()
124 worldports_tree.columns = 2
125 worldports_tree.hide_root = true
126 worldports_tree.theme = preload("res://assets/themes/baseUI.tres")
127 worldports_tree.add_theme_font_size_override("font_size", 24)
128 worldports_tab.add_child(worldports_tree)
129
130 var runtime = global.get_node("Runtime")
131 var location_image = Image.new()
132 location_image.load_png_from_buffer(runtime.read_path("assets/location.png"))
133 location_texture = ImageTexture.create_from_image(location_image)
134
135 var worldport_image = Image.new()
136 worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png"))
137 worldport_texture = ImageTexture.create_from_image(worldport_image)
138
139 var goal_image = Image.new()
140 goal_image.load_png_from_buffer(runtime.read_path("assets/goal.png"))
141 goal_texture = ImageTexture.create_from_image(goal_image)
142
143
144func _input(event):
145 if global.loaded and event is InputEventKey and event.pressed:
146 if event.keycode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT):
147 if !get_tree().paused:
148 is_open = true
149 get_tree().paused = true
150 Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
151 tabs.visible = true
152 entry.grab_focus()
153 get_viewport().set_input_as_handled()
154 else:
155 dismiss()
156 elif event.keycode == KEY_ESCAPE:
157 if is_open:
158 dismiss()
159 get_viewport().set_input_as_handled()
160
161
162func dismiss():
163 if is_open:
164 get_tree().paused = false
165 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
166 tabs.visible = false
167 is_open = false
168
169
170func parse_printjson(text):
171 label.append_text("[p]" + text + "[/p]")
172
173
174func text_entered(text):
175 var ap = global.get_node("Archipelago")
176 var cmd = text.trim_suffix("\n")
177 entry.text = ""
178 if OS.is_debug_build():
179 if cmd.begins_with("/tp_map "):
180 var new_map = cmd.substr(8)
181 global.map = new_map
182 global.sets_entry_point = false
183 switcher.switch_map("res://objects/scenes/%s.tscn" % new_map)
184 return
185
186 ap.client.say(cmd)
187
188
189func update_locations(reset_locations = true):
190 var ap = global.get_node("Archipelago")
191 var gamedata = global.get_node("Gamedata")
192
193 locations_overlay.clear()
194 locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf"))
195 locations_overlay.push_font_size(24)
196 locations_overlay.push_color(Color(0.9, 0.9, 0.9, 1))
197 locations_overlay.push_outline_color(Color(0, 0, 0, 1))
198 locations_overlay.push_outline_size(2)
199
200 var locations = []
201 for location_id in ap.client._accessible_locations:
202 if not ap.client._checked_locations.has(location_id):
203 var location_name = gamedata.location_name_by_id.get(location_id, "(Unknown)")
204 (
205 locations
206 . append(
207 {
208 "name": location_name,
209 "type": kLocation,
210 "id": location_id,
211 }
212 )
213 )
214
215 for port_id in ap.client._accessible_worldports:
216 if not ap.client._checked_worldports.has(port_id):
217 var port_name = gamedata.get_worldport_display_name(port_id)
218 (
219 locations
220 . append(
221 {
222 "name": port_name,
223 "type": kWorldport,
224 "id": port_id,
225 }
226 )
227 )
228
229 locations.sort_custom(func(a, b): return a["name"] < b["name"])
230
231 if ap.client._goal_accessible:
232 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
233 ap.victory_condition
234 ]]
235 (
236 locations
237 . push_front(
238 {
239 "name": location_name,
240 "type": kGoal,
241 }
242 )
243 )
244
245 var count = 0
246 for location in locations:
247 if count < 18:
248 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT)
249 locations_overlay.append_text(location["name"])
250 locations_overlay.append_text(" ")
251 if location["type"] == kLocation:
252 locations_overlay.add_image(location_texture)
253 elif location["type"] == kWorldport:
254 locations_overlay.add_image(worldport_texture)
255 elif location["type"] == kGoal:
256 locations_overlay.add_image(goal_texture)
257 locations_overlay.pop()
258 count += 1
259
260 if count > 18:
261 locations_overlay.append_text("[p align=right][lb]...[rb][/p]")
262
263 if reset_locations:
264 reset_tracker_tab()
265
266 var root_ti = tracker_tree.create_item(null)
267
268 for location in locations:
269 var loc_row = root_ti.create_child()
270 loc_row.set_cell_mode(0, TreeItem.CELL_MODE_ICON)
271 loc_row.set_selectable(0, false)
272 loc_row.set_text(1, location["name"])
273 loc_row.set_selectable(1, false)
274 loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM)
275 loc_row.set_text(2, "Show Path")
276 loc_row.set_custom_as_button(2, true)
277 loc_row.set_editable(2, true)
278 loc_row.set_selectable(2, false)
279 loc_row.set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER)
280
281 if location["type"] == kLocation:
282 loc_row.set_icon(0, location_texture)
283 tracker_loc_tree_item_by_id[location["id"]] = loc_row
284 elif location["type"] == kWorldport:
285 loc_row.set_icon(0, worldport_texture)
286 tracker_port_tree_item_by_id[location["id"]] = loc_row
287 elif location["type"] == kGoal:
288 loc_row.set_icon(0, goal_texture)
289 tracker_goal_tree_item = loc_row
290
291 tracker_object_by_index[loc_row.get_index()] = location
292 else:
293 for loc_row in tracker_tree.get_root().get_children():
294 loc_row.visible = false
295
296 for location_id in tracker_loc_tree_item_by_id.keys():
297 if (
298 ap.client._accessible_locations.has(location_id)
299 and not ap.client._checked_locations.has(location_id)
300 ):
301 tracker_loc_tree_item_by_id[location_id].visible = true
302
303 for port_id in tracker_port_tree_item_by_id.keys():
304 if (
305 ap.client._accessible_worldports.has(port_id)
306 and not ap.client._checked_worldports.has(port_id)
307 ):
308 tracker_port_tree_item_by_id[port_id].visible = true
309
310 if tracker_goal_tree_item != null and ap.client._goal_accessible:
311 tracker_goal_tree_item.visible = true
312
313
314func update_locations_visibility():
315 var ap = global.get_node("Archipelago")
316 locations_overlay.visible = ap.show_locations
317
318
319func _on_tracker_button_clicked():
320 var edited_item = tracker_tree.get_edited()
321 var edited_index = edited_item.get_index()
322
323 if tracker_object_by_index.has(edited_index):
324 var tracker_object = tracker_object_by_index[edited_index]
325 var ap = global.get_node("Archipelago")
326 var type_str = ""
327 if tracker_object["type"] == kLocation:
328 type_str = "location"
329 elif tracker_object["type"] == kWorldport:
330 type_str = "worldport"
331 elif tracker_object["type"] == kGoal:
332 type_str = "goal"
333 ap.client.getLogicalPath(type_str, tracker_object.get("id", null))
334
335
336func display_logical_path(object_type, object_id, paths):
337 var ap = global.get_node("Archipelago")
338 var gamedata = global.get_node("Gamedata")
339
340 var location_name = "(Unknown)"
341 if object_type == "location" and object_id != null:
342 location_name = gamedata.location_name_by_id.get(object_id, "(Unknown)")
343 elif object_type == "worldport" and object_id != null:
344 location_name = gamedata.get_worldport_display_name(object_id)
345 elif object_type == "goal":
346 location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
347 ap.victory_condition
348 ]]
349
350 label.append_text("[p]Path to %s:[/p]" % location_name)
351 label.append_text("[ol]" + "\n".join(paths) + "[/ol]")
352
353 panel.visible = true
354
355
356func setup_worldports():
357 tabs.set_tab_hidden(2, false)
358
359 var root_ti = worldports_tree.create_item(null)
360
361 var ports_by_map_id = {}
362 var display_names_by_map_id = {}
363 var display_names_by_port_id = {}
364
365 var ap = global.get_node("Archipelago")
366 var gamedata = global.get_node("Gamedata")
367 for fpid in ap.port_pairings:
368 var port = gamedata.objects.get_ports()[fpid]
369 var room = gamedata.objects.get_rooms()[port.get_room_id()]
370
371 if not ports_by_map_id.has(room.get_map_id()):
372 ports_by_map_id[room.get_map_id()] = []
373
374 var map = gamedata.objects.get_maps()[room.get_map_id()]
375 display_names_by_map_id[map.get_id()] = map.get_display_name()
376
377 ports_by_map_id[room.get_map_id()].append(fpid)
378 display_names_by_port_id[fpid] = port.get_display_name()
379
380 var sorted_map_ids = ports_by_map_id.keys().duplicate()
381 sorted_map_ids.sort_custom(
382 func(a, b): return display_names_by_map_id[a] < display_names_by_map_id[b]
383 )
384
385 for map_id in sorted_map_ids:
386 var map_ti = root_ti.create_child()
387 map_ti.set_text(0, display_names_by_map_id[map_id])
388 map_ti.visible = false
389 map_ti.collapsed = true
390 port_tree_item_by_map[map_id] = map_ti
391 port_tree_item_by_map_port[map_id] = {}
392
393 var port_ids = ports_by_map_id[map_id]
394 port_ids.sort_custom(
395 func(a, b): return display_names_by_port_id[a] < display_names_by_port_id[b]
396 )
397
398 for port_id in port_ids:
399 var port_ti = map_ti.create_child()
400 port_ti.set_text(0, display_names_by_port_id[port_id])
401 port_ti.set_text(1, gamedata.get_worldport_display_name(ap.port_pairings[port_id]))
402 port_ti.visible = false
403 port_tree_item_by_map_port[map_id][port_id] = port_ti
404
405 update_worldports()
406
407
408func update_worldports():
409 var ap = global.get_node("Archipelago")
410
411 for map_id in port_tree_item_by_map_port.keys():
412 var map_visible = false
413
414 for port_id in port_tree_item_by_map_port[map_id].keys():
415 var ti = port_tree_item_by_map_port[map_id][port_id]
416 ti.visible = ap.client._checked_worldports.has(port_id)
417
418 if ti.visible:
419 map_visible = true
420
421 port_tree_item_by_map[map_id].visible = map_visible
422
423
424func reset():
425 locations_overlay.clear()
426 tabs.set_tab_hidden(2, true)
427 port_tree_item_by_map.clear()
428 port_tree_item_by_map_port.clear()
429 worldports_tree.clear()
430 reset_tracker_tab()
431
432
433func reset_tracker_tab():
434 tracker_loc_tree_item_by_id.clear()
435 tracker_port_tree_item_by_id.clear()
436 tracker_goal_tree_item = null
437 tracker_object_by_index.clear()
438 tracker_tree.clear()