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/client.gd6
-rw-r--r--client/Archipelago/compass.gd66
-rw-r--r--client/Archipelago/compass_overlay.gd17
-rw-r--r--client/Archipelago/door.gd8
-rw-r--r--client/Archipelago/gamedata.gd7
-rw-r--r--client/Archipelago/keyboard.gd25
-rw-r--r--client/Archipelago/manager.gd36
-rw-r--r--client/Archipelago/messages.gd18
-rw-r--r--client/Archipelago/pauseMenu.gd32
-rw-r--r--client/Archipelago/player.gd148
-rw-r--r--client/Archipelago/saver.gd14
-rw-r--r--client/Archipelago/settings_screen.gd48
-rw-r--r--client/Archipelago/teleport.gd38
-rw-r--r--client/Archipelago/teleportListener.gd11
-rw-r--r--client/Archipelago/worldport.gd10
-rw-r--r--client/Archipelago/worldportListener.gd4
16 files changed, 472 insertions, 16 deletions
diff --git a/client/Archipelago/client.gd b/client/Archipelago/client.gd index 2e080fd..843647d 100644 --- a/client/Archipelago/client.gd +++ b/client/Archipelago/client.gd
@@ -47,6 +47,8 @@ signal location_scout_received(item_id, location_id, player, flags)
47func _init(): 47func _init():
48 set_process_mode(Node.PROCESS_MODE_ALWAYS) 48 set_process_mode(Node.PROCESS_MODE_ALWAYS)
49 49
50 _ws.inbound_buffer_size = 8388608
51
50 global._print("Instantiated APClient") 52 global._print("Instantiated APClient")
51 53
52 # Read AP datapackages from file, if there are any 54 # Read AP datapackages from file, if there are any
@@ -225,7 +227,7 @@ func _process(_delta):
225 error_message = "Unknown error." 227 error_message = "Unknown error."
226 228
227 _initiated_disconnect = true 229 _initiated_disconnect = true
228 _ws.disconnect_from_host() 230 _ws.close()
229 231
230 emit_signal("could_not_connect", error_message) 232 emit_signal("could_not_connect", error_message)
231 global._print("Connection to AP refused") 233 global._print("Connection to AP refused")
@@ -309,7 +311,7 @@ func connectToServer(server, un, pw):
309 % err 311 % err
310 ) 312 )
311 ) 313 )
312 global._print("Could not connect to AP: " + err) 314 global._print("Could not connect to AP: %d" % err)
313 return 315 return
314 _should_process = true 316 _should_process = true
315 317
diff --git a/client/Archipelago/compass.gd b/client/Archipelago/compass.gd new file mode 100644 index 0000000..c90475a --- /dev/null +++ b/client/Archipelago/compass.gd
@@ -0,0 +1,66 @@
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 new file mode 100644 index 0000000..56e81ff --- /dev/null +++ b/client/Archipelago/compass_overlay.gd
@@ -0,0 +1,17 @@
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 index fead818..49f5728 100644 --- a/client/Archipelago/door.gd +++ b/client/Archipelago/door.gd
@@ -28,6 +28,14 @@ func _ready():
28 28
29 call_deferred("_readier") 29 call_deferred("_readier")
30 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
31 super._ready() 39 super._ready()
32 40
33 41
diff --git a/client/Archipelago/gamedata.gd b/client/Archipelago/gamedata.gd index d8d16ed..41d966a 100644 --- a/client/Archipelago/gamedata.gd +++ b/client/Archipelago/gamedata.gd
@@ -11,6 +11,7 @@ var map_id_by_name = {}
11var progressive_id_by_ap_id = {} 11var progressive_id_by_ap_id = {}
12var letter_id_by_ap_id = {} 12var letter_id_by_ap_id = {}
13var symbol_item_ids = [] 13var symbol_item_ids = []
14var anti_trap_ids = {}
14 15
15var kSYMBOL_ITEMS 16var kSYMBOL_ITEMS
16 17
@@ -97,6 +98,12 @@ func load(data_bytes):
97 for symbol_name in kSYMBOL_ITEMS.values(): 98 for symbol_name in kSYMBOL_ITEMS.values():
98 symbol_item_ids.append(objects.get_special_ids()[symbol_name]) 99 symbol_item_ids.append(objects.get_special_ids()[symbol_name])
99 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
100 107
101func get_door_for_map_node_path(map_name, node_path): 108func get_door_for_map_node_path(map_name, node_path):
102 if not door_id_by_map_node_path.has(map_name): 109 if not door_id_by_map_node_path.has(map_name):
diff --git a/client/Archipelago/keyboard.gd b/client/Archipelago/keyboard.gd index 600a047..450566d 100644 --- a/client/Archipelago/keyboard.gd +++ b/client/Archipelago/keyboard.gd
@@ -4,6 +4,7 @@ const kALL_LETTERS = "abcdefghjiklmnopqrstuvwxyz"
4 4
5var letters_saved = {} 5var letters_saved = {}
6var letters_in_keyholders = [] 6var letters_in_keyholders = []
7var letters_blocked = []
7var letters_dynamic = {} 8var letters_dynamic = {}
8var keyholder_state = {} 9var keyholder_state = {}
9 10
@@ -17,6 +18,7 @@ func _init():
17func reset(): 18func reset():
18 letters_saved.clear() 19 letters_saved.clear()
19 letters_in_keyholders.clear() 20 letters_in_keyholders.clear()
21 letters_blocked.clear()
20 letters_dynamic.clear() 22 letters_dynamic.clear()
21 keyholder_state.clear() 23 keyholder_state.clear()
22 24
@@ -91,6 +93,9 @@ func update_unlocks():
91 level = 2 93 level = 2
92 has_doubles = true 94 has_doubles = true
93 95
96 if letters_blocked.has(k):
97 level = 0
98
94 unlocks.unlockKey(k, level) 99 unlocks.unlockKey(k, level)
95 100
96 if has_doubles and unlocks.data["double_letters"] != "unlocked": 101 if has_doubles and unlocks.data["double_letters"] != "unlocked":
@@ -105,6 +110,9 @@ func collect_local_letter(key, level):
105 110
106 letters_saved[key] = level 111 letters_saved[key] = level
107 112
113 if letters_blocked.has(key):
114 letters_blocked.erase(key)
115
108 update_unlocks() 116 update_unlocks()
109 save() 117 save()
110 118
@@ -115,6 +123,9 @@ func collect_remote_letter(key, level):
115 123
116 letters_dynamic[key] = level 124 letters_dynamic[key] = level
117 125
126 if letters_blocked.has(key):
127 letters_blocked.erase(key)
128
118 update_unlocks() 129 update_unlocks()
119 save() 130 save()
120 131
@@ -148,6 +159,13 @@ func remove_from_keyholder(key, map, kh_path):
148 save() 159 save()
149 160
150 161
162func block_letter(key):
163 if not letters_blocked.has(key):
164 letters_blocked.append(key)
165
166 update_unlocks()
167
168
151func load_keyholders(map): 169func load_keyholders(map):
152 if keyholder_state.has(map): 170 if keyholder_state.has(map):
153 var khs = keyholder_state[map] 171 var khs = keyholder_state[map]
@@ -160,9 +178,11 @@ func load_keyholders(map):
160 178
161 179
162func reset_keyholders(): 180func reset_keyholders():
163 if letters_in_keyholders.is_empty(): 181 if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
164 return false 182 return false
165 183
184 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()
185
166 if keyholder_state.has(global.map): 186 if keyholder_state.has(global.map):
167 for path in keyholder_state[global.map]: 187 for path in keyholder_state[global.map]:
168 get_tree().get_root().get_node("scene").get_node(path).setFromAp( 188 get_tree().get_root().get_node("scene").get_node(path).setFromAp(
@@ -171,8 +191,9 @@ func reset_keyholders():
171 191
172 keyholder_state.clear() 192 keyholder_state.clear()
173 letters_in_keyholders.clear() 193 letters_in_keyholders.clear()
194 letters_blocked.clear()
174 195
175 update_unlocks() 196 update_unlocks()
176 save() 197 save()
177 198
178 return true 199 return cleared_anything
diff --git a/client/Archipelago/manager.gd b/client/Archipelago/manager.gd index 6eea2bd..218870c 100644 --- a/client/Archipelago/manager.gd +++ b/client/Archipelago/manager.gd
@@ -1,6 +1,6 @@
1extends Node 1extends Node
2 2
3const MOD_VERSION = 0 3const MOD_VERSION = 7
4 4
5var SCRIPT_client 5var SCRIPT_client
6var SCRIPT_keyboard 6var SCRIPT_keyboard
@@ -12,6 +12,7 @@ var ap_server = ""
12var ap_user = "" 12var ap_user = ""
13var ap_pass = "" 13var ap_pass = ""
14var connection_history = [] 14var connection_history = []
15var show_compass = false
15 16
16var client 17var client
17var keyboard 18var keyboard
@@ -41,13 +42,17 @@ const kCYAN_DOOR_BEHAVIOR_H2 = 0
41const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 42const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
42const kCYAN_DOOR_BEHAVIOR_ITEM = 2 43const kCYAN_DOOR_BEHAVIOR_ITEM = 2
43 44
45var apworld_version = [0, 0]
44var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 46var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
45var daedalus_roof_access = false 47var daedalus_roof_access = false
46var keyholder_sanity = false 48var keyholder_sanity = false
47var shuffle_control_center_colors = false 49var shuffle_control_center_colors = false
48var shuffle_doors = false 50var shuffle_doors = false
51var shuffle_gallery_paintings = false
49var shuffle_letters = kSHUFFLE_LETTERS_VANILLA 52var shuffle_letters = kSHUFFLE_LETTERS_VANILLA
50var shuffle_symbols = false 53var shuffle_symbols = false
54var strict_cyan_ending = false
55var strict_purple_ending = false
51var victory_condition = -1 56var victory_condition = -1
52 57
53signal could_not_connect 58signal could_not_connect
@@ -78,6 +83,9 @@ func _init():
78 if data.size() > 3: 83 if data.size() > 3:
79 connection_history = data[3] 84 connection_history = data[3]
80 85
86 if data.size() > 4:
87 show_compass = data[4]
88
81 89
82func _ready(): 90func _ready():
83 client = SCRIPT_client.new() 91 client = SCRIPT_client.new()
@@ -106,6 +114,7 @@ func saveSettings():
106 ap_user, 114 ap_user,
107 ap_pass, 115 ap_pass,
108 connection_history, 116 connection_history,
117 show_compass,
109 ] 118 ]
110 file.store_var(data, true) 119 file.store_var(data, true)
111 file.close() 120 file.close()
@@ -213,6 +222,9 @@ func _process_item(item, index, from, flags, amount):
213 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name] 222 "Received [color=%s]%s[/color] from %s" % [item_color, full_item_name, player_name]
214 ) 223 )
215 224
225 if gamedata.anti_trap_ids.has(item):
226 keyboard.block_letter(gamedata.anti_trap_ids[item])
227
216 global._print(message) 228 global._print(message)
217 229
218 global.get_node("Messages").showMessage(message) 230 global.get_node("Messages").showMessage(message)
@@ -307,6 +319,10 @@ func parse_printjson_for_textclient(message):
307func _process_location_scout(item_id, location_id, player, flags): 319func _process_location_scout(item_id, location_id, player, flags):
308 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags} 320 _location_scouts[location_id] = {"item": item_id, "player": player, "flags": flags}
309 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
310 var gamedata = global.get_node("Gamedata") 326 var gamedata = global.get_node("Gamedata")
311 var map_id = gamedata.map_id_by_name.get(global.map) 327 var map_id = gamedata.map_id_by_name.get(global.map)
312 328
@@ -327,8 +343,8 @@ func _process_location_scout(item_id, location_id, player, flags):
327 collectable.setScoutedText(item_name) 343 collectable.setScoutedText(item_name)
328 344
329 345
330func _client_could_not_connect(): 346func _client_could_not_connect(message):
331 emit_signal("could_not_connect") 347 emit_signal("could_not_connect", message)
332 348
333 349
334func _client_connect_status(message): 350func _client_connect_status(message):
@@ -361,10 +377,16 @@ func _client_connected(slot_data):
361 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) 377 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
362 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) 378 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
363 shuffle_doors = bool(slot_data.get("shuffle_doors", false)) 379 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
380 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
364 shuffle_letters = int(slot_data.get("shuffle_letters", 0)) 381 shuffle_letters = int(slot_data.get("shuffle_letters", 0))
365 shuffle_symbols = bool(slot_data.get("shuffle_symbols", false)) 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))
366 victory_condition = int(slot_data.get("victory_condition", 0)) 385 victory_condition = int(slot_data.get("victory_condition", 0))
367 386
387 if slot_data.has("version"):
388 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])]
389
368 # Set up item locks. 390 # Set up item locks.
369 _item_locks = {} 391 _item_locks = {}
370 392
@@ -399,6 +421,11 @@ func _client_connected(slot_data):
399 for door in door_group.get_doors(): 421 for door in door_group.get_doors():
400 _item_locks[door] = [door_group.get_ap_id(), 1] 422 _item_locks[door] = [door_group.get_ap_id(), 1]
401 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
402 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM: 429 if cyan_door_behavior == kCYAN_DOOR_BEHAVIOR_ITEM:
403 for door_group in gamedata.objects.get_door_groups(): 430 for door_group in gamedata.objects.get_door_groups():
404 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS: 431 if door_group.get_type() == gamedata.SCRIPT_proto.DoorGroupType.CYAN_DOORS:
@@ -511,4 +538,7 @@ func _process_key_item(key, level):
511 _held_letters[key] = max(_held_letters.get(key, 0), level) 538 _held_letters[key] = max(_held_letters.get(key, 0), level)
512 return 539 return
513 540
541 if shuffle_letters == kSHUFFLE_LETTERS_ITEM_CYAN:
542 level += 1
543
514 keyboard.collect_remote_letter(key, level) 544 keyboard.collect_remote_letter(key, level)
diff --git a/client/Archipelago/messages.gd b/client/Archipelago/messages.gd index 52f38b9..82fdbc4 100644 --- a/client/Archipelago/messages.gd +++ b/client/Archipelago/messages.gd
@@ -48,10 +48,11 @@ func showMessage(text):
48 while !_ordered_labels.is_empty(): 48 while !_ordered_labels.is_empty():
49 await get_tree().create_timer(timeout).timeout 49 await get_tree().create_timer(timeout).timeout
50 50
51 var to_remove = _ordered_labels.pop_front() 51 if !_ordered_labels.is_empty():
52 var to_tween = get_tree().create_tween().bind_node(to_remove) 52 var to_remove = _ordered_labels.pop_front()
53 to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5) 53 var to_tween = get_tree().create_tween().bind_node(to_remove)
54 to_tween.tween_callback(to_remove.queue_free) 54 to_tween.tween_property(to_remove, "modulate:a", 0.0, 0.5)
55 to_tween.tween_callback(to_remove.queue_free)
55 56
56 if !_message_queue.is_empty(): 57 if !_message_queue.is_empty():
57 var next_msg = _message_queue.pop_front() 58 var next_msg = _message_queue.pop_front()
@@ -59,3 +60,12 @@ func showMessage(text):
59 60
60 if timeout > 4: 61 if timeout > 4:
61 timeout -= 3 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/pauseMenu.gd b/client/Archipelago/pauseMenu.gd index 5da114a..cd1813c 100644 --- a/client/Archipelago/pauseMenu.gd +++ b/client/Archipelago/pauseMenu.gd
@@ -1,5 +1,26 @@
1extends "res://scripts/ui/pauseMenu.gd" 1extends "res://scripts/ui/pauseMenu.gd"
2 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
3 24
4func _pause_game(): 25func _pause_game():
5 global.get_node("Textclient").dismiss() 26 global.get_node("Textclient").dismiss()
@@ -9,4 +30,15 @@ func _pause_game():
9func _main_menu(): 30func _main_menu():
10 global.loaded = false 31 global.loaded = false
11 global.get_node("Archipelago").disconnect_from_ap() 32 global.get_node("Archipelago").disconnect_from_ap()
33 global.get_node("Messages").clear()
34 global.get_node("Compass").visible = false
12 super._main_menu() 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 index 4b995bc..538830f 100644 --- a/client/Archipelago/player.gd +++ b/client/Archipelago/player.gd
@@ -18,6 +18,8 @@ const kEndingNameByVictoryValue = {
18 18
19signal evaluate_solvability 19signal evaluate_solvability
20 20
21var compass
22
21 23
22func _ready(): 24func _ready():
23 var khl_script = load("res://scripts/nodes/keyHolderListener.gd") 25 var khl_script = load("res://scripts/nodes/keyHolderListener.gd")
@@ -25,6 +27,9 @@ func _ready():
25 var ap = global.get_node("Archipelago") 27 var ap = global.get_node("Archipelago")
26 var gamedata = global.get_node("Gamedata") 28 var gamedata = global.get_node("Gamedata")
27 29
30 compass = global.get_node("Compass")
31 compass.visible = ap.show_compass
32
28 ap.start_batching_locations() 33 ap.start_batching_locations()
29 34
30 # Set up door locations. 35 # Set up door locations.
@@ -36,7 +41,10 @@ func _ready():
36 if not door.has_ap_id(): 41 if not door.has_ap_id():
37 continue 42 continue
38 43
39 if door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY: 44 if (
45 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
46 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
47 ):
40 continue 48 continue
41 49
42 var locationListener = ap.SCRIPT_locationListener.new() 50 var locationListener = ap.SCRIPT_locationListener.new()
@@ -68,6 +76,12 @@ func _ready():
68 76
69 locationListener.senders.append(NodePath("../" + khl.name)) 77 locationListener.senders.append(NodePath("../" + khl.name))
70 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
71 get_parent().add_child.call_deferred(locationListener) 85 get_parent().add_child.call_deferred(locationListener)
72 86
73 # Set up letter locations. 87 # Set up letter locations.
@@ -88,7 +102,10 @@ func _ready():
88 != ap.kLETTER_BEHAVIOR_VANILLA 102 != ap.kLETTER_BEHAVIOR_VANILLA
89 ): 103 ):
90 var scout = ap.scout_location(letter.get_ap_id()) 104 var scout = ap.scout_location(letter.get_ap_id())
91 if scout != null: 105 if (
106 scout != null
107 and not (scout["player"] == ap.client._slot and scout["flags"] & 4 != 0)
108 ):
92 var item_name = "Unknown" 109 var item_name = "Unknown"
93 var item_player_game = ap.client._game_by_player[float(scout["player"])] 110 var item_player_game = ap.client._game_by_player[float(scout["player"])]
94 if ap.client._item_id_to_name[item_player_game].has(scout["item"]): 111 if ap.client._item_id_to_name[item_player_game].has(scout["item"]):
@@ -190,11 +207,132 @@ func _ready():
190 warp_enter.rotation_degrees.y = 90 207 warp_enter.rotation_degrees.y = 90
191 get_parent().add_child.call_deferred(warp_enter) 208 get_parent().add_child.call_deferred(warp_enter)
192 209
193 # Remove door behind X1.
194 if global.map == "the_entry": 210 if global.map == "the_entry":
211 # Remove door behind X1.
195 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1") 212 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
196 door_node.handleTriggered() 213 door_node.handleTriggered()
197 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
198 super._ready() 336 super._ready()
199 337
200 await get_tree().process_frame 338 await get_tree().process_frame
@@ -218,3 +356,7 @@ func _set_up_invis_wall(x, y, z, sx, sy, sz):
218 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF 356 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
219 newwall.skeleton = ".." 357 newwall.skeleton = ".."
220 get_parent().add_child.call_deferred(newwall) 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 index 0fba9e7..44bc179 100644 --- a/client/Archipelago/saver.gd +++ b/client/Archipelago/saver.gd
@@ -7,3 +7,17 @@ func levelLoaded():
7 ap.keyboard.load_keyholders.call_deferred(global.map) 7 ap.keyboard.load_keyholders.call_deferred(global.map)
8 else: 8 else:
9 reload.call_deferred() 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_screen.gd b/client/Archipelago/settings_screen.gd index ff6f9df..b7bfacf 100644 --- a/client/Archipelago/settings_screen.gd +++ b/client/Archipelago/settings_screen.gd
@@ -44,8 +44,10 @@ func _ready():
44 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd")) 44 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/pauseMenu.gd"))
45 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd")) 45 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/player.gd"))
46 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd")) 46 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/saver.gd"))
47 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleport.gd"))
47 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd")) 48 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/teleportListener.gd"))
48 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd")) 49 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/visibilityListener.gd"))
50 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldport.gd"))
49 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd")) 51 installScriptExtension(ResourceLoader.load("user://maps/Archipelago/worldportListener.gd"))
50 52
51 var proto_script = load("user://maps/Archipelago/generated/proto.gd") 53 var proto_script = load("user://maps/Archipelago/generated/proto.gd")
@@ -67,6 +69,12 @@ func _ready():
67 textclient_instance.name = "Textclient" 69 textclient_instance.name = "Textclient"
68 global.add_child(textclient_instance) 70 global.add_child(textclient_instance)
69 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
70 var ap = global.get_node("Archipelago") 78 var ap = global.get_node("Archipelago")
71 var gamedata = global.get_node("Gamedata") 79 var gamedata = global.get_node("Gamedata")
72 ap.connect("ap_connected", connectionSuccessful) 80 ap.connect("ap_connected", connectionSuccessful)
@@ -99,6 +107,10 @@ func _ready():
99 $Panel/player_box.add_theme_font_size_override("font_size", 36) 107 $Panel/player_box.add_theme_font_size_override("font_size", 36)
100 $Panel/password_box.add_theme_font_size_override("font_size", 36) 108 $Panel/password_box.add_theme_font_size_override("font_size", 36)
101 109
110 # Set up version mismatch dialog.
111 $Panel/VersionMismatch.connect("confirmed", startGame)
112 $Panel/VersionMismatch.get_cancel_button().pressed.connect(versionMismatchDeclined)
113
102 114
103# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd 115# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd
104func installScriptExtension(childScript: Resource): 116func installScriptExtension(childScript: Resource):
@@ -128,6 +140,33 @@ func connectionStatus(message):
128 140
129func connectionSuccessful(): 141func connectionSuccessful():
130 var ap = global.get_node("Archipelago") 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")
131 170
132 # Save connection details 171 # Save connection details
133 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] 172 var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass]
@@ -166,6 +205,8 @@ func connectionSuccessful():
166 clearResourceCache("res://objects/nodes/panel.tscn") 205 clearResourceCache("res://objects/nodes/panel.tscn")
167 clearResourceCache("res://objects/nodes/player.tscn") 206 clearResourceCache("res://objects/nodes/player.tscn")
168 clearResourceCache("res://objects/nodes/saver.tscn") 207 clearResourceCache("res://objects/nodes/saver.tscn")
208 clearResourceCache("res://objects/nodes/teleport.tscn")
209 clearResourceCache("res://objects/nodes/worldport.tscn")
169 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") 210 clearResourceCache("res://objects/scenes/menus/pause_menu.tscn")
170 211
171 var paintings_dir = DirAccess.open("res://objects/meshes/paintings") 212 var paintings_dir = DirAccess.open("res://objects/meshes/paintings")
@@ -190,6 +231,13 @@ func connectionUnsuccessful(error_message):
190 popup.get_ok_button().visible = true 231 popup.get_ok_button().visible = true
191 popup.popup_centered() 232 popup.popup_centered()
192 233
234 $Panel/connect_button.disabled = false
235
236
237func versionMismatchDeclined():
238 $Panel/AcceptDialog.hide()
239 $Panel/connect_button.disabled = false
240
193 241
194func historySelected(index): 242func historySelected(index):
195 var ap = global.get_node("Archipelago") 243 var ap = global.get_node("Archipelago")
diff --git a/client/Archipelago/teleport.gd b/client/Archipelago/teleport.gd new file mode 100644 index 0000000..428d50b --- /dev/null +++ b/client/Archipelago/teleport.gd
@@ -0,0 +1,38 @@
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 index 4a7deec..6f363af 100644 --- a/client/Archipelago/teleportListener.gd +++ b/client/Archipelago/teleportListener.gd
@@ -9,6 +9,17 @@ func _ready():
9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names() 9 get_tree().get_root().get_node("scene").get_path_to(self).get_concatenated_names()
10 ) 10 )
11 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
12 var gamedata = global.get_node("Gamedata") 23 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path) 24 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null: 25 if door_id != null:
diff --git a/client/Archipelago/worldport.gd b/client/Archipelago/worldport.gd new file mode 100644 index 0000000..d0fb6c9 --- /dev/null +++ b/client/Archipelago/worldport.gd
@@ -0,0 +1,10 @@
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 index c31c825..5c2faff 100644 --- a/client/Archipelago/worldportListener.gd +++ b/client/Archipelago/worldportListener.gd
@@ -1,8 +1,8 @@
1extends "res://scripts/nodes/listeners/worldportListener.gd" 1extends "res://scripts/nodes/listeners/worldportListener.gd"
2 2
3 3
4func changeScene(): 4func handleTriggered():
5 if exit == "menus/credits": 5 if exit == "menus/credits":
6 return 6 return
7 7
8 super.changeScene() 8 super.handleTriggered()