Files
hello-godot/tests/powerup_test.gd
T

195 lines
5.3 KiB
GDScript

extends SceneTree
## 道具系统回归测试(时间驱动):
## L1 双发 → 拾取升级 L2 四发 → 直设 L3 六发(4 直射+2 斜射)
## → 直设 L4 十二发(6 直射+4×22°+2×30°)
## → 满级拾取 +500 分 → 死亡降一级 → 掉落标记敌机死亡产出道具
## 注意:SceneTree._initialize 中 add_child 后节点 _ready 会在首帧才执行,
## 因此容器引用在首个 _process 里惰性获取。
## 运行:godot --headless -s tests/powerup_test.gd
const GLOBAL_TIMEOUT := 30.0
var _phase := 0
var _step := 0
var _phase_time := 0.0
var _time := 0.0
var _game: Game
var _pbullets: Node2D
var _powerups: Node2D
var _score_before := 0
var _fails: Array[String] = []
func _initialize() -> void:
_game = (load("res://scenes/game.tscn") as PackedScene).instantiate()
root.add_child(_game)
print("[powerup_test] game instantiated")
func _fail(msg: String) -> void:
_fails.append(msg)
print("[FAIL] " + msg)
func _next_phase() -> void:
_phase += 1
_step = 0
_phase_time = 0.0
func _restart_step_timer() -> void:
_step += 1
_phase_time = 0.0
## 容器引用就绪前不进入任何阶段(_ready 在首帧执行)
func _ensure_refs() -> bool:
if _pbullets == null:
_pbullets = _game.get_node_or_null("PlayerBullets") as Node2D
_powerups = _game.get_node_or_null("Powerups") as Node2D
return _pbullets != null and _powerups != null
## 清空玩家弹容器并返回清空前数量
func _count_and_clear_bullets() -> int:
var children := _pbullets.get_children()
var n := children.size()
for bullet in children:
_pbullets.remove_child(bullet)
(bullet as Node).queue_free()
return n
func _spawn_pickup_at_player() -> void:
var pickup := Powerup.new()
pickup.position = _game.player.global_position
_powerups.add_child(pickup)
func _check_volley(expected: int, level_name: String) -> void:
var n := _count_and_clear_bullets()
Input.action_release("fire")
if n != expected:
_fail("%s volley=%d expected %d" % [level_name, n, expected])
else:
print("[powerup_test] %s volley ok: %d bullets" % [level_name, n])
func _finish() -> bool:
if _fails.is_empty():
print("[powerup_test] PASS")
else:
print("[powerup_test] FAIL: %d issue(s)" % _fails.size())
quit(0 if _fails.is_empty() else 1)
return true
func _process(delta: float) -> bool:
_time += delta
if _time > GLOBAL_TIMEOUT:
_fail("global timeout in phase %d" % _phase)
return _finish()
if not _ensure_refs():
return false
_phase_time += delta
match _phase:
0:
if _step == 0:
Input.action_press("fire")
_restart_step_timer()
elif _phase_time >= 0.1:
_check_volley(2, "L1")
_next_phase()
1:
match _step:
0:
_spawn_pickup_at_player()
_restart_step_timer()
1:
if _phase_time >= 0.6:
if _game.player.weapon_level != 2:
_fail("pickup did not level up: level=%d" % _game.player.weapon_level)
elif _powerups.get_child_count() != 0:
_fail("pickup not consumed: %d left" % _powerups.get_child_count())
else:
print("[powerup_test] pickup -> L2 ok")
Input.action_press("fire")
_restart_step_timer()
2:
if _phase_time >= 0.1:
_check_volley(4, "L2")
_next_phase()
2:
match _step:
0:
_game.player.weapon_level = 3
Input.action_press("fire")
_restart_step_timer()
1:
if _phase_time >= 0.1:
_check_volley(6, "L3")
_game.player.weapon_level = 4
Input.action_press("fire")
_restart_step_timer()
2:
if _phase_time >= 0.1:
_check_volley(12, "L4")
_next_phase()
3:
if _step == 0:
_score_before = _game.score
_spawn_pickup_at_player()
_restart_step_timer()
elif _phase_time >= 0.6:
if _game.player.weapon_level != 4:
_fail("capped pickup changed level: %d" % _game.player.weapon_level)
elif _game.score != _score_before + 500:
_fail("capped pickup score: %d -> %d (expected +500)" % [_score_before, _game.score])
else:
print("[powerup_test] capped pickup +500 ok")
_next_phase()
4:
if _step == 0:
_game.player.die()
_restart_step_timer()
elif _phase_time >= 0.1:
if _game.player.weapon_level != 3:
_fail("death downgrade: level=%d expected 3" % _game.player.weapon_level)
else:
print("[powerup_test] death downgrade 4->3 ok")
_next_phase()
5:
if _step == 0:
var enemy := EnemySmall.new()
enemy.setup(270.0, 200.0, 100.0)
enemy.drops_powerup = true
_game._enemies.add_child(enemy)
enemy.apply_damage(99)
_restart_step_timer()
elif _phase_time >= 0.5:
if _powerups.get_child_count() != 1:
_fail("dropper enemy produced %d powerups (expected 1)" % _powerups.get_child_count())
else:
print("[powerup_test] dropper spawn ok")
_next_phase()
6:
if _step == 0:
# 清掉前面阶段残留的道具再验证 Boss 必掉 P
for pickup in _powerups.get_children():
_powerups.remove_child(pickup)
(pickup as Node).queue_free()
var boss := _game.spawn_boss(1.0)
boss.apply_damage(99999)
_restart_step_timer()
elif _phase_time >= 1.2:
var kinds := []
for pickup in _powerups.get_children():
kinds.append((pickup as Powerup).kind)
if kinds != [Powerup.Kind.POWER]:
_fail("boss drop kinds=%s expected [POWER]" % str(kinds))
else:
print("[powerup_test] boss always drops POWER ok")
return _finish()
return false