# tests/unit/battle_settlement/damage_math_test.gd — 战斗结算·伤害公式族纯函数单元测试 # 运行:godot --headless --path game --script res://tests/unit/battle_settlement/damage_math_test.gd # 协议:DamageMath = 零状态纯算术宿主(headless 可跑);掷点档位参数全读 settlement 表(零玩法数值字面量)。 # 注:0/100 等出现在断言中的数值 = 语义常量(中性池/百分比满额),非玩法行值。 extends SceneTree const DamageMathScript := preload("res://core/damage_math.gd") const ConfigDBScript := preload("res://core/config_db.gd") const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点 var db func _init() -> void: db = ConfigDBScript.new() var load_ok: bool = db.load_tables("res://data/tables", true) Kit.check("前置:settlement 表加载零 error", load_ok and db.diagnostics_count("error") == 0) if load_ok: _test_pool_damage() _test_counter_component() _test_roll_discount_percent() _test_rolled_component() quit(Kit.finish("damage_math_test")) func _floor_val() -> int: return int(db.settlement.damage_floor) # ---- F2 乘区:池内加算 → 池间乘算 → floor(单一取整规则)---- func _test_pool_damage() -> void: var d := _floor_val() + 9 # 表值推导的任意正探针量(非玩法行值) Kit.check("中性双池 (0,0) 原样透传", DamageMathScript.pool_damage(d, 0, 0) == d) Kit.check("负敌方池压低不增高", DamageMathScript.pool_damage(d, 0, int(db.settlement.pool_clamp_lo)) <= d) Kit.check("正·主角池抬升不低于原值", DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_hi), 0) >= d) Kit.check("主角池单调不减(+50% vs +49% 探针档)", DamageMathScript.pool_damage(d, 50, 0) >= DamageMathScript.pool_damage(d, 49, 0)) Kit.check("对称性:池值互换结果不变(乘算交换)", DamageMathScript.pool_damage(d, 30, -20) == DamageMathScript.pool_damage(d, -20, 30)) Kit.check("输出非负(maxi(0,·) 封底)", DamageMathScript.pool_damage(d, int(db.settlement.pool_clamp_lo), int(db.settlement.pool_clamp_lo)) >= 0) # ---- 克制分量:空桶不吃修正恒 0;落地下限保底([1,+∞) 全来源)---- func _test_counter_component() -> void: var floor_v := _floor_val() Kit.check("空桶(点数≤0)恒 0 不吃修正", DamageMathScript.counter_component(0, floor_v, floor_v) == 0 and DamageMathScript.counter_component(-1, 5, floor_v) == 0) Kit.check("正向修正直加", DamageMathScript.counter_component(floor_v, floor_v, floor_v) == floor_v * 2) Kit.check("负修正触底保落地伤害下限", DamageMathScript.counter_component(floor_v, -floor_v * 10, floor_v) == floor_v) Kit.check("下限参数缺省 = 0(无下限语义面)", DamageMathScript.counter_component(floor_v, -floor_v * 10) == 0) # ---- 掷点折扣查档(纯函数):满阈值恒全额;档位边界/末档语义与表数组一致 ---- func _test_roll_discount_percent() -> void: var threshold := 10 # 查档探针阈值(夹具值——档位参数本身全部来自表) var bounds: Array = db.settlement.roll_tier_bounds var discounts: Array = db.settlement.roll_tier_discounts Kit.check("前置:档位数 = 边界数 + 1(查档契约——档数 = 边界数 + 1)", discounts.size() == bounds.size() + 1 and discounts.size() > 0) Kit.check("掷点 ≥ 阈值恒全额 100", DamageMathScript.roll_discount_percent(threshold, threshold, bounds, discounts) == 100 and DamageMathScript.roll_discount_percent(threshold + 5, threshold, bounds, discounts) == 100) # 缺口 = 1:落首档(缺口 ∈ [1, bounds[0]] → discounts[0]) var tier0_expected: int = int(discounts[0]) if int(bounds[0]) >= 1: Kit.check("缺口 1 落首档", DamageMathScript.roll_discount_percent(threshold - 1, threshold, bounds, discounts) == tier0_expected) # 缺口超末边界:落末档 var last_expected: int = int(discounts[-1]) Kit.check("缺口超末边界落末档", DamageMathScript.roll_discount_percent(threshold - int(bounds[-1]) - 1, threshold, bounds, discounts) == last_expected) # 档位值降序(重档折扣更深)——表自检 var descending := true for i in range(discounts.size() - 1): if int(discounts[i]) < int(discounts[i + 1]): descending = false Kit.check("档位值随档位加深不升(折扣单调)", descending) # ---- 吃掷点分量算术:折扣→(暴击)→floor→+克制修正(单一取整链)---- func _test_rolled_component() -> void: var base := float(_floor_val() + 7) # 表值推导探针基线 var crit_mult: int = int(db.settlement.crit_multiplier) Kit.check("满额折扣无暴击 = floor(基线) + 修正 0", DamageMathScript.rolled_component(base, 100, false, crit_mult, 0) == int(floor(base))) Kit.check("满额折扣暴击按表倍率放大", DamageMathScript.rolled_component(base, 100, true, crit_mult, 0) == int(floor(base * float(crit_mult) / 100.0))) Kit.check("折扣降低不高于满额", DamageMathScript.rolled_component(base, 50, false, crit_mult, 0) <= DamageMathScript.rolled_component(base, 100, false, crit_mult, 0)) Kit.check("克制修正加在 floor 之后(加法核)", DamageMathScript.rolled_component(base, 100, false, crit_mult, _floor_val()) == int(floor(base)) + _floor_val()) Kit.check("复合序 = 基线×折扣%×暴击%(floor 只在链末)", DamageMathScript.rolled_component(base, 50, true, crit_mult, 0) == int(floor(base * 50.0 / 100.0 * float(crit_mult) / 100.0)))