about summary refs log tree commit diff stats
path: root/client/Archipelago/manager.gd
diff options
context:
space:
mode:
Diffstat (limited to 'client/Archipelago/manager.gd')
-rw-r--r--client/Archipelago/manager.gd528
1 files changed, 0 insertions, 528 deletions
diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd deleted file mode 100644 index 5a36336..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null
@@ -1,528 +0,0 @@
1extends Node
2
3const MOD_VERSION = 3
4
5var SCRIPT_client
6var SCRIPT_keyboard
7var SCRIPT_locationListener
8var SCRIPT_uuid
9var SCRIPT_victoryListener
10
11var ap_server = ""
12var ap_user = ""
13var ap_pass = ""
14var connection_history = []
15
16var client
17var keyboard
18
19var _localdata_file = ""
20var _last_new_item = -1
21var _batch_locations = false
22var _held_locations = []
23var _held_location_scouts = []
24var _location_scouts = {}
25var _item_locks = {}
26var _inverse_item_locks = {}
27var _held_letters = {}
28var _letters_setup = false
29
30const kSHUFFLE_LETTERS_VANILLA = 0
31const kSHUFFLE_LETTERS_UNLOCKED = 1
32const kSHUFFLE_LETTERS_PROGRESSIVE = 2
33const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
34const kSHUFFLE_LETTERS_ITEM_CYAN = 4
35
36const kLETTER_BEHAVIOR_VANILLA = 0
37const kLETTER_BEHAVIOR_ITEM = 1
38const kLETTER_BEHAVIOR_UNLOCKED = 2
39
40const kCYAN_DOOR_BEHAVIOR_H2 = 0
41const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
42const kCYAN_DOOR_BEHAVIOR_ITEM = 2
43
44var apworld_version = [0, 0]
45var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
46var daedalus_roof_access = false
47var keyholder_sanity = false
48var shuffle_control_center_colors = false
49var shuffle_doors = false
50var shuffle_gallery_paintings = false
51var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
52var shuffle_symbols = false
53var victory_condition = -1
54
55signal could_not_connect
56signal connect_status
57signal ap_connected
58
59
60func _init():
61 # Read AP settings from file, if there are any
62 if FileAccess.file_exists("user://ap_settings"):
63 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
64 var data = file.get_var(true)
65 file.close()
66
67 if typeof(data) != TYPE_ARRAY:
68 global._print("AP settings file is corrupted")
69 data = []
70
71 if data.size() > 0:
72 ap_server = data[0]
73
74 if data.size() > 1:
75 ap_user = data[1]
76
77 if data.size() > 2:
78 ap_pass = data[2]
79
80 if data.size() > 3:
81 connection_history = data[3]
82
83
84func _ready():
85 client = SCRIPT_client.new()
86 client.SCRIPT_uuid = SCRIPT_uuid
87
88 client.connect("item_received", _process_item)
89 client.connect("message_received", _process_message)
90 client.connect("location_scout_received", _process_location_scout)
91 client.connect("could_not_connect", _client_could_not_connect)
92 client.connect("connect_status", _client_connect_status)
93 client.connect("client_connected", _client_connected)
94
95 add_child(client)
96
97 keyboard = SCRIPT_keyboard.new()
98 add_child(keyboard)
99
100
101func saveSettings():
102 # Save the AP settings to disk.
103 var path = "user://ap_settings"
104 var file = FileAccess.open(path, FileAccess.WRITE)
105
106 var data = [
107 ap_server,
108 ap_user,
109 ap_pass,
110 connection_history,
111 ]
112 file.store_var(data, true)
113 file.close()
114
115
116func saveLocaldata():
117 # Save the MW/slot specific settings to disk.
118 var dir = DirAccess.open("user://")
119 var folder = "archipelago_data"
120 if not dir.dir_exists(folder):
121 dir.make_dir(folder)
122
123 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
124
125 var data = [
126 _last_new_item,
127 ]
128 file.store_var(data, true)
129 file.close()
130
131
132func connectToServer():
133 _last_new_item = -1
134 _batch_locations = false
135 _held_locations = []
136 _held_location_scouts = []
137 _location_scouts = {}
138 _letters_setup = false
139 _held_letters = {}
140
141 client.connectToServer(ap_server, ap_user, ap_pass)
142
143
144func getSaveFileName():
145 return "zzAP_%s_%d" % [client._seed, client._slot]
146
147
148func disconnect_from_ap():
149 client.disconnect_from_ap()
150
151
152func get_item_id_for_door(door_id):
153 return _item_locks.get(door_id, null)
154
155
156func _process_item(item, index, from, flags, amount):
157 var item_name = "Unknown"
158 if client._item_id_to_name["Lingo 2"].has(item):
159 item_name = client._item_id_to_name["Lingo 2"][item]
160
161 var gamedata = global.get_node("Gamedata")
162
163 var prog_id = null
164 if _inverse_item_locks.has(item):
165 for lock in _inverse_item_locks.get(item):
166 if lock[1] != amount:
167 continue
168
169 if gamedata.progressive_id_by_ap_id.has(item):
170 prog_id = lock[0]
171
172 if gamedata.get_door_map_name(lock[0]) != global.map:
173 continue
174
175 var receivers = gamedata.get_door_receivers(lock[0])
176 var scene = get_tree().get_root().get_node_or_null("scene")
177 if scene != null:
178 for receiver in receivers:
179 var rnode = scene.get_node_or_null(receiver)
180 if rnode != null:
181 rnode.handleTriggered()
182
183 var letter_id = gamedata.letter_id_by_ap_id.get(item, null)
184 if letter_id != null:
185 var letter = gamedata.objects.get_letters()[letter_id]
186 if not letter.has_level2() or not letter.get_level2():
187 _process_key_item(letter.get_key(), amount)
188
189 if gamedata.symbol_item_ids.has(item):
190 var player = get_tree().get_root().get_node_or_null("scene/player")
191 if player != null:
192 player.emit_signal("evaluate_solvability")
193
194 # Show a message about the item if it's new.
195 if index != null and index > _last_new_item:
196 _last_new_item = index
197 saveLocaldata()
198
199 var player_name = "Unknown"
200 if client._player_name_by_slot.has(float(from)):
201 player_name = client._player_name_by_slot[float(from)]
202
203 var item_color = colorForItemType(flags)
204
205 var full_item_name = item_name
206 if prog_id != null:
207 var door = gamedata.objects.get_doors()[prog_id]
208 full_item_name = "%s (%s)" % [item_name, door.get_name()]
209
210 var message
211 if from == client._slot:
212 message = "Found [color=%s]%s[/color]" % [item_color, full_item_name]
213 else:
214 message = (
215 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
216 )
217
218 if gamedata.anti_trap_ids.has(item):
219 keyboard.block_letter(gamedata.anti_trap_ids[item])
220
221 global._print(message)
222
223 global.get_node("Messages").showMessage(message)
224
225
226func _process_message(message):
227 parse_printjson_for_textclient(message)
228
229 if (
230 !message.has("receiving")
231 or !message.has("item")
232 or message["item"]["player"] != client._slot
233 ):
234 return
235
236 var item_name = "Unknown"
237 var item_player_game = client._game_by_player[message["receiving"]]
238 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
239 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
240
241 var location_name = "Unknown"
242 var location_player_game = client._game_by_player[message["item"]["player"]]
243 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
244 location_name = (client._location_id_to_name[location_player_game][int(
245 message["item"]["location"]
246 )])
247
248 var player_name = "Unknown"
249 if client._player_name_by_slot.has(message["receiving"]):
250 player_name = client._player_name_by_slot[message["receiving"]]
251
252 var item_color = colorForItemType(message["item"]["flags"])
253
254 if message["type"] == "Hint":
255 var is_for = ""
256 if message["receiving"] != client._slot:
257 is_for = " for %s" % player_name
258 if !message.has("found") || !message["found"]:
259 global.get_node("Messages").showMessage(
260 (
261 "Hint: [color=%s]%s[/color]%s is on %s"
262 % [item_color, item_name, is_for, location_name]
263 )
264 )
265 else:
266 if message["receiving"] != client._slot:
267 var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
268 #if _hinted_locations.has(message["item"]["location"]):
269 # sentMsg += " ([color=#fafad2]Hinted![/color])"
270 global.get_node("Messages").showMessage(sentMsg)
271
272
273func parse_printjson_for_textclient(message):
274 var parts = []
275 for message_part in message["data"]:
276 if !message_part.has("type") and message_part.has("text"):
277 parts.append(message_part["text"])
278 elif message_part["type"] == "player_id":
279 if int(message_part["text"]) == client._slot:
280 parts.append(
281 "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot]
282 )
283 else:
284 var from = float(message_part["text"])
285 parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from])
286 elif message_part["type"] == "item_id":
287 var item_name = "Unknown"
288 var item_player_game = client._game_by_player[message_part["player"]]
289 if client._item_id_to_name[item_player_game].has(int(message_part["text"])):
290 item_name = client._item_id_to_name[item_player_game][int(message_part["text"])]
291
292 parts.append(
293 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
294 )
295 elif message_part["type"] == "location_id":
296 var location_name = "Unknown"
297 var location_player_game = client._game_by_player[message_part["player"]]
298 if client._location_id_to_name[location_player_game].has(int(message_part["text"])):
299 location_name = client._location_id_to_name[location_player_game][int(
300 message_part["text"]
301 )]
302
303 parts.append("[color=#00ff7f]%s[/color]" % location_name)
304 elif message_part.has("text"):
305 parts.append(message_part["text"])
306
307 var textclient_node = global.get_node("Textclient")
308 if textclient_node != null:
309 textclient_node.parse_printjson("".join(parts))
310
311
312func _process_location_scout(item_id, location_id, player, flags):
313 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
314
315 var gamedata = global.get_node("Gamedata")
316 var map_id = gamedata.map_id_by_name.get(global.map)
317
318 var item_name = "Unknown"
319 var item_player_game = client._game_by_player[float(player)]
320 if client._item_id_to_name[item_player_game].has(item_id):
321 item_name = client._item_id_to_name[item_player_game][item_id]
322
323 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
324 if letter_id != null:
325 var letter = gamedata.objects.get_letters()[letter_id]
326 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
327 if room.get_map_id() == map_id:
328 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
329 letter.get_path()
330 )
331 if collectable != null:
332 collectable.setScoutedText(item_name)
333
334
335func _client_could_not_connect(message):
336 emit_signal("could_not_connect", message)
337
338
339func _client_connect_status(message):
340 emit_signal("connect_status", message)
341
342
343func _client_connected(slot_data):
344 var gamedata = global.get_node("Gamedata")
345
346 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
347 _last_new_item = -1
348
349 if FileAccess.file_exists(_localdata_file):
350 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
351 var localdata = []
352 if ap_file != null:
353 localdata = ap_file.get_var(true)
354 ap_file.close()
355
356 if typeof(localdata) != TYPE_ARRAY:
357 print("AP localdata file is corrupted")
358 localdata = []
359
360 if localdata.size() > 0:
361 _last_new_item = localdata[0]
362
363 # Read slot data.
364 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
365 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
366 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
367 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
368 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
369 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
370 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
371 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false))
372 victory_condition = int(slot_data.get("victory_condition", 0))
373
374 if slot_data.has("version"):
375 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])]
376
377 # Set up item locks.
378 _item_locks = {}
379
380 if shuffle_doors:
381 for door in gamedata.objects.get_doors():
382 if (
383 door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD
384 or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
385 ):
386 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
387
388 for progressive in gamedata.objects.get_progressives():
389 for i in range(0, progressive.get_doors().size()):
390 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
391 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
392
393 for door_group in gamedata.objects.get_door_groups():
394 if (
395 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR
396 or door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP
397 ):
398 for door in door_group.get_doors():
399 _item_locks[door] = [door_group.get_ap_id(), 1]
400
401 if shuffle_control_center_colors:
402 for door in gamedata.objects.get_doors():
403 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
404 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
405
406 for door_group in gamedata.objects.get_door_groups():
407 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
408 for door in door_group.get_doors():
409 _item_locks[door] = [door_group.get_ap_id(), 1]
410
411 if shuffle_gallery_paintings:
412 for door in gamedata.objects.get_doors():
413 if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING:
414 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
415
416 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
417 for door_group in gamedata.objects.get_door_groups():
418 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
419 for door in door_group.get_doors():
420 if not _item_locks.has(door):
421 _item_locks[door] = [door_group.get_ap_id(), 1]
422
423 # Create a reverse item locks map for processing items.
424 _inverse_item_locks = {}
425
426 for door_id in _item_locks.keys():
427 var lock = _item_locks.get(door_id)
428
429 if not _inverse_item_locks.has(lock[0]):
430 _inverse_item_locks[lock[0]] = []
431
432 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
433
434 emit_signal("ap_connected")
435
436
437func start_batching_locations():
438 _batch_locations = true
439
440
441func send_location(loc_id):
442 if _batch_locations:
443 _held_locations.append(loc_id)
444 else:
445 client.sendLocation(loc_id)
446
447
448func scout_location(loc_id):
449 if _location_scouts.has(loc_id):
450 return _location_scouts.get(loc_id)
451
452 if _batch_locations:
453 _held_location_scouts.append(loc_id)
454 else:
455 client.scoutLocation(loc_id)
456
457 return null
458
459
460func stop_batching_locations():
461 _batch_locations = false
462
463 if not _held_locations.is_empty():
464 client.sendLocations(_held_locations)
465 _held_locations.clear()
466
467 if not _held_location_scouts.is_empty():
468 client.scoutLocations(_held_location_scouts)
469 _held_location_scouts.clear()
470
471
472func colorForItemType(flags):
473 var int_flags = int(flags)
474 if int_flags & 1: # progression
475 if int_flags & 2: # proguseful
476 return "#f0d200"
477 else:
478 return "#bc51e0"
479 elif int_flags & 2: # useful
480 return "#2b67ff"
481 elif int_flags & 4: # trap
482 return "#d63a22"
483 else: # filler
484 return "#14de9e"
485
486
487func get_letter_behavior(key, level2):
488 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
489 return kLETTER_BEHAVIOR_UNLOCKED
490
491 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
492 if level2:
493 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
494 return kLETTER_BEHAVIOR_VANILLA
495 else:
496 return kLETTER_BEHAVIOR_ITEM
497 else:
498 return kLETTER_BEHAVIOR_UNLOCKED
499
500 if not level2 and ["h", "i", "n", "t"].has(key):
501 # This differs from the equivalent function in the apworld. Logically it is
502 # the same as UNLOCKED since they are in the starting room, but VANILLA
503 # means the player still has to actually pick up the letters.
504 return kLETTER_BEHAVIOR_VANILLA
505
506 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
507 return kLETTER_BEHAVIOR_ITEM
508
509 return kLETTER_BEHAVIOR_VANILLA
510
511
512func setup_keys():
513 keyboard.load_seed()
514
515 _letters_setup = true
516
517 for k in _held_letters.keys():
518 _process_key_item(k, _held_letters[k])
519
520 _held_letters.clear()
521
522
523func _process_key_item(key, level):
524 if not _letters_setup:
525 _held_letters[key] = max(_held_letters.get(key, 0), level)
526 return
527
528 keyboard.collect_remote_letter(key, level)