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.gd267
1 files changed, 229 insertions, 38 deletions
diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd index 3aa3c50..95f8e1a 100644 --- a/client/Archipelago/manager.gd +++ b/client/Archipelago/manager.gd
@@ -1,8 +1,9 @@
1extends Node 1extends Node
2 2
3const my_version = "0.1.0" 3const MOD_VERSION = 4
4 4
5var SCRIPT_client 5var SCRIPT_client
6var SCRIPT_keyboard
6var SCRIPT_locationListener 7var SCRIPT_locationListener
7var SCRIPT_uuid 8var SCRIPT_uuid
8var SCRIPT_victoryListener 9var SCRIPT_victoryListener
@@ -13,15 +14,42 @@ var ap_pass = ""
13var connection_history = [] 14var connection_history = []
14 15
15var client 16var client
17var keyboard
16 18
17var _localdata_file = "" 19var _localdata_file = ""
18var _last_new_item = -1 20var _last_new_item = -1
19var _batch_locations = false 21var _batch_locations = false
20var _held_locations = [] 22var _held_locations = []
23var _held_location_scouts = []
24var _location_scouts = {}
21var _item_locks = {} 25var _item_locks = {}
22 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
23var keyholder_sanity = false 47var keyholder_sanity = false
48var shuffle_control_center_colors = false
24var shuffle_doors = false 49var shuffle_doors = false
50var shuffle_gallery_paintings = false
51var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
52var shuffle_symbols = false
25var victory_condition = -1 53var victory_condition = -1
26 54
27signal could_not_connect 55signal could_not_connect
@@ -59,12 +87,16 @@ func _ready():
59 87
60 client.connect("item_received", _process_item) 88 client.connect("item_received", _process_item)
61 client.connect("message_received", _process_message) 89 client.connect("message_received", _process_message)
90 client.connect("location_scout_received", _process_location_scout)
62 client.connect("could_not_connect", _client_could_not_connect) 91 client.connect("could_not_connect", _client_could_not_connect)
63 client.connect("connect_status", _client_connect_status) 92 client.connect("connect_status", _client_connect_status)
64 client.connect("client_connected", _client_connected) 93 client.connect("client_connected", _client_connected)
65 94
66 add_child(client) 95 add_child(client)
67 96
97 keyboard = SCRIPT_keyboard.new()
98 add_child(keyboard)
99
68 100
69func saveSettings(): 101func saveSettings():
70 # Save the AP settings to disk. 102 # Save the AP settings to disk.
@@ -99,6 +131,12 @@ func saveLocaldata():
99 131
100func connectToServer(): 132func connectToServer():
101 _last_new_item = -1 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 = {}
102 140
103 client.connectToServer(ap_server, ap_user, ap_pass) 141 client.connectToServer(ap_server, ap_user, ap_pass)
104 142
@@ -121,29 +159,37 @@ func _process_item(item, index, from, flags, amount):
121 item_name = client._item_id_to_name["Lingo 2"][item] 159 item_name = client._item_id_to_name["Lingo 2"][item]
122 160
123 var gamedata = global.get_node("Gamedata") 161 var gamedata = global.get_node("Gamedata")
124 var door_id = gamedata.door_id_by_ap_id.get(item, null)
125 var prog_id = null
126 162
127 if door_id == null: 163 var prog_id = null
128 prog_id = gamedata.progressive_id_by_ap_id.get(item, null) 164 if _inverse_item_locks.has(item):
129 if prog_id != null: 165 for lock in _inverse_item_locks.get(item):
130 var progressive = gamedata.objects.get_progressives()[prog_id] 166 if lock[1] != amount:
131 if progressive.get_doors().size() >= amount: 167 continue
132 door_id = progressive.get_doors()[amount - 1] 168
133 169 if gamedata.progressive_id_by_ap_id.has(item):
134 if door_id != null and gamedata.get_door_map_name(door_id) == global.map: 170 prog_id = lock[0]
135 var receivers = gamedata.get_door_receivers(door_id) 171
136 var scene = get_tree().get_root().get_node_or_null("scene") 172 if gamedata.get_door_map_name(lock[0]) != global.map:
137 if scene != null: 173 continue
138 for receiver in receivers: 174
139 var rnode = scene.get_node_or_null(receiver) 175 var receivers = gamedata.get_door_receivers(lock[0])
140 if rnode != null: 176 var scene = get_tree().get_root().get_node_or_null("scene")
141 rnode.handleTriggered() 177 if scene != null:
142 #for painting_id in gamedata.objects.get_doors()[door_id].get_move_paintings(): 178 for receiver in receivers:
143 # var painting = gamedata.objects.get_paintings()[painting_id] 179 var rnode = scene.get_node_or_null(receiver)
144 # var pnode = scene.get_node_or_null(painting.get_path() + "/teleportListener") 180 if rnode != null:
145 # if pnode != null: 181 rnode.handleTriggered()
146 # pnode.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")
147 193
148 # Show a message about the item if it's new. 194 # Show a message about the item if it's new.
149 if index != null and index > _last_new_item: 195 if index != null and index > _last_new_item:
@@ -151,14 +197,14 @@ func _process_item(item, index, from, flags, amount):
151 saveLocaldata() 197 saveLocaldata()
152 198
153 var player_name = "Unknown" 199 var player_name = "Unknown"
154 if client._player_name_by_slot.has(from): 200 if client._player_name_by_slot.has(float(from)):
155 player_name = client._player_name_by_slot[from] 201 player_name = client._player_name_by_slot[float(from)]
156 202
157 var item_color = colorForItemType(flags) 203 var item_color = colorForItemType(flags)
158 204
159 var full_item_name = item_name 205 var full_item_name = item_name
160 if prog_id != null and door_id != null: 206 if prog_id != null:
161 var door = gamedata.objects.get_doors()[door_id] 207 var door = gamedata.objects.get_doors()[prog_id]
162 full_item_name = "%s (%s)" % [item_name, door.get_name()] 208 full_item_name = "%s (%s)" % [item_name, door.get_name()]
163 209
164 var message 210 var message
@@ -169,6 +215,9 @@ func _process_item(item, index, from, flags, amount):
169 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name] 215 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
170 ) 216 )
171 217
218 if gamedata.anti_trap_ids.has(item):
219 keyboard.block_letter(gamedata.anti_trap_ids[item])
220
172 global._print(message) 221 global._print(message)
173 222
174 global.get_node("Messages").showMessage(message) 223 global.get_node("Messages").showMessage(message)
@@ -186,15 +235,15 @@ func _process_message(message):
186 235
187 var item_name = "Unknown" 236 var item_name = "Unknown"
188 var item_player_game = client._game_by_player[message["receiving"]] 237 var item_player_game = client._game_by_player[message["receiving"]]
189 if client._item_id_to_name[item_player_game].has(message["item"]["item"]): 238 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
190 item_name = client._item_id_to_name[item_player_game][message["item"]["item"]] 239 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
191 240
192 var location_name = "Unknown" 241 var location_name = "Unknown"
193 var location_player_game = client._game_by_player[message["item"]["player"]] 242 var location_player_game = client._game_by_player[message["item"]["player"]]
194 if client._location_id_to_name[location_player_game].has(message["item"]["location"]): 243 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
195 location_name = ( 244 location_name = (client._location_id_to_name[location_player_game][int(
196 client._location_id_to_name[location_player_game][message["item"]["location"]] 245 message["item"]["location"]
197 ) 246 )])
198 247
199 var player_name = "Unknown" 248 var player_name = "Unknown"
200 if client._player_name_by_slot.has(message["receiving"]): 249 if client._player_name_by_slot.has(message["receiving"]):
@@ -260,8 +309,35 @@ func parse_printjson_for_textclient(message):
260 textclient_node.parse_printjson("".join(parts)) 309 textclient_node.parse_printjson("".join(parts))
261 310
262 311
263func _client_could_not_connect(): 312func _process_location_scout(item_id, location_id, player, flags):
264 emit_signal("could_not_connect") 313 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
314
315 if player == client._slot and flags & 4 != 0:
316 # This is a trap for us, so let's not display it.
317 return
318
319 var gamedata = global.get_node("Gamedata")
320 var map_id = gamedata.map_id_by_name.get(global.map)
321
322 var item_name = "Unknown"
323 var item_player_game = client._game_by_player[float(player)]
324 if client._item_id_to_name[item_player_game].has(item_id):
325 item_name = client._item_id_to_name[item_player_game][item_id]
326
327 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
328 if letter_id != null:
329 var letter = gamedata.objects.get_letters()[letter_id]
330 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
331 if room.get_map_id() == map_id:
332 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
333 letter.get_path()
334 )
335 if collectable != null:
336 collectable.setScoutedText(item_name)
337
338
339func _client_could_not_connect(message):
340 emit_signal("could_not_connect", message)
265 341
266 342
267func _client_connect_status(message): 343func _client_connect_status(message):
@@ -289,10 +365,19 @@ func _client_connected(slot_data):
289 _last_new_item = localdata[0] 365 _last_new_item = localdata[0]
290 366
291 # Read slot data. 367 # Read slot data.
368 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
369 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
292 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) 370 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
371 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
293 shuffle_doors = bool(slot_data.get("shuffle_doors", false)) 372 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
373 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
374 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
375 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false))
294 victory_condition = int(slot_data.get("victory_condition", 0)) 376 victory_condition = int(slot_data.get("victory_condition", 0))
295 377
378 if slot_data.has("version"):
379 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])]
380
296 # Set up item locks. 381 # Set up item locks.
297 _item_locks = {} 382 _item_locks = {}
298 383
@@ -309,6 +394,47 @@ func _client_connected(slot_data):
309 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]] 394 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
310 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1] 395 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
311 396
397 for door_group in gamedata.objects.get_door_groups():
398 if (
399 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR
400 or door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP
401 ):
402 for door in door_group.get_doors():
403 _item_locks[door] = [door_group.get_ap_id(), 1]
404
405 if shuffle_control_center_colors:
406 for door in gamedata.objects.get_doors():
407 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
408 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
409
410 for door_group in gamedata.objects.get_door_groups():
411 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
412 for door in door_group.get_doors():
413 _item_locks[door] = [door_group.get_ap_id(), 1]
414
415 if shuffle_gallery_paintings:
416 for door in gamedata.objects.get_doors():
417 if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING:
418 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
419
420 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
421 for door_group in gamedata.objects.get_door_groups():
422 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
423 for door in door_group.get_doors():
424 if not _item_locks.has(door):
425 _item_locks[door] = [door_group.get_ap_id(), 1]
426
427 # Create a reverse item locks map for processing items.
428 _inverse_item_locks = {}
429
430 for door_id in _item_locks.keys():
431 var lock = _item_locks.get(door_id)
432
433 if not _inverse_item_locks.has(lock[0]):
434 _inverse_item_locks[lock[0]] = []
435
436 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
437
312 emit_signal("ap_connected") 438 emit_signal("ap_connected")
313 439
314 440
@@ -323,10 +449,28 @@ func send_location(loc_id):
323 client.sendLocation(loc_id) 449 client.sendLocation(loc_id)
324 450
325 451
452func scout_location(loc_id):
453 if _location_scouts.has(loc_id):
454 return _location_scouts.get(loc_id)
455
456 if _batch_locations:
457 _held_location_scouts.append(loc_id)
458 else:
459 client.scoutLocation(loc_id)
460
461 return null
462
463
326func stop_batching_locations(): 464func stop_batching_locations():
327 _batch_locations = false 465 _batch_locations = false
328 client.sendLocations(_held_locations) 466
329 _held_locations.clear() 467 if not _held_locations.is_empty():
468 client.sendLocations(_held_locations)
469 _held_locations.clear()
470
471 if not _held_location_scouts.is_empty():
472 client.scoutLocations(_held_location_scouts)
473 _held_location_scouts.clear()
330 474
331 475
332func colorForItemType(flags): 476func colorForItemType(flags):
@@ -342,3 +486,50 @@ func colorForItemType(flags):
342 return "#d63a22" 486 return "#d63a22"
343 else: # filler 487 else: # filler
344 return "#14de9e" 488 return "#14de9e"
489
490
491func get_letter_behavior(key, level2):
492 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
493 return kLETTER_BEHAVIOR_UNLOCKED
494
495 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
496 if level2:
497 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
498 return kLETTER_BEHAVIOR_VANILLA
499 else:
500 return kLETTER_BEHAVIOR_ITEM
501 else:
502 return kLETTER_BEHAVIOR_UNLOCKED
503
504 if not level2 and ["h", "i", "n", "t"].has(key):
505 # This differs from the equivalent function in the apworld. Logically it is
506 # the same as UNLOCKED since they are in the starting room, but VANILLA
507 # means the player still has to actually pick up the letters.
508 return kLETTER_BEHAVIOR_VANILLA
509
510 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
511 return kLETTER_BEHAVIOR_ITEM
512
513 return kLETTER_BEHAVIOR_VANILLA
514
515
516func setup_keys():
517 keyboard.load_seed()
518
519 _letters_setup = true
520
521 for k in _held_letters.keys():
522 _process_key_item(k, _held_letters[k])
523
524 _held_letters.clear()
525
526
527func _process_key_item(key, level):
528 if not _letters_setup:
529 _held_letters[key] = max(_held_letters.get(key, 0), level)
530 return
531
532 if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN:
533 level += 1
534
535 keyboard.collect_remote_letter(key, level)