104 lines
5.4 KiB
GDScript
104 lines
5.4 KiB
GDScript
# tests/unit/battle_settlement/skill_unlock_test.gd — Boss 半血解锁技·单位容器机制单元测试
|
||
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/skill_unlock_test.gd
|
||
# 覆盖(迭代14④ 技能池执行批 T6):门谓词(hp ≤ round(max_hp×pct/100))/闩不可逆(治疗抬血不解锁)/
|
||
# 双面过滤(rollable+special 不入锁定)/保底不逼锁定(保底集合排除锁定)/解锁即可摇(冷却初值 0)/
|
||
# 普通行无字段逐位零扰动(goblin 池快照零 unlock 键、过滤恒空转)/同种子确定性(容器零随机)。
|
||
# 红线:期望值由表读值推导(零玩法数值字面量);零自建 RNG。
|
||
extends SceneTree
|
||
|
||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||
const UnitContainerScript := preload("res://core/unit_container.gd")
|
||
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
|
||
|
||
func _init() -> void:
|
||
var db = ConfigDBScript.new()
|
||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||
Kit.check("前置:表加载零 error", load_ok and db.diagnostics_count("error") == 0)
|
||
if load_ok:
|
||
_test_gate_predicate(db)
|
||
_test_latch_irreversible(db)
|
||
_test_plain_rows_zero_drift(db)
|
||
quit(Kit.finish("skill_unlock_test"))
|
||
|
||
|
||
# 门谓词:卓尔 (2,9) hp 130(×mult 1.0)→ venom_curb unlock 50 → 阈值 round(130×0.5)=65
|
||
func _boss(db, L: int, D: int):
|
||
var ec = UnitContainerScript.new(db)
|
||
var ok: bool = ec.spawn(L, D, int(db.hero.max_hp))
|
||
Kit.check("前置:spawn (%d,%d) 受理" % [L, D], ok)
|
||
return ec
|
||
|
||
|
||
func _boss_u(ec) -> Dictionary:
|
||
return ec.all_enemies()[0]
|
||
|
||
|
||
func _has_sid(arr: Array, sid: String) -> bool:
|
||
for e in arr:
|
||
if String(e.skill_id) == sid:
|
||
return true
|
||
return false
|
||
|
||
|
||
func _test_gate_predicate(db) -> void:
|
||
var ec = _boss(db, 2, 9)
|
||
var u: Dictionary = _boss_u(ec)
|
||
var mhp: int = int(u.max_hp)
|
||
Kit.check("前置:卓尔 max_hp=130(表值——难度乘数空表恒 1.0)", mhp == 130)
|
||
# 未到阈值:hp = 阈值+1 → 不解锁,锁定条目不入可摇/保底集合
|
||
ec.apply_damage(u, mhp - 66)
|
||
Kit.check("门谓词:hp=66 > 阈值 65 → 零解锁", ec.check_skill_unlocks(u).is_empty())
|
||
Kit.check("双面过滤:未解锁 venom_curb 不入可摇集合", not _has_sid(ec.rollable_skills(u), "venom_curb"))
|
||
Kit.check("双面过滤:未解锁 venom_curb 不入保底特殊技集合", not _has_sid(ec.special_skills(u), "venom_curb"))
|
||
# 恰到阈值:hp = 65 ≤ round(130×0.5) → 解锁(判式取 ≤)
|
||
var hit65: bool = ec.apply_damage(u, 1)
|
||
Kit.check("前置:补一刀至阈值 65 受理", hit65 and int(u.hp) == 65)
|
||
var unlocked: Array = ec.check_skill_unlocks(u)
|
||
Kit.check("门谓词:hp=65 ≤ 阈值 65 → 恰解锁 venom_curb 一次", unlocked.size() == 1 and String(unlocked[0]) == "venom_curb")
|
||
# 解锁即可摇(冷却初值 0)+ 保底特殊技集合同步可见
|
||
Kit.check("解锁即可摇:venom_curb 入可摇集合(冷却初值 0)", _has_sid(ec.rollable_skills(u), "venom_curb"))
|
||
Kit.check("解锁即可摇:venom_curb 入保底特殊技集合", _has_sid(ec.special_skills(u), "venom_curb"))
|
||
# 保底不逼锁定:锁定期保底集合 = web_snare 唯一(venom_burst 被过滤——保底不得逼出未解锁技)
|
||
var ec2 = _boss(db, 2, 9)
|
||
var u2: Dictionary = _boss_u(ec2)
|
||
ec2.apply_damage(u2, mhp - 66)
|
||
var specials: Array = ec2.special_skills(u2)
|
||
# 特殊技集合语义 = 全部非普攻池条目(含伤害技 heavy——保底「必出特殊技」口径;proposal §2.1)
|
||
Kit.check("保底不逼锁定:锁定期保底集合 = heavy+venom_burst+web_snare(venom_curb 过滤不出)", specials.size() == 3 and _has_sid(specials, "heavy") and _has_sid(specials, "web_snare") and _has_sid(specials, "venom_burst") and not _has_sid(specials, "venom_curb"))
|
||
|
||
|
||
# 闩不可逆:解锁后治疗抬回满血 → 复查零新解锁、可摇集合不回退
|
||
func _test_latch_irreversible(db) -> void:
|
||
var ec = _boss(db, 2, 9)
|
||
var u: Dictionary = _boss_u(ec)
|
||
ec.apply_damage(u, int(u.max_hp) / 2) # → 半血触发
|
||
Kit.check("前置:半血触发解锁", ec.check_skill_unlocks(u).size() == 1)
|
||
ec.heal(u, int(u.max_hp)) # 抬回满血
|
||
Kit.check("闩不可逆:满血复查零新解锁(单向闩不解锁回退)", ec.check_skill_unlocks(u).is_empty())
|
||
Kit.check("闩不可逆:可摇集合仍含 venom_curb", _has_sid(ec.rollable_skills(u), "venom_curb"))
|
||
|
||
|
||
# 普通行无字段逐位零扰动:地精 (1,0) 池快照零 unlock 键、过滤恒空转、可摇=全池
|
||
func _test_plain_rows_zero_drift(db) -> void:
|
||
var ec = _boss(db, 1, 0)
|
||
var u: Dictionary = _boss_u(ec)
|
||
var any_unlock_key := false
|
||
for e in u.pool:
|
||
if e.has("unlock_hp_pct") or e.has("unlocked"):
|
||
any_unlock_key = true
|
||
Kit.check("零扰动:无字段行池快照零 unlock 键(16/19 行逐位同前)", not any_unlock_key)
|
||
Kit.check("零扰动:满血复查零解锁", ec.check_skill_unlocks(u).is_empty())
|
||
var rollable: Array = ec.rollable_skills(u)
|
||
var pool_count: int = (u.pool as Array).size()
|
||
Kit.check("零扰动:可摇集合 = 全池(普攻冷却恒 0,过滤恒空转)", rollable.size() == pool_count)
|
||
# 同种子确定性:容器零随机——两实例同输入查询逐位同
|
||
var ec2 = _boss(db, 1, 0)
|
||
var u2: Dictionary = _boss_u(ec2)
|
||
ec.apply_damage(u, 3)
|
||
ec2.apply_damage(u2, 3)
|
||
var a := JSON.stringify(ec.rollable_skills(u))
|
||
var b := JSON.stringify(ec2.rollable_skills(u2))
|
||
Kit.check("确定性:两实例同输入可摇集合逐位同(容器零随机纪律)", a == b)
|