diff options
Diffstat (limited to 'apworld')
-rw-r--r-- | apworld/__init__.py | 1 | ||||
-rw-r--r-- | apworld/client/manager.gd | 2 | ||||
-rw-r--r-- | apworld/client/player.gd | 423 | ||||
-rw-r--r-- | apworld/options.py | 28 | ||||
-rw-r--r-- | apworld/player_logic.py | 7 |
5 files changed, 322 insertions, 139 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index 6540b08..4ebf845 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py | |||
@@ -130,6 +130,7 @@ class Lingo2World(World): | |||
130 | slot_options = [ | 130 | slot_options = [ |
131 | "cyan_door_behavior", | 131 | "cyan_door_behavior", |
132 | "daedalus_roof_access", | 132 | "daedalus_roof_access", |
133 | "enable_gift_maps", | ||
133 | "enable_icarus", | 134 | "enable_icarus", |
134 | "keyholder_sanity", | 135 | "keyholder_sanity", |
135 | "shuffle_control_center_colors", | 136 | "shuffle_control_center_colors", |
diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index a17bee8..91797b2 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd | |||
@@ -63,6 +63,7 @@ const kEndingNameByVictoryValue = { | |||
63 | var apworld_version = [0, 0, 0] | 63 | var apworld_version = [0, 0, 0] |
64 | var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 | 64 | var cyan_door_behavior = kCYAN_DOOR_BEHAVIOR_H2 |
65 | var daedalus_roof_access = false | 65 | var daedalus_roof_access = false |
66 | var enable_gift_maps = [] | ||
66 | var keyholder_sanity = false | 67 | var keyholder_sanity = false |
67 | var port_pairings = {} | 68 | var port_pairings = {} |
68 | var shuffle_control_center_colors = false | 69 | var shuffle_control_center_colors = false |
@@ -439,6 +440,7 @@ func _client_connected(slot_data): | |||
439 | # Read slot data. | 440 | # Read slot data. |
440 | cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) | 441 | cyan_door_behavior = int(slot_data.get("cyan_door_behavior", 0)) |
441 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) | 442 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) |
443 | enable_gift_maps = slot_data.get("enable_gift_maps", []) | ||
442 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | 444 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) |
443 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | 445 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) |
444 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | 446 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) |
diff --git a/apworld/client/player.gd b/apworld/client/player.gd index 1330e24..9aac5a6 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd | |||
@@ -19,141 +19,6 @@ func _ready(): | |||
19 | 19 | ||
20 | ap.start_batching_locations() | 20 | ap.start_batching_locations() |
21 | 21 | ||
22 | # Set up door locations. | ||
23 | var map_id = gamedata.map_id_by_name.get(global.map) | ||
24 | for door in gamedata.objects.get_doors(): | ||
25 | if door.get_map_id() != map_id: | ||
26 | continue | ||
27 | |||
28 | if not door.has_ap_id(): | ||
29 | continue | ||
30 | |||
31 | if ( | ||
32 | door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
33 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING | ||
34 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR | ||
35 | ): | ||
36 | continue | ||
37 | |||
38 | var locationListener = ap.SCRIPT_locationListener.new() | ||
39 | locationListener.location_id = door.get_ap_id() | ||
40 | locationListener.name = "locationListener_%d" % door.get_ap_id() | ||
41 | |||
42 | for panel_ref in door.get_panels(): | ||
43 | var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()] | ||
44 | var panel_path = panel_data.get_path() | ||
45 | |||
46 | if panel_ref.has_answer(): | ||
47 | for proxy in panel_data.get_proxies(): | ||
48 | if proxy.get_answer() == panel_ref.get_answer(): | ||
49 | panel_path = proxy.get_path() | ||
50 | break | ||
51 | |||
52 | locationListener.senders.append(NodePath("/root/scene/" + panel_path)) | ||
53 | |||
54 | for keyholder_ref in door.get_keyholders(): | ||
55 | var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()] | ||
56 | |||
57 | var khl = khl_script.new() | ||
58 | khl.name = ( | ||
59 | "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()] | ||
60 | ) | ||
61 | khl.answer = keyholder_ref.get_key() | ||
62 | khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path())) | ||
63 | get_parent().add_child.call_deferred(khl) | ||
64 | |||
65 | locationListener.senders.append(NodePath("../" + khl.name)) | ||
66 | |||
67 | for sender in door.get_senders(): | ||
68 | locationListener.senders.append(NodePath("/root/scene/" + sender)) | ||
69 | |||
70 | if door.has_complete_at(): | ||
71 | locationListener.complete_at = door.get_complete_at() | ||
72 | |||
73 | get_parent().add_child.call_deferred(locationListener) | ||
74 | |||
75 | # Set up letter locations. | ||
76 | for letter in gamedata.objects.get_letters(): | ||
77 | var room = gamedata.objects.get_rooms()[letter.get_room_id()] | ||
78 | if room.get_map_id() != map_id: | ||
79 | continue | ||
80 | |||
81 | var locationListener = ap.SCRIPT_locationListener.new() | ||
82 | locationListener.location_id = letter.get_ap_id() | ||
83 | locationListener.name = "locationListener_%d" % letter.get_ap_id() | ||
84 | locationListener.senders.append(NodePath("/root/scene/" + letter.get_path())) | ||
85 | |||
86 | get_parent().add_child.call_deferred(locationListener) | ||
87 | |||
88 | if ( | ||
89 | ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2()) | ||
90 | != ap.kLETTER_BEHAVIOR_VANILLA | ||
91 | ): | ||
92 | var scout = ap.scout_location(letter.get_ap_id()) | ||
93 | if scout != null and not (scout["for_self"] and scout["flags"] & 4 != 0): | ||
94 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( | ||
95 | letter.get_path() | ||
96 | ) | ||
97 | if collectable != null: | ||
98 | collectable.setScoutedText.call_deferred(scout["item"]) | ||
99 | |||
100 | # Set up mastery locations. | ||
101 | for mastery in gamedata.objects.get_masteries(): | ||
102 | var room = gamedata.objects.get_rooms()[mastery.get_room_id()] | ||
103 | if room.get_map_id() != map_id: | ||
104 | continue | ||
105 | |||
106 | var locationListener = ap.SCRIPT_locationListener.new() | ||
107 | locationListener.location_id = mastery.get_ap_id() | ||
108 | locationListener.name = "locationListener_%d" % mastery.get_ap_id() | ||
109 | locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path())) | ||
110 | |||
111 | get_parent().add_child.call_deferred(locationListener) | ||
112 | |||
113 | # Set up ending locations. | ||
114 | for ending in gamedata.objects.get_endings(): | ||
115 | var room = gamedata.objects.get_rooms()[ending.get_room_id()] | ||
116 | if room.get_map_id() != map_id: | ||
117 | continue | ||
118 | |||
119 | var locationListener = ap.SCRIPT_locationListener.new() | ||
120 | locationListener.location_id = ending.get_ap_id() | ||
121 | locationListener.name = "locationListener_%d" % ending.get_ap_id() | ||
122 | locationListener.senders.append(NodePath("/root/scene/" + ending.get_path())) | ||
123 | |||
124 | get_parent().add_child.call_deferred(locationListener) | ||
125 | |||
126 | if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): | ||
127 | var victoryListener = ap.SCRIPT_victoryListener.new() | ||
128 | victoryListener.name = "victoryListener" | ||
129 | victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) | ||
130 | |||
131 | get_parent().add_child.call_deferred(victoryListener) | ||
132 | |||
133 | # Set up keyholder locations, in keyholder sanity. | ||
134 | if ap.keyholder_sanity: | ||
135 | for keyholder in gamedata.objects.get_keyholders(): | ||
136 | if not keyholder.has_key(): | ||
137 | continue | ||
138 | |||
139 | var room = gamedata.objects.get_rooms()[keyholder.get_room_id()] | ||
140 | if room.get_map_id() != map_id: | ||
141 | continue | ||
142 | |||
143 | var locationListener = ap.SCRIPT_locationListener.new() | ||
144 | locationListener.location_id = keyholder.get_ap_id() | ||
145 | locationListener.name = "locationListener_%d" % keyholder.get_ap_id() | ||
146 | |||
147 | var khl = khl_script.new() | ||
148 | khl.name = "location_%d_keyholder" % keyholder.get_ap_id() | ||
149 | khl.answer = keyholder.get_key() | ||
150 | khl.senders.append(NodePath("/root/scene/" + keyholder.get_path())) | ||
151 | get_parent().add_child.call_deferred(khl) | ||
152 | |||
153 | locationListener.senders.append(NodePath("../" + khl.name)) | ||
154 | |||
155 | get_parent().add_child.call_deferred(locationListener) | ||
156 | |||
157 | # Block off roof access in Daedalus. | 22 | # Block off roof access in Daedalus. |
158 | if global.map == "daedalus" and not ap.daedalus_roof_access: | 23 | if global.map == "daedalus" and not ap.daedalus_roof_access: |
159 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) | 24 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) |
@@ -210,6 +75,84 @@ func _ready(): | |||
210 | sign2.material = load("res://assets/materials/%s.material" % sign2_color) | 75 | sign2.material = load("res://assets/materials/%s.material" % sign2_color) |
211 | get_parent().add_child.call_deferred(sign2) | 76 | get_parent().add_child.call_deferred(sign2) |
212 | 77 | ||
78 | # Add the gift map entry panel if needed. | ||
79 | if not ap.enable_gift_maps.is_empty(): | ||
80 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | ||
81 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
82 | var wpl_prefab = preload("res://objects/nodes/listeners/worldportListener.tscn") | ||
83 | |||
84 | var giftmap_parent = Node.new() | ||
85 | giftmap_parent.name = "GiftMapEntrance" | ||
86 | get_node("/root/scene/Components").add_child.call_deferred(giftmap_parent) | ||
87 | |||
88 | var symbolless_player = "" | ||
89 | for i in range(ap.client.ap_user.length()): | ||
90 | if "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".contains( | ||
91 | ap.client.ap_user[i] | ||
92 | ): | ||
93 | symbolless_player = symbolless_player + ap.client.ap_user[i].to_lower() | ||
94 | |||
95 | var giftmap_panel = panel_prefab.instantiate() | ||
96 | giftmap_panel.name = "Panel" | ||
97 | giftmap_panel.position = Vector3(33.5, -190, 5.5) | ||
98 | giftmap_panel.rotation_degrees = Vector3(-45, 0, 0) | ||
99 | giftmap_panel.clue = "player" | ||
100 | giftmap_panel.answer = symbolless_player | ||
101 | |||
102 | if ap.enable_gift_maps.has("The Advanced"): | ||
103 | var icely_panel = panel_prefab.instantiate() | ||
104 | icely_panel.name = "IcelyPanel" | ||
105 | icely_panel.answer = "icely" | ||
106 | icely_panel.position = Vector3(33.5, -200, 5.5) | ||
107 | giftmap_panel.proxies.append(NodePath("../IcelyPanel")) | ||
108 | giftmap_parent.add_child.call_deferred(icely_panel) | ||
109 | |||
110 | var icely_wpl = wpl_prefab.instantiate() | ||
111 | icely_wpl.name = "IcelyWpl" | ||
112 | icely_wpl.exit = "the_advanced" | ||
113 | icely_wpl.senders.append(NodePath("../IcelyPanel")) | ||
114 | giftmap_parent.add_child.call_deferred(icely_wpl) | ||
115 | |||
116 | if ap.enable_gift_maps.has("The Charismatic"): | ||
117 | var souvey_panel = panel_prefab.instantiate() | ||
118 | souvey_panel.name = "SouveyPanel" | ||
119 | souvey_panel.answer = "souvey" | ||
120 | souvey_panel.position = Vector3(33.5, -210, 5.5) | ||
121 | giftmap_panel.proxies.append(NodePath("../SouveyPanel")) | ||
122 | giftmap_parent.add_child.call_deferred(souvey_panel) | ||
123 | |||
124 | var souvey_wpl = wpl_prefab.instantiate() | ||
125 | souvey_wpl.name = "SouveyWpl" | ||
126 | souvey_wpl.exit = "the_charismatic" | ||
127 | souvey_wpl.senders.append(NodePath("../SouveyPanel")) | ||
128 | giftmap_parent.add_child.call_deferred(souvey_wpl) | ||
129 | |||
130 | if ap.enable_gift_maps.has("The Crystalline"): | ||
131 | var q_panel = panel_prefab.instantiate() | ||
132 | q_panel.name = "QPanel" | ||
133 | q_panel.answer = "q" | ||
134 | q_panel.position = Vector3(33.5, -220, 5.5) | ||
135 | giftmap_panel.proxies.append(NodePath("../QPanel")) | ||
136 | giftmap_parent.add_child.call_deferred(q_panel) | ||
137 | |||
138 | var q_wpl = wpl_prefab.instantiate() | ||
139 | q_wpl.name = "QWpl" | ||
140 | q_wpl.exit = "the_crystalline" | ||
141 | q_wpl.senders.append(NodePath("../QPanel")) | ||
142 | giftmap_parent.add_child.call_deferred(q_wpl) | ||
143 | |||
144 | giftmap_parent.add_child.call_deferred(giftmap_panel) | ||
145 | |||
146 | var giftmap_tpl = tpl_prefab.instantiate() | ||
147 | giftmap_tpl.name = "PanelTeleporter" | ||
148 | giftmap_tpl.teleport_point = Vector3(33.5, 1, 5.5) | ||
149 | giftmap_tpl.teleport_rotate = Vector3(-45, 0, 0) | ||
150 | giftmap_tpl.target_path = giftmap_panel | ||
151 | giftmap_tpl.senders.append( | ||
152 | NodePath("/root/scene/Components/Listeners/unlockReaderListenerDoubles") | ||
153 | ) | ||
154 | giftmap_parent.add_child.call_deferred(giftmap_tpl) | ||
155 | |||
213 | # Add the strict purple ending validation. | 156 | # Add the strict purple ending validation. |
214 | if global.map == "the_sun_temple" and ap.strict_purple_ending: | 157 | if global.map == "the_sun_temple" and ap.strict_purple_ending: |
215 | var panel_prefab = preload("res://objects/nodes/panel.tscn") | 158 | var panel_prefab = preload("res://objects/nodes/panel.tscn") |
@@ -318,8 +261,218 @@ func _ready(): | |||
318 | var rte_trigger = get_node("/root/scene/Components/Warps/triggerArea") | 261 | var rte_trigger = get_node("/root/scene/Components/Warps/triggerArea") |
319 | rte_trigger.position.z = 0 | 262 | rte_trigger.position.z = 0 |
320 | 263 | ||
264 | # Add the mastery to The Advanced. | ||
265 | if global.map == "the_advanced": | ||
266 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
267 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
268 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
269 | |||
270 | var mastery = collectable_prefab.instantiate() | ||
271 | mastery.name = "collectable" | ||
272 | mastery.position = Vector3(0, -200, -5) | ||
273 | mastery.unlock_type = "smiley" | ||
274 | mastery.material_override = load("res://assets/materials/gold.material") | ||
275 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
276 | |||
277 | var tpl = tpl_prefab.instantiate() | ||
278 | tpl.teleport_point = Vector3(0, 2, -5) | ||
279 | tpl.teleport_rotate = Vector3(0, 0, 0) | ||
280 | tpl.target_path = mastery | ||
281 | tpl.name = "Teleport" | ||
282 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_29")) | ||
283 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_30")) | ||
284 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) | ||
285 | mastery.add_child.call_deferred(tpl) | ||
286 | |||
287 | var saver = saver_prefab.instantiate() | ||
288 | saver.name = "saver_collectables" | ||
289 | saver.type = "collectables" | ||
290 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
291 | get_node("/root/scene").add_child.call_deferred(saver) | ||
292 | |||
293 | # Add the mastery to The Charismatic. | ||
294 | if global.map == "the_charismatic": | ||
295 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
296 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
297 | |||
298 | var mastery = collectable_prefab.instantiate() | ||
299 | mastery.name = "collectable" | ||
300 | mastery.position = Vector3(-17, 2, -29) | ||
301 | mastery.rotation_degrees = Vector3(0, 45, 0) | ||
302 | mastery.unlock_type = "smiley" | ||
303 | mastery.material_override = load("res://assets/materials/gold.material") | ||
304 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
305 | |||
306 | var saver = saver_prefab.instantiate() | ||
307 | saver.name = "saver_collectables" | ||
308 | saver.type = "collectables" | ||
309 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
310 | get_node("/root/scene").add_child.call_deferred(saver) | ||
311 | |||
312 | # Add the mastery to The Crystalline. | ||
313 | if global.map == "the_crystalline": | ||
314 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
315 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
316 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | ||
317 | |||
318 | var mastery = collectable_prefab.instantiate() | ||
319 | mastery.name = "collectable" | ||
320 | mastery.position = Vector3(0, 13, 37) | ||
321 | mastery.unlock_type = "smiley" | ||
322 | mastery.material_override = load("res://assets/materials/gold.material") | ||
323 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
324 | |||
325 | var tpl = tpl_prefab.instantiate() | ||
326 | tpl.teleport_point = Vector3(0, 11.5, -20) | ||
327 | tpl.teleport_rotate = Vector3(0, 0, 180) | ||
328 | tpl.target_path = mastery | ||
329 | tpl.name = "Teleport" | ||
330 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) | ||
331 | mastery.add_child.call_deferred(tpl) | ||
332 | |||
333 | var saver = saver_prefab.instantiate() | ||
334 | saver.name = "saver_collectables" | ||
335 | saver.type = "collectables" | ||
336 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
337 | get_node("/root/scene").add_child.call_deferred(saver) | ||
338 | |||
321 | ap.update_job_well_done_sign() | 339 | ap.update_job_well_done_sign() |
322 | 340 | ||
341 | # Set up door locations. | ||
342 | var map_id = gamedata.map_id_by_name.get(global.map) | ||
343 | for door in gamedata.objects.get_doors(): | ||
344 | if door.get_map_id() != map_id: | ||
345 | continue | ||
346 | |||
347 | if not door.has_ap_id(): | ||
348 | continue | ||
349 | |||
350 | if ( | ||
351 | door.get_type() == gamedata.SCRIPT_proto.DoorType.ITEM_ONLY | ||
352 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.GALLERY_PAINTING | ||
353 | or door.get_type() == gamedata.SCRIPT_proto.DoorType.CONTROL_CENTER_COLOR | ||
354 | ): | ||
355 | continue | ||
356 | |||
357 | var locationListener = ap.SCRIPT_locationListener.new() | ||
358 | locationListener.location_id = door.get_ap_id() | ||
359 | locationListener.name = "locationListener_%d" % door.get_ap_id() | ||
360 | |||
361 | for panel_ref in door.get_panels(): | ||
362 | var panel_data = gamedata.objects.get_panels()[panel_ref.get_panel()] | ||
363 | var panel_path = panel_data.get_path() | ||
364 | |||
365 | if panel_ref.has_answer(): | ||
366 | for proxy in panel_data.get_proxies(): | ||
367 | if proxy.get_answer() == panel_ref.get_answer(): | ||
368 | panel_path = proxy.get_path() | ||
369 | break | ||
370 | |||
371 | locationListener.senders.append(NodePath("/root/scene/" + panel_path)) | ||
372 | |||
373 | for keyholder_ref in door.get_keyholders(): | ||
374 | var keyholder_data = gamedata.objects.get_keyholders()[keyholder_ref.get_keyholder()] | ||
375 | |||
376 | var khl = khl_script.new() | ||
377 | khl.name = ( | ||
378 | "location_%d_keyholder_%d" % [door.get_ap_id(), keyholder_ref.get_keyholder()] | ||
379 | ) | ||
380 | khl.answer = keyholder_ref.get_key() | ||
381 | khl.senders.append(NodePath("/root/scene/" + keyholder_data.get_path())) | ||
382 | get_parent().add_child.call_deferred(khl) | ||
383 | |||
384 | locationListener.senders.append(NodePath("../" + khl.name)) | ||
385 | |||
386 | for sender in door.get_senders(): | ||
387 | locationListener.senders.append(NodePath("/root/scene/" + sender)) | ||
388 | |||
389 | if door.has_complete_at(): | ||
390 | locationListener.complete_at = door.get_complete_at() | ||
391 | |||
392 | get_parent().add_child.call_deferred(locationListener) | ||
393 | |||
394 | # Set up letter locations. | ||
395 | for letter in gamedata.objects.get_letters(): | ||
396 | var room = gamedata.objects.get_rooms()[letter.get_room_id()] | ||
397 | if room.get_map_id() != map_id: | ||
398 | continue | ||
399 | |||
400 | var locationListener = ap.SCRIPT_locationListener.new() | ||
401 | locationListener.location_id = letter.get_ap_id() | ||
402 | locationListener.name = "locationListener_%d" % letter.get_ap_id() | ||
403 | locationListener.senders.append(NodePath("/root/scene/" + letter.get_path())) | ||
404 | |||
405 | get_parent().add_child.call_deferred(locationListener) | ||
406 | |||
407 | if ( | ||
408 | ap.get_letter_behavior(letter.get_key(), letter.has_level2() and letter.get_level2()) | ||
409 | != ap.kLETTER_BEHAVIOR_VANILLA | ||
410 | ): | ||
411 | var scout = ap.scout_location(letter.get_ap_id()) | ||
412 | if scout != null and not (scout["for_self"] and scout["flags"] & 4 != 0): | ||
413 | var collectable = get_tree().get_root().get_node("scene").get_node_or_null( | ||
414 | letter.get_path() | ||
415 | ) | ||
416 | if collectable != null: | ||
417 | collectable.setScoutedText.call_deferred(scout["item"]) | ||
418 | |||
419 | # Set up mastery locations. | ||
420 | for mastery in gamedata.objects.get_masteries(): | ||
421 | var room = gamedata.objects.get_rooms()[mastery.get_room_id()] | ||
422 | if room.get_map_id() != map_id: | ||
423 | continue | ||
424 | |||
425 | var locationListener = ap.SCRIPT_locationListener.new() | ||
426 | locationListener.location_id = mastery.get_ap_id() | ||
427 | locationListener.name = "locationListener_%d" % mastery.get_ap_id() | ||
428 | locationListener.senders.append(NodePath("/root/scene/" + mastery.get_path())) | ||
429 | |||
430 | get_parent().add_child.call_deferred(locationListener) | ||
431 | |||
432 | # Set up ending locations. | ||
433 | for ending in gamedata.objects.get_endings(): | ||
434 | var room = gamedata.objects.get_rooms()[ending.get_room_id()] | ||
435 | if room.get_map_id() != map_id: | ||
436 | continue | ||
437 | |||
438 | var locationListener = ap.SCRIPT_locationListener.new() | ||
439 | locationListener.location_id = ending.get_ap_id() | ||
440 | locationListener.name = "locationListener_%d" % ending.get_ap_id() | ||
441 | locationListener.senders.append(NodePath("/root/scene/" + ending.get_path())) | ||
442 | |||
443 | get_parent().add_child.call_deferred(locationListener) | ||
444 | |||
445 | if ap.kEndingNameByVictoryValue.get(ap.victory_condition, null) == ending.get_name(): | ||
446 | var victoryListener = ap.SCRIPT_victoryListener.new() | ||
447 | victoryListener.name = "victoryListener" | ||
448 | victoryListener.senders.append(NodePath("/root/scene/" + ending.get_path())) | ||
449 | |||
450 | get_parent().add_child.call_deferred(victoryListener) | ||
451 | |||
452 | # Set up keyholder locations, in keyholder sanity. | ||
453 | if ap.keyholder_sanity: | ||
454 | for keyholder in gamedata.objects.get_keyholders(): | ||
455 | if not keyholder.has_key(): | ||
456 | continue | ||
457 | |||
458 | var room = gamedata.objects.get_rooms()[keyholder.get_room_id()] | ||
459 | if room.get_map_id() != map_id: | ||
460 | continue | ||
461 | |||
462 | var locationListener = ap.SCRIPT_locationListener.new() | ||
463 | locationListener.location_id = keyholder.get_ap_id() | ||
464 | locationListener.name = "locationListener_%d" % keyholder.get_ap_id() | ||
465 | |||
466 | var khl = khl_script.new() | ||
467 | khl.name = "location_%d_keyholder" % keyholder.get_ap_id() | ||
468 | khl.answer = keyholder.get_key() | ||
469 | khl.senders.append(NodePath("/root/scene/" + keyholder.get_path())) | ||
470 | get_parent().add_child.call_deferred(khl) | ||
471 | |||
472 | locationListener.senders.append(NodePath("../" + khl.name)) | ||
473 | |||
474 | get_parent().add_child.call_deferred(locationListener) | ||
475 | |||
323 | var minimap = ap.SCRIPT_minimap.new() | 476 | var minimap = ap.SCRIPT_minimap.new() |
324 | minimap.name = "Minimap" | 477 | minimap.name = "Minimap" |
325 | minimap.visible = ap.show_minimap | 478 | minimap.visible = ap.show_minimap |
diff --git a/apworld/options.py b/apworld/options.py index 3d8abee..7577e0c 100644 --- a/apworld/options.py +++ b/apworld/options.py | |||
@@ -1,6 +1,6 @@ | |||
1 | from dataclasses import dataclass | 1 | from dataclasses import dataclass |
2 | 2 | ||
3 | from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range | 3 | from Options import PerGameCommonOptions, Toggle, Choice, DefaultOnToggle, Range, OptionSet |
4 | 4 | ||
5 | 5 | ||
6 | class ShuffleDoors(DefaultOnToggle): | 6 | class ShuffleDoors(DefaultOnToggle): |
@@ -57,9 +57,6 @@ class ShuffleWorldports(Toggle): | |||
57 | Randomizes the connections between maps. This affects worldports only, which are the loading zones you walk into in | 57 | Randomizes the connections between maps. This affects worldports only, which are the loading zones you walk into in |
58 | order to change maps. This does not affect paintings, panels that teleport you, or certain other special connections | 58 | order to change maps. This does not affect paintings, panels that teleport you, or certain other special connections |
59 | like the one between The Shop and Control Center. | 59 | like the one between The Shop and Control Center. |
60 | |||
61 | NOTE: It is highly recommended that you turn on Shuffle Control Center Colors when using Shuffle Worldports. Not | ||
62 | doing so runs the risk of creating an unfinishable seed. | ||
63 | """ | 60 | """ |
64 | display_name = "Shuffle Worldports" | 61 | display_name = "Shuffle Worldports" |
65 | 62 | ||
@@ -102,6 +99,28 @@ class EnableIcarus(Toggle): | |||
102 | display_name = "Enable Icarus" | 99 | display_name = "Enable Icarus" |
103 | 100 | ||
104 | 101 | ||
102 | class EnableGiftMaps(OptionSet): | ||
103 | """ | ||
104 | Controls whether the beta tester gift maps are randomized. By default, these are not accessible at all from within | ||
105 | the randomizer. This option allows you to enter the maps, and creates items and locations for them. If worldport | ||
106 | shuffle is on, their worldports will be included in the randomization. | ||
107 | |||
108 | The gift maps are accessed via a panel in The Entry's Starting Room, which only appears if at least one gift map is | ||
109 | enabled. It is also treated like a cyan door, and will not appear until the condition specified in the Cyan Door | ||
110 | Behavior option is satisfied. Solving this panel with the name of one of the beta testers will teleport you to their | ||
111 | corresponding gift map. | ||
112 | |||
113 | In the base game, nothing happens once you complete a gift map. Masteries have been added to the gift maps in the | ||
114 | randomizer so that the player can be rewarded for completing them. | ||
115 | |||
116 | Note that the gift maps were originally only intended to be played by specific people, and as a result may be | ||
117 | frustrating or require knowledge of inside jokes. The Crystalline is particularly difficult as it requires | ||
118 | completing a parkour course. | ||
119 | """ | ||
120 | display_name = "Enable Gift Maps" | ||
121 | valid_keys = ["The Advanced", "The Charismatic", "The Crystalline", "The Fuzzy", "The Stellar"] | ||
122 | |||
123 | |||
105 | class DaedalusRoofAccess(Toggle): | 124 | class DaedalusRoofAccess(Toggle): |
106 | """ | 125 | """ |
107 | If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus | 126 | If enabled, the player will be logically expected to be able to go from the castle entrance to any part of Daedalus |
@@ -181,6 +200,7 @@ class Lingo2Options(PerGameCommonOptions): | |||
181 | keyholder_sanity: KeyholderSanity | 200 | keyholder_sanity: KeyholderSanity |
182 | cyan_door_behavior: CyanDoorBehavior | 201 | cyan_door_behavior: CyanDoorBehavior |
183 | enable_icarus: EnableIcarus | 202 | enable_icarus: EnableIcarus |
203 | enable_gift_maps: EnableGiftMaps | ||
184 | daedalus_roof_access: DaedalusRoofAccess | 204 | daedalus_roof_access: DaedalusRoofAccess |
185 | strict_purple_ending: StrictPurpleEnding | 205 | strict_purple_ending: StrictPurpleEnding |
186 | strict_cyan_ending: StrictCyanEnding | 206 | strict_cyan_ending: StrictCyanEnding |
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 0cf0473..5f4f1d7 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py | |||
@@ -234,6 +234,13 @@ class Lingo2PlayerLogic: | |||
234 | return True | 234 | return True |
235 | elif game_map.type == data_pb2.MapType.ICARUS: | 235 | elif game_map.type == data_pb2.MapType.ICARUS: |
236 | return bool(world.options.enable_icarus) | 236 | return bool(world.options.enable_icarus) |
237 | elif game_map.type == data_pb2.MapType.GIFT_MAP: | ||
238 | if game_map.name == "the_advanced": | ||
239 | return "The Advanced" in world.options.enable_gift_maps.value | ||
240 | elif game_map.name == "the_charismatic": | ||
241 | return "The Charismatic" in world.options.enable_gift_maps.value | ||
242 | elif game_map.name == "the_crystalline": | ||
243 | return "The Crystalline" in world.options.enable_gift_maps.value | ||
237 | 244 | ||
238 | return False | 245 | return False |
239 | 246 | ||