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.gd417
-rw-r--r--client/Archipelago/collectable.gd16
-rw-r--r--client/Archipelago/compass.gd66
-rw-r--r--client/Archipelago/compass_overlay.gd17
-rw-r--r--client/Archipelago/door.gd46
-rw-r--r--client/Archipelago/gamedata.gd140
-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.gd199
-rw-r--r--client/Archipelago/locationListener.gd20
-rw-r--r--client/Archipelago/manager.gd544
-rw-r--r--client/Archipelago/messages.gd71
-rw-r--r--client/Archipelago/painting.gd38
-rw-r--r--client/Archipelago/panel.gd101
-rw-r--r--client/Archipelago/pauseMenu.gd44
-rw-r--r--client/Archipelago/player.gd362
-rw-r--r--client/Archipelago/saver.gd23
-rw-r--r--client/Archipelago/settings_buttons.gd24
-rw-r--r--client/Archipelago/settings_screen.gd252
-rw-r--r--client/Archipelago/teleport.gd38
-rw-r--r--client/Archipelago/teleportListener.gd49
-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/visibilityListener.gd38
-rw-r--r--client/Archipelago/worldport.gd10
-rw-r--r--client/Archipelago/worldportListener.gd8
30 files changed, 0 insertions, 2953 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 843647d..0000000 --- a/client/Archipelago/client.gd +++ /dev/null
@@ -1,417 +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 _ws.inbound_buffer_size = 8388608
51
52 global._print("Instantiated APClient")
53
54 # Read AP datapackages from file, if there are any
55 if FileAccess.file_exists("user://ap_datapackages"):
56 var file = FileAccess.open("user://ap_datapackages", FileAccess.READ)
57 var data = file.get_var(true)
58 file.close()
59
60 if typeof(data) != TYPE_DICTIONARY:
61 global._print("AP datapackages file is corrupted")
62 data = {}
63
64 _datapackages = data
65
66 processDatapackages()
67
68
69func _ready():
70 pass
71 #_ws.connect("connection_closed", _closed)
72 #_ws.connect("connection_failed", _closed)
73 #_ws.connect("server_disconnected", _closed)
74 #_ws.connect("connection_error", _errored)
75 #_ws.connect("connection_established", _connected)
76
77
78func _reset_state():
79 _should_process = false
80 _authenticated = false
81 _try_wss = false
82 _has_connected = false
83 _received_items = {}
84 _received_indexes = []
85
86
87func _errored():
88 if _try_wss:
89 global._print("Could not connect to AP with ws://, now trying wss://")
90 connectToServer(ap_server, ap_user, ap_pass)
91 else:
92 global._print("AP connection failed")
93 _reset_state()
94
95 emit_signal(
96 "could_not_connect",
97 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information."
98 )
99
100
101func _closed(_was_clean = true):
102 global._print("Connection closed")
103 _reset_state()
104
105 if not _initiated_disconnect:
106 emit_signal("could_not_connect", "Disconnected from Archipelago")
107
108 _initiated_disconnect = false
109
110
111func _connected(_proto = ""):
112 global._print("Connected!")
113 _try_wss = false
114
115
116func disconnect_from_ap():
117 _initiated_disconnect = true
118 _ws.close()
119
120
121func _process(_delta):
122 if _should_process:
123 _ws.poll()
124
125 var state = _ws.get_ready_state()
126 if state == WebSocketPeer.STATE_OPEN:
127 if not _has_connected:
128 _has_connected = true
129
130 _connected()
131
132 while _ws.get_available_packet_count():
133 var packet = _ws.get_packet()
134 global._print("Got data from server: " + packet.get_string_from_utf8())
135 var json = JSON.new()
136 var jserror = json.parse(packet.get_string_from_utf8())
137 if jserror != OK:
138 global._print("Error parsing packet from AP: " + jserror.error_string)
139 return
140
141 for message in json.data:
142 var cmd = message["cmd"]
143 global._print("Received command: " + cmd)
144
145 if cmd == "RoomInfo":
146 _seed = message["seed_name"]
147 _remote_version = message["version"]
148 _gen_version = message["generator_version"]
149
150 var needed_games = []
151 for game in message["datapackage_checksums"].keys():
152 if (
153 !_datapackages.has(game)
154 or (
155 _datapackages[game]["checksum"]
156 != message["datapackage_checksums"][game]
157 )
158 ):
159 needed_games.append(game)
160
161 if !needed_games.is_empty():
162 _pending_packages = needed_games
163 var cur_needed = _pending_packages.pop_front()
164 requestDatapackages([cur_needed])
165 else:
166 connectToRoom()
167
168 elif cmd == "DataPackage":
169 for game in message["data"]["games"].keys():
170 _datapackages[game] = message["data"]["games"][game]
171 saveDatapackages()
172
173 if !_pending_packages.is_empty():
174 var cur_needed = _pending_packages.pop_front()
175 requestDatapackages([cur_needed])
176 else:
177 processDatapackages()
178 connectToRoom()
179
180 elif cmd == "Connected":
181 _authenticated = true
182 _team = message["team"]
183 _slot = message["slot"]
184 _players = message["players"]
185 _checked_locations = message["checked_locations"]
186 _slot_data = message["slot_data"]
187
188 for player in _players:
189 _player_name_by_slot[player["slot"]] = player["alias"]
190 _game_by_player[player["slot"]] = message["slot_info"][str(
191 player["slot"]
192 )]["game"]
193
194 emit_signal("client_connected", _slot_data)
195
196 elif cmd == "ConnectionRefused":
197 var error_message = ""
198 for error in message["errors"]:
199 var submsg = ""
200 if error == "InvalidSlot":
201 submsg = "Invalid player name."
202 elif error == "InvalidGame":
203 submsg = "The specified player is not playing Lingo."
204 elif error == "IncompatibleVersion":
205 submsg = (
206 "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d."
207 % [
208 ap_version["major"],
209 ap_version["minor"],
210 ap_version["build"],
211 _remote_version["major"],
212 _remote_version["minor"],
213 _remote_version["build"]
214 ]
215 )
216 elif error == "InvalidPassword":
217 submsg = "Incorrect password."
218 elif error == "InvalidItemsHandling":
219 submsg = "Invalid item handling flag. This is a bug with the client."
220
221 if submsg != "":
222 if error_message != "":
223 error_message += " "
224 error_message += submsg
225
226 if error_message == "":
227 error_message = "Unknown error."
228
229 _initiated_disconnect = true
230 _ws.close()
231
232 emit_signal("could_not_connect", error_message)
233 global._print("Connection to AP refused")
234 global._print(message)
235
236 elif cmd == "ReceivedItems":
237 var i = 0
238 for item in message["items"]:
239 var index = int(message["index"] + i)
240 i += 1
241
242 if _received_indexes.has(index):
243 # Do not re-process items.
244 continue
245
246 _received_indexes.append(index)
247
248 var item_id = int(item["item"])
249 _received_items[item_id] = _received_items.get(item_id, 0) + 1
250
251 emit_signal(
252 "item_received",
253 item_id,
254 index,
255 int(item["player"]),
256 int(item["flags"]),
257 _received_items[item_id]
258 )
259
260 elif cmd == "PrintJSON":
261 emit_signal("message_received", message)
262
263 elif cmd == "LocationInfo":
264 for loc in message["locations"]:
265 emit_signal(
266 "location_scout_received",
267 int(loc["item"]),
268 int(loc["location"]),
269 int(loc["player"]),
270 int(loc["flags"])
271 )
272
273 elif state == WebSocketPeer.STATE_CLOSED:
274 if _has_connected:
275 _closed()
276 else:
277 _errored()
278
279
280func saveDatapackages():
281 # Save the AP datapackages to disk.
282 var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE)
283 file.store_var(_datapackages, true)
284 file.close()
285
286
287func connectToServer(server, un, pw):
288 ap_server = server
289 ap_user = un
290 ap_pass = pw
291
292 _initiated_disconnect = false
293
294 var url = ""
295 if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"):
296 url = ap_server
297 _try_wss = false
298 elif _try_wss:
299 url = "wss://" + ap_server
300 _try_wss = false
301 else:
302 url = "ws://" + ap_server
303 _try_wss = true
304
305 var err = _ws.connect_to_url(url)
306 if err != OK:
307 emit_signal(
308 "could_not_connect",
309 (
310 "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d."
311 % err
312 )
313 )
314 global._print("Could not connect to AP: %d" % err)
315 return
316 _should_process = true
317
318 emit_signal("connect_status", "Connecting...")
319
320
321func sendMessage(msg):
322 var payload = JSON.stringify(msg)
323 _ws.send_text(payload)
324
325
326func requestDatapackages(games):
327 emit_signal("connect_status", "Downloading %s data package..." % games[0])
328
329 sendMessage([{"cmd": "GetDataPackage", "games": games}])
330
331
332func processDatapackages():
333 _item_id_to_name = {}
334 _location_id_to_name = {}
335 for game in _datapackages.keys():
336 var package = _datapackages[game]
337
338 _item_id_to_name[game] = {}
339 for item_name in package["item_name_to_id"].keys():
340 _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name
341
342 _location_id_to_name[game] = {}
343 for location_name in package["location_name_to_id"].keys():
344 _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name
345
346 if _datapackages.has("Lingo 2"):
347 _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"]
348 _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"]
349
350
351func connectToRoom():
352 emit_signal("connect_status", "Authenticating...")
353
354 sendMessage(
355 [
356 {
357 "cmd": "Connect",
358 "password": ap_pass,
359 "game": "Lingo 2",
360 "name": ap_user,
361 "uuid": SCRIPT_uuid.v4(),
362 "version": ap_version,
363 "items_handling": 0b111, # always receive our items
364 "tags": [],
365 "slot_data": true
366 }
367 ]
368 )
369
370
371func sendConnectUpdate(tags):
372 sendMessage([{"cmd": "ConnectUpdate", "tags": tags}])
373
374
375func requestSync():
376 sendMessage([{"cmd": "Sync"}])
377
378
379func sendLocation(loc_id):
380 sendMessage([{"cmd": "LocationChecks", "locations": [loc_id]}])
381
382
383func sendLocations(loc_ids):
384 sendMessage([{"cmd": "LocationChecks", "locations": loc_ids}])
385
386
387func setValue(key, value, operation = "replace"):
388 sendMessage(
389 [
390 {
391 "cmd": "Set",
392 "key": "Lingo2_%d_%s" % [_slot, key],
393 "want_reply": false,
394 "operations": [{"operation": operation, "value": value}]
395 }
396 ]
397 )
398
399
400func say(textdata):
401 sendMessage([{"cmd": "Say", "text": textdata}])
402
403
404func completedGoal():
405 sendMessage([{"cmd": "StatusUpdate", "status": 30}]) # CLIENT_GOAL
406
407
408func scoutLocations(loc_ids):
409 sendMessage([{"cmd": "LocationScouts", "locations": loc_ids}])
410
411
412func hasItem(item_id):
413 return _received_items.has(item_id)
414
415
416func getItemAmount(item_id):
417 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/compass.gd b/client/Archipelago/compass.gd deleted file mode 100644 index c90475a..0000000 --- a/client/Archipelago/compass.gd +++ /dev/null
@@ -1,66 +0,0 @@
1extends Node2D
2
3const RADIUS = 48
4
5var _font
6
7
8func _ready():
9 _font = load("res://assets/fonts/Lingo2.ttf")
10
11
12func _draw():
13 draw_circle(Vector2.ZERO, RADIUS, Color(1.0, 1.0, 1.0, 0.8), true)
14 draw_circle(Vector2.ZERO, RADIUS, Color.BLACK, false)
15 draw_string(
16 _font,
17 Vector2(-4, -RADIUS * 3.0 / 4.0),
18 "N",
19 HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT,
20 -1,
21 16,
22 Color.BLACK
23 )
24 draw_set_transform(Vector2.ZERO, PI / 2)
25 draw_string(
26 _font,
27 Vector2(-4, -RADIUS * 3.0 / 4.0),
28 "E",
29 HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT,
30 -1,
31 16,
32 Color.BLACK
33 )
34 draw_set_transform(Vector2.ZERO, PI)
35 draw_string(
36 _font,
37 Vector2(-4, -RADIUS * 3.0 / 4.0),
38 "S",
39 HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT,
40 -1,
41 16,
42 Color.BLACK
43 )
44 draw_set_transform(Vector2.ZERO, PI * 3.0 / 2.0)
45 draw_string(
46 _font,
47 Vector2(-4, -RADIUS * 3.0 / 4.0),
48 "W",
49 HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT,
50 -1,
51 16,
52 Color.BLACK
53 )
54 draw_set_transform(Vector2.ZERO)
55 draw_colored_polygon(
56 PackedVector2Array(
57 [Vector2(0, -RADIUS * 5.0 / 8.0), Vector2(-RADIUS / 6.0, 0), Vector2(RADIUS / 6.0, 0)]
58 ),
59 Color.RED
60 )
61 draw_colored_polygon(
62 PackedVector2Array(
63 [Vector2(0, RADIUS * 5.0 / 8.0), Vector2(-RADIUS / 6.0, 0), Vector2(RADIUS / 6.0, 0)]
64 ),
65 Color.GRAY
66 )
diff --git a/client/Archipelago/compass_overlay.gd b/client/Archipelago/compass_overlay.gd deleted file mode 100644 index 56e81ff..0000000 --- a/client/Archipelago/compass_overlay.gd +++ /dev/null
@@ -1,17 +0,0 @@
1extends CanvasLayer
2
3var SCRIPT_compass
4
5var compass
6
7
8func _ready():
9 compass = SCRIPT_compass.new()
10 compass.position = Vector2(1840, 80)
11 add_child(compass)
12
13 visible = false
14
15
16func update_rotation(ry):
17 compass.rotation = ry
diff --git a/client/Archipelago/door.gd b/client/Archipelago/door.gd deleted file mode 100644 index 49f5728..0000000 --- a/client/Archipelago/door.gd +++ /dev/null
@@ -1,46 +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 if global.map == "the_sun_temple":
32 if name == "spe_EndPlatform" or name == "spe_entry_2":
33 senders = [NodePath("/root/scene/Panels/EndCheck_dog")]
34
35 if global.map == "the_parthenon":
36 if name == "spe_entry_1":
37 senders = [NodePath("/root/scene/Panels/EndCheck_dog")]
38
39 super._ready()
40
41
42func _readier():
43 var ap = global.get_node("Archipelago")
44
45 if ap.client.getItemAmount(item_id) >= item_amount:
46 handleTriggered()
diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd deleted file mode 100644 index 41d966a..0000000 --- a/client/Archipelago/gamedata.gd +++ /dev/null
@@ -1,140 +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 panel_id_by_map_node_path = {}
9var door_id_by_ap_id = {}
10var map_id_by_name = {}
11var progressive_id_by_ap_id = {}
12var letter_id_by_ap_id = {}
13var symbol_item_ids = []
14var anti_trap_ids = {}
15
16var kSYMBOL_ITEMS
17
18
19func _init(proto_script):
20 SCRIPT_proto = proto_script
21
22 kSYMBOL_ITEMS = {
23 SCRIPT_proto.PuzzleSymbol.SUN: "Sun Symbol",
24 SCRIPT_proto.PuzzleSymbol.SPARKLES: "Sparkles Symbol",
25 SCRIPT_proto.PuzzleSymbol.ZERO: "Zero Symbol",
26 SCRIPT_proto.PuzzleSymbol.EXAMPLE: "Example Symbol",
27 SCRIPT_proto.PuzzleSymbol.BOXES: "Boxes Symbol",
28 SCRIPT_proto.PuzzleSymbol.PLANET: "Planet Symbol",
29 SCRIPT_proto.PuzzleSymbol.PYRAMID: "Pyramid Symbol",
30 SCRIPT_proto.PuzzleSymbol.CROSS: "Cross Symbol",
31 SCRIPT_proto.PuzzleSymbol.SWEET: "Sweet Symbol",
32 SCRIPT_proto.PuzzleSymbol.GENDER: "Gender Symbol",
33 SCRIPT_proto.PuzzleSymbol.AGE: "Age Symbol",
34 SCRIPT_proto.PuzzleSymbol.SOUND: "Sound Symbol",
35 SCRIPT_proto.PuzzleSymbol.ANAGRAM: "Anagram Symbol",
36 SCRIPT_proto.PuzzleSymbol.JOB: "Job Symbol",
37 SCRIPT_proto.PuzzleSymbol.STARS: "Stars Symbol",
38 SCRIPT_proto.PuzzleSymbol.NULL: "Null Symbol",
39 SCRIPT_proto.PuzzleSymbol.EVAL: "Eval Symbol",
40 SCRIPT_proto.PuzzleSymbol.LINGO: "Lingo Symbol",
41 SCRIPT_proto.PuzzleSymbol.QUESTION: "Question Symbol",
42 }
43
44
45func load(data_bytes):
46 objects = SCRIPT_proto.AllObjects.new()
47
48 var result_code = objects.from_bytes(data_bytes)
49 if result_code != SCRIPT_proto.PB_ERR.NO_ERRORS:
50 print("Could not load generated data: %d" % result_code)
51 return
52
53 for map in objects.get_maps():
54 map_id_by_name[map.get_name()] = map.get_id()
55
56 for door in objects.get_doors():
57 var map = objects.get_maps()[door.get_map_id()]
58
59 if not map.get_name() in door_id_by_map_node_path:
60 door_id_by_map_node_path[map.get_name()] = {}
61
62 var map_data = door_id_by_map_node_path[map.get_name()]
63 for receiver in door.get_receivers():
64 map_data[receiver] = door.get_id()
65
66 for painting_id in door.get_move_paintings():
67 var painting = objects.get_paintings()[painting_id]
68 map_data[painting.get_path()] = door.get_id()
69
70 if door.has_ap_id():
71 door_id_by_ap_id[door.get_ap_id()] = door.get_id()
72
73 for painting in objects.get_paintings():
74 var room = objects.get_rooms()[painting.get_room_id()]
75 var map = objects.get_maps()[room.get_map_id()]
76
77 if not map.get_name() in painting_id_by_map_node_path:
78 painting_id_by_map_node_path[map.get_name()] = {}
79
80 var _map_data = painting_id_by_map_node_path[map.get_name()]
81
82 for progressive in objects.get_progressives():
83 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id()
84
85 for letter in objects.get_letters():
86 letter_id_by_ap_id[letter.get_ap_id()] = letter.get_id()
87
88 for panel in objects.get_panels():
89 var room = objects.get_rooms()[panel.get_room_id()]
90 var map = objects.get_maps()[room.get_map_id()]
91
92 if not map.get_name() in panel_id_by_map_node_path:
93 panel_id_by_map_node_path[map.get_name()] = {}
94
95 var map_data = panel_id_by_map_node_path[map.get_name()]
96 map_data[panel.get_path()] = panel.get_id()
97
98 for symbol_name in kSYMBOL_ITEMS.values():
99 symbol_item_ids.append(objects.get_special_ids()[symbol_name])
100
101 for special_name in objects.get_special_ids().keys():
102 if special_name.begins_with("Anti "):
103 anti_trap_ids[objects.get_special_ids()[special_name]] = (
104 special_name.substr(5).to_lower()
105 )
106
107
108func get_door_for_map_node_path(map_name, node_path):
109 if not door_id_by_map_node_path.has(map_name):
110 return null
111
112 var map_data = door_id_by_map_node_path[map_name]
113 return map_data.get(node_path, null)
114
115
116func get_panel_for_map_node_path(map_name, node_path):
117 if not panel_id_by_map_node_path.has(map_name):
118 return null
119
120 var map_data = panel_id_by_map_node_path[map_name]
121 return map_data.get(node_path, null)
122
123
124func get_door_ap_id(door_id):
125 var door = objects.get_doors()[door_id]
126 if door.has_ap_id():
127 return door.get_ap_id()
128 else:
129 return null
130
131
132func get_door_receivers(door_id):
133 var door = objects.get_doors()[door_id]
134 return door.get_receivers()
135
136
137func get_door_map_name(door_id):
138 var door = objects.get_doors()[door_id]
139 var map = objects.get_maps()[door.get_map_id()]
140 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 450566d..0000000 --- a/client/Archipelago/keyboard.gd +++ /dev/null
@@ -1,199 +0,0 @@
1extends Node
2
3const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz"
4
5var letters_saved = {}
6var letters_in_keyholders = []
7var letters_blocked = []
8var letters_dynamic = {}
9var keyholder_state = {}
10
11var filename = ""
12
13
14func _init():
15 reset()
16
17
18func reset():
19 letters_saved.clear()
20 letters_in_keyholders.clear()
21 letters_blocked.clear()
22 letters_dynamic.clear()
23 keyholder_state.clear()
24
25
26func load_seed():
27 var ap = global.get_node("Archipelago")
28
29 reset()
30
31 filename = "user://archipelago_keys/%s_%d" % [ap.client._seed, ap.client._slot]
32
33 if FileAccess.file_exists(filename):
34 var ap_file = FileAccess.open(filename, FileAccess.READ)
35 var localdata = []
36 if ap_file != null:
37 localdata = ap_file.get_var(true)
38 ap_file.close()
39
40 if typeof(localdata) != TYPE_ARRAY:
41 print("AP keyboard file is corrupted")
42 localdata = []
43
44 if localdata.size() > 0:
45 letters_saved = localdata[0]
46 if localdata.size() > 1:
47 letters_in_keyholders = localdata[1]
48 if localdata.size() > 2:
49 keyholder_state = localdata[2]
50
51 for k in kALL_LETTERS:
52 var level = 0
53
54 if ap.get_letter_behavior(k, false) == ap.kLETTER_BEHAVIOR_UNLOCKED:
55 level += 1
56 if ap.get_letter_behavior(k, true) == ap.kLETTER_BEHAVIOR_UNLOCKED:
57 level += 1
58
59 letters_dynamic[k] = level
60
61 update_unlocks()
62
63
64func save():
65 var dir = DirAccess.open("user://")
66 var folder = "archipelago_keys"
67 if not dir.dir_exists(folder):
68 dir.make_dir(folder)
69
70 var file = FileAccess.open(filename, FileAccess.WRITE)
71
72 var data = [
73 letters_saved,
74 letters_in_keyholders,
75 keyholder_state,
76 ]
77 file.store_var(data, true)
78 file.close()
79
80
81func update_unlocks():
82 unlocks.resetKeys()
83
84 var has_doubles = false
85
86 for k in kALL_LETTERS:
87 var level = 0
88
89 if not letters_in_keyholders.has(k):
90 level = letters_saved.get(k, 0) + letters_dynamic.get(k, 0)
91
92 if level >= 2:
93 level = 2
94 has_doubles = true
95
96 if letters_blocked.has(k):
97 level = 0
98
99 unlocks.unlockKey(k, level)
100
101 if has_doubles and unlocks.data["double_letters"] != "unlocked":
102 var ap = global.get_node("Archipelago")
103 if ap.cyan_door_behavior == ap.kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER:
104 unlocks.setData("double_letters", "unlocked")
105
106
107func collect_local_letter(key, level):
108 if level < 0 or level > 2 or level < letters_saved.get(key, 0):
109 return
110
111 letters_saved[key] = level
112
113 if letters_blocked.has(key):
114 letters_blocked.erase(key)
115
116 update_unlocks()
117 save()
118
119
120func collect_remote_letter(key, level):
121 if level < 0 or level > 2 or level < letters_dynamic.get(key, 0):
122 return
123
124 letters_dynamic[key] = level
125
126 if letters_blocked.has(key):
127 letters_blocked.erase(key)
128
129 update_unlocks()
130 save()
131
132
133func put_in_keyholder(key, map, kh_path):
134 if not keyholder_state.has(map):
135 keyholder_state[map] = {}
136
137 keyholder_state[map][kh_path] = key
138 letters_in_keyholders.append(key)
139
140 get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(
141 key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
142 )
143
144 update_unlocks()
145 save()
146
147
148func remove_from_keyholder(key, map, kh_path):
149 if not keyholder_state.has(map):
150 # This... shouldn't happen.
151 keyholder_state[map] = {}
152
153 keyholder_state[map].erase(kh_path)
154 letters_in_keyholders.erase(key)
155
156 get_tree().get_root().get_node("scene").get_node(kh_path).setFromAp(key, 0)
157
158 update_unlocks()
159 save()
160
161
162func block_letter(key):
163 if not letters_blocked.has(key):
164 letters_blocked.append(key)
165
166 update_unlocks()
167
168
169func load_keyholders(map):
170 if keyholder_state.has(map):
171 var khs = keyholder_state[map]
172
173 for path in khs.keys():
174 var key = khs[path]
175 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
176 key, min(letters_saved.get(key, 0) + letters_dynamic.get(key, 0), 2)
177 )
178
179
180func reset_keyholders():
181 if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
182 return false
183
184 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()
185
186 if keyholder_state.has(global.map):
187 for path in keyholder_state[global.map]:
188 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
189 keyholder_state[global.map][path], 0
190 )
191
192 keyholder_state.clear()
193 letters_in_keyholders.clear()
194 letters_blocked.clear()
195
196 update_unlocks()
197 save()
198
199 return cleared_anything
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 a585167..0000000 --- a/client/Archipelago/manager.gd +++ /dev/null
@@ -1,544 +0,0 @@
1extends Node
2
3const MOD_VERSION = 6
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 = []
15var show_compass = false
16
17var client
18var keyboard
19
20var _localdata_file = ""
21var _last_new_item = -1
22var _batch_locations = false
23var _held_locations = []
24var _held_location_scouts = []
25var _location_scouts = {}
26var _item_locks = {}
27var _inverse_item_locks = {}
28var _held_letters = {}
29var _letters_setup = false
30
31const kSHUFFLE_LETTERS_VANILLA = 0
32const kSHUFFLE_LETTERS_UNLOCKED = 1
33const kSHUFFLE_LETTERS_PROGRESSIVE = 2
34const kSHUFFLE_LETTERS_VANILLA_CYAN = 3
35const kSHUFFLE_LETTERS_ITEM_CYAN = 4
36
37const kLETTER_BEHAVIOR_VANILLA = 0
38const kLETTER_BEHAVIOR_ITEM = 1
39const kLETTER_BEHAVIOR_UNLOCKED = 2
40
41const kCYAN_DOOR_BEHAVIOR_H2 = 0
42const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
43const kCYAN_DOOR_BEHAVIOR_ITEM = 2
44
45var apworld_version = [0, 0]
46var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
47var daedalus_roof_access = false
48var keyholder_sanity = false
49var shuffle_control_center_colors = false
50var shuffle_doors = false
51var shuffle_gallery_paintings = false
52var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
53var shuffle_symbols = false
54var strict_cyan_ending = false
55var strict_purple_ending = false
56var victory_condition = -1
57
58signal could_not_connect
59signal connect_status
60signal ap_connected
61
62
63func _init():
64 # Read AP settings from file, if there are any
65 if FileAccess.file_exists("user://ap_settings"):
66 var file = FileAccess.open("user://ap_settings", FileAccess.READ)
67 var data = file.get_var(true)
68 file.close()
69
70 if typeof(data) != TYPE_ARRAY:
71 global._print("AP settings file is corrupted")
72 data = []
73
74 if data.size() > 0:
75 ap_server = data[0]
76
77 if data.size() > 1:
78 ap_user = data[1]
79
80 if data.size() > 2:
81 ap_pass = data[2]
82
83 if data.size() > 3:
84 connection_history = data[3]
85
86 if data.size() > 4:
87 show_compass = data[4]
88
89
90func _ready():
91 client = SCRIPT_client.new()
92 client.SCRIPT_uuid = SCRIPT_uuid
93
94 client.connect("item_received", _process_item)
95 client.connect("message_received", _process_message)
96 client.connect("location_scout_received", _process_location_scout)
97 client.connect("could_not_connect", _client_could_not_connect)
98 client.connect("connect_status", _client_connect_status)
99 client.connect("client_connected", _client_connected)
100
101 add_child(client)
102
103 keyboard = SCRIPT_keyboard.new()
104 add_child(keyboard)
105
106
107func saveSettings():
108 # Save the AP settings to disk.
109 var path = "user://ap_settings"
110 var file = FileAccess.open(path, FileAccess.WRITE)
111
112 var data = [
113 ap_server,
114 ap_user,
115 ap_pass,
116 connection_history,
117 show_compass,
118 ]
119 file.store_var(data, true)
120 file.close()
121
122
123func saveLocaldata():
124 # Save the MW/slot specific settings to disk.
125 var dir = DirAccess.open("user://")
126 var folder = "archipelago_data"
127 if not dir.dir_exists(folder):
128 dir.make_dir(folder)
129
130 var file = FileAccess.open(_localdata_file, FileAccess.WRITE)
131
132 var data = [
133 _last_new_item,
134 ]
135 file.store_var(data, true)
136 file.close()
137
138
139func connectToServer():
140 _last_new_item = -1
141 _batch_locations = false
142 _held_locations = []
143 _held_location_scouts = []
144 _location_scouts = {}
145 _letters_setup = false
146 _held_letters = {}
147
148 client.connectToServer(ap_server, ap_user, ap_pass)
149
150
151func getSaveFileName():
152 return "zzAP_%s_%d" % [client._seed, client._slot]
153
154
155func disconnect_from_ap():
156 client.disconnect_from_ap()
157
158
159func get_item_id_for_door(door_id):
160 return _item_locks.get(door_id, null)
161
162
163func _process_item(item, index, from, flags, amount):
164 var item_name = "Unknown"
165 if client._item_id_to_name["Lingo 2"].has(item):
166 item_name = client._item_id_to_name["Lingo 2"][item]
167
168 var gamedata = global.get_node("Gamedata")
169
170 var prog_id = null
171 if _inverse_item_locks.has(item):
172 for lock in _inverse_item_locks.get(item):
173 if lock[1] != amount:
174 continue
175
176 if gamedata.progressive_id_by_ap_id.has(item):
177 prog_id = lock[0]
178
179 if gamedata.get_door_map_name(lock[0]) != global.map:
180 continue
181
182 var receivers = gamedata.get_door_receivers(lock[0])
183 var scene = get_tree().get_root().get_node_or_null("scene")
184 if scene != null:
185 for receiver in receivers:
186 var rnode = scene.get_node_or_null(receiver)
187 if rnode != null:
188 rnode.handleTriggered()
189
190 var letter_id = gamedata.letter_id_by_ap_id.get(item, null)
191 if letter_id != null:
192 var letter = gamedata.objects.get_letters()[letter_id]
193 if not letter.has_level2() or not letter.get_level2():
194 _process_key_item(letter.get_key(), amount)
195
196 if gamedata.symbol_item_ids.has(item):
197 var player = get_tree().get_root().get_node_or_null("scene/player")
198 if player != null:
199 player.emit_signal("evaluate_solvability")
200
201 # Show a message about the item if it's new.
202 if index != null and index > _last_new_item:
203 _last_new_item = index
204 saveLocaldata()
205
206 var player_name = "Unknown"
207 if client._player_name_by_slot.has(float(from)):
208 player_name = client._player_name_by_slot[float(from)]
209
210 var item_color = colorForItemType(flags)
211
212 var full_item_name = item_name
213 if prog_id != null:
214 var door = gamedata.objects.get_doors()[prog_id]
215 full_item_name = "%s (%s)" % [item_name, door.get_name()]
216
217 var message
218 if from == client._slot:
219 message = "Found [color=%s]%s[/color]" % [item_color, full_item_name]
220 else:
221 message = (
222 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
223 )
224
225 if gamedata.anti_trap_ids.has(item):
226 keyboard.block_letter(gamedata.anti_trap_ids[item])
227
228 global._print(message)
229
230 global.get_node("Messages").showMessage(message)
231
232
233func _process_message(message):
234 parse_printjson_for_textclient(message)
235
236 if (
237 !message.has("receiving")
238 or !message.has("item")
239 or message["item"]["player"] != client._slot
240 ):
241 return
242
243 var item_name = "Unknown"
244 var item_player_game = client._game_by_player[message["receiving"]]
245 if client._item_id_to_name[item_player_game].has(int(message["item"]["item"])):
246 item_name = client._item_id_to_name[item_player_game][int(message["item"]["item"])]
247
248 var location_name = "Unknown"
249 var location_player_game = client._game_by_player[message["item"]["player"]]
250 if client._location_id_to_name[location_player_game].has(int(message["item"]["location"])):
251 location_name = (client._location_id_to_name[location_player_game][int(
252 message["item"]["location"]
253 )])
254
255 var player_name = "Unknown"
256 if client._player_name_by_slot.has(message["receiving"]):
257 player_name = client._player_name_by_slot[message["receiving"]]
258
259 var item_color = colorForItemType(message["item"]["flags"])
260
261 if message["type"] == "Hint":
262 var is_for = ""
263 if message["receiving"] != client._slot:
264 is_for = " for %s" % player_name
265 if !message.has("found") || !message["found"]:
266 global.get_node("Messages").showMessage(
267 (
268 "Hint: [color=%s]%s[/color]%s is on %s"
269 % [item_color, item_name, is_for, location_name]
270 )
271 )
272 else:
273 if message["receiving"] != client._slot:
274 var sentMsg = "Sent [color=%s]%s[/color] to %s" % [item_color, item_name, player_name]
275 #if _hinted_locations.has(message["item"]["location"]):
276 # sentMsg += " ([color=#fafad2]Hinted![/color])"
277 global.get_node("Messages").showMessage(sentMsg)
278
279
280func parse_printjson_for_textclient(message):
281 var parts = []
282 for message_part in message["data"]:
283 if !message_part.has("type") and message_part.has("text"):
284 parts.append(message_part["text"])
285 elif message_part["type"] == "player_id":
286 if int(message_part["text"]) == client._slot:
287 parts.append(
288 "[color=#ee00ee]%s[/color]" % client._player_name_by_slot[client._slot]
289 )
290 else:
291 var from = float(message_part["text"])
292 parts.append("[color=#fafad2]%s[/color]" % client._player_name_by_slot[from])
293 elif message_part["type"] == "item_id":
294 var item_name = "Unknown"
295 var item_player_game = client._game_by_player[message_part["player"]]
296 if client._item_id_to_name[item_player_game].has(int(message_part["text"])):
297 item_name = client._item_id_to_name[item_player_game][int(message_part["text"])]
298
299 parts.append(
300 "[color=%s]%s[/color]" % [colorForItemType(message_part["flags"]), item_name]
301 )
302 elif message_part["type"] == "location_id":
303 var location_name = "Unknown"
304 var location_player_game = client._game_by_player[message_part["player"]]
305 if client._location_id_to_name[location_player_game].has(int(message_part["text"])):
306 location_name = client._location_id_to_name[location_player_game][int(
307 message_part["text"]
308 )]
309
310 parts.append("[color=#00ff7f]%s[/color]" % location_name)
311 elif message_part.has("text"):
312 parts.append(message_part["text"])
313
314 var textclient_node = global.get_node("Textclient")
315 if textclient_node != null:
316 textclient_node.parse_printjson("".join(parts))
317
318
319func _process_location_scout(item_id, location_id, player, flags):
320 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
321
322 if player == client._slot and flags & 4 != 0:
323 # This is a trap for us, so let's not display it.
324 return
325
326 var gamedata = global.get_node("Gamedata")
327 var map_id = gamedata.map_id_by_name.get(global.map)
328
329 var item_name = "Unknown"
330 var item_player_game = client._game_by_player[float(player)]
331 if client._item_id_to_name[item_player_game].has(item_id):
332 item_name = client._item_id_to_name[item_player_game][item_id]
333
334 var letter_id = gamedata.letter_id_by_ap_id.get(location_id, null)
335 if letter_id != null:
336 var letter = gamedata.objects.get_letters()[letter_id]
337 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
338 if room.get_map_id() == map_id:
339 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
340 letter.get_path()
341 )
342 if collectable != null:
343 collectable.setScoutedText(item_name)
344
345
346func _client_could_not_connect(message):
347 emit_signal("could_not_connect", message)
348
349
350func _client_connect_status(message):
351 emit_signal("connect_status", message)
352
353
354func _client_connected(slot_data):
355 var gamedata = global.get_node("Gamedata")
356
357 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
358 _last_new_item = -1
359
360 if FileAccess.file_exists(_localdata_file):
361 var ap_file = FileAccess.open(_localdata_file, FileAccess.READ)
362 var localdata = []
363 if ap_file != null:
364 localdata = ap_file.get_var(true)
365 ap_file.close()
366
367 if typeof(localdata) != TYPE_ARRAY:
368 print("AP localdata file is corrupted")
369 localdata = []
370
371 if localdata.size() > 0:
372 _last_new_item = localdata[0]
373
374 # Read slot data.
375 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
376 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
377 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
378 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
379 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
380 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
381 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
382 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false))
383 strict_cyan_ending = bool(slot_data.get("strict_cyan_ending", false))
384 strict_purple_ending = bool(slot_data.get("strict_purple_ending", false))
385 victory_condition = int(slot_data.get("victory_condition", 0))
386
387 if slot_data.has("version"):
388 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])]
389
390 # Set up item locks.
391 _item_locks = {}
392
393 if shuffle_doors:
394 for door in gamedata.objects.get_doors():
395 if (
396 door.get_type() == gamedata.SCRIPT_proto.DoorType.STANDARD
397 or door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
398 ):
399 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
400
401 for progressive in gamedata.objects.get_progressives():
402 for i in range(0, progressive.get_doors().size()):
403 var door = gamedata.objects.get_doors()[progressive.get_doors()[i]]
404 _item_locks[door.get_id()] = [progressive.get_ap_id(), i + 1]
405
406 for door_group in gamedata.objects.get_door_groups():
407 if (
408 door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CONNECTOR
409 or door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.SHUFFLE_GROUP
410 ):
411 for door in door_group.get_doors():
412 _item_locks[door] = [door_group.get_ap_id(), 1]
413
414 if shuffle_control_center_colors:
415 for door in gamedata.objects.get_doors():
416 if door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR:
417 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
418
419 for door_group in gamedata.objects.get_door_groups():
420 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.COLOR_CONNECTOR:
421 for door in door_group.get_doors():
422 _item_locks[door] = [door_group.get_ap_id(), 1]
423
424 if shuffle_gallery_paintings:
425 for door in gamedata.objects.get_doors():
426 if door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING:
427 _item_locks[door.get_id()] = [door.get_ap_id(), 1]
428
429 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
430 for door_group in gamedata.objects.get_door_groups():
431 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
432 for door in door_group.get_doors():
433 if not _item_locks.has(door):
434 _item_locks[door] = [door_group.get_ap_id(), 1]
435
436 # Create a reverse item locks map for processing items.
437 _inverse_item_locks = {}
438
439 for door_id in _item_locks.keys():
440 var lock = _item_locks.get(door_id)
441
442 if not _inverse_item_locks.has(lock[0]):
443 _inverse_item_locks[lock[0]] = []
444
445 _inverse_item_locks[lock[0]].append([door_id, lock[1]])
446
447 emit_signal("ap_connected")
448
449
450func start_batching_locations():
451 _batch_locations = true
452
453
454func send_location(loc_id):
455 if _batch_locations:
456 _held_locations.append(loc_id)
457 else:
458 client.sendLocation(loc_id)
459
460
461func scout_location(loc_id):
462 if _location_scouts.has(loc_id):
463 return _location_scouts.get(loc_id)
464
465 if _batch_locations:
466 _held_location_scouts.append(loc_id)
467 else:
468 client.scoutLocation(loc_id)
469
470 return null
471
472
473func stop_batching_locations():
474 _batch_locations = false
475
476 if not _held_locations.is_empty():
477 client.sendLocations(_held_locations)
478 _held_locations.clear()
479
480 if not _held_location_scouts.is_empty():
481 client.scoutLocations(_held_location_scouts)
482 _held_location_scouts.clear()
483
484
485func colorForItemType(flags):
486 var int_flags = int(flags)
487 if int_flags & 1: # progression
488 if int_flags & 2: # proguseful
489 return "#f0d200"
490 else:
491 return "#bc51e0"
492 elif int_flags & 2: # useful
493 return "#2b67ff"
494 elif int_flags & 4: # trap
495 return "#d63a22"
496 else: # filler
497 return "#14de9e"
498
499
500func get_letter_behavior(key, level2):
501 if shuffle_letters == kSHUFFLE_LETTERS_UNLOCKED:
502 return kLETTER_BEHAVIOR_UNLOCKED
503
504 if [kSHUFFLE_LETTERS_VANILLA_CYAN, kSHUFFLE_LETTERS_ITEM_CYAN].has(shuffle_letters):
505 if level2:
506 if shuffle_letters == kSHUFFLE_LETTERS_VANILLA_CYAN:
507 return kLETTER_BEHAVIOR_VANILLA
508 else:
509 return kLETTER_BEHAVIOR_ITEM
510 else:
511 return kLETTER_BEHAVIOR_UNLOCKED
512
513 if not level2 and ["h", "i", "n", "t"].has(key):
514 # This differs from the equivalent function in the apworld. Logically it is
515 # the same as UNLOCKED since they are in the starting room, but VANILLA
516 # means the player still has to actually pick up the letters.
517 return kLETTER_BEHAVIOR_VANILLA
518
519 if shuffle_letters == kSHUFFLE_LETTERS_PROGRESSIVE:
520 return kLETTER_BEHAVIOR_ITEM
521
522 return kLETTER_BEHAVIOR_VANILLA
523
524
525func setup_keys():
526 keyboard.load_seed()
527
528 _letters_setup = true
529
530 for k in _held_letters.keys():
531 _process_key_item(k, _held_letters[k])
532
533 _held_letters.clear()
534
535
536func _process_key_item(key, level):
537 if not _letters_setup:
538 _held_letters[key] = max(_held_letters.get(key, 0), level)
539 return
540
541 if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN:
542 level += 1
543
544 keyboard.collect_remote_letter(key, level)
diff --git a/client/Archipelago/messages.gd b/client/Archipelago/messages.gd deleted file mode 100644 index 82fdbc4..0000000 --- a/client/Archipelago/messages.gd +++ /dev/null
@@ -1,71 +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 if !_ordered_labels.is_empty():
52 var to_remove = _ordered_labels.pop_front()
53 var to_tween = get_tree().create_tween().bind_node(to_remove)
54 to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5)
55 to_tween.tween_callback(to_remove.queue_free)
56
57 if !_message_queue.is_empty():
58 var next_msg = _message_queue.pop_front()
59 _add_message(next_msg)
60
61 if timeout > 4:
62 timeout -= 3
63
64
65func clear():
66 _message_queue.clear()
67
68 for message_label in _ordered_labels:
69 message_label.queue_free()
70
71 _ordered_labels.clear()
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/panel.gd b/client/Archipelago/panel.gd deleted file mode 100644 index fdaaf0e..0000000 --- a/client/Archipelago/panel.gd +++ /dev/null
@@ -1,101 +0,0 @@
1extends "res://scripts/nodes/panel.gd"
2
3var panel_logic = null
4var symbol_solvable = true
5
6var black = load("res://assets/materials/black.material")
7
8
9func _ready():
10 super._ready()
11
12 var node_path = String(
13 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
14 )
15
16 var gamedata = global.get_node("Gamedata")
17 var panel_id = gamedata.get_panel_for_map_node_path(global.map, node_path)
18 if panel_id != null:
19 var ap = global.get_node("Archipelago")
20 if ap.shuffle_symbols:
21 if global.map == "the_entry" and node_path == "Panels/Entry/front_1":
22 clue = "i"
23 symbol = ""
24
25 setField("clue", clue)
26 setField("symbol", symbol)
27
28 panel_logic = gamedata.objects.get_panels()[panel_id]
29 checkSymbolSolvable()
30
31 if not symbol_solvable:
32 get_tree().get_root().get_node("scene/player").connect(
33 "evaluate_solvability", evaluateSolvability
34 )
35
36
37func checkSymbolSolvable():
38 var old_solvable = symbol_solvable
39 symbol_solvable = true
40
41 if panel_logic == null:
42 # There's no logic for this panel.
43 return
44
45 var ap = global.get_node("Archipelago")
46 if not ap.shuffle_symbols:
47 # Symbols aren't item-locked.
48 return
49
50 var gamedata = global.get_node("Gamedata")
51 for symbol in panel_logic.get_symbols():
52 var item_name = gamedata.kSYMBOL_ITEMS.get(symbol)
53 var item_id = gamedata.objects.get_special_ids()[item_name]
54 if ap.client.getItemAmount(item_id) < 1:
55 symbol_solvable = false
56 break
57
58 if symbol_solvable != old_solvable:
59 if symbol_solvable:
60 setField("clue", clue)
61 setField("symbol", symbol)
62 setField("answer", answer)
63 else:
64 quad_mesh.surface_set_material(0, black)
65 get_node("Hinge/clue").text = "missing"
66 get_node("Hinge/answer").text = "symbols"
67
68
69func checkSolvable(key):
70 checkSymbolSolvable()
71 if not symbol_solvable:
72 return false
73
74 return super.checkSolvable(key)
75
76
77func evaluateSolvability():
78 checkSolvable("")
79
80
81func passedInput(key, skip_focus_check = false):
82 if not symbol_solvable:
83 return
84
85 super.passedInput(key, skip_focus_check)
86
87
88func focus():
89 if not symbol_solvable:
90 has_focus = false
91 return
92
93 super.focus()
94
95
96func unfocus():
97 if not symbol_solvable:
98 has_focus = false
99 return
100
101 super.unfocus()
diff --git a/client/Archipelago/pauseMenu.gd b/client/Archipelago/pauseMenu.gd deleted file mode 100644 index cd1813c..0000000 --- a/client/Archipelago/pauseMenu.gd +++ /dev/null
@@ -1,44 +0,0 @@
1extends "res://scripts/ui/pauseMenu.gd"
2
3var compass_button
4
5
6func _ready():
7 var ap_panel = Panel.new()
8 ap_panel.name = "Archipelago"
9 get_node("menu/settings/settingsInner/TabContainer").add_child(ap_panel)
10
11 var ap = global.get_node("Archipelago")
12
13 compass_button = CheckBox.new()
14 compass_button.text = "show compass"
15 compass_button.button_pressed = ap.show_compass
16 compass_button.position = Vector2(65, 100)
17 compass_button.theme = preload("res://assets/themes/baseUI.tres")
18 compass_button.add_theme_font_size_override("font_size", 60)
19 compass_button.pressed.connect(_toggle_compass)
20 ap_panel.add_child(compass_button)
21
22 super._ready()
23
24
25func _pause_game():
26 global.get_node("Textclient").dismiss()
27 super._pause_game()
28
29
30func _main_menu():
31 global.loaded = false
32 global.get_node("Archipelago").disconnect_from_ap()
33 global.get_node("Messages").clear()
34 global.get_node("Compass").visible = false
35 super._main_menu()
36
37
38func _toggle_compass():
39 var ap = global.get_node("Archipelago")
40 ap.show_compass = compass_button.button_pressed
41 ap.saveSettings()
42
43 var compass = global.get_node("Compass")
44 compass.visible = compass_button.button_pressed
diff --git a/client/Archipelago/player.gd b/client/Archipelago/player.gd deleted file mode 100644 index 538830f..0000000 --- a/client/Archipelago/player.gd +++ /dev/null
@@ -1,362 +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
19signal evaluate_solvability
20
21var compass
22
23
24func _ready():
25 var khl_script = load("res://scripts/nodes/keyHolderListener.gd")
26
27 var ap = global.get_node("Archipelago")
28 var gamedata = global.get_node("Gamedata")
29
30 compass = global.get_node("Compass")
31 compass.visible = ap.show_compass
32
33 ap.start_batching_locations()
34
35 # Set up door locations.
36 var map_id = gamedata.map_id_by_name.get(global.map)
37 for door in gamedata.objects.get_doors():
38 if door.get_map_id() != map_id:
39 continue
40
41 if not door.has_ap_id():
42 continue
43
44 if (
45 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
46 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
47 ):
48 continue
49
50 var locationListener = ap.SCRIPT_locationListener.new()
51 locationListener.location_id = door.get_ap_id()
52 locationListener.name = "locationListener_%d" % door.get_ap_id()
53
54 for panel_ref in door.get_panels():
55 var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()]
56 var panel_path = panel_data.get_path()
57
58 if panel_ref.has_answer():
59 for proxy in panel_data.get_proxies():
60 if proxy.get_answer() == panel_ref.get_answer():
61 panel_path = proxy.get_path()
62 break
63
64 locationListener.senders.append(NodePath("/root/scene/" + panel_path))
65
66 for keyholder_ref in door.get_keyholders():
67 var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()]
68
69 var khl = khl_script.new()
70 khl.name = (
71 "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()]
72 )
73 khl.answer = keyholder_ref.get_key()
74 khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path()))
75 get_parent().add_child.call_deferred(khl)
76
77 locationListener.senders.append(NodePath("../" + khl.name))
78
79 for sender in door.get_senders():
80 locationListener.senders.append(NodePath("/root/scene/" + sender))
81
82 if door.has_complete_at():
83 locationListener.complete_at = door.get_complete_at()
84
85 get_parent().add_child.call_deferred(locationListener)
86
87 # Set up letter locations.
88 for letter in gamedata.objects.get_letters():
89 var room = gamedata.objects.get_rooms()[letter.get_room_id()]
90 if room.get_map_id() != map_id:
91 continue
92
93 var locationListener = ap.SCRIPT_locationListener.new()
94 locationListener.location_id = letter.get_ap_id()
95 locationListener.name = "locationListener_%d" % letter.get_ap_id()
96 locationListener.senders.append(NodePath("/root/scene/" + letter.get_path()))
97
98 get_parent().add_child.call_deferred(locationListener)
99
100 if (
101 ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2())
102 != ap.kLETTER_BEHAVIOR_VANILLA
103 ):
104 var scout = ap.scout_location(letter.get_ap_id())
105 if (
106 scout != null
107 and not (scout["player"] == ap.client._slot and scout["flags"] & 4 != 0)
108 ):
109 var item_name = "Unknown"
110 var item_player_game = ap.client._game_by_player[float(scout["player"])]
111 if ap.client._item_id_to_name[item_player_game].has(scout["item"]):
112 item_name = ap.client._item_id_to_name[item_player_game][scout["item"]]
113
114 var collectable = get_tree().get_root().get_node("scene").get_node_or_null(
115 letter.get_path()
116 )
117 if collectable != null:
118 collectable.setScoutedText.call_deferred(item_name)
119
120 # Set up mastery locations.
121 for mastery in gamedata.objects.get_masteries():
122 var room = gamedata.objects.get_rooms()[mastery.get_room_id()]
123 if room.get_map_id() != map_id:
124 continue
125
126 var locationListener = ap.SCRIPT_locationListener.new()
127 locationListener.location_id = mastery.get_ap_id()
128 locationListener.name = "locationListener_%d" % mastery.get_ap_id()
129 locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path()))
130
131 get_parent().add_child.call_deferred(locationListener)
132
133 # Set up ending locations.
134 for ending in gamedata.objects.get_endings():
135 var room = gamedata.objects.get_rooms()[ending.get_room_id()]
136 if room.get_map_id() != map_id:
137 continue
138
139 var locationListener = ap.SCRIPT_locationListener.new()
140 locationListener.location_id = ending.get_ap_id()
141 locationListener.name = "locationListener_%d" % ending.get_ap_id()
142 locationListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
143
144 get_parent().add_child.call_deferred(locationListener)
145
146 if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
147 var victoryListener = ap.SCRIPT_victoryListener.new()
148 victoryListener.name = "victoryListener"
149 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
150
151 get_parent().add_child.call_deferred(victoryListener)
152
153 # Set up keyholder locations, in keyholder sanity.
154 if ap.keyholder_sanity:
155 for keyholder in gamedata.objects.get_keyholders():
156 if not keyholder.has_key():
157 continue
158
159 var room = gamedata.objects.get_rooms()[keyholder.get_room_id()]
160 if room.get_map_id() != map_id:
161 continue
162
163 var locationListener = ap.SCRIPT_locationListener.new()
164 locationListener.location_id = keyholder.get_ap_id()
165 locationListener.name = "locationListener_%d" % keyholder.get_ap_id()
166
167 var khl = khl_script.new()
168 khl.name = "location_%d_keyholder" % keyholder.get_ap_id()
169 khl.answer = keyholder.get_key()
170 khl.senders.append(NodePath("/root/scene/" + keyholder.get_path()))
171 get_parent().add_child.call_deferred(khl)
172
173 locationListener.senders.append(NodePath("../" + khl.name))
174
175 get_parent().add_child.call_deferred(locationListener)
176
177 # Block off roof access in Daedalus.
178 if global.map == "daedalus" and not ap.daedalus_roof_access:
179 _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
180 _set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
181 _set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
182 _set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
183 _set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
184 _set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
185 _set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
186 _set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
187 _set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
188 _set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
189 _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
190 _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
191 _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
192 _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
193 _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)
194
195 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
196 var warp_exit = warp_exit_prefab.instantiate()
197 warp_exit.name = "roof_access_blocker_warp_exit"
198 warp_exit.position = Vector3(58, 10, 0)
199 warp_exit.rotation_degrees.y = 90
200 get_parent().add_child.call_deferred(warp_exit)
201
202 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
203 var warp_enter = warp_enter_prefab.instantiate()
204 warp_enter.target = warp_exit
205 warp_enter.position = Vector3(76.5, 30, 1)
206 warp_enter.scale = Vector3(4, 1.5, 1)
207 warp_enter.rotation_degrees.y = 90
208 get_parent().add_child.call_deferred(warp_enter)
209
210 if global.map == "the_entry":
211 # Remove door behind X1.
212 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
213 door_node.handleTriggered()
214
215 # Display win condition.
216 var sign_prefab = preload("res://objects/nodes/sign.tscn")
217 var sign1 = sign_prefab.instantiate()
218 sign1.position = Vector3(-7, 5, -15.01)
219 sign1.text = "victory"
220 get_parent().add_child.call_deferred(sign1)
221
222 var sign2 = sign_prefab.instantiate()
223 sign2.position = Vector3(-7, 4, -15.01)
224 sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?")
225
226 var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
227 if sign2_color == "white":
228 sign2_color = "silver"
229
230 sign2.material = load("res://assets/materials/%s.material" % sign2_color)
231 get_parent().add_child.call_deferred(sign2)
232
233 # Add the strict purple ending validation.
234 if global.map == "the_sun_temple" and ap.strict_purple_ending:
235 var panel_prefab = preload("res://objects/nodes/panel.tscn")
236 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
237 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
238
239 var previous_panel = null
240 var next_y = -100
241 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
242 for word in words:
243 var panel = panel_prefab.instantiate()
244 panel.position = Vector3(0, next_y, 0)
245 next_y -= 10
246 panel.clue = word
247 panel.symbol = ""
248 panel.answer = word
249 panel.name = "EndCheck_%s" % word
250
251 var tpl = tpl_prefab.instantiate()
252 tpl.teleport_point = Vector3(0, 1, 0)
253 tpl.teleport_rotate = Vector3(-45, 180, 0)
254 tpl.target_path = panel
255 tpl.name = "Teleport"
256
257 if previous_panel == null:
258 tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24"))
259 else:
260 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
261
262 var reversing = reverse_prefab.instantiate()
263 reversing.senders.append(NodePath(".."))
264 reversing.name = "Reversing"
265 tpl.senders.append(NodePath("../Reversing"))
266
267 panel.add_child.call_deferred(tpl)
268 panel.add_child.call_deferred(reversing)
269 get_parent().get_node("Panels").add_child.call_deferred(panel)
270
271 previous_panel = panel
272
273 # Duplicate the doors that usually wait on EQUINOX. We can't set the senders
274 # here for some reason so we actually set them in the door ready function.
275 var endplat = get_node("/root/scene/Components/Doors/EndPlatform")
276 var endplat2 = endplat.duplicate()
277 endplat2.name = "spe_EndPlatform"
278 endplat.get_parent().add_child.call_deferred(endplat2)
279 endplat.queue_free()
280
281 var entry2 = get_node("/root/scene/Components/Doors/entry_2")
282 var entry22 = entry2.duplicate()
283 entry22.name = "spe_entry_2"
284 entry2.get_parent().add_child.call_deferred(entry22)
285 entry2.queue_free()
286
287 # Add the strict cyan ending validation.
288 if global.map == "the_parthenon" and ap.strict_cyan_ending:
289 var panel_prefab = preload("res://objects/nodes/panel.tscn")
290 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
291 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
292
293 var previous_panel = null
294 var next_y = -100
295 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
296 for word in words:
297 var panel = panel_prefab.instantiate()
298 panel.position = Vector3(0, next_y, 0)
299 next_y -= 10
300 panel.clue = word
301 panel.symbol = "."
302 panel.answer = "%s%s" % [word, word]
303 panel.name = "EndCheck_%s" % word
304
305 var tpl = tpl_prefab.instantiate()
306 tpl.teleport_point = Vector3(0, 1, -11)
307 tpl.teleport_rotate = Vector3(-45, 0, 0)
308 tpl.target_path = panel
309 tpl.name = "Teleport"
310
311 if previous_panel == null:
312 tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers"))
313 else:
314 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
315
316 var reversing = reverse_prefab.instantiate()
317 reversing.senders.append(NodePath(".."))
318 reversing.name = "Reversing"
319 tpl.senders.append(NodePath("../Reversing"))
320
321 panel.add_child.call_deferred(tpl)
322 panel.add_child.call_deferred(reversing)
323 get_parent().get_node("Panels").add_child.call_deferred(panel)
324
325 previous_panel = panel
326
327 # Duplicate the door that usually waits on the rulers. We can't set the
328 # senders here for some reason so we actually set them in the door ready
329 # function.
330 var entry1 = get_node("/root/scene/Components/Doors/entry_1")
331 var entry12 = entry1.duplicate()
332 entry12.name = "spe_entry_1"
333 entry1.get_parent().add_child.call_deferred(entry12)
334 entry1.queue_free()
335
336 super._ready()
337
338 await get_tree().process_frame
339 await get_tree().process_frame
340
341 ap.stop_batching_locations()
342
343
344func _set_up_invis_wall(x, y, z, sx, sy, sz):
345 var prefab = preload("res://objects/nodes/block.tscn")
346 var newwall = prefab.instantiate()
347 newwall.position.x = x
348 newwall.position.y = y
349 newwall.position.z = z
350 newwall.scale.x = sz
351 newwall.scale.y = sy
352 newwall.scale.z = sx
353 newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
354 newwall.visibility_range_end = 3
355 newwall.visibility_range_end_margin = 1
356 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
357 newwall.skeleton = ".."
358 get_parent().add_child.call_deferred(newwall)
359
360
361func _process(_dt):
362 compass.update_rotation(global_rotation.y)
diff --git a/client/Archipelago/saver.gd b/client/Archipelago/saver.gd deleted file mode 100644 index 44bc179..0000000 --- a/client/Archipelago/saver.gd +++ /dev/null
@@ -1,23 +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()
10
11
12func reload():
13 # Just rewriting this whole thing so I can remove Chris's safeguard.
14 var file = FileAccess.open(path + type + ".save", FileAccess.READ)
15 if file:
16 var data = file.get_var(true)
17 file.close()
18 for datum in data:
19 var saveable = get_node_or_null(datum[0])
20 if saveable != null:
21 saveable.is_complete = datum[1]
22 if saveable.is_complete:
23 saveable.loadData(saveable.is_complete)
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 b7bfacf..0000000 --- a/client/Archipelago/settings_screen.gd +++ /dev/null
@@ -1,252 +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/panel.gd"))
44 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd"))
45 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd"))
46 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd"))
47 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd"))
48 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd"))
49 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd"))
50 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldport.gd"))
51 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd"))
52
53 var proto_script = load("user://maps/Archipelago/generated/proto.gd")
54 var gamedata_script = load("user://maps/Archipelago/gamedata.gd")
55 var gamedata_instance = gamedata_script.new(proto_script)
56 gamedata_instance.load(
57 FileAccess.get_file_as_bytes("user://maps/Archipelago/generated/data.binpb")
58 )
59 gamedata_instance.name = "Gamedata"
60 global.add_child(gamedata_instance)
61
62 var messages_script = load("user://maps/Archipelago/messages.gd")
63 var messages_instance = messages_script.new()
64 messages_instance.name = "Messages"
65 global.add_child(messages_instance)
66
67 var textclient_script = load("user://maps/Archipelago/textclient.gd")
68 var textclient_instance = textclient_script.new()
69 textclient_instance.name = "Textclient"
70 global.add_child(textclient_instance)
71
72 var compass_overlay_script = load("user://maps/Archipelago/compass_overlay.gd")
73 var compass_overlay_instance = compass_overlay_script.new()
74 compass_overlay_instance.name = "Compass"
75 compass_overlay_instance.SCRIPT_compass = load("user://maps/Archipelago/compass.gd")
76 global.add_child(compass_overlay_instance)
77
78 var ap = global.get_node("Archipelago")
79 var gamedata = global.get_node("Gamedata")
80 ap.connect("ap_connected", connectionSuccessful)
81 ap.connect("could_not_connect", connectionUnsuccessful)
82 ap.connect("connect_status", connectionStatus)
83
84 # Populate textboxes with AP settings.
85 $Panel/server_box.text = ap.ap_server
86 $Panel/player_box.text = ap.ap_user
87 $Panel/password_box.text = ap.ap_pass
88
89 var history_box = $Panel/connection_history
90 if ap.connection_history.is_empty():
91 history_box.disabled = true
92 else:
93 history_box.disabled = false
94
95 var i = 0
96 for details in ap.connection_history:
97 history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i)
98 i += 1
99
100 history_box.get_popup().connect("id_pressed", historySelected)
101
102 # Show client version.
103 $Panel/title.text = "ARCHIPELAGO (%d.%d)" % [gamedata.objects.get_version(), ap.MOD_VERSION]
104
105 # Increase font size in text boxes.
106 $Panel/server_box.add_theme_font_size_override("font_size", 36)
107 $Panel/player_box.add_theme_font_size_override("font_size", 36)
108 $Panel/password_box.add_theme_font_size_override("font_size", 36)
109
110 # Set up version mismatch dialog.
111 $Panel/VersionMismatch.connect("confirmed", startGame)
112 $Panel/VersionMismatch.get_cancel_button().pressed.connect(versionMismatchDeclined)
113
114
115# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
116func installScriptExtension(childScript: Resource):
117 # Force Godot to compile the script now.
118 # We need to do this here to ensure that the inheritance chain is
119 # properly set up, and multiple mods can chain-extend the same
120 # class multiple times.
121 # This is also needed to make Godot instantiate the extended class
122 # when creating singletons.
123 # The actual instance is thrown away.
124 childScript.new()
125
126 var parentScript = childScript.get_base_script()
127 var parentScriptPath = parentScript.resource_path
128 global._print("ModLoader: Installing script extension over %s" % parentScriptPath)
129 childScript.take_over_path(parentScriptPath)
130
131
132func connectionStatus(message):
133 var popup = self.get_node("Panel/AcceptDialog")
134 popup.title = "Connecting to Archipelago"
135 popup.dialog_text = message
136 popup.exclusive = true
137 popup.get_ok_button().visible = false
138 popup.popup_centered()
139
140
141func connectionSuccessful():
142 var ap = global.get_node("Archipelago")
143 var gamedata = global.get_node("Gamedata")
144
145 # Check for major version mismatch.
146 if ap.apworld_version[0] != gamedata.objects.get_version():
147 $Panel/AcceptDialog.exclusive = false
148
149 var popup = self.get_node("Panel/VersionMismatch")
150 popup.title = "Version Mismatch!"
151 popup.dialog_text = (
152 "This slot was generated using v%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%d).\nIt is highly recommended to play using the correct version of the client.\nYou may experience bugs or logic issues if you continue."
153 % [
154 ap.apworld_version[0],
155 ap.apworld_version[1],
156 gamedata.objects.get_version(),
157 ap.MOD_VERSION
158 ]
159 )
160 popup.exclusive = true
161 popup.popup_centered()
162
163 return
164
165 startGame()
166
167
168func startGame():
169 var ap = global.get_node("Archipelago")
170
171 # Save connection details
172 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass]
173 if ap.connection_history.has(connection_details):
174 ap.connection_history.erase(connection_details)
175 ap.connection_history.push_front(connection_details)
176 if ap.connection_history.size() > 10:
177 ap.connection_history.resize(10)
178 ap.saveSettings()
179
180 # Switch to the_entry
181 Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
182 global.user = ap.getSaveFileName()
183 global.universe = "lingo"
184 global.map = "the_entry"
185
186 unlocks.resetCollectables()
187 unlocks.resetData()
188
189 ap.setup_keys()
190
191 unlocks.loadCollectables()
192 unlocks.loadData()
193 unlocks.unlockKey("capslock", 1)
194
195 clearResourceCache("res://objects/meshes/gridDoor.tscn")
196 clearResourceCache("res://objects/nodes/collectable.tscn")
197 clearResourceCache("res://objects/nodes/door.tscn")
198 clearResourceCache("res://objects/nodes/keyHolder.tscn")
199 clearResourceCache("res://objects/nodes/listeners/animationListener.tscn")
200 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
201 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
202 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
203 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn")
204 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
205 clearResourceCache("res://objects/nodes/panel.tscn")
206 clearResourceCache("res://objects/nodes/player.tscn")
207 clearResourceCache("res://objects/nodes/saver.tscn")
208 clearResourceCache("res://objects/nodes/teleport.tscn")
209 clearResourceCache("res://objects/nodes/worldport.tscn")
210 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn")
211
212 var paintings_dir = DirAccess.open("res://objects/meshes/paintings")
213 if paintings_dir:
214 paintings_dir.list_dir_begin()
215 var file_name = paintings_dir.get_next()
216 while file_name != "":
217 if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"):
218 clearResourceCache("res://objects/meshes/paintings/" + file_name)
219 file_name = paintings_dir.get_next()
220
221 switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn")
222
223
224func connectionUnsuccessful(error_message):
225 $Panel/connect_button.disabled = false
226
227 var popup = $Panel/AcceptDialog
228 popup.title = "Could not connect to Archipelago"
229 popup.dialog_text = error_message
230 popup.exclusive = true
231 popup.get_ok_button().visible = true
232 popup.popup_centered()
233
234 $Panel/connect_button.disabled = false
235
236
237func versionMismatchDeclined():
238 $Panel/AcceptDialog.hide()
239 $Panel/connect_button.disabled = false
240
241
242func historySelected(index):
243 var ap = global.get_node("Archipelago")
244 var details = ap.connection_history[index]
245
246 $Panel/server_box.text = details[0]
247 $Panel/player_box.text = details[1]
248 $Panel/password_box.text = details[2]
249
250
251func clearResourceCache(path):
252 ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE)
diff --git a/client/Archipelago/teleport.gd b/client/Archipelago/teleport.gd deleted file mode 100644 index 428d50b..0000000 --- a/client/Archipelago/teleport.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/teleport.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/teleportListener.gd b/client/Archipelago/teleportListener.gd deleted file mode 100644 index 6f363af..0000000 --- a/client/Archipelago/teleportListener.gd +++ /dev/null
@@ -1,49 +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 if (
13 global.map == "daedalus"
14 and (
15 node_path == "Components/Triggers/teleportListenerConnections"
16 or node_path == "Components/Triggers/teleportListenerConnections2"
17 )
18 ):
19 # Effectively disable these.
20 teleport_point = target_path.position
21 return
22
23 var gamedata = global.get_node("Gamedata")
24 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
25 if door_id != null:
26 var ap = global.get_node("Archipelago")
27 var item_lock = ap.get_item_id_for_door(door_id)
28
29 if item_lock != null:
30 item_id = item_lock[0]
31 item_amount = item_lock[1]
32
33 self.senders = []
34 self.senderGroup = []
35 self.nested = false
36 self.complete_at = 0
37 self.max_length = 0
38 self.excludeSenders = []
39
40 call_deferred("_readier")
41
42 super._ready()
43
44
45func _readier():
46 var ap = global.get_node("Archipelago")
47
48 if ap.client.getItemAmount(item_id) >= item_amount:
49 handleTriggered()
diff --git a/client/Archipelago/textclient.gd b/client/Archipelago/textclient.gd deleted file mode 100644 index 85cc6d2..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 global.loaded and 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/visibilityListener.gd b/client/Archipelago/visibilityListener.gd deleted file mode 100644 index 5ea17a0..0000000 --- a/client/Archipelago/visibilityListener.gd +++ /dev/null
@@ -1,38 +0,0 @@
1extends "res://scripts/nodes/listeners/visibilityListener.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/worldport.gd b/client/Archipelago/worldport.gd deleted file mode 100644 index d0fb6c9..0000000 --- a/client/Archipelago/worldport.gd +++ /dev/null
@@ -1,10 +0,0 @@
1extends "res://scripts/nodes/worldport.gd"
2
3
4func _ready():
5 if global.map == "icarus" and exit == "daedalus":
6 var ap = global.get_node("Archipelago")
7 if not ap.daedalus_roof_access:
8 entry_point = Vector3(58, 10, 0)
9
10 super._ready()
diff --git a/client/Archipelago/worldportListener.gd b/client/Archipelago/worldportListener.gd deleted file mode 100644 index 5c2faff..0000000 --- a/client/Archipelago/worldportListener.gd +++ /dev/null
@@ -1,8 +0,0 @@
1extends "res://scripts/nodes/listeners/worldportListener.gd"
2
3
4func handleTriggered():
5 if exit == "menus/credits":
6 return
7
8 super.handleTriggered()