diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-10-25 11:20:55 -0400 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-10-25 11:20:55 -0400 |
| commit | ea87cbbe4a23ceff72f31e461c7ead32f560031e (patch) | |
| tree | 2983fb51bc4f9a3f7ccaf0cb71a2dfad54eceeeb /apworld | |
| parent | 8b07ae101fb9e0f2fc0e2d46a4cd77ff78d62d13 (diff) | |
| download | lingo2-archipelago-ea87cbbe4a23ceff72f31e461c7ead32f560031e.tar.gz lingo2-archipelago-ea87cbbe4a23ceff72f31e461c7ead32f560031e.tar.bz2 lingo2-archipelago-ea87cbbe4a23ceff72f31e461c7ead32f560031e.zip | |
Made White Ending customizable
Diffstat (limited to 'apworld')
| -rw-r--r-- | apworld/__init__.py | 2 | ||||
| -rw-r--r-- | apworld/client/gamedata.gd | 6 | ||||
| -rw-r--r-- | apworld/client/main.gd | 6 | ||||
| -rw-r--r-- | apworld/client/manager.gd | 4 | ||||
| -rw-r--r-- | apworld/client/player.gd | 127 | ||||
| -rw-r--r-- | apworld/options.py | 22 | ||||
| -rw-r--r-- | apworld/player_logic.py | 29 | ||||
| -rw-r--r-- | apworld/static_logic.py | 2 |
8 files changed, 187 insertions, 11 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/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 1d0df1f..a3b21c5 100644 --- a/apworld/client/main.gd +++ b/apworld/client/main.gd | |||
| @@ -83,6 +83,12 @@ func _ready(): | |||
| 83 | compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") | 83 | compass_overlay_instance.SCRIPT_compass = runtime.load_script("compass.gd") |
| 84 | global.add_child(compass_overlay_instance) | 84 | global.add_child(compass_overlay_instance) |
| 85 | 85 | ||
| 86 | unlocks.data["advanced_mastery"] = "" | ||
| 87 | unlocks.data["charismatic_mastery"] = "" | ||
| 88 | unlocks.data["crystalline_mastery"] = "" | ||
| 89 | unlocks.data["icarus_mastery"] = "" | ||
| 90 | unlocks.data["stellar_mastery"] = "" | ||
| 91 | |||
| 86 | var ap = global.get_node("Archipelago") | 92 | var ap = global.get_node("Archipelago") |
| 87 | var gamedata = global.get_node("Gamedata") | 93 | var gamedata = global.get_node("Gamedata") |
| 88 | ap.ap_connected.connect(connectionSuccessful) | 94 | ap.ap_connected.connect(connectionSuccessful) |
| diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index 41ab648..399d6a5 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 |
| @@ -443,7 +445,9 @@ func _client_connected(slot_data): | |||
| 443 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) | 445 | daedalus_roof_access = bool(slot_data.get("daedalus_roof_access", false)) |
| 444 | enable_gift_maps = slot_data.get("enable_gift_maps", []) | 446 | enable_gift_maps = slot_data.get("enable_gift_maps", []) |
| 445 | enable_icarus = bool(slot_data.get("enable_icarus", false)) | 447 | enable_icarus = bool(slot_data.get("enable_icarus", false)) |
| 448 | endings_requirement = int(slot_data.get("endings_requirement", 0)) | ||
| 446 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) | 449 | keyholder_sanity = bool(slot_data.get("keyholder_sanity", false)) |
| 450 | masteries_requirement = int(slot_data.get("masteries_requirement", 0)) | ||
| 447 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) | 451 | shuffle_control_center_colors = bool(slot_data.get("shuffle_control_center_colors", false)) |
| 448 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) | 452 | shuffle_doors = bool(slot_data.get("shuffle_doors", false)) |
| 449 | shuffle_gallery_paintings = bool(slot_data.get("shuffle_gallery_paintings", false)) | 453 | 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..9acb942 100644 --- a/apworld/client/player.gd +++ b/apworld/client/player.gd | |||
| @@ -19,6 +19,84 @@ 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("icarus") | ||
| 29 | _set_up_mastery_listener("stellar") | ||
| 30 | |||
| 31 | if ap.endings_requirement != 12 or ap.masteries_requirement != 0: | ||
| 32 | # Set up listeners for the potential White Ending requirements. | ||
| 33 | var merging_prefab = preload("res://objects/nodes/listeners/mergingListener.tscn") | ||
| 34 | |||
| 35 | var old_door = get_node("/root/scene/Components/Doors/entry_19") | ||
| 36 | var new_door = old_door.duplicate() | ||
| 37 | new_door.name = "entry_19_new" | ||
| 38 | new_door.senders.clear() | ||
| 39 | new_door.senderGroup.clear() | ||
| 40 | new_door.excludeSenders.clear() | ||
| 41 | |||
| 42 | if ap.endings_requirement == 12: | ||
| 43 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 44 | elif ap.endings_requirement > 0: | ||
| 45 | if ap.masteries_requirement == 0: | ||
| 46 | new_door.senderGroup.append(NodePath("/root/scene/Meshes/Trophies/Listeners")) | ||
| 47 | new_door.complete_at = ap.endings_requirement | ||
| 48 | else: | ||
| 49 | var endings_merge = merging_prefab.instantiate() | ||
| 50 | endings_merge.name = "EndingsMerge" | ||
| 51 | endings_merge.senderGroup.append( | ||
| 52 | NodePath("/root/scene/Meshes/Trophies/Listeners") | ||
| 53 | ) | ||
| 54 | endings_merge.complete_at = ap.endings_requirement | ||
| 55 | get_node("/root/scene/Components").add_child.call_deferred(endings_merge) | ||
| 56 | new_door.senders.append(NodePath("/root/scene/Components/EndingsMerge")) | ||
| 57 | |||
| 58 | var max_masteries = 13 + ap.enable_gift_maps.size() | ||
| 59 | if ap.enable_icarus: | ||
| 60 | max_masteries += 1 | ||
| 61 | |||
| 62 | if ap.masteries_requirement == max_masteries: | ||
| 63 | new_door.senderGroup.append( | ||
| 64 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 65 | ) | ||
| 66 | new_door.excludeSenders.append( | ||
| 67 | NodePath( | ||
| 68 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 69 | ) | ||
| 70 | ) | ||
| 71 | elif ap.masteries_requirement > 0: | ||
| 72 | if ap.endings_requirement == 0: | ||
| 73 | new_door.senderGroup.append( | ||
| 74 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 75 | ) | ||
| 76 | new_door.excludeSenders.append( | ||
| 77 | NodePath( | ||
| 78 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 79 | ) | ||
| 80 | ) | ||
| 81 | new_door.complete_at = ap.masteries_requirement | ||
| 82 | else: | ||
| 83 | var masteries_merge = merging_prefab.instantiate() | ||
| 84 | masteries_merge.name = "MasteriesMerge" | ||
| 85 | masteries_merge.senderGroup.append( | ||
| 86 | NodePath("/root/scene/Meshes/Trophies/MasteryListeners") | ||
| 87 | ) | ||
| 88 | masteries_merge.excludeSenders.append( | ||
| 89 | NodePath( | ||
| 90 | "/root/scene/Meshes/Trophies/MasteryListeners/unlockReaderListenerWhite" | ||
| 91 | ) | ||
| 92 | ) | ||
| 93 | masteries_merge.complete_at = ap.masteries_requirement | ||
| 94 | get_node("/root/scene/Components").add_child.call_deferred(masteries_merge) | ||
| 95 | new_door.senders.append(NodePath("/root/scene/Components/MasteriesMerge")) | ||
| 96 | |||
| 97 | old_door.queue_free() | ||
| 98 | get_node("/root/scene/Components/Doors").add_child.call_deferred(new_door) | ||
| 99 | |||
| 22 | # Block off roof access in Daedalus. | 100 | # Block off roof access in Daedalus. |
| 23 | if global.map == "daedalus" and not ap.daedalus_roof_access: | 101 | if global.map == "daedalus" and not ap.daedalus_roof_access: |
| 24 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) | 102 | _set_up_invis_wall(75.5, 11, -24.5, 1, 10, 49) |
| @@ -297,6 +375,7 @@ func _ready(): | |||
| 297 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 375 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 298 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 376 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 299 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 377 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 378 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 300 | 379 | ||
| 301 | var mastery = collectable_prefab.instantiate() | 380 | var mastery = collectable_prefab.instantiate() |
| 302 | mastery.name = "collectable" | 381 | mastery.name = "collectable" |
| @@ -314,6 +393,13 @@ func _ready(): | |||
| 314 | tpl.nested = true | 393 | tpl.nested = true |
| 315 | mastery.add_child.call_deferred(tpl) | 394 | mastery.add_child.call_deferred(tpl) |
| 316 | 395 | ||
| 396 | var usl = usl_prefab.instantiate() | ||
| 397 | usl.name = "unlockSetterListenerMastery" | ||
| 398 | usl.key = "icarus_mastery" | ||
| 399 | usl.value = "unlocked" | ||
| 400 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 401 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 402 | |||
| 317 | var saver = saver_prefab.instantiate() | 403 | var saver = saver_prefab.instantiate() |
| 318 | saver.name = "saver_collectables" | 404 | saver.name = "saver_collectables" |
| 319 | saver.type = "collectables" | 405 | saver.type = "collectables" |
| @@ -325,6 +411,7 @@ func _ready(): | |||
| 325 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 411 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 326 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 412 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 327 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 413 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 414 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 328 | 415 | ||
| 329 | var mastery = collectable_prefab.instantiate() | 416 | var mastery = collectable_prefab.instantiate() |
| 330 | mastery.name = "collectable" | 417 | mastery.name = "collectable" |
| @@ -343,6 +430,13 @@ func _ready(): | |||
| 343 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) | 430 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_31")) |
| 344 | mastery.add_child.call_deferred(tpl) | 431 | mastery.add_child.call_deferred(tpl) |
| 345 | 432 | ||
| 433 | var usl = usl_prefab.instantiate() | ||
| 434 | usl.name = "unlockSetterListenerMastery" | ||
| 435 | usl.key = "advanced_mastery" | ||
| 436 | usl.value = "unlocked" | ||
| 437 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 438 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 439 | |||
| 346 | var saver = saver_prefab.instantiate() | 440 | var saver = saver_prefab.instantiate() |
| 347 | saver.name = "saver_collectables" | 441 | saver.name = "saver_collectables" |
| 348 | saver.type = "collectables" | 442 | saver.type = "collectables" |
| @@ -353,6 +447,7 @@ func _ready(): | |||
| 353 | if global.map == "the_charismatic": | 447 | if global.map == "the_charismatic": |
| 354 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 448 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 355 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 449 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 450 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 356 | 451 | ||
| 357 | var mastery = collectable_prefab.instantiate() | 452 | var mastery = collectable_prefab.instantiate() |
| 358 | mastery.name = "collectable" | 453 | mastery.name = "collectable" |
| @@ -362,6 +457,13 @@ func _ready(): | |||
| 362 | mastery.material_override = load("res://assets/materials/gold.material") | 457 | mastery.material_override = load("res://assets/materials/gold.material") |
| 363 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) | 458 | get_node("/root/scene/Components/Collectables").add_child.call_deferred(mastery) |
| 364 | 459 | ||
| 460 | var usl = usl_prefab.instantiate() | ||
| 461 | usl.name = "unlockSetterListenerMastery" | ||
| 462 | usl.key = "charismatic_mastery" | ||
| 463 | usl.value = "unlocked" | ||
| 464 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 465 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 466 | |||
| 365 | var saver = saver_prefab.instantiate() | 467 | var saver = saver_prefab.instantiate() |
| 366 | saver.name = "saver_collectables" | 468 | saver.name = "saver_collectables" |
| 367 | saver.type = "collectables" | 469 | saver.type = "collectables" |
| @@ -373,6 +475,7 @@ func _ready(): | |||
| 373 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 475 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 374 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 476 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 375 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") | 477 | var tpl_prefab = preload("res://objects/nodes/listeners/teleportListener.tscn") |
| 478 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 376 | 479 | ||
| 377 | var mastery = collectable_prefab.instantiate() | 480 | var mastery = collectable_prefab.instantiate() |
| 378 | mastery.name = "collectable" | 481 | mastery.name = "collectable" |
| @@ -389,6 +492,13 @@ func _ready(): | |||
| 389 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) | 492 | tpl.senders.append(NodePath("/root/scene/Panels/Room_1/panel_3")) |
| 390 | mastery.add_child.call_deferred(tpl) | 493 | mastery.add_child.call_deferred(tpl) |
| 391 | 494 | ||
| 495 | var usl = usl_prefab.instantiate() | ||
| 496 | usl.name = "unlockSetterListenerMastery" | ||
| 497 | usl.key = "crystalline_mastery" | ||
| 498 | usl.value = "unlocked" | ||
| 499 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 500 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 501 | |||
| 392 | var saver = saver_prefab.instantiate() | 502 | var saver = saver_prefab.instantiate() |
| 393 | saver.name = "saver_collectables" | 503 | saver.name = "saver_collectables" |
| 394 | saver.type = "collectables" | 504 | saver.type = "collectables" |
| @@ -399,6 +509,7 @@ func _ready(): | |||
| 399 | if global.map == "the_stellar": | 509 | if global.map == "the_stellar": |
| 400 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") | 510 | var collectable_prefab = preload("res://objects/nodes/collectable.tscn") |
| 401 | var saver_prefab = preload("res://objects/nodes/saver.tscn") | 511 | var saver_prefab = preload("res://objects/nodes/saver.tscn") |
| 512 | var usl_prefab = preload("res://objects/nodes/listeners/unlockSetterListener.tscn") | ||
| 402 | 513 | ||
| 403 | var collectables = Node.new() | 514 | var collectables = Node.new() |
| 404 | collectables.name = "Collectables" | 515 | collectables.name = "Collectables" |
| @@ -412,6 +523,13 @@ func _ready(): | |||
| 412 | collectables.add_child.call_deferred(mastery) | 523 | collectables.add_child.call_deferred(mastery) |
| 413 | get_node("/root/scene/Components").add_child.call_deferred(collectables) | 524 | get_node("/root/scene/Components").add_child.call_deferred(collectables) |
| 414 | 525 | ||
| 526 | var usl = usl_prefab.instantiate() | ||
| 527 | usl.name = "unlockSetterListenerMastery" | ||
| 528 | usl.key = "stellar_mastery" | ||
| 529 | usl.value = "unlocked" | ||
| 530 | usl.senders.append(NodePath("/root/scene/Components/Collectables/collectable")) | ||
| 531 | get_node("/root/scene/Components").add_child.call_deferred(usl) | ||
| 532 | |||
| 415 | var saver = saver_prefab.instantiate() | 533 | var saver = saver_prefab.instantiate() |
| 416 | saver.name = "saver_collectables" | 534 | saver.name = "saver_collectables" |
| 417 | saver.type = "collectables" | 535 | saver.type = "collectables" |
| @@ -585,5 +703,14 @@ func _set_up_invis_wall(x, y, z, sx, sy, sz): | |||
| 585 | get_parent().add_child.call_deferred(newwall) | 703 | get_parent().add_child.call_deferred(newwall) |
| 586 | 704 | ||
| 587 | 705 | ||
| 706 | func _set_up_mastery_listener(name): | ||
| 707 | var prefab = preload("res://objects/nodes/listeners/unlockReaderListener.tscn") | ||
| 708 | var url = prefab.instantiate() | ||
| 709 | url.name = "unlockReaderListenerMastery_%s" % name | ||
| 710 | url.key = "%s_mastery" % name | ||
| 711 | url.value = "unlocked" | ||
| 712 | get_node("/root/scene/Meshes/Trophies/MasteryListeners").add_child.call_deferred(url) | ||
| 713 | |||
| 714 | |||
| 588 | func _process(_dt): | 715 | func _process(_dt): |
| 589 | compass.update_rotation(global_rotation.y) | 716 | 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..e21e2c3 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py | |||
| @@ -249,6 +249,13 @@ class Lingo2PlayerLogic: | |||
| 249 | self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps | 249 | self.shuffled_maps = set(game_map.id for game_map in world.static_logic.objects.maps |
| 250 | if should_shuffle_map(game_map)) | 250 | if should_shuffle_map(game_map)) |
| 251 | 251 | ||
| 252 | maximum_masteries = 13 + len(world.options.enable_gift_maps.value) | ||
| 253 | if world.options.enable_icarus: | ||
| 254 | maximum_masteries += 1 | ||
| 255 | |||
| 256 | if world.options.masteries_requirement > maximum_masteries: | ||
| 257 | world.options.masteries_requirement.value = maximum_masteries | ||
| 258 | |||
| 252 | if self.world.options.shuffle_doors: | 259 | if self.world.options.shuffle_doors: |
| 253 | for progressive in world.static_logic.objects.progressives: | 260 | for progressive in world.static_logic.objects.progressives: |
| 254 | for i in range(0, len(progressive.doors)): | 261 | for i in range(0, len(progressive.doors)): |
| @@ -362,18 +369,23 @@ class Lingo2PlayerLogic: | |||
| 362 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, | 369 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, |
| 363 | AccessRequirements())) | 370 | AccessRequirements())) |
| 364 | 371 | ||
| 372 | if world.options.masteries_requirement > 0: | ||
| 373 | event_name = f"{world.static_logic.get_room_object_map_name(mastery)} - Mastery (Collected)" | ||
| 374 | self.event_loc_item_by_room.setdefault(mastery.room_id, {})[event_name] = "Mastery" | ||
| 375 | |||
| 365 | for ending in world.static_logic.objects.endings: | 376 | for ending in world.static_logic.objects.endings: |
| 366 | if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: | 377 | if world.static_logic.get_room_object_map_id(ending) not in self.shuffled_maps: |
| 367 | continue | 378 | continue |
| 368 | 379 | ||
| 369 | # Don't create a location for your selected ending, and never create a location for White Ending. | 380 | # Don't create a location for your selected ending. Also don't create a location for White Ending if it's |
| 381 | # 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\ | 382 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() != ending.name\ |
| 371 | and ending.name != "WHITE": | 383 | and (ending.name != "WHITE" or world.options.endings_requirement < 12): |
| 372 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, | 384 | self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, |
| 373 | AccessRequirements())) | 385 | AccessRequirements())) |
| 374 | 386 | ||
| 375 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" | 387 | event_name = f"{ending.name.capitalize()} Ending (Achieved)" |
| 376 | item_name = event_name | 388 | item_name = "Ending" |
| 377 | 389 | ||
| 378 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: | 390 | if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: |
| 379 | item_name = "Victory" | 391 | item_name = "Victory" |
| @@ -520,13 +532,12 @@ class Lingo2PlayerLogic: | |||
| 520 | for room in door.rooms: | 532 | for room in door.rooms: |
| 521 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) | 533 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) |
| 522 | 534 | ||
| 523 | for ending_id in door.endings: | 535 | if door.white_ending: |
| 524 | ending = self.world.static_logic.objects.endings[ending_id] | 536 | if self.world.options.endings_requirement > 0: |
| 537 | reqs.progressives["Ending"] = self.world.options.endings_requirement.value | ||
| 525 | 538 | ||
| 526 | if self.world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name: | 539 | if self.world.options.masteries_requirement > 0: |
| 527 | reqs.items.add("Victory") | 540 | reqs.progressives["Mastery"] = self.world.options.masteries_requirement.value |
| 528 | else: | ||
| 529 | reqs.items.add(f"{ending.name.capitalize()} Ending (Achieved)") | ||
| 530 | 541 | ||
| 531 | for sub_door_id in door.doors: | 542 | for sub_door_id in door.doors: |
| 532 | sub_reqs = self.get_door_open_reqs(sub_door_id) | 543 | sub_reqs = self.get_door_open_reqs(sub_door_id) |
| diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 2546007..fb23e4c 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py | |||
| @@ -105,7 +105,7 @@ class Lingo2StaticLogic: | |||
| 105 | if door.type != data_pb2.DoorType.STANDARD: | 105 | if door.type != data_pb2.DoorType.STANDARD: |
| 106 | return None | 106 | return None |
| 107 | 107 | ||
| 108 | if len(door.keyholders) > 0 or len(door.endings) > 0 or door.HasField("complete_at"): | 108 | if len(door.keyholders) > 0 or door.white_ending or door.HasField("complete_at"): |
| 109 | return None | 109 | return None |
| 110 | 110 | ||
| 111 | if len(door.panels) > 4: | 111 | if len(door.panels) > 4: |
