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)