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/client.gd65
-rw-r--r--apworld/client/door.gd31
-rw-r--r--apworld/client/gamedata.gd30
-rw-r--r--apworld/client/keyHolderResetterListener.gd2
-rw-r--r--apworld/client/keyboard.gd3
-rw-r--r--apworld/client/main.gd16
-rw-r--r--apworld/client/manager.gd118
-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.gd191
-rw-r--r--apworld/client/settings_screen.gd10
-rw-r--r--apworld/client/source_runtime.gd4
-rw-r--r--apworld/client/textclient.gd258
-rw-r--r--apworld/client/unlockReaderListener.gd46
-rw-r--r--apworld/client/worldportListener.gd2
31 files changed, 1240 insertions, 242 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/client.gd b/apworld/client/client.gd index 62d7fd8..c149482 100644 --- a/apworld/client/client.gd +++ b/apworld/client/client.gd
@@ -25,6 +25,8 @@ var _slot_data = {}
25var _accessible_locations = [] 25var _accessible_locations = []
26var _accessible_worldports = [] 26var _accessible_worldports = []
27var _goal_accessible = false 27var _goal_accessible = false
28var _latched_doors = []
29var _hinted_locations = []
28 30
29signal could_not_connect 31signal could_not_connect
30signal connect_status 32signal connect_status
@@ -34,10 +36,13 @@ signal location_scout_received(location_id, item_name, player_name, flags, for_s
34signal text_message_received(message) 36signal text_message_received(message)
35signal item_sent_notification(message) 37signal item_sent_notification(message)
36signal hint_received(message) 38signal hint_received(message)
39signal door_latched(id)
37signal accessible_locations_updated 40signal accessible_locations_updated
38signal checked_locations_updated 41signal checked_locations_updated
42signal ignored_locations_updated(locations)
39signal checked_worldports_updated 43signal checked_worldports_updated
40signal keyboard_update_received 44signal keyboard_update_received
45signal hinted_locations_updated
41 46
42 47
43func _init(): 48func _init():
@@ -108,7 +113,7 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
108 113
109 _checked_locations = [] 114 _checked_locations = []
110 for location in message["checked_locations"]: 115 for location in message["checked_locations"]:
111 _checked_locations.append(int(message["checked_locations"])) 116 _checked_locations.append(int(location))
112 117
113 client_connected.emit(_slot_data) 118 client_connected.emit(_slot_data)
114 119
@@ -158,11 +163,7 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
158 elif cmd == "LocationInfo": 163 elif cmd == "LocationInfo":
159 for loc in message["locations"]: 164 for loc in message["locations"]:
160 location_scout_received.emit( 165 location_scout_received.emit(
161 int(loc["id"]), 166 int(loc["id"]), loc["item"], loc["player"], int(loc["flags"]), int(loc["self"])
162 loc["item"],
163 loc["player"],
164 int(loc["flags"]),
165 int(loc["for_self"])
166 ) 167 )
167 168
168 elif cmd == "AccessibleLocations": 169 elif cmd == "AccessibleLocations":
@@ -187,6 +188,35 @@ func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> vo
187 188
188 keyboard_update_received.emit(updates) 189 keyboard_update_received.emit(updates)
189 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
190 220
191func connectToServer(server, un, pw): 221func connectToServer(server, un, pw):
192 sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) 222 sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}])
@@ -253,6 +283,29 @@ func checkWorldport(port_id):
253 sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}]) 283 sendMessage([{"cmd": "CheckWorldport", "port_id": port_id}])
254 284
255 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
256func sendQuit(): 309func sendQuit():
257 sendMessage([{"cmd": "Quit"}]) 310 sendMessage([{"cmd": "Quit"}])
258 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/gamedata.gd b/apworld/client/gamedata.gd index 334d42a..d7e3136 100644 --- a/apworld/client/gamedata.gd +++ b/apworld/client/gamedata.gd
@@ -15,6 +15,7 @@ var 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 = {} 17var ending_display_name_by_name = {}
18var port_id_by_ap_id = {}
18 19
19var kSYMBOL_ITEMS 20var kSYMBOL_ITEMS
20 21
@@ -72,7 +73,13 @@ func load(data_bytes):
72 73
73 if door.has_ap_id(): 74 if door.has_ap_id():
74 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()
75 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)
76 83
77 for painting in objects.get_paintings(): 84 for painting in objects.get_paintings():
78 var room = objects.get_rooms()[painting.get_room_id()] 85 var room = objects.get_rooms()[painting.get_room_id()]
@@ -93,6 +100,9 @@ func load(data_bytes):
93 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()]
94 map_data[port.get_path()] = port.get_id() 101 map_data[port.get_path()] = port.get_id()
95 102
103 if port.has_ap_id():
104 port_id_by_ap_id[port.get_ap_id()] = port.get_id()
105
96 for progressive in objects.get_progressives(): 106 for progressive in objects.get_progressives():
97 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()
98 108
@@ -166,8 +176,7 @@ func get_door_ap_id(door_id):
166 176
167func get_door_map_name(door_id): 177func get_door_map_name(door_id):
168 var door = objects.get_doors()[door_id] 178 var door = objects.get_doors()[door_id]
169 var room = objects.get_rooms()[door.get_room_id()] 179 var map = objects.get_maps()[door.get_map_id()]
170 var map = objects.get_maps()[room.get_map_id()]
171 return map.get_name() 180 return map.get_name()
172 181
173 182
@@ -216,7 +225,11 @@ func _get_generated_door_location_name(door):
216 if door.get_type() != SCRIPT_proto.DoorType.STANDARD: 225 if door.get_type() != SCRIPT_proto.DoorType.STANDARD:
217 return null 226 return null
218 227
219 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 ):
220 return null 233 return null
221 234
222 if door.get_panels().size() > 4: 235 if door.get_panels().size() > 4:
@@ -227,8 +240,11 @@ func _get_generated_door_location_name(door):
227 var panel = objects.get_panels()[panel_id.get_panel()] 240 var panel = objects.get_panels()[panel_id.get_panel()]
228 var panel_room = objects.get_rooms()[panel.get_room_id()] 241 var panel_room = objects.get_rooms()[panel.get_room_id()]
229 # 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.
230 if not map_areas.has(panel_room.get_panel_display_name()): 243 var panel_display_name = ""
231 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)
232 248
233 if map_areas.size() > 1: 249 if map_areas.size() > 1:
234 return null 250 return null
@@ -264,7 +280,7 @@ func _get_generated_door_location_name(door):
264 280
265 281
266func _get_letter_location_name(letter): 282func _get_letter_location_name(letter):
267 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
268 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]
269 return "%s - %s" % [_get_room_object_map_name(letter), letter_name] 285 return "%s - %s" % [_get_room_object_map_name(letter), letter_name]
270 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 e1f9610..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"))
@@ -82,6 +85,13 @@ func _ready():
82 compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") 85 compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd")
83 global.add_child(compass_overlay_instance) 86 global.add_child(compass_overlay_instance)
84 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
85 var ap = global.get_node("Archipelago") 95 var ap = global.get_node("Archipelago")
86 var gamedata = global.get_node("Gamedata") 96 var gamedata = global.get_node("Gamedata")
87 ap.ap_connected.connect(connectionSuccessful) 97 ap.ap_connected.connect(connectionSuccessful)
@@ -223,11 +233,11 @@ func startGame():
223 233
224 unlocks.resetCollectables() 234 unlocks.resetCollectables()
225 unlocks.resetData() 235 unlocks.resetData()
236 unlocks.loadCollectables()
237 unlocks.loadData()
226 238
227 ap.setup_keys() 239 ap.setup_keys()
228 240
229 unlocks.loadCollectables()
230 unlocks.loadData()
231 unlocks.unlockKey("capslock", 1) 241 unlocks.unlockKey("capslock", 1)
232 242
233 if ap.shuffle_worldports: 243 if ap.shuffle_worldports:
@@ -236,6 +246,7 @@ func startGame():
236 settings.worldport_fades = "never" 246 settings.worldport_fades = "never"
237 247
238 clearResourceCache("res://objects/meshes/gridDoor.tscn") 248 clearResourceCache("res://objects/meshes/gridDoor.tscn")
249 clearResourceCache("res://objects/nodes/allowNumbers.tscn")
239 clearResourceCache("res://objects/nodes/collectable.tscn") 250 clearResourceCache("res://objects/nodes/collectable.tscn")
240 clearResourceCache("res://objects/nodes/door.tscn") 251 clearResourceCache("res://objects/nodes/door.tscn")
241 clearResourceCache("res://objects/nodes/keyHolder.tscn") 252 clearResourceCache("res://objects/nodes/keyHolder.tscn")
@@ -243,6 +254,7 @@ func startGame():
243 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn") 254 clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn")
244 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn") 255 clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn")
245 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn") 256 clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn")
257 clearResourceCache("res://objects/nodes/listeners/unlockReaderListener.tscn")
246 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn") 258 clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn")
247 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn") 259 clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn")
248 clearResourceCache("res://objects/nodes/panel.tscn") 260 clearResourceCache("res://objects/nodes/panel.tscn")
diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 9212233..8c981f9 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd
@@ -29,6 +29,8 @@ var _inverse_item_locks = {}
29var _held_letters = {} 29var _held_letters = {}
30var _letters_setup = false 30var _letters_setup = false
31var _already_connected = false 31var _already_connected = false
32var _ignored_locations = []
33var _map_scripts = {}
32 34
33const kSHUFFLE_LETTERS_VANILLA = 0 35const kSHUFFLE_LETTERS_VANILLA = 0
34const kSHUFFLE_LETTERS_UNLOCKED = 1 36const kSHUFFLE_LETTERS_UNLOCKED = 1
@@ -63,7 +65,11 @@ const kEndingNameByVictoryValue = {
63var apworld_version = [0, 0, 0] 65var apworld_version = [0, 0, 0]
64var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 66var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2
65var daedalus_roof_access = false 67var daedalus_roof_access = false
68var enable_gift_maps = []
69var enable_icarus = false
70var endings_requirement = 0
66var keyholder_sanity = false 71var keyholder_sanity = false
72var masteries_requirement = 0
67var port_pairings = {} 73var port_pairings = {}
68var shuffle_control_center_colors = false 74var shuffle_control_center_colors = false
69var shuffle_doors = false 75var shuffle_doors = false
@@ -75,6 +81,8 @@ var strict_cyan_ending = false
75var strict_purple_ending = false 81var strict_purple_ending = false
76var victory_condition = -1 82var victory_condition = -1
77 83
84var color_by_material_path = {}
85
78signal could_not_connect 86signal could_not_connect
79signal connect_status 87signal connect_status
80signal ap_connected 88signal ap_connected
@@ -112,6 +120,20 @@ func _init():
112 if data.size() > 6: 120 if data.size() > 6:
113 show_minimap = data[6] 121 show_minimap = data[6]
114 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
115 137
116func _ready(): 138func _ready():
117 client = SCRIPT_client.new() 139 client = SCRIPT_client.new()
@@ -124,7 +146,10 @@ func _ready():
124 client.hint_received.connect(_process_hint_received) 146 client.hint_received.connect(_process_hint_received)
125 client.accessible_locations_updated.connect(_on_accessible_locations_updated) 147 client.accessible_locations_updated.connect(_on_accessible_locations_updated)
126 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)
127 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)
128 153
129 client.could_not_connect.connect(_client_could_not_connect) 154 client.could_not_connect.connect(_client_could_not_connect)
130 client.connect_status.connect(_client_connect_status) 155 client.connect_status.connect(_client_connect_status)
@@ -237,6 +262,12 @@ func _process_item(item, amount):
237 if player != null: 262 if player != null:
238 player.evaluate_solvability.emit() 263 player.evaluate_solvability.emit()
239 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
240 # Show a message about the item if it's new. 271 # Show a message about the item if it's new.
241 if int(item["index"]) > _last_new_item: 272 if int(item["index"]) > _last_new_item:
242 _last_new_item = int(item["index"]) 273 _last_new_item = int(item["index"])
@@ -347,7 +378,7 @@ func _on_accessible_locations_updated():
347func _on_checked_locations_updated(): 378func _on_checked_locations_updated():
348 var textclient_node = global.get_node("Textclient") 379 var textclient_node = global.get_node("Textclient")
349 if textclient_node != null: 380 if textclient_node != null:
350 textclient_node.update_locations() 381 textclient_node.update_locations(false)
351 382
352 383
353func _on_checked_worldports_updated(): 384func _on_checked_worldports_updated():
@@ -357,6 +388,34 @@ func _on_checked_worldports_updated():
357 textclient_node.update_worldports() 388 textclient_node.update_worldports()
358 389
359 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
360func _client_could_not_connect(message): 419func _client_could_not_connect(message):
361 could_not_connect.emit(message) 420 could_not_connect.emit(message)
362 421
@@ -405,7 +464,11 @@ func _client_connected(slot_data):
405 # Read slot data. 464 # Read slot data.
406 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) 465 cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0))
407 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))
408 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))
409 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))
410 shuffle_doors = bool(slot_data.get("shuffle_doors", false)) 473 shuffle_doors = bool(slot_data.get("shuffle_doors", false))
411 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) 474 shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false))
@@ -427,7 +490,9 @@ func _client_connected(slot_data):
427 var raw_pp = slot_data.get("port_pairings") 490 var raw_pp = slot_data.get("port_pairings")
428 491
429 for p1 in raw_pp.keys(): 492 for p1 in raw_pp.keys():
430 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 )]
431 496
432 # Set up item locks. 497 # Set up item locks.
433 _item_locks = {} 498 _item_locks = {}
@@ -503,6 +568,9 @@ func start_batching_locations():
503 568
504 569
505func send_location(loc_id): 570func send_location(loc_id):
571 if client._checked_locations.has(loc_id):
572 return
573
506 if _batch_locations: 574 if _batch_locations:
507 _held_locations.append(loc_id) 575 _held_locations.append(loc_id)
508 else: 576 else:
@@ -601,3 +669,49 @@ func _process_key_item(key, level):
601 level += 1 669 level += 1
602 670
603 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 5417a48..5fac9fd 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd
@@ -19,6 +19,13 @@ func _ready():
19 19
20 ap.start_batching_locations() 20 ap.start_batching_locations()
21 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
22 # Set up door locations. 29 # Set up door locations.
23 var map_id = gamedata.map_id_by_name.get(global.map) 30 var map_id = gamedata.map_id_by_name.get(global.map)
24 for door in gamedata.objects.get_doors(): 31 for door in gamedata.objects.get_doors():
@@ -29,8 +36,12 @@ func _ready():
29 continue 36 continue
30 37
31 if ( 38 if (
32 door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY 39 not (door.has_legacy_location() and door.get_legacy_location())
33 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 )
34 ): 45 ):
35 continue 46 continue
36 47
@@ -153,165 +164,6 @@ func _ready():
153 164
154 get_parent().add_child.call_deferred(locationListener) 165 get_parent().add_child.call_deferred(locationListener)
155 166
156 # Block off roof access in Daedalus.
157 if global.map == "daedalus" and not ap.daedalus_roof_access:
158 _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49)
159 _set_up_invis_wall(51.5, 11, -17, 16, 10, 1)
160 _set_up_invis_wall(46, 10, -9.5, 1, 10, 10)
161 _set_up_invis_wall(67.5, 11, 17, 16, 10, 1)
162 _set_up_invis_wall(50.5, 11, 14, 10, 10, 1)
163 _set_up_invis_wall(39, 10, 18.5, 1, 10, 22)
164 _set_up_invis_wall(20, 15, 18.5, 1, 10, 16)
165 _set_up_invis_wall(11.5, 15, 3, 32, 10, 1)
166 _set_up_invis_wall(11.5, 16, -20, 14, 20, 1)
167 _set_up_invis_wall(14, 16, -26.5, 1, 20, 4)
168 _set_up_invis_wall(28.5, 20.5, -26.5, 1, 15, 25)
169 _set_up_invis_wall(40.5, 20.5, -11, 30, 15, 1)
170 _set_up_invis_wall(50.5, 15, 5.5, 7, 10, 1)
171 _set_up_invis_wall(83.5, 33.5, 5.5, 1, 7, 11)
172 _set_up_invis_wall(83.5, 33.5, -5.5, 1, 7, 11)
173
174 var warp_exit_prefab = preload("res://objects/nodes/exit.tscn")
175 var warp_exit = warp_exit_prefab.instantiate()
176 warp_exit.name = "roof_access_blocker_warp_exit"
177 warp_exit.position = Vector3(58, 10, 0)
178 warp_exit.rotation_degrees.y = 90
179 get_parent().add_child.call_deferred(warp_exit)
180
181 var warp_enter_prefab = preload("res://objects/nodes/teleportAuto.tscn")
182 var warp_enter = warp_enter_prefab.instantiate()
183 warp_enter.target = warp_exit
184 warp_enter.position = Vector3(76.5, 30, 1)
185 warp_enter.scale = Vector3(4, 1.5, 1)
186 warp_enter.rotation_degrees.y = 90
187 get_parent().add_child.call_deferred(warp_enter)
188
189 if global.map == "the_entry":
190 # Remove door behind X1.
191 var door_node = get_tree().get_root().get_node("/root/scene/Components/Doors/exit_1")
192 door_node.handleTriggered()
193
194 # Display win condition.
195 var sign_prefab = preload("res://objects/nodes/sign.tscn")
196 var sign1 = sign_prefab.instantiate()
197 sign1.position = Vector3(-7, 5, -15.01)
198 sign1.text = "victory"
199 get_parent().add_child.call_deferred(sign1)
200
201 var sign2 = sign_prefab.instantiate()
202 sign2.position = Vector3(-7, 4, -15.01)
203 sign2.text = "%s ending" % ap.kEndingNameByVictoryValue.get(ap.victory_condition, "?")
204
205 var sign2_color = ap.kEndingNameByVictoryValue.get(ap.victory_condition, "coral").to_lower()
206 if sign2_color == "white":
207 sign2_color = "silver"
208
209 sign2.material = load("res://assets/materials/%s.material" % sign2_color)
210 get_parent().add_child.call_deferred(sign2)
211
212 # Add the strict purple ending validation.
213 if global.map == "the_sun_temple" and ap.strict_purple_ending:
214 var panel_prefab = preload("res://objects/nodes/panel.tscn")
215 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
216 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
217
218 var previous_panel = null
219 var next_y = -100
220 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
221 for word in words:
222 var panel = panel_prefab.instantiate()
223 panel.position = Vector3(0, next_y, 0)
224 next_y -= 10
225 panel.clue = word
226 panel.symbol = ""
227 panel.answer = word
228 panel.name = "EndCheck_%s" % word
229
230 var tpl = tpl_prefab.instantiate()
231 tpl.teleport_point = Vector3(0, 1, 0)
232 tpl.teleport_rotate = Vector3(-45, 180, 0)
233 tpl.target_path = panel
234 tpl.name = "Teleport"
235
236 if previous_panel == null:
237 tpl.senders.append(NodePath("/root/scene/Panels/End/panel_24"))
238 else:
239 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
240
241 var reversing = reverse_prefab.instantiate()
242 reversing.senders.append(NodePath(".."))
243 reversing.name = "Reversing"
244 tpl.senders.append(NodePath("../Reversing"))
245
246 panel.add_child.call_deferred(tpl)
247 panel.add_child.call_deferred(reversing)
248 get_parent().get_node("Panels").add_child.call_deferred(panel)
249
250 previous_panel = panel
251
252 # Duplicate the doors that usually wait on EQUINOX. We can't set the senders
253 # here for some reason so we actually set them in the door ready function.
254 var endplat = get_node("/root/scene/Components/Doors/EndPlatform")
255 var endplat2 = endplat.duplicate()
256 endplat2.name = "spe_EndPlatform"
257 endplat.get_parent().add_child.call_deferred(endplat2)
258 endplat.queue_free()
259
260 var entry2 = get_node("/root/scene/Components/Doors/entry_2")
261 var entry22 = entry2.duplicate()
262 entry22.name = "spe_entry_2"
263 entry2.get_parent().add_child.call_deferred(entry22)
264 entry2.queue_free()
265
266 # Add the strict cyan ending validation.
267 if global.map == "the_parthenon" and ap.strict_cyan_ending:
268 var panel_prefab = preload("res://objects/nodes/panel.tscn")
269 var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn")
270 var reverse_prefab = preload("res://objects/nodes/listeners/reversingListener.tscn")
271
272 var previous_panel = null
273 var next_y = -100
274 var words = ["quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]
275 for word in words:
276 var panel = panel_prefab.instantiate()
277 panel.position = Vector3(0, next_y, 0)
278 next_y -= 10
279 panel.clue = word
280 panel.symbol = "."
281 panel.answer = "%s%s" % [word, word]
282 panel.name = "EndCheck_%s" % word
283
284 var tpl = tpl_prefab.instantiate()
285 tpl.teleport_point = Vector3(0, 1, -11)
286 tpl.teleport_rotate = Vector3(-45, 0, 0)
287 tpl.target_path = panel
288 tpl.name = "Teleport"
289
290 if previous_panel == null:
291 tpl.senderGroup.append(NodePath("/root/scene/Panels/Rulers"))
292 else:
293 tpl.senders.append(NodePath("../../%s" % previous_panel.name))
294
295 var reversing = reverse_prefab.instantiate()
296 reversing.senders.append(NodePath(".."))
297 reversing.name = "Reversing"
298 tpl.senders.append(NodePath("../Reversing"))
299
300 panel.add_child.call_deferred(tpl)
301 panel.add_child.call_deferred(reversing)
302 get_parent().get_node("Panels").add_child.call_deferred(panel)
303
304 previous_panel = panel
305
306 # Duplicate the door that usually waits on the rulers. We can't set the
307 # senders here for some reason so we actually set them in the door ready
308 # function.
309 var entry1 = get_node("/root/scene/Components/Doors/entry_1")
310 var entry12 = entry1.duplicate()
311 entry12.name = "spe_entry_1"
312 entry1.get_parent().add_child.call_deferred(entry12)
313 entry1.queue_free()
314
315 var minimap = ap.SCRIPT_minimap.new() 167 var minimap = ap.SCRIPT_minimap.new()
316 minimap.name = "Minimap" 168 minimap.name = "Minimap"
317 minimap.visible = ap.show_minimap 169 minimap.visible = ap.show_minimap
@@ -325,22 +177,5 @@ func _ready():
325 ap.stop_batching_locations() 177 ap.stop_batching_locations()
326 178
327 179
328func _set_up_invis_wall(x, y, z, sx, sy, sz):
329 var prefab = preload("res://objects/nodes/block.tscn")
330 var newwall = prefab.instantiate()
331 newwall.position.x = x
332 newwall.position.y = y
333 newwall.position.z = z
334 newwall.scale.x = sz
335 newwall.scale.y = sy
336 newwall.scale.z = sx
337 newwall.set_surface_override_material(0, preload("res://assets/materials/blackMatte.material"))
338 newwall.visibility_range_end = 3
339 newwall.visibility_range_end_margin = 1
340 newwall.visibility_range_fade_mode = RenderingServer.VISIBILITY_RANGE_FADE_SELF
341 newwall.skeleton = ".."
342 get_parent().add_child.call_deferred(newwall)
343
344
345func _process(_dt): 180func _process(_dt):
346 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 530eddb..ce28a3a 100644 --- a/apworld/client/textclient.gd +++ b/apworld/client/textclient.gd
@@ -4,7 +4,6 @@ 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
@@ -12,11 +11,23 @@ var location_texture
12var worldport_texture 11var worldport_texture
13var goal_texture 12var goal_texture
14 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
21
15var worldports_tab 22var worldports_tab
16var worldports_tree 23var worldports_tree
17var port_tree_item_by_map = {} 24var port_tree_item_by_map = {}
18var port_tree_item_by_map_port = {} 25var port_tree_item_by_map_port = {}
19 26
27const kLocation = 0
28const kWorldport = 1
29const kGoal = 2
30
20 31
21func _ready(): 32func _ready():
22 process_mode = ProcessMode.PROCESS_MODE_ALWAYS 33 process_mode = ProcessMode.PROCESS_MODE_ALWAYS
@@ -61,7 +72,7 @@ func _ready():
61 label.size_flags_horizontal = Control.SIZE_EXPAND_FILL 72 label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
62 label.size_flags_vertical = Control.SIZE_EXPAND_FILL 73 label.size_flags_vertical = Control.SIZE_EXPAND_FILL
63 label.push_font(preload("res://assets/fonts/Lingo2.ttf")) 74 label.push_font(preload("res://assets/fonts/Lingo2.ttf"))
64 label.push_font_size(36) 75 label.push_font_size(30)
65 76
66 var entry_style = StyleBoxFlat.new() 77 var entry_style = StyleBoxFlat.new()
67 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)
@@ -89,8 +100,20 @@ func _ready():
89 tracker_margins.add_theme_constant_override("margin_bottom", 60) 100 tracker_margins.add_theme_constant_override("margin_bottom", 60)
90 tabs.add_child(tracker_margins) 101 tabs.add_child(tracker_margins)
91 102
92 tracker_label = RichTextLabel.new() 103 tracker_tree = Tree.new()
93 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)
94 117
95 worldports_tab = MarginContainer.new() 118 worldports_tab = MarginContainer.new()
96 worldports_tab.name = "Worldports" 119 worldports_tab.name = "Worldports"
@@ -167,14 +190,10 @@ func text_entered(text):
167 ap.client.say(cmd) 190 ap.client.say(cmd)
168 191
169 192
170func update_locations(): 193func update_locations(reset_locations = true):
171 var ap = global.get_node("Archipelago") 194 var ap = global.get_node("Archipelago")
172 var gamedata = global.get_node("Gamedata") 195 var gamedata = global.get_node("Gamedata")
173 196
174 tracker_label.clear()
175 tracker_label.push_font(preload("res://assets/fonts/Lingo2.ttf"))
176 tracker_label.push_font_size(24)
177
178 locations_overlay.clear() 197 locations_overlay.clear()
179 locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf")) 198 locations_overlay.push_font(preload("res://assets/fonts/Lingo2.ttf"))
180 locations_overlay.push_font_size(24) 199 locations_overlay.push_font_size(24)
@@ -182,58 +201,226 @@ func update_locations():
182 locations_overlay.push_outline_color(Color(0, 0, 0, 1)) 201 locations_overlay.push_outline_color(Color(0, 0, 0, 1))
183 locations_overlay.push_outline_size(2) 202 locations_overlay.push_outline_size(2)
184 203
185 const kLocation = 0 204 var locations = []
186 const kWorldport = 1
187 const kGoal = 2
188
189 var location_names = []
190 var type_by_name = {}
191 for location_id in ap.client._accessible_locations: 205 for location_id in ap.client._accessible_locations:
192 if not ap.client._checked_locations.has(location_id): 206 if not ap.client._checked_locations.has(location_id):
193 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)")
194 location_names.append(location_name) 208 (
195 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 )
196 220
197 for port_id in ap.client._accessible_worldports: 221 for port_id in ap.client._accessible_worldports:
198 if not ap.client._checked_worldports.has(port_id): 222 if not ap.client._checked_worldports.has(port_id):
199 var port_name = gamedata.get_worldport_display_name(port_id) 223 var port_name = gamedata.get_worldport_display_name(port_id)
200 location_names.append(port_name) 224 (
201 type_by_name[port_name] = kWorldport 225 locations
202 226 . append(
203 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)
204 238
205 if ap.client._goal_accessible: 239 if ap.client._goal_accessible:
206 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[ 240 var location_name = gamedata.ending_display_name_by_name[ap.kEndingNameByVictoryValue[
207 ap.victory_condition 241 ap.victory_condition
208 ]] 242 ]]
209 location_names.push_front(location_name) 243 (
210 type_by_name[location_name] = kGoal 244 locations
245 . push_front(
246 {
247 "name": location_name,
248 "type": kGoal,
249 "ignored": false,
250 "hint": false,
251 }
252 )
253 )
211 254
212 var count = 0 255 var count = 0
213 for location_name in location_names: 256 for location in locations:
214 tracker_label.append_text("[p]%s[/p]" % location_name) 257 if count < 18 and not location["ignored"]:
215 if count < 18:
216 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT) 258 locations_overlay.push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT)
217 locations_overlay.append_text(location_name) 259 if location["hint"]:
260 locations_overlay.push_color(Color("#fafad2"))
261 locations_overlay.append_text(location["name"])
218 locations_overlay.append_text(" ") 262 locations_overlay.append_text(" ")
219 if type_by_name[location_name] == kLocation: 263 if location["type"] == kLocation:
220 locations_overlay.add_image(location_texture) 264 locations_overlay.add_image(location_texture)
221 elif type_by_name[location_name] == kWorldport: 265 elif location["type"] == kWorldport:
222 locations_overlay.add_image(worldport_texture) 266 locations_overlay.add_image(worldport_texture)
223 elif type_by_name[location_name] == kGoal: 267 elif location["type"] == kGoal:
224 locations_overlay.add_image(goal_texture) 268 locations_overlay.add_image(goal_texture)
269 if location["hint"]:
270 locations_overlay.pop()
225 locations_overlay.pop() 271 locations_overlay.pop()
226 count += 1 272 count += 1
227 273
228 if count > 18: 274 if count > 18:
229 locations_overlay.append_text("[p align=right][lb]...[rb][/p]") 275 locations_overlay.append_text("[p align=right][lb]...[rb][/p]")
230 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
231 368
232func update_locations_visibility(): 369func update_locations_visibility():
233 var ap = global.get_node("Archipelago") 370 var ap = global.get_node("Archipelago")
234 locations_overlay.visible = ap.show_locations 371 locations_overlay.visible = ap.show_locations
235 372
236 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
237func setup_worldports(): 424func setup_worldports():
238 tabs.set_tab_hidden(2, false) 425 tabs.set_tab_hidden(2, false)
239 426
@@ -308,3 +495,14 @@ func reset():
308 port_tree_item_by_map.clear() 495 port_tree_item_by_map.clear()
309 port_tree_item_by_map_port.clear() 496 port_tree_item_by_map_port.clear()
310 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()