5f6258e3d4
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
195 lines
5.3 KiB
GDScript
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
|