diff options
Diffstat (limited to 'apworld/client/textclient.gd')
| -rw-r--r-- | apworld/client/textclient.gd | 310 |
1 files changed, 310 insertions, 0 deletions
| diff --git a/apworld/client/textclient.gd b/apworld/client/textclient.gd new file mode 100644 index 0000000..530eddb --- /dev/null +++ b/apworld/client/textclient.gd | |||
| @@ -0,0 +1,310 @@ | |||
| 1 | extends CanvasLayer | ||
| 2 | |||
| 3 | var tabs | ||
| 4 | var panel | ||
| 5 | var label | ||
| 6 | var entry | ||
| 7 | var tracker_label | ||
| 8 | var is_open = false | ||
| 9 | |||
| 10 | var locations_overlay | ||
| 11 | var location_texture | ||
| 12 | var worldport_texture | ||
| 13 | var goal_texture | ||
| 14 | |||
| 15 | var worldports_tab | ||
| 16 | var worldports_tree | ||
| 17 | var port_tree_item_by_map = {} | ||
| 18 | var port_tree_item_by_map_port = {} | ||
| 19 | |||
| 20 | |||
| 21 | func _ready(): | ||
| 22 | process_mode = ProcessMode.PROCESS_MODE_ALWAYS | ||
| 23 | layer = 2 | ||
| 24 | |||
| 25 | locations_overlay = RichTextLabel.new() | ||
| 26 | locations_overlay.name = "LocationsOverlay" | ||
| 27 | locations_overlay.offset_top = 220 | ||
| 28 | locations_overlay.offset_bottom = 720 | ||
| 29 | locations_overlay.offset_left = 20 | ||
| 30 | locations_overlay.anchor_right = 1.0 | ||
| 31 | locations_overlay.offset_right = -10 | ||
| 32 | locations_overlay.scroll_active = false | ||
| 33 | locations_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE | ||
| 34 | locations_overlay.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST | ||
| 35 | add_child(locations_overlay) | ||
| 36 | update_locations_visibility() | ||
| 37 | |||
| 38 | tabs = TabContainer.new() | ||
| 39 | tabs.name = "Tabs" | ||
| 40 | tabs.offset_left = 100 | ||
| 41 | tabs.offset_right = 1820 | ||
| 42 | tabs.offset_top = 100 | ||
| 43 | tabs.offset_bottom = 980 | ||
| 44 | tabs.visible = false | ||
| 45 | tabs.theme = preload("res://assets/themes/baseUI.tres") | ||
| 46 | tabs.add_theme_font_size_override("font_size", 36) | ||
| 47 | add_child(tabs) | ||
| 48 | |||
| 49 | panel = MarginContainer.new() | ||
| 50 | panel.name = "Text Client" | ||
| 51 | panel.add_theme_constant_override("margin_top", 60) | ||
| 52 | panel.add_theme_constant_override("margin_left", 60) | ||
| 53 | panel.add_theme_constant_override("margin_right", 60) | ||
| 54 | panel.add_theme_constant_override("margin_bottom", 60) | ||
| 55 | tabs.add_child(panel) | ||
| 56 | |||
| 57 | label = RichTextLabel.new() | ||
| 58 | label.set_name("Label") | ||
| 59 | label.scroll_following = true | ||
| 60 | label.selection_enabled = true | ||
| 61 | label.size_flags_horizontal = Control.SIZE_EXPAND_FILL | ||
| 62 | label.size_flags_vertical = Control.SIZE_EXPAND_FILL | ||
| 63 | label.push_font(preload("res://assets/fonts/Lingo2.ttf")) | ||
| 64 | label.push_font_size(36) | ||
| 65 | |||
| 66 | var entry_style = StyleBoxFlat.new() | ||
| 67 | entry_style.bg_color = Color(0.9, 0.9, 0.9, 1) | ||
| 68 | |||
| 69 | entry = LineEdit.new() | ||
| 70 | entry.set_name("Entry") | ||
| 71 | entry.add_theme_font_override("font", preload("res://assets/fonts/Lingo2.ttf")) | ||
| 72 | entry.add_theme_font_size_override("font_size", 36) | ||
| 73 | entry.add_theme_color_override("font_color", Color(0, 0, 0, 1)) | ||
| 74 | entry.add_theme_color_override("cursor_color", Color(0, 0, 0, 1)) | ||
| 75 | entry.add_theme_stylebox_override("focus", entry_style) | ||
| 76 | entry.text_submitted.connect(text_entered) | ||
| 77 | |||
| 78 | var tc_arranger = VBoxContainer.new() | ||
| 79 | tc_arranger.add_child(label) | ||
| 80 | tc_arranger.add_child(entry) | ||
| 81 | tc_arranger.add_theme_constant_override("separation", 40) | ||
| 82 | panel.add_child(tc_arranger) | ||
| 83 | |||
| 84 | var tracker_margins = MarginContainer.new() | ||
| 85 | tracker_margins.name = "Locations" | ||
| 86 | tracker_margins.add_theme_constant_override("margin_top", 60) | ||
| 87 | tracker_margins.add_theme_constant_override("margin_left", 60) | ||
| 88 | tracker_margins.add_theme_constant_override("margin_right", 60) | ||
| 89 | tracker_margins.add_theme_constant_override("margin_bottom", 60) | ||
| 90 | tabs.add_child(tracker_margins) | ||
| 91 | |||
| 92 | tracker_label = RichTextLabel.new() | ||
| 93 | tracker_margins.add_child(tracker_label) | ||
| 94 | |||
| 95 | worldports_tab = MarginContainer.new() | ||
| 96 | worldports_tab.name = "Worldports" | ||
| 97 | worldports_tab.add_theme_constant_override("margin_top", 60) | ||
| 98 | worldports_tab.add_theme_constant_override("margin_left", 60) | ||
| 99 | worldports_tab.add_theme_constant_override("margin_right", 60) | ||
| 100 | worldports_tab.add_theme_constant_override("margin_bottom", 60) | ||
| 101 | tabs.add_child(worldports_tab) | ||
| 102 | tabs.set_tab_hidden(2, true) | ||
| 103 | |||
| 104 | worldports_tree = Tree.new() | ||
| 105 | worldports_tree.columns = 2 | ||
| 106 | worldports_tree.hide_root = true | ||
| 107 | worldports_tree.theme = preload("res://assets/themes/baseUI.tres") | ||
| 108 | worldports_tree.add_theme_font_size_override("font_size", 24) | ||
| 109 | worldports_tab.add_child(worldports_tree) | ||
| 110 | |||
| 111 | var runtime = global.get_node("Runtime") | ||
| 112 | var location_image = Image.new() | ||
| 113 | location_image.load_png_from_buffer(runtime.read_path("assets/location.png")) | ||
| 114 | location_texture = ImageTexture.create_from_image(location_image) | ||
| 115 | |||
| 116 | var worldport_image = Image.new() | ||
| 117 | worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png")) | ||
| 118 | worldport_texture = ImageTexture.create_from_image(worldport_image) | ||
| 119 | |||
| 120 | var goal_image = Image.new() | ||
| 121 | goal_image.load_png_from_buffer(runtime.read_path("assets/goal.png")) | ||
| 122 | goal_texture = ImageTexture.create_from_image(goal_image) | ||
| 123 | |||
| 124 | |||
| 125 | func _input(event): | ||
| 126 | if global.loaded and event is InputEventKey and event.pressed: | ||
| 127 | if event.keycode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT): | ||
| 128 | if !get_tree().paused: | ||
| 129 | is_open = true | ||
| 130 | get_tree().paused = true | ||
| 131 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) | ||
| 132 | tabs.visible = true | ||
| 133 | entry.grab_focus() | ||
| 134 | get_viewport().set_input_as_handled() | ||
| 135 | else: | ||
| 136 | dismiss() | ||
| 137 | elif event.keycode == KEY_ESCAPE: | ||
| 138 | if is_open: | ||
| 139 | dismiss() | ||
| 140 | get_viewport().set_input_as_handled() | ||
| 141 | |||
| 142 | |||
| 143 | func dismiss(): | ||
| 144 | if is_open: | ||
| 145 | get_tree().paused = false | ||
| 146 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) | ||
| 147 | tabs.visible = false | ||
| 148 | is_open = false | ||
| 149 | |||
| 150 | |||
| 151 | func parse_printjson(text): | ||
| 152 | label.append_text("[p]" + text + "[/p]") | ||
| 153 | |||
| 154 | |||
| 155 | func text_entered(text): | ||
| 156 | var ap = global.get_node("Archipelago") | ||
| 157 | var cmd = text.trim_suffix("\n") | ||
| 158 | entry.text = "" | ||
| 159 | if OS.is_debug_build(): | ||
| 160 | if cmd.begins_with("/tp_map "): | ||
| 161 | var new_map = cmd.substr(8) | ||
| 162 | global.map = new_map | ||
| 163 | global.sets_entry_point = false | ||
| 164 | switcher.switch_map("res://objects/scenes/%s.tscn" % new_map) | ||
| 165 | return | ||
| 166 | |||
| 167 | ap.client.say(cmd) | ||
| 168 | |||
| 169 | |||
| 170 | func update_locations(): | ||
| 171 | var ap = global.get_node("Archipelago") | ||
| 172 | var gamedata = global.get_node("Gamedata") | ||
| 173 | |||
| 174 | tracker_label.clear() | ||
| 175 | tracker_label.push_font(preload("res://assets/fonts/Lingo2.ttf")) | ||
| 176 | tracker_label.push_font_size(24) | ||
| 177 | |||
| 178 | locations_overlay.clear() | ||
| 179 | locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf")) | ||
| 180 | locations_overlay.push_font_size(24) | ||
| 181 | locations_overlay.push_color(Color(0.9, 0.9, 0.9, 1)) | ||
| 182 | locations_overlay.push_outline_color(Color(0, 0, 0, 1)) | ||
| 183 | locations_overlay.push_outline_size(2) | ||
| 184 | |||
| 185 | const kLocation = 0 | ||
| 186 | const kWorldport = 1 | ||
| 187 | const kGoal = 2 | ||
| 188 | |||
| 189 | var location_names = [] | ||
| 190 | var type_by_name = {} | ||
| 191 | for location_id in ap.client._accessible_locations: | ||
| 192 | if not ap.client._checked_locations.has(location_id): | ||
| 193 | var location_name = gamedata.location_name_by_id.get(location_id, "(Unknown)") | ||
| 194 | location_names.append(location_name) | ||
| 195 | type_by_name[location_name] = kLocation | ||
| 196 | |||
| 197 | for port_id in ap.client._accessible_worldports: | ||
| 198 | if not ap.client._checked_worldports.has(port_id): | ||
| 199 | var port_name = gamedata.get_worldport_display_name(port_id) | ||
| 200 | location_names.append(port_name) | ||
| 201 | type_by_name[port_name] = kWorldport | ||
| 202 | |||
| 203 | location_names.sort() | ||
| 204 | |||
| 205 | if ap.client._goal_accessible: | ||
| 206 | var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ | ||
| 207 | ap.victory_condition | ||
| 208 | ]] | ||
| 209 | location_names.push_front(location_name) | ||
| 210 | type_by_name[location_name] = kGoal | ||
| 211 | |||
| 212 | var count = 0 | ||
| 213 | for location_name in location_names: | ||
| 214 | tracker_label.append_text("[p]%s[/p]" % location_name) | ||
| 215 | if count < 18: | ||
| 216 | locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) | ||
| 217 | locations_overlay.append_text(location_name) | ||
| 218 | locations_overlay.append_text(" ") | ||
| 219 | if type_by_name[location_name] == kLocation: | ||
| 220 | locations_overlay.add_image(location_texture) | ||
| 221 | elif type_by_name[location_name] == kWorldport: | ||
| 222 | locations_overlay.add_image(worldport_texture) | ||
| 223 | elif type_by_name[location_name] == kGoal: | ||
| 224 | locations_overlay.add_image(goal_texture) | ||
| 225 | locations_overlay.pop() | ||
| 226 | count += 1 | ||
| 227 | |||
| 228 | if count > 18: | ||
| 229 | locations_overlay.append_text("[p align=right][lb]...[rb][/p]") | ||
| 230 | |||
| 231 | |||
| 232 | func update_locations_visibility(): | ||
| 233 | var ap = global.get_node("Archipelago") | ||
| 234 | locations_overlay.visible = ap.show_locations | ||
| 235 | |||
| 236 | |||
| 237 | func setup_worldports(): | ||
| 238 | tabs.set_tab_hidden(2, false) | ||
| 239 | |||
| 240 | var root_ti = worldports_tree.create_item(null) | ||
| 241 | |||
| 242 | var ports_by_map_id = {} | ||
| 243 | var display_names_by_map_id = {} | ||
| 244 | var display_names_by_port_id = {} | ||
| 245 | |||
| 246 | var ap = global.get_node("Archipelago") | ||
| 247 | var gamedata = global.get_node("Gamedata") | ||
| 248 | for fpid in ap.port_pairings: | ||
| 249 | var port = gamedata.objects.get_ports()[fpid] | ||
| 250 | var room = gamedata.objects.get_rooms()[port.get_room_id()] | ||
| 251 | |||
| 252 | if not ports_by_map_id.has(room.get_map_id()): | ||
| 253 | ports_by_map_id[room.get_map_id()] = [] | ||
| 254 | |||
| 255 | var map = gamedata.objects.get_maps()[room.get_map_id()] | ||
| 256 | display_names_by_map_id[map.get_id()] = map.get_display_name() | ||
| 257 | |||
| 258 | ports_by_map_id[room.get_map_id()].append(fpid) | ||
| 259 | display_names_by_port_id[fpid] = port.get_display_name() | ||
| 260 | |||
| 261 | var sorted_map_ids = ports_by_map_id.keys().duplicate() | ||
| 262 | sorted_map_ids.sort_custom( | ||
| 263 | func(a, b): return display_names_by_map_id[a] < display_names_by_map_id[b] | ||
| 264 | ) | ||
| 265 | |||
| 266 | for map_id in sorted_map_ids: | ||
| 267 | var map_ti = root_ti.create_child() | ||
| 268 | map_ti.set_text(0, display_names_by_map_id[map_id]) | ||
| 269 | map_ti.visible = false | ||
| 270 | map_ti.collapsed = true | ||
| 271 | port_tree_item_by_map[map_id] = map_ti | ||
| 272 | port_tree_item_by_map_port[map_id] = {} | ||
| 273 | |||
| 274 | var port_ids = ports_by_map_id[map_id] | ||
| 275 | port_ids.sort_custom( | ||
| 276 | func(a, b): return display_names_by_port_id[a] < display_names_by_port_id[b] | ||
| 277 | ) | ||
| 278 | |||
| 279 | for port_id in port_ids: | ||
| 280 | var port_ti = map_ti.create_child() | ||
| 281 | port_ti.set_text(0, display_names_by_port_id[port_id]) | ||
| 282 | port_ti.set_text(1, gamedata.get_worldport_display_name(ap.port_pairings[port_id])) | ||
| 283 | port_ti.visible = false | ||
| 284 | port_tree_item_by_map_port[map_id][port_id] = port_ti | ||
| 285 | |||
| 286 | update_worldports() | ||
| 287 | |||
| 288 | |||
| 289 | func update_worldports(): | ||
| 290 | var ap = global.get_node("Archipelago") | ||
| 291 | |||
| 292 | for map_id in port_tree_item_by_map_port.keys(): | ||
| 293 | var map_visible = false | ||
| 294 | |||
| 295 | for port_id in port_tree_item_by_map_port[map_id].keys(): | ||
| 296 | var ti = port_tree_item_by_map_port[map_id][port_id] | ||
| 297 | ti.visible = ap.client._checked_worldports.has(port_id) | ||
| 298 | |||
| 299 | if ti.visible: | ||
| 300 | map_visible = true | ||
| 301 | |||
| 302 | port_tree_item_by_map[map_id].visible = map_visible | ||
| 303 | |||
| 304 | |||
| 305 | func reset(): | ||
| 306 | locations_overlay.clear() | ||
| 307 | tabs.set_tab_hidden(2, true) | ||
| 308 | port_tree_item_by_map.clear() | ||
| 309 | port_tree_item_by_map_port.clear() | ||
| 310 | worldports_tree.clear() | ||
