116 lines
4.9 KiB
GDScript
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)
|