Files
2026-07-06 22:59:47 -07:00

116 lines
4.9 KiB
GDScript

extends SceneTree
## Boss-room gate (AnchorV1.0 §4.5 + 关卡设计指南 §8): starts SEALED until the
## front area is cleared, opens via unseal(), locks behind the player, then
## fires the entry hook, and keeps the air wall fully gone after entry.
var failures: Array[String] = []
var entered_signals := 0
var unsealed_signals := 0
func _init() -> void:
_run.call_deferred()
func _run() -> void:
var stage_scene: PackedScene = load("res://scenes/stage/stage.tscn")
var stage: Node = stage_scene.instantiate()
root.add_child(stage)
await process_frame
var gate: Node = stage.get_node_or_null("BossRoomGate")
_expect_bool(gate != null, true, "stage should carry a BossRoomGate")
if gate == null:
_finish()
return
gate.connect("boss_room_entered", func() -> void: entered_signals += 1)
gate.connect("unsealed", func() -> void: unsealed_signals += 1)
var wall_shape := gate.get_node("GateWall").get_child(0) as CollisionShape2D
var door_visual := gate.get_node_or_null("DoorVisual") as CanvasItem
_expect_bool(bool(gate.get("sealed")), true, "gate starts sealed until the six minions fall (指南 §8)")
_expect_bool(wall_shape.disabled, false, "sealed gate blocks the boss room physically")
_expect_bool(door_visual != null, true, "gate should keep an inspectable DoorVisual node")
if door_visual != null:
_expect_bool(door_visual.visible, false, "sealed boss-room air wall should stay invisible")
_expect_bool(bool(gate.get("locked")), false, "gate starts unlocked")
var flow := root.get_node_or_null("GameFlowManager")
if flow != null:
flow.set("state", &"Gameplay_FrontArea")
flow.set("_boss_room_entered", false)
var player := stage.get_node("ActorsContainer/Player") as Node2D
var boss: Node = stage.get_node("ActorsContainer/Boss")
var floor_y := float(gate.get("floor_y"))
_expect_bool(
(boss as Node2D).global_position.x >= float(gate.get("gate_x")) + float(gate.get("wall_thickness")),
true,
"Boss authored anchor should sit inside the boss-room trigger line so reaching the Boss starts the fight"
)
_expect_bool(bool(boss.get("combat_enabled")), false, "boss combat stays locked until the room is entered")
# While sealed, even a player beyond the threshold must not trigger the lock.
player.global_position = Vector2(float(gate.get("gate_x")) + 120.0, floor_y)
gate.call("_physics_process", 0.016)
_expect_bool(bool(gate.get("locked")), false, "sealed gate never locks")
player.global_position = Vector2(float(gate.get("gate_x")) - 120.0, floor_y)
# Front area cleared → unseal opens the way.
gate.call("unseal")
await process_frame
_expect_bool(bool(gate.get("sealed")), false, "unseal() clears the seal")
_expect_bool(wall_shape.disabled, true, "unsealed gate opens the passage")
if door_visual != null:
_expect_bool(door_visual.visible, false, "unsealed boss-room passage should not draw an air wall")
_expect_int(unsealed_signals, 1, "unseal() announces itself once")
# Player left of the gate: nothing happens.
gate.call("_physics_process", 0.016)
_expect_bool(bool(gate.get("locked")), false, "gate must not lock while the player is in the front area")
# The instant the player steps across the gate line the room starts — the
# boss must NOT wait until the player is point-blank at its anchor.
player.global_position = Vector2(float(gate.get("gate_x")) + 1.0, floor_y)
gate.call("_physics_process", 0.016)
_expect_bool(bool(gate.get("locked")), true, "crossing the gate line immediately locks the gate")
_expect_int(entered_signals, 1, "entering emits boss_room_entered once")
await process_frame
_expect_bool(wall_shape.disabled, true, "locked gate should keep the air wall collision fully disabled")
if door_visual != null:
_expect_bool(door_visual.visible, false, "locked boss-room air wall should remain invisible")
_expect_bool(bool(boss.get("combat_enabled")), true, "locking the gate should wake the Boss even when the flow manager is not driving the scene")
_expect_bool(bool(boss.get("stationary")), false, "locking the gate should release the Boss from training-dummy mode")
if flow != null:
_expect_string(str(flow.get("state")), "Gameplay_BossRoom", "gate entry should switch GameFlow into boss-room gameplay")
gate.call("_physics_process", 0.016)
_expect_int(entered_signals, 1, "the gate never re-fires after locking")
stage.free()
_finish()
func _expect_bool(actual: bool, expected: bool, label: String) -> void:
if actual != expected:
failures.append("%s: expected %s, got %s" % [label, expected, actual])
func _expect_int(actual: int, expected: int, label: String) -> void:
if actual != expected:
failures.append("%s: expected %d, got %d" % [label, expected, actual])
func _expect_string(actual: String, expected: String, label: String) -> void:
if actual != expected:
failures.append("%s: expected %s, got %s" % [label, expected, actual])
func _finish() -> void:
if failures.is_empty():
print("PASS boss room gate")
quit(0)
else:
for failure: String in failures:
push_error(failure)
quit(1)