feat(通关口径换代·提前实施卡批 T2): 12 测试文件 58 处标签/注释随动(save_manager_domain 8/save_slot_summary 1/adventure_feats 15/level_shop_economy 12/meta_progression_forge 9/reward_overlay 1/cross_system_flow 3/breakpoint_resume 1/save_boot_no_activate 1/curio_refresh_paid 2/table_structure_guard 2/theme_projection 3)+三探针(ui_struct wr2 摘要逐字断言+:446 禁项注释改写/ui_tune wr3+V7/ui_click 7 注释)+V4-V6 theme_projection 新增(生涯页标签换代+零残留负断言/解锁标签常量/营地最近冒险行 open_camp 直调)——断言尾缀口径换代 Vn(避退役词自撞 S11);验证集清单重生成 1591 条对账零差异;撤销证明 a6-a12 逐条闭合(V4/V5/V6/sl2/wr3/V7 各自红→复绿,ui_tune 双红验证 wr3+V7 独立钉住);修复 worktree 环境红=补拷 .import 边车与 assets 三目录(art 113 png+fonts+theme,坑册三件套完整形态)
This commit is contained in:
1 parent
54395b8b43
commit
51c6cf56f0
17 files changed
+470
-424
No files matched your search
@@ -31,7 +31,7 @@ var _tested_curio_head := false # 迭代36 卡A——三选一头部旧话清
|
||||
var _tested_map_cap := false # 迭代36 卡A——地图屏加成帽换代断言(每 run 一次)
|
||||
var _tested_status := false # 状态面板角标开合/守卫断言(每 run 一次)
|
||||
var _tested_map_status := false # 地图屏角标一次性 case(选路态零扰动)
|
||||
var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 通关收口)
|
||||
var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 关卡完成收口)
|
||||
var _settlement_confirms := 0 # 卡⑥ 胜利结算框确认计数(观测面——每场普通战斗胜利恰一)
|
||||
var _route_clicks := 0 # 选路覆盖层点击计数(岔路使用率观测面)
|
||||
var _locktest_state := 0 # T1 D1 锁期/重放断言状态机(0=待注入 1=等重放 2=完结)——每 run 一次
|
||||
@@ -368,7 +368,7 @@ func _ready() -> void:
|
||||
_problems.append("生涯页关闭后未恢复开始层")
|
||||
# 换回全新零档(营地购买态不泄入后续冒险段——确定性;档案残留下次段首清理兜底)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_click_probe_reset", true, false, true) # 零档换注(继续上局钮已随三入口化撤出——卡② mn8)
|
||||
main.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可通关——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
|
||||
var t1 := Time.get_ticks_msec()
|
||||
main._start_adventure_from_menu()
|
||||
@@ -399,7 +399,7 @@ func _ready() -> void:
|
||||
_problems.append("首格未可点(cells=0)")
|
||||
print("[click_probe] 首格可点计时 %dms(进入战斗屏口径,≤500ms AC)· 场景冷启动 %dms(含 UI 构建 %dms,观测项)" % [
|
||||
first_cell_ms, t_total, t_ui_ready])
|
||||
const ACTION_BUDGET_FRAMES := 20000 # 主循环帧预算(坏态挂死兜底上限——常态通关远低于此;有挂帧证据才收紧,勿让坏态等满预算才报)
|
||||
const ACTION_BUDGET_FRAMES := 20000 # 主循环帧预算(坏态挂死兜底上限——常态跑完远低于此;有挂帧证据才收紧,勿让坏态等满预算才报)
|
||||
var action_budget := ACTION_BUDGET_FRAMES
|
||||
# P0-2 回归:本 while 循环体缩进必须整体在循环内——continue 落在
|
||||
# 循环外成解析错误、探针不可加载。bonus 分支保留为体内兄弟分支。
|
||||
@@ -411,7 +411,7 @@ func _ready() -> void:
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.curio_overlay.visible:
|
||||
# 三选一停点(CurioPickOverlay——奖励节点/通关兑付共用面,须与硬锁态适配,
|
||||
# 三选一停点(CurioPickOverlay——奖励节点/关卡完成兑付共用面,须与硬锁态适配,
|
||||
# 未适配则走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/
|
||||
# 待选态(点首件+确认)。真实信号路径全线。〔无替换子态——装配不可变契约。〕
|
||||
# 三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。
|
||||
@@ -475,7 +475,7 @@ func _ready() -> void:
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main._inter_overlay.visible:
|
||||
# 大奖励屏(冒险级停点——非末关通关恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已通关,收口
|
||||
# 大奖励屏(冒险级停点——非末关的关卡完成恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已关卡完成,收口
|
||||
if not main._input_locked():
|
||||
_problems.append("大奖励屏打开时输入未锁定")
|
||||
_inter_confirms += 1
|
||||
@@ -491,7 +491,7 @@ func _ready() -> void:
|
||||
_problems.append("大奖励确认后未推进到第二关(level_index != 2)")
|
||||
else:
|
||||
_done = true
|
||||
print("[click_probe] 第二关大奖励屏(L2 通关)——探针收口 · 选路口点击 %d 次" % _route_clicks)
|
||||
print("[click_probe] 第二关大奖励屏(L2 关卡完成)——探针收口 · 选路口点击 %d 次" % _route_clicks)
|
||||
break
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
@@ -639,7 +639,7 @@ func _ready() -> void:
|
||||
# 驱动设计(0/0 白板局生存性不成立的实证处置——L2 记录进实现报告):
|
||||
# ① 菜单开新局·白板反向=独立短局:0/0 滑条开局断言 L1 开场行零后缀(不开驱动——
|
||||
# 断言完成后该局被 ② 的开局自然替换,白板局生存性不参与判定);
|
||||
# ② 局内加算区正向=生存局:400/200 滑条开局保可通关(:363 先例),三选一专选 atk/max_hp
|
||||
# ② 局内加算区正向=生存局:400/200 滑条开局保可完成(:363 先例),三选一专选 atk/max_hp
|
||||
# 卡(无则任选推进——不触后缀、循环重试);至大奖励屏先 reset_sliders() 再确认进下一层
|
||||
# ——L2 开场行时点 pct=0 且新档 ov=0,后缀只能归因局内加算区(变异:回退条件 hunk→红);
|
||||
# ③ 断点续玩正向:L2 入口选路停点(bonus 已入账、bonus_pending/bonus_result 皆空——选路停点形)
|
||||
|
||||
@@ -431,10 +431,10 @@ func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
else:
|
||||
if not (has_new and not has_load and not has_del):
|
||||
problems.append("[savesel] 行%d 操作集失守(空/坏槽应=仅新档、零进档删档含置灰形态)〔sl4〕" % row_idx)
|
||||
# sl2 摘要三项逐槽读数:槽1 喂入值逐字(钥石 555/通关 2 局/冒险 7 局)
|
||||
# sl2 摘要三项逐槽读数:槽1 喂入值逐字(钥石 555/冒险成功 2 局/冒险 7 局)
|
||||
var sum1: Label = sso._summary_labels.get(1)
|
||||
if sum1 == null or sum1.text != "通关 2 局 · 冒险 7 局":
|
||||
problems.append("[savesel] 槽1 摘要读数失守(got=%s 应=通关 2 局 · 冒险 7 局)〔sl2〕" % (sum1.text if sum1 else "null"))
|
||||
if sum1 == null or sum1.text != "冒险成功 2 局 · 冒险 7 局":
|
||||
problems.append("[savesel] 槽1 摘要读数失守(got=%s 应=冒险成功 2 局 · 冒险 7 局)〔sl2〕" % (sum1.text if sum1 else "null"))
|
||||
var all_texts: Array[String] = []
|
||||
_collect_labels(sso, all_texts)
|
||||
var has_keystone := false
|
||||
@@ -443,7 +443,7 @@ func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
has_keystone = true
|
||||
if not has_keystone:
|
||||
problems.append("[savesel] 槽1 钥石徽读数缺失(应=钥石 555)〔sl2〕")
|
||||
# sl3 摘要禁项:断点/种子/「继续」/停点/「 关」(关数形——「通关」无空格不误伤)零命中
|
||||
# sl3 摘要禁项:断点/种子/「继续」/停点/「 关」(关数形——摘要「冒险成功 X 局 · 冒险 Y 局」不含「关」字)零命中
|
||||
#(裁决 4——零未完冒险读数;词形覆盖=对照表 sl3 缺陷形态「断点关数拼进槽卡」的拦截面)
|
||||
for needle in ["断点", "种子", "继续", "停点", " 关"]:
|
||||
for t in all_texts:
|
||||
|
||||
@@ -72,8 +72,11 @@ func _ready() -> void:
|
||||
if main.debug_overlay.is_running():
|
||||
problems.append("模拟未在预算帧内完成(疑似挂死)")
|
||||
var out: String = main.debug_overlay.output_text()
|
||||
if not out.contains("通关率"):
|
||||
problems.append("模拟输出缺通关率")
|
||||
if not out.contains("关卡完成率"):
|
||||
problems.append("模拟输出缺关卡完成率")
|
||||
# 口径换代 V7:胜局统计行措辞钉子(atk+100% 加成档 10 局必有胜局——hp_left_win 非空路径恒出)
|
||||
if not out.contains("关卡完成剩余 HP 均"):
|
||||
problems.append("模拟输出缺关卡完成剩余 HP 行〔口径换代 V7〕")
|
||||
if not out.contains("败北节点"):
|
||||
problems.append("模拟输出缺败北节点")
|
||||
if "N=10" not in out:
|
||||
|
||||
@@ -116,14 +116,14 @@ func _save_summary_integration() -> void:
|
||||
var ks_run := 0
|
||||
for li in range(1, int(_db.level.rows.size()) + 1):
|
||||
ks_run += int(_db.meta_boss_keystone_row(li).get("first_kill", 0))
|
||||
Kit.check("摘要集成:冒险驱动满配通关(seed 42)", String(st9.get("result", "")) == "completed")
|
||||
Kit.check("摘要集成:冒险驱动满配冒险成功(seed 42)", String(st9.get("result", "")) == "completed")
|
||||
Kit.check("集成钥石余额 = ΣBoss 首杀行值(%d——逐杀掉落全源)" % f9.keystone_balance(), f9.keystone_balance() == ks_run)
|
||||
Kit.check("集成通关次数表全 1", f9.clear_count(1) == 1 and f9.clear_count(2) == 1 and f9.clear_count(3) == 1)
|
||||
Kit.check("集成关卡完成次数表全 1", f9.clear_count(1) == 1 and f9.clear_count(2) == 1 and f9.clear_count(3) == 1)
|
||||
Kit.check("集成三关首杀标记齐", f9.boss_first_kill_marked(1) and f9.boss_first_kill_marked(2) and f9.boss_first_kill_marked(3))
|
||||
Kit.check("集成 recent 1 条且 seed 42", f9.recent_summaries().size() == 1 and int(f9.recent_summaries()[0].get("master_seed", -1)) == 42)
|
||||
var st9b: Dictionary = LevelAutoplay.run_adventure(_db, 42, 900, 400)
|
||||
f9.apply_adventure_summary(st9b.get("summary", {}))
|
||||
Kit.check("第二局后通关次数全 2", f9.clear_count(1) == 2 and f9.clear_count(2) == 2 and f9.clear_count(3) == 2)
|
||||
Kit.check("第二局后关卡完成次数全 2", f9.clear_count(1) == 2 and f9.clear_count(2) == 2 and f9.clear_count(3) == 2)
|
||||
Kit.check("第二局后余额翻倍(2 × Σ首杀——模拟驱动恒首杀口径)", f9.keystone_balance() == 2 * ks_run)
|
||||
Kit.check("集成 recent 长度 2", f9.recent_summaries().size() == 2)
|
||||
Kit.check("集成写盘成功", f9.write_to_disk())
|
||||
|
||||
@@ -99,11 +99,11 @@ func _api_mark_node(_node: Dictionary, _info: Dictionary, seq: Array) -> void:
|
||||
seq.append("node_entered")
|
||||
|
||||
|
||||
# ③ 冒险验收断言(映射 冒险结构.md 验收标准——表覆盖/满配通关/种子复现/大奖励/败北线/
|
||||
# ③ 冒险验收断言(映射 冒险结构.md 验收标准——表覆盖/满配冒险成功/种子复现/大奖励/败北线/
|
||||
# 重复拒绝/GP 跨关延续/加算区同账/恢复数学/放弃)
|
||||
|
||||
|
||||
# ③ 冒险验收断言(映射 冒险结构.md 验收标准——表覆盖/满配通关/种子复现/大奖励/败北线/
|
||||
# ③ 冒险验收断言(映射 冒险结构.md 验收标准——表覆盖/满配冒险成功/种子复现/大奖励/败北线/
|
||||
# 重复拒绝/GP 跨关延续/加算区同账/恢复数学/放弃)
|
||||
func _smoke_adventure() -> void:
|
||||
print("--- 冒险结构验收(三关序列 · 大奖励 · 摘要五件事 · 账本封闭)---")
|
||||
@@ -129,11 +129,11 @@ func _smoke_adventure() -> void:
|
||||
cov_miss += 1
|
||||
Kit.check("生成图每个战斗/Boss 节点经解析链命中敌行集与棋盘行(扫描 %d 节点,缺失 %d)〔fv24〕" % [cov_scan, cov_miss],
|
||||
cov_scan > 0 and cov_miss == 0)
|
||||
# ② 满配通关 + ④ 三层奖励兑付(三选一恰 2 次 · 回血封顶 · 钥石逐杀 · 候选零重复)
|
||||
# ② 满配冒险成功 + ④ 三层奖励兑付(三选一恰 2 次 · 回血封顶 · 钥石逐杀 · 候选零重复)
|
||||
var st: Dictionary = autoplay.run_adventure(_db, 42, 900, 400)
|
||||
var sum: Dictionary = st["summary"]
|
||||
Kit.check("满配通关(42 / atk+900 / hp+400——cleared=true;v2 怪阶梯后旧 200/100 档不再满配)", String(st["result"]) == "completed" and bool(sum.get("cleared", false)))
|
||||
Kit.check("通关几关 = 3(levels_cleared)", int(sum.get("levels_cleared", -1)) == 3)
|
||||
Kit.check("满配冒险成功(42 / atk+900 / hp+400——cleared=true;v2 怪阶梯后旧 200/100 档不再满配)", String(st["result"]) == "completed" and bool(sum.get("cleared", false)))
|
||||
Kit.check("完成几关 = 3(levels_cleared)", int(sum.get("levels_cleared", -1)) == 3)
|
||||
var ks_exp := 0
|
||||
for li in range(1, int(_db.level.rows.size()) + 1):
|
||||
ks_exp += int(_db.meta_boss_keystone_row(li).get("first_kill", 0))
|
||||
@@ -151,7 +151,7 @@ func _smoke_adventure() -> void:
|
||||
node_picks += 1 # 奖励节点三选一(每关奖励节点各 1 次)
|
||||
elif (ev as Dictionary).has("curio_resolved"):
|
||||
resolves.append((ev as Dictionary)["curio_resolved"])
|
||||
Kit.check("通关三选一恰呈现 2 次、恰兑付 2 次(L1/L2 后——末关不发;resolves 含节点回执另计)", picks.size() == 2 and resolves.size() == 2 + node_picks)
|
||||
Kit.check("关卡完成三选一恰呈现 2 次、恰兑付 2 次(L1/L2 后——末关不发;resolves 含节点回执另计)", picks.size() == 2 and resolves.size() == 2 + node_picks)
|
||||
# 奖励节点在生成图里是可选路位(旧单链模板的「L1 必经 1 个」形状前提随模板消亡)——
|
||||
# 接线证明拆两半:图侧每关含 ≥1 奖励节点,行进侧种子扫描内确有经停(扫描零经停=三选一接线不可达)
|
||||
var reward_levels := 0
|
||||
@@ -242,7 +242,7 @@ func _smoke_adventure() -> void:
|
||||
if rk == "max_hp":
|
||||
hp_acc += int(r.get("value", max_flat))
|
||||
Kit.check("三选一候选 2–3 枚且零重复(令牌洗牌不放回——kind+tier 复合身份)", cand_ok)
|
||||
Kit.check("通关部分回血封顶(floor(观测上限×比例) 封顶——购血观测值精确断言,核代码增补)", heal_ok)
|
||||
Kit.check("关卡完成部分回血封顶(floor(观测上限×比例) 封顶——购血观测值精确断言,核代码增补)", heal_ok)
|
||||
Kit.check("钥石逐杀入账(首杀档位 + 累计逐位)", ks_gain_ok)
|
||||
Kit.check("点选恰兑付选中一件(值=令牌档位表值;未选项零生效)", resolve_ok)
|
||||
var seq: Array = st["encounter_max_hp_seq"]
|
||||
@@ -271,7 +271,7 @@ func _smoke_adventure() -> void:
|
||||
Kit.check("零配 L1 败北种子可达(seed=%s)" % str(lose.get("s", -1)), not lose.is_empty())
|
||||
if not lose.is_empty():
|
||||
var sl: Dictionary = lose["st"]["summary"]
|
||||
Kit.check("L1 败北摘要:未打穿 · 通关 0 关 · 钥石 0 · 止步第 1 层",
|
||||
Kit.check("L1 败北摘要:未打穿 · 完成 0 关 · 钥石 0 · 止步第 1 层",
|
||||
not bool(sl["cleared"]) and int(sl["levels_cleared"]) == 0 \
|
||||
and int(sl["keystone_total"]) == 0 and int(sl["stopped_at"]) == 1)
|
||||
var dl: Dictionary = sl.get("defeat", {})
|
||||
@@ -351,8 +351,8 @@ func _smoke_adventure() -> void:
|
||||
var acc_shop := int(a8.run_ledger.bonus.get("max_hp", -1)) # L1 三选一后 + 商店 3(首件随种子)
|
||||
Kit.check("统一冒险加算区含商店值(max_hp=%d ≥ 商店 3——L1 三选一件值另计)" % acc_shop, acc_shop >= 3)
|
||||
a8._on_level_finished({"result": "victory", "hp": int(r8.hero_hp), "node_index": r8.node_index,
|
||||
"node_id": r8.current_node_id(), "node_type": "shop"}) # 模拟 L2 通关(victory 载荷直调——非末关)
|
||||
Kit.check("L2 通关三选一停点在案(直调兑付前——零自动推进)", not a8._curio_pending.is_empty())
|
||||
"node_id": r8.current_node_id(), "node_type": "shop"}) # 模拟 L2 关卡完成(victory 载荷直调——非末关)
|
||||
Kit.check("L2 关卡完成三选一停点在案(直调兑付前——零自动推进)", not a8._curio_pending.is_empty())
|
||||
Kit.check("未兑付推进拒绝(选择停点零自动推进契约)", not a8.continue_to_next_level())
|
||||
var l2_cands: Array = a8._curio_pending["candidates"]
|
||||
var l2_pick: Dictionary = l2_cands[0]
|
||||
@@ -361,7 +361,7 @@ func _smoke_adventure() -> void:
|
||||
var expect_acc: int = acc_shop + (int(a8._curio_result.get("value", 0)) if picked2 == "max_hp" else 0)
|
||||
Kit.check("三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" % [int(a8.run_ledger.bonus.get("max_hp", -1)), acc_shop, picked2], int(a8.run_ledger.bonus.get("max_hp", -1)) == expect_acc)
|
||||
var max_eff8: int = a8.effective_max_hp() # 账本生效上限含调试浮窗 pct(a8 已 set 400)
|
||||
Kit.check("通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" % [int(a8.ledger["hero_hp"]), max_eff8], int(a8.ledger["hero_hp"]) <= max_eff8)
|
||||
Kit.check("关卡完成部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" % [int(a8.ledger["hero_hp"]), max_eff8], int(a8.ledger["hero_hp"]) <= max_eff8)
|
||||
Kit.check("兑付后推进受理(下一关开始)", a8.continue_to_next_level())
|
||||
# ⑨ 恢复点数学(接口③封顶公式三读数 + 同式两落点一致)
|
||||
var a9 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(99))
|
||||
@@ -406,7 +406,7 @@ func _smoke_adventure() -> void:
|
||||
treasure_seen = true
|
||||
break
|
||||
Kit.check("池收缩——40 次重抽零 aux 候选(停点渠道 aux 卡退役,迭代 11 第①批 T7)", not treasure_seen)
|
||||
# 满血回 0 合法(通关回血封顶口径)
|
||||
# 满血回 0 合法(关卡完成回血封顶口径)
|
||||
var a12 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(12))
|
||||
a12.start_adventure(12)
|
||||
var cap := {"payload": {}}
|
||||
@@ -414,7 +414,7 @@ func _smoke_adventure() -> void:
|
||||
var max_full := int(_db.hero.max_hp) + int(a12._base_override.get("max_hp", 0))
|
||||
a12.ledger["hero_hp"] = max_full
|
||||
a12._on_level_finished({"result": "victory", "hp": max_full, "node_index": 1, "node_id": 1, "node_type": "shop"})
|
||||
Kit.check("满血通关回血 0 合法(封顶口径——heal_gained=0 非负溢出)", int(cap["payload"].get("heal_gained", -1)) == 0)
|
||||
Kit.check("满血关卡完成回血 0 合法(封顶口径——heal_gained=0 非负溢出)", int(cap["payload"].get("heal_gained", -1)) == 0)
|
||||
# ⑬ 奖励节点共享池(同库同池同摘牌;TP0:attack 令牌耗尽后节点域同样零出现)
|
||||
var a13 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(13))
|
||||
Kit.check("节点共享池场景:冒险启动(seed 13)", a13.start_adventure(13, {"treasure_unlocked": {"wide_scout_band": true}}))
|
||||
@@ -509,7 +509,7 @@ func _smoke_feats() -> void:
|
||||
if String((t as Dictionary).get("kind", "")) == "relic":
|
||||
deck_relic += 1
|
||||
Kit.check("专长带入池:5 行 relic 每件 1 张(四级 rarity 直映射)——牌库 102+5=107", deck_relic == 5 and a_r._curio_deck.size() == 107)
|
||||
# 渠道白名单断言(渠道轴定位:通关渠道只出专长/节点渠道零专长)
|
||||
# 渠道白名单断言(渠道轴定位:关卡完成渠道只出专长/节点渠道零专长)
|
||||
var feat_seen: Dictionary = {}
|
||||
var node_relic_seen := false
|
||||
for i in 60:
|
||||
@@ -519,7 +519,7 @@ func _smoke_feats() -> void:
|
||||
for t in a_r._draw_curio_candidates("reward_node"):
|
||||
if String((t as Dictionary).get("kind", "")) == "relic":
|
||||
node_relic_seen = true
|
||||
Kit.check("通关渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)", feat_seen.size() >= 2)
|
||||
Kit.check("关卡完成渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)", feat_seen.size() >= 2)
|
||||
Kit.check("节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以专长凭证形态出现)", not node_relic_seen)
|
||||
# T5 专长凭证行为断言(A5 拿到即开 / A6 保底计数——契约④四口径;独立 runner 防污染 a_r 牌库)
|
||||
var a_t = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(91))
|
||||
|
||||
@@ -96,7 +96,7 @@ func _init() -> void:
|
||||
adr_e._curio_taken["attack"] = 1 # 白盒预置计数(断点附随件往返的判据面——计数不可由加算区反推)
|
||||
adr_e._on_level_finished({"result": "victory", "hp": 30, "node_index": 1, "node_id": 1, "node_type": "shop"})
|
||||
var cands_e: Array = (adr_e._curio_pending.get("candidates", []) as Array).duplicate()
|
||||
Kit.check_quiet(not cands_e.is_empty(), "P5 非末关通关三选一停点在案(直调 victory——L1 非末关)")
|
||||
Kit.check_quiet(not cands_e.is_empty(), "P5 非末关的关卡完成三选一停点在案(直调 victory——L1 非末关)")
|
||||
var sm_f = SaveManagerScript.new(_bp_path)
|
||||
Kit.check_quiet(sm_f.load_from_disk() and sm_f.has_breakpoint(), "P5 选择停点断点在档(选择停点可中断契约)")
|
||||
var bp_f: Dictionary = sm_f.breakpoint_snapshot()
|
||||
|
||||
@@ -23,9 +23,9 @@ func _init() -> void:
|
||||
Kit.check_quiet(adr.curio_stop_pending() == false, "无停点=在途谓词 false")
|
||||
Kit.check_quiet(adr.refresh_curio() == false and adr.refresh_curio_paid() == false, "无停点:免费/付费刷新皆拒绝零副作用")
|
||||
|
||||
# ---- ② 免费位优先序 X→Y(白盒造通关停点——smoke_all ⑫ 同构)----
|
||||
# ---- ② 免费位优先序 X→Y(白盒造关卡完成停点——smoke_all ⑫ 同构)----
|
||||
adr._curio_pending = {"candidates": _cands(), "payload": {}}
|
||||
Kit.check_quiet(adr.curio_stop_pending() == true, "通关停点在途=谓词 true")
|
||||
Kit.check_quiet(adr.curio_stop_pending() == true, "关卡完成停点在途=谓词 true")
|
||||
adr._attune_refresh_x = 1
|
||||
adr._attune_refresh_y = 2
|
||||
Kit.check_quiet(adr.refresh_curio() and adr._attune_refresh_x == 0 and adr._attune_refresh_y == 2, "X 优先消耗(x1y2→x0y2)")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# tests/unit/core/level_shop_economy_test.gd — 关卡图与 GP 商店单元测试宿主
|
||||
# 断言面 = 七套(批 B 断言迁移自 smoke_all.gd,G8 施工组):
|
||||
# 关卡节点图·生成图推进(满配通关/恢复点公式/败北线/回放确定性/属性覆盖恢复)
|
||||
# 关卡节点图·生成图推进(满配冒险成功/恢复点公式/败北线/回放确定性/属性覆盖恢复)
|
||||
# 分支节点图(图校验正反/L2·L3 行进与行集定点/混编运行面/真图类型可见性)
|
||||
# GP 冒险钱包·到账(掷定窗口/关级加值)
|
||||
# escrow 隔离(格面兑付入缓冲/破财直验/钱包钳制不透支)
|
||||
@@ -323,15 +323,15 @@ func _smoke_generated_coverage() -> void:
|
||||
# ---- 关卡节点图·生成图推进冒烟(图 = 该关参数档 × 种子)----
|
||||
|
||||
func _smoke_level() -> void:
|
||||
print("--- 关卡节点图·生成图推进(参数档×种子产图·满配通关/恢复公式/败北线/回放确定性)---")
|
||||
print("--- 关卡节点图·生成图推进(参数档×种子产图·满配冒险成功/恢复公式/败北线/回放确定性)---")
|
||||
var autoplay = load("res://sim/level_autoplay.gd")
|
||||
var hero_base := {"atk": int(_db.hero.atk), "hp": int(_db.hero.max_hp), "ac": int(_db.hero.armor_class)} # 配置基线快照——⑤ 回基线对比用(改表自动跟随,不再硬编码)
|
||||
var nodes1: Array = _level_active_nodes(1)
|
||||
var params1: Dictionary = _db.level_gen_params_row(1)
|
||||
Kit.check("L1 现行起关图取数非空(种子 %d × 参数档 → 节点 %d 颗)" % [ACTIVE_GRAPH_SEED, nodes1.size()], not nodes1.is_empty())
|
||||
# ① 满配通关路径(atk+200%/hp+100%——测试面板同源的属性覆盖)
|
||||
# ① 满配冒险成功路径(atk+200%/hp+100%——测试面板同源的属性覆盖)
|
||||
var win = autoplay.run_level(_db, ACTIVE_GRAPH_SEED, 1, 200, 100)
|
||||
Kit.check("关卡·满配(atk+200/hp+100)通关(实测:%s @节点 %d)" % [win.result, win.defeat_node_index], win.result == "victory")
|
||||
Kit.check("关卡·满配(atk+200/hp+100)完成(实测:%s @节点 %d)" % [win.result, win.defeat_node_index], win.result == "victory")
|
||||
# 行进面读数与图读数自洽:路径逐跳为图出边,路径上战斗/Boss 计数 = battles 读数
|
||||
var by_id1 := _nodes_by_id(nodes1)
|
||||
var path_battles := 0
|
||||
@@ -339,10 +339,10 @@ func _smoke_level() -> void:
|
||||
var vkind := String(by_id1.get(int(vid), {}).get("type", ""))
|
||||
if vkind == "battle" or vkind == "boss":
|
||||
path_battles += 1
|
||||
Kit.check("L1 通关路径逐跳为图出边(入口起步·末节点无出边——句柄轴行进自洽)", _path_in_graph(nodes1, win.visited_ids))
|
||||
Kit.check("L1 通关路径战斗/Boss 节点数 = 行进 battles 读数(路径 %d 节点·战斗 %d 颗·battles %d)" % [win.visited_ids.size(), path_battles, int(win.battles)],
|
||||
Kit.check("L1 关卡完成路径逐跳为图出边(入口起步·末节点无出边——句柄轴行进自洽)", _path_in_graph(nodes1, win.visited_ids))
|
||||
Kit.check("L1 关卡完成路径战斗/Boss 节点数 = 行进 battles 读数(路径 %d 节点·战斗 %d 颗·battles %d)" % [win.visited_ids.size(), path_battles, int(win.battles)],
|
||||
path_battles == int(win.battles) and path_battles >= 2)
|
||||
# 终点恒落最长路径步数:Boss 深度 = 参数档层高,且通关路径末节点即该终点
|
||||
# 终点恒落最长路径步数:Boss 深度 = 参数档层高,且关卡完成路径末节点即该终点
|
||||
var boss_depth1 := -1
|
||||
for n in nodes1:
|
||||
if String(n.get("type", "")) == "boss":
|
||||
@@ -350,7 +350,7 @@ func _smoke_level() -> void:
|
||||
var last_id := -1
|
||||
if win.visited_ids.size() > 0:
|
||||
last_id = int(win.visited_ids[win.visited_ids.size() - 1])
|
||||
Kit.check("L1 终点 Boss 深度 = 参数档层高 %d 且通关路径末节点落该深度(实测终点深度 %d·末句柄 %d)" % [int(params1.get("layers", -1)), boss_depth1, last_id],
|
||||
Kit.check("L1 终点 Boss 深度 = 参数档层高 %d 且关卡完成路径末节点落该深度(实测终点深度 %d·末句柄 %d)" % [int(params1.get("layers", -1)), boss_depth1, last_id],
|
||||
boss_depth1 == int(params1.get("layers", -1)) and int(by_id1.get(last_id, {}).get("depth", -1)) == boss_depth1)
|
||||
Kit.check("L1 终点恰一个(大岔口自 L2 起的图侧投影)", _terminal_ids(nodes1).size() == 1 and _ids_of_type(nodes1, "boss").size() == 1)
|
||||
Kit.check("L1 生成图含岔口(出度 ≥2 节点 %d 处)" % _fork_ids(nodes1).size(), _fork_ids(nodes1).size() >= 1)
|
||||
@@ -371,7 +371,7 @@ func _smoke_level() -> void:
|
||||
else:
|
||||
Kit.check("恢复点公式·缺口不足档:回复封顶到缺口 2(实测 %d,比例项 %d)" % [int(rest_narrow["healed"]), expect_pct],
|
||||
int(rest_narrow["healed"]) == mini(expect_pct, 2) and expect_pct > 2)
|
||||
# ③ 裸装败北路径(atk1/hp10——Roguelike 基线应难以通关)
|
||||
# ③ 裸装败北路径(atk1/hp10——Roguelike 基线应难以完成)
|
||||
var lose = autoplay.run_level(_db, ACTIVE_GRAPH_SEED, 1, -80, -80)
|
||||
Kit.check("关卡·裸装(atk≈1/hp≈10)败北路径可达(实测:%s)" % lose.result, lose.result == "defeat")
|
||||
Kit.check("败北节点记录在域内(节点 %d,图节点数 %d)" % [lose.defeat_node_index, nodes1.size()],
|
||||
@@ -488,7 +488,7 @@ func _smoke_level_graph() -> void:
|
||||
Kit.check("图校验拒绝:非 Boss 终点(终点恒为 Boss)〔v1〕", _has_err_category(LT.validate_graph(orphan), "structural"))
|
||||
# ④ L2 选路推进:min / max 双路径全通(确定性路径轴——同种子同图,方向轴只换候选取值)
|
||||
var win_min: Dictionary = autoplay.run_level(_db, ACTIVE_GRAPH_SEED, 2, 200, 100, false, null, "normal", "min")
|
||||
Kit.check("L2·满配 min 路径通关(实测:%s)" % win_min.result, win_min.result == "victory")
|
||||
Kit.check("L2·满配 min 路径完成(实测:%s)" % win_min.result, win_min.result == "victory")
|
||||
var n2_last := -1
|
||||
if win_min.visited_ids.size() > 0:
|
||||
n2_last = int(win_min.visited_ids[win_min.visited_ids.size() - 1])
|
||||
@@ -501,7 +501,7 @@ func _smoke_level_graph() -> void:
|
||||
_path_shop_count(nodes2, win_min.visited_ids) == 1)
|
||||
Kit.check("选路口发生 %d 次(每节点兑付后——零自动推进)" % win_min.choices, win_min.choices >= 3)
|
||||
var win_max: Dictionary = autoplay.run_level(_db, ACTIVE_GRAPH_SEED, 2, 200, 100, false, null, "normal", "max")
|
||||
Kit.check("L2·满配 max 路径(候选取最大句柄)通关(实测:%s·路径 %s)" % [win_max.result, str(win_max.visited_ids)], win_max.result == "victory")
|
||||
Kit.check("L2·满配 max 路径(候选取最大句柄)完成(实测:%s·路径 %s)" % [win_max.result, str(win_max.visited_ids)], win_max.result == "victory")
|
||||
var n2_last_max := -1
|
||||
if win_max.visited_ids.size() > 0:
|
||||
n2_last_max = int(win_max.visited_ids[win_max.visited_ids.size() - 1])
|
||||
@@ -973,7 +973,7 @@ func _smoke_shop_room() -> void:
|
||||
var shop_run := {}
|
||||
for s in range(42, 92):
|
||||
var r0 = load("res://core/level_runner.gd").new(_db, _fresh_rng(s))
|
||||
r0.set_hero_override(900, 400) # 行进段快速通关档(购买断言与百分比轴正交——加算区单独断言)
|
||||
r0.set_hero_override(900, 400) # 行进段快速完成档(购买断言与百分比轴正交——加算区单独断言)
|
||||
r0.start_level(2, s)
|
||||
var st := _battle_st("normal")
|
||||
var guard := 60
|
||||
|
||||
@@ -159,7 +159,7 @@ func _meta_settle_and_workshop() -> void:
|
||||
var row1: Dictionary = _db.meta_boss_keystone_row(1)
|
||||
var row2: Dictionary = _db.meta_boss_keystone_row(2)
|
||||
# ⑥ 逐杀掉落收束入账(掉落已在冒险内逐杀入账〔summary.keystone_total 含全部〕,
|
||||
# keystone_drops 明细随摘要归档;首杀标记仍由 apply 逐通关关次置位)
|
||||
# keystone_drops 明细随摘要归档;首杀标记仍由 apply 逐关卡完成关次置位)
|
||||
var d1: Dictionary = mp.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 1,
|
||||
"stopped_at": 2, "keystone_total": int(row1.get("first_kill", 0)),
|
||||
"keystone_drops": [{"level": 1, "first": true, "amount": int(row1.get("first_kill", 0))}],
|
||||
@@ -186,21 +186,21 @@ func _meta_settle_and_workshop() -> void:
|
||||
"master_seed": 13})
|
||||
Kit.check("重复杀第三轮恒定不递减(验收5——无衰减曲线)",
|
||||
not bool(d3["boss_drops"][0].get("first", true)) and int(d3["boss_drops"][0].get("amount", 0)) == int(row1.get("repeat_kill", -1)))
|
||||
# 混合归因:通关两关 → L1 重复 + L2 首杀(明细随摘要逐笔归档)
|
||||
# 混合归因:完成两关 → L1 重复 + L2 首杀(明细随摘要逐笔归档)
|
||||
var d4: Dictionary = mp.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 2,
|
||||
"stopped_at": 0, "keystone_total": int(row1.get("repeat_kill", 0)) + int(row2.get("first_kill", 0)),
|
||||
"keystone_drops": [{"level": 1, "first": false, "amount": int(row1.get("repeat_kill", 0))},
|
||||
{"level": 2, "first": true, "amount": int(row2.get("first_kill", 0))}],
|
||||
"master_seed": 14})
|
||||
Kit.check("两关通关逐关归因:L1 重复档 + L2 首杀档(first_kill 金额)",
|
||||
Kit.check("两关完成逐关归因:L1 重复档 + L2 首杀档(first_kill 金额)",
|
||||
not bool(d4["boss_drops"][0].get("first", true)) and bool(d4["boss_drops"][1].get("first", false))
|
||||
and int(d4["boss_drops"][1].get("amount", 0)) == int(row2.get("first_kill", -1)))
|
||||
# ⑦ 败北零通关摘要:余额只增不减(零入账)、clear_counts 不变
|
||||
# ⑦ 败北零关卡完成摘要:余额只增不减(零入账)、clear_counts 不变
|
||||
var bal_before: int = sv.keystone_balance()
|
||||
var cc1_before: int = sv.clear_count(1)
|
||||
var dz: Dictionary = mp.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 0,
|
||||
"stopped_at": 1, "keystone_total": 0, "master_seed": 15})
|
||||
Kit.check("败北零通关摘要:零入账零标记(钥石只增不减——验收1;败北局 clear_counts 不变)",
|
||||
Kit.check("败北零关卡完成摘要:零入账零标记(钥石只增不减——验收1;败北局 clear_counts 不变)",
|
||||
dz["boss_drops"].is_empty() and sv.keystone_balance() == bal_before and sv.clear_count(1) == cc1_before)
|
||||
# ⑧ 秘宝解锁服务(V3 钥石购买/V4 条件解锁;注入=直调解锁写口+直置存档生涯计数,
|
||||
# 不走正常资源积累流程——卡片 7.1 测试方式)
|
||||
@@ -217,10 +217,10 @@ func _meta_settle_and_workshop() -> void:
|
||||
and sv_u.keystone_balance() == 0)
|
||||
Kit.check("已解锁重复购买拒绝〔秘宝批 V3〕",
|
||||
not mp_u.buy_treasure_unlock("morning_order"))
|
||||
Kit.check("条件未达不解锁(远程镐=通关 1 次)〔秘宝批 V4〕",
|
||||
Kit.check("条件未达不解锁(远程镐=冒险成功 1 次)〔秘宝批 V4〕",
|
||||
mp_u.refresh_treasure_unlocks().is_empty() and not sv_u.item_unlocked("ranged_pick"))
|
||||
sv_u._data["meta"]["adventure_cleared"] = 1 # 直置存档生涯计数(调试注入口径)
|
||||
Kit.check("远程镐=adventure_cleared≥1 达标恰解锁一次+重复刷新幂等(C6:通关=打通整局冒险)〔秘宝批 V4〕",
|
||||
Kit.check("远程镐=adventure_cleared≥1 达标恰解锁一次+重复刷新幂等(C6:冒险成功=打通整局冒险)〔秘宝批 V4〕",
|
||||
(mp_u.refresh_treasure_unlocks() as Array).has("ranged_pick")
|
||||
and sv_u.item_unlocked("ranged_pick") and mp_u.refresh_treasure_unlocks().is_empty())
|
||||
sv_u._data["meta"]["boss_kill_count"] = 3
|
||||
@@ -230,7 +230,7 @@ func _meta_settle_and_workshop() -> void:
|
||||
Kit.check("天眼单条件不足不解锁(AND 语义——cleared 3 达 boss 4 未达)〔秘宝批 V4〕",
|
||||
not sv_u.item_unlocked("sky_eye"))
|
||||
sv_u._data["meta"]["boss_kill_count"] = 4
|
||||
Kit.check("天眼双条件 AND(通关≥3 且 Boss≥4)达标解锁〔秘宝批 V4〕",
|
||||
Kit.check("天眼双条件 AND(冒险成功≥3 且 Boss≥4)达标解锁〔秘宝批 V4〕",
|
||||
(mp_u.refresh_treasure_unlocks() as Array).has("sky_eye"))
|
||||
sv_u._data["meta"]["vision_used"] = 10
|
||||
Kit.check("信标网络=真视类工具累计使用≥10(行为计数新域——use_treasure 受理成功累计)〔秘宝批 V4〕",
|
||||
@@ -267,7 +267,7 @@ func _meta_settle_and_workshop() -> void:
|
||||
and int(mp_a.attune_camp_status()["refresh"]["charges"]) == 1)
|
||||
mp_a.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 1,
|
||||
"stopped_at": 2, "keystone_total": 0, "master_seed": 21})
|
||||
Kit.check("通关 1 关 → Slots 恒 0(旧「按击杀 Boss 次数自动涨」条件已删——槽位唯一来源=树点亮)且 Boss 击杀总数=1(计数域保留)〔天赋树批 V14〕",
|
||||
Kit.check("完成 1 关 → Slots 恒 0(旧「按击杀 Boss 次数自动涨」条件已删——槽位唯一来源=树点亮)且 Boss 击杀总数=1(计数域保留)〔天赋树批 V14〕",
|
||||
Attunement.slots(_db, sv_a) == 0 and sv_a.boss_kill_count() == 1)
|
||||
sv_a.set_tree_level("att_socket1", 1)
|
||||
Kit.check("树上点亮同调位 1 → Slots=1(槽位收编树节点——Q2 裁甲)",
|
||||
|
||||
@@ -27,7 +27,7 @@ func _test_no_arg_instance_has_no_activation() -> void:
|
||||
"sv2 无参实例无激活态(路径空/激活号 0/写盘目标空)〔卡① sv2〕")
|
||||
Kit.check_quiet(sm.keystone_balance() == 0 and not sm.has_breakpoint()
|
||||
and sm.adventure_total() == 0 and sm.adventure_cleared() == 0,
|
||||
"sv2 内存=默认合法空态(钥石/断点/总局数/通关数全零值——访问器 fail-safe)〔卡① sv2〕")
|
||||
"sv2 内存=默认合法空态(钥石/断点/总局数/冒险成功数全零值——访问器 fail-safe)〔卡① sv2〕")
|
||||
# attach_path 注入任意隔离路径=注入即激活(契约 7 单路径语义——隔离面行为零变化)
|
||||
sm.attach_path("user://smoke_boot_noact_iso.json")
|
||||
Kit.check_quiet(sm.has_write_target() and sm.keystone_balance() == 0,
|
||||
|
||||
@@ -78,11 +78,11 @@ func _smoke_save() -> void:
|
||||
w.unlock_item("probe_compass")
|
||||
w.apply_adventure_summary({"result": "failed", "cleared": false, "levels_cleared": 2,
|
||||
"stopped_at": 3, "keystone_total": 3, "master_seed": 99})
|
||||
Kit.check("apply 原语入账(钥石 7+3、通关次数 1/1/0)",
|
||||
Kit.check("apply 原语入账(钥石 7+3、关卡完成次数 1/1/0)",
|
||||
w.keystone_balance() == 10 and w.clear_count(1) == 1 and w.clear_count(2) == 1 and w.clear_count(3) == 0)
|
||||
Kit.check("首杀标记随通关关卡置位(1/2 有、3 无)",
|
||||
Kit.check("首杀标记随关卡完成置位(1/2 有、3 无)",
|
||||
w.boss_first_kill_marked(1) and w.boss_first_kill_marked(2) and not w.boss_first_kill_marked(3))
|
||||
Kit.check("生涯终身计数随 apply 自增恰一(中止局:总局数 1、通关局数 0——迭代 18 卡④)",
|
||||
Kit.check("生涯终身计数随 apply 自增恰一(中止局:总局数 1、冒险成功局数 0——迭代 18 卡④)",
|
||||
w.adventure_total() == 1 and w.adventure_cleared() == 0)
|
||||
Kit.check("写盘成功", w.write_to_disk())
|
||||
var r = sm_script.new(paths[0])
|
||||
@@ -95,7 +95,7 @@ func _smoke_save() -> void:
|
||||
and r.forge_unlocked_purchases() == ["spear"]
|
||||
and r.forge_weapon() == "spear" and r.forge_armor() == "ward")
|
||||
Kit.check("道具解锁往返相等", r.item_unlocked("probe_compass") and not r.item_unlocked("rope"))
|
||||
Kit.check("通关次数表往返相等", r.clear_count(1) == 1 and r.clear_count(2) == 1 and r.clear_count(3) == 0)
|
||||
Kit.check("关卡完成次数表往返相等", r.clear_count(1) == 1 and r.clear_count(2) == 1 and r.clear_count(3) == 0)
|
||||
Kit.check("首杀标记往返相等",
|
||||
r.boss_first_kill_marked(1) and r.boss_first_kill_marked(2) and not r.boss_first_kill_marked(3))
|
||||
Kit.check("生涯终身计数往返相等(1/0)", r.adventure_total() == 1 and r.adventure_cleared() == 0)
|
||||
@@ -265,11 +265,11 @@ func _smoke_save() -> void:
|
||||
f13b.load_from_disk()
|
||||
f13b.apply_adventure_summary({"result": "completed", "cleared": true, "levels_cleared": 3,
|
||||
"stopped_at": 0, "keystone_total": 0, "master_seed": 1})
|
||||
Kit.check("收束自增恰一·通关局(三关全通 cleared=true:总局数+1 且通关局数+1)",
|
||||
Kit.check("收束自增恰一·冒险成功局(三关全通 cleared=true:总局数+1 且冒险成功局数+1)",
|
||||
f13b.adventure_total() == 1 and f13b.adventure_cleared() == 1)
|
||||
f13b.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1,
|
||||
"stopped_at": 2, "keystone_total": 0, "master_seed": 2})
|
||||
Kit.check("收束自增恰一·中止局(放弃局:仅总局数+1,通关局数不动)",
|
||||
Kit.check("收束自增恰一·中止局(放弃局:仅总局数+1,冒险成功局数不动)",
|
||||
f13b.adventure_total() == 2 and f13b.adventure_cleared() == 1)
|
||||
|
||||
# ⑮ 旧档破坏性废弃(契约 4:换代前结构检测=树域废弃重置,禁迁移禁折算)〔V16〕
|
||||
@@ -321,11 +321,11 @@ func _smoke_save() -> void:
|
||||
var mp13 = load("res://core/meta_progression.gd").new(_db, f13b)
|
||||
mp13.settle_adventure({"result": "failed", "cleared": false, "levels_cleared": 2,
|
||||
"stopped_at": 3, "keystone_total": 0, "master_seed": 3})
|
||||
Kit.check("收束链自增·中止局(settle_adventure 真实挂点:总局数+1、通关局数不动)",
|
||||
Kit.check("收束链自增·中止局(settle_adventure 真实挂点:总局数+1、冒险成功局数不动)",
|
||||
f13b.adventure_total() == 3 and f13b.adventure_cleared() == 1)
|
||||
mp13.settle_adventure({"result": "completed", "cleared": true, "levels_cleared": 3,
|
||||
"stopped_at": 0, "keystone_total": 0, "master_seed": 4})
|
||||
Kit.check("收束链自增·通关局(三关全通:总局数+1 且通关局数+1——全链恰一)",
|
||||
Kit.check("收束链自增·冒险成功局(三关全通:总局数+1 且冒险成功局数+1——全链恰一)",
|
||||
f13b.adventure_total() == 4 and f13b.adventure_cleared() == 2)
|
||||
|
||||
# 段尾清理(测试文件绝不残留)
|
||||
|
||||
@@ -46,7 +46,7 @@ func _test_peek_three_slots_distinct_values() -> void:
|
||||
var ps: Dictionary = t.peek_slot_summary(i)
|
||||
Kit.check_quiet(bool(ps.get("readable", false)) and int(ps.get("keystone", -1)) == 200 * i
|
||||
and int(ps.get("cleared", -1)) == i and int(ps.get("total", -1)) == 5 * i,
|
||||
"sv7 窥视三项逐槽正确·槽 %d(钥石/通关/总局)〔卡① sv7〕" % i)
|
||||
"sv7 窥视三项逐槽正确·槽 %d(钥石/冒险成功/总局)〔卡① sv7〕" % i)
|
||||
Kit.check_quiet(t.active_slot() == 0 and not t.has_write_target() and t.keystone_balance() == 0,
|
||||
"sv7 窥视不激活不写内存(激活号 0/无写盘目标/内存余额零)〔卡① sv7〕")
|
||||
# 路径注入口同语义(契约 7 注入面)
|
||||
|
||||
@@ -93,7 +93,7 @@ func _smoke_adventure_table() -> void:
|
||||
if _db.adventure == null:
|
||||
return
|
||||
Kit.check("AdventureTable.validate() 零违例(全键域检+档位配额结构锚在案)", _db.adventure.validate().is_empty())
|
||||
Kit.check("clear_heal_pct ∈ [0,100](%d——通关部分回血比例)" % int(_db.adventure.clear_heal_pct),
|
||||
Kit.check("clear_heal_pct ∈ [0,100](%d——关卡完成部分回血比例)" % int(_db.adventure.clear_heal_pct),
|
||||
int(_db.adventure.clear_heal_pct) >= 0 and int(_db.adventure.clear_heal_pct) <= 100)
|
||||
for tier in _db.adventure.TIERS:
|
||||
Kit.check("档位配额结构锚(四级 70/25/7/0——迭代 31 卡② 净删两件投放):%s = %d 张" % [tier, int(_db.adventure.TIER_QUOTAS[tier])],
|
||||
@@ -464,7 +464,7 @@ func _smoke_nine_types_gp_stream() -> void:
|
||||
gp_pos = int(row.weight)
|
||||
if String(row.get("reward_id", "")) == "gp_lose":
|
||||
gp_neg = int(row.weight)
|
||||
Kit.check("奖励字典 18 行(九型×正负 18 格——aux 挖格投放四行已退役 2026-10-01 用户拍板甲:战斗/挖格不掉道具,秘宝唯一来路=三选一共享池〔节点/通关〕;清图槽独立批尾不计)",
|
||||
Kit.check("奖励字典 18 行(九型×正负 18 格——aux 挖格投放四行已退役 2026-10-01 用户拍板甲:战斗/挖格不掉道具,秘宝唯一来路=三选一共享池〔节点/关卡完成〕;清图槽独立批尾不计)",
|
||||
cell_rows == 18)
|
||||
# 危险格负向直伤当量(负向≈正向 2 倍定价——真人反馈「三雷 3/4 血无后果感」的投值口径)
|
||||
var mine_power := {"heal_backlash": 6, "pierce_snare": 8, "magic_recoil": 8} # 危险格负向直伤当量(抬档值;依据见 numfix 报告)
|
||||
|
||||
@@ -83,19 +83,19 @@ func _test_boss_name_uses_boss_rowset() -> void:
|
||||
Kit.check("Boss 名与同深度战斗行集名不同(期望「%s」≠「%s」——行类路由可判别)〔fv24〕" % [boss_expect, battle_name], boss_expect != battle_name)
|
||||
|
||||
|
||||
# 关卡完成停点文案族措辞钉子(通关口径换代批):回血行/满血合法态/渠道句/摘要关数行——
|
||||
# 「通关」退役后玩家可见面由本组断言钉在新措辞上(撤销证明:回退 battle_narrator 对应行→断言红→复原复绿)
|
||||
# 关卡完成停点文案族措辞钉子(口径换代批):回血行/满血合法态/渠道句/摘要关数行——
|
||||
# 旧措辞退役后玩家可见面由本组断言钉在新措辞上(撤销证明:回退 battle_narrator 对应行→断言红→复原复绿)
|
||||
func _test_level_clear_wording() -> void:
|
||||
var healed: String = NarratorScript.curio_pick_header({"level_index": 1, "heal_gained": 5, "hp_before": 10, "hp_after": 15})
|
||||
Kit.check("关卡完成回血行带入回血量与生命区间(实际「%s」)〔通关换代 V1〕" % healed,
|
||||
Kit.check("关卡完成回血行带入回血量与生命区间(实际「%s」)〔口径换代 V1〕" % healed,
|
||||
healed.contains("关卡完成回血 +5(生命 10 → 15)"))
|
||||
var full_hp: String = NarratorScript.curio_pick_header({"level_index": 1, "heal_gained": 0, "hp_before": 12, "hp_after": 12})
|
||||
Kit.check("满血零回血合法态文案(实际「%s」)〔通关换代 V1〕" % full_hp,
|
||||
Kit.check("满血零回血合法态文案(实际「%s」)〔口径换代 V1〕" % full_hp,
|
||||
full_hp.contains("生命已满——关卡完成回血 0(合法态)"))
|
||||
var node_header: String = NarratorScript.node_curio_header()
|
||||
Kit.check("奖励节点头部行渠道句点明关卡完成三选一(实际「%s」)〔通关换代 V2〕" % node_header,
|
||||
Kit.check("奖励节点头部行渠道句点明关卡完成三选一(实际「%s」)〔口径换代 V2〕" % node_header,
|
||||
node_header.contains("与关卡完成专长三选一同库分流"))
|
||||
var summary: String = NarratorScript.adventure_summary_text(
|
||||
{"cleared": false, "result": "failed", "levels_cleared": 1, "stopped_at": 2, "keystone_total": 4, "master_seed": 42})
|
||||
Kit.check("冒险摘要完成关数行(实际「%s」)〔通关换代 V3〕" % summary,
|
||||
Kit.check("冒险摘要完成关数行(实际「%s」)〔口径换代 V3〕" % summary,
|
||||
summary.contains("完成 1 关 · 止步第 2 层") and not summary.contains("通关"))
|
||||
@@ -1,6 +1,6 @@
|
||||
extends SceneTree
|
||||
# 三选一覆盖层 UI 直验套件——
|
||||
# 守 node_overlay.show_reward_items 的继任共用面;奖励节点三选一与通关三选一共用 CurioPickOverlay,
|
||||
# 守 node_overlay.show_reward_items 的继任共用面;奖励节点三选一与关卡完成三选一共用 CurioPickOverlay,
|
||||
# 本套件守共用面的「功能存在 ≠ 功能可达」盲区:选择外发/强制确认两段式/结果态。
|
||||
# 〔无替换子面板路段——装配不可变契约 1,overlay 无 replace 态。〕
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/reward_overlay_test.gd(expect 0)
|
||||
|
||||
@@ -52,6 +52,7 @@ func _init() -> void:
|
||||
_smoke_diag_button_retired()
|
||||
_status_panel_projection()
|
||||
_career_overlay_projection()
|
||||
_camp_recent_projection()
|
||||
quit(Kit.finish("theme_projection_test"))
|
||||
|
||||
|
||||
@@ -806,6 +807,20 @@ func _all_labels(root: Node) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# ---- 营地最近冒险行投影(口径换代 V6:完成关数措辞钉子——open_camp 全量重建路径直调;
|
||||
# 最小档 data=recent 单条 completed 局,其余键缺省走 .get 兜底——与组合根 _camp_data 投影同键面)----
|
||||
func _camp_recent_projection() -> void:
|
||||
print("--- 营地·最近冒险行投影(open_camp 最小档)---")
|
||||
var camp = load("res://presentation/views/camp_overlay_view.gd").new()
|
||||
camp.open_camp({"balance": 0, "recent": [
|
||||
{"result": "completed", "levels_cleared": 2, "keystone_total": 5, "master_seed": 42}]})
|
||||
var recent_text: String = String(camp._recent_list.text)
|
||||
Kit.check("营地最近冒险行完成关数措辞(实际「%s」)〔口径换代 V6〕" % recent_text,
|
||||
recent_text.contains("完成 2 关") and not recent_text.contains("通关"))
|
||||
camp.visible = false
|
||||
camp.free()
|
||||
|
||||
|
||||
# ---- 生涯页只读投影一致性(汇总段六类九项逐行 == SaveManager 计数访问器/
|
||||
# MetaProgression 访问器读数;投影拼装与 HudPresenter.career_data 同构(同访问器族同序)。
|
||||
# 只读性连接巡检与零动效断言归 ui_struct_probe 呈现侧,本段只管数据面+旧档载入显示
|
||||
@@ -837,17 +852,27 @@ func _career_overlay_projection() -> void:
|
||||
Kit.check("生涯页开层可见(open_career 直开——零动效路径)", co.visible)
|
||||
Kit.check("总局数投影 = adventure_total() 读数(%s)" % co._total_label.text,
|
||||
String(co._total_label.text) == "2 局")
|
||||
Kit.check("通关局数投影 = adventure_cleared() 读数(%s)" % co._cleared_label.text,
|
||||
Kit.check("冒险成功局数投影 = adventure_cleared() 读数(%s)" % co._cleared_label.text,
|
||||
String(co._cleared_label.text) == "1 局(三关全通)")
|
||||
var expect_clears: Array[String] = []
|
||||
for row in (data["clears"] as Array):
|
||||
expect_clears.append("%s ×%d" % [String(row.get("display_name", "")), int(row.get("count", 0))])
|
||||
Kit.check("各关通关次数投影 = clear_count() 逐关读数(%s)" % co._clears_label.text,
|
||||
Kit.check("各关完成次数投影 = clear_count() 逐关读数(%s)" % co._clears_label.text,
|
||||
String(co._clears_label.text) == " · ".join(expect_clears))
|
||||
Kit.check("Boss 首杀/总杀投影 = 首杀标记计数 + boss_kill_count()(%s / %s)" % [co._boss_first_label.text, co._boss_kills_label.text],
|
||||
String(co._boss_first_label.text) == "3/3 关" and String(co._boss_kills_label.text) == "4 次")
|
||||
Kit.check("钥石余额徽投影 = keystone_balance() 读数(%s)" % co._keystone_badge.value_label.text,
|
||||
String(co._keystone_badge.value_label.text) == "钥石 %d" % mp.keystone_balance())
|
||||
# 口径换代(2026-10-26):行标签换代钉子+生涯页零「通关」残留(负断言兜全页)
|
||||
var career_label_texts: Array[String] = []
|
||||
for career_lbl: Label in _all_labels(co):
|
||||
career_label_texts.append(String(career_lbl.text))
|
||||
var career_labels_joined: String = "|".join(career_label_texts)
|
||||
Kit.check("生涯页行标签换代(冒险成功局数/各关完成次数)〔口径换代 V4〕",
|
||||
career_labels_joined.contains("冒险成功局数") and career_labels_joined.contains("各关完成次数"))
|
||||
Kit.check("生涯页 label 全集零「通关」残留〔口径换代 V4〕", not career_labels_joined.contains("通关"))
|
||||
Kit.check("秘宝解锁条件标签冒险成功口径(UiKit 单点常量)〔口径换代 V5〕",
|
||||
UiKit.TREASURE_REQ_LABELS["adventure_cleared"] == "冒险成功")
|
||||
co.visible = false
|
||||
co.free()
|
||||
# 旧档载入显示(完成定义 6):v5 现役档形态(计数域两键不存在)升 v6 → 生涯页正常呈现缺省值
|
||||
@@ -866,7 +891,7 @@ func _career_overlay_projection() -> void:
|
||||
"count": v5.clear_count(i + 1)})
|
||||
var co_v5 = load("res://presentation/views/career_overlay.gd").new()
|
||||
co_v5.open_career(data_v5)
|
||||
Kit.check("旧档载入后生涯页正常显示(v5 升 v6:总局数/通关局数显示 0,既有钥石余额原样呈现)",
|
||||
Kit.check("旧档载入后生涯页正常显示(v5 升 v6:总局数/冒险成功局数显示 0,既有钥石余额原样呈现)",
|
||||
co_v5.visible and String(co_v5._total_label.text) == "0 局" \
|
||||
and String(co_v5._cleared_label.text) == "0 局(三关全通)" \
|
||||
and String(co_v5._keystone_badge.value_label.text) == "钥石 3")
|
||||
|
||||
+365
-347
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user