150 lines
6.7 KiB
GDScript
150 lines
6.7 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 呈现域缺陷扫掠断言(行为缺陷定点修批卡① T2 五族):
|
||
# ① 意图蓄力档可达(d2——_intent_charge 分档,旧 GOLD_DEEP 判据恒不可达);
|
||
# ② 庆祝 tween 句柄纪律(d4——重开不叠加、close 必杀);
|
||
# ③ 互斥四口(d5 调试钮同判据 / d8 控制器运行态可查);
|
||
# ⑤ 站桩按敌 id 解析(d12 两口:on_step_row_fx 闪白落点 + enemy_standee_center 弹道落点)。
|
||
# d6/d7 两守卫断言宿主 = system_test;d3/d17/d18 属非行为面(实现报告等价申报)。
|
||
# 运行:godot --headless --path game --script res://tests/unit/presentation/standee_fx_sweep_test.gd(expect 0)
|
||
extends SceneTree
|
||
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
const StandeeBodyScript := preload("res://presentation/views/standee_body.gd")
|
||
const BattleEndScript := preload("res://presentation/views/battle_end_overlay.gd")
|
||
const DebugOverlayScript := preload("res://presentation/views/debug_overlay.gd")
|
||
const TuneSimScript := preload("res://presentation/tune_sim_controller.gd")
|
||
const StageScript := preload("res://presentation/views/battle_stage_view.gd")
|
||
|
||
|
||
# 假调试面板(TuneSimController 所需 setter 面 + 树内节点供给 get_tree)
|
||
class FakePanel extends Node:
|
||
var running := false
|
||
var progress := ""
|
||
var report := ""
|
||
var atk := 0
|
||
var hp := 0
|
||
|
||
func atk_pct() -> int:
|
||
return atk
|
||
|
||
func hp_pct() -> int:
|
||
return hp
|
||
|
||
func set_sim_running(r: bool) -> void:
|
||
running = r
|
||
|
||
func set_sim_progress(t: String) -> void:
|
||
progress = t
|
||
|
||
func set_sim_report(t: String) -> void:
|
||
report = t
|
||
|
||
|
||
# 假自动策略(run_level 签名同 level_autoplay——回调内读控制器运行态)
|
||
class FakeDriver extends RefCounted:
|
||
var ctrl
|
||
var seen: Array[bool] = []
|
||
var calls := 0
|
||
|
||
func run_level(_db: Variant, _seed: int, _level: int, _atk: int, _hp: int, _other: bool, _logger: Variant) -> Dictionary:
|
||
calls += 1
|
||
seen.append(ctrl.is_running())
|
||
if calls == 1:
|
||
return {"hero_dmg": 12.0, "rounds": 4, "result": "victory", "hp_left": 3.0, "hp_at_boss": 5.0}
|
||
return {"hero_dmg": 6.0, "rounds": 2, "result": "defeat",
|
||
"defeat_node_index": 3, "defeat_node_type": "battle", "hp_left": 0.0, "hp_at_boss": 0.0}
|
||
|
||
|
||
# 假配置库(TuneSimController 只读 db.run.action_economy.ap_per_round——最小域对象图)
|
||
class FakeActionEconomy:
|
||
var ap_per_round := 2
|
||
|
||
|
||
class FakeRun:
|
||
var action_economy := FakeActionEconomy.new()
|
||
|
||
|
||
class FakeDB:
|
||
var run := FakeRun.new()
|
||
|
||
|
||
func _init() -> void:
|
||
UiKit.motion_scale = 1.0
|
||
|
||
# ---- ⑤ 站桩按 id 解析(两口共用同名字敌载体)----
|
||
var stage = StageScript.new(null)
|
||
root.add_child(stage)
|
||
var s1 = StandeeBodyScript.new("enemy")
|
||
var s2 = StandeeBodyScript.new("enemy")
|
||
stage.world.add_child(s1)
|
||
stage.world.add_child(s2)
|
||
s1.set_data({"display_name": "地精", "enemy_id": "goblin_a", "hp": 3, "max_hp": 3, "order": 1})
|
||
s2.set_data({"display_name": "地精", "enemy_id": "goblin_b", "hp": 3, "max_hp": 3, "order": 2})
|
||
s1.position = Vector2(10, 20)
|
||
s2.position = Vector2(200, 20)
|
||
stage._cards.clear()
|
||
stage._cards.append(s1)
|
||
stage._cards.append(s2)
|
||
Kit.check("站桩 enemy_id 访问器读装载值〔wd12〕", s1.enemy_id() == "goblin_a" and s2.enemy_id() == "goblin_b")
|
||
Kit.check("同名敌按 id 精确解析(不做名子串撞车)〔wd12〕",
|
||
stage._card_by_id("goblin_b") == s2 and stage._card_by_id("goblin_a") == s1)
|
||
Kit.check("空 id / 未登记 id 恒不命中(名兜底归调用点)〔wd12〕",
|
||
stage._card_by_id("") == null and stage._card_by_id("no_such") == null)
|
||
# 口一:on_step_row_fx —— 伤害行携 target_id,闪白落点须在 id 指向的站桩
|
||
stage.on_step_row_fx({"type": "damage", "target": "地精", "target_id": "goblin_b"})
|
||
await create_timer(0.6).timeout # 弹道到达拍 0.33s + 余量
|
||
Kit.check("伤害行按 target_id 命中正确站桩(闪白落点)〔wd12〕",
|
||
s2.fx_probe().flash_count == 1 and s1.fx_probe().flash_count == 0)
|
||
# 口二:enemy_standee_center —— id 先、display_name 兜底
|
||
var c_b = stage.enemy_standee_center("goblin_b", "地精")
|
||
var c_b_expect: Vector2 = stage.world_to_shell(s2.position + s2.box * 0.5)
|
||
Kit.check("弹道落点按 target_id 取站桩中心〔wd12〕", c_b != null and c_b == c_b_expect)
|
||
var c_fallback = stage.enemy_standee_center("", "地精")
|
||
var c_fallback_expect: Vector2 = stage.world_to_shell(s1.position + s1.box * 0.5)
|
||
Kit.check("无 target_id 时按名兜底同旧行为(首命中)〔wd12〕", c_fallback != null and c_fallback == c_fallback_expect)
|
||
|
||
# ---- ① 意图蓄力档可达(d2)----
|
||
# 蓄力判据表:[kind, skill_id, 期望 _intent_charge, 个案标签]
|
||
var charge_cases := [
|
||
["damage", "heavy", true, "蓄力技能(heavy)置 _intent_charge=true"],
|
||
["damage", "goblin_slash", false, "非蓄力攻击技(goblin_slash)零置位"],
|
||
["buff", "goblin_rage", false, "增益系零置位"],
|
||
["damage", "basic", false, "普攻零表示路径复位 _intent_charge"],
|
||
]
|
||
for c in charge_cases:
|
||
s1.set_pending(c[0], c[1])
|
||
Kit.check("%s〔wd2〕" % c[3], s1._intent_charge == c[2])
|
||
|
||
# ---- ② 庆祝 tween 句柄纪律(d4)----
|
||
var ov = BattleEndScript.new()
|
||
root.add_child(ov)
|
||
ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100))
|
||
var first: Tween = ov.get_meta(&"celebrate_tw", null)
|
||
Kit.check("首开胜利画面创建庆祝句柄〔wd4〕", first != null and first.is_valid())
|
||
ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100))
|
||
var second: Tween = ov.get_meta(&"celebrate_tw", null)
|
||
Kit.check("重开胜利画面:旧句柄已 kill、新句柄在位(至多一个活跃)〔wd4〕",
|
||
first != null and not first.is_valid() and second != null and second.is_valid())
|
||
ov.close()
|
||
Kit.check("close 必杀庆祝句柄(句柄清空)〔wd4〕", ov.get_meta(&"celebrate_tw", null) == null)
|
||
|
||
# ---- ③ 互斥四口(d5/d8)----
|
||
var dbg = DebugOverlayScript.new(null, 0, 0)
|
||
dbg.set_sim_running(true)
|
||
Kit.check("模拟运行期调试入口钮同置灰〔wd5〕", dbg._dbg_btn.disabled)
|
||
dbg.set_sim_running(false)
|
||
Kit.check("模拟收尾调试入口钮恢复(未锁定)〔wd5〕", not dbg._dbg_btn.disabled)
|
||
dbg.free()
|
||
|
||
var panel := FakePanel.new()
|
||
root.add_child(panel)
|
||
var drv := FakeDriver.new()
|
||
var ctrl = TuneSimScript.new(FakeDB.new(), panel, 100, drv)
|
||
drv.ctrl = ctrl
|
||
await ctrl.run(2) # 假 driver 回调内读 is_running()(两局各一读)
|
||
Kit.check("控制器运行中 is_running()=true(驱动回调内读数)〔wd8〕", drv.seen == [true, true])
|
||
Kit.check("控制器收尾 is_running()=false〔wd8〕", not ctrl.is_running() and not panel.running)
|
||
Kit.check("两局报告全文注入面板(run 收尾链路完整)〔wd8〕", String(panel.report) != "")
|
||
|
||
quit(Kit.finish("standee_fx_sweep"))
|