about summary refs log tree commit diff stats
path: root/apworld/client
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/client')
-rw-r--r--apworld/client/allowNumbers.gd10
-rw-r--r--apworld/client/apworld_runtime.gd5
-rw-r--r--apworld/client/assets/goal.pngbin0 -> 215 bytes
-rw-r--r--apworld/client/client.gd69
-rw-r--r--apworld/client/door.gd31
-rw-r--r--apworld/client/effects.gd32
-rw-r--r--apworld/client/gamedata.gd35
-rw-r--r--apworld/client/keyHolderResetterListener.gd2
-rw-r--r--apworld/client/keyboard.gd3
-rw-r--r--apworld/client/main.gd37
-rw-r--r--apworld/client/manager.gd167
-rw-r--r--apworld/client/maps/control_center.gd85
-rw-r--r--apworld/client/maps/daedalus.gd85
-rw-r--r--apworld/client/maps/icarus.gd38
-rw-r--r--apworld/client/maps/the_advanced.gd36
-rw-r--r--apworld/client/maps/the_charismatic.gd26
-rw-r--r--apworld/client/maps/the_crystalline.gd34
-rw-r--r--apworld/client/maps/the_entry.gd156
-rw-r--r--apworld/client/maps/the_fuzzy.gd25
-rw-r--r--apworld/client/maps/the_parthenon.gd51
-rw-r--r--apworld/client/maps/the_plaza.gd4
-rw-r--r--apworld/client/maps/the_stellar.gd30
-rw-r--r--apworld/client/maps/the_sun_temple.gd56
-rw-r--r--apworld/client/maps/the_unkempt.gd4
-rw-r--r--apworld/client/maps/the_unyielding.gd5
-rw-r--r--apworld/client/minimap.gd13
-rw-r--r--apworld/client/paintingAuto.gd43
-rw-r--r--apworld/client/player.gd209
-rw-r--r--apworld/client/settings_screen.gd10
-rw-r--r--apworld/client/source_runtime.gd4
-rw-r--r--apworld/client/textclient.gd265
-rw-r--r--apworld/client/unlockReaderListener.gd46
-rw-r--r--apworld/client/worldportListener.gd2
33 files changed, 1353 insertions, 265 deletions
diff --git a/apworld/client/allowNumbers.gd b/apworld/client/allowNumbers.gd new file mode 100644 index 0000000..d958b50 --- /dev/null +++ b/apworld/client/allowNumbers.gd
@@ -0,0 +1,10 @@
1extends "res://scripts/nodes/allowNumbers.gd"
2
3
4func _readier():
5 var ap = global.get_node("Archipelago")
6 var gamedata = global.get_node("Gamedata")
7
8 var item_id = gamedata.objects.get_special_ids()["Numbers"]
9 if ap.client.getItemAmount(item_id) >= 1:
10 global.allow_numbers = true
diff --git a/apworld/client/apworld_runtime.gd b/apworld/client/apworld_runtime.gd index faf8e0c..03568bf 100644 --- a/apworld/client/apworld_runtime.gd +++ b/apworld/client/apworld_runtime.gd
@@ -15,6 +15,11 @@ func _get_true_path(path):
15 return "lingo2/client/%s" % path 15 return "lingo2/client/%s" % path
16 16
17 17
18func path_exists(path):
19 var true_path = _get_true_path(path)
20 return apworld_reader.file_exists(true_path)
21
22
18func load_script(path): 23func load_script(path):
19 var true_path = _get_true_path(path) 24 var true_path = _get_true_path(path)
20 25
diff --git a/apworld/client/assets/goal.png b/apworld/client/assets/goal.png new file mode 100644 index 0000000..bd1650d --- /dev/null +++ b/apworld/client/assets/goal.png
Binary files differ
diff --git a/apworld/client/client.gd b/apworld/client/client.gd index a23e85a..c149482 100644 --- a/apworld/client/client.gd +++ b/apworld/client/client.gd
@@ -24,6 +24,9 @@ var _received_items = {}
24var _slot_data = {} 24var _slot_data = {}
25var _accessible_locations = [] 25var _accessible_locations = []
26var _accessible_worldports = [] 26var _accessible_worldports = []
27var _goal_accessible = false
28var _latched_doors = []
29var _hinted_locations = []
27 30
28signal could_not_connect 31signal could_not_connect
29signal connect_status 32signal connect_status
@@ -33,10 +36,13 @@ signal location_scout_received(location_id, item_name, player_name, flags, for_s
33signal text_message_received(message) 36signal text_message_received(message)
34signal item_sent_notification(message) 37signal item_sent_notification(message)
35signal hint_received(message) 38signal hint_received(message)
39signal door_latched(id)
36signal accessible_locations_updated 40signal accessible_locations_updated
37signal checked_locations_updated 41signal checked_locations_updated
42signal ignored_locations_updated(locations)
38signal checked_worldports_updated 43signal checked_worldports_updated
39signal keyboard_update_received 44signal keyboard_update_received
45signal hinted_locations_updated
40 46
41 47
42func _init(): 48func _init():
@@ -61,6 +67,7 @@ func _reset_state():
61 _checked_worldports = [] 67 _checked_worldports = []
62 _accessible_locations = [] 68 _accessible_locations = []
63 _accessible_worldports = [] 69 _accessible_worldports = []
70 _goal_accessible = false
64 71
65 72
66func disconnect_from_ap(): 73func disconnect_from_ap():
@@ -106,7 +113,7 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
106 113
107 _checked_locations = [] 114 _checked_locations = []
108 for location in message["checked_locations"]: 115 for location in message["checked_locations"]:
109 _checked_locations.append(int(message["checked_locations"])) 116 _checked_locations.append(int(location))
110 117
111 client_connected.emit(_slot_data) 118 client_connected.emit(_slot_data)
112 119
@@ -156,11 +163,7 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
156 elif cmd == "LocationInfo": 163 elif cmd == "LocationInfo":
157 for loc in message["locations"]: 164 for loc in message["locations"]:
158 location_scout_received.emit( 165 location_scout_received.emit(
159 int(loc["id"]), 166 int(loc["id"]), loc["item"], loc["player"], int(loc["flags"]), int(loc["self"])
160 loc["item"],
161 loc["player"],
162 int(loc["flags"]),
163 int(loc["for_self"])
164 ) 167 )
165 168
166 elif cmd == "AccessibleLocations": 169 elif cmd == "AccessibleLocations":
@@ -174,6 +177,8 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
174 for port_id in message["worldports"]: 177 for port_id in message["worldports"]:
175 _accessible_worldports.append(int(port_id)) 178 _accessible_worldports.append(int(port_id))
176 179
180 _goal_accessible = bool(message.get("goal", false))
181
177 accessible_locations_updated.emit() 182 accessible_locations_updated.emit()
178 183
179 elif cmd == "UpdateKeyboard": 184 elif cmd == "UpdateKeyboard":
@@ -183,6 +188,35 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
183 188
184 keyboard_update_received.emit(updates) 189 keyboard_update_received.emit(updates)
185 190
191 elif cmd == "PathReply":
192 var textclient = global.get_node("Textclient")
193 textclient.display_logical_path(
194 message["type"], int(message.get("id", null)), message["path"]
195 )
196
197 elif cmd == "UpdateLatches":
198 for id in message["latches"]:
199 var iid = int(id)
200 if not _latched_doors.has(iid):
201 _latched_doors.append(iid)
202
203 door_latched.emit(iid)
204
205 elif cmd == "SetIgnoredLocations":
206 var locs = []
207 for id in message["locations"]:
208 locs.append(int(id))
209
210 ignored_locations_updated.emit(locs)
211
212 elif cmd == "UpdateHintedLocations":
213 for id in message["locations"]:
214 var iid = int(id)
215 if !_hinted_locations.has(iid):
216 _hinted_locations.append(iid)
217
218 hinted_locations_updated.emit()
219
186 220
187func connectToServer(server, un, pw): 221func connectToServer(server, un, pw):
188 sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) 222 sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}])
@@ -249,6 +283,29 @@ func checkWorldport(port_id):
249 sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}]) 283 sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}])
250 284
251 285
286func latchDoor(id):
287 if not _latched_doors.has(id):
288 _latched_doors.append(id)
289
290 sendMessage([{"cmd": "LatchDoor", "door": id}])
291
292
293func getLogicalPath(object_type, object_id):
294 var msg = {"cmd": "GetPath", "type": object_type}
295 if object_id != null:
296 msg["id"] = object_id
297
298 sendMessage([msg])
299
300
301func addIgnoredLocation(loc_id):
302 sendMessage([{"cmd": "IgnoreLocation", "id": loc_id}])
303
304
305func removeIgnoredLocation(loc_id):
306 sendMessage([{"cmd": "UnignoreLocation", "id": loc_id}])
307
308
252func sendQuit(): 309func sendQuit():
253 sendMessage([{"cmd": "Quit"}]) 310 sendMessage([{"cmd": "Quit"}])
254 311
diff --git a/apworld/client/door.gd b/apworld/client/door.gd index 49f5728..63cfa99 100644 --- a/apworld/client/door.gd +++ b/apworld/client/door.gd
@@ -1,7 +1,9 @@
1extends "res://scripts/nodes/door.gd" 1extends "res://scripts/nodes/door.gd"
2 2
3var door_id
3var item_id 4var item_id
4var item_amount 5var item_amount
6var latched = false
5 7
6 8
7func _ready(): 9func _ready():
@@ -10,7 +12,7 @@ func _ready():
10 ) 12 )
11 13
12 var gamedata = global.get_node("Gamedata") 14 var gamedata = global.get_node("Gamedata")
13 var door_id = gamedata.get_door_for_map_node_path(global.map, node_path) 15 door_id = gamedata.get_door_for_map_node_path(global.map, node_path)
14 if door_id != null: 16 if door_id != null:
15 var ap = global.get_node("Archipelago") 17 var ap = global.get_node("Archipelago")
16 var item_lock = ap.get_item_id_for_door(door_id) 18 var item_lock = ap.get_item_id_for_door(door_id)
@@ -27,6 +29,12 @@ func _ready():
27 self.excludeSenders = [] 29 self.excludeSenders = []
28 30
29 call_deferred("_readier") 31 call_deferred("_readier")
32 else:
33 var door_data = gamedata.objects.get_doors()[door_id]
34 if door_data.has_latch() and door_data.get_latch():
35 _check_latched.call_deferred(door_id)
36
37 latched = true
30 38
31 if global.map == "the_sun_temple": 39 if global.map == "the_sun_temple":
32 if name == "spe_EndPlatform" or name == "spe_entry_2": 40 if name == "spe_EndPlatform" or name == "spe_entry_2":
@@ -44,3 +52,24 @@ func _readier():
44 52
45 if ap.client.getItemAmount(item_id) >= item_amount: 53 if ap.client.getItemAmount(item_id) >= item_amount:
46 handleTriggered() 54 handleTriggered()
55
56
57func _check_latched(door_id):
58 var ap = global.get_node("Archipelago")
59
60 if ap.client._latched_doors.has(door_id):
61 triggered = total
62 handleTriggered()
63
64
65func handleTriggered():
66 super.handleTriggered()
67
68 if latched and ran:
69 var ap = global.get_node("Archipelago")
70 ap.client.latchDoor(door_id)
71
72
73func handleUntriggered():
74 if not latched or not ran:
75 super.handleUntriggered()
diff --git a/apworld/client/effects.gd b/apworld/client/effects.gd new file mode 100644 index 0000000..9dc1dd8 --- /dev/null +++ b/apworld/client/effects.gd
@@ -0,0 +1,32 @@
1extends CanvasLayer
2
3var _label
4
5var _disconnected = false
6
7
8func _ready():
9 _label = Label.new()
10 _label.name = "Label"
11 _label.offset_left = 20
12 _label.offset_top = 20
13 _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
14 _label.vertical_alignment = VERTICAL_ALIGNMENT_TOP
15 _label.theme = preload("res://assets/themes/baseUI.tres")
16 _label.add_theme_font_size_override("font_size", 36)
17 add_child(_label)
18
19
20func set_connection_lost(arg):
21 _disconnected = arg
22
23 _update_label()
24
25
26func _update_label():
27 var text = []
28
29 if _disconnected:
30 text.append("Disconnected from multiworld.")
31
32 _label.text = "\n".join(text)
diff --git a/apworld/client/gamedata.gd b/apworld/client/gamedata.gd index 1424721..d7e3136 100644 --- a/apworld/client/gamedata.gd +++ b/apworld/client/gamedata.gd
@@ -14,6 +14,8 @@ var letter_id_by_ap_id = {}
14var symbol_item_ids = [] 14var symbol_item_ids = []
15var anti_trap_ids = {} 15var anti_trap_ids = {}
16var location_name_by_id = {} 16var location_name_by_id = {}
17var ending_display_name_by_name = {}
18var port_id_by_ap_id = {}
17 19
18var kSYMBOL_ITEMS 20var kSYMBOL_ITEMS
19 21
@@ -71,7 +73,13 @@ func load(data_bytes):
71 73
72 if door.has_ap_id(): 74 if door.has_ap_id():
73 door_id_by_ap_id[door.get_ap_id()] = door.get_id() 75 door_id_by_ap_id[door.get_ap_id()] = door.get_id()
74 location_name_by_id[door.get_ap_id()] = _get_door_location_name(door) 76
77 if (
78 door.get_type() == SCRIPT_proto.DoorType.STANDARD
79 or door.get_type() == SCRIPT_proto.DoorType.LOCATION_ONLY
80 or door.get_type() == SCRIPT_proto.DoorType.GRAVESTONE
81 ):
82 location_name_by_id[door.get_ap_id()] = _get_door_location_name(door)
75 83
76 for painting in objects.get_paintings(): 84 for painting in objects.get_paintings():
77 var room = objects.get_rooms()[painting.get_room_id()] 85 var room = objects.get_rooms()[painting.get_room_id()]
@@ -92,6 +100,9 @@ func load(data_bytes):
92 var map_data = port_id_by_map_node_path[map.get_name()] 100 var map_data = port_id_by_map_node_path[map.get_name()]
93 map_data[port.get_path()] = port.get_id() 101 map_data[port.get_path()] = port.get_id()
94 102
103 if port.has_ap_id():
104 port_id_by_ap_id[port.get_ap_id()] = port.get_id()
105
95 for progressive in objects.get_progressives(): 106 for progressive in objects.get_progressives():
96 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id() 107 progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id()
97 108
@@ -103,7 +114,9 @@ func load(data_bytes):
103 location_name_by_id[mastery.get_ap_id()] = _get_mastery_location_name(mastery) 114 location_name_by_id[mastery.get_ap_id()] = _get_mastery_location_name(mastery)
104 115
105 for ending in objects.get_endings(): 116 for ending in objects.get_endings():
106 location_name_by_id[ending.get_ap_id()] = _get_ending_location_name(ending) 117 var location_name = _get_ending_location_name(ending)
118 location_name_by_id[ending.get_ap_id()] = location_name
119 ending_display_name_by_name[ending.get_name()] = location_name
107 120
108 for keyholder in objects.get_keyholders(): 121 for keyholder in objects.get_keyholders():
109 if keyholder.has_key(): 122 if keyholder.has_key():
@@ -163,8 +176,7 @@ func get_door_ap_id(door_id):
163 176
164func get_door_map_name(door_id): 177func get_door_map_name(door_id):
165 var door = objects.get_doors()[door_id] 178 var door = objects.get_doors()[door_id]
166 var room = objects.get_rooms()[door.get_room_id()] 179 var map = objects.get_maps()[door.get_map_id()]
167 var map = objects.get_maps()[room.get_map_id()]
168 return map.get_name() 180 return map.get_name()
169 181
170 182
@@ -213,7 +225,11 @@ func _get_generated_door_location_name(door):
213 if door.get_type() != SCRIPT_proto.DoorType.STANDARD: 225 if door.get_type() != SCRIPT_proto.DoorType.STANDARD:
214 return null 226 return null
215 227
216 if door.get_keyholders().size() > 0 or door.get_endings().size() > 0 or door.has_complete_at(): 228 if (
229 door.get_keyholders().size() > 0
230 or (door.has_white_ending() and door.get_white_ending())
231 or door.has_complete_at()
232 ):
217 return null 233 return null
218 234
219 if door.get_panels().size() > 4: 235 if door.get_panels().size() > 4:
@@ -224,8 +240,11 @@ func _get_generated_door_location_name(door):
224 var panel = objects.get_panels()[panel_id.get_panel()] 240 var panel = objects.get_panels()[panel_id.get_panel()]
225 var panel_room = objects.get_rooms()[panel.get_room_id()] 241 var panel_room = objects.get_rooms()[panel.get_room_id()]
226 # It's okay if panel_display_name is not present because then it's coalesced with other unnamed areas. 242 # It's okay if panel_display_name is not present because then it's coalesced with other unnamed areas.
227 if not map_areas.has(panel_room.get_panel_display_name()): 243 var panel_display_name = ""
228 map_areas.append(panel_room.get_panel_display_name()) 244 if panel_room.has_panel_display_name():
245 panel_display_name = panel_room.get_panel_display_name()
246 if not map_areas.has(panel_display_name):
247 map_areas.append(panel_display_name)
229 248
230 if map_areas.size() > 1: 249 if map_areas.size() > 1:
231 return null 250 return null
@@ -261,7 +280,7 @@ func _get_generated_door_location_name(door):
261 280
262 281
263func _get_letter_location_name(letter): 282func _get_letter_location_name(letter):
264 var letter_level = 2 if letter.get_level2() else 1 283 var letter_level = 2 if (letter.has_level2() and letter.get_level2()) else 1
265 var letter_name = "%s%d" % [letter.get_key().to_upper(), letter_level] 284 var letter_name = "%s%d" % [letter.get_key().to_upper(), letter_level]
266 return "%s - %s" % [_get_room_object_map_name(letter), letter_name] 285 return "%s - %s" % [_get_room_object_map_name(letter), letter_name]
267 286
diff --git a/apworld/client/keyHolderResetterListener.gd b/apworld/client/keyHolderResetterListener.gd index d5300f3..9ab45f9 100644 --- a/apworld/client/keyHolderResetterListener.gd +++ b/apworld/client/keyHolderResetterListener.gd
@@ -6,3 +6,5 @@ func reset():
6 var was_removed = ap.keyboard.reset_keyholders() 6 var was_removed = ap.keyboard.reset_keyholders()
7 if was_removed: 7 if was_removed:
8 sfxPlayer.sfx_play("pickup") 8 sfxPlayer.sfx_play("pickup")
9
10 ap.client.requestSync()
diff --git a/apworld/client/keyboard.gd b/apworld/client/keyboard.gd index a59c4d0..9026c06 100644 --- a/apworld/client/keyboard.gd +++ b/apworld/client/keyboard.gd
@@ -191,9 +191,6 @@ func load_keyholders(map):
191 191
192 192
193func reset_keyholders(): 193func reset_keyholders():
194 if letters_in_keyholders.is_empty() and letters_blocked.is_empty():
195 return false
196
197 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty() 194 var cleared_anything = not letters_in_keyholders.is_empty() or not letters_blocked.is_empty()
198 195
199 if keyholder_state.has(global.map): 196 if keyholder_state.has(global.map):
diff --git a/apworld/client/main.gd b/apworld/client/main.gd index 8425d8c..c90d6e7 100644 --- a/apworld/client/main.gd +++ b/apworld/client/main.gd
@@ -36,6 +36,7 @@ func _ready():
36 global.add_child(ap_instance) 36 global.add_child(ap_instance)
37 37
38 # Let's also inject any scripts we need to inject now. 38 # Let's also inject any scripts we need to inject now.
39 installScriptExtension(runtime.load_script("allowNumbers.gd"))
39 installScriptExtension(runtime.load_script("animationListener.gd")) 40 installScriptExtension(runtime.load_script("animationListener.gd"))
40 installScriptExtension(runtime.load_script("collectable.gd")) 41 installScriptExtension(runtime.load_script("collectable.gd"))
41 installScriptExtension(runtime.load_script("door.gd")) 42 installScriptExtension(runtime.load_script("door.gd"))
@@ -43,12 +44,14 @@ func _ready():
43 installScriptExtension(runtime.load_script("keyHolderChecker.gd")) 44 installScriptExtension(runtime.load_script("keyHolderChecker.gd"))
44 installScriptExtension(runtime.load_script("keyHolderResetterListener.gd")) 45 installScriptExtension(runtime.load_script("keyHolderResetterListener.gd"))
45 installScriptExtension(runtime.load_script("painting.gd")) 46 installScriptExtension(runtime.load_script("painting.gd"))
47 installScriptExtension(runtime.load_script("paintingAuto.gd"))
46 installScriptExtension(runtime.load_script("panel.gd")) 48 installScriptExtension(runtime.load_script("panel.gd"))
47 installScriptExtension(runtime.load_script("pauseMenu.gd")) 49 installScriptExtension(runtime.load_script("pauseMenu.gd"))
48 installScriptExtension(runtime.load_script("player.gd")) 50 installScriptExtension(runtime.load_script("player.gd"))
49 installScriptExtension(runtime.load_script("saver.gd")) 51 installScriptExtension(runtime.load_script("saver.gd"))
50 installScriptExtension(runtime.load_script("teleport.gd")) 52 installScriptExtension(runtime.load_script("teleport.gd"))
51 installScriptExtension(runtime.load_script("teleportListener.gd")) 53 installScriptExtension(runtime.load_script("teleportListener.gd"))
54 installScriptExtension(runtime.load_script("unlockReaderListener.gd"))
52 installScriptExtension(runtime.load_script("visibilityListener.gd")) 55 installScriptExtension(runtime.load_script("visibilityListener.gd"))
53 installScriptExtension(runtime.load_script("worldport.gd")) 56 installScriptExtension(runtime.load_script("worldport.gd"))
54 installScriptExtension(runtime.load_script("worldportListener.gd")) 57 installScriptExtension(runtime.load_script("worldportListener.gd"))
@@ -66,6 +69,11 @@ func _ready():
66 messages_instance.SCRIPT_rainbowText = runtime.load_script("rainbowText.gd") 69 messages_instance.SCRIPT_rainbowText = runtime.load_script("rainbowText.gd")
67 global.add_child(messages_instance) 70 global.add_child(messages_instance)
68 71
72 var effects_script = runtime.load_script("effects.gd")
73 var effects_instance = effects_script.new()
74 effects_instance.name = "Effects"
75 global.add_child(effects_instance)
76
69 var textclient_script = runtime.load_script("textclient.gd") 77 var textclient_script = runtime.load_script("textclient.gd")
70 var textclient_instance = textclient_script.new() 78 var textclient_instance = textclient_script.new()
71 textclient_instance.name = "Textclient" 79 textclient_instance.name = "Textclient"
@@ -77,6 +85,13 @@ func _ready():
77 compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") 85 compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd")
78 global.add_child(compass_overlay_instance) 86 global.add_child(compass_overlay_instance)
79 87
88 unlocks.data["advanced_mastery"] = ""
89 unlocks.data["charismatic_mastery"] = ""
90 unlocks.data["crystalline_mastery"] = ""
91 unlocks.data["fuzzy_mastery"] = ""
92 unlocks.data["icarus_mastery"] = ""
93 unlocks.data["stellar_mastery"] = ""
94
80 var ap = global.get_node("Archipelago") 95 var ap = global.get_node("Archipelago")
81 var gamedata = global.get_node("Gamedata") 96 var gamedata = global.get_node("Gamedata")
82 ap.ap_connected.connect(connectionSuccessful) 97 ap.ap_connected.connect(connectionSuccessful)
@@ -102,8 +117,9 @@ func _ready():
102 history_box.get_popup().id_pressed.connect(historySelected) 117 history_box.get_popup().id_pressed.connect(historySelected)
103 118
104 # Show client version. 119 # Show client version.
120 var version = gamedata.objects.get_version()
105 get_node("../Panel/title").text = ( 121 get_node("../Panel/title").text = (
106 "ARCHIPELAGO (%d.%d)" % [gamedata.objects.get_version(), ap.MOD_VERSION] 122 "ARCHIPELAGO (%d.%d.%d)" % [version.get_major(), version.get_minor(), version.get_patch()]
107 ) 123 )
108 124
109 # Increase font size in text boxes. 125 # Increase font size in text boxes.
@@ -173,18 +189,20 @@ func connectionSuccessful():
173 var gamedata = global.get_node("Gamedata") 189 var gamedata = global.get_node("Gamedata")
174 190
175 # Check for major version mismatch. 191 # Check for major version mismatch.
176 if ap.apworld_version[0] != gamedata.objects.get_version(): 192 if ap.apworld_version[0] != gamedata.objects.get_version().get_major():
177 get_node("../Panel/AcceptDialog").exclusive = false 193 get_node("../Panel/AcceptDialog").exclusive = false
178 194
179 var popup = get_node("../Panel/VersionMismatch") 195 var popup = get_node("../Panel/VersionMismatch")
180 popup.title = "Version Mismatch!" 196 popup.title = "Version Mismatch!"
181 popup.dialog_text = ( 197 popup.dialog_text = (
182 "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." 198 "This slot was generated using v%d.%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%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."
183 % [ 199 % [
184 ap.apworld_version[0], 200 ap.apworld_version[0],
185 ap.apworld_version[1], 201 ap.apworld_version[1],
186 gamedata.objects.get_version(), 202 ap.apworld_version[2],
187 ap.MOD_VERSION 203 gamedata.objects.get_version().get_major(),
204 gamedata.objects.get_version().get_minor(),
205 gamedata.objects.get_version().get_patch()
188 ] 206 ]
189 ) 207 )
190 popup.exclusive = true 208 popup.exclusive = true
@@ -215,11 +233,11 @@ func startGame():
215 233
216 unlocks.resetCollectables() 234 unlocks.resetCollectables()
217 unlocks.resetData() 235 unlocks.resetData()
236 unlocks.loadCollectables()
237 unlocks.loadData()
218 238
219 ap.setup_keys() 239 ap.setup_keys()
220 240
221 unlocks.loadCollectables()
222 unlocks.loadData()
223 unlocks.unlockKey("capslock", 1) 241 unlocks.unlockKey("capslock", 1)
224 242
225 if ap.shuffle_worldports: 243 if ap.shuffle_worldports:
@@ -228,6 +246,7 @@ func startGame():
228 settings.worldport_fades = "never" 246 settings.worldport_fades = "never"
229 247
230 clearResourceCache("res://objects/meshes/gridDoor.tscn") 248 clearResourceCache("res://objects/meshes/gridDoor.tscn")
249 clearResourceCache("res://objects/nodes/allowNumbers.tscn")
231 clearResourceCache("res://objects/nodes/collectable.tscn") 250 clearResourceCache("res://objects/nodes/collectable.tscn")
232 clearResourceCache("res://objects/nodes/door.tscn") 251 clearResourceCache("res://objects/nodes/door.tscn")
233 clearResourceCache("res://objects/nodes/keyHolder.tscn") 252 clearResourceCache("res://objects/nodes/keyHolder.tscn")
@@ -235,6 +254,7 @@ func startGame():
235 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn") 254 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
236 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn") 255 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
237 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn") 256 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
257 clearResourceCache("res://objects/nodes/listeners/unlockReaderListener.tscn")
238 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn") 258 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn")
239 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn") 259 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
240 clearResourceCache("res://objects/nodes/panel.tscn") 260 clearResourceCache("res://objects/nodes/panel.tscn")
@@ -271,6 +291,9 @@ func versionMismatchDeclined():
271 get_node("../Panel/AcceptDialog").hide() 291 get_node("../Panel/AcceptDialog").hide()
272 get_node("../Panel/connect_button").disabled = false 292 get_node("../Panel/connect_button").disabled = false
273 293
294 var ap = global.get_node("Archipelago")
295 ap.disconnect_from_ap()
296
274 297
275func historySelected(index): 298func historySelected(index):
276 var ap = global.get_node("Archipelago") 299 var ap = global.get_node("Archipelago")
diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 5b731d2..8c981f9 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd
@@ -1,7 +1,5 @@
1extends Node 1extends Node
2 2
3const MOD_VERSION = 7
4
5var SCRIPT_client 3var SCRIPT_client
6var SCRIPT_keyboard 4var SCRIPT_keyboard
7var SCRIPT_locationListener 5var SCRIPT_locationListener
@@ -30,6 +28,9 @@ var _item_locks = {}
30var _inverse_item_locks = {} 28var _inverse_item_locks = {}
31var _held_letters = {} 29var _held_letters = {}
32var _letters_setup = false 30var _letters_setup = false
31var _already_connected = false
32var _ignored_locations = []
33var _map_scripts = {}
33 34
34const kSHUFFLE_LETTERS_VANILLA = 0 35const kSHUFFLE_LETTERS_VANILLA = 0
35const kSHUFFLE_LETTERS_UNLOCKED = 1 36const kSHUFFLE_LETTERS_UNLOCKED = 1
@@ -45,10 +46,30 @@ const kCYAN_DOOR_BEHAVIOR_H2 = 0
45const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1 46const kCYAN_DOOR_BEHAVIOR_DOUBLE_LETTER = 1
46const kCYAN_DOOR_BEHAVIOR_ITEM = 2 47const kCYAN_DOOR_BEHAVIOR_ITEM = 2
47 48
48var apworld_version = [0, 0] 49const kEndingNameByVictoryValue = {
50 0: "GRAY",
51 1: "PURPLE",
52 2: "MINT",
53 3: "BLACK",
54 4: "BLUE",
55 5: "CYAN",
56 6: "RED",
57 7: "PLUM",
58 8: "ORANGE",
59 9: "GOLD",
60 10: "YELLOW",
61 11: "GREEN",
62 12: "WHITE",
63}
64
65var apworld_version = [0, 0, 0]
49var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 66var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
50var daedalus_roof_access = false 67var daedalus_roof_access = false
68var enable_gift_maps = []
69var enable_icarus = false
70var endings_requirement = 0
51var keyholder_sanity = false 71var keyholder_sanity = false
72var masteries_requirement = 0
52var port_pairings = {} 73var port_pairings = {}
53var shuffle_control_center_colors = false 74var shuffle_control_center_colors = false
54var shuffle_doors = false 75var shuffle_doors = false
@@ -60,6 +81,8 @@ var strict_cyan_ending = false
60var strict_purple_ending = false 81var strict_purple_ending = false
61var victory_condition = -1 82var victory_condition = -1
62 83
84var color_by_material_path = {}
85
63signal could_not_connect 86signal could_not_connect
64signal connect_status 87signal connect_status
65signal ap_connected 88signal ap_connected
@@ -97,6 +120,20 @@ func _init():
97 if data.size() > 6: 120 if data.size() > 6:
98 show_minimap = data[6] 121 show_minimap = data[6]
99 122
123 # We need to create a mapping from material paths to the original colors of
124 # those materials. We force reload the materials, overwriting any custom
125 # textures, and create the mapping. We then reload the textures in case the
126 # player had a custom one enabled.
127 var directory = DirAccess.open("res://assets/materials")
128 for material_name in directory.get_files():
129 var material = ResourceLoader.load(
130 "res://assets/materials/" + material_name, "", ResourceLoader.CACHE_MODE_REPLACE
131 )
132
133 color_by_material_path[material.resource_path] = Color(material.albedo_color)
134
135 settings.load_user_textures()
136
100 137
101func _ready(): 138func _ready():
102 client = SCRIPT_client.new() 139 client = SCRIPT_client.new()
@@ -109,7 +146,10 @@ func _ready():
109 client.hint_received.connect(_process_hint_received) 146 client.hint_received.connect(_process_hint_received)
110 client.accessible_locations_updated.connect(_on_accessible_locations_updated) 147 client.accessible_locations_updated.connect(_on_accessible_locations_updated)
111 client.checked_locations_updated.connect(_on_checked_locations_updated) 148 client.checked_locations_updated.connect(_on_checked_locations_updated)
149 client.ignored_locations_updated.connect(_on_ignored_locations_updated)
150 client.hinted_locations_updated.connect(_on_hinted_locations_updated)
112 client.checked_worldports_updated.connect(_on_checked_worldports_updated) 151 client.checked_worldports_updated.connect(_on_checked_worldports_updated)
152 client.door_latched.connect(_on_door_latched)
113 153
114 client.could_not_connect.connect(_client_could_not_connect) 154 client.could_not_connect.connect(_client_could_not_connect)
115 client.connect_status.connect(_client_connect_status) 155 client.connect_status.connect(_client_connect_status)
@@ -164,6 +204,7 @@ func connectToServer():
164 _location_scouts = {} 204 _location_scouts = {}
165 _letters_setup = false 205 _letters_setup = false
166 _held_letters = {} 206 _held_letters = {}
207 _already_connected = false
167 208
168 client.connectToServer(ap_server, ap_user, ap_pass) 209 client.connectToServer(ap_server, ap_user, ap_pass)
169 210
@@ -173,6 +214,11 @@ func getSaveFileName():
173 214
174 215
175func disconnect_from_ap(): 216func disconnect_from_ap():
217 _already_connected = false
218
219 var effects = global.get_node("Effects")
220 effects.set_connection_lost(false)
221
176 client.disconnect_from_ap() 222 client.disconnect_from_ap()
177 223
178 224
@@ -216,6 +262,12 @@ func _process_item(item, amount):
216 if player != null: 262 if player != null:
217 player.evaluate_solvability.emit() 263 player.evaluate_solvability.emit()
218 264
265 if item_id == gamedata.objects.get_special_ids()["A Job Well Done"]:
266 update_job_well_done_sign()
267
268 if item_id == gamedata.objects.get_special_ids()["Numbers"] and global.map == "the_fuzzy":
269 global.allow_numbers = true
270
219 # Show a message about the item if it's new. 271 # Show a message about the item if it's new.
220 if int(item["index"]) > _last_new_item: 272 if int(item["index"]) > _last_new_item:
221 _last_new_item = int(item["index"]) 273 _last_new_item = int(item["index"])
@@ -326,7 +378,7 @@ func _on_accessible_locations_updated():
326func _on_checked_locations_updated(): 378func _on_checked_locations_updated():
327 var textclient_node = global.get_node("Textclient") 379 var textclient_node = global.get_node("Textclient")
328 if textclient_node != null: 380 if textclient_node != null:
329 textclient_node.update_locations() 381 textclient_node.update_locations(false)
330 382
331 383
332func _on_checked_worldports_updated(): 384func _on_checked_worldports_updated():
@@ -336,15 +388,60 @@ func _on_checked_worldports_updated():
336 textclient_node.update_worldports() 388 textclient_node.update_worldports()
337 389
338 390
391func _on_ignored_locations_updated(locations):
392 _ignored_locations = locations
393
394 var textclient_node = global.get_node("Textclient")
395 if textclient_node != null:
396 textclient_node.update_locations()
397
398
399func _on_hinted_locations_updated():
400 var textclient_node = global.get_node("Textclient")
401 if textclient_node != null:
402 textclient_node.update_locations()
403
404
405func _on_door_latched(door_id):
406 var gamedata = global.get_node("Gamedata")
407 if gamedata.get_door_map_name(door_id) != global.map:
408 return
409
410 var receivers = gamedata.get_door_receivers(door_id)
411 var scene = get_tree().get_root().get_node_or_null("scene")
412 if scene != null:
413 for receiver in receivers:
414 var rnode = scene.get_node_or_null(receiver)
415 if rnode != null:
416 rnode.handleTriggered()
417
418
339func _client_could_not_connect(message): 419func _client_could_not_connect(message):
340 could_not_connect.emit(message) 420 could_not_connect.emit(message)
341 421
422 if global.loaded:
423 var effects = global.get_node("Effects")
424 effects.set_connection_lost(true)
425
426 var messages = global.get_node("Messages")
427 messages.showMessage("Connection to multiworld lost.")
428
342 429
343func _client_connect_status(message): 430func _client_connect_status(message):
344 connect_status.emit(message) 431 connect_status.emit(message)
345 432
346 433
347func _client_connected(slot_data): 434func _client_connected(slot_data):
435 var effects = global.get_node("Effects")
436 effects.set_connection_lost(false)
437
438 if _already_connected:
439 var messages = global.get_node("Messages")
440 messages.showMessage("Reconnected to multiworld!")
441 return
442
443 _already_connected = true
444
348 var gamedata = global.get_node("Gamedata") 445 var gamedata = global.get_node("Gamedata")
349 446
350 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot] 447 _localdata_file = "user://archipelago_data/%s_%d" % [client._seed, client._slot]
@@ -367,7 +464,11 @@ func _client_connected(slot_data):
367 # Read slot data. 464 # Read slot data.
368 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) 465 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
369 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) 466 daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false))
467 enable_gift_maps = slot_data.get("enable_gift_maps", [])
468 enable_icarus = bool(slot_data.get("enable_icarus", false))
469 endings_requirement = int(slot_data.get("endings_requirement", 0))
370 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) 470 keyholder_sanity = bool(slot_data.get("keyholder_sanity", false))
471 masteries_requirement = int(slot_data.get("masteries_requirement", 0))
371 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) 472 shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false))
372 shuffle_doors = bool(slot_data.get("shuffle_doors", false)) 473 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
373 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) 474 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
@@ -379,14 +480,19 @@ func _client_connected(slot_data):
379 victory_condition = int(slot_data.get("victory_condition", 0)) 480 victory_condition = int(slot_data.get("victory_condition", 0))
380 481
381 if slot_data.has("version"): 482 if slot_data.has("version"):
382 apworld_version = [int(slot_data["version"][0]), int(slot_data["version"][1])] 483 var version_msg = slot_data["version"]
484 apworld_version = [int(version_msg[0]), int(version_msg[1]), 0]
485 if version_msg.size() > 2:
486 apworld_version[2] = int(version_msg[2])
383 487
384 port_pairings.clear() 488 port_pairings.clear()
385 if slot_data.has("port_pairings"): 489 if slot_data.has("port_pairings"):
386 var raw_pp = slot_data.get("port_pairings") 490 var raw_pp = slot_data.get("port_pairings")
387 491
388 for p1 in raw_pp.keys(): 492 for p1 in raw_pp.keys():
389 port_pairings[int(p1)] = int(raw_pp[p1]) 493 port_pairings[gamedata.port_id_by_ap_id[int(p1)]] = gamedata.port_id_by_ap_id[int(
494 raw_pp[p1]
495 )]
390 496
391 # Set up item locks. 497 # Set up item locks.
392 _item_locks = {} 498 _item_locks = {}
@@ -462,6 +568,9 @@ func start_batching_locations():
462 568
463 569
464func send_location(loc_id): 570func send_location(loc_id):
571 if client._checked_locations.has(loc_id):
572 return
573
465 if _batch_locations: 574 if _batch_locations:
466 _held_locations.append(loc_id) 575 _held_locations.append(loc_id)
467 else: 576 else:
@@ -560,3 +669,49 @@ func _process_key_item(key, level):
560 level += 1 669 level += 1
561 670
562 keyboard.collect_remote_letter(key, level) 671 keyboard.collect_remote_letter(key, level)
672
673
674func update_job_well_done_sign():
675 if global.map != "daedalus":
676 return
677
678 var gamedata = global.get_node("Gamedata")
679 var job_item = gamedata.objects.get_special_ids()["A Job Well Done"]
680 var jobs_done = client.getItemAmount(job_item)
681
682 var sign2 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign2")
683 var sign3 = get_tree().get_root().get_node_or_null("scene/Meshes/Miscellaneous/sign3")
684
685 if sign2 != null and sign3 != null:
686 if jobs_done == 0:
687 sign2.text = "what are you doing"
688 sign3.text = "?"
689 elif jobs_done == 1:
690 sign2.text = "a job well done"
691 sign3.text = "is its own reward"
692 else:
693 sign2.text = "%d jobs well done" % jobs_done
694 sign3.text = "are their own reward"
695
696 sign2.get_node("MeshInstance3D").mesh.text = sign2.text
697 sign3.get_node("MeshInstance3D").mesh.text = sign3.text
698
699
700func toggle_ignored_location(loc_id):
701 if loc_id in _ignored_locations:
702 client.removeIgnoredLocation(loc_id)
703 else:
704 client.addIgnoredLocation(loc_id)
705
706
707func get_map_script(map_name):
708 if !_map_scripts.has(map_name):
709 var runtime = global.get_node("Runtime")
710 var script_path = "maps/%s.gd" % map_name
711 if runtime.path_exists(script_path):
712 var script = runtime.load_script(script_path)
713 _map_scripts[map_name] = script.new()
714 else:
715 _map_scripts[map_name] = null
716
717 return _map_scripts[map_name]
diff --git a/apworld/client/maps/control_center.gd b/apworld/client/maps/control_center.gd new file mode 100644 index 0000000..de9ae4b --- /dev/null +++ b/apworld/client/maps/control_center.gd
@@ -0,0 +1,85 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Remove the door blocking the trophy case.
5 root.get_node("/root/scene/Components/Doors/entry_18").queue_free()
6
7 # Set up mastery listeners for extra maps.
8 _set_up_mastery_listener(root, "advanced")
9 _set_up_mastery_listener(root, "charismatic")
10 _set_up_mastery_listener(root, "crystalline")
11 _set_up_mastery_listener(root, "fuzzy")
12 _set_up_mastery_listener(root, "icarus")
13 _set_up_mastery_listener(root, "stellar")
14
15 if ap.endings_requirement != 12 or ap.masteries_requirement != 0:
16 # Set up listeners for the potential White Ending requirements.
17 var merging_prefab = preload("res://objects/nodes/listeners/mergingListener.tscn")
18
19 var old_door = root.get_node("/root/scene/Components/Doors/entry_19")
20 var new_door = old_door.duplicate()
21 new_door.name = "entry_19_new"
22 new_door.senders.clear()
23 new_door.senderGroup.clear()
24 new_door.excludeSenders.clear()
25
26 if ap.endings_requirement == 12:
27 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
28 elif ap.endings_requirement > 0:
29 if ap.masteries_requirement == 0:
30 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
31 new_door.complete_at = ap.endings_requirement
32 else:
33 var endings_merge = merging_prefab.instantiate()
34 endings_merge.name = "EndingsMerge"
35 endings_merge.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners"))
36 endings_merge.complete_at = ap.endings_requirement
37 root.get_node("/root/scene/Components").add_child.call_deferred(endings_merge)
38 new_door.senders.append(NodePath("/root/scene/Components/EndingsMerge"))
39
40 var max_masteries = 13 + ap.enable_gift_maps.size()
41 if ap.enable_icarus:
42 max_masteries += 1
43
44 if ap.masteries_requirement == max_masteries:
45 new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/MasteryListeners"))
46 new_door.excludeSenders.append(
47 NodePath("/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite")
48 )
49 elif ap.masteries_requirement > 0:
50 if ap.endings_requirement == 0:
51 new_door.senderGroup.append(
52 NodePath("/root/scene/Meshes/Trophies/MasteryListeners")
53 )
54 new_door.excludeSenders.append(
55 NodePath(
56 "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite"
57 )
58 )
59 new_door.complete_at = ap.masteries_requirement
60 else:
61 var masteries_merge = merging_prefab.instantiate()
62 masteries_merge.name = "MasteriesMerge"
63 masteries_merge.senderGroup.append(
64 NodePath("/root/scene/Meshes/Trophies/MasteryListeners")
65 )
66 masteries_merge.excludeSenders.append(
67 NodePath(
68 "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite"
69 )
70 )
71 masteries_merge.complete_at = ap.masteries_requirement
72 root.get_node("/root/scene/Components").add_child.call_deferred(masteries_merge)
73 new_door.senders.append(NodePath("/root/scene/Components/MasteriesMerge"))
74
75 old_door.queue_free()
76 root.get_node("/root/scene/Components/Doors").add_child.call_deferred(new_door)
77
78
79func _set_up_mastery_listener(root, name):
80 var prefab = preload("res://objects/nodes/listeners/unlockReaderListener.tscn")
81 var url = prefab.instantiate()
82 url.name = "unlockReaderListenerMastery_%s" % name
83 url.key = "%s_mastery" % name
84 url.value = "unlocked"
85 root.get_node("/root/scene/Meshes/Trophies/MasteryListeners").add_child.call_deferred(url)
diff --git a/apworld/client/maps/daedalus.gd b/apworld/client/maps/daedalus.gd new file mode 100644 index 0000000..5fcf7a5 --- /dev/null +++ b/apworld/client/maps/daedalus.gd
@@ -0,0 +1,85 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Teleport the direction panels when the stairs are there.
5 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
6
7 var dir1 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_1")
8 var dir1_tpl = tpl_prefab.instantiate()
9 dir1_tpl.target_path = dir1
10 dir1_tpl.teleport_point = Vector3(59.5, 8, -6.5)
11 dir1_tpl.teleport_rotate = Vector3(-45, 0, 0)
12 dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south"))
13 dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north"))
14 dir1_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west"))
15 dir1.add_child.call_deferred(dir1_tpl)
16
17 var dir2 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_2")
18 var dir2_tpl = tpl_prefab.instantiate()
19 dir2_tpl.target_path = dir2
20 dir2_tpl.teleport_point = Vector3(59.5, 8, 6.5)
21 dir2_tpl.teleport_rotate = Vector3(-45, -180, 0)
22 dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south"))
23 dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north"))
24 dir2_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west"))
25 dir2.add_child.call_deferred(dir2_tpl)
26
27 var dir3 = root.get_node("/root/scene/Panels/Castle Entrance/castle_direction_3")
28 var dir3_tpl = tpl_prefab.instantiate()
29 dir3_tpl.target_path = dir3
30 dir3_tpl.teleport_point = Vector3(54, 8, 0)
31 dir3_tpl.teleport_rotate = Vector3(-45, 90, 0)
32 dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_south"))
33 dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_north"))
34 dir3_tpl.senders.append(NodePath("/root/scene/Panels/Castle Entrance/castle_west"))
35 dir3.add_child.call_deferred(dir3_tpl)
36
37 # Block off roof access in Daedalus.
38 if not ap.daedalus_roof_access:
39 _set_up_invis_wall(root, 75.5, 11, -24.5, 1, 10, 49)
40 _set_up_invis_wall(root, 51.5, 11, -17, 16, 10, 1)
41 _set_up_invis_wall(root, 46, 10, -9.5, 1, 10, 10)
42 _set_up_invis_wall(root, 67.5, 11, 17, 16, 10, 1)
43 _set_up_invis_wall(root, 50.5, 11, 14, 10, 10, 1)
44 _set_up_invis_wall(root, 39, 10, 18.5, 1, 10, 22)
45 _set_up_invis_wall(root, 20, 15, 18.5, 1, 10, 16)
46 _set_up_invis_wall(root, 11.5, 15, 3, 32, 10, 1)
47 _set_up_invis_wall(root, 11.5, 16, -20, 14, 20, 1)
48 _set_up_invis_wall(root, 14, 16, -26.5, 1, 20, 4)
49 _set_up_invis_wall(root, 28.5, 20.5, -26.5, 1, 15, 25)
50 _set_up_invis_wall(root, 40.5, 20.5, -11, 30, 15, 1)
51 _set_up_invis_wall(root, 50.5, 15, 5.5, 7, 10, 1)
52 _set_up_invis_wall(root, 83.5, 33.5, 5.5, 1, 7, 11)
53 _set_up_invis_wall(root, 83.5, 33.5, -5.5, 1, 7, 11)
54
55 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
56 var warp_exit = warp_exit_prefab.instantiate()
57 warp_exit.name = "roof_access_blocker_warp_exit"
58 warp_exit.position = Vector3(58, 10, 0)
59 warp_exit.rotation_degrees.y = 90
60 root.get_node("/root/scene").add_child.call_deferred(warp_exit)
61
62 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
63 var warp_enter = warp_enter_prefab.instantiate()
64 warp_enter.target = warp_exit
65 warp_enter.position = Vector3(76.5, 30, 1)
66 warp_enter.scale = Vector3(4, 1.5, 1)
67 warp_enter.rotation_degrees.y = 90
68 root.get_node("/root/scene").add_child.call_deferred(warp_enter)
69
70
71func _set_up_invis_wall(root, x, y, z, sx, sy, sz):
72 var prefab = preload("res://objects/nodes/block.tscn")
73 var newwall = prefab.instantiate()
74 newwall.position.x = x
75 newwall.position.y = y
76 newwall.position.z = z
77 newwall.scale.x = sz
78 newwall.scale.y = sy
79 newwall.scale.z = sx
80 newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
81 newwall.visibility_range_end = 3
82 newwall.visibility_range_end_margin = 1
83 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
84 newwall.skeleton = ".."
85 root.get_node("/root/scene").add_child.call_deferred(newwall)
diff --git a/apworld/client/maps/icarus.gd b/apworld/client/maps/icarus.gd new file mode 100644 index 0000000..ad00741 --- /dev/null +++ b/apworld/client/maps/icarus.gd
@@ -0,0 +1,38 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Add the mastery to Icarus.
5 if ap.enable_icarus:
6 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
7 var saver_prefab = preload("res://objects/nodes/saver.tscn")
8 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
9 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
10
11 var mastery = collectable_prefab.instantiate()
12 mastery.name = "collectable"
13 mastery.position = Vector3(0, -2000, 0)
14 mastery.unlock_type = "smiley"
15 mastery.material_override = load("res://assets/materials/gold.material")
16 root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery)
17
18 var tpl = tpl_prefab.instantiate()
19 tpl.teleport_point = Vector3(56.25, 0, -5.5)
20 tpl.teleport_rotate = Vector3(0, 0, 0)
21 tpl.target_path = mastery
22 tpl.name = "Teleport"
23 tpl.senderGroup.append(NodePath("/root/scene/Panels"))
24 tpl.nested = true
25 mastery.add_child.call_deferred(tpl)
26
27 var usl = usl_prefab.instantiate()
28 usl.name = "unlockSetterListenerMastery"
29 usl.key = "icarus_mastery"
30 usl.value = "unlocked"
31 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
32 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
33
34 var saver = saver_prefab.instantiate()
35 saver.name = "saver_collectables"
36 saver.type = "collectables"
37 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
38 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_advanced.gd b/apworld/client/maps/the_advanced.gd new file mode 100644 index 0000000..b41549c --- /dev/null +++ b/apworld/client/maps/the_advanced.gd
@@ -0,0 +1,36 @@
1func on_map_load(root):
2 # Add the mastery to The Advanced.
3 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
4 var saver_prefab = preload("res://objects/nodes/saver.tscn")
5 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
6 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
7
8 var mastery = collectable_prefab.instantiate()
9 mastery.name = "collectable"
10 mastery.position = Vector3(0, -200, -5)
11 mastery.unlock_type = "smiley"
12 mastery.material_override = load("res://assets/materials/gold.material")
13 root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery)
14
15 var tpl = tpl_prefab.instantiate()
16 tpl.teleport_point = Vector3(0, 2, -5)
17 tpl.teleport_rotate = Vector3(0, 0, 0)
18 tpl.target_path = mastery
19 tpl.name = "Teleport"
20 tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_29"))
21 tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_30"))
22 tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31"))
23 mastery.add_child.call_deferred(tpl)
24
25 var usl = usl_prefab.instantiate()
26 usl.name = "unlockSetterListenerMastery"
27 usl.key = "advanced_mastery"
28 usl.value = "unlocked"
29 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
30 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
31
32 var saver = saver_prefab.instantiate()
33 saver.name = "saver_collectables"
34 saver.type = "collectables"
35 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
36 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_charismatic.gd b/apworld/client/maps/the_charismatic.gd new file mode 100644 index 0000000..734001d --- /dev/null +++ b/apworld/client/maps/the_charismatic.gd
@@ -0,0 +1,26 @@
1func on_map_load(root):
2 # Add the mastery to The Charismatic.
3 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
4 var saver_prefab = preload("res://objects/nodes/saver.tscn")
5 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
6
7 var mastery = collectable_prefab.instantiate()
8 mastery.name = "collectable"
9 mastery.position = Vector3(-17, 2, -29)
10 mastery.rotation_degrees = Vector3(0, 45, 0)
11 mastery.unlock_type = "smiley"
12 mastery.material_override = load("res://assets/materials/gold.material")
13 root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery)
14
15 var usl = usl_prefab.instantiate()
16 usl.name = "unlockSetterListenerMastery"
17 usl.key = "charismatic_mastery"
18 usl.value = "unlocked"
19 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
20 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
21
22 var saver = saver_prefab.instantiate()
23 saver.name = "saver_collectables"
24 saver.type = "collectables"
25 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
26 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_crystalline.gd b/apworld/client/maps/the_crystalline.gd new file mode 100644 index 0000000..7d43e78 --- /dev/null +++ b/apworld/client/maps/the_crystalline.gd
@@ -0,0 +1,34 @@
1func on_map_load(root):
2 # Add the mastery to The Crystalline.
3 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
4 var saver_prefab = preload("res://objects/nodes/saver.tscn")
5 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
6 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
7
8 var mastery = collectable_prefab.instantiate()
9 mastery.name = "collectable"
10 mastery.position = Vector3(0, 13, 37)
11 mastery.unlock_type = "smiley"
12 mastery.material_override = load("res://assets/materials/gold.material")
13 root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery)
14
15 var tpl = tpl_prefab.instantiate()
16 tpl.teleport_point = Vector3(0, 11.5, -20)
17 tpl.teleport_rotate = Vector3(0, 0, 180)
18 tpl.target_path = mastery
19 tpl.name = "Teleport"
20 tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3"))
21 mastery.add_child.call_deferred(tpl)
22
23 var usl = usl_prefab.instantiate()
24 usl.name = "unlockSetterListenerMastery"
25 usl.key = "crystalline_mastery"
26 usl.value = "unlocked"
27 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
28 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
29
30 var saver = saver_prefab.instantiate()
31 saver.name = "saver_collectables"
32 saver.type = "collectables"
33 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
34 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_entry.gd b/apworld/client/maps/the_entry.gd new file mode 100644 index 0000000..3608bb3 --- /dev/null +++ b/apworld/client/maps/the_entry.gd
@@ -0,0 +1,156 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Remove door behind X1.
5 var door_node = root.get_node("/root/scene/Components/Doors/exit_1")
6 door_node.handleTriggered()
7
8 # Display win condition.
9 var sign_prefab = preload("res://objects/nodes/sign.tscn")
10 var sign1 = sign_prefab.instantiate()
11 sign1.position = Vector3(-7, 5, -15.01)
12 sign1.text = "victory"
13 root.get_node("/root/scene").add_child.call_deferred(sign1)
14
15 var sign2 = sign_prefab.instantiate()
16 sign2.position = Vector3(-7, 4, -15.01)
17 sign2.text = "%s ending" % ap.kEndingNameByVictoryValue.get(ap.victory_condition, "?")
18
19 var sign2_color = ap.kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
20 if sign2_color == "white":
21 sign2_color = "silver"
22
23 sign2.material = load("res://assets/materials/%s.material" % sign2_color)
24 root.get_node("/root/scene").add_child.call_deferred(sign2)
25
26 # Add the gift map entry panel if needed.
27 if not ap.enable_gift_maps.is_empty():
28 var panel_prefab = preload("res://objects/nodes/panel.tscn")
29 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
30 var wpl_prefab = preload("res://objects/nodes/listeners/worldportListener.tscn")
31
32 var giftmap_parent = Node.new()
33 giftmap_parent.name = "GiftMapEntrance"
34 root.get_node("/root/scene/Components").add_child.call_deferred(giftmap_parent)
35
36 var symbolless_player = ""
37 for i in range(ap.client.ap_user.length()):
38 if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains(
39 ap.client.ap_user[i]
40 ):
41 symbolless_player = symbolless_player + ap.client.ap_user[i].to_lower()
42
43 var giftmap_panel = panel_prefab.instantiate()
44 giftmap_panel.name = "Panel"
45 giftmap_panel.position = Vector3(33.5, -190, 5.5)
46 giftmap_panel.rotation_degrees = Vector3(-45, 0, 0)
47 giftmap_panel.clue = "player"
48 giftmap_panel.answer = symbolless_player
49
50 if ap.enable_gift_maps.has("The Advanced"):
51 var icely_panel = panel_prefab.instantiate()
52 icely_panel.name = "IcelyPanel"
53 icely_panel.answer = "icely"
54 icely_panel.position = Vector3(33.5, -200, 5.5)
55 giftmap_panel.proxies.append(NodePath("../IcelyPanel"))
56 giftmap_parent.add_child.call_deferred(icely_panel)
57
58 var icely_wpl = wpl_prefab.instantiate()
59 icely_wpl.name = "IcelyWpl"
60 icely_wpl.exit = "the_advanced"
61 icely_wpl.senders.append(NodePath("../IcelyPanel"))
62 giftmap_parent.add_child.call_deferred(icely_wpl)
63
64 if ap.enable_gift_maps.has("The Charismatic"):
65 var souvey_panel = panel_prefab.instantiate()
66 souvey_panel.name = "SouveyPanel"
67 souvey_panel.answer = "souvey"
68 souvey_panel.position = Vector3(33.5, -210, 5.5)
69 giftmap_panel.proxies.append(NodePath("../SouveyPanel"))
70 giftmap_parent.add_child.call_deferred(souvey_panel)
71
72 var souvey_wpl = wpl_prefab.instantiate()
73 souvey_wpl.name = "SouveyWpl"
74 souvey_wpl.exit = "the_charismatic"
75 souvey_wpl.senders.append(NodePath("../SouveyPanel"))
76 giftmap_parent.add_child.call_deferred(souvey_wpl)
77
78 if ap.enable_gift_maps.has("The Crystalline"):
79 var q_panel = panel_prefab.instantiate()
80 q_panel.name = "QPanel"
81 q_panel.answer = "q"
82 q_panel.position = Vector3(33.5, -220, 5.5)
83 giftmap_panel.proxies.append(NodePath("../QPanel"))
84 giftmap_parent.add_child.call_deferred(q_panel)
85
86 var q_wpl = wpl_prefab.instantiate()
87 q_wpl.name = "QWpl"
88 q_wpl.exit = "the_crystalline"
89 q_wpl.senders.append(NodePath("../QPanel"))
90 giftmap_parent.add_child.call_deferred(q_wpl)
91
92 if ap.enable_gift_maps.has("The Fuzzy"):
93 var gongus_panel = panel_prefab.instantiate()
94 gongus_panel.name = "GongusPanel"
95 gongus_panel.answer = "gongus"
96 gongus_panel.position = Vector3(33.5, -260, 5.5)
97 giftmap_panel.proxies.append(NodePath("../GongusPanel"))
98 giftmap_parent.add_child.call_deferred(gongus_panel)
99
100 var kiwi_panel = panel_prefab.instantiate()
101 kiwi_panel.name = "KiwiPanel"
102 kiwi_panel.answer = "kiwi"
103 kiwi_panel.position = Vector3(33.5, -270, 5.5)
104 giftmap_panel.proxies.append(NodePath("../KiwiPanel"))
105 giftmap_parent.add_child.call_deferred(kiwi_panel)
106
107 var fuzzy_wpl = wpl_prefab.instantiate()
108 fuzzy_wpl.name = "FuzzyWpl"
109 fuzzy_wpl.exit = "the_fuzzy"
110 fuzzy_wpl.senders.append(NodePath("../GongusPanel"))
111 fuzzy_wpl.senders.append(NodePath("../KiwiPanel"))
112 fuzzy_wpl.complete_at = 1
113 giftmap_parent.add_child.call_deferred(fuzzy_wpl)
114
115 if ap.enable_gift_maps.has("The Stellar"):
116 var hatkirby_panel = panel_prefab.instantiate()
117 hatkirby_panel.name = "HatkirbyPanel"
118 hatkirby_panel.answer = "hatkirby"
119 hatkirby_panel.position = Vector3(33.5, -230, 5.5)
120 giftmap_panel.proxies.append(NodePath("../HatkirbyPanel"))
121 giftmap_parent.add_child.call_deferred(hatkirby_panel)
122
123 var kirby_panel = panel_prefab.instantiate()
124 kirby_panel.name = "KirbyPanel"
125 kirby_panel.answer = "kirby"
126 kirby_panel.position = Vector3(33.5, -240, 5.5)
127 giftmap_panel.proxies.append(NodePath("../KirbyPanel"))
128 giftmap_parent.add_child.call_deferred(kirby_panel)
129
130 var star_panel = panel_prefab.instantiate()
131 star_panel.name = "StarPanel"
132 star_panel.answer = "star"
133 star_panel.position = Vector3(33.5, -250, 5.5)
134 giftmap_panel.proxies.append(NodePath("../StarPanel"))
135 giftmap_parent.add_child.call_deferred(star_panel)
136
137 var stellar_wpl = wpl_prefab.instantiate()
138 stellar_wpl.name = "StellarWpl"
139 stellar_wpl.exit = "the_stellar"
140 stellar_wpl.senders.append(NodePath("../HatkirbyPanel"))
141 stellar_wpl.senders.append(NodePath("../KirbyPanel"))
142 stellar_wpl.senders.append(NodePath("../StarPanel"))
143 stellar_wpl.complete_at = 1
144 giftmap_parent.add_child.call_deferred(stellar_wpl)
145
146 giftmap_parent.add_child.call_deferred(giftmap_panel)
147
148 var giftmap_tpl = tpl_prefab.instantiate()
149 giftmap_tpl.name = "PanelTeleporter"
150 giftmap_tpl.teleport_point = Vector3(33.5, 1, 5.5)
151 giftmap_tpl.teleport_rotate = Vector3(-45, 0, 0)
152 giftmap_tpl.target_path = giftmap_panel
153 giftmap_tpl.senders.append(
154 NodePath("/root/scene/Components/Listeners/unlockReaderListenerDoubles")
155 )
156 giftmap_parent.add_child.call_deferred(giftmap_tpl)
diff --git a/apworld/client/maps/the_fuzzy.gd b/apworld/client/maps/the_fuzzy.gd new file mode 100644 index 0000000..269dcee --- /dev/null +++ b/apworld/client/maps/the_fuzzy.gd
@@ -0,0 +1,25 @@
1func on_map_load(root):
2 # Add the mastery to The Fuzzy.
3 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
4 var saver_prefab = preload("res://objects/nodes/saver.tscn")
5 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
6
7 var mastery = collectable_prefab.instantiate()
8 mastery.name = "collectable"
9 mastery.position = Vector3(0, 2, -20)
10 mastery.unlock_type = "smiley"
11 mastery.material_override = load("res://assets/materials/gold.material")
12 root.get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery)
13
14 var usl = usl_prefab.instantiate()
15 usl.name = "unlockSetterListenerMastery"
16 usl.key = "fuzzy_mastery"
17 usl.value = "unlocked"
18 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
19 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
20
21 var saver = saver_prefab.instantiate()
22 saver.name = "saver_collectables"
23 saver.type = "collectables"
24 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
25 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_parthenon.gd b/apworld/client/maps/the_parthenon.gd new file mode 100644 index 0000000..96510da --- /dev/null +++ b/apworld/client/maps/the_parthenon.gd
@@ -0,0 +1,51 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Add the strict cyan ending validation.
5 if ap.strict_cyan_ending:
6 var panel_prefab = preload("res://objects/nodes/panel.tscn")
7 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
8 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
9
10 var previous_panel = null
11 var next_y = -100
12 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
13 for word in words:
14 var panel = panel_prefab.instantiate()
15 panel.position = Vector3(0, next_y, 0)
16 next_y -= 10
17 panel.clue = word
18 panel.symbol = "."
19 panel.answer = "%s%s" % [word, word]
20 panel.name = "EndCheck_%s" % word
21
22 var tpl = tpl_prefab.instantiate()
23 tpl.teleport_point = Vector3(0, 1, -11)
24 tpl.teleport_rotate = Vector3(-45, 0, 0)
25 tpl.target_path = panel
26 tpl.name = "Teleport"
27
28 if previous_panel == null:
29 tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers"))
30 else:
31 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
32
33 var reversing = reverse_prefab.instantiate()
34 reversing.senders.append(NodePath(".."))
35 reversing.name = "Reversing"
36 tpl.senders.append(NodePath("../Reversing"))
37
38 panel.add_child.call_deferred(tpl)
39 panel.add_child.call_deferred(reversing)
40 root.get_node("/root/scene/Panels").add_child.call_deferred(panel)
41
42 previous_panel = panel
43
44 # Duplicate the door that usually waits on the rulers. We can't set the
45 # senders here for some reason so we actually set them in the door ready
46 # function.
47 var entry1 = root.get_node("/root/scene/Components/Doors/entry_1")
48 var entry12 = entry1.duplicate()
49 entry12.name = "spe_entry_1"
50 entry1.get_parent().add_child.call_deferred(entry12)
51 entry1.queue_free()
diff --git a/apworld/client/maps/the_plaza.gd b/apworld/client/maps/the_plaza.gd new file mode 100644 index 0000000..13e002d --- /dev/null +++ b/apworld/client/maps/the_plaza.gd
@@ -0,0 +1,4 @@
1func on_map_load(root):
2 # Move the Plaza RTE trigger outside of the turtle.
3 var rte_trigger = root.get_node("/root/scene/Components/Warps/triggerArea")
4 rte_trigger.position.z = 0
diff --git a/apworld/client/maps/the_stellar.gd b/apworld/client/maps/the_stellar.gd new file mode 100644 index 0000000..d633535 --- /dev/null +++ b/apworld/client/maps/the_stellar.gd
@@ -0,0 +1,30 @@
1func on_map_load(root):
2 # Add the mastery to The Stellar.
3 var collectable_prefab = preload("res://objects/nodes/collectable.tscn")
4 var saver_prefab = preload("res://objects/nodes/saver.tscn")
5 var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn")
6
7 var collectables = Node.new()
8 collectables.name = "Collectables"
9
10 var mastery = collectable_prefab.instantiate()
11 mastery.name = "collectable"
12 mastery.position = Vector3(2, 2, -31)
13 mastery.rotation_degrees = Vector3(0, 90, 0)
14 mastery.unlock_type = "smiley"
15 mastery.material_override = load("res://assets/materials/gold.material")
16 collectables.add_child.call_deferred(mastery)
17 root.get_node("/root/scene/Components").add_child.call_deferred(collectables)
18
19 var usl = usl_prefab.instantiate()
20 usl.name = "unlockSetterListenerMastery"
21 usl.key = "stellar_mastery"
22 usl.value = "unlocked"
23 usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable"))
24 root.get_node("/root/scene/Components").add_child.call_deferred(usl)
25
26 var saver = saver_prefab.instantiate()
27 saver.name = "saver_collectables"
28 saver.type = "collectables"
29 saver.senderGroup.append(NodePath("/root/scene/Components/Collectables"))
30 root.get_node("/root/scene").add_child.call_deferred(saver)
diff --git a/apworld/client/maps/the_sun_temple.gd b/apworld/client/maps/the_sun_temple.gd new file mode 100644 index 0000000..9804bf8 --- /dev/null +++ b/apworld/client/maps/the_sun_temple.gd
@@ -0,0 +1,56 @@
1func on_map_load(root):
2 var ap = global.get_node("Archipelago")
3
4 # Add the strict purple ending validation.
5 if ap.strict_purple_ending:
6 var panel_prefab = preload("res://objects/nodes/panel.tscn")
7 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
8 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
9
10 var previous_panel = null
11 var next_y = -100
12 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
13 for word in words:
14 var panel = panel_prefab.instantiate()
15 panel.position = Vector3(0, next_y, 0)
16 next_y -= 10
17 panel.clue = word
18 panel.symbol = ""
19 panel.answer = word
20 panel.name = "EndCheck_%s" % word
21
22 var tpl = tpl_prefab.instantiate()
23 tpl.teleport_point = Vector3(0, 1, 0)
24 tpl.teleport_rotate = Vector3(-45, 180, 0)
25 tpl.target_path = panel
26 tpl.name = "Teleport"
27
28 if previous_panel == null:
29 tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24"))
30 else:
31 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
32
33 var reversing = reverse_prefab.instantiate()
34 reversing.senders.append(NodePath(".."))
35 reversing.name = "Reversing"
36 tpl.senders.append(NodePath("../Reversing"))
37
38 panel.add_child.call_deferred(tpl)
39 panel.add_child.call_deferred(reversing)
40 root.get_node("/root/scene/Panels").add_child.call_deferred(panel)
41
42 previous_panel = panel
43
44 # Duplicate the doors that usually wait on EQUINOX. We can't set the senders
45 # here for some reason so we actually set them in the door ready function.
46 var endplat = root.get_node("/root/scene/Components/Doors/EndPlatform")
47 var endplat2 = endplat.duplicate()
48 endplat2.name = "spe_EndPlatform"
49 endplat.get_parent().add_child.call_deferred(endplat2)
50 endplat.queue_free()
51
52 var entry2 = root.get_node("/root/scene/Components/Doors/entry_2")
53 var entry22 = entry2.duplicate()
54 entry22.name = "spe_entry_2"
55 entry2.get_parent().add_child.call_deferred(entry22)
56 entry2.queue_free()
diff --git a/apworld/client/maps/the_unkempt.gd b/apworld/client/maps/the_unkempt.gd new file mode 100644 index 0000000..c907650 --- /dev/null +++ b/apworld/client/maps/the_unkempt.gd
@@ -0,0 +1,4 @@
1func on_map_load(root):
2 # Prevent the COLOR panel from disappearing.
3 var color_tpl = root.get_node("/root/scene/Panels/Assorted/panel_1/teleportListener")
4 color_tpl.target_path = color_tpl
diff --git a/apworld/client/maps/the_unyielding.gd b/apworld/client/maps/the_unyielding.gd new file mode 100644 index 0000000..a2f8eee --- /dev/null +++ b/apworld/client/maps/the_unyielding.gd
@@ -0,0 +1,5 @@
1func on_map_load(root):
2 # Shrink the painting trigger in The Unyielding.
3 var trigger_area = root.get_node("/root/scene/Components/PaintingUnlocker/triggerArea")
4 trigger_area.position = Vector3(0, 0, -6)
5 trigger_area.scale = Vector3(6, 1, 6)
diff --git a/apworld/client/minimap.gd b/apworld/client/minimap.gd index 5640716..bf70114 100644 --- a/apworld/client/minimap.gd +++ b/apworld/client/minimap.gd
@@ -126,6 +126,7 @@ func _process(_delta):
126 126
127 127
128func _renderMap(gridmap): 128func _renderMap(gridmap):
129 var ap = global.get_node("Archipelago")
129 var heights = {} 130 var heights = {}
130 131
131 var rendered = Image.create_empty(cell_width, cell_height, false, Image.FORMAT_RGBA8) 132 var rendered = Image.create_empty(cell_width, cell_height, false, Image.FORMAT_RGBA8)
@@ -133,7 +134,7 @@ func _renderMap(gridmap):
133 134
134 var meshes_node = get_tree().get_root().get_node("scene/Meshes") 135 var meshes_node = get_tree().get_root().get_node("scene/Meshes")
135 if meshes_node != null: 136 if meshes_node != null:
136 _renderMeshNode(gridmap, meshes_node, rendered) 137 _renderMeshNode(ap, gridmap, meshes_node, rendered)
137 138
138 for pos in gridmap.get_used_cells(): 139 for pos in gridmap.get_used_cells():
139 var in_plane = Vector2i(pos.x, pos.z) 140 var in_plane = Vector2i(pos.x, pos.z)
@@ -146,20 +147,22 @@ func _renderMap(gridmap):
146 var cell_item = gridmap.get_cell_item(pos) 147 var cell_item = gridmap.get_cell_item(pos)
147 var mesh = gridmap.mesh_library.get_item_mesh(cell_item) 148 var mesh = gridmap.mesh_library.get_item_mesh(cell_item)
148 var material = mesh.surface_get_material(0) 149 var material = mesh.surface_get_material(0)
149 var color = material.albedo_color 150 var color = ap.color_by_material_path.get(material.resource_path, Color.TRANSPARENT)
150 151
151 rendered.set_pixel(pos.x - cell_left, pos.z - cell_top, color) 152 rendered.set_pixel(pos.x - cell_left, pos.z - cell_top, color)
152 153
153 return rendered 154 return rendered
154 155
155 156
156func _renderMeshNode(gridmap, mesh, rendered): 157func _renderMeshNode(ap, gridmap, mesh, rendered):
157 if mesh is MeshInstance3D: 158 if mesh is MeshInstance3D:
158 var local_tl = gridmap.map_to_local(Vector3i(cell_left, 0, cell_top)) 159 var local_tl = gridmap.map_to_local(Vector3i(cell_left, 0, cell_top))
159 var global_tl = gridmap.to_global(local_tl) 160 var global_tl = gridmap.to_global(local_tl)
160 var mesh_material = mesh.get_surface_override_material(0) 161 var mesh_material = mesh.get_surface_override_material(0)
161 if mesh_material != null: 162 if mesh_material != null:
162 var mesh_color = mesh_material.albedo_color 163 var mesh_color = ap.color_by_material_path.get(
164 mesh_material.resource_path, Color.TRANSPARENT
165 )
163 166
164 for y in range( 167 for y in range(
165 max(mesh.position.z - mesh.scale.z / 2 - global_tl.z, 0), 168 max(mesh.position.z - mesh.scale.z / 2 - global_tl.z, 0),
@@ -172,4 +175,4 @@ func _renderMeshNode(gridmap, mesh, rendered):
172 rendered.set_pixel(x, y, mesh_color) 175 rendered.set_pixel(x, y, mesh_color)
173 176
174 for child in mesh.get_children(): 177 for child in mesh.get_children():
175 _renderMeshNode(gridmap, child, rendered) 178 _renderMeshNode(ap, gridmap, child, rendered)
diff --git a/apworld/client/paintingAuto.gd b/apworld/client/paintingAuto.gd new file mode 100644 index 0000000..553c2c9 --- /dev/null +++ b/apworld/client/paintingAuto.gd
@@ -0,0 +1,43 @@
1extends "res://scripts/nodes/paintingAuto.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 if item_id != null and activate_on_sender_complete:
34 enabled = false
35 if not hide_particles:
36 get_node("Hinge/paintingColliders/TeleportParticles").emitting = false
37
38
39func _readier():
40 var ap = global.get_node("Archipelago")
41
42 if ap.client.getItemAmount(item_id) >= item_amount:
43 handleTriggered()
diff --git a/apworld/client/player.gd b/apworld/client/player.gd index 366c3b0..5fac9fd 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd
@@ -1,21 +1,5 @@
1extends "res://scripts/nodes/player.gd" 1extends "res://scripts/nodes/player.gd"
2 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 3signal evaluate_solvability
20 4
21var compass 5var compass
@@ -35,6 +19,13 @@ func _ready():
35 19
36 ap.start_batching_locations() 20 ap.start_batching_locations()
37 21
22 # Run map-specific initialization.
23 var map_script = ap.get_map_script(global.map)
24 if map_script != null:
25 map_script.on_map_load(get_tree().get_root())
26
27 ap.update_job_well_done_sign()
28
38 # Set up door locations. 29 # Set up door locations.
39 var map_id = gamedata.map_id_by_name.get(global.map) 30 var map_id = gamedata.map_id_by_name.get(global.map)
40 for door in gamedata.objects.get_doors(): 31 for door in gamedata.objects.get_doors():
@@ -45,8 +36,12 @@ func _ready():
45 continue 36 continue
46 37
47 if ( 38 if (
48 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY 39 not (door.has_legacy_location() and door.get_legacy_location())
49 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING 40 and (
41 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY
42 or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING
43 or door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR
44 )
50 ): 45 ):
51 continue 46 continue
52 47
@@ -138,7 +133,7 @@ func _ready():
138 133
139 get_parent().add_child.call_deferred(locationListener) 134 get_parent().add_child.call_deferred(locationListener)
140 135
141 if kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): 136 if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name():
142 var victoryListener = ap.SCRIPT_victoryListener.new() 137 var victoryListener = ap.SCRIPT_victoryListener.new()
143 victoryListener.name = "victoryListener" 138 victoryListener.name = "victoryListener"
144 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) 139 victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path()))
@@ -169,165 +164,6 @@ func _ready():
169 164
170 get_parent().add_child.call_deferred(locationListener) 165 get_parent().add_child.call_deferred(locationListener)
171 166
172 # Block off roof access in Daedalus.
173 if global.map == "daedalus" and not ap.daedalus_roof_access:
174 _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
175 _set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
176 _set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
177 _set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
178 _set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
179 _set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
180 _set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
181 _set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
182 _set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
183 _set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
184 _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
185 _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
186 _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
187 _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
188 _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)
189
190 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
191 var warp_exit = warp_exit_prefab.instantiate()
192 warp_exit.name = "roof_access_blocker_warp_exit"
193 warp_exit.position = Vector3(58, 10, 0)
194 warp_exit.rotation_degrees.y = 90
195 get_parent().add_child.call_deferred(warp_exit)
196
197 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
198 var warp_enter = warp_enter_prefab.instantiate()
199 warp_enter.target = warp_exit
200 warp_enter.position = Vector3(76.5, 30, 1)
201 warp_enter.scale = Vector3(4, 1.5, 1)
202 warp_enter.rotation_degrees.y = 90
203 get_parent().add_child.call_deferred(warp_enter)
204
205 if global.map == "the_entry":
206 # Remove door behind X1.
207 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
208 door_node.handleTriggered()
209
210 # Display win condition.
211 var sign_prefab = preload("res://objects/nodes/sign.tscn")
212 var sign1 = sign_prefab.instantiate()
213 sign1.position = Vector3(-7, 5, -15.01)
214 sign1.text = "victory"
215 get_parent().add_child.call_deferred(sign1)
216
217 var sign2 = sign_prefab.instantiate()
218 sign2.position = Vector3(-7, 4, -15.01)
219 sign2.text = "%s ending" % kEndingNameByVictoryValue.get(ap.victory_condition, "?")
220
221 var sign2_color = kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
222 if sign2_color == "white":
223 sign2_color = "silver"
224
225 sign2.material = load("res://assets/materials/%s.material" % sign2_color)
226 get_parent().add_child.call_deferred(sign2)
227
228 # Add the strict purple ending validation.
229 if global.map == "the_sun_temple" and ap.strict_purple_ending:
230 var panel_prefab = preload("res://objects/nodes/panel.tscn")
231 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
232 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
233
234 var previous_panel = null
235 var next_y = -100
236 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
237 for word in words:
238 var panel = panel_prefab.instantiate()
239 panel.position = Vector3(0, next_y, 0)
240 next_y -= 10
241 panel.clue = word
242 panel.symbol = ""
243 panel.answer = word
244 panel.name = "EndCheck_%s" % word
245
246 var tpl = tpl_prefab.instantiate()
247 tpl.teleport_point = Vector3(0, 1, 0)
248 tpl.teleport_rotate = Vector3(-45, 180, 0)
249 tpl.target_path = panel
250 tpl.name = "Teleport"
251
252 if previous_panel == null:
253 tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24"))
254 else:
255 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
256
257 var reversing = reverse_prefab.instantiate()
258 reversing.senders.append(NodePath(".."))
259 reversing.name = "Reversing"
260 tpl.senders.append(NodePath("../Reversing"))
261
262 panel.add_child.call_deferred(tpl)
263 panel.add_child.call_deferred(reversing)
264 get_parent().get_node("Panels").add_child.call_deferred(panel)
265
266 previous_panel = panel
267
268 # Duplicate the doors that usually wait on EQUINOX. We can't set the senders
269 # here for some reason so we actually set them in the door ready function.
270 var endplat = get_node("/root/scene/Components/Doors/EndPlatform")
271 var endplat2 = endplat.duplicate()
272 endplat2.name = "spe_EndPlatform"
273 endplat.get_parent().add_child.call_deferred(endplat2)
274 endplat.queue_free()
275
276 var entry2 = get_node("/root/scene/Components/Doors/entry_2")
277 var entry22 = entry2.duplicate()
278 entry22.name = "spe_entry_2"
279 entry2.get_parent().add_child.call_deferred(entry22)
280 entry2.queue_free()
281
282 # Add the strict cyan ending validation.
283 if global.map == "the_parthenon" and ap.strict_cyan_ending:
284 var panel_prefab = preload("res://objects/nodes/panel.tscn")
285 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
286 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
287
288 var previous_panel = null
289 var next_y = -100
290 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
291 for word in words:
292 var panel = panel_prefab.instantiate()
293 panel.position = Vector3(0, next_y, 0)
294 next_y -= 10
295 panel.clue = word
296 panel.symbol = "."
297 panel.answer = "%s%s" % [word, word]
298 panel.name = "EndCheck_%s" % word
299
300 var tpl = tpl_prefab.instantiate()
301 tpl.teleport_point = Vector3(0, 1, -11)
302 tpl.teleport_rotate = Vector3(-45, 0, 0)
303 tpl.target_path = panel
304 tpl.name = "Teleport"
305
306 if previous_panel == null:
307 tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers"))
308 else:
309 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
310
311 var reversing = reverse_prefab.instantiate()
312 reversing.senders.append(NodePath(".."))
313 reversing.name = "Reversing"
314 tpl.senders.append(NodePath("../Reversing"))
315
316 panel.add_child.call_deferred(tpl)
317 panel.add_child.call_deferred(reversing)
318 get_parent().get_node("Panels").add_child.call_deferred(panel)
319
320 previous_panel = panel
321
322 # Duplicate the door that usually waits on the rulers. We can't set the
323 # senders here for some reason so we actually set them in the door ready
324 # function.
325 var entry1 = get_node("/root/scene/Components/Doors/entry_1")
326 var entry12 = entry1.duplicate()
327 entry12.name = "spe_entry_1"
328 entry1.get_parent().add_child.call_deferred(entry12)
329 entry1.queue_free()
330
331 var minimap = ap.SCRIPT_minimap.new() 167 var minimap = ap.SCRIPT_minimap.new()
332 minimap.name = "Minimap" 168 minimap.name = "Minimap"
333 minimap.visible = ap.show_minimap 169 minimap.visible = ap.show_minimap
@@ -341,22 +177,5 @@ func _ready():
341 ap.stop_batching_locations() 177 ap.stop_batching_locations()
342 178
343 179
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): 180func _process(_dt):
362 compass.update_rotation(global_rotation.y) 181 compass.update_rotation(global_rotation.y)
diff --git a/apworld/client/settings_screen.gd b/apworld/client/settings_screen.gd index b430b17..89e8b68 100644 --- a/apworld/client/settings_screen.gd +++ b/apworld/client/settings_screen.gd
@@ -100,7 +100,7 @@ func _ready():
100 server_box.offset_top = 295.0 100 server_box.offset_top = 295.0
101 server_box.offset_right = 1144.0 101 server_box.offset_right = 1144.0
102 server_box.offset_bottom = 445.0 102 server_box.offset_bottom = 445.0
103 server_box.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER 103 server_box.alignment = HORIZONTAL_ALIGNMENT_CENTER
104 server_box.caret_blink = true 104 server_box.caret_blink = true
105 panel.add_child(server_box) 105 panel.add_child(server_box)
106 106
@@ -110,7 +110,7 @@ func _ready():
110 player_box.offset_top = 477.0 110 player_box.offset_top = 477.0
111 player_box.offset_right = 1144.0 111 player_box.offset_right = 1144.0
112 player_box.offset_bottom = 627.0 112 player_box.offset_bottom = 627.0
113 player_box.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER 113 player_box.alignment = HORIZONTAL_ALIGNMENT_CENTER
114 player_box.caret_blink = true 114 player_box.caret_blink = true
115 panel.add_child(player_box) 115 panel.add_child(player_box)
116 116
@@ -120,20 +120,16 @@ func _ready():
120 password_box.offset_top = 659.0 120 password_box.offset_top = 659.0
121 password_box.offset_right = 1144.0 121 password_box.offset_right = 1144.0
122 password_box.offset_bottom = 809.0 122 password_box.offset_bottom = 809.0
123 password_box.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER 123 password_box.alignment = HORIZONTAL_ALIGNMENT_CENTER
124 password_box.caret_blink = true 124 password_box.caret_blink = true
125 panel.add_child(password_box) 125 panel.add_child(password_box)
126 126
127 var accept_dialog = AcceptDialog.new() 127 var accept_dialog = AcceptDialog.new()
128 accept_dialog.name = "AcceptDialog" 128 accept_dialog.name = "AcceptDialog"
129 accept_dialog.offset_right = 83.0
130 accept_dialog.offset_bottom = 58.0
131 panel.add_child(accept_dialog) 129 panel.add_child(accept_dialog)
132 130
133 var version_mismatch = ConfirmationDialog.new() 131 var version_mismatch = ConfirmationDialog.new()
134 version_mismatch.name = "VersionMismatch" 132 version_mismatch.name = "VersionMismatch"
135 version_mismatch.offset_right = 83.0
136 version_mismatch.offset_bottom = 58.0
137 panel.add_child(version_mismatch) 133 panel.add_child(version_mismatch)
138 134
139 var connection_history = MenuButton.new() 135 var connection_history = MenuButton.new()
diff --git a/apworld/client/source_runtime.gd b/apworld/client/source_runtime.gd index 35428ea..146587a 100644 --- a/apworld/client/source_runtime.gd +++ b/apworld/client/source_runtime.gd
@@ -7,6 +7,10 @@ func _init(path):
7 source_path = path 7 source_path = path
8 8
9 9
10func path_exists(path):
11 return FileAccess.file_exists("%s/%s" % [source_path, path])
12
13
10func load_script(path): 14func load_script(path):
11 return ResourceLoader.load("%s/%s" % [source_path, path]) 15 return ResourceLoader.load("%s/%s" % [source_path, path])
12 16
diff --git a/apworld/client/textclient.gd b/apworld/client/textclient.gd index 0c4e675..ce28a3a 100644 --- a/apworld/client/textclient.gd +++ b/apworld/client/textclient.gd
@@ -4,18 +4,30 @@ var tabs
4var panel 4var panel
5var label 5var label
6var entry 6var entry
7var tracker_label
8var is_open = false 7var is_open = false
9 8
10var locations_overlay 9var locations_overlay
11var location_texture 10var location_texture
12var worldport_texture 11var worldport_texture
12var goal_texture
13
14var tracker_tree
15var tracker_loc_tree_item_by_id = {}
16var tracker_port_tree_item_by_id = {}
17var tracker_goal_tree_item = null
18var tracker_object_by_index = {}
19var tracker_object_by_ignored_index = {}
20var tracker_ignored_group = null
13 21
14var worldports_tab 22var worldports_tab
15var worldports_tree 23var worldports_tree
16var port_tree_item_by_map = {} 24var port_tree_item_by_map = {}
17var port_tree_item_by_map_port = {} 25var port_tree_item_by_map_port = {}
18 26
27const kLocation = 0
28const kWorldport = 1
29const kGoal = 2
30
19 31
20func _ready(): 32func _ready():
21 process_mode = ProcessMode.PROCESS_MODE_ALWAYS 33 process_mode = ProcessMode.PROCESS_MODE_ALWAYS
@@ -60,7 +72,7 @@ func _ready():
60 label.size_flags_horizontal = Control.SIZE_EXPAND_FILL 72 label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
61 label.size_flags_vertical = Control.SIZE_EXPAND_FILL 73 label.size_flags_vertical = Control.SIZE_EXPAND_FILL
62 label.push_font(preload("res://assets/fonts/Lingo2.ttf")) 74 label.push_font(preload("res://assets/fonts/Lingo2.ttf"))
63 label.push_font_size(36) 75 label.push_font_size(30)
64 76
65 var entry_style = StyleBoxFlat.new() 77 var entry_style = StyleBoxFlat.new()
66 entry_style.bg_color = Color(0.9, 0.9, 0.9, 1) 78 entry_style.bg_color = Color(0.9, 0.9, 0.9, 1)
@@ -88,8 +100,20 @@ func _ready():
88 tracker_margins.add_theme_constant_override("margin_bottom", 60) 100 tracker_margins.add_theme_constant_override("margin_bottom", 60)
89 tabs.add_child(tracker_margins) 101 tabs.add_child(tracker_margins)
90 102
91 tracker_label = RichTextLabel.new() 103 tracker_tree = Tree.new()
92 tracker_margins.add_child(tracker_label) 104 tracker_tree.columns = 4
105 tracker_tree.hide_root = true
106 tracker_tree.add_theme_font_size_override("font_size", 24)
107 tracker_tree.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8, 1))
108 tracker_tree.add_theme_constant_override("v_separation", 1)
109 tracker_tree.item_edited.connect(_on_tracker_button_clicked)
110 tracker_tree.set_column_expand(0, false)
111 tracker_tree.set_column_expand(1, true)
112 tracker_tree.set_column_expand(2, false)
113 tracker_tree.set_column_expand(3, false)
114 tracker_tree.set_column_custom_minimum_width(2, 200)
115 tracker_tree.set_column_custom_minimum_width(3, 200)
116 tracker_margins.add_child(tracker_tree)
93 117
94 worldports_tab = MarginContainer.new() 118 worldports_tab = MarginContainer.new()
95 worldports_tab.name = "Worldports" 119 worldports_tab.name = "Worldports"
@@ -116,6 +140,10 @@ func _ready():
116 worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png")) 140 worldport_image.load_png_from_buffer(runtime.read_path("assets/worldport.png"))
117 worldport_texture = ImageTexture.create_from_image(worldport_image) 141 worldport_texture = ImageTexture.create_from_image(worldport_image)
118 142
143 var goal_image = Image.new()
144 goal_image.load_png_from_buffer(runtime.read_path("assets/goal.png"))
145 goal_texture = ImageTexture.create_from_image(goal_image)
146
119 147
120func _input(event): 148func _input(event):
121 if global.loaded and event is InputEventKey and event.pressed: 149 if global.loaded and event is InputEventKey and event.pressed:
@@ -162,14 +190,10 @@ func text_entered(text):
162 ap.client.say(cmd) 190 ap.client.say(cmd)
163 191
164 192
165func update_locations(): 193func update_locations(reset_locations = true):
166 var ap = global.get_node("Archipelago") 194 var ap = global.get_node("Archipelago")
167 var gamedata = global.get_node("Gamedata") 195 var gamedata = global.get_node("Gamedata")
168 196
169 tracker_label.clear()
170 tracker_label.push_font(preload("res://assets/fonts/Lingo2.ttf"))
171 tracker_label.push_font_size(24)
172
173 locations_overlay.clear() 197 locations_overlay.clear()
174 locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf")) 198 locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf"))
175 locations_overlay.push_font_size(24) 199 locations_overlay.push_font_size(24)
@@ -177,48 +201,226 @@ func update_locations():
177 locations_overlay.push_outline_color(Color(0, 0, 0, 1)) 201 locations_overlay.push_outline_color(Color(0, 0, 0, 1))
178 locations_overlay.push_outline_size(2) 202 locations_overlay.push_outline_size(2)
179 203
180 const kLocation = 0 204 var locations = []
181 const kWorldport = 1
182
183 var location_names = []
184 var type_by_name = {}
185 for location_id in ap.client._accessible_locations: 205 for location_id in ap.client._accessible_locations:
186 if not ap.client._checked_locations.has(location_id): 206 if not ap.client._checked_locations.has(location_id):
187 var location_name = gamedata.location_name_by_id.get(location_id, "(Unknown)") 207 var location_name = gamedata.location_name_by_id.get(location_id, "(Unknown)")
188 location_names.append(location_name) 208 (
189 type_by_name[location_name] = kLocation 209 locations
210 . append(
211 {
212 "name": location_name,
213 "type": kLocation,
214 "id": location_id,
215 "ignored": ap._ignored_locations.has(location_id),
216 "hint": ap.client._hinted_locations.has(location_id),
217 }
218 )
219 )
190 220
191 for port_id in ap.client._accessible_worldports: 221 for port_id in ap.client._accessible_worldports:
192 if not ap.client._checked_worldports.has(port_id): 222 if not ap.client._checked_worldports.has(port_id):
193 var port_name = gamedata.get_worldport_display_name(port_id) 223 var port_name = gamedata.get_worldport_display_name(port_id)
194 location_names.append(port_name) 224 (
195 type_by_name[port_name] = kWorldport 225 locations
196 226 . append(
197 location_names.sort() 227 {
228 "name": port_name,
229 "type": kWorldport,
230 "id": port_id,
231 "ignored": false,
232 "hint": false,
233 }
234 )
235 )
236
237 locations.sort_custom(_cmp_tracker_objects)
238
239 if ap.client._goal_accessible:
240 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
241 ap.victory_condition
242 ]]
243 (
244 locations
245 . push_front(
246 {
247 "name": location_name,
248 "type": kGoal,
249 "ignored": false,
250 "hint": false,
251 }
252 )
253 )
198 254
199 var count = 0 255 var count = 0
200 for location_name in location_names: 256 for location in locations:
201 tracker_label.append_text("[p]%s[/p]" % location_name) 257 if count < 18 and not location["ignored"]:
202 if count < 18:
203 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) 258 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT)
204 locations_overlay.append_text(location_name) 259 if location["hint"]:
260 locations_overlay.push_color(Color("#fafad2"))
261 locations_overlay.append_text(location["name"])
205 locations_overlay.append_text(" ") 262 locations_overlay.append_text(" ")
206 if type_by_name[location_name] == kLocation: 263 if location["type"] == kLocation:
207 locations_overlay.add_image(location_texture) 264 locations_overlay.add_image(location_texture)
208 elif type_by_name[location_name] == kWorldport: 265 elif location["type"] == kWorldport:
209 locations_overlay.add_image(worldport_texture) 266 locations_overlay.add_image(worldport_texture)
267 elif location["type"] == kGoal:
268 locations_overlay.add_image(goal_texture)
269 if location["hint"]:
270 locations_overlay.pop()
210 locations_overlay.pop() 271 locations_overlay.pop()
211 count += 1 272 count += 1
212 273
213 if count > 18: 274 if count > 18:
214 locations_overlay.append_text("[p align=right][lb]...[rb][/p]") 275 locations_overlay.append_text("[p align=right][lb]...[rb][/p]")
215 276
277 if reset_locations:
278 reset_tracker_tab()
279
280 var root_ti = tracker_tree.create_item(null)
281
282 for location in locations:
283 var loc_row
284
285 if location["ignored"]:
286 if tracker_ignored_group == null:
287 tracker_ignored_group = root_ti.create_child()
288 tracker_ignored_group.set_text(1, "Ignored Locations")
289 tracker_ignored_group.set_selectable(0, false)
290 tracker_ignored_group.set_selectable(1, false)
291 tracker_ignored_group.set_selectable(2, false)
292 tracker_ignored_group.set_selectable(3, false)
293
294 loc_row = tracker_ignored_group.create_child()
295 else:
296 loc_row = root_ti.create_child()
297
298 loc_row.set_cell_mode(0, TreeItem.CELL_MODE_ICON)
299 loc_row.set_selectable(0, false)
300 loc_row.set_text(1, location["name"])
301 loc_row.set_selectable(1, false)
302 if location["hint"]:
303 loc_row.set_custom_color(1, Color("#fafad2"))
304 loc_row.set_cell_mode(2, TreeItem.CELL_MODE_CUSTOM)
305 loc_row.set_text(2, "Show Path")
306 loc_row.set_custom_as_button(2, true)
307 loc_row.set_editable(2, true)
308 loc_row.set_selectable(2, false)
309 loc_row.set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER)
310 loc_row.set_selectable(3, false)
311 if location["type"] == kLocation:
312 loc_row.set_cell_mode(3, TreeItem.CELL_MODE_CUSTOM)
313 if location["ignored"]:
314 loc_row.set_text(3, "Unignore")
315 else:
316 loc_row.set_text(3, "Ignore")
317 loc_row.set_custom_as_button(3, true)
318 loc_row.set_editable(3, true)
319 loc_row.set_text_alignment(3, HORIZONTAL_ALIGNMENT_CENTER)
320
321 if location["type"] == kLocation:
322 loc_row.set_icon(0, location_texture)
323 tracker_loc_tree_item_by_id[location["id"]] = loc_row
324 elif location["type"] == kWorldport:
325 loc_row.set_icon(0, worldport_texture)
326 tracker_port_tree_item_by_id[location["id"]] = loc_row
327 elif location["type"] == kGoal:
328 loc_row.set_icon(0, goal_texture)
329 tracker_goal_tree_item = loc_row
330
331 if location["ignored"]:
332 tracker_object_by_ignored_index[loc_row.get_index()] = location
333 else:
334 tracker_object_by_index[loc_row.get_index()] = location
335 else:
336 for loc_row in tracker_tree.get_root().get_children():
337 loc_row.visible = false
338
339 for location_id in tracker_loc_tree_item_by_id.keys():
340 if (
341 ap.client._accessible_locations.has(location_id)
342 and not ap.client._checked_locations.has(location_id)
343 ):
344 tracker_loc_tree_item_by_id[location_id].visible = true
345
346 for port_id in tracker_port_tree_item_by_id.keys():
347 if (
348 ap.client._accessible_worldports.has(port_id)
349 and not ap.client._checked_worldports.has(port_id)
350 ):
351 tracker_port_tree_item_by_id[port_id].visible = true
352
353 if tracker_goal_tree_item != null and ap.client._goal_accessible:
354 tracker_goal_tree_item.visible = true
355
356 if tracker_ignored_group != null:
357 tracker_ignored_group.visible = true
358
359
360func _cmp_tracker_objects(a, b) -> bool:
361 if a["ignored"] != b["ignored"]:
362 return !a["ignored"]
363 elif a["hint"] != b["hint"]:
364 return a["hint"]
365 else:
366 return a["name"] < b["name"]
367
216 368
217func update_locations_visibility(): 369func update_locations_visibility():
218 var ap = global.get_node("Archipelago") 370 var ap = global.get_node("Archipelago")
219 locations_overlay.visible = ap.show_locations 371 locations_overlay.visible = ap.show_locations
220 372
221 373
374func _on_tracker_button_clicked():
375 var ap = global.get_node("Archipelago")
376
377 var edited_item = tracker_tree.get_edited()
378 var edited_index = edited_item.get_index()
379
380 if edited_item.get_parent() == tracker_tree.get_root():
381 if tracker_object_by_index.has(edited_index):
382 var tracker_object = tracker_object_by_index[edited_index]
383 if tracker_tree.get_edited_column() == 2:
384 var type_str = ""
385 if tracker_object["type"] == kLocation:
386 type_str = "location"
387 elif tracker_object["type"] == kWorldport:
388 type_str = "worldport"
389 elif tracker_object["type"] == kGoal:
390 type_str = "goal"
391 ap.client.getLogicalPath(type_str, tracker_object.get("id", null))
392 elif tracker_tree.get_edited_column() == 3:
393 ap.toggle_ignored_location(tracker_object["id"])
394 elif edited_item.get_parent() == tracker_ignored_group:
395 # This is the ignored locations group.
396 if (
397 tracker_object_by_ignored_index.has(edited_index)
398 and tracker_tree.get_edited_column() == 3
399 ):
400 var tracker_object = tracker_object_by_ignored_index[edited_index]
401 ap.toggle_ignored_location(tracker_object["id"])
402
403
404func display_logical_path(object_type, object_id, paths):
405 var ap = global.get_node("Archipelago")
406 var gamedata = global.get_node("Gamedata")
407
408 var location_name = "(Unknown)"
409 if object_type == "location" and object_id != null:
410 location_name = gamedata.location_name_by_id.get(object_id, "(Unknown)")
411 elif object_type == "worldport" and object_id != null:
412 location_name = gamedata.get_worldport_display_name(object_id)
413 elif object_type == "goal":
414 location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
415 ap.victory_condition
416 ]]
417
418 label.append_text("[p]Path to %s:[/p]" % location_name)
419 label.append_text("[ol]" + "\n".join(paths) + "[/ol]")
420
421 panel.visible = true
422
423
222func setup_worldports(): 424func setup_worldports():
223 tabs.set_tab_hidden(2, false) 425 tabs.set_tab_hidden(2, false)
224 426
@@ -293,3 +495,14 @@ func reset():
293 port_tree_item_by_map.clear() 495 port_tree_item_by_map.clear()
294 port_tree_item_by_map_port.clear() 496 port_tree_item_by_map_port.clear()
295 worldports_tree.clear() 497 worldports_tree.clear()
498 reset_tracker_tab()
499
500
501func reset_tracker_tab():
502 tracker_loc_tree_item_by_id.clear()
503 tracker_port_tree_item_by_id.clear()
504 tracker_goal_tree_item = null
505 tracker_object_by_index.clear()
506 tracker_object_by_ignored_index.clear()
507 tracker_ignored_group = null
508 tracker_tree.clear()
diff --git a/apworld/client/unlockReaderListener.gd b/apworld/client/unlockReaderListener.gd new file mode 100644 index 0000000..a5754b9 --- /dev/null +++ b/apworld/client/unlockReaderListener.gd
@@ -0,0 +1,46 @@
1extends "res://scripts/nodes/listeners/unlockReaderListener.gd"
2
3var item_id = null
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 super._ready()
30
31
32func _readier():
33 if item_id != null:
34 var ap = global.get_node("Archipelago")
35
36 if ap.client.getItemAmount(item_id) >= item_amount:
37 handleTriggered()
38 else:
39 super._readier()
40
41
42func handleTriggered():
43 if item_id != null:
44 emit_signal("trigger")
45 else:
46 super.handleTriggered()
diff --git a/apworld/client/worldportListener.gd b/apworld/client/worldportListener.gd index 5c2faff..4cff8e9 100644 --- a/apworld/client/worldportListener.gd +++ b/apworld/client/worldportListener.gd
@@ -2,7 +2,7 @@ extends "res://scripts/nodes/listeners/worldportListener.gd"
2 2
3 3
4func handleTriggered(): 4func handleTriggered():
5 if exit == "menus/credits": 5 if exit.begins_with("menus/credits"):
6 return 6 return
7 7
8 super.handleTriggered() 8 super.handleTriggered()