diff options
27 files changed, 527 insertions, 52 deletions
| diff --git a/apworld/__init__.py b/apworld/__init__.py index 4ebf845..f5774c6 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py | |||
| @@ -132,7 +132,9 @@ class Lingo2World(World): | |||
| 132 | "daedalus_roof_access", | 132 | "daedalus_roof_access", |
| 133 | "enable_gift_maps", | 133 | "enable_gift_maps", |
| 134 | "enable_icarus", | 134 | "enable_icarus", |
| 135 | "endings_requirement", | ||
| 135 | "keyholder_sanity", | 136 | "keyholder_sanity", |
| 137 | "masteries_requirement", | ||
| 136 | "shuffle_control_center_colors", | 138 | "shuffle_control_center_colors", |
| 137 | "shuffle_doors", | 139 | "shuffle_doors", |
| 138 | "shuffle_gallery_paintings", | 140 | "shuffle_gallery_paintings", |
| 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 @@ | |||
| 1 | extends "res://scripts/nodes/allowNumbers.gd" | ||
| 2 | |||
| 3 | |||
| 4 | func _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/gamedata.gd b/apworld/client/gamedata.gd index 9305003..3a35125 100644 --- a/apworld/client/gamedata.gd +++ b/apworld/client/gamedata.gd | |||
| @@ -221,7 +221,11 @@ func _get_generated_door_location_name(door): | |||
| 221 | if door.get_type() != SCRIPT_proto.DoorType.STANDARD: | 221 | if door.get_type() != SCRIPT_proto.DoorType.STANDARD: |
| 222 | return null | 222 | return null |
| 223 | 223 | ||
| 224 | if door.get_keyholders().size() > 0 or door.get_endings().size() > 0 or door.has_complete_at(): | 224 | if ( |
| 225 | door.get_keyholders().size() > 0 | ||
| 226 | or (door.has_white_ending() and door.get_white_ending()) | ||
| 227 | or door.has_complete_at() | ||
| 228 | ): | ||
| 225 | return null | 229 | return null |
| 226 | 230 | ||
| 227 | if door.get_panels().size() > 4: | 231 | if door.get_panels().size() > 4: |
| diff --git a/apworld/client/main.gd b/apworld/client/main.gd index 3a62f81..a543678 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")) |
| @@ -83,6 +84,13 @@ func _ready(): | |||
| 83 | compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") | 84 | compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") |
| 84 | global.add_child(compass_overlay_instance) | 85 | global.add_child(compass_overlay_instance) |
| 85 | 86 | ||
| 87 | unlocks.data["advanced_mastery"] = "" | ||
| 88 | unlocks.data["charismatic_mastery"] = "" | ||
| 89 | unlocks.data["crystalline_mastery"] = "" | ||
| 90 | unlocks.data["fuzzy_mastery"] = "" | ||
| 91 | unlocks.data["icarus_mastery"] = "" | ||
| 92 | unlocks.data["stellar_mastery"] = "" | ||
| 93 | |||
| 86 | var ap = global.get_node("Archipelago") | 94 | var ap = global.get_node("Archipelago") |
| 87 | var gamedata = global.get_node("Gamedata") | 95 | var gamedata = global.get_node("Gamedata") |
| 88 | ap.ap_connected.connect(connectionSuccessful) | 96 | ap.ap_connected.connect(connectionSuccessful) |
| @@ -224,11 +232,11 @@ func startGame(): | |||
| 224 | 232 | ||
| 225 | unlocks.resetCollectables() | 233 | unlocks.resetCollectables() |
| 226 | unlocks.resetData() | 234 | unlocks.resetData() |
| 235 | unlocks.loadCollectables() | ||
| 236 | unlocks.loadData() | ||
| 227 | 237 | ||
| 228 | ap.setup_keys() | 238 | ap.setup_keys() |
| 229 | 239 | ||
| 230 | unlocks.loadCollectables() | ||
| 231 | unlocks.loadData() | ||
| 232 | unlocks.unlockKey("capslock", 1) | 240 | unlocks.unlockKey("capslock", 1) |
| 233 | 241 | ||
| 234 | if ap.shuffle_worldports: | 242 | if ap.shuffle_worldports: |
| @@ -237,6 +245,7 @@ func startGame(): | |||
| 237 | settings.worldport_fades = "never" | 245 | settings.worldport_fades = "never" |
| 238 | 246 | ||
| 239 | clearResourceCache("res://objects/meshes/gridDoor.tscn") | 247 | clearResourceCache("res://objects/meshes/gridDoor.tscn") |
| 248 | clearResourceCache("res://objects/nodes/allowNumbers.tscn") | ||
| 240 | clearResourceCache("res://objects/nodes/collectable.tscn") | 249 | clearResourceCache("res://objects/nodes/collectable.tscn") |
| 241 | clearResourceCache("res://objects/nodes/door.tscn") | 250 | clearResourceCache("res://objects/nodes/door.tscn") |
| 242 | clearResourceCache("res://objects/nodes/keyHolder.tscn") | 251 | clearResourceCache("res://objects/nodes/keyHolder.tscn") |
| diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 41ab648..aa07559 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd | |||
| @@ -65,7 +65,9 @@ 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 enable_gift_maps = [] |
| 67 | var enable_icarus = false | 67 | var enable_icarus = false |
| 68 | var endings_requirement = 0 | ||
| 68 | var keyholder_sanity = false | 69 | var keyholder_sanity = false |
| 70 | var masteries_requirement = 0 | ||
| 69 | var port_pairings = {} | 71 | var port_pairings = {} |
| 70 | var shuffle_control_center_colors = false | 72 | var shuffle_control_center_colors = false |
| 71 | var shuffle_doors = false | 73 | var shuffle_doors = false |
| @@ -259,6 +261,9 @@ func _process_item(item, amount): | |||
| 259 | if item_id == gamedata.objects.get_special_ids()["A Job Well Done"]: | 261 | if item_id == gamedata.objects.get_special_ids()["A Job Well Done"]: |
| 260 | update_job_well_done_sign() | 262 | update_job_well_done_sign() |
| 261 | 263 | ||
| 264 | if item_id == gamedata.objects.get_special_ids()["Numbers"] and global.map == "the_fuzzy": | ||
| 265 | global.allow_numbers = true | ||
| 266 | |||
| 262 | # Show a message about the item if it's new. | 267 | # Show a message about the item if it's new. |
| 263 | if int(item["index"]) > _last_new_item: | 268 | if int(item["index"]) > _last_new_item: |
| 264 | _last_new_item = int(item["index"]) | 269 | _last_new_item = int(item["index"]) |
| @@ -443,7 +448,9 @@ func _client_connected(slot_data): | |||
| 443 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) | 448 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) |
| 444 | enable_gift_maps = slot_data.get("enable_gift_maps", []) | 449 | enable_gift_maps = slot_data.get("enable_gift_maps", []) |
| 445 | enable_icarus = bool(slot_data.get("enable_icarus", false)) | 450 | enable_icarus = bool(slot_data.get("enable_icarus", false)) |
| 451 | endings_requirement = int(slot_data.get("endings_requirement", 0)) | ||
| 446 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | 452 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) |
| 453 | masteries_requirement = int(slot_data.get("masteries_requirement", 0)) | ||
| 447 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | 454 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) |
| 448 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | 455 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) |
| 449 | shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) | 456 | shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) |
| diff --git a/apworld/client/player.gd b/apworld/client/player.gd index 789d1b7..712a59b 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd | |||
| @@ -19,6 +19,85 @@ func _ready(): | |||
| 19 | 19 | ||
| 20 | ap.start_batching_locations() | 20 | ap.start_batching_locations() |
| 21 | 21 | ||
| 22 | if global.map == "control_center": | ||
| 23 | get_node("/root/scene/Components/Doors/entry_18").queue_free() | ||
| 24 | |||
| 25 | _set_up_mastery_listener("advanced") | ||
| 26 | _set_up_mastery_listener("charismatic") | ||
| 27 | _set_up_mastery_listener("crystalline") | ||
| 28 | _set_up_mastery_listener("fuzzy") | ||
| 29 | _set_up_mastery_listener("icarus") | ||
| 30 | _set_up_mastery_listener("stellar") | ||
| 31 | |||
| 32 | if ap.endings_requirement != 12 or ap.masteries_requirement != 0: | ||
| 33 | # Set up listeners for the potential White Ending requirements. | ||
| 34 | var merging_prefab = preload("res://objects/nodes/listeners/mergingListener.tscn") | ||
| 35 | |||
| 36 | var old_door = get_node("/root/scene/Components/Doors/entry_19") | ||
| 37 | var new_door = old_door.duplicate() | ||
| 38 | new_door.name = "entry_19_new" | ||
| 39 | new_door.senders.clear() | ||
| 40 | new_door.senderGroup.clear() | ||
| 41 | new_door.excludeSenders.clear() | ||
| 42 | |||
| 43 | if ap.endings_requirement == 12: | ||
| 44 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 45 | elif ap.endings_requirement > 0: | ||
| 46 | if ap.masteries_requirement == 0: | ||
| 47 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 48 | new_door.complete_at = ap.endings_requirement | ||
| 49 | else: | ||
| 50 | var endings_merge = merging_prefab.instantiate() | ||
| 51 | endings_merge.name = "EndingsMerge" | ||
| 52 | endings_merge.senderGroup.append( | ||
| 53 | NodePath("/root/scene/Meshes/Trophies/Listeners") | ||
| 54 | ) | ||
| 55 | endings_merge.complete_at = ap.endings_requirement | ||
| 56 | get_node("/root/scene/Components").add_child.call_deferred(endings_merge) | ||
| 57 | new_door.senders.append(NodePath("/root/scene/Components/EndingsMerge")) | ||
| 58 | |||
| 59 | var max_masteries = 13 + ap.enable_gift_maps.size() | ||
| 60 | if ap.enable_icarus: | ||
| 61 | max_masteries += 1 | ||
| 62 | |||
| 63 | if ap.masteries_requirement == max_masteries: | ||
| 64 | new_door.senderGroup.append( | ||
| 65 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 66 | ) | ||
| 67 | new_door.excludeSenders.append( | ||
| 68 | NodePath( | ||
| 69 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 70 | ) | ||
| 71 | ) | ||
| 72 | elif ap.masteries_requirement > 0: | ||
| 73 | if ap.endings_requirement == 0: | ||
| 74 | new_door.senderGroup.append( | ||
| 75 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 76 | ) | ||
| 77 | new_door.excludeSenders.append( | ||
| 78 | NodePath( | ||
| 79 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 80 | ) | ||
| 81 | ) | ||
| 82 | new_door.complete_at = ap.masteries_requirement | ||
| 83 | else: | ||
| 84 | var masteries_merge = merging_prefab.instantiate() | ||
| 85 | masteries_merge.name = "MasteriesMerge" | ||
| 86 | masteries_merge.senderGroup.append( | ||
| 87 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 88 | ) | ||
| 89 | masteries_merge.excludeSenders.append( | ||
| 90 | NodePath( | ||
| 91 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 92 | ) | ||
| 93 | ) | ||
| 94 | masteries_merge.complete_at = ap.masteries_requirement | ||
| 95 | get_node("/root/scene/Components").add_child.call_deferred(masteries_merge) | ||
| 96 | new_door.senders.append(NodePath("/root/scene/Components/MasteriesMerge")) | ||
| 97 | |||
| 98 | old_door.queue_free() | ||
| 99 | get_node("/root/scene/Components/Doors").add_child.call_deferred(new_door) | ||
| 100 | |||
| 22 | # Block off roof access in Daedalus. | 101 | # Block off roof access in Daedalus. |
| 23 | if global.map == "daedalus" and not ap.daedalus_roof_access: | 102 | if global.map == "daedalus" and not ap.daedalus_roof_access: |
| 24 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) | 103 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) |
| @@ -141,6 +220,29 @@ func _ready(): | |||
| 141 | q_wpl.senders.append(NodePath("../QPanel")) | 220 | q_wpl.senders.append(NodePath("../QPanel")) |
| 142 | giftmap_parent.add_child.call_deferred(q_wpl) | 221 | giftmap_parent.add_child.call_deferred(q_wpl) |
| 143 | 222 | ||
| 223 | if ap.enable_gift_maps.has("The Fuzzy"): | ||
| 224 | var gongus_panel = panel_prefab.instantiate() | ||
| 225 | gongus_panel.name = "GongusPanel" | ||
| 226 | gongus_panel.answer = "gongus" | ||
| 227 | gongus_panel.position = Vector3(33.5, -260, 5.5) | ||
| 228 | giftmap_panel.proxies.append(NodePath("../GongusPanel")) | ||
| 229 | giftmap_parent.add_child.call_deferred(gongus_panel) | ||
| 230 | |||
| 231 | var kiwi_panel = panel_prefab.instantiate() | ||
| 232 | kiwi_panel.name = "KiwiPanel" | ||
| 233 | kiwi_panel.answer = "kiwi" | ||
| 234 | kiwi_panel.position = Vector3(33.5, -270, 5.5) | ||
| 235 | giftmap_panel.proxies.append(NodePath("../KiwiPanel")) | ||
| 236 | giftmap_parent.add_child.call_deferred(kiwi_panel) | ||
| 237 | |||
| 238 | var fuzzy_wpl = wpl_prefab.instantiate() | ||
| 239 | fuzzy_wpl.name = "FuzzyWpl" | ||
| 240 | fuzzy_wpl.exit = "the_fuzzy" | ||
| 241 | fuzzy_wpl.senders.append(NodePath("../GongusPanel")) | ||
| 242 | fuzzy_wpl.senders.append(NodePath("../KiwiPanel")) | ||
| 243 | fuzzy_wpl.complete_at = 1 | ||
| 244 | giftmap_parent.add_child.call_deferred(fuzzy_wpl) | ||
| 245 | |||
| 144 | if ap.enable_gift_maps.has("The Stellar"): | 246 | if ap.enable_gift_maps.has("The Stellar"): |
| 145 | var hatkirby_panel = panel_prefab.instantiate() | 247 | var hatkirby_panel = panel_prefab.instantiate() |
| 146 | hatkirby_panel.name = "HatkirbyPanel" | 248 | hatkirby_panel.name = "HatkirbyPanel" |
| @@ -297,6 +399,7 @@ func _ready(): | |||
| 297 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 399 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 298 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 400 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 299 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 401 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 402 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 300 | 403 | ||
| 301 | var mastery = collectable_prefab.instantiate() | 404 | var mastery = collectable_prefab.instantiate() |
| 302 | mastery.name = "collectable" | 405 | mastery.name = "collectable" |
| @@ -314,6 +417,13 @@ func _ready(): | |||
| 314 | tpl.nested = true | 417 | tpl.nested = true |
| 315 | mastery.add_child.call_deferred(tpl) | 418 | mastery.add_child.call_deferred(tpl) |
| 316 | 419 | ||
| 420 | var usl = usl_prefab.instantiate() | ||
| 421 | usl.name = "unlockSetterListenerMastery" | ||
| 422 | usl.key = "icarus_mastery" | ||
| 423 | usl.value = "unlocked" | ||
| 424 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 425 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 426 | |||
| 317 | var saver = saver_prefab.instantiate() | 427 | var saver = saver_prefab.instantiate() |
| 318 | saver.name = "saver_collectables" | 428 | saver.name = "saver_collectables" |
| 319 | saver.type = "collectables" | 429 | saver.type = "collectables" |
| @@ -325,6 +435,7 @@ func _ready(): | |||
| 325 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 435 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 326 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 436 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 327 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 437 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 438 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 328 | 439 | ||
| 329 | var mastery = collectable_prefab.instantiate() | 440 | var mastery = collectable_prefab.instantiate() |
| 330 | mastery.name = "collectable" | 441 | mastery.name = "collectable" |
| @@ -343,6 +454,13 @@ func _ready(): | |||
| 343 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) | 454 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) |
| 344 | mastery.add_child.call_deferred(tpl) | 455 | mastery.add_child.call_deferred(tpl) |
| 345 | 456 | ||
| 457 | var usl = usl_prefab.instantiate() | ||
| 458 | usl.name = "unlockSetterListenerMastery" | ||
| 459 | usl.key = "advanced_mastery" | ||
| 460 | usl.value = "unlocked" | ||
| 461 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 462 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 463 | |||
| 346 | var saver = saver_prefab.instantiate() | 464 | var saver = saver_prefab.instantiate() |
| 347 | saver.name = "saver_collectables" | 465 | saver.name = "saver_collectables" |
| 348 | saver.type = "collectables" | 466 | saver.type = "collectables" |
| @@ -353,6 +471,7 @@ func _ready(): | |||
| 353 | if global.map == "the_charismatic": | 471 | if global.map == "the_charismatic": |
| 354 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 472 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 355 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 473 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 474 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 356 | 475 | ||
| 357 | var mastery = collectable_prefab.instantiate() | 476 | var mastery = collectable_prefab.instantiate() |
| 358 | mastery.name = "collectable" | 477 | mastery.name = "collectable" |
| @@ -362,6 +481,13 @@ func _ready(): | |||
| 362 | mastery.material_override = load("res://assets/materials/gold.material") | 481 | mastery.material_override = load("res://assets/materials/gold.material") |
| 363 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | 482 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) |
| 364 | 483 | ||
| 484 | var usl = usl_prefab.instantiate() | ||
| 485 | usl.name = "unlockSetterListenerMastery" | ||
| 486 | usl.key = "charismatic_mastery" | ||
| 487 | usl.value = "unlocked" | ||
| 488 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 489 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 490 | |||
| 365 | var saver = saver_prefab.instantiate() | 491 | var saver = saver_prefab.instantiate() |
| 366 | saver.name = "saver_collectables" | 492 | saver.name = "saver_collectables" |
| 367 | saver.type = "collectables" | 493 | saver.type = "collectables" |
| @@ -373,6 +499,7 @@ func _ready(): | |||
| 373 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 499 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 374 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 500 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 375 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 501 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 502 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 376 | 503 | ||
| 377 | var mastery = collectable_prefab.instantiate() | 504 | var mastery = collectable_prefab.instantiate() |
| 378 | mastery.name = "collectable" | 505 | mastery.name = "collectable" |
| @@ -389,6 +516,39 @@ func _ready(): | |||
| 389 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) | 516 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) |
| 390 | mastery.add_child.call_deferred(tpl) | 517 | mastery.add_child.call_deferred(tpl) |
| 391 | 518 | ||
| 519 | var usl = usl_prefab.instantiate() | ||
| 520 | usl.name = "unlockSetterListenerMastery" | ||
| 521 | usl.key = "crystalline_mastery" | ||
| 522 | usl.value = "unlocked" | ||
| 523 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 524 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 525 | |||
| 526 | var saver = saver_prefab.instantiate() | ||
| 527 | saver.name = "saver_collectables" | ||
| 528 | saver.type = "collectables" | ||
| 529 | saver.senderGroup.append(NodePath("/root/scene/Components/Collectables")) | ||
| 530 | get_node("/root/scene").add_child.call_deferred(saver) | ||
| 531 | |||
| 532 | # Add the mastery to The Fuzzy. | ||
| 533 | if global.map == "the_fuzzy": | ||
| 534 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | ||
| 535 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | ||
| 536 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 537 | |||
| 538 | var mastery = collectable_prefab.instantiate() | ||
| 539 | mastery.name = "collectable" | ||
| 540 | mastery.position = Vector3(0, 2, -20) | ||
| 541 | mastery.unlock_type = "smiley" | ||
| 542 | mastery.material_override = load("res://assets/materials/gold.material") | ||
| 543 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | ||
| 544 | |||
| 545 | var usl = usl_prefab.instantiate() | ||
| 546 | usl.name = "unlockSetterListenerMastery" | ||
| 547 | usl.key = "fuzzy_mastery" | ||
| 548 | usl.value = "unlocked" | ||
| 549 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 550 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 551 | |||
| 392 | var saver = saver_prefab.instantiate() | 552 | var saver = saver_prefab.instantiate() |
| 393 | saver.name = "saver_collectables" | 553 | saver.name = "saver_collectables" |
| 394 | saver.type = "collectables" | 554 | saver.type = "collectables" |
| @@ -399,6 +559,7 @@ func _ready(): | |||
| 399 | if global.map == "the_stellar": | 559 | if global.map == "the_stellar": |
| 400 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 560 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 401 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 561 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 562 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 402 | 563 | ||
| 403 | var collectables = Node.new() | 564 | var collectables = Node.new() |
| 404 | collectables.name = "Collectables" | 565 | collectables.name = "Collectables" |
| @@ -412,6 +573,13 @@ func _ready(): | |||
| 412 | collectables.add_child.call_deferred(mastery) | 573 | collectables.add_child.call_deferred(mastery) |
| 413 | get_node("/root/scene/Components").add_child.call_deferred(collectables) | 574 | get_node("/root/scene/Components").add_child.call_deferred(collectables) |
| 414 | 575 | ||
| 576 | var usl = usl_prefab.instantiate() | ||
| 577 | usl.name = "unlockSetterListenerMastery" | ||
| 578 | usl.key = "stellar_mastery" | ||
| 579 | usl.value = "unlocked" | ||
| 580 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 581 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 582 | |||
| 415 | var saver = saver_prefab.instantiate() | 583 | var saver = saver_prefab.instantiate() |
| 416 | saver.name = "saver_collectables" | 584 | saver.name = "saver_collectables" |
| 417 | saver.type = "collectables" | 585 | saver.type = "collectables" |
| @@ -585,5 +753,14 @@ func _set_up_invis_wall(x, y, z, sx, sy, sz): | |||
| 585 | get_parent().add_child.call_deferred(newwall) | 753 | get_parent().add_child.call_deferred(newwall) |
| 586 | 754 | ||
| 587 | 755 | ||
| 756 | func _set_up_mastery_listener(name): | ||
| 757 | var prefab = preload("res://objects/nodes/listeners/unlockReaderListener.tscn") | ||
| 758 | var url = prefab.instantiate() | ||
| 759 | url.name = "unlockReaderListenerMastery_%s" % name | ||
| 760 | url.key = "%s_mastery" % name | ||
| 761 | url.value = "unlocked" | ||
| 762 | get_node("/root/scene/Meshes/Trophies/MasteryListeners").add_child.call_deferred(url) | ||
| 763 | |||
| 764 | |||
| 588 | func _process(_dt): | 765 | func _process(_dt): |
| 589 | compass.update_rotation(global_rotation.y) | 766 | compass.update_rotation(global_rotation.y) |
| diff --git a/apworld/options.py b/apworld/options.py index 7577e0c..a56b40d 100644 --- a/apworld/options.py +++ b/apworld/options.py | |||
| @@ -181,6 +181,26 @@ class VictoryCondition(Choice): | |||
| 181 | option_white_ending = 12 | 181 | option_white_ending = 12 |
| 182 | 182 | ||
| 183 | 183 | ||
| 184 | class EndingsRequirement(Range): | ||
| 185 | """The number of endings required to unlock White Ending.""" | ||
| 186 | display_name = "Endings Requirement" | ||
| 187 | range_start = 0 | ||
| 188 | range_end = 12 | ||
| 189 | default = 12 | ||
| 190 | |||
| 191 | |||
| 192 | class MasteriesRequirement(Range): | ||
| 193 | """The number of masteries required to unlock White Ending. | ||
| 194 | |||
| 195 | There are only 13 masteries in the base game, but some of the other slot options may add more masteries to the | ||
| 196 | world. If the chosen number of masteries is higher than the total in your world, it will be automatically lowered to | ||
| 197 | the maximum.""" | ||
| 198 | display_name = "Masteries Requirement" | ||
| 199 | range_start = 0 | ||
| 200 | range_end = 18 | ||
| 201 | default = 0 | ||
| 202 | |||
| 203 | |||
| 184 | class TrapPercentage(Range): | 204 | class TrapPercentage(Range): |
| 185 | """Replaces junk items with traps, at the specified rate.""" | 205 | """Replaces junk items with traps, at the specified rate.""" |
| 186 | display_name = "Trap Percentage" | 206 | display_name = "Trap Percentage" |
| @@ -205,4 +225,6 @@ class Lingo2Options(PerGameCommonOptions): | |||
| 205 | strict_purple_ending: StrictPurpleEnding | 225 | strict_purple_ending: StrictPurpleEnding |
| 206 | strict_cyan_ending: StrictCyanEnding | 226 | strict_cyan_ending: StrictCyanEnding |
| 207 | victory_condition: VictoryCondition | 227 | victory_condition: VictoryCondition |
| 228 | endings_requirement: EndingsRequirement | ||
| 229 | masteries_requirement: MasteriesRequirement | ||
| 208 | trap_percentage: TrapPercentage | 230 | trap_percentage: TrapPercentage |
| diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 67365b7..0cbcdec 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py | |||
| @@ -241,6 +241,8 @@ class Lingo2PlayerLogic: | |||
| 241 | return "The Charismatic" in world.options.enable_gift_maps.value | 241 | return "The Charismatic" in world.options.enable_gift_maps.value |
| 242 | elif game_map.name == "the_crystalline": | 242 | elif game_map.name == "the_crystalline": |
| 243 | return "The Crystalline" in world.options.enable_gift_maps.value | 243 | return "The Crystalline" in world.options.enable_gift_maps.value |
| 244 | elif game_map.name == "the_fuzzy": | ||
| 245 | return "The Fuzzy" in world.options.enable_gift_maps.value | ||
| 244 | elif game_map.name == "the_stellar": | 246 | elif game_map.name == "the_stellar": |
| 245 | return "The Stellar" in world.options.enable_gift_maps.value | 247 | return "The Stellar" in world.options.enable_gift_maps.value |
| 246 | 248 | ||
| @@ -249,6 +251,16 @@ class Lingo2PlayerLogic: | |||
| 249 | self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps | 251 | self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps |
| 250 | if should_shuffle_map(game_map)) | 252 | if should_shuffle_map(game_map)) |
| 251 | 253 | ||
| 254 | maximum_masteries = 13 + len(world.options.enable_gift_maps.value) | ||
| 255 | if world.options.enable_icarus: | ||
| 256 | maximum_masteries += 1 | ||
| 257 | |||
| 258 | if world.options.masteries_requirement > maximum_masteries: | ||
| 259 | world.options.masteries_requirement.value = maximum_masteries | ||
| 260 | |||
| 261 | if "The Fuzzy" in world.options.enable_gift_maps.value: | ||
| 262 | self.real_items.append("Numbers") | ||
| 263 | |||
| 252 | if self.world.options.shuffle_doors: | 264 | if self.world.options.shuffle_doors: |
| 253 | for progressive in world.static_logic.objects.progressives: | 265 | for progressive in world.static_logic.objects.progressives: |
| 254 | for i in range(0, len(progressive.doors)): | 266 | for i in range(0, len(progressive.doors)): |
| @@ -362,18 +374,23 @@ class Lingo2PlayerLogic: | |||
| 362 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, | 374 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, |
| 363 | AccessRequirements())) | 375 | AccessRequirements())) |
| 364 | 376 | ||
| 377 | if world.options.masteries_requirement > 0: | ||
| 378 | event_name = f"{world.static_logic.get_room_object_map_name(mastery)} - Mastery (Collected)" | ||
| 379 | self.event_loc_item_by_room.setdefault(mastery.room_id, {})[event_name] = "Mastery" | ||
| 380 | |||
| 365 | for ending in world.static_logic.objects.endings: | 381 | for ending in world.static_logic.objects.endings: |
| 366 | if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: | 382 | if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: |
| 367 | continue | 383 | continue |
| 368 | 384 | ||
| 369 | # Don't create a location for your selected ending, and never create a location for White Ending. | 385 | # Don't create a location for your selected ending. Also don't create a location for White Ending if it's |
| 386 | # necessarily in the postgame, i.e. it requires all 12 other endings. | ||
| 370 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() != ending.name\ | 387 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() != ending.name\ |
| 371 | and ending.name != "WHITE": | 388 | and (ending.name != "WHITE" or world.options.endings_requirement < 12): |
| 372 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, | 389 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, |
| 373 | AccessRequirements())) | 390 | AccessRequirements())) |
| 374 | 391 | ||
| 375 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" | 392 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" |
| 376 | item_name = event_name | 393 | item_name = "Ending" |
| 377 | 394 | ||
| 378 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: | 395 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: |
| 379 | item_name = "Victory" | 396 | item_name = "Victory" |
| @@ -520,13 +537,12 @@ class Lingo2PlayerLogic: | |||
| 520 | for room in door.rooms: | 537 | for room in door.rooms: |
| 521 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) | 538 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) |
| 522 | 539 | ||
| 523 | for ending_id in door.endings: | 540 | if door.white_ending: |
| 524 | ending = self.world.static_logic.objects.endings[ending_id] | 541 | if self.world.options.endings_requirement > 0: |
| 542 | reqs.progressives["Ending"] = self.world.options.endings_requirement.value | ||
| 525 | 543 | ||
| 526 | if self.world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: | 544 | if self.world.options.masteries_requirement > 0: |
| 527 | reqs.items.add("Victory") | 545 | reqs.progressives["Mastery"] = self.world.options.masteries_requirement.value |
| 528 | else: | ||
| 529 | reqs.items.add(f"{ending.name.capitalize()} Ending (Achieved)") | ||
| 530 | 546 | ||
| 531 | for sub_door_id in door.doors: | 547 | for sub_door_id in door.doors: |
| 532 | sub_reqs = self.get_door_open_reqs(sub_door_id) | 548 | sub_reqs = self.get_door_open_reqs(sub_door_id) |
| @@ -588,3 +604,6 @@ class Lingo2PlayerLogic: | |||
| 588 | 604 | ||
| 589 | if needed > 0: | 605 | if needed > 0: |
| 590 | reqs.letters[l] = max(reqs.letters.get(l, 0), needed) | 606 | reqs.letters[l] = max(reqs.letters.get(l, 0), needed) |
| 607 | |||
| 608 | if any(l.isnumeric() for l in solution): | ||
| 609 | reqs.items.add("Numbers") | ||
| diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 2546007..8e07b82 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py | |||
| @@ -68,6 +68,7 @@ class Lingo2StaticLogic: | |||
| 68 | self.location_name_groups.setdefault("Keyholders", []).append(location_name) | 68 | self.location_name_groups.setdefault("Keyholders", []).append(location_name) |
| 69 | 69 | ||
| 70 | self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done" | 70 | self.item_id_to_name[self.objects.special_ids["A Job Well Done"]] = "A Job Well Done" |
| 71 | self.item_id_to_name[self.objects.special_ids["Numbers"]] = "Numbers" | ||
| 71 | 72 | ||
| 72 | for symbol_name in SYMBOL_ITEMS.values(): | 73 | for symbol_name in SYMBOL_ITEMS.values(): |
| 73 | self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name | 74 | self.item_id_to_name[self.objects.special_ids[symbol_name]] = symbol_name |
| @@ -105,7 +106,7 @@ class Lingo2StaticLogic: | |||
| 105 | if door.type != data_pb2.DoorType.STANDARD: | 106 | if door.type != data_pb2.DoorType.STANDARD: |
| 106 | return None | 107 | return None |
| 107 | 108 | ||
| 108 | if len(door.keyholders) > 0 or len(door.endings) > 0 or door.HasField("complete_at"): | 109 | if len(door.keyholders) > 0 or door.white_ending or door.HasField("complete_at"): |
| 109 | return None | 110 | return None |
| 110 | 111 | ||
| 111 | if len(door.panels) > 4: | 112 | if len(door.panels) > 4: |
| diff --git a/data/connections.txtpb b/data/connections.txtpb index 4bec584..8d75dab 100644 --- a/data/connections.txtpb +++ b/data/connections.txtpb | |||
| @@ -2688,3 +2688,53 @@ connections { | |||
| 2688 | } | 2688 | } |
| 2689 | oneway: true | 2689 | oneway: true |
| 2690 | } | 2690 | } |
| 2691 | connections { | ||
| 2692 | from { | ||
| 2693 | panel { | ||
| 2694 | map: "the_entry" | ||
| 2695 | room: "Starting Room" | ||
| 2696 | name: "Gift Maps" | ||
| 2697 | answer: "gongus" | ||
| 2698 | } | ||
| 2699 | } | ||
| 2700 | to { | ||
| 2701 | room { | ||
| 2702 | map: "the_fuzzy" | ||
| 2703 | name: "Main Area" | ||
| 2704 | } | ||
| 2705 | } | ||
| 2706 | oneway: true | ||
| 2707 | } | ||
| 2708 | connections { | ||
| 2709 | from { | ||
| 2710 | panel { | ||
| 2711 | map: "the_entry" | ||
| 2712 | room: "Starting Room" | ||
| 2713 | name: "Gift Maps" | ||
| 2714 | answer: "kiwi" | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | to { | ||
| 2718 | room { | ||
| 2719 | map: "the_fuzzy" | ||
| 2720 | name: "Main Area" | ||
| 2721 | } | ||
| 2722 | } | ||
| 2723 | oneway: true | ||
| 2724 | } | ||
| 2725 | connections { | ||
| 2726 | from { | ||
| 2727 | port { | ||
| 2728 | map: "the_fuzzy" | ||
| 2729 | room: "Main Area" | ||
| 2730 | name: "WORLDPORT" | ||
| 2731 | } | ||
| 2732 | } | ||
| 2733 | to { | ||
| 2734 | room { | ||
| 2735 | map: "the_entry" | ||
| 2736 | name: "Starting Room" | ||
| 2737 | } | ||
| 2738 | } | ||
| 2739 | oneway: true | ||
| 2740 | } | ||
| diff --git a/data/ids.yaml b/data/ids.yaml index 8ef294a..dc82306 100644 --- a/data/ids.yaml +++ b/data/ids.yaml | |||
| @@ -2022,6 +2022,30 @@ maps: | |||
| 2022 | panels: | 2022 | panels: |
| 2023 | CACTUS: 410 | 2023 | CACTUS: 410 |
| 2024 | TAIL: 411 | 2024 | TAIL: 411 |
| 2025 | the_fuzzy: | ||
| 2026 | rooms: | ||
| 2027 | Main Area: | ||
| 2028 | panels: | ||
| 2029 | ACHIEVES: 3033 | ||
| 2030 | BEFORE: 3028 | ||
| 2031 | BOTH: 3036 | ||
| 2032 | Blank: 3022 | ||
| 2033 | CAGED: 3027 | ||
| 2034 | COMBINED: 3032 | ||
| 2035 | DICE: 3026 | ||
| 2036 | FIRST: 3035 | ||
| 2037 | FORGED: 3030 | ||
| 2038 | LOTTO: 3024 | ||
| 2039 | OTHERS: 3031 | ||
| 2040 | TOED: 3029 | ||
| 2041 | TUTU: 3023 | ||
| 2042 | UNVEILED: 3034 | ||
| 2043 | WHERETO: 3025 | ||
| 2044 | Mastery: | ||
| 2045 | masteries: | ||
| 2046 | MASTERY: 3037 | ||
| 2047 | doors: | ||
| 2048 | Black Panels: 3021 | ||
| 2025 | the_gallery: | 2049 | the_gallery: |
| 2026 | rooms: | 2050 | rooms: |
| 2027 | Back Room: | 2051 | Back Room: |
| @@ -2871,8 +2895,9 @@ maps: | |||
| 2871 | HIDE (2): 1021 | 2895 | HIDE (2): 1021 |
| 2872 | MORE: 1022 | 2896 | MORE: 1022 |
| 2873 | doors: | 2897 | doors: |
| 2874 | Left/Turn Door: 984 | 2898 | Left Only Puzzles: 3020 |
| 2875 | Turn/Shop Door: 985 | 2899 | Shop Only Puzzles: 3019 |
| 2900 | Turn Only Puzzles: 3018 | ||
| 2876 | the_repetitive: | 2901 | the_repetitive: |
| 2877 | rooms: | 2902 | rooms: |
| 2878 | Anti Room: | 2903 | Anti Room: |
| @@ -4153,6 +4178,7 @@ special: | |||
| 4153 | Job Symbol: 2798 | 4178 | Job Symbol: 2798 |
| 4154 | Lingo Symbol: 2799 | 4179 | Lingo Symbol: 2799 |
| 4155 | Null Symbol: 2800 | 4180 | Null Symbol: 2800 |
| 4181 | Numbers: 3038 | ||
| 4156 | Planet Symbol: 2801 | 4182 | Planet Symbol: 2801 |
| 4157 | Pyramid Symbol: 2802 | 4183 | Pyramid Symbol: 2802 |
| 4158 | Question Symbol: 2803 | 4184 | Question Symbol: 2803 |
| diff --git a/data/maps/control_center/doors.txtpb b/data/maps/control_center/doors.txtpb index cac1937..1422301 100644 --- a/data/maps/control_center/doors.txtpb +++ b/data/maps/control_center/doors.txtpb | |||
| @@ -86,21 +86,7 @@ doors { | |||
| 86 | doors { | 86 | doors { |
| 87 | name: "White Ending Door" | 87 | name: "White Ending Door" |
| 88 | type: EVENT | 88 | type: EVENT |
| 89 | # This is the only time a door depends on endings. However, it's nice to do it | 89 | white_ending: true |
| 90 | # this way instead of just checking for ending room access because this lets | ||
| 91 | # us use events, which makes the playthrough more readable. | ||
| 92 | endings: "MINT" | ||
| 93 | endings: "ORANGE" | ||
| 94 | endings: "GREEN" | ||
| 95 | endings: "GRAY" | ||
| 96 | endings: "PLUM" | ||
| 97 | endings: "YELLOW" | ||
| 98 | endings: "GOLD" | ||
| 99 | endings: "BLACK" | ||
| 100 | endings: "CYAN" | ||
| 101 | endings: "PURPLE" | ||
| 102 | endings: "RED" | ||
| 103 | endings: "BLUE" | ||
| 104 | } | 90 | } |
| 105 | doors { | 91 | doors { |
| 106 | name: "Repetitive Entrance" | 92 | name: "Repetitive Entrance" |
| diff --git a/data/maps/the_entry/metadata.txtpb b/data/maps/the_entry/metadata.txtpb index bdcdf83..da2194b 100644 --- a/data/maps/the_entry/metadata.txtpb +++ b/data/maps/the_entry/metadata.txtpb | |||
| @@ -12,9 +12,11 @@ excluded_nodes: "Panels/Back Left/backleft_4_proxied_2" | |||
| 12 | # This is a proxy related to the first panel and it doesn't seem useful. | 12 | # This is a proxy related to the first panel and it doesn't seem useful. |
| 13 | excluded_nodes: "Panels/Entry/entry_proxied_fake" | 13 | excluded_nodes: "Panels/Entry/entry_proxied_fake" |
| 14 | # The gift map entrance is created by the mod. | 14 | # The gift map entrance is created by the mod. |
| 15 | custom_nodes: "Components/GiftMapEntrance/GongusPanel" | ||
| 15 | custom_nodes: "Components/GiftMapEntrance/HatkirbyPanel" | 16 | custom_nodes: "Components/GiftMapEntrance/HatkirbyPanel" |
| 16 | custom_nodes: "Components/GiftMapEntrance/IcelyPanel" | 17 | custom_nodes: "Components/GiftMapEntrance/IcelyPanel" |
| 17 | custom_nodes: "Components/GiftMapEntrance/KirbyPanel" | 18 | custom_nodes: "Components/GiftMapEntrance/KirbyPanel" |
| 19 | custom_nodes: "Components/GiftMapEntrance/KiwiPanel" | ||
| 18 | custom_nodes: "Components/GiftMapEntrance/Panel" | 20 | custom_nodes: "Components/GiftMapEntrance/Panel" |
| 19 | custom_nodes: "Components/GiftMapEntrance/QPanel" | 21 | custom_nodes: "Components/GiftMapEntrance/QPanel" |
| 20 | custom_nodes: "Components/GiftMapEntrance/SouveyPanel" | 22 | custom_nodes: "Components/GiftMapEntrance/SouveyPanel" |
| diff --git a/data/maps/the_entry/rooms/Starting Room.txtpb b/data/maps/the_entry/rooms/Starting Room.txtpb index 9c73766..d01d807 100644 --- a/data/maps/the_entry/rooms/Starting Room.txtpb +++ b/data/maps/the_entry/rooms/Starting Room.txtpb | |||
| @@ -55,9 +55,11 @@ panels { | |||
| 55 | # The puzzle solution doesn't matter. We'll change it to the player's name | 55 | # The puzzle solution doesn't matter. We'll change it to the player's name |
| 56 | # for fun. | 56 | # for fun. |
| 57 | symbols: QUESTION | 57 | symbols: QUESTION |
| 58 | proxies { answer: "gongus" path: "Components/GiftMapEntrance/GongusPanel" } | ||
| 58 | proxies { answer: "hatkirby" path: "Components/GiftMapEntrance/HatkirbyPanel" } | 59 | proxies { answer: "hatkirby" path: "Components/GiftMapEntrance/HatkirbyPanel" } |
| 59 | proxies { answer: "icely" path: "Components/GiftMapEntrance/IcelyPanel" } | 60 | proxies { answer: "icely" path: "Components/GiftMapEntrance/IcelyPanel" } |
| 60 | proxies { answer: "kirby" path: "Components/GiftMapEntrance/KirbyPanel" } | 61 | proxies { answer: "kirby" path: "Components/GiftMapEntrance/KirbyPanel" } |
| 62 | proxies { answer: "kiwi" path: "Components/GiftMapEntrance/KiwiPanel" } | ||
| 61 | proxies { answer: "q" path: "Components/GiftMapEntrance/QPanel" } | 63 | proxies { answer: "q" path: "Components/GiftMapEntrance/QPanel" } |
| 62 | proxies { answer: "souvey" path: "Components/GiftMapEntrance/SouveyPanel" } | 64 | proxies { answer: "souvey" path: "Components/GiftMapEntrance/SouveyPanel" } |
| 63 | proxies { answer: "star" path: "Components/GiftMapEntrance/StarPanel" } | 65 | proxies { answer: "star" path: "Components/GiftMapEntrance/StarPanel" } |
| diff --git a/data/maps/the_fuzzy/connections.txtpb b/data/maps/the_fuzzy/connections.txtpb new file mode 100644 index 0000000..ea39f34 --- /dev/null +++ b/data/maps/the_fuzzy/connections.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | connections { | ||
| 2 | from_room: "Main Area" | ||
| 3 | to_room: "Mastery" | ||
| 4 | door { name: "Mastery Door" } | ||
| 5 | } | ||
| diff --git a/data/maps/the_fuzzy/doors.txtpb b/data/maps/the_fuzzy/doors.txtpb new file mode 100644 index 0000000..0f89b80 --- /dev/null +++ b/data/maps/the_fuzzy/doors.txtpb | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | doors { | ||
| 2 | name: "Black Panels" | ||
| 3 | type: LOCATION_ONLY | ||
| 4 | panels { room: "Main Area" name: "WHERETO" } | ||
| 5 | panels { room: "Main Area" name: "COMBINED" } | ||
| 6 | location_room: "Main Area" | ||
| 7 | } | ||
| 8 | doors { | ||
| 9 | name: "Mastery Door" | ||
| 10 | type: EVENT | ||
| 11 | panels { room: "Main Area" name: "OTHERS" } | ||
| 12 | } | ||
| diff --git a/data/maps/the_fuzzy/metadata.txtpb b/data/maps/the_fuzzy/metadata.txtpb new file mode 100644 index 0000000..b4178c7 --- /dev/null +++ b/data/maps/the_fuzzy/metadata.txtpb | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | display_name: "The Fuzzy" | ||
| 2 | type: GIFT_MAP | ||
| 3 | # The map's mastery is created at runtime. | ||
| 4 | custom_nodes: "Components/Collectables/collectable" | ||
| diff --git a/data/maps/the_fuzzy/rooms/Main Area.txtpb b/data/maps/the_fuzzy/rooms/Main Area.txtpb new file mode 100644 index 0000000..9c06df8 --- /dev/null +++ b/data/maps/the_fuzzy/rooms/Main Area.txtpb | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | name: "Main Area" | ||
| 2 | panels { | ||
| 3 | name: "Blank" | ||
| 4 | path: "Panels/Room_1/panel_1" | ||
| 5 | clue: "" | ||
| 6 | answer: "2475" | ||
| 7 | symbols: LINGO | ||
| 8 | symbols: QUESTION | ||
| 9 | } | ||
| 10 | panels { | ||
| 11 | name: "TUTU" | ||
| 12 | path: "Panels/Room_1/panel_2" | ||
| 13 | clue: "tutu" | ||
| 14 | answer: "22" | ||
| 15 | symbols: ZERO | ||
| 16 | symbols: EVAL | ||
| 17 | } | ||
| 18 | panels { | ||
| 19 | name: "LOTTO" | ||
| 20 | path: "Panels/Room_1/panel_3" | ||
| 21 | clue: "lotto" | ||
| 22 | answer: "22222222" | ||
| 23 | symbols: ZERO | ||
| 24 | symbols: EVAL | ||
| 25 | } | ||
| 26 | panels { | ||
| 27 | name: "WHERETO" | ||
| 28 | path: "Panels/Room_1/panel_10" | ||
| 29 | clue: "whereto" | ||
| 30 | answer: "sides" | ||
| 31 | symbols: QUESTION | ||
| 32 | } | ||
| 33 | panels { | ||
| 34 | name: "DICE" | ||
| 35 | path: "Panels/Room_1/panel_11" | ||
| 36 | clue: "dice" | ||
| 37 | answer: "4935" | ||
| 38 | symbols: QUESTION | ||
| 39 | } | ||
| 40 | panels { | ||
| 41 | name: "CAGED" | ||
| 42 | path: "Panels/Room_1/panel_12" | ||
| 43 | clue: "caged" | ||
| 44 | answer: "31754" | ||
| 45 | symbols: QUESTION | ||
| 46 | } | ||
| 47 | panels { | ||
| 48 | name: "BEFORE" | ||
| 49 | path: "Panels/Room_1/panel_13" | ||
| 50 | clue: "before" | ||
| 51 | answer: "100" | ||
| 52 | symbols: ZERO | ||
| 53 | symbols: EVAL | ||
| 54 | } | ||
| 55 | panels { | ||
| 56 | name: "TOED" | ||
| 57 | path: "Panels/Room_1/panel_14" | ||
| 58 | clue: "toed" | ||
| 59 | answer: "108" | ||
| 60 | symbols: ZERO | ||
| 61 | symbols: EVAL | ||
| 62 | } | ||
| 63 | panels { | ||
| 64 | name: "FORGED" | ||
| 65 | path: "Panels/Room_1/panel_15" | ||
| 66 | clue: "forged" | ||
| 67 | answer: "3016" | ||
| 68 | symbols: ZERO | ||
| 69 | symbols: EVAL | ||
| 70 | } | ||
| 71 | panels { | ||
| 72 | name: "OTHERS" | ||
| 73 | path: "Panels/Room_1/panel_4" | ||
| 74 | clue: "others" | ||
| 75 | answer: "34390869" | ||
| 76 | symbols: QUESTION | ||
| 77 | } | ||
| 78 | panels { | ||
| 79 | name: "COMBINED" | ||
| 80 | path: "Panels/Room_1/panel_9" | ||
| 81 | clue: "combined" | ||
| 82 | answer: "added" | ||
| 83 | symbols: SUN | ||
| 84 | } | ||
| 85 | panels { | ||
| 86 | name: "ACHIEVES" | ||
| 87 | path: "Panels/Room_1/panel_5" | ||
| 88 | clue: "achieves" | ||
| 89 | answer: "4214" | ||
| 90 | symbols: QUESTION | ||
| 91 | } | ||
| 92 | panels { | ||
| 93 | name: "UNVEILED" | ||
| 94 | path: "Panels/Room_1/panel_6" | ||
| 95 | clue: "unveiled" | ||
| 96 | answer: "12122021" | ||
| 97 | symbols: QUESTION | ||
| 98 | } | ||
| 99 | panels { | ||
| 100 | name: "FIRST" | ||
| 101 | path: "Panels/Room_1/panel_8" | ||
| 102 | clue: "first" | ||
| 103 | answer: "1" | ||
| 104 | symbols: QUESTION | ||
| 105 | } | ||
| 106 | panels { | ||
| 107 | name: "BOTH" | ||
| 108 | path: "Panels/Room_1/panel_7" | ||
| 109 | clue: "both" | ||
| 110 | answer: "2" | ||
| 111 | symbols: QUESTION | ||
| 112 | } | ||
| 113 | ports { | ||
| 114 | name: "WORLDPORT" | ||
| 115 | display_name: "Entrance" | ||
| 116 | path: "Components/Warps/worldport" | ||
| 117 | destination { x: 0 y: 0 z: 9 } | ||
| 118 | rotation: 0 | ||
| 119 | } | ||
| diff --git a/data/maps/the_fuzzy/rooms/Mastery.txtpb b/data/maps/the_fuzzy/rooms/Mastery.txtpb new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/data/maps/the_fuzzy/rooms/Mastery.txtpb | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | name: "Mastery" | ||
| 2 | masteries { | ||
| 3 | name: "MASTERY" | ||
| 4 | path: "Components/Collectables/collectable" | ||
| 5 | } | ||
| diff --git a/data/maps/the_relentless/doors.txtpb b/data/maps/the_relentless/doors.txtpb index 11f6369..727599f 100644 --- a/data/maps/the_relentless/doors.txtpb +++ b/data/maps/the_relentless/doors.txtpb | |||
| @@ -1,17 +1,42 @@ | |||
| 1 | doors { | 1 | doors { |
| 2 | name: "Left/Turn Door" | 2 | name: "Turn Only Puzzles" |
| 3 | type: LOCATION_ONLY | ||
| 4 | panels { room: "Turn Room" name: "HIDE (1)" } | ||
| 5 | panels { room: "Turn Room" name: "HIDE (2)" } | ||
| 6 | panels { room: "Turn Room" name: "MORE" } | ||
| 7 | location_room: "Turn Room" | ||
| 8 | } | ||
| 9 | doors { | ||
| 10 | name: "Shop Only Puzzles" | ||
| 11 | type: LOCATION_ONLY | ||
| 12 | panels { room: "Shop Room" name: "LEFT (1)" } | ||
| 13 | panels { room: "Shop Room" name: "LEFT (2)" } | ||
| 14 | panels { room: "Shop Room" name: "EXIT (1)" } | ||
| 15 | panels { room: "Shop Room" name: "EXIT (2)" } | ||
| 16 | panels { room: "Shop Room" name: "EXIT (3)" } | ||
| 17 | location_room: "Shop Room" | ||
| 18 | } | ||
| 19 | doors { | ||
| 20 | name: "Left Only Puzzles" | ||
| 3 | type: LOCATION_ONLY | 21 | type: LOCATION_ONLY |
| 4 | panels { room: "Left Room" name: "HIDE" } | 22 | panels { room: "Left Room" name: "HIDE" } |
| 5 | panels { room: "Left Room" name: "LEFT" } | 23 | panels { room: "Left Room" name: "LEFT" } |
| 6 | panels { room: "Left Room" name: "MORE" } | 24 | panels { room: "Left Room" name: "MORE" } |
| 25 | location_room: "Left Room" | ||
| 26 | } | ||
| 27 | doors { | ||
| 28 | name: "Left/Turn Door" | ||
| 29 | type: EVENT | ||
| 30 | panels { room: "Left Room" name: "HIDE" } | ||
| 31 | panels { room: "Left Room" name: "LEFT" } | ||
| 32 | panels { room: "Left Room" name: "MORE" } | ||
| 7 | panels { room: "Turn Room" name: "HIDE (1)" } | 33 | panels { room: "Turn Room" name: "HIDE (1)" } |
| 8 | panels { room: "Turn Room" name: "HIDE (2)" } | 34 | panels { room: "Turn Room" name: "HIDE (2)" } |
| 9 | panels { room: "Turn Room" name: "MORE" } | 35 | panels { room: "Turn Room" name: "MORE" } |
| 10 | location_room: "Turn Room" | ||
| 11 | } | 36 | } |
| 12 | doors { | 37 | doors { |
| 13 | name: "Turn/Shop Door" | 38 | name: "Turn/Shop Door" |
| 14 | type: LOCATION_ONLY | 39 | type: EVENT |
| 15 | panels { room: "Turn Room" name: "HIDE (1)" } | 40 | panels { room: "Turn Room" name: "HIDE (1)" } |
| 16 | panels { room: "Turn Room" name: "HIDE (2)" } | 41 | panels { room: "Turn Room" name: "HIDE (2)" } |
| 17 | panels { room: "Turn Room" name: "MORE" } | 42 | panels { room: "Turn Room" name: "MORE" } |
| @@ -20,7 +45,6 @@ doors { | |||
| 20 | panels { room: "Shop Room" name: "EXIT (1)" } | 45 | panels { room: "Shop Room" name: "EXIT (1)" } |
| 21 | panels { room: "Shop Room" name: "EXIT (2)" } | 46 | panels { room: "Shop Room" name: "EXIT (2)" } |
| 22 | panels { room: "Shop Room" name: "EXIT (3)" } | 47 | panels { room: "Shop Room" name: "EXIT (3)" } |
| 23 | location_room: "Turn Room" | ||
| 24 | } | 48 | } |
| 25 | doors { | 49 | doors { |
| 26 | name: "All Doors" | 50 | name: "All Doors" |
| diff --git a/data/metadata.txtpb b/data/metadata.txtpb index c5c48c2..eb3fe54 100644 --- a/data/metadata.txtpb +++ b/data/metadata.txtpb | |||
| @@ -52,3 +52,5 @@ special_names: "Anti W" | |||
| 52 | special_names: "Anti X" | 52 | special_names: "Anti X" |
| 53 | special_names: "Anti Y" | 53 | special_names: "Anti Y" |
| 54 | special_names: "Anti Z" | 54 | special_names: "Anti Z" |
| 55 | # Numbers for The Fuzzy | ||
| 56 | special_names: "Numbers" | ||
| diff --git a/proto/data.proto b/proto/data.proto index 0f668f2..be18100 100644 --- a/proto/data.proto +++ b/proto/data.proto | |||
| @@ -149,7 +149,7 @@ message Door { | |||
| 149 | repeated KeyholderAnswer keyholders = 13; | 149 | repeated KeyholderAnswer keyholders = 13; |
| 150 | repeated uint64 rooms = 14; | 150 | repeated uint64 rooms = 14; |
| 151 | repeated uint64 doors = 15; | 151 | repeated uint64 doors = 15; |
| 152 | repeated uint64 endings = 16; | 152 | optional bool white_ending = 16; |
| 153 | optional bool double_letters = 18; | 153 | optional bool double_letters = 18; |
| 154 | repeated string senders = 19; | 154 | repeated string senders = 19; |
| 155 | 155 | ||
| diff --git a/proto/human.proto b/proto/human.proto index df33a5e..a5f7a7c 100644 --- a/proto/human.proto +++ b/proto/human.proto | |||
| @@ -104,7 +104,7 @@ message HumanDoor { | |||
| 104 | repeated KeyholderIdentifier keyholders = 10; | 104 | repeated KeyholderIdentifier keyholders = 10; |
| 105 | repeated RoomIdentifier rooms = 11; | 105 | repeated RoomIdentifier rooms = 11; |
| 106 | repeated DoorIdentifier doors = 12; | 106 | repeated DoorIdentifier doors = 12; |
| 107 | repeated string endings = 13; | 107 | optional bool white_ending = 13; |
| 108 | optional bool double_letters = 15; | 108 | optional bool double_letters = 15; |
| 109 | 109 | ||
| 110 | // Sender nodes to be added to the list of requirements for triggering the | 110 | // Sender nodes to be added to the list of requirements for triggering the |
| diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index e3ab100..bda4ee4 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp | |||
| @@ -411,8 +411,8 @@ class DataPacker { | |||
| 411 | container_.FindOrAddDoor(map_name, di.name(), current_map_name)); | 411 | container_.FindOrAddDoor(map_name, di.name(), current_map_name)); |
| 412 | } | 412 | } |
| 413 | 413 | ||
| 414 | for (const std::string& ending_name : h_door.endings()) { | 414 | if (h_door.has_white_ending()) { |
| 415 | door.add_endings(container_.FindOrAddEnding(ending_name)); | 415 | door.set_white_ending(h_door.white_ending()); |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | if (h_door.has_control_center_color()) { | 418 | if (h_door.has_control_center_color()) { |
| diff --git a/tools/validator/human_processor.cpp b/tools/validator/human_processor.cpp index 5a1c095..407d951 100644 --- a/tools/validator/human_processor.cpp +++ b/tools/validator/human_processor.cpp | |||
| @@ -373,11 +373,6 @@ class HumanProcessor { | |||
| 373 | DoorInfo& other_door_info = info_.doors[complete_door_identifier]; | 373 | DoorInfo& other_door_info = info_.doors[complete_door_identifier]; |
| 374 | other_door_info.doors_referenced_by.push_back(door_identifier); | 374 | other_door_info.doors_referenced_by.push_back(door_identifier); |
| 375 | } | 375 | } |
| 376 | |||
| 377 | for (const std::string& ei : h_door.endings()) { | ||
| 378 | EndingInfo& ending_info = info_.endings[ei]; | ||
| 379 | ending_info.doors_referenced_by.push_back(door_identifier); | ||
| 380 | } | ||
| 381 | } | 376 | } |
| 382 | 377 | ||
| 383 | void ProcessConnectionsFile(std::filesystem::path path, | 378 | void ProcessConnectionsFile(std::filesystem::path path, |
| diff --git a/tools/validator/structs.h b/tools/validator/structs.h index a6787cf..51215e9 100644 --- a/tools/validator/structs.h +++ b/tools/validator/structs.h | |||
| @@ -107,8 +107,6 @@ struct LetterInfo { | |||
| 107 | struct EndingInfo { | 107 | struct EndingInfo { |
| 108 | std::vector<RoomIdentifier> defined_in; | 108 | std::vector<RoomIdentifier> defined_in; |
| 109 | bool has_id = false; | 109 | bool has_id = false; |
| 110 | |||
| 111 | std::vector<DoorIdentifier> doors_referenced_by; | ||
| 112 | }; | 110 | }; |
| 113 | 111 | ||
| 114 | struct PanelNameInfo { | 112 | struct PanelNameInfo { |
| diff --git a/tools/validator/validator.cpp b/tools/validator/validator.cpp index 43d842d..e1fc138 100644 --- a/tools/validator/validator.cpp +++ b/tools/validator/validator.cpp | |||
| @@ -111,7 +111,7 @@ class Validator { | |||
| 111 | return false; | 111 | return false; |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | if (h_door.keyholders_size() > 0 || h_door.endings_size() > 0 || | 114 | if (h_door.keyholders_size() > 0 || h_door.white_ending() || |
| 115 | h_door.complete_at() > 0) { | 115 | h_door.complete_at() > 0) { |
| 116 | return true; | 116 | return true; |
| 117 | } | 117 | } |
| @@ -488,12 +488,6 @@ class Validator { | |||
| 488 | std::cout << "Ending " << ending_name | 488 | std::cout << "Ending " << ending_name |
| 489 | << " has no definition, but was referenced:" << std::endl; | 489 | << " has no definition, but was referenced:" << std::endl; |
| 490 | 490 | ||
| 491 | for (const DoorIdentifier& door_identifier : | ||
| 492 | ending_info.doors_referenced_by) { | ||
| 493 | std::cout << " DOOR " << door_identifier.ShortDebugString() | ||
| 494 | << std::endl; | ||
| 495 | } | ||
| 496 | |||
| 497 | if (ending_info.has_id) { | 491 | if (ending_info.has_id) { |
| 498 | std::cout << " An AP ID is present." << std::endl; | 492 | std::cout << " An AP ID is present." << std::endl; |
| 499 | } | 493 | } |
