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