Boss 战修复(Plan B,用户拍板): - HP 公式 15xPxhall_factor -> HP = P x boss_army_seconds_n(S=23.0→28.0 递增, 每场军列约 23-28 秒清空,战斗时长成为可直接书写的设计旋钮,结构性消除 高殿 P 抵消导致不可胜的旧 bug);hall_factor 降级为纯叙事/视觉参数 - 新增 souls_par 列(0→90000):开场冲锋 intro_dmg = HP x 10% x clamp(souls/souls_par,0,1), 上限 20%->10%;删 hp_factor/intro_dmg_souls_factor/intro_dmg_cap_ratio 三列 - 点击伤害 P x κ x combo_mult,κ = (0.010 + 0.052 x click_ratio) x (1 + 0.30 x m06_owned), click_ratio = 已购点击升级 / 12(κ 0.010→0.0806,满配点击占比约 20.5%±3pp) - 防双重计入:L5-12 点击升级 +1% output/s 留全局区(抬 P 同时抬军列 DPS 与点击 伤害,可接受),不得再计入 κ 分子(recompute 注释固化) 离线契约 v2(P-8,改契约不改表): - OfflineModel.settle 第三参 offline_mult -> offline_add(加法);各档效率 min(rate+add, 1.0) 硬顶 100%,离线永不超在线 - 长明灯 x1.15 乘性 -> +5%/级 加法(满级 +30%,各档封顶 100%) - 0-2h 100% 保持;8h = 6.2 等效小时(77.5% 效率) - Gate #9 拆 9a/9b/9c:9a FAIL->PASS(0-2h 全额 + 8h∈[5.5,6.5]), 9b 长明灯满级 8h∈[7.5,8.0] 且 ≤100%,9c 任意配置效率 ≤100% 数据与工具: - gen_balance_sheets.py 补登 W3 遗留 7 行 tune(防重跑静默丢失),self_check 更新 - boss_battles/upgrades schema 同步(offline_mult->offline_add enum,漏改会违反导入) 测试(runner 7->8 用例,Node 交叉验证 145 断言全过): - 可胜性哨兵重写为「十殿全可胜」:每殿 S_n<30、军列+点击≥HP、余时∈[2,8]、 满配余时差≥4、点击占比∈[15,25]、H10≈20.5%±3pp - κ 四档断言(0.010/0.036/0.062/0.0806,分母 12) - 新增 test_offline_contract.gd:契约 v2 数值面 + 9a/9b/9c 全 PASS + β 缓议
85 lines
4.3 KiB
GDScript
85 lines
4.3 KiB
GDScript
extends RefCounted
|
||
|
||
# 用例:W4 离线契约 v2(systems-spec §2.4/§2.5,用户 2026-10-20 拍板)
|
||
# 契约 v2:8h = 6.2 等效小时(效率 77.5%);24h = 12.6h;72h 封顶 = 17.4h(24.2%);
|
||
# 长明灯 6 条 × +5% 加法(满 +30%,各档速率硬顶 100%);效率硬顶 ≤ 100%。
|
||
# 门 #9 拆三条子检查(a 由 FAIL 转 PASS):9a 契约效率 / 9b 长明灯上限 / 9c 硬顶。
|
||
|
||
func run(a, data) -> void:
|
||
_test_contract_v2(a, data)
|
||
_test_gate_offline_v2(a, data)
|
||
|
||
|
||
func _settle(a, data, hours: float, add: float) -> float:
|
||
var r: Dictionary = OfflineModel.settle(hours * 3600.0, 1.0, add,
|
||
data.tune_num("tune_offline_band1_h", 2.0), data.tune_num("tune_offline_band1_rate", 1.0),
|
||
data.tune_num("tune_offline_band2_h", 8.0), data.tune_num("tune_offline_band2_rate", 0.7),
|
||
data.tune_num("tune_offline_band3_h", 24.0), data.tune_num("tune_offline_band3_rate", 0.4),
|
||
data.tune_num("tune_offline_band4_rate", 0.1),
|
||
data.tune_num("tune_offline_cap_h", 72.0))
|
||
return float(r["effective_hours"])
|
||
|
||
|
||
# ---- 契约 v2 数值面 ----
|
||
func _test_contract_v2(a, data) -> void:
|
||
# 衰减表一字不改(systems-spec §2.4:错的是契约不是表)
|
||
a.near(data.tune_num("tune_offline_band1_rate", 0.0), 1.0, 1e-9, "0-2h = 100%")
|
||
a.near(data.tune_num("tune_offline_band2_rate", 0.0), 0.7, 1e-9, "2-8h = 70%")
|
||
a.near(data.tune_num("tune_offline_band3_rate", 0.0), 0.4, 1e-9, "8-24h = 40%")
|
||
a.near(data.tune_num("tune_offline_band4_rate", 0.0), 0.1, 1e-9, ">24h = 10%")
|
||
a.near(data.tune_num("tune_offline_cap_h", 0.0), 72.0, 1e-9, "72h 封顶")
|
||
# 8h = 6.2 等效小时(77.5%)∈ [5.5, 7.0](用户口径)/ [5.5, 6.5](spec 口径,双重满足)
|
||
var e8 := _settle(a, data, 8.0, 0.0)
|
||
a.near(e8, 6.2, 1e-9, "8h 离线 = 6.2h 等效(契约 v2)")
|
||
a.true_(e8 >= 5.5 and e8 <= 7.0, "8h 等效 ∈ [5.5, 7.0](用户构造口径)")
|
||
a.true_(e8 >= 5.5 and e8 <= 6.5, "8h 等效 ∈ [5.5, 6.5](systems-spec §2.4 口径)")
|
||
a.near(e8 / 8.0, 0.775, 1e-9, "8h 效率 = 77.5%")
|
||
# 24h = 12.6h;72h 封顶 = 17.4h(24.2%)
|
||
a.near(_settle(a, data, 24.0, 0.0), 12.6, 1e-9, "24h 离线 = 12.6h 等效")
|
||
a.near(_settle(a, data, 72.0, 0.0), 17.4, 1e-9, "72h 封顶 = 17.4h 等效")
|
||
a.near(_settle(a, data, 72.0, 0.0) / 72.0, 17.4 / 72.0, 1e-9, "72h 效率 ≈ 24.2%")
|
||
a.near(_settle(a, data, 100.0, 0.0), 17.4, 1e-9, "100h 也被 72h 硬顶钳到 17.4h")
|
||
# 长明灯:数据面 = 6 条 × 0.05 加法(effect_type offline_add)
|
||
var lamp := 0.0
|
||
var lamp_rows := 0
|
||
for u in data.upgrades:
|
||
if u.type == "offline":
|
||
lamp += u.effect_param
|
||
lamp_rows += 1
|
||
a.eq(lamp_rows, 6, "长明灯 6 条")
|
||
a.near(lamp, 0.30, 1e-9, "长明灯满级 = 加法 +30%(旧 ×2.31 乘性已废止)")
|
||
# 长明灯满级:8h = 8.0h,效率恰好 100%(硬顶允许的最大值)
|
||
var e8l := _settle(a, data, 8.0, lamp)
|
||
a.near(e8l, 8.0, 1e-9, "长明灯满级 8h = 8.0h 等效")
|
||
a.near(e8l / 8.0, 1.0, 1e-9, "效率 = 100%(≤ 硬顶,离线不优于在线)")
|
||
# + m05(+25%)也不越过硬顶
|
||
var m05 := data.meta("meta_05")
|
||
var m05_add := 0.0
|
||
if m05 != null:
|
||
m05_add = m05.effect_param * float(m05.max_rank)
|
||
a.near(_settle(a, data, 8.0, lamp + m05_add), 8.0, 1e-9, "+ m05(+25%)仍钳 8.0h")
|
||
# 效率硬顶:任意配置/任意时长 ≤ 100%
|
||
for dur_h in [1.0, 8.0, 24.0, 72.0]:
|
||
for add in [0.0, lamp, lamp + m05_add, 5.0]:
|
||
var eff := _settle(a, data, dur_h, add)
|
||
a.true_(eff <= dur_h + 1e-9,
|
||
"效率硬顶:%sh + 加法 %.2f ⇒ %.2fh ≤ %sh" % [str(dur_h), add, eff, str(dur_h)])
|
||
|
||
|
||
# ---- 门 #9 三条子检查(a 由 FAIL 转 PASS) ----
|
||
func _test_gate_offline_v2(a, data) -> void:
|
||
var g: Dictionary = SimGates.gate_offline_v2(data)
|
||
a.true_(bool(g.get("pass", false)), "门 #9 三条子检查全过(a 由 FAIL 转 PASS)")
|
||
a.true_(str(g.get("detail", "")).contains("契约 v2"), "detail 标注契约 v2")
|
||
var sub: Dictionary = g.get("sub", {})
|
||
var g9a: Dictionary = sub.get("9a", {})
|
||
var g9b: Dictionary = sub.get("9b", {})
|
||
var g9c: Dictionary = sub.get("9c", {})
|
||
a.true_(bool(g9a.get("pass", false)),
|
||
"9a PASS:%s" % str(g9a.get("detail", "")))
|
||
a.true_(bool(g9b.get("pass", false)),
|
||
"9b PASS:%s" % str(g9b.get("detail", "")))
|
||
a.true_(bool(g9c.get("pass", false)),
|
||
"9c PASS:%s" % str(g9c.get("detail", "")))
|
||
a.true_(str(g.get("deferred_beta", "")).contains("β"), "进度口径 β 标记为待 sim 标定")
|