about summary refs log tree commit diff stats
path: root/client/Archipelago
diff options
context:
space:
mode:
Diffstat (limited to 'client/Archipelago')
-rw-r--r--client/Archipelago/animationListener.gd38
-rw-r--r--client/Archipelago/client.gd415
-rw-r--r--client/Archipelago/collectable.gd16
-rw-r--r--client/Archipelago/door.gd38
-rw-r--r--client/Archipelago/gamedata.gd86
-rw-r--r--client/Archipelago/keyHolder.gd38
-rw-r--r--client/Archipelago/keyHolderChecker.gd24
-rw-r--r--client/Archipelago/keyHolderResetterListener.gd8
-rw-r--r--client/Archipelago/keyboard.gd170
-rw-r--r--client/Archipelago/locationListener.gd20
-rw-r--r--client/Archipelago/manager.gd473
-rw-r--r--client/Archipelago/messages.gd61
-rw-r--r--client/Archipelago/painting.gd38
-rw-r--r--client/Archipelago/pauseMenu.gd6
-rw-r--r--client/Archipelago/player.gd213
-rw-r--r--client/Archipelago/saver.gd9
-rw-r--r--client/Archipelago/settings_buttons.gd24
-rw-r--r--client/Archipelago/settings_screen.gd199
-rw-r--r--client/Archipelago/teleportListener.gd38
-rw-r--r--client/Archipelago/textclient.gd86
-rw-r--r--client/Archipelago/vendor/LICENSE21
-rw-r--r--client/Archipelago/vendor/uuid.gd195
-rw-r--r--client/Archipelago/victoryListener.gd20
-rw-r--r--client/Archipelago/worldportListener.gd8
24 files changed, 0 insertions, 2244 deletions
diff --git a/client/Archipelago/animationListener.gd b/client/Archipelago/animationListener.gd deleted file mode 100644 index c3b26db..0000000 --- a/client/Archipelago/animationListener.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/listeners/animationListener.gd"
2
3var item_id
4var item_amount
5
6
7func _ready():
8 var node_path = String(
9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
10 )
11
12 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null:
15 var ap = global.get_node("Archipelago")
16 var item_lock = ap.get_item_id_for_door(door_id)
17
18 if item_lock != null:
19 item_id = item_lock[0]
20 item_amount = item_lock[1]
21
22 self.senders = []
23 self.senderGroup = []
24 self.nested = false
25 self.complete_at = 0
26 self.max_length = 0
27 self.excludeSenders = []
28
29 call_deferred("_readier")
30
31 super._ready()
32
33
34func _readier():
35 var ap = global.get_node("Archipelago")
36
37 if ap.client.getItemAmount(item_id) >= item_amount:
38 handleTriggered()
diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd deleted file mode 100644 index 2e080fd..0000000 --- a/client/Archipelago/client.gd +++ /dev/null
@@ -1,415 +0,0 @@
1extends Node
2
3const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"}
4
5var SCRIPT_uuid
6
7var _ws = WebSocketPeer.new()
8var _should_process = false
9var _initiated_disconnect = false
10var _try_wss = false
11var _has_connected = false
12
13var _datapackages = {}
14var _pending_packages = []
15var _item_id_to_name = {} # All games
16var _location_id_to_name = {} # All games
17var _item_name_to_id = {} # Lingo 2 only
18var _location_name_to_id = {} # Lingo 2 only
19
20var _remote_version = {"major": 0, "minor": 0, "build": 0}
21var _gen_version = {"major": 0, "minor": 0, "build": 0}
22
23var ap_server = ""
24var ap_user = ""
25var ap_pass = ""
26
27var _authenticated = false
28var _seed = ""
29var _team = 0
30var _slot = 0
31var _players = []
32var _player_name_by_slot = {}
33var _game_by_player = {}
34var _checked_locations = []
35var _received_indexes = []
36var _received_items = {}
37var _slot_data = {}
38
39signal could_not_connect
40signal connect_status
41signal client_connected(slot_data)
42signal item_received(item_id, index, player, flags, amount)
43signal message_received(message)
44signal location_scout_received(item_id, location_id, player, flags)
45
46
47func _init():
48 set_process_mode(Node.PROCESS_MODE_ALWAYS)
49
50 global._print("Instantiated APClient")
51
52 # Read AP datapackages from file, if there are any
53 if FileAccess.file_exists("user://ap_datapackages"):
54 var file = FileAccess.open("user://ap_datapackages", FileAccess.READ)
55 var data = file.get_var(true)
56 file.close()
57
58 if typeof(data) != TYPE_DICTIONARY:
59 global._print("AP datapackages file is corrupted")
60 data = {}
61
62 _datapackages = data
63
64 processDatapackages()
65
66
67func _ready():
68 pass
69 #_ws.connect("connection_closed", _closed)
70 #_ws.connect("connection_failed", _closed)
71 #_ws.connect("server_disconnected", _closed)
72 #_ws.connect("connection_error", _errored)
73 #_ws.connect("connection_established", _connected)
74
75
76func _reset_state():
77 _should_process = false
78 _authenticated = false
79 _try_wss = false
80 _has_connected = false
81 _received_items = {}
82 _received_indexes = []
83
84
85func _errored():
86 if _try_wss:
87 global._print("Could not connect to AP with ws://, now trying wss://")
88 connectToServer(ap_server, ap_user, ap_pass)
89 else:
90 global._print("AP connection failed")
91 _reset_state()
92
93 emit_signal(
94 "could_not_connect",
95 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information."
96 )
97
98
99func _closed(_was_clean = true):
100 global._print("Connection closed")
101 _reset_state()
102
103 if not _initiated_disconnect:
104 emit_signal("could_not_connect", "Disconnected from Archipelago")
105
106 _initiated_disconnect = false
107
108
109func _connected(_proto = ""):
110 global._print("Connected!")
111 _try_wss = false
112
113
114func disconnect_from_ap():
115 _initiated_disconnect = true
116 _ws.close()
117
118
119func _process(_delta):
120 if _should_process:
121 _ws.poll()
122
123 var state = _ws.get_ready_state()
124 if state == WebSocketPeer.STATE_OPEN:
125 if not _has_connected:
126 _has_connected = true
127
128 _connected()
129
130 while _ws.get_available_packet_count():
131 var packet = _ws.get_packet()
132 global._print("Got data from server: " + packet.get_string_from_utf8())
133 var json = JSON.new()
134 var jserror = json.parse(packet.get_string_from_utf8())
135 if jserror != OK:
136 global._print("Error parsing packet from AP: " + jserror.error_string)
137 return
138
139 for message in json.data:
140 var cmd = message["cmd"]
141 global._print("Received command: " + cmd)
142
143 if cmd == "RoomInfo":
144 _seed = message["seed_name"]
145 _remote_version = message["version"]
146 _gen_version = message["generator_version"]
147
148 var needed_games = []
149 for game in message["datapackage_checksums"].keys():
150 if (
151 !_datapackages.has(game)
152 or (
153 _datapackages[game]["checksum"]
154 != message["datapackage_checksums"][game]
155 )
156 ):
157 needed_games.append(game)
158
159 if !needed_games.is_empty():
160 _pending_packages = needed_games
161 var cur_needed = _pending_packages.pop_front()
162 requestDatapackages([cur_needed])
163 else:
164 connectToRoom()
165
166 elif cmd == "DataPackage":
167 for game in message["data"]["games"].keys():
168 _datapackages[game] = message["data"]["games"][game]
169 saveDatapackages()
170
171 if !_pending_packages.is_empty():
172 var cur_needed = _pending_packages.pop_front()
173 requestDatapackages([cur_needed])
174 else:
175 processDatapackages()
176 connectToRoom()
177
178 elif cmd == "Connected":
179 _authenticated = true
180 _team = message["team"]
181 _slot = message["slot"]
182 _players = message["players"]
183 _checked_locations = message["checked_locations"]
184 _slot_data = message["slot_data"]
185
186 for player in _players:
187 _player_name_by_slot[player["slot"]] = player["alias"]
188 _game_by_player[player["slot"]] = message["slot_info"][str(
189 player["slot"]
190 )]["game"]
191
192 emit_signal("client_connected", _slot_data)
193
194 elif cmd == "ConnectionRefused":
195 var error_message = ""
196 for error in message["errors"]:
197 var submsg = ""
198 if error == "InvalidSlot":
199 submsg = "Invalid player name."
200 elif error == "InvalidGame":
201 submsg = "The specified player is not playing Lingo."
202 elif error == "IncompatibleVersion":
203 submsg = (
204 "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d."
205 % [
206 ap_version["major"],
207 ap_version["minor"],
208 ap_version["build"],
209 _remote_version["major"],
210 _remote_version["minor"],
211 _remote_version["build"]
212 ]
213 )
214 elif error == "InvalidPassword":
215 submsg = "Incorrect password."
216 elif error == "InvalidItemsHandling":
217 submsg = "Invalid item handling flag. This is a bug with the client."
218
219 if submsg != "":
220 if error_message != "":
221 error_message += " "
222 error_message += submsg
223
224 if error_message == "":
225 error_message = "Unknown error."
226
227 _initiated_disconnect = true
228 _ws.disconnect_from_host()
229
230 emit_signal("could_not_connect", error_message)
231 global._print("Connection to AP refused")
232 global._print(message)
233
234 elif cmd == "ReceivedItems":
235 var i = 0
236 for item in message["items"]:
237 var index = int(message["index"] + i)
238 i += 1
239
240 if _received_indexes.has(index):
241 # Do not re-process items.
242 continue
243
244 _received_indexes.append(index)
245
246 var item_id = int(item["item"])
247 _received_items[item_id] = _received_items.get(item_id, 0) + 1
248
249 emit_signal(
250 "item_received",
251 item_id,
252 index,
253 int(item["player"]),
254 int(item["flags"]),
255 _received_items[item_id]
256 )
257
258 elif cmd == "PrintJSON":
259 emit_signal("message_received", message)
260
261 elif cmd == "LocationInfo":
262 for loc in message["locations"]:
263 emit_signal(
264 "location_scout_received",
265 int(loc["item"]),
266 int(loc["location"]),
267 int(loc["player"]),
268 int(loc["flags"])
269 )
270
271 elif state == WebSocketPeer.STATE_CLOSED:
272 if _has_connected:
273 _closed()
274 else:
275 _errored()
276
277
278func saveDatapackages():
279 # Save the AP datapackages to disk.
280 var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE)
281 file.store_var(_datapackages, true)
282 file.close()
283
284
285func connectToServer(server, un, pw):
286 ap_server = server
287 ap_user = un
288 ap_pass = pw
289
290 _initiated_disconnect = false
291
292 var url = ""
293 if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"):
294 url = ap_server
295 _try_wss = false
296 elif _try_wss:
297 url = "wss://" + ap_server
298 _try_wss = false
299 else:
300 url = "ws://" + ap_server
301 _try_wss = true
302
303 var err = _ws.connect_to_url(url)
304 if err != OK:
305 emit_signal(
306 "could_not_connect",
307 (
308 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d."
309 % err
310 )
311 )
312 global._print("Could not connect to AP: " + err)
313 return
314 _should_process = true
315
316 emit_signal("connect_status", "Connecting...")
317
318
319func sendMessage(msg):
320 var payload = JSON.stringify(msg)
321 _ws.send_text(payload)
322
323
324func requestDatapackages(games):
325 emit_signal("connect_status", "Downloading %s data package..." % games[0])
326
327 sendMessage([{"cmd": "GetDataPackage", "games": games}])
328
329
330func processDatapackages():
331 _item_id_to_name = {}
332 _location_id_to_name = {}
333 for game in _datapackages.keys():
334 var package = _datapackages[game]
335
336 _item_id_to_name[game] = {}
337 for item_name in package["item_name_to_id"].keys():
338 _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name
339
340 _location_id_to_name[game] = {}
341 for location_name in package["location_name_to_id"].keys():
342 _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name
343
344 if _datapackages.has("Lingo 2"):
345 _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"]
346 _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"]
347
348
349func connectToRoom():
350 emit_signal("connect_status", "Authenticating...")
351
352 sendMessage(
353 [
354 {
355 "cmd": "Connect",
356 "password": ap_pass,
357 "game": "Lingo 2",
358 "name": ap_user,
359 "uuid": SCRIPT_uuid.v4(),
360 "version": ap_version,
361 "items_handling": 0b111, # always receive our items
362 "tags": [],
363 "slot_data": true
364 }
365 ]
366 )
367
368
369func sendConnectUpdate(tags):
370 sendMessage([{"cmd": "ConnectUpdate", "tags": tags}])
371
372
373func requestSync():
374 sendMessage([{"cmd": "Sync"}])
375
376
377func sendLocation(loc_id):
378 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
379
380
381func sendLocations(loc_ids):
382 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
383
384
385func setValue(key, value, operation = "replace"):
386 sendMessage(
387 [
388 {
389 "cmd": "Set",
390 "key": "Lingo2_%d_%s" % [_slot, key],
391 "want_reply": false,
392 "operations": [{"operation": operation, "value": value}]
393 }
394 ]
395 )
396
397
398func say(textdata):
399 sendMessage([{"cmd": "Say", "text": textdata}])
400
401
402func completedGoal():
403 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
404
405
406func scoutLocations(loc_ids):
407 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
408
409
410func hasItem(item_id):
411 return _received_items.has(item_id)
412
413
414func getItemAmount(item_id):
415 return _received_items.get(item_id, 0)
diff --git a/client/Archipelago/collectable.gd b/client/Archipelago/collectable.gd deleted file mode 100644 index 4a17a2a..0000000 --- a/client/Archipelago/collectable.gd +++ /dev/null
@@ -1,16 +0,0 @@
1extends "res://scripts/nodes/collectable.gd"
2
3
4func pickedUp():
5 if unlock_type == "key":
6 var ap = global.get_node("Archipelago")
7 if ap.get_letter_behavior(unlock_key, level == 2) == ap.kLETTER_BEHAVIOR_VANILLA:
8 ap.keyboard.collect_local_letter(unlock_key, level)
9 else:
10 ap.keyboard.update_unlocks()
11
12 super.pickedUp()
13
14
15func setScoutedText(text):
16 get_node("MeshInstance3D").mesh.text = text.replace(" ", "\n")
diff --git a/client/Archipelago/door.gd b/client/Archipelago/door.gd deleted file mode 100644 index fead818..0000000 --- a/client/Archipelago/door.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/door.gd"
2
3var item_id
4var item_amount
5
6
7func _ready():
8 var node_path = String(
9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
10 )
11
12 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null:
15 var ap = global.get_node("Archipelago")
16 var item_lock = ap.get_item_id_for_door(door_id)
17
18 if item_lock != null:
19 item_id = item_lock[0]
20 item_amount = item_lock[1]
21
22 self.senders = []
23 self.senderGroup = []
24 self.nested = false
25 self.complete_at = 0
26 self.max_length = 0
27 self.excludeSenders = []
28
29 call_deferred("_readier")
30
31 super._ready()
32
33
34func _readier():
35 var ap = global.get_node("Archipelago")
36
37 if ap.client.getItemAmount(item_id) >= item_amount:
38 handleTriggered()
diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd deleted file mode 100644 index f7a5d90..0000000 --- a/client/Archipelago/gamedata.gd +++ /dev/null
@@ -1,86 +0,0 @@
1extends Node
2
3var SCRIPT_proto
4
5var objects
6var door_id_by_map_node_path = {}
7var painting_id_by_map_node_path = {}
8var door_id_by_ap_id = {}
9var map_id_by_name = {}
10var progressive_id_by_ap_id = {}
11var letter_id_by_ap_id = {}
12
13
14func _init(proto_script):
15 SCRIPT_proto = proto_script
16
17
18func load(data_bytes):
19 objects = SCRIPT_proto.AllObjects.new()
20
21 var result_code = objects.from_bytes(data_bytes)
22 if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS:
23 print("Could not load generated data: %d" % result_code)
24 return
25
26 for map in objects.get_maps():
27 map_id_by_name[map.get_name()] = map.get_id()
28
29 for door in objects.get_doors():
30 var map = objects.get_maps()[door.get_map_id()]
31
32 if not map.get_name() in door_id_by_map_node_path:
33 door_id_by_map_node_path[map.get_name()] = {}
34
35 var map_data = door_id_by_map_node_path[map.get_name()]
36 for receiver in door.get_receivers():
37 map_data[receiver] = door.get_id()
38
39 for painting_id in door.get_move_paintings():
40 var painting = objects.get_paintings()[painting_id]
41 map_data[painting.get_path()] = door.get_id()
42
43 if door.has_ap_id():
44 door_id_by_ap_id[door.get_ap_id()] = door.get_id()
45
46 for painting in objects.get_paintings():
47 var room = objects.get_rooms()[painting.get_room_id()]
48 var map = objects.get_maps()[room.get_map_id()]
49
50 if not map.get_name() in painting_id_by_map_node_path:
51 painting_id_by_map_node_path[map.get_name()] = {}
52
53 var _map_data = painting_id_by_map_node_path[map.get_name()]
54
55 for progressive in objects.get_progressives():
56 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id()
57
58 for letter in objects.get_letters():
59 letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id()
60
61
62func get_door_for_map_node_path(map_name, node_path):
63 if not door_id_by_map_node_path.has(map_name):
64 return null
65
66 var map_data = door_id_by_map_node_path[map_name]
67 return map_data.get(node_path, null)
68
69
70func get_door_ap_id(door_id):
71 var door = objects.get_doors()[door_id]
72 if door.has_ap_id():
73 return door.get_ap_id()
74 else:
75 return null
76
77
78func get_door_receivers(door_id):
79 var door = objects.get_doors()[door_id]
80 return door.get_receivers()
81
82
83func get_door_map_name(door_id):
84 var door = objects.get_doors()[door_id]
85 var map = objects.get_maps()[door.get_map_id()]
86 return map.get_name()
diff --git a/client/Archipelago/keyHolder.gd b/client/Archipelago/keyHolder.gd deleted file mode 100644 index 3c037ff..0000000 --- a/client/Archipelago/keyHolder.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/keyHolder.gd"
2
3
4func setFromAp(key, level):
5 if level > 0:
6 has_key = true
7 is_complete = "%s%d" % [key, level]
8 held_key = key
9 held_level = level
10 get_node("Hinge/Letter").mesh.text = held_key
11 get_node("Hinge/Letter2").mesh.text = held_key
12 setMaterial()
13 emit_signal("trigger")
14 else:
15 has_key = false
16 held_key = ""
17 held_level = 0
18 setMaterial()
19 get_node("Hinge/Letter").mesh.text = "-"
20 get_node("Hinge/Letter2").mesh.text = "-"
21 is_complete = ""
22 emit_signal("untrigger")
23
24
25func addKey(key):
26 var node_path = String(
27 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
28 )
29 var ap = global.get_node("Archipelago")
30 ap.keyboard.put_in_keyholder(key, global.map, node_path)
31
32
33func removeKey():
34 var node_path = String(
35 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
36 )
37 var ap = global.get_node("Archipelago")
38 ap.keyboard.remove_from_keyholder(held_key, global.map, node_path)
diff --git a/client/Archipelago/keyHolderChecker.gd b/client/Archipelago/keyHolderChecker.gd deleted file mode 100644 index a75a9e4..0000000 --- a/client/Archipelago/keyHolderChecker.gd +++ /dev/null
@@ -1,24 +0,0 @@
1extends "res://scripts/nodes/listeners/keyHolderChecker.gd"
2
3
4func check():
5 var ap = global.get_node("Archipelago")
6 var matches = []
7 for map in ap.keyboard.keyholder_state.keys():
8 var nodes = ap.keyboard.keyholder_state[map]
9 for node in nodes.keys():
10 matches.append([nodes[node], 1, map, "/root/scene/%s" % node])
11
12 var count = 0
13 for key_match in matches:
14 var active = (
15 key_match[2] + String(key_match[3]).replace("/root/scene/Components/KeyHolders/", ".")
16 )
17 if map[active] == key_match[0]:
18 emit_signal("trigger_letter", key_match[0], true)
19 count += 1
20 else:
21 emit_signal("trigger_letter", key_match[0], false)
22
23 if count > 25:
24 emit_signal("trigger")
diff --git a/client/Archipelago/keyHolderResetterListener.gd b/client/Archipelago/keyHolderResetterListener.gd deleted file mode 100644 index d5300f3..0000000 --- a/client/Archipelago/keyHolderResetterListener.gd +++ /dev/null
@@ -1,8 +0,0 @@
1extends "res://scripts/nodes/listeners/keyHolderResetterListener.gd"
2
3
4func reset():
5 var ap = global.get_node("Archipelago")
6 var was_removed = ap.keyboard.reset_keyholders()
7 if was_removed:
8 sfxPlayer.sfx_play("pickup")
diff --git a/client/Archipelago/keyboard.gd b/client/Archipelago/keyboard.gd deleted file mode 100644 index e43ec9f..0000000 --- a/client/Archipelago/keyboard.gd +++ /dev/null
@@ -1,170 +0,0 @@
1extends Node
2
3const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz"
4
5var letters_saved = {}
6var letters_in_keyholders = []
7var letters_dynamic = {}
8var keyholder_state = {}
9
10var filename = ""
11
12
13func _init():
14 reset()
15
16
17func reset():
18 letters_saved.clear()
19 letters_in_keyholders.clear()
20 letters_dynamic.clear()
21 keyholder_state.clear()
22
23
24func load_seed():
25 var ap = global.get_node("Archipelago")
26
27 reset()
28
29 filename = "user://archipelago_keys/%s_%d" % [ap.client._seed, ap.client._slot]
30
31 if FileAccess.file_exists(filename):
32 var ap_file = FileAccess.open(filename, FileAccess.READ)
33 var localdata = []
34 if ap_file != null:
35 localdata = ap_file.get_var(true)
36 ap_file.close()
37
38 if typeof(localdata) != TYPE_ARRAY:
39 print("AP keyboard file is corrupted")
40 localdata = []
41
42 if localdata.size() > 0:
43 letters_saved = localdata[0]
44 if localdata.size() > 1:
45 letters_in_keyholders = localdata[1]
46 if localdata.size() > 2:
47 keyholder_state = localdata[2]
48
49 for k in kALL_LETTERS:
50 var level = 0
51
52 if ap.get_letter_behavior(k, false) == ap.kLETTER_BEHAVIOR_UNLOCKED:
53 level += 1
54 if ap.get_letter_behavior(k, true) == ap.kLETTER_BEHAVIOR_UNLOCKED:
55 level += 1
56
57 letters_dynamic[k] = level
58
59 update_unlocks()
60
61
62func save():
63 var dir = DirAccess.open("user://")
64 var folder = "archipelago_keys"
65 if not dir.dir_exists(folder):
66 dir.make_dir(folder)
67
68 var file = FileAccess.open(filename, FileAccess.WRITE)
69
70 var data = [
71 letters_saved,
72 letters_in_keyholders,
73 keyholder_state,
74 ]
75 file.store_var(data, true)
76 file.close()
77
78
79func update_unlocks():
80 unlocks.resetKeys()
81
82 for k in kALL_LETTERS:
83 var level = 0
84
85 if not letters_in_keyholders.has(k):
86 level = letters_saved.get(k, 0) + letters_dynamic.get(k, 0)
87
88 if level > 2:
89 level = 2
90
91 unlocks.unlockKey(k, level)
92
93
94func collect_local_letter(key, level):
95 if level < 0 or level > 2 or level < letters_saved.get(key, 0):
96 return
97
98 letters_saved[key] = level
99
100 update_unlocks()
101 save()
102
103
104func collect_remote_letter(key, level):
105 if level < 0 or level > 2 or level < letters_dynamic.get(key, 0):
106 return
107
108 letters_dynamic[key] = level
109
110 update_unlocks()
111 save()
112
113
114func put_in_keyholder(key, map, kh_path):
115 if not keyholder_state.has(map):
116 keyholder_state[map] = {}
117
118 keyholder_state[map][kh_path] = key
119 letters_in_keyholders.append(key)
120
121 get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(
122 key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
123 )
124
125 update_unlocks()
126 save()
127
128
129func remove_from_keyholder(key, map, kh_path):
130 if not keyholder_state.has(map):
131 # This... shouldn't happen.
132 keyholder_state[map] = {}
133
134 keyholder_state[map].erase(kh_path)
135 letters_in_keyholders.erase(key)
136
137 get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(key, 0)
138
139 update_unlocks()
140 save()
141
142
143func load_keyholders(map):
144 if keyholder_state.has(map):
145 var khs = keyholder_state[map]
146
147 for path in khs.keys():
148 var key = khs[path]
149 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
150 key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
151 )
152
153
154func reset_keyholders():
155 if letters_in_keyholders.is_empty():
156 return false
157
158 if keyholder_state.has(global.map):
159 for path in keyholder_state[global.map]:
160 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
161 keyholder_state[global.map][path], 0
162 )
163
164 keyholder_state.clear()
165 letters_in_keyholders.clear()
166
167 update_unlocks()
168 save()
169
170 return true
diff --git a/client/Archipelago/locationListener.gd b/client/Archipelago/locationListener.gd deleted file mode 100644 index 71792ed..0000000 --- a/client/Archipelago/locationListener.gd +++ /dev/null
@@ -1,20 +0,0 @@
1extends Receiver
2
3var location_id
4
5
6func _ready():
7 super._ready()
8
9
10func handleTriggered():
11 triggered += 1
12 if triggered >= total:
13 var ap = global.get_node("Archipelago")
14 ap.send_location(location_id)
15
16
17func handleUntriggered():
18 triggered -= 1
19 if triggered < total:
20 pass
diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd deleted file mode 100644 index 72abf34..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null
@@ -1,473 +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 _held_letters = {}
27var _letters_setup = false
28
29const kSHUFFLE_LETTERS_VANILLA = 0
30const kSHUFFLE_LETTERS_UNLOCKED = 1
31const kSHUFFLE_LETTERS_PROGRESSIVE = 2
32const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
33const kSHUFFLE_LETTERS_ITEM_CYAN = 4
34
35const kLETTER_BEHAVIOR_VANILLA = 0
36const kLETTER_BEHAVIOR_ITEM = 1
37const kLETTER_BEHAVIOR_UNLOCKED = 2
38
39var daedalus_roof_access = false
40var keyholder_sanity = false
41var shuffle_control_center_colors = false
42var shuffle_doors = false
43var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
44var victory_condition = -1
45
46signal could_not_connect
47signal connect_status
48signal ap_connected
49
50
51func _init():
52 # Read AP settings from file, if there are any
53 if FileAccess.file_exists("user://ap_settings"):
54 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
55 var data = file.get_var(true)
56 file.close()
57
58 if typeof(data) != TYPE_ARRAY:
59 global._print("AP settings file is corrupted")
60 data = []
61
62 if data.size() > 0:
63 ap_server = data[0]
64
65 if data.size() > 1:
66 ap_user = data[1]
67
68 if data.size() > 2:
69 ap_pass = data[2]
70
71 if data.size() > 3:
72 connection_history = data[3]
73
74
75func _ready():
76 client = SCRIPT_client.new()
77 client.SCRIPT_uuid = SCRIPT_uuid
78
79 client.connect("item_received", _process_item)
80 client.connect("message_received", _process_message)
81 client.connect("location_scout_received", _process_location_scout)
82 client.connect("could_not_connect", _client_could_not_connect)
83 client.connect("connect_status", _client_connect_status)
84 client.connect("client_connected", _client_connected)
85
86 add_child(client)
87
88 keyboard = SCRIPT_keyboard.new()
89 add_child(keyboard)
90
91
92func saveSettings():
93 # Save the AP settings to disk.
94 var path = "user://ap_settings"
95 var file = FileAccess.open(path, FileAccess.WRITE)
96
97 var data = [
98 ap_server,
99 ap_user,
100 ap_pass,
101 connection_history,
102 ]
103 file.store_var(data, true)
104 file.close()
105
106
107func saveLocaldata():
108 # Save the MW/slot specific settings to disk.
109 var dir = DirAccess.open("user://")
110 var folder = "archipelago_data"
111 if not dir.dir_exists(folder):
112 dir.make_dir(folder)
113
114 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
115
116 var data = [
117 _last_new_item,
118 ]
119 file.store_var(data, true)
120 file.close()
121
122
123func connectToServer():
124 _last_new_item = -1
125 _letters_setup = false
126 _held_letters = {}
127
128 client.connectToServer(ap_server, ap_user, ap_pass)
129
130
131func getSaveFileName():
132 return "zzAP_%s_%d" % [client._seed, client._slot]
133
134
135func disconnect_from_ap():
136 client.disconnect_from_ap()
137
138
139func get_item_id_for_door(door_id):
140 return _item_locks.get(door_id, null)
141
142
143func _process_item(item, index, from, flags, amount):
144 var item_name = "Unknown"
145 if client._item_id_to_name["Lingo 2"].has(item):
146 item_name = client._item_id_to_name["Lingo 2"][item]
147
148 var gamedata = global.get_node("Gamedata")
149 var door_id = gamedata.door_id_by_ap_id.get(item, null)
150 var prog_id = null
151
152 if door_id == null:
153 prog_id = gamedata.progressive_id_by_ap_id.get(item, null)
154 if prog_id != null:
155 var progressive = gamedata.objects.get_progressives()[prog_id]
156 if progressive.get_doors().size() >= amount:
157 door_id = progressive.get_doors()[amount - 1]
158
159 if door_id != null and gamedata.get_door_map_name(door_id) == global.map:
160 var receivers = gamedata.get_door_receivers(door_id)
161 var scene = get_tree().get_root().get_node_or_null("scene")
162 if scene != null:
163 for receiver in receivers:
164 var rnode = scene.get_node_or_null(receiver)
165 if rnode != null:
166 rnode.handleTriggered()
167
168 var letter_id = gamedata.letter_id_by_ap_id.get(item, null)
169 if letter_id != null:
170 var letter = gamedata.objects.get_letters()[letter_id]
171 if not letter.has_level2() or not letter.get_level2():
172 _process_key_item(letter.get_key(), amount)
173
174 # Show a message about the item if it's new.
175 if index != null and index > _last_new_item:
176 _last_new_item = index
177 saveLocaldata()
178
179 var player_name = "Unknown"
180 if client._player_name_by_slot.has(float(from)):
181 player_name = client._player_name_by_slot[float(from)]
182
183 var item_color = colorForItemType(flags)
184
185 var full_item_name = item_name
186 if prog_id != null and door_id != null:
187 var door = gamedata.objects.get_doors()[door_id]
188 full_item_name = "%s (%s)" % [item_name, door.get_name()]
189
190 var message
191 if from == client._slot:
192 message = "Found [color=%s]%s[/color]" % [item_color, full_item_name]
193 else:
194 message = (
195 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
196 )
197
198 global._print(message)
199
200 global.get_node("Messages").showMessage(message)
201
202
203func _process_message(message):
204 parse_printjson_for_textclient(message)
205
206 if (
207 !message.has("receiving")
208 or !message.has("item")
209 or message["item"]["player"] != client._slot
210 ):
211 return
212
213 var item_name = "Unknown"
214 var item_player_game = client._game_by_player[message["receiving"]]
215 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
216 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
217
218 var location_name = "Unknown"
219 var location_player_game = client._game_by_player[message["item"]["player"]]
220 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
221 location_name = (client._location_id_to_name[location_player_game][int(
222 message["item"]["location"]
223 )])
224
225 var player_name = "Unknown"
226 if client._player_name_by_slot.has(message["receiving"]):
227 player_name = client._player_name_by_slot[message["receiving"]]
228
229 var item_color = colorForItemType(message["item"]["flags"])
230
231 if message["type"] == "Hint":
232 var is_for = ""
233 if message["receiving"] != client._slot:
234 is_for = " for %s" % player_name
235 if !message.has("found") || !message["found"]:
236 global.get_node("Messages").showMessage(
237 (
238 "Hint: [color=%s]%s[/color]%s is on %s"
239 % [item_color, item_name, is_for, location_name]
240 )
241 )
242 else:
243 if message["receiving"] != client._slot:
244 var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
245 #if _hinted_locations.has(message["item"]["location"]):
246 # sentMsg += " ([color=#fafad2]Hinted![/color])"
247 global.get_node("Messages").showMessage(sentMsg)
248
249
250func parse_printjson_for_textclient(message):
251 var parts = []
252 for message_part in message["data"]:
253 if !message_part.has("type") and message_part.has("text"):
254 parts.append(message_part["text"])
255 elif message_part["type"] == "player_id":
256 if int(message_part["text"]) == client._slot:
257 parts.append(
258 "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot]
259 )
260 else:
261 var from = float(message_part["text"])
262 parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from])
263 elif message_part["type"] == "item_id":
264 var item_name = "Unknown"
265 var item_player_game = client._game_by_player[message_part["player"]]
266 if client._item_id_to_name[item_player_game].has(int(message_part["text"])):
267 item_name = client._item_id_to_name[item_player_game][int(message_part["text"])]
268
269 parts.append(
270 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
271 )
272 elif message_part["type"] == "location_id":
273 var location_name = "Unknown"
274 var location_player_game = client._game_by_player[message_part["player"]]
275 if client._location_id_to_name[location_player_game].has(int(message_part["text"])):
276 location_name = client._location_id_to_name[location_player_game][int(
277 message_part["text"]
278 )]
279
280 parts.append("[color=#00ff7f]%s[/color]" % location_name)
281 elif message_part.has("text"):
282 parts.append(message_part["text"])
283
284 var textclient_node = global.get_node("Textclient")
285 if textclient_node != null:
286 textclient_node.parse_printjson("".join(parts))
287
288
289func _process_location_scout(item_id, location_id, player, flags):
290 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
291
292 var gamedata = global.get_node("Gamedata")
293 var map_id = gamedata.map_id_by_name.get(global.map)
294
295 var item_name = "Unknown"
296 var item_player_game = client._game_by_player[float(player)]
297 if client._item_id_to_name[item_player_game].has(item_id):
298 item_name = client._item_id_to_name[item_player_game][item_id]
299
300 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
301 if letter_id != null:
302 var letter = gamedata.objects.get_letters()[letter_id]
303 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
304 if room.get_map_id() == map_id:
305 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
306 letter.get_path()
307 )
308 if collectable != null:
309 collectable.setScoutedText(item_name)
310
311
312func _client_could_not_connect():
313 emit_signal("could_not_connect")
314
315
316func _client_connect_status(message):
317 emit_signal("connect_status", message)
318
319
320func _client_connected(slot_data):
321 var gamedata = global.get_node("Gamedata")
322
323 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
324 _last_new_item = -1
325
326 if FileAccess.file_exists(_localdata_file):
327 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
328 var localdata = []
329 if ap_file != null:
330 localdata = ap_file.get_var(true)
331 ap_file.close()
332
333 if typeof(localdata) != TYPE_ARRAY:
334 print("AP localdata file is corrupted")
335 localdata = []
336
337 if localdata.size() > 0:
338 _last_new_item = localdata[0]
339
340 # Read slot data.
341 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
342 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
343 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
344 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
345 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
346 victory_condition = int(slot_data.get("victory_condition", 0))
347
348 # Set up item locks.
349 _item_locks = {}
350
351 if shuffle_doors:
352 for door in gamedata.objects.get_doors():
353 if (
354 door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD
355 or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
356 ):
357 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
358
359 for progressive in gamedata.objects.get_progressives():
360 for i in range(0, progressive.get_doors().size()):
361 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
362 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
363
364 for door_group in gamedata.objects.get_door_groups():
365 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR:
366 for door in door_group.get_doors():
367 _item_locks[door] = [door_group.get_ap_id(), 1]
368
369 if shuffle_control_center_colors:
370 for door in gamedata.objects.get_doors():
371 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
372 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
373
374 for door_group in gamedata.objects.get_door_groups():
375 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
376 for door in door_group.get_doors():
377 _item_locks[door] = [door_group.get_ap_id(), 1]
378
379 emit_signal("ap_connected")
380
381
382func start_batching_locations():
383 _batch_locations = true
384
385
386func send_location(loc_id):
387 if _batch_locations:
388 _held_locations.append(loc_id)
389 else:
390 client.sendLocation(loc_id)
391
392
393func scout_location(loc_id):
394 if _location_scouts.has(loc_id):
395 return _location_scouts.get(loc_id)
396
397 if _batch_locations:
398 _held_location_scouts.append(loc_id)
399 else:
400 client.scoutLocation(loc_id)
401
402 return null
403
404
405func stop_batching_locations():
406 _batch_locations = false
407
408 if not _held_locations.is_empty():
409 client.sendLocations(_held_locations)
410 _held_locations.clear()
411
412 if not _held_location_scouts.is_empty():
413 client.scoutLocations(_held_location_scouts)
414 _held_location_scouts.clear()
415
416
417func colorForItemType(flags):
418 var int_flags = int(flags)
419 if int_flags & 1: # progression
420 if int_flags & 2: # proguseful
421 return "#f0d200"
422 else:
423 return "#bc51e0"
424 elif int_flags & 2: # useful
425 return "#2b67ff"
426 elif int_flags & 4: # trap
427 return "#d63a22"
428 else: # filler
429 return "#14de9e"
430
431
432func get_letter_behavior(key, level2):
433 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
434 return kLETTER_BEHAVIOR_UNLOCKED
435
436 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
437 if level2:
438 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
439 return kLETTER_BEHAVIOR_VANILLA
440 else:
441 return kLETTER_BEHAVIOR_ITEM
442 else:
443 return kLETTER_BEHAVIOR_UNLOCKED
444
445 if not level2 and ["h", "i", "n", "t"].has(key):
446 # This differs from the equivalent function in the apworld. Logically it is
447 # the same as UNLOCKED since they are in the starting room, but VANILLA
448 # means the player still has to actually pick up the letters.
449 return kLETTER_BEHAVIOR_VANILLA
450
451 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
452 return kLETTER_BEHAVIOR_ITEM
453
454 return kLETTER_BEHAVIOR_VANILLA
455
456
457func setup_keys():
458 keyboard.load_seed()
459
460 _letters_setup = true
461
462 for k in _held_letters.keys():
463 _process_key_item(k, _held_letters[k])
464
465 _held_letters.clear()
466
467
468func _process_key_item(key, level):
469 if not _letters_setup:
470 _held_letters[key] = max(_held_letters.get(key, 0), level)
471 return
472
473 keyboard.collect_remote_letter(key, level)
diff --git a/client/Archipelago/messages.gd b/client/Archipelago/messages.gd deleted file mode 100644 index 52f38b9..0000000 --- a/client/Archipelago/messages.gd +++ /dev/null
@@ -1,61 +0,0 @@
1extends CanvasLayer
2
3var _message_queue = []
4var _font
5var _container
6var _ordered_labels = []
7
8
9func _ready():
10 _container = VBoxContainer.new()
11 _container.set_name("Container")
12 _container.anchor_bottom = 1
13 _container.offset_left = 20.0
14 _container.offset_right = 1920.0
15 _container.offset_top = 0.0
16 _container.offset_bottom = -20.0
17 _container.alignment = BoxContainer.ALIGNMENT_END
18 _container.mouse_filter = Control.MOUSE_FILTER_IGNORE
19 self.add_child(_container)
20
21 _font = load("res://assets/fonts/Lingo2.ttf")
22
23
24func _add_message(text):
25 var new_label = RichTextLabel.new()
26 new_label.push_font(_font)
27 new_label.push_font_size(36)
28 new_label.push_outline_color(Color(0, 0, 0, 1))
29 new_label.push_outline_size(2)
30 new_label.append_text(text)
31 new_label.fit_content = true
32
33 _container.add_child(new_label)
34 _ordered_labels.push_back(new_label)
35
36
37func showMessage(text):
38 if _ordered_labels.size() >= 9:
39 _message_queue.append(text)
40 return
41
42 _add_message(text)
43
44 if _ordered_labels.size() > 1:
45 return
46
47 var timeout = 10.0
48 while !_ordered_labels.is_empty():
49 await get_tree().create_timer(timeout).timeout
50
51 var to_remove = _ordered_labels.pop_front()
52 var to_tween = get_tree().create_tween().bind_node(to_remove)
53 to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5)
54 to_tween.tween_callback(to_remove.queue_free)
55
56 if !_message_queue.is_empty():
57 var next_msg = _message_queue.pop_front()
58 _add_message(next_msg)
59
60 if timeout > 4:
61 timeout -= 3
diff --git a/client/Archipelago/painting.gd b/client/Archipelago/painting.gd deleted file mode 100644 index 276d4eb..0000000 --- a/client/Archipelago/painting.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/painting.gd"
2
3var item_id
4var item_amount
5
6
7func _ready():
8 var node_path = String(
9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
10 )
11
12 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null:
15 var ap = global.get_node("Archipelago")
16 var item_lock = ap.get_item_id_for_door(door_id)
17
18 if item_lock != null:
19 item_id = item_lock[0]
20 item_amount = item_lock[1]
21
22 self.senders = []
23 self.senderGroup = []
24 self.nested = false
25 self.complete_at = 0
26 self.max_length = 0
27 self.excludeSenders = []
28
29 call_deferred("_readier")
30
31 super._ready()
32
33
34func _readier():
35 var ap = global.get_node("Archipelago")
36
37 if ap.client.getItemAmount(item_id) >= item_amount:
38 handleTriggered()
diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd deleted file mode 100644 index 6c013a5..0000000 --- a/client/Archipelago/pauseMenu.gd +++ /dev/null
@@ -1,6 +0,0 @@
1extends "res://scripts/ui/pauseMenu.gd"
2
3
4func _pause_game():
5 global.get_node("Textclient").dismiss()
6 super._pause_game()
diff --git a/client/Archipelago/player.gd b/client/Archipelago/player.gd deleted file mode 100644 index dd6aa2b..0000000 --- a/client/Archipelago/player.gd +++ /dev/null
@@ -1,213 +0,0 @@
1extends "res://scripts/nodes/player.gd"
2
3const kEndingNameByVictoryValue = {
4 0: "GRAY",
5 1: "PURPLE",
6 2: "MINT",
7 3: "BLACK",
8 4: "BLUE",
9 5: "CYAN",
10 6: "RED",
11 7: "PLUM",
12 8: "ORANGE",
13 9: "GOLD",
14 10: "YELLOW",
15 11: "GREEN",
16 12: "WHITE",
17}
18
19
20func _ready():
21 var khl_script = load("res://scripts/nodes/keyHolderListener.gd")
22
23 var ap = global.get_node("Archipelago")
24 var gamedata = global.get_node("Gamedata")
25
26 ap.start_batching_locations()
27
28 # Set up door locations.
29 var map_id = gamedata.map_id_by_name.get(global.map)
30 for door in gamedata.objects.get_doors():
31 if door.get_map_id() != map_id:
32 continue
33
34 if not door.has_ap_id():
35 continue
36
37 if door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY:
38 continue
39
40 var locationListener = ap.SCRIPT_locationListener.new()
41 locationListener.location_id = door.get_ap_id()
42 locationListener.name = "locationListener_%d" % door.get_ap_id()
43
44 for panel_ref in door.get_panels():
45 var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()]
46 var panel_path = panel_data.get_path()
47
48 if panel_ref.has_answer():
49 for proxy in panel_data.get_proxies():
50 if proxy.get_answer() == panel_ref.get_answer():
51 panel_path = proxy.get_path()
52 break
53
54 locationListener.senders.append(NodePath("/root/scene/" + panel_path))
55
56 for keyholder_ref in door.get_keyholders():
57 var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()]
58
59 var khl = khl_script.new()
60 khl.name = (
61 "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()]
62 )
63 khl.answer = keyholder_ref.get_key()
64 khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path()))
65 get_parent().add_child.call_deferred(khl)
66
67 locationListener.senders.append(NodePath("../" + khl.name))
68
69 get_parent().add_child.call_deferred(locationListener)
70
71 # Set up letter locations.
72 for letter in gamedata.objects.get_letters():
73 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
74 if room.get_map_id() != map_id:
75 continue
76
77 var locationListener = ap.SCRIPT_locationListener.new()
78 locationListener.location_id = letter.get_ap_id()
79 locationListener.name = "locationListener_%d" % letter.get_ap_id()
80 locationListener.senders.append(NodePath("/root/scene/" + letter.get_path()))
81
82 get_parent().add_child.call_deferred(locationListener)
83
84 if (
85 ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2())
86 != ap.kLETTER_BEHAVIOR_VANILLA
87 ):
88 var scout = ap.scout_location(letter.get_ap_id())
89 if scout != null:
90 var item_name = "Unknown"
91 var item_player_game = ap.client._game_by_player[float(scout["player"])]
92 if ap.client._item_id_to_name[item_player_game].has(scout["item"]):
93 item_name = ap.client._item_id_to_name[item_player_game][scout["item"]]
94
95 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
96 letter.get_path()
97 )
98 if collectable != null:
99 collectable.setScoutedText.call_deferred(item_name)
100
101 # Set up mastery locations.
102 for mastery in gamedata.objects.get_masteries():
103 var room = gamedata.objects.get_rooms()[mastery.get_room_id()]
104 if room.get_map_id() != map_id:
105 continue
106
107 var locationListener = ap.SCRIPT_locationListener.new()
108 locationListener.location_id = mastery.get_ap_id()
109 locationListener.name = "locationListener_%d" % mastery.get_ap_id()
110 locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path()))
111
112 get_parent().add_child.call_deferred(locationListener)
113
114 # Set up ending locations.
115 for ending in gamedata.objects.get_endings():
116 var room = gamedata.objects.get_rooms()[ending.get_room_id()]
117 if room.get_map_id() != map_id:
118 continue
119
120 var locationListener = ap.SCRIPT_locationListener.new()
121 locationListener.location_id = ending.get_ap_id()
122 locationListener.name = "locationListener_%d" % ending.get_ap_id()
123 locationListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
124
125 get_parent().add_child.call_deferred(locationListener)
126
127 if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
128 var victoryListener = ap.SCRIPT_victoryListener.new()
129 victoryListener.name = "victoryListener"
130 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
131
132 get_parent().add_child.call_deferred(victoryListener)
133
134 # Set up keyholder locations, in keyholder sanity.
135 if ap.keyholder_sanity:
136 for keyholder in gamedata.objects.get_keyholders():
137 if not keyholder.has_key():
138 continue
139
140 var room = gamedata.objects.get_rooms()[keyholder.get_room_id()]
141 if room.get_map_id() != map_id:
142 continue
143
144 var locationListener = ap.SCRIPT_locationListener.new()
145 locationListener.location_id = keyholder.get_ap_id()
146 locationListener.name = "locationListener_%d" % keyholder.get_ap_id()
147
148 var khl = khl_script.new()
149 khl.name = "location_%d_keyholder" % keyholder.get_ap_id()
150 khl.answer = keyholder.get_key()
151 khl.senders.append(NodePath("/root/scene/" + keyholder.get_path()))
152 get_parent().add_child.call_deferred(khl)
153
154 locationListener.senders.append(NodePath("../" + khl.name))
155
156 get_parent().add_child.call_deferred(locationListener)
157
158 # Block off roof access in Daedalus.
159 if global.map == "daedalus" and not ap.daedalus_roof_access:
160 _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
161 _set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
162 _set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
163 _set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
164 _set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
165 _set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
166 _set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
167 _set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
168 _set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
169 _set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
170 _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
171 _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
172 _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
173 _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
174 _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)
175
176 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
177 var warp_exit = warp_exit_prefab.instantiate()
178 warp_exit.name = "roof_access_blocker_warp_exit"
179 warp_exit.position = Vector3(58, 10, 0)
180 warp_exit.rotation_degrees.y = 90
181 get_parent().add_child.call_deferred(warp_exit)
182
183 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
184 var warp_enter = warp_enter_prefab.instantiate()
185 warp_enter.target = warp_exit
186 warp_enter.position = Vector3(76.5, 30, 1)
187 warp_enter.scale = Vector3(4, 1.5, 1)
188 warp_enter.rotation_degrees.y = 90
189 get_parent().add_child.call_deferred(warp_enter)
190
191 super._ready()
192
193 await get_tree().process_frame
194 await get_tree().process_frame
195
196 ap.stop_batching_locations()
197
198
199func _set_up_invis_wall(x, y, z, sx, sy, sz):
200 var prefab = preload("res://objects/nodes/block.tscn")
201 var newwall = prefab.instantiate()
202 newwall.position.x = x
203 newwall.position.y = y
204 newwall.position.z = z
205 newwall.scale.x = sz
206 newwall.scale.y = sy
207 newwall.scale.z = sx
208 newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
209 newwall.visibility_range_end = 3
210 newwall.visibility_range_end_margin = 1
211 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
212 newwall.skeleton = ".."
213 get_parent().add_child.call_deferred(newwall)
diff --git a/client/Archipelago/saver.gd b/client/Archipelago/saver.gd deleted file mode 100644 index 0fba9e7..0000000 --- a/client/Archipelago/saver.gd +++ /dev/null
@@ -1,9 +0,0 @@
1extends "res://scripts/nodes/saver.gd"
2
3
4func levelLoaded():
5 if type == "keyholders":
6 var ap = global.get_node("Archipelago")
7 ap.keyboard.load_keyholders.call_deferred(global.map)
8 else:
9 reload.call_deferred()
diff --git a/client/Archipelago/settings_buttons.gd b/client/Archipelago/settings_buttons.gd deleted file mode 100644 index 9e61cb0..0000000 --- a/client/Archipelago/settings_buttons.gd +++ /dev/null
@@ -1,24 +0,0 @@
1extends Button
2
3
4func _ready():
5 pass
6
7
8func _connect_pressed():
9 self.disabled = true
10
11 var ap = global.get_node("Archipelago")
12 ap.ap_server = self.get_parent().get_node("server_box").text
13 ap.ap_user = self.get_parent().get_node("player_box").text
14 ap.ap_pass = self.get_parent().get_node("password_box").text
15 ap.saveSettings()
16
17 ap.connectToServer()
18
19
20func _back_pressed():
21 var ap = global.get_node("Archipelago")
22 ap.disconnect_from_ap()
23
24 get_tree().change_scene_to_file("res://objects/scenes/menus/main_menu.tscn")
diff --git a/client/Archipelago/settings_screen.gd b/client/Archipelago/settings_screen.gd deleted file mode 100644 index aaaf72a..0000000 --- a/client/Archipelago/settings_screen.gd +++ /dev/null
@@ -1,199 +0,0 @@
1extends Node2D
2
3
4func _ready():
5 # Some helpful logging.
6 if Steam.isSubscribed():
7 global._print("Provisioning successful! Build ID: %d" % Steam.getAppBuildId())
8 else:
9 global._print("Provisioning failed.")
10
11 # Undo the load screen removing our cursor
12 get_tree().get_root().set_disable_input(false)
13 Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
14
15 # Increase the WebSocket input buffer size so that we can download large
16 # data packages.
17 ProjectSettings.set_setting("network/limits/websocket_client/max_in_buffer_kb", 8192)
18
19 # Create the global AP manager, if it doesn't already exist.
20 if not global.has_node("Archipelago"):
21 var ap_script = ResourceLoader.load("user://maps/Archipelago/manager.gd")
22 var ap_instance = ap_script.new()
23 ap_instance.name = "Archipelago"
24
25 ap_instance.SCRIPT_client = load("user://maps/Archipelago/client.gd")
26 ap_instance.SCRIPT_keyboard = load("user://maps/Archipelago/keyboard.gd")
27 ap_instance.SCRIPT_locationListener = load("user://maps/Archipelago/locationListener.gd")
28 ap_instance.SCRIPT_uuid = load("user://maps/Archipelago/vendor/uuid.gd")
29 ap_instance.SCRIPT_victoryListener = load("user://maps/Archipelago/victoryListener.gd")
30
31 global.add_child(ap_instance)
32
33 # Let's also inject any scripts we need to inject now.
34 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/animationListener.gd"))
35 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/collectable.gd"))
36 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/door.gd"))
37 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/keyHolder.gd"))
38 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/keyHolderChecker.gd"))
39 installScriptExtension(
40 ResourceLoader.load("user://maps/Archipelago/keyHolderResetterListener.gd")
41 )
42 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/painting.gd"))
43 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd"))
44 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd"))
45 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd"))
46 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd"))
47 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd"))
48
49 var proto_script = load("user://maps/Archipelago/generated/proto.gd")
50 var gamedata_script = load("user://maps/Archipelago/gamedata.gd")
51 var gamedata_instance = gamedata_script.new(proto_script)
52 gamedata_instance.load(
53 FileAccess.get_file_as_bytes("user://maps/Archipelago/generated/data.binpb")
54 )
55 gamedata_instance.name = "Gamedata"
56 global.add_child(gamedata_instance)
57
58 var messages_script = load("user://maps/Archipelago/messages.gd")
59 var messages_instance = messages_script.new()
60 messages_instance.name = "Messages"
61 global.add_child(messages_instance)
62
63 var textclient_script = load("user://maps/Archipelago/textclient.gd")
64 var textclient_instance = textclient_script.new()
65 textclient_instance.name = "Textclient"
66 global.add_child(textclient_instance)
67
68 var ap = global.get_node("Archipelago")
69 ap.connect("ap_connected", connectionSuccessful)
70 ap.connect("could_not_connect", connectionUnsuccessful)
71 ap.connect("connect_status", connectionStatus)
72
73 # Populate textboxes with AP settings.
74 $Panel/server_box.text = ap.ap_server
75 $Panel/player_box.text = ap.ap_user
76 $Panel/password_box.text = ap.ap_pass
77
78 var history_box = $Panel/connection_history
79 if ap.connection_history.is_empty():
80 history_box.disabled = true
81 else:
82 history_box.disabled = false
83
84 var i = 0
85 for details in ap.connection_history:
86 history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i)
87 i += 1
88
89 history_box.get_popup().connect("id_pressed", historySelected)
90
91 # Show client version.
92 $Panel/title.text = "ARCHIPELAGO (%s)" % ap.my_version
93
94 # Increase font size in text boxes.
95 $Panel/server_box.add_theme_font_size_override("font_size", 36)
96 $Panel/player_box.add_theme_font_size_override("font_size", 36)
97 $Panel/password_box.add_theme_font_size_override("font_size", 36)
98
99
100# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
101func installScriptExtension(childScript: Resource):
102 # Force Godot to compile the script now.
103 # We need to do this here to ensure that the inheritance chain is
104 # properly set up, and multiple mods can chain-extend the same
105 # class multiple times.
106 # This is also needed to make Godot instantiate the extended class
107 # when creating singletons.
108 # The actual instance is thrown away.
109 childScript.new()
110
111 var parentScript = childScript.get_base_script()
112 var parentScriptPath = parentScript.resource_path
113 global._print("ModLoader: Installing script extension over %s" % parentScriptPath)
114 childScript.take_over_path(parentScriptPath)
115
116
117func connectionStatus(message):
118 var popup = self.get_node("Panel/AcceptDialog")
119 popup.title = "Connecting to Archipelago"
120 popup.dialog_text = message
121 popup.exclusive = true
122 popup.get_ok_button().visible = false
123 popup.popup_centered()
124
125
126func connectionSuccessful():
127 var ap = global.get_node("Archipelago")
128
129 # Save connection details
130 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass]
131 if ap.connection_history.has(connection_details):
132 ap.connection_history.erase(connection_details)
133 ap.connection_history.push_front(connection_details)
134 if ap.connection_history.size() > 10:
135 ap.connection_history.resize(10)
136 ap.saveSettings()
137
138 # Switch to the_entry
139 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
140 global.user = ap.getSaveFileName()
141 global.universe = "lingo"
142 global.map = "the_entry"
143
144 unlocks.resetCollectables()
145 unlocks.resetData()
146
147 ap.setup_keys()
148
149 unlocks.loadCollectables()
150 unlocks.loadData()
151 unlocks.unlockKey("capslock", 1)
152
153 clearResourceCache("res://objects/meshes/gridDoor.tscn")
154 clearResourceCache("res://objects/nodes/collectable.tscn")
155 clearResourceCache("res://objects/nodes/door.tscn")
156 clearResourceCache("res://objects/nodes/keyHolder.tscn")
157 clearResourceCache("res://objects/nodes/listeners/animationListener.tscn")
158 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
159 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
160 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
161 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
162 clearResourceCache("res://objects/nodes/player.tscn")
163 clearResourceCache("res://objects/nodes/saver.tscn")
164 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn")
165
166 var paintings_dir = DirAccess.open("res://objects/meshes/paintings")
167 if paintings_dir:
168 paintings_dir.list_dir_begin()
169 var file_name = paintings_dir.get_next()
170 while file_name != "":
171 if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"):
172 clearResourceCache("res://objects/meshes/paintings/" + file_name)
173 file_name = paintings_dir.get_next()
174
175 switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn")
176
177
178func connectionUnsuccessful(error_message):
179 $Panel/connect_button.disabled = false
180
181 var popup = $Panel/AcceptDialog
182 popup.title = "Could not connect to Archipelago"
183 popup.dialog_text = error_message
184 popup.exclusive = true
185 popup.get_ok_button().visible = true
186 popup.popup_centered()
187
188
189func historySelected(index):
190 var ap = global.get_node("Archipelago")
191 var details = ap.connection_history[index]
192
193 $Panel/server_box.text = details[0]
194 $Panel/player_box.text = details[1]
195 $Panel/password_box.text = details[2]
196
197
198func clearResourceCache(path):
199 ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE)
diff --git a/client/Archipelago/teleportListener.gd b/client/Archipelago/teleportListener.gd deleted file mode 100644 index 4a7deec..0000000 --- a/client/Archipelago/teleportListener.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/listeners/teleportListener.gd"
2
3var item_id
4var item_amount
5
6
7func _ready():
8 var node_path = String(
9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
10 )
11
12 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null:
15 var ap = global.get_node("Archipelago")
16 var item_lock = ap.get_item_id_for_door(door_id)
17
18 if item_lock != null:
19 item_id = item_lock[0]
20 item_amount = item_lock[1]
21
22 self.senders = []
23 self.senderGroup = []
24 self.nested = false
25 self.complete_at = 0
26 self.max_length = 0
27 self.excludeSenders = []
28
29 call_deferred("_readier")
30
31 super._ready()
32
33
34func _readier():
35 var ap = global.get_node("Archipelago")
36
37 if ap.client.getItemAmount(item_id) >= item_amount:
38 handleTriggered()
diff --git a/client/Archipelago/textclient.gd b/client/Archipelago/textclient.gd deleted file mode 100644 index 4b03151..0000000 --- a/client/Archipelago/textclient.gd +++ /dev/null
@@ -1,86 +0,0 @@
1extends CanvasLayer
2
3var panel
4var label
5var entry
6var is_open = false
7
8
9func _ready():
10 process_mode = ProcessMode.PROCESS_MODE_ALWAYS
11
12 panel = Panel.new()
13 panel.set_name("Panel")
14 panel.offset_left = 100
15 panel.offset_right = 1820
16 panel.offset_top = 100
17 panel.offset_bottom = 980
18 panel.visible = false
19 add_child(panel)
20
21 label = RichTextLabel.new()
22 label.set_name("Label")
23 label.offset_left = 80
24 label.offset_right = 1640
25 label.offset_top = 80
26 label.offset_bottom = 720
27 label.scroll_following = true
28 label.selection_enabled = true
29 panel.add_child(label)
30
31 label.push_font(load("res://assets/fonts/Lingo2.ttf"))
32 label.push_font_size(36)
33
34 var entry_style = StyleBoxFlat.new()
35 entry_style.bg_color = Color(0.9, 0.9, 0.9, 1)
36
37 entry = LineEdit.new()
38 entry.set_name("Entry")
39 entry.offset_left = 80
40 entry.offset_right = 1640
41 entry.offset_top = 760
42 entry.offset_bottom = 840
43 entry.add_theme_font_override("font", load("res://assets/fonts/Lingo2.ttf"))
44 entry.add_theme_font_size_override("font_size", 36)
45 entry.add_theme_color_override("font_color", Color(0, 0, 0, 1))
46 entry.add_theme_color_override("cursor_color", Color(0, 0, 0, 1))
47 entry.add_theme_stylebox_override("focus", entry_style)
48 panel.add_child(entry)
49 entry.connect("text_submitted", text_entered)
50
51
52func _input(event):
53 if event is InputEventKey and event.pressed:
54 if event.keycode == KEY_TAB and !Input.is_key_pressed(KEY_SHIFT):
55 if !get_tree().paused:
56 is_open = true
57 get_tree().paused = true
58 Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
59 panel.visible = true
60 entry.grab_focus()
61 get_viewport().set_input_as_handled()
62 else:
63 dismiss()
64 elif event.keycode == KEY_ESCAPE:
65 if is_open:
66 dismiss()
67 get_viewport().set_input_as_handled()
68
69
70func dismiss():
71 if is_open:
72 get_tree().paused = false
73 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
74 panel.visible = false
75 is_open = false
76
77
78func parse_printjson(text):
79 label.append_text("[p]" + text + "[/p]")
80
81
82func text_entered(text):
83 var ap = global.get_node("Archipelago")
84 var cmd = text.trim_suffix("\n")
85 ap.client.say(cmd)
86 entry.text = ""
diff --git a/client/Archipelago/vendor/LICENSE b/client/Archipelago/vendor/LICENSE deleted file mode 100644 index 115ba15..0000000 --- a/client/Archipelago/vendor/LICENSE +++ /dev/null
@@ -1,21 +0,0 @@
1MIT License
2
3Copyright (c) 2023 Xavier Sellier
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE. \ No newline at end of file
diff --git a/client/Archipelago/vendor/uuid.gd b/client/Archipelago/vendor/uuid.gd deleted file mode 100644 index b63fa04..0000000 --- a/client/Archipelago/vendor/uuid.gd +++ /dev/null
@@ -1,195 +0,0 @@
1# Note: The code might not be as pretty it could be, since it's written
2# in a way that maximizes performance. Methods are inlined and loops are avoided.
3extends Node
4
5const BYTE_MASK: int = 0b11111111
6
7
8static func uuidbin():
9 randomize()
10 # 16 random bytes with the bytes on index 6 and 8 modified
11 return [
12 randi() & BYTE_MASK,
13 randi() & BYTE_MASK,
14 randi() & BYTE_MASK,
15 randi() & BYTE_MASK,
16 randi() & BYTE_MASK,
17 randi() & BYTE_MASK,
18 ((randi() & BYTE_MASK) & 0x0f) | 0x40,
19 randi() & BYTE_MASK,
20 ((randi() & BYTE_MASK) & 0x3f) | 0x80,
21 randi() & BYTE_MASK,
22 randi() & BYTE_MASK,
23 randi() & BYTE_MASK,
24 randi() & BYTE_MASK,
25 randi() & BYTE_MASK,
26 randi() & BYTE_MASK,
27 randi() & BYTE_MASK,
28 ]
29
30
31static func uuidbinrng(rng: RandomNumberGenerator):
32 rng.randomize()
33 return [
34 rng.randi() & BYTE_MASK,
35 rng.randi() & BYTE_MASK,
36 rng.randi() & BYTE_MASK,
37 rng.randi() & BYTE_MASK,
38 rng.randi() & BYTE_MASK,
39 rng.randi() & BYTE_MASK,
40 ((rng.randi() & BYTE_MASK) & 0x0f) | 0x40,
41 rng.randi() & BYTE_MASK,
42 ((rng.randi() & BYTE_MASK) & 0x3f) | 0x80,
43 rng.randi() & BYTE_MASK,
44 rng.randi() & BYTE_MASK,
45 rng.randi() & BYTE_MASK,
46 rng.randi() & BYTE_MASK,
47 rng.randi() & BYTE_MASK,
48 rng.randi() & BYTE_MASK,
49 rng.randi() & BYTE_MASK,
50 ]
51
52
53static func v4():
54 # 16 random bytes with the bytes on index 6 and 8 modified
55 var b = uuidbin()
56
57 return (
58 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
59 % [
60 # low
61 b[0],
62 b[1],
63 b[2],
64 b[3],
65 # mid
66 b[4],
67 b[5],
68 # hi
69 b[6],
70 b[7],
71 # clock
72 b[8],
73 b[9],
74 # clock
75 b[10],
76 b[11],
77 b[12],
78 b[13],
79 b[14],
80 b[15]
81 ]
82 )
83
84
85static func v4_rng(rng: RandomNumberGenerator):
86 # 16 random bytes with the bytes on index 6 and 8 modified
87 var b = uuidbinrng(rng)
88
89 return (
90 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
91 % [
92 # low
93 b[0],
94 b[1],
95 b[2],
96 b[3],
97 # mid
98 b[4],
99 b[5],
100 # hi
101 b[6],
102 b[7],
103 # clock
104 b[8],
105 b[9],
106 # clock
107 b[10],
108 b[11],
109 b[12],
110 b[13],
111 b[14],
112 b[15]
113 ]
114 )
115
116
117var _uuid: Array
118
119
120func _init(rng := RandomNumberGenerator.new()) -> void:
121 _uuid = uuidbinrng(rng)
122
123
124func as_array() -> Array:
125 return _uuid.duplicate()
126
127
128func as_dict(big_endian := true) -> Dictionary:
129 if big_endian:
130 return {
131 "low": (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8) + _uuid[3],
132 "mid": (_uuid[4] << 8) + _uuid[5],
133 "hi": (_uuid[6] << 8) + _uuid[7],
134 "clock": (_uuid[8] << 8) + _uuid[9],
135 "node":
136 (
137 (_uuid[10] << 40)
138 + (_uuid[11] << 32)
139 + (_uuid[12] << 24)
140 + (_uuid[13] << 16)
141 + (_uuid[14] << 8)
142 + _uuid[15]
143 )
144 }
145 else:
146 return {
147 "low": _uuid[0] + (_uuid[1] << 8) + (_uuid[2] << 16) + (_uuid[3] << 24),
148 "mid": _uuid[4] + (_uuid[5] << 8),
149 "hi": _uuid[6] + (_uuid[7] << 8),
150 "clock": _uuid[8] + (_uuid[9] << 8),
151 "node":
152 (
153 _uuid[10]
154 + (_uuid[11] << 8)
155 + (_uuid[12] << 16)
156 + (_uuid[13] << 24)
157 + (_uuid[14] << 32)
158 + (_uuid[15] << 40)
159 )
160 }
161
162
163func as_string() -> String:
164 return (
165 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
166 % [
167 # low
168 _uuid[0],
169 _uuid[1],
170 _uuid[2],
171 _uuid[3],
172 # mid
173 _uuid[4],
174 _uuid[5],
175 # hi
176 _uuid[6],
177 _uuid[7],
178 # clock
179 _uuid[8],
180 _uuid[9],
181 # node
182 _uuid[10],
183 _uuid[11],
184 _uuid[12],
185 _uuid[13],
186 _uuid[14],
187 _uuid[15]
188 ]
189 )
190
191
192func is_equal(other) -> bool:
193 # Godot Engine compares Array recursively
194 # There's no need for custom comparison here.
195 return _uuid == other._uuid
diff --git a/client/Archipelago/victoryListener.gd b/client/Archipelago/victoryListener.gd deleted file mode 100644 index e9089d7..0000000 --- a/client/Archipelago/victoryListener.gd +++ /dev/null
@@ -1,20 +0,0 @@
1extends Receiver
2
3
4func _ready():
5 super._ready()
6
7
8func handleTriggered():
9 triggered += 1
10 if triggered >= total:
11 var ap = global.get_node("Archipelago")
12 ap.client.completedGoal()
13
14 global.get_node("Messages").showMessage("You have completed your goal!")
15
16
17func handleUntriggered():
18 triggered -= 1
19 if triggered < total:
20 pass
diff --git a/client/Archipelago/worldportListener.gd b/client/Archipelago/worldportListener.gd deleted file mode 100644 index c31c825..0000000 --- a/client/Archipelago/worldportListener.gd +++ /dev/null
@@ -1,8 +0,0 @@
1extends "res://scripts/nodes/listeners/worldportListener.gd"
2
3
4func changeScene():
5 if exit == "menus/credits":
6 return
7
8 super.changeScene()