Merge branch 'termsweep-game-20261026':通关口径换代批(提前实施卡批,用户裁准)——game 面「通关」全量退役:A 表 12 条玩家可见字串逐字落地(关卡完成/冒险成功短语族)+B 面 13 测试文件 60 处与三探针断言随动(必红三处 wr1-3)+V1-V7 新增断言(真实入口+撤销证明 12+1 条全闭合)+验证集清单重生成 1591 条对账零差异+C 面注释清扫 61 条+S11 豁免单 7→20(带日期历史读数 13 行)+体验验证清单 3 行引语+tests/README 豁免计数订正;计数器键名/数据键/锚文件名/数值行零触碰,零数值改动 11 对拍不补跑;独立验收审计通过(对照表 17 行全过/撤销证明抽三复现/削弱零发现);收口前并 master(daa8860b+90291be9 闸后重跑+4717cf0a 纯文档件并入不作废回执),全量域闸绿回执 a51cbec7 domain=full 代码面覆盖分支尖;卡片与待办条目随批删除(quotaface 先例)

This commit is contained in:
DKChan committed 2026-10-26 08:19:25 +08:00
commit e4315ec2ac
53 files changed
+767 -847

No files matched your search

+7 -7
View File
@@ -2,9 +2,9 @@
> // VERTICAL SLICE - NOT FOR PRODUCTION
> // Validation Question(v2,2026-09-11):线性第一关(战×2→道具→战×2→恢复→Boss)能否承载
> // "数值提升→通关期望改善"的 Roguelike 元进度验证——基线属性应该难到打不过、天赋数值提升把
> // 通关期望拉回可玩带,且这条曲线能用数值调整板 + 批量模拟**测量出来**
> // (通关攻击期望 / 败北血量数学期望);v1 的单战斗循环问题(逐行归因 P2 / Core headless 可跑)
> // "数值提升→冒险成功期望改善"的 Roguelike 元进度验证——基线属性应该难到打不过、天赋数值提升把
> // 冒险成功期望拉回可玩带,且这条曲线能用数值调整板 + 批量模拟**测量出来**
> // (冒险成功攻击期望 / 败北血量数学期望);v1 的单战斗循环问题(逐行归因 P2 / Core headless 可跑)
> // 已于 2026-09-10 验证并保持回归(初版断言面 46 项,后并迁系统测试宿主,沿革查 git log)。
> // Date: 2026-09-10(v1)· 2026-09-11(v2 关卡扩展)· 引擎 Godot 4.7.2 · 本目录仅作设计验证参考,禁止 import 进生产代码
@@ -12,10 +12,10 @@
1. 用 Godot 4.7.2 打开本目录(含 `project.godot` 的文件夹)
2. 按 F5 运行(主场景 `presentation/main.tscn`)
3. **主入口:`▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)`**——一条命走完三关(冒险结构实现批 2026-09-21:生命/GP/加算区跨关同账、非末关通关发大奖励〔上限提升+血回满+钥石〕、任意战斗倒下冒险终、结算屏出冒险摘要五件事;选路停点可「放弃冒险」(同失败结算,2026-09-21 用户拍板补入口));首关节点链:战→战→赏→战→战→疗→王。唯一试玩入口 = 完整冒险——单关测试直入口(L2/L3)已退役(2026-09-23 用户拍板)
4. `调试` 角标浮窗(恒开——2026-10-16 用户令 DS_DEBUG 写死,原环境变量门控退役;2026-10-04 迭代 14.5 调试整合批:原数值调整板/随机种子框/两层调试三散件收一):攻击/生命百分比加成(模拟天赋树解锁)+ 批量模拟(输出通关率/承伤期望/败北节点分布)+ 两层调试入口
3. **主入口:`▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)`**——一条命走完三关(冒险结构实现批 2026-09-21:生命/GP/加算区跨关同账、非末关的关卡完成发大奖励〔上限提升+血回满+钥石〕、任意战斗倒下冒险终、结算屏出冒险摘要五件事;选路停点可「放弃冒险」(同失败结算,2026-09-21 用户拍板补入口));首关节点链:战→战→赏→战→战→疗→王。唯一试玩入口 = 完整冒险——单关测试直入口(L2/L3)已退役(2026-09-23 用户拍板)
4. `调试` 角标浮窗(恒开——2026-10-16 用户令 DS_DEBUG 写死,原环境变量门控退役;2026-10-04 迭代 14.5 调试整合批:原数值调整板/随机种子框/两层调试三散件收一):攻击/生命百分比加成(模拟天赋树解锁)+ 批量模拟(输出关卡完成率/承伤期望/败北节点分布)+ 两层调试入口
headless 冒烟(752 项断言〔计数随批增长,现值以最新批末闸读数为准〕,含关卡线性推进/满配通关/裸装败北/回放确定性;2026-10-02 性能报告实测 60.55 秒,报告在 `性能实测报告(已退役,git 历史可查)`):
headless 冒烟(752 项断言〔计数随批增长,现值以最新批末闸读数为准〕,含关卡线性推进/满配冒险成功/裸装败北/回放确定性;2026-10-02 性能报告实测 60.55 秒,报告在 `性能实测报告(已退役,git 历史可查)`):
```
Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://probe/smoke_all.gd
@@ -35,7 +35,7 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . res://probe/ui_tune_pr
其余八场景探针(ui_click_probe 行为链 / ui_input_probe 输入合成 / ui_struct_probe 全屏结构 / ui_contrast_probe 对比度 / ui_probe·ui_tune_probe 调参面板 / t17_defer_probe 结算演播延迟链回归 / shot_probe·fx_probe·fx_shot_probe·board_shot_probe 画面与特效采样;全部由 `tools/probe_gate.sh` 与 `tools/render_gate.sh` 调度,闸清单见 `tools/run_all_gates.sh`)+ 板图校准工具 `sim/calibrate_boards.gd`(board_calibration.csv 重跑入口,--script 运行):
全冒险期望扫描(零加成三关连探批量——通关率/止步分布/败退节点;调参期难度读数一律走全冒险口径):
全冒险期望扫描(零加成三关连探批量——关卡完成率/止步分布/败退节点;调参期难度读数一律走全冒险口径):
```
Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://sim/adventure_monte_carlo.gd -- [N] [seed0]
+14 -14
View File
@@ -7,9 +7,9 @@ class_name AdventureRunner
extends RefCounted
signal level_started(level_index: int, display_name: String) ## 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
signal curio_choice_presented(payload: Dictionary) ## 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
signal curio_choice_presented(payload: Dictionary) ## 专长三选一停点(非末关的关卡完成恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
signal curio_resolved(result: Dictionary) ## 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
signal adventure_finished(summary: Dictionary) ## 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops)
signal adventure_finished(summary: Dictionary) ## 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/完成几关/止步第几关/钥石合计/主种子(+additive keystone_drops)
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;S6 经 _forward_* 方法)----
signal node_entered(node: Dictionary, info: Dictionary)
signal choices_presented(candidates: Array)
@@ -266,7 +266,7 @@ func _resume_pending(bp_pending: Dictionary) -> void:
var migrated: Array[Variant] = []
for c in (bp_pending.get("candidates", []) as Array):
if c is Dictionary:
# T4——feat_id 随迁移(通关渠道候选=纯专长令牌,身份键 relic|tier|feat_id 三段全要)
# T4——feat_id 随迁移(关卡完成渠道候选=纯专长令牌,身份键 relic|tier|feat_id 三段全要)
var mig := {"kind": String((c as Dictionary).get("kind", "")), "tier": String((c as Dictionary).get("tier", ""))}
if String(mig["kind"]) == "relic":
mig["feat_id"] = String((c as Dictionary).get("feat_id", ""))
@@ -339,7 +339,7 @@ func continue_to_next_level() -> bool:
# 停点空候选消化(池空=三选一环节整体跳过:候选 0 件时停点视同已处理,
# 呈现层跳开选择面直接走后续推进。仅空候选合法;非空停点拒绝——零自动推进契约不破,正常
# 消化仍只走 pick_curio/apply_node_curio 兑付。触发源=渠道白名单过滤后池空(关卡 Boss 通关
# 消化仍只走 pick_curio/apply_node_curio 兑付。触发源=渠道白名单过滤后池空(关卡 Boss 击破=关卡完成
# 渠道只出专长,专长零配置/旧档 deck 无 relic 余库即空集)。)
func skip_empty_curio() -> bool:
if _curio_pending.is_empty():
@@ -390,7 +390,7 @@ func _feat_payload_keys(feat_id: String) -> Dictionary:
# 三选一件兑付共享体(TP0——令牌消费、摘牌单点,含 relic 分支):
# 属性件写加算区 + 件获取计数、AP 件写第四轴、GP 件走钱包、恢复件当即回血(封顶生效上限——
# 与账本置满/通关回血同式 effective_max_hp)、relic 件入持有集(散件单体保底——效果接线面
# 与账本置满/关卡完成回血同式 effective_max_hp)、relic 件入持有集(散件单体保底——效果接线面
# 按挂点消费,档位质变由 FeatsEngine 求值)。兑付即从 _curio_deck 摘除该令牌(选中踢池;
# 未选候选从未摘牌=自动回池)。道具件不入此体——未知件返回 false 由调用方按渠道名报错。
func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String = "") -> bool:
@@ -483,7 +483,7 @@ func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String
_snapshot_breakpoint()
# ---- 奖励节点三选一(与通关三选一共享同池同权重同次数上限)----
# ---- 奖励节点三选一(与关卡完成三选一共享同池同权重同次数上限)----
# 候选呈现(节点到达时调用——恰一 reward_pick scope 洗牌 + 道具件掷定 scope;本停点断点不载,
# 续玩重建到选路停点后重走重抽,恢复的全局计数器保证同候选逐位复现)。
@@ -498,7 +498,7 @@ func present_node_curio(candidates: Array) -> void:
_node_curio_pending = {"candidates": candidates.duplicate(true)}
# 点选兑付(节点域——无部分回血无钥石;令牌消费与牌库摘牌与通关三选一同库;relic 同权入持有集)
# 点选兑付(节点域——无部分回血无钥石;令牌消费与牌库摘牌与关卡完成三选一同库;relic 同权入持有集)
func apply_node_curio(kind: String, tier: String = "", feat_id: String = "") -> bool:
if kind == "feat_ticket":
return _redeem_feat_ticket() # T5 拿到即开——节点停点候选替换为专长三选一(呈现层本地重绘)
@@ -562,7 +562,7 @@ func node_curio_pending_state() -> Dictionary:
}
# 三选一刷新(免费位 X/Y——组合根唯一调用口;无停点/无余量 false 零副作用)。通关停点重抽后重发
# 三选一刷新(免费位 X/Y——组合根唯一调用口;无停点/无余量 false 零副作用)。关卡完成停点重抽后重发
# curio_choice_presented(呈现层重建卡片+刷新余量);节点停点由组合根读 node_curio_pending_state 重 present。
func refresh_curio() -> bool:
return _refresh_curio_common(false)
@@ -651,7 +651,7 @@ func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
func _redeem_feat_ticket() -> bool:
_feat_ticket_sold += 1
_feat_ticket_pity = 0
var cands: Array[Variant] = _draw_curio_candidates("level_clear") # 专长池(与通关渠道同白名单)
var cands: Array[Variant] = _draw_curio_candidates("level_clear") # 专长池(与关卡完成渠道同白名单)
if cands.is_empty():
push_error("[冒险结构] 专长凭证兑付失败——专长池空集")
_feat_ticket_sold -= 1
@@ -668,7 +668,7 @@ func _redeem_feat_ticket() -> bool:
# Boss 钥石逐杀掉落(掉落渠道 = Boss/精英击杀;首杀/复杀两档读 meta.tres boss_keystone;
# 首杀判据 = 存档 boss_first_kill 标记位〔断点未注入 = 恒首杀——模拟/冒烟零扰动;标记仍在收束
# apply_adventure_summary 逐通关关次置位〕;末关 Boss 照常入账;明细随摘要 additive 键供结算面归因)
# apply_adventure_summary 逐关卡完成关次置位〕;末关 Boss 照常入账;明细随摘要 additive 键供结算面归因)
func _boss_keystone_drop(li: int) -> void:
var first := true
if _save_manager != null:
@@ -779,7 +779,7 @@ func _on_level_finished(payload: Dictionary) -> void:
# Boss 钥石逐杀掉落(击杀掉落渠道,末关照常入账;首杀/复杀读 meta.tres boss_keystone)
_boss_keystone_drop(level_index)
if level_index < levels_total:
# 通关兑付(非末关恰一次;三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点
# 关卡完成兑付(非末关恰一次;三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点
#(顺序即契约——回血按当刻账本与上限结算,选择停点零自动推进)
var hp_before := int(ledger.get("hero_hp", 0))
var max_eff := effective_max_hp() # 回血封顶口径含调试浮窗 pct(同战斗内上限)
@@ -791,7 +791,7 @@ func _on_level_finished(payload: Dictionary) -> void:
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零)
var pick_payload: Dictionary[Variant, Variant] = {
"level_index": level_index,
"candidates": _draw_curio_candidates("level_clear"), # T4 渠道白名单——关底 Boss 击败=通关渠道只出专长
"candidates": _draw_curio_candidates("level_clear"), # T4 渠道白名单——关底 Boss 击败=关卡完成渠道只出专长
"heal_gained": heal,
"hp_before": hp_before,
"hp_after": int(ledger.get("hero_hp", 0)),
@@ -805,12 +805,12 @@ func _on_level_finished(payload: Dictionary) -> void:
curio_choice_presented.emit(pick_payload)
_snapshot_breakpoint() # 选择停点可中断(契约 5——续玩不重抽不静默丢失)
return
# 末关通关:不发三选一与部分回血(没有下一战可受益)、Boss 钥石照常入账——冒险完成结算
# 末关的关卡完成:不发三选一与部分回血(没有下一战可受益)、Boss 钥石照常入账——冒险完成结算
_settle("completed")
func _settle(result: String) -> void:
# 冒险摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石入账合计 / 本局主种子
# 冒险摘要五件事:打穿没有 / 完成几关 / 止步第几关 / 钥石入账合计 / 本局主种子
summary = {
"result": result,
"cleared": result == "completed",
+1 -1
View File
@@ -254,7 +254,7 @@ func level_gen_params_row(level_index: int) -> Dictionary:
return level_gen_params.row_by_level_index(level_index) if level_gen_params != null else {}
# 序列总关数(AdventureRunner levels_total / MetaProgression 通关累加域的取数口径)
# 序列总关数(AdventureRunner levels_total / MetaProgression 关卡完成累加域的取数口径)
func level_count() -> int:
return level.rows.size() if level != null else 0
+1 -1
View File
@@ -40,7 +40,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
if not treasure_live.has(treasure_eff):
push_diag.call("error", "reward_dangling_treasure", "RewardTable %s aux 行引用无效道具 %s(retired/deferred/不存在): %s" % [row.get("reward_id"), treasure_eff, "双表校验"])
# ①c F2 投放维度覆盖校验不启用:挖格不掉道具,秘宝
# 唯一来路=三选一共享池〔节点/通关〕;共享池为「抽到道具位再动态掷定具体道具」,维度覆盖不可
# 唯一来路=三选一共享池〔节点/关卡完成〕;共享池为「抽到道具位再动态掷定具体道具」,维度覆盖不可
# 静态校验。
# ② SettlementTable.effect_pool_map 悬空
for eff in st_tab.effect_pool_map.keys():
+3 -3
View File
@@ -561,7 +561,7 @@ func _on_battle_finished(payload: Dictionary) -> void:
hero_hp = maxi(1, int(payload.get("hero_hp", 0))) # despawn 前捕获值(载荷自包含)
_gain_wallet(int(payload.get("gp_dropped", 0))) # 战斗完成入账(clamp ≥0——破财可把 escrow 打负,钱包入账时钳制)
if String(node.get("type", "")) == "boss" or result == "defeat":
# Boss 胜利 = 关卡通关;任意战斗败北 = 关卡失败(漂移⑦清偿——每战结果上报冒险结构,
# Boss 胜利 = 关卡完成;任意战斗败北 = 关卡失败(漂移⑦清偿——每战结果上报冒险结构,
# 失败结算归 AdventureRunner;本类只发 level_finished)
level_result = "victory" if result == "victory" else "defeat"
var hp_left := hero_hp if result == "victory" else 0
@@ -742,7 +742,7 @@ func _gain_wallet(amount: int) -> void:
wallet_changed.emit("gain", gp, {"amount": amount})
# 冒险收束清零(通关/倒下/放弃——局内货币生命周期终点,不入存档不入摘要;public——冒险模式由
# 冒险收束清零(冒险成功/倒下/放弃——局内货币生命周期终点,不入存档不入摘要;public——冒险模式由
# AdventureRunner._settle 在冒险收束时调用,单关模式 = 关卡收束即冒险收束)
func close_wallet() -> void:
gp = 0
@@ -772,7 +772,7 @@ func _emit_wallet_event(event: String, gp_now: int, extra: Dictionary) -> void:
wallet_changed.emit(event, gp_now, extra)
# 购血(商店 max_hp 商品——当前生命 +Δ 并封顶新上限,与账本置满/通关回血同式)
# 购血(商店 max_hp 商品——当前生命 +Δ 并封顶新上限,与账本置满/关卡完成回血同式)
func _gain_max_hp_flat(flat: int) -> void:
hero_hp = mini(hero_hp + flat, effective_max_hp())
+1 -1
View File
@@ -271,7 +271,7 @@ func forge_shop_status() -> Array:
# (keystone_drops 副本随摘要入 recent_summaries)。
# 钥石为 Boss 逐杀掉落——掉落金额已在冒险内逐杀入账(summary.keystone_total
# 含全部掉落;首杀/复杀判定在 AdventureRunner._boss_keystone_drop,首杀标记在下方
# apply_adventure_summary 逐通关关次置位)。本函数只做标记+入账+归档。
# apply_adventure_summary 逐关卡完成关次置位)。本函数只做标记+入账+归档。
func settle_adventure(summary: Dictionary) -> Dictionary:
# 首次冒险布尔位置位(关卡节点图生成 v0.4——完成/失败/放弃三结局同经本收束挂点,写入只在收束)
save.mark_first_adventure_done()
+9 -9
View File
@@ -7,7 +7,7 @@
class_name SaveManagerCore
extends Node
const SAVE_FORMAT_VERSION := 7 # registry schema_version:v7=锻造工坊域(meta 新域 forge_routes〔六路线等级 levels+直购解锁清单 unlocked+当前武器/甲选中标记 weapon/armor;无特效域〕,旧档缺键由 _normalize 按默认补齐=初始持有态〔sword/light 在档余四锁、选中=初始路线〕,v6→v7 空迁移=缺键补齐模式);v6=生涯终身计数域(meta 两新域 adventure_total〔冒险总局数,开局即计含放弃与失败〕/adventure_cleared〔通关局数,三关全通才计 1——判定字段 summary.cleared〕,旧档缺键由 _normalize 按默认补零,收束自增唯一入账=apply_adventure_summary);v5=天赋树域(meta 三新域 tree_nodes〔node_id→已购等级〕/fork_sides〔fork_group→a|b〕/migration_note〔三体系迁移补偿注记〕);v4=同调位域(meta 三新域 boss_kill_count/attunement_equipped/attune_refresh_level——旧档缺键 = 合法空态);v3=断点附随件扩列+摘要 keystone_drops;v2=断点域出生;v1=首版;高版本拒读 owner=存档序列化
const SAVE_FORMAT_VERSION := 7 # registry schema_version:v7=锻造工坊域(meta 新域 forge_routes〔六路线等级 levels+直购解锁清单 unlocked+当前武器/甲选中标记 weapon/armor;无特效域〕,旧档缺键由 _normalize 按默认补齐=初始持有态〔sword/light 在档余四锁、选中=初始路线〕,v6→v7 空迁移=缺键补齐模式);v6=生涯终身计数域(meta 两新域 adventure_total〔冒险总局数,开局即计含放弃与失败〕/adventure_cleared〔冒险成功局数,三关全通才计 1——判定字段 summary.cleared〕,旧档缺键由 _normalize 按默认补零,收束自增唯一入账=apply_adventure_summary);v5=天赋树域(meta 三新域 tree_nodes〔node_id→已购等级〕/fork_sides〔fork_group→a|b〕/migration_note〔三体系迁移补偿注记〕);v4=同调位域(meta 三新域 boss_kill_count/attunement_equipped/attune_refresh_level——旧档缺键 = 合法空态);v3=断点附随件扩列+摘要 keystone_drops;v2=断点域出生;v1=首版;高版本拒读 owner=存档序列化
const SUMMARY_CAP := 10 # 最近冒险摘要保留条数(最新在前)
const TREE_STRUCT_VERSION := 2 # 树结构版本标记(12 节点 v0.8 结构;加载检测缺失
@@ -154,7 +154,7 @@ func delete_slot(slot: int) -> bool:
return true
# 摘要三项按槽窥视读器(sv7/sv8,契约 5/9):钥石余额/通关局数/冒险总局数。
# 摘要三项按槽窥视读器(sv7/sv8,契约 5/9):钥石余额/冒险成功局数/冒险总局数。
# 只读零副作用:不激活、不写盘、不触发 _corrupt_fallback 改名(.corrupt.bak 只在正式进档路径产生)。
# 返回 {"readable", "has_file", "keystone", "cleared", "total"}——readable=true=可读槽(空槽=零值合法态,
# .tmp 残留按空槽——主文件不在即空);readable=false=不可读槽(损坏/高版本/缺版本字段/打开失败,
@@ -285,7 +285,7 @@ func _default_data() -> Dictionary[String, Variant]:
"recent_summaries": [],
"first_adventure_done": false,
"adventure_total": 0, # 生涯终身计数·冒险总局数(开局即计含放弃与失败,收束唯一入账)
"adventure_cleared": 0, # 生涯终身计数·通关局数(三关全通才计 1——判定字段 summary.cleared)
"adventure_cleared": 0, # 生涯终身计数·冒险成功局数(三关全通才计 1——判定字段 summary.cleared)
"vision_used": 0, # 生涯终身计数·真视类工具累计使用次数(信标网络解锁行为条件;
# 受理成功唯一计数点=battle 层经冒险层聚合、收束入账;缺键补零不升版本)
"forge_routes": { # 锻造工坊域(六路线等级+直购解锁清单+当前选中标记;无特效域)
@@ -417,7 +417,7 @@ func boss_first_kill_marked(level_index: int) -> bool:
# ---- 同调位账本域(里程碑谓词表计数源 + 装配关系 + Y 升级态)----
# Boss 击杀总数(含重复——底稿裁决 4 计数源;收束入账 = apply_adventure_summary 逐通关关次 +1,
# Boss 击杀总数(含重复——底稿裁决 4 计数源;收束入账 = apply_adventure_summary 逐关卡完成关次 +1,
# Boss 恒唯一终点故 levels_cleared 即本次冒险 Boss 击杀数)
func boss_kill_count() -> int:
return int(_data["meta"].get("boss_kill_count", 0))
@@ -431,8 +431,8 @@ func adventure_total() -> int:
return int(_data["meta"].get("adventure_total", 0))
# 通关局数(口径=三关全通才计 1——判定字段 summary.cleared,adventure_runner「completed」
# 唯一出口=末关通关)
# 冒险成功局数(口径=三关全通才计 1——判定字段 summary.cleared,adventure_runner「completed」
# 唯一出口=末关的关卡完成)
func adventure_cleared() -> int:
return int(_data["meta"].get("adventure_cleared", 0))
@@ -558,7 +558,7 @@ func set_attune_refresh_level(n: int) -> void:
_data["meta"]["attune_refresh_level"] = maxi(0, n)
# 同调位计数源组装(Attunement 服务取数口——clear_count = 逐关通关次数合计〔含重复关,底稿 §2.1 口径〕;
# 同调位计数源组装(Attunement 服务取数口——clear_count = 逐关完成次数合计〔含重复关,底稿 §2.1 口径〕;
# clear_count(level_index) 逐关访问器零改动复用)
func recent_summaries() -> Array:
return _data["meta"]["recent_summaries"].duplicate(true) # 值拷贝出户(本类契约「禁碰内部结构」——消费方直改不得渗入存档内存态)
@@ -566,10 +566,10 @@ func recent_summaries() -> Array:
# 域更新原语(正式冒险收束唯一入账口——组合根经本函数入账,禁直改内部结构):
# 钥石入账(summary.keystone_total——逐杀掉落已在冒险内计入,boss_drops 参数为
# 摘要归档副本不重复入账)+ 逐通关关次 clear_count +1 与 Boss 首杀标记 + Boss 击杀总数累计
# 摘要归档副本不重复入账)+ 逐关卡完成关次 clear_count +1 与 Boss 首杀标记 + Boss 击杀总数累计
# (同调位里程碑计数源)+ 摘要头部插入(cap 10,最新在前,元素附
# keystone_drops 副本——复盘可归因)+ 生涯终身计数自增(总局数开局即计:
# 三结局同经本口恰计一次;通关局数按 summary.cleared 判定三关全通才计 1。终身计数写入
# 三结局同经本口恰计一次;冒险成功局数按 summary.cleared 判定三关全通才计 1。终身计数写入
# 单口=本函数,禁第二写入点)。
func apply_adventure_summary(summary: Dictionary, boss_drops: Array = []) -> void:
_data["meta"]["adventure_total"] = adventure_total() + 1
+2 -2
View File
@@ -1,7 +1,7 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 冒险大奖励表(奇物三选一令牌池)schema:档位配额/件值/已拿上限的类型化读取(单行全局;垂直切片验证:三关连探一条命走完)。
## kind/tier 词表封闭——断点附随件与牌库剔除态依赖,扩件走本表+registry 修订,禁调用侧私扩。
## 档位配额总量是结构锚(validate 硬断言):改投放总量=重裁非调参;全部 knob provisional,上调须重扫通关率带。
## 档位配额总量是结构锚(validate 硬断言):改投放总量=重裁非调参;全部 knob provisional,上调须重扫关卡完成率带。
class_name AdventureTable
extends Resource
@@ -36,7 +36,7 @@ const TIER_LEGENDARY := "legendary"
## validate() 硬断言;此后改总量=重裁非调参)。
const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
# —— 通关部分回血(固定型,非末关通关兑付)——
# —— 关卡完成部分回血(固定型,非末关兑付)——
@export var clear_heal_pct: int = 20 # [0,100] knob——回复生效上限的百分比,封顶上限不回满
# —— 三选一候选数——
+2 -2
View File
@@ -9,8 +9,8 @@ extends Resource
@export var rows: Array[Variant] = [] # Array[Dictionary] 键 item_id
# 计数键封闭枚举(就地内联于本表,无独立谓词表;
# 通关口径=C6(通关=打通整局冒险),键名 adventure_cleared
# (生涯通关局数,save_manager 生涯计数域在役);扩 vision_used(真视类工具
# 解锁口径=C6(冒险成功=打通整局冒险),键名 adventure_cleared
# (生涯冒险成功局数,save_manager 生涯计数域在役);扩 vision_used(真视类工具
# 累计使用——信标网络行为条件计数;use_treasure 受理成功处累计、冒险收束导出入档);
# 键域以本枚举为准)
const UNLOCK_COUNTER_KEYS := ["adventure_cleared", "boss_kill_count", "vision_used"]
+1 -1
View File
@@ -48,6 +48,6 @@ feat_ticket_pity_step = 5
; 洗牌改令牌 Fisher-Yates(不放回抽 3,消费 reward_pick 封闭流)。
; 迭代 11 第②批 商店共享池注记:货架 = 令牌级抽签(同 kind 去重每 kind 至多一卡、价格公式 tier-blind
; ceil(基价×b^(关序−1)×A^visit) 维持批②裁定、购买消费在架令牌本体)。
; 末关通关不发三选一与回血(奖励只朝下一战生效)。
; 末关的关卡完成不发三选一与回血(奖励只朝下一战生效)。
; 全部 provisional-pending-曲线调参批(值锚与上调约束归 难度曲线与Boss;档位配额结构锚 70/25/7/0——
; schema TIER_QUOTAS,validate 硬断言,改总量须重裁)。
+2 -2
View File
@@ -765,7 +765,7 @@ rows = [{
; 敌方攻击区间批(2026-09-14 用户拍板②第二步——"上限掺运气"敌方侧:atk 上阶/下阶,中点恒等行值;
; roll 时机 = 遭遇骰同流〔encounter〕spawn 期整战持久,每拍抖动否决〔数字可读性锚〕;B2 零随机纪律保持
; ——收 override 即确定值。宽度 ±3 provisional 归 C3 测量迭代;验收 = 完美策略档 0 加成通关 ≤40%。
; ——收 override 即确定值。宽度 ±3 provisional 归 C3 测量迭代;验收 = 完美策略档 0 加成关卡完成 ≤40%。
; 护甲等级掷点批(2026-09-16 用户拍板):armor_class 字段入列(缺省 0 向后兼容)——防守方掷点判定阈值基数,
; 与 armor_type(克制属性轴)两概念严格分离、不做映射。首关六行暂定 0(正式行值归怪物/敌人系统设定批——
@@ -868,7 +868,7 @@ rows = [{
; d6 巢群/d7 小队/d9 石君王】。清单:L2 d0 45→50 · d1 18→22 · d4 20→24 · d5 68→88+atk3 · d6 18→22 ·
; d7 18→22 · d9 80→112+atk3;L3 d0 50→62+atk3 · d1 25→30+atk3 · d4 20→26+atk3 · d5 76→98+atk3 ·
; d6 20→26+atk3 · d7 20→26+atk3 · d9 92→140+atk3。
; 目标对齿(熟练玩家):S0 零成长 L2 达 boss ~20%/通 ~0%、L3 ~0;S1(≈5 次 L1 通关锻造后,hero 快照
; 目标对齿(熟练玩家):S0 零成长 L2 达 boss ~20%/通 ~0%、L3 ~0;S1(≈5 次 L1 关卡完成锻造后,hero 快照
; 攻4/血26/甲11)L2 达 ~70%/通 ~50%;S2(攻6/血32/甲12)L3 通 ~55%。验证 = hero.tres 阶段快照 MC
; (设计文档 §6,快照后恢复基线)。验证出带微调旋钮序:L2 d1/d6/d7 血 22→20 → d4 24→22 → d9 112→108。
; provisional 未提交。
+1 -1
View File
@@ -52,7 +52,7 @@ attune_refresh_upgrade = [{
;
; 数值设计批·关卡成长×永久成长对齿(2026-09-22——设计权威 qa-data/数值设计_关卡成长×永久成长_v1.md,
; game-balancer 工作流,用户三拍板 1A/2A/3A):forge base_price 10→20(三行同,ratio 150% 不变,
; 效果档 血+3/攻+1/甲+1 不变)——S1 一档(攻+2/血+6/甲+1 ≈ 120 GP)对齐「≈5 次 L1 通关开门」节奏
; 效果档 血+3/攻+1/甲+1 不变)——S1 一档(攻+2/血+6/甲+1 ≈ 120 GP)对齐「≈5 次 L1 关卡完成开门」节奏
; (实测 L1 通关局收入 ≈29-35 GP)。换算率表维持全 0(墙/钥匙闭环不依赖该轴,留扩展)。
; 配套怪表改动见 enemy.tres 同日批注。provisional 未提交。
;
+1 -1
View File
@@ -8,7 +8,7 @@ script = ExtResource("1")
; 旧 12 行删除——venom_edge/seal_burst 已随迭代 20 专长批迁出删除,本批实删 12 行)。三档=普通 7/优秀 3/稀有 2
; (传说留空=空集合法,迭代 20 追认③先例)。解锁混合制(契约 8 逐件分配锁定表,2026-10-11 用户裁 C1):
; 钥石购买件 unlock_keystone_cost(provisional 占位);条件件 unlock_requires 谓词行——计数键
; adventure_cleared=生涯通关局数(C6 裁:通关=打通整局冒险)/boss_kill_count=生涯 Boss 击杀/vision_used=
; adventure_cleared=生涯冒险成功局数(C6 裁:打通整局冒险)/boss_kill_count=生涯 Boss 击杀/vision_used=
; 真视类工具累计使用(本批新增计数域)。全部数值 provisional 归调参批(契约 7)。
; 常时参数面件(无订阅行——效果=开局/装板参数注入,非事件订阅):阔视野带/储备罐/远程镐/信标网络/刷新练习/
; 刷新执照六件 effect_kinds 挂参数 kind,由注入面按装配集 kind 分发读取(_attune_refresh_x_total 先例)。
+7 -7
View File
@@ -47,7 +47,7 @@ static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> Stri
# 调试战斗终局文案(level_end = 关内两层模式的关卡末)
static func battle_end_text(result: String, level_end: bool) -> String:
if level_end:
return "关卡通关!"
return "关卡完成!"
if result == "victory":
return "战斗胜利!敌人全灭。"
return "战斗失败……探险者倒下了。"
@@ -87,9 +87,9 @@ static func curio_pick_header(payload: Dictionary) -> String:
var lines: Array[String] = ["第 %d 层通过!" % int(payload.get("level_index", 0))]
var heal := int(payload.get("heal_gained", 0))
if heal > 0:
lines.append("通关回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
lines.append("关卡完成回血 +%d(生命 %d → %d)" % [heal, int(payload.get("hp_before", 0)), int(payload.get("hp_after", 0))])
else:
lines.append("生命已满——通关回血 0(合法态)")
lines.append("生命已满——关卡完成回血 0(合法态)")
lines.append("选择一件提升带走(另两件丢弃不可反悔):")
return "\n".join(lines)
@@ -151,19 +151,19 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
# 奖励节点三选一头部行(与通关三选一共享同库;渠道白名单分流——node-reward.md v2)
# 奖励节点三选一头部行(与关卡完成三选一共享同库;渠道白名单分流——node-reward.md v2)
static func node_curio_header() -> String:
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:"
return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与关卡完成专长三选一同库分流):\n选择一件带走:"
# 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子)
# 冒险结算屏文案(摘要五件事:打穿没有 / 完成几关 / 止步第几关 / 钥石合计 / 主种子)
static func adventure_summary_text(summary: Dictionary) -> String:
var verdict := "冒险失败 · 未打穿"
if bool(summary.get("cleared", false)):
verdict = "冒险完成 · 已打穿"
elif String(summary.get("result", "")) == "abandoned":
verdict = "冒险放弃 · 未打穿"
return "%s\n通关 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
return "%s\n完成 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d" % [
verdict, int(summary.get("levels_cleared", 0)), int(summary.get("stopped_at", 0)),
int(summary.get("keystone_total", 0)), int(summary.get("master_seed", 0))]
@@ -25,7 +25,7 @@ var _fatal_screen: Callable
var _gp_summary: Callable # () → 战斗总结 GP 行文案(组合根 lambda——_last_gain_amount 取数面在根)
var _pending_curio := {} # 三选一停点载荷暂存(战斗总结结算确认后开三选一面;续玩停点复用)
var _curio_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
var _curio_context := "level_clear" # 三选一面上下文(level_clear=关卡完成停点→推进下一关 / reward_node=奖励节点→回图选路)
func _init(ctx: CurioFlowContext) -> void:
@@ -82,7 +82,7 @@ func _skip_empty_stop() -> bool:
return true
# 三选一停点(非末关通关恰一次):Boss 胜利先经战斗总结结算面(settlement-summary v1.1——钥石
# 三选一停点(非末关的关卡完成恰一次):Boss 胜利先经战斗总结结算面(settlement-summary v1.1——钥石
# 增量行在其上),确认后进三选一面(level-clear.md v2);续玩停点恢复同入口(零自动推进契约)
func on_presented(payload: Dictionary) -> void:
if String(payload.get("context", "")) == "feat_ticket":
@@ -111,7 +111,7 @@ func on_presented(payload: Dictionary) -> void:
_refresh_presentations.call()
# 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 无替换子交互(同调装配不可变,见类头)。
func present_node_curio() -> void:
var adventure = _adventure_get.call()
@@ -177,7 +177,7 @@ func _curio_card_texts(candidates: Array) -> Array:
# 新件写行即有名有姓)
# 三选一刷新(X/Y 双源 + 对齐商店 MC-1——免费位用尽转 GP 付费档:
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;通关停点经重发 curio_choice_presented 直落
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;关卡完成停点经重发 curio_choice_presented 直落
# 选择面,节点停点在此读在途态重 present——零自动推进契约不破,停点仍在途)
func on_refresh() -> void:
var adventure = _adventure_get.call()
@@ -228,7 +228,7 @@ func on_resolved(result: Dictionary) -> void:
_refresh_presentations.call()
# 继续钮:收三选一面 → 上下文分派(通关停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进)
# 继续钮:收三选一面 → 上下文分派(关卡完成停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进)
func on_continue() -> void:
_close_modal.call(curio_overlay)
curio_overlay.close_pick()
+2 -2
View File
@@ -188,10 +188,10 @@ static func outcome_text(outcome: Dictionary) -> String:
static func tune_sim_report(n: int, atk_pct: int, hp_pct: int, wins: int, dmg_all: Array, hp_left_win: Array, hp_boss: Array, rounds_all: Array, death_nodes: Dictionary, base_hp: int) -> String:
var lines: Array[String] = []
lines.append("[b]模拟 N=%d(atk+%d%% / hp+%d%%)[/b]" % [n, atk_pct, hp_pct])
lines.append("通关率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("关卡完成率 [color=#%s]%.0f%%[/color](%d/%d)" % [UiKit.HEX_LOG_WIN, 100.0 * float(wins) / float(maxi(1, n)), wins, n])
lines.append("整关承伤:均 %.1f · p90 %.0f(基础 HP %d)" % [_mean(dmg_all), _p90(dmg_all), base_hp])
if not hp_left_win.is_empty():
lines.append("通关剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("关卡完成剩余 HP 均 %.1f · 进 Boss HP 均 %.1f" % [_mean(hp_left_win), _mean(hp_boss)])
lines.append("平均轮数 %.1f" % _mean(rounds_all))
if death_nodes.is_empty():
lines.append("败北节点:—")
+1 -1
View File
@@ -51,7 +51,7 @@ static func _on_runner_node(root: Variant, node: Dictionary, info: Dictionary) -
root._show_board_fx_for_node(t)
root.log_view.append_bb(root.Narrator.node_banner(root.run_scope.runner, node, t))
elif t == "reward":
# 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 呈现编排归 curio_interact_controller.present_node_curio。
root._bonus.present_node_curio()
root.log_view.append_bb(root.Narrator.reward_line())
+1 -1
View File
@@ -35,7 +35,7 @@ func is_running() -> bool:
func run(n: int) -> void:
_running = true
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL;
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/关卡完成全量 JSONL;
# 一文件多局,run 行携带种子切分局次)
var logger = PlaytestLogger.new()
logger.ap_per_round = int(db.action_economy.ap_per_round)
+1 -1
View File
@@ -542,7 +542,7 @@ func _rebuild_recent(data: Dictionary) -> void:
verdict = "完成"
elif String(s.get("result", "")) == "abandoned":
verdict = "放弃"
lines.append("最近冒险:%s · 通关 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
lines.append("最近冒险:%s · 完成 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))])
_recent_list.text = " | ".join(lines)
+5 -5
View File
@@ -8,8 +8,8 @@ extends Control
signal closed
var _total_label: Label # 冒险总局数(开局即计含放弃与失败)
var _cleared_label: Label # 通关局数(三关全通才计 1)
var _clears_label: Label # 各关通关次数(一行三关——display_name ×次数)
var _cleared_label: Label # 冒险成功局数(三关全通才计 1)
var _clears_label: Label # 各关完成次数(一行三关——display_name ×次数)
var _boss_first_label: Label # Boss 首杀(已首杀关数/总关数)
var _boss_kills_label: Label # Boss 总击杀(含重复)
var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件——状态面板同形制)
@@ -33,14 +33,14 @@ func _init() -> void:
UiKit.FS_HINT, false, UiKit.WARM_SOFT)
box.add_child(hint)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、通关局数三关全通才计)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、冒险成功局数三关全通才计)
# 对比度口径:页底=暖亮档深棕(BG_BRIGHT),文字全用亮墨族 token(WARM 9.0:1 / WARM_SOFT 8.0:1 /
# CANDLE 7.2:1 / INK_L_GOLD 9.1:1 实测对 BG_BRIGHT 全过 §7.2 ≥4.5:1——深墨族〔INK_LABEL〕
# 压深棕底仅 ~1.3:1 属违例,禁用)
_section_head(box, "冒险战绩")
_total_label = _row(box, "冒险总局数")
_cleared_label = _row(box, "通关局数")
_clears_label = _row(box, "各关通关次数")
_cleared_label = _row(box, "冒险成功局数")
_clears_label = _row(box, "各关完成次数")
_boss_first_label = _row(box, "Boss 首杀")
_boss_kills_label = _row(box, "Boss 总击杀")
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 提升三选一覆盖层(模态):通关停点的三卡点选→确认→兑付反馈两态呈现。
## 提升三选一覆盖层(模态):关卡完成停点的三卡点选→确认→兑付反馈两态呈现。
## 文案拼装归 Narrator 经组合根喂入——本类零文案装配。
class_name CurioPickOverlay
extends Control
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/通关局数/冒险总局数)。
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/冒险成功局数/冒险总局数)。
## 结构树层级:选档 → 进档 → 营地(本面零存档内功能入口——禁营地/冒险/生涯跳转与购买/查看入口);
## 删档必经二次确认(确认弹窗与分派归组合根消费方编排——本面只发意图信号)。
## 槽数与槽态(空/有档)唯一来自组合根喂入数据(open_save_select 每开重建——组合根经卡① 窥视 API
@@ -15,7 +15,7 @@ signal load_slot_requested(slot: int)
signal delete_slot_requested(slot: int)
var _rows_box: VBoxContainer
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(通关/冒险两值——探针取值缝)
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(冒险成功/冒险两值——探针取值缝)
var _occupied_flags: Dictionary[int, bool] = {} # 槽号→有档态(操作集二分断言缝;判定源=喂入数据)
var _new_btns: Dictionary[int, Button] = {} # 槽号→新档钮(空/坏槽——操作集断言与点击链驱动缝)
var _load_btns: Dictionary[int, Button] = {} # 槽号→进档钮(有档槽)
@@ -84,7 +84,7 @@ func _build_slot_row(sd: Dictionary) -> void:
sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if occupied:
row.add_child(UiKit.keystone_badge("钥石 %d" % int(sd.get("keystone", 0))))
var summary_l := UiKit.label("通关 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
var summary_l := UiKit.label("冒险成功 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
UiKit.FS_BODY, false, UiKit.CANDLE)
_summary_labels[slot] = summary_l # 摘要读数缝(禁词断言枚举 Label 全集时含此件)
row.add_child(summary_l)
+2 -2
View File
@@ -208,7 +208,7 @@ const HEX_LOG_DMG_BODY := "e0a050" # 伤害行正文橙
const HEX_LOG_VOIDED := "b08060" # 未生效行暗橙(⚠ 划线)
const HEX_LOG_CAST := "b09a72" # 施放行暖石(✦ 具名技能)
const HEX_LOG_DECAY := "8a9ab0" # 状态衰减蓝灰
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告通关率同档)
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告关卡完成率同档)
const HEX_ROUTE_NOTE := "d0c090" # 奖励点预告暗金(narrator 行)
const HEX_SEED_NOTE := "c8a860" # 种子/开场行暗金(narrator 行)
@@ -400,7 +400,7 @@ const FS_H1 := 34 # 主标题/演算暴击
# ── 秘宝解锁条件计数键 → 呈现标签(秘宝匣柜台条件进度文案单点;键域=
# TreasureItemTable.UNLOCK_COUNTER_KEYS 封闭枚举,键出域走「条件」兜底不炸)──
const TREASURE_REQ_LABELS := {"adventure_cleared": "通关", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
const TREASURE_REQ_LABELS := {"adventure_cleared": "冒险成功", "boss_kill_count": "Boss击杀", "vision_used": "真视使用"}
# 秘宝档位 → 呈现名(契约 2 三档;legendary 传说空集合法不在柜)
const TREASURE_TIER_LABELS := {"common": "普通", "excellent": "优秀", "rare": "稀有", "legendary": "传说"}
+7 -7
View File
@@ -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 皆空——选路停点形)
+4 -4
View File
@@ -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:
+5 -2
View File
@@ -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:
+1 -1
View File
@@ -6,7 +6,7 @@
# 刷新/行进序列/逐战上限序列——后续统计分析的取数面);
# ② 全量操作日志 = playtest_logger 共享会话 JSONL(与真人试玩同合规口径,每策略一批一文件,
# run 行携带种子/策略切分局次,落 user://logs/)。
# 本工具只采集与落盘数据,不做统计判读(通关率/败因分布归分析会话)。
# 本工具只采集与落盘数据,不做统计判读(关卡完成率/败因分布归分析会话)。
# 运行:Godot_console.exe --headless --path . --script res://sim/adventure_monte_carlo.gd -- [N] [seed0] [normal|perfect|both] [route min|max]
extends SceneTree
+1 -1
View File
@@ -33,7 +33,7 @@ static func choose_dig(minesweeper) -> Dictionary:
# 进阶级 = 联动定式(1-1 / 1-2 / 1-2-1 / 1-2-2-1 / 子集缩减)与概率分析——本切片归完美档 subset 推理承载,
# 不入普通画像(新手不可靠掌握)。本档语义 = 只做新手级推理 + 无处可推时按既有口径盲挖边界格(新手冲动)+
# 不主动刷新(新手无刷新策略意识)。测量定位:教会基础规则后的真实玩家下限近似
# (乱点/最近格口径测的是无认知玩家下限,对通关率带判读失真,不作普通画像)。
# (乱点/最近格口径测的是无认知玩家下限,对关卡完成率带判读失真,不作普通画像)。
# 普通画像单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/seal/stop 四值(无 refresh)。
static func novice_action(minesweeper) -> Dictionary:
+5 -5
View File
@@ -1,16 +1,16 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 日志合规:任何模拟运行与真人试玩同日志——操作记录 / 轮次状态 / 战斗结束 / 通关结束 全量 JSONL。
# 日志合规:任何模拟运行与真人试玩同日志——操作记录 / 轮次状态 / 战斗结束 / 关卡完成 全量 JSONL。
# logger=null 时自建会话(每局一文件),批量调用方传共享 logger(log_run 局次标记切分)。
# 关卡自动通关策略(理性玩家近似)——呈现层数值调整板的批量模拟与 headless 扫描
# 关卡自动完成策略(理性玩家近似)——呈现层数值调整板的批量模拟与 headless 扫描
# 共用同一实现,保证"面板模拟读数"与"扫描表读数"同口径。策略与 smoke_all 战斗自动玩家同族:
# 挖最近可挖格 / 无可挖停手 / 奖励恢复点立即确认。
# run_level = 机制夹具/单战锚点专用(smoke_all 机制断言、rhythm/defense/damage 单战扫描)——
# 通关率/难度类测量禁用本入口(满血零上下文进关,与真实冒险条件通关率脱节),一律走 run_adventure。
# 关卡完成率/难度类测量禁用本入口(满血零上下文进关,与真实冒险条件关卡完成率脱节),一律走 run_adventure。
# 策略档 policy:"normal"(默认)/ "perfect"(零盲挖/全封印/卡死刷新——上界测量仪,求解器与
# 动作语义见 auto_player.gd 完美策略档节)。
# 选路驱动(零自动推进):兑付确认/战斗胜利后 runner 进选路态,驱动循环按 route 策略显式
# pick_next_node;route = "min"/"max"(候选节点句柄最小/最大——确定性路径轴,零随机流自开;默认 "min")。
# run_adventure 三关连探驱动(AdventureRunner——主种子恰一次、通关兑付停点确认、adventure_finished
# run_adventure 三关连探驱动(AdventureRunner——主种子恰一次、关卡完成兑付停点确认、adventure_finished
# 置 result/summary;外层每关重取 runner 实例经 _drive_level 复用单关驱动)。
# 大奖励停点 = 三选一驱动(候选取首件/替换放弃新件)。
# 普通档 v3 = 完美策略 + 受控失误注入(失误流走独立策略 RNG——种子派生自局种子,逐局确定性/
@@ -303,7 +303,7 @@ static func _pick_token(candidates: Array, feat_mode: String = "off") -> Diction
return best
# 三选一兑付完成 → 上下文分派(通关停点=推进下一关 / 奖励节点=回图选路)
# 三选一兑付完成 → 上下文分派(关卡完成停点=推进下一关 / 奖励节点=回图选路)
static func _on_adventure_curio_resolved(result: Dictionary, st: Dictionary, adr) -> void:
st.reward_events.append({"curio_resolved": result.duplicate(true)})
if String(result.get("context", "")) == "reward_node":
+4 -4
View File
@@ -2,16 +2,16 @@
# 判定端 tools/balance_verifier.py 的数据源,本文件零判定逻辑
# 两段一次采集,stdout 以「VJSON 」前缀行出机器读 JSON(其余行为人读进度/摘要):
# adventure 段 全冒险连探基线(run_adventure 零永久强化)× 双画像 × na——止步分布、败退节点
# (摘要 defeat 明细 + 末场战斗敌方名)与逐关条件通关率派生源;inject 档行带
# (摘要 defeat 明细 + 末场战斗敌方名)与逐关条件关卡完成率派生源;inject 档行带
# inject_tag/override(flat 三键)
# campaign 段 营地循环战役 × 双画像 × k 局——SaveManager 隔离档 + MetaProgression 真实账本:
# 逐局 hero_override() 注入 → 冒险 → settle_adventure 入账 → 花费策略买锻造,
# 回答「累计几次通关冒险后下一关可过」(元进度换算率标定的模拟读数源)
# 回答「累计几次冒险成功后下一关可过」(元进度换算率标定的模拟读数源)
# 运行:Godot_console.exe --headless --path . --script res://sim/verifier_batch.gd -- [k=v ...]
# na=40(基线冒险局数) k=40(战役局数) seed0=8000
# only=adventure,campaign(缺省全段) spend=hp_first|atk_first|armor_first(锻造花费优先序)
# inject=max_hp:3,atk:1(冒险段 flat 注入档——三键 {max_hp, atk, armor_class},缺省全零白板;
# 换算率标定用:每档与白板同种子流配对,逐关条件通关率差 = 该档的带平移量实测)
# 换算率标定用:每档与白板同种子流配对,逐关条件关卡完成率差 = 该档的带平移量实测)
# 种子纪律:unique 逐局独立(掷点测量修复批 A 案——seed0 + 全局游标,每掷点恰消费一次)。
# 同 seed0 跨档复用 = 配对读数(同序种子流——分析侧按种子配对消方差)。
# 日志合规:全段共享 playtest_logger 会话(与真人试玩同日志),分析工具可直接消费。
@@ -108,7 +108,7 @@ func _emit(row: Dictionary) -> void:
print("VJSON " + JSON.stringify(row))
# ---- adventure 段:全冒险连探基线(零永久强化——止步分布/败退节点归因/逐关条件通关率派生源);
# ---- adventure 段:全冒险连探基线(零永久强化——止步分布/败退节点归因/逐关条件关卡完成率派生源);
# inject 档 = 同种子流配对的 OFAT 边际读数(行带 inject_tag/override flat 三键) ----
func _seg_adventure(db, autoplay, logger, opt: Dictionary, counts: Dictionary) -> void:
+1 -1
View File
@@ -150,7 +150,7 @@ timeout 120s godot --headless --script tests/run_all.gd
`tests/static_scan.sh` 头注**——迭代 32 两卡是代码实现批,按流水线红线禁写 `design/`,故这两段未进架构文档。
新违规案例必须**先补扫描规则再修码**。
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除,另设同形异义豁免「去重闸」(封印重判去重的机制判别器,与某闸档退役词后二字同形——行内含「去重闸」整行跳过);三层豁免(闸门自指/显式留档 4 处/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除,另设同形异义豁免「去重闸」(封印重判去重的机制判别器,与某闸档退役词后二字同形——行内含「去重闸」整行跳过);三层豁免(闸门自指/显式留档 20 处〔契约原名引述与带日期历史读数注释,清单见 `tests/static_scan.sh` 头注〕/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
S12 效果引擎无状态化旧件零残留:扫描 game/ 全域 `.gd` 与 `game/data/tables/*.tres`,禁已删符号(`counters_export`/`counters_seed`/`_reset_tree_limited_counters` 等)与带引号的旧计数器 id 字面(引号锚定,不误伤 `adv_`/`round_` 前缀新名);扫描面文件数为零即判红。
@@ -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())
+18 -1
View File
@@ -127,9 +127,13 @@ fi
# 在册断言标签成片误红;代价=该行同时出现的真闸档用词也不报,靠文档域 08 闸兜住)
# 扫描面:game/ 全域 .gd/.tres/.md/.json/.sh(.txt 不收=第三方许可证非本仓库文本)
# 注释文本提取:.gd/.sh 取 # 后文本;.tres 取 ; 后文本;.md 取整行;.json 无注释语法(保留扩展名但不产生注释段)
# 三层豁免依序:①闸门自指(行内含检查/禁令形态即跳过)②显式留档豁免单 7 处 ③非注释面天然不取
# 三层豁免依序:①闸门自指(行内含检查/禁令形态即跳过)②显式留档豁免单 20 处 ③非注释面天然不取
# 豁免单新增三处(2026-10-26 令牌配额口径清扫批 B4b 用户裁定保留):adventure_table.gd:26 档名映射对照注
# +adventure_table.gd:93 与 curio_deck.gd:130 四口径契约④原名引述——合法载体非残留,改写即断链
# 豁免单再增 13 处(2026-10-26 口径换代批用户裁「带日期历史读数注释保留原文」):
# hero.tres:21/:36、enemy.tres:751/:891、action_economy.tres:15/:16/:26/:27、status_effects.tres:91、
# meta.tres:48/:56/:62(.tres 十二处历史读数/带日期批记录)+level_autoplay.gd:560(完美档 41% 历史实证)——
# 历史读数原文锚定当时实测口径,改写即失真;现行口径描述不豁免(已随批换代)
if [ -d game/core ]; then
S11_TABLE="design/gdd/术语表.md"
@@ -184,6 +188,7 @@ else
if(FILENAME~"adventure_table"&&FNR==26)next
if(FILENAME~"adventure_table"&&FNR==93)next
if(FILENAME~"curio_deck"&&FNR==130)next
if(FILENAME~"level_autoplay"&&FNR==560)next
print FILENAME":"FNR":"words[i]
next
}
@@ -201,6 +206,18 @@ else
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
if(index(cmt,"去重闸")>0)next
if(FILENAME~"enemy.tres"&&FNR==653)next
if(FILENAME~"hero.tres"&&FNR==21)next
if(FILENAME~"hero.tres"&&FNR==36)next
if(FILENAME~"enemy.tres"&&FNR==751)next
if(FILENAME~"enemy.tres"&&FNR==891)next
if(FILENAME~"action_economy.tres"&&FNR==15)next
if(FILENAME~"action_economy.tres"&&FNR==16)next
if(FILENAME~"action_economy.tres"&&FNR==26)next
if(FILENAME~"action_economy.tres"&&FNR==27)next
if(FILENAME~"status_effects.tres"&&FNR==91)next
if(FILENAME~"meta.tres"&&FNR==48)next
if(FILENAME~"meta.tres"&&FNR==56)next
if(FILENAME~"meta.tres"&&FNR==62)next
print FILENAME":"FNR":"words[i]
next
}
+15 -15
View File
@@ -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)")
+12 -12
View File
@@ -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 报告)
@@ -22,6 +22,7 @@ func _init() -> void:
_test_end_text_differs_by_result()
_test_phase_text_nonempty()
_test_boss_name_uses_boss_rowset()
_test_level_clear_wording()
quit(Kit.finish("battle_narrator_test"))
@@ -36,9 +37,9 @@ func _test_numeric_lines_carry_values() -> void:
func _test_end_text_differs_by_result() -> void:
# 关卡收束(level_end=true)恒为「关卡通关!」——单场胜负文案只在非收束态出现
# 关卡收束(level_end=true)恒为「关卡完成!」——单场胜负文案只在非收束态出现
var level: String = NarratorScript.battle_end_text("lose", true)
Kit.check("关卡收束态恒为「关卡通关!」(实际「%s」)" % level, level == "关卡通关!")
Kit.check("关卡收束态恒为「关卡完成!」(实际「%s」)" % level, level == "关卡完成!")
var win: String = NarratorScript.battle_end_text("victory", false)
var lose: String = NarratorScript.battle_end_text("lose", false)
Kit.check("单场胜负文案互异(胜「%s」/负「%s」)" % [win, lose], win != lose)
@@ -80,3 +81,21 @@ func _test_boss_name_uses_boss_rowset() -> void:
var got: String = NarratorScript.boss_name(runner, db)
Kit.check("Boss 名取自 Boss 行集首行(期望「%s」实际「%s」)〔fv24〕" % [boss_expect, got], got == boss_expect)
Kit.check("Boss 名与同深度战斗行集名不同(期望「%s」≠「%s」——行类路由可判别)〔fv24〕" % [boss_expect, battle_name], boss_expect != battle_name)
# 关卡完成停点文案族措辞钉子(口径换代批):回血行/满血合法态/渠道句/摘要关数行——
# 旧措辞退役后玩家可见面由本组断言钉在新措辞上(撤销证明:回退 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,
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,
full_hp.contains("生命已满——关卡完成回血 0(合法态)"))
var node_header: String = NarratorScript.node_curio_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,
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")
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,11 @@
# 代拍板追认单:通关口径换代批(提前实施卡批)
依据需求卡片 v1.1 §10 代拍板白名单,逐条追认(备案确认):
| # | 白名单授权 | 实际执行 | 追认 |
|---|---|---|---|
| 1 | B/C 面标签与注释措辞在映射表两可时的具体选词(限标签/注释面) | 全部按卡内映射表与逐处换法表执行;表外裁量仅两处——①ui_click_probe:402「常态通关远低于此」→「常态跑完远低于此」(帧预算语境,卡内映射表 v1.1 已补行)②「非末关通关」族防连读歧义统一「非末关的关卡完成」(grilling 应修 4 折入项) | 无越权 |
| 2 | 豁免单行号按实施后实测定 | 13 行豁免行号与卡片基线完全一致(C 面同位替换零行数变化)——零偏移 | 无偏差 |
| 3 | V1-V7 断言夹具数据具体取值 | V1(heal 5/10→15;满血 0/12→12)、V3(cleared=false/failed/1 关/止步 2/钥石 4/种子 42)、V6(recent 单条 completed/2 关/5 钥石/种子 42)、V7(探针既有 atk+100%/N=10 档)——全部确定性取值 | 无越权 |
白名单外零拍板。批内三次异常处置(worktree 环境补搬 assets、尾缀避词、S11 头注避词)均属 L1 自救(卡 §9 预案内),无 L2/L3 事件。
@@ -0,0 +1,13 @@
# GDD 反哺点清单:通关口径换代批(提前实施卡批)
实现批禁写 design/ 与根规则文件;以下条目由用户在会话中裁决后归文档批落文(执行入口 doc-design):
| # | 文档 | 关联点 | 反哺内容 | 依据 |
|---|---|---|---|---|
| ① | design/gdd/术语表.md v1.23 | 「关卡完成」「冒险成功」「冒险落幕」三行+退役词对照 | **预期零改动**——三行与退役词对照已由 termsweep2-20261026 批立入,本批实施数据(12 产品字串/111 随动/84 注释全消化)与三行口径零冲突(T0 核对+全批实施验证) | 卡片 §6;无落文义务,本条为核对注记 |
| ② | design/engineering/architecture/architecture.md「测试与质量域」节 | S11 规则行是否载豁免计数 | **预期零改动**——实查该节无 S11 行(S11/S12 规则说明自持于 game/tests/README.md 与 static_scan.sh 头注,README:149 明示);豁免单计数已随批在 README 订正 4→20 | 卡片 §6 T0 查证项 |
| ③ | 体验验证清单.md | 生涯页目检项引语(:107/:193/:198) | **已随批落地**(用户 B 表指令授权批内改)——引语随 a6/a7 换代+:107 关卡完成率口径;:258 历史读数记载行按豁免保留 | 卡片 §6 q5 |
| ④ | 待办清单.md | 「game 面玩家可见字串『通关』退役换代」条目 | **已随批删除**(活干完登记即毁——quotaface 先例) | 卡片 §6 |
| ⑤ | game/tests/README.md | S11 豁免计数「4 处」过期(quotaface 漏同步,实际 7) | **已随批订正**(4→20 并注明清单见 static_scan.sh 头注)——game/ 内 .md 属批内可写面 | 实现报告 §五.4 |
结论:①②为核对注记(零落文义务);③④⑤已随批完成。**无待裁决反哺点**——用户无需另行落文。
@@ -0,0 +1,76 @@
# 实现报告:通关口径换代批(提前实施卡批 · termsweep-game-20261026)
- 依据:reports/需求卡片-通关口径换代批-2026-10-26.md v1.1(已随批退役,git 历史可考)
- 执行形态:提前实施卡批(用户 2026-10-26 裁准,先例 quotaface-20261026);分支 termsweep-game-20261026
- 状态:**全部落地,批末全量域闸见批末回执**
## 一 · 各卡状态
| 卡 | 内容 | 状态 |
|---|---|---|
| T0 | worktree+缓存三件套(.godot/.import 边车/assets 三目录——art 113 png 不入 git 需手工搬)+前置验证(V6 open_camp 最小 data 健壮性经 grilling 读码+实跑双确认) | 完成 |
| T1 | A 面产品字串 12 条(A 表逐字)+wr1 随动+V1-V3 新增断言 | 完成 |
| T2 | B 面断言随动(12 文件 58 处+theme_projection 3 处)+三探针(wr2/wr3+V7+ui_click 7)+V4-V6+验证集清单重生成 1591 条对账零差异 | 完成 |
| T3 | C 面注释清扫 61 条+S11 豁免单 7→20 处+tests/README 计数订正 | 完成 |
| T4 | 体验验证清单 3 行引语+件核三判据 | 完成 |
| T5 | 产出物三份+删卡删条目+全量域闸 | 完成(本报告即其一) |
## 二 · 实际对照表(卡片 §8 的实现侧回填)
| 需求点 | 改动(文件:行) | 验收测试 | 层级 | 真实入口 | 撤销证明 | 结果 |
|---|---|---|---|---|---|---|
| a1 关卡完成文案 | battle_narrator.gd:50 | battle_narrator_test:41(wr1 随动) | 单元 | 直调 battle_end_text | 回退→wr1 红→复绿 | 绿 |
| a2/a3 回血两分支 | battle_narrator.gd:90/:92 | 〔口径换代 V1〕battle_narrator_test._test_level_clear_wording | 单元 | 直调 curio_pick_header | 回退→V1 双红→复绿 | 绿 |
| a4 渠道句 | battle_narrator.gd:156 | 〔口径换代 V2〕同上 | 单元 | 直调 node_curio_header | 回退→V2 红→复绿 | 绿 |
| a5 完成关数行 | battle_narrator.gd:166(+:159 注释) | 〔口径换代 V3〕同上 | 单元 | 直调 adventure_summary_text | 回退→V3 红(含负断言)→复绿 | 绿 |
| a6/a7 生涯标签 | career_overlay.gd:42/:43 | 〔口径换代 V4〕theme_projection_test(树内 label 收集) | 接口 | open_career+_all_labels | 分条回退→V4 双红→复绿 | 绿 |
| a8 槽摘要 | save_select_overlay.gd:87 | ui_struct_probe:436(wr2 随动) | 探针 | 场景实例化+喂入数据 | 回退→sl2 红→复绿 | 绿 |
| a9 解锁标签 | ui_kit.gd:403 | 〔口径换代 V5〕theme_projection_test | 单元 | UiKit 常量直读 | 回退→V5 红→复绿 | 绿 |
| a10 最近冒险行 | camp_overlay_view.gd:545 | 〔口径换代 V6〕theme_projection_test._camp_recent_projection | 接口 | open_camp 最小档直调 | 回退→V6 红→复绿 | 绿 |
| a11 完成率读数 | hud_presenter.gd:191 | ui_tune_probe:75(wr3 随动) | 探针 | 批量模拟输出 | 回退→wr3 红→复绿 | 绿 |
| a12 剩余 HP 行 | hud_presenter.gd:194 | 〔口径换代 V7〕ui_tune_probe(同报告补检) | 探针 | 批量模拟输出 | 回退→V7 红(与 wr3 双红验证独立钉住)→复绿 | 绿 |
| q2 断言随动 | 13 文件 60 处+探针 13+清单 38 | extract_checks --diff 零差异+各套件闸 | 机器对账 | 由 a1-a12 撤销证明连带覆盖 | 连带 | 绿 |
| q3 注释清扫 | C 面 61 条(豁免 13 行保留) | S11 零命中(等价验证器)+豁免撤销证明 | 静态 | 非行为改动(注释文本,零运行时语义) | hero.tres:21 去豁免→红→还原 | 绿 |
| q4 S11 豁免单 | static_scan.sh 7→20 处 | S11 零命中+头注避词自检 | 静态 | 同上 | 同上 | 绿 |
| q5 目检项引语 | 体验验证清单 :107/:193/:198 | --docs 域 doc_term_audit(批末全量档) | 文档域 | 非行为改动(引语) | — | 绿(批末闸) |
**撤销证明记录表**(a1-a12 十二条+q4 豁免一条,全部「回退 hunk→最窄断言红→复原复绿」逐条执行,执行序见 git 提交 54395b8b/51c6cf56 信息):
| # | 撤销对象 | 变红断言 | 红/绿 |
|---|---|---|---|
| a1 | battle_narrator:50 | 「关卡收束态恒为「关卡完成!」」(1/17 失败) | 红→复绿 |
| a2/a3 | :90/:92 | V1 两条(回血行/满血合法态) | 红→复绿 |
| a4 | :156 | V2 渠道句 | 红→复绿 |
| a5 | :166 | V3(输出含「通关 1 关」负断言同时抓) | 红→复绿 |
| a6 | career:42 | V4 双断言(标签缺失+旧词残留) | 红→复绿 |
| a7 | career:43 | V4 双断言 | 红→复绿 |
| a8 | save_select:87 | ui_struct sl2「got=通关 2 局」 | 红→复绿 |
| a9 | ui_kit:403 | V5 | 红→复绿 |
| a10 | camp:545 | V6(「实际…通关 2 关」实测抓旧文案) | 红→复绿 |
| a11 | hud:191 | wr3「缺关卡完成率」 | 红→复绿 |
| a12 | hud:194 | V7「缺关卡完成剩余 HP 行」 | 红→复绿 |
| q4 | S11 豁免 hero.tres:21 | 验证器精确命中 hero.tres:21 | 红→复绿 |
## 三 · 实机目检总清单(登记不阻塞——验收时间由用户自排)
1. 战斗总结结算:单关末行「关卡完成!」(非末关收束态);
2. 三选一停点头部:回血行「关卡完成回血 +N(生命 X → Y)」/满血态「生命已满——关卡完成回血 0(合法态)」;
3. 奖励节点三选一头行「与关卡完成专长三选一同库分流」;
4. 冒险结算屏:「完成 N 关 · 止步第 M 层 · 钥石合计 K · 主种子 S」;
5. 生涯页行标签「冒险成功局数〔1 局(三关全通)〕」「各关完成次数」;
6. 选档界面槽摘要「冒险成功 X 局 · 冒险 Y 局」;
7. 营地秘宝匣解锁条件行「冒险成功 0/1」形(adventure_cleared 条件件);
8. 营地底栏最近冒险「最近冒险:完成 · 完成 N 关 · …」;
9. 调试浮层批量模拟报告「关卡完成率 N%(W/T)」「关卡完成剩余 HP 均 x · 进 Boss HP 均 y」。
## 四 · 遗留读数义务
- 零数值改动=零 MC:全部改动为字串/标签/注释/断言文本,计数器键名(adventure_cleared/level_clear_counts)、存档域键、sim-data 锚与 JSONL 数据键、RNG 流名、数值行逐字未动;11 号 MC 对拍按零数值改动红线不补跑(quotaface 同款)。
- 断言条件表达式仅 wr1/wr2/wr3 三处随产品字串换代(判据随动非削弱),其余 57 处仅标签文本;零断言删除、零阈值放宽、零扫描面缩小。
## 五 · 异常与处置记录
1. worktree 环境红(theme_projection V1 主题段 7 失败)=.import 边车与 assets(art/fonts/theme,113 png 不入 git)未搬——补拷后 166 checks 全绿;坑册三件套的完整形态应含 assets 目录,已随 T2 提交信息留档。
2. 断言尾缀「通关换代」自撞 S11(新词含退役词「通关」进验证集清单 .md 整行扫描面)——尾缀改「口径换代 Vn」、S11 头注同避词;根因=卡短名含退役词,后续批次命名避开退役词。
3. S11 头注初版「通关口径换代批」自撞(static_scan.sh 属 .sh 扫描面)——改「口径换代批」。
4. tests/README.md S11 豁免计数「4 处」为 quotaface 批漏同步(实际已 7)——随批订正为 20。
@@ -0,0 +1,76 @@
# 验收审计报告:通关口径换代批(提前实施卡批 · termsweep-game-20261026)
- 审计对象:分支 termsweep-game-20261026 尖 ed49a7a5(基线 master 0b501743),全量 diff 52 文件
- 审计人:独立验收审计员(未参与实现;test-conventions §8.7 流程)
- 审计输入:需求卡片 v1.1(git show ed49a7a5~1)、实现报告、逐文件 diff、worktree 内实测复跑
- 审计日期:2026-10-26(合回前)
- **总结论:通过**(对照表 17 行全过;撤销证明抽三条复现全闭合;削弱检查机器对账零超申报;例外三件均有依据,不构成缺漏)
## 一 · 对照表逐行核对(实现报告 §二)
实测命令均于 worktree game/ 下执行(console 版 headless)。逐行结论:
| 行 | 核对内容 | 结论 | 依据(实测) |
|---|---|---|---|
| a1 | battle_narrator.gd:50=「关卡完成!」;wr1 断言在 battle_narrator_test:41;套件 17 checks 全绿;直调 battle_end_text 真实入口 | 通过 | sed :50 实读;diff hunk 行号推证 :41 为条件行;套件实跑 `[battle_narrator_test] all green (17 checks)` |
| a2/a3 | :90/:92 两分支新文案与 A 表逐字一致;V1 两断言(〔口径换代 V1〕尾缀)grep 可得(battle_narrator_test:90/:93);实跑绿 | 通过 | sed :90/:92 实读;grep 尾缀命中;17 checks 内含 V1 两条 PASS |
| a4 | :156 渠道句;V2(:96)绿 | 通过 | 同上 |
| a5 | :166 完成关数行+:159 注释同步;V3(:100)绿且带负断言 `not summary.contains("通关")` | 通过 | 同上;V3 输出实值「冒险失败 · 未打穿\n完成 1 关 · 止步第 2 层 …」PASS |
| a6/a7 | career_overlay.gd:42「冒险成功局数」/:43「各关完成次数」;V4(theme_projection_test:871/:873 含正断言+零残留负断言)经 open_career 实例化+树内 label 收集 | 通过 | sed :42/:43 实读;theme_projection_test 166 checks 全绿(V4 两断言 PASS) |
| a8 | save_select_overlay.gd:87「冒险成功 %d 局 · 冒险 %d 局」;wr2=ui_struct_probe:436 条件同结构换串;探针 PASS | 通过 | sed :87 实读;diff 确认条件结构 `sum1 == null or sum1.text != "…"` 仅换期望串;`[ui_struct_probe] PASS — 全屏结构断言零红`(exit 0) |
| a9 | ui_kit.gd:403 TREASURE_REQ_LABELS["adventure_cleared"]=="冒险成功";V5(theme_projection_test:874)直读真实常量 | 通过 | sed :403 实读;V5 PASS |
| a10 | camp_overlay_view.gd:545「完成 %d 关」;V6(theme_projection_test:818)经 open_camp 最小档实例化全量重建路径 | 通过 | sed :545 实读;V6 PASS,实测输出「最近冒险:完成 · 完成 2 关 · 钥石 +5 · 种子 42」 |
| a11 | hud_presenter.gd:191「关卡完成率」;wr3=ui_tune_probe:75 同结构换串;探针真实模拟输出含「关卡完成率 100%(10/10)」 | 通过 | sed :191 实读;探针 exit 0、报告输出实值核得 |
| a12 | hud_presenter.gd:194「关卡完成剩余 HP 均」;V7(ui_tune_probe:79)真实输出核得「关卡完成剩余 HP 均 16.2 · 进 Boss HP 均 20.4」 | 通过 | 同上 |
| q2 | 13 文件 60 处随动:标签 38 处进验证集清单(1583→1591 条=38 换代+8 新增);探针 13 处;`extract_checks --diff` 零差异 | 通过 | 清单标签列基线对比:removed 38 / added 46(38 换代+V 组 8);`python tools/extract_checks.py --diff` 输出「PASS——标签/cond/计数三对齐,零差异」exit 0;各受影响套件全绿(见 §三) |
| q3 | C 面 61 条=注释同位替换,零运行时语义 | 通过 | 机器核对:core/data/sim 全部 +/- 行去注释段后代码部分逐字节相同(含 README 外全部 .gd/.tres);README 行纯文本换代 |
| q4 | S11 豁免单 7→20;S11 直跑绿 | 通过 | static_scan.sh 现有 21 条 `next` 豁免行=20 唯一 file:line(enemy.tres:653 在两段 awk 重复计一次)=既有 7+新增 13;头注「20 处」已更;`bash game/tests/static_scan.sh` → `[static_scan] S11 ok(扫描面 237 个文件,词表 23 词)` exit 0 |
| q5 | 体验验证清单恰 3 行(:107/:193/:198),:258 历史读数行未动 | 通过 | diff 实查三 hunk 位置吻合;批末回执 08 doc_term → 0 green |
| wr判①②③ | S11 零命中/「冒险终结」零输出/清单对账退出码 0 | 通过 | 三项均实测(①见 q4 行;②grep 零命中;③见 q2 行) |
尾缀 grep 汇总:〔口径换代 V〕共 9 处=battle_narrator_test:90/:93/:96/:100(V1×2/V2/V3)+theme_projection_test:818/:871/:873/:874(V6/V4×2/V5)+ui_tune_probe:79(V7)。
真实入口核查:V1–V3 直调 BattleNarrator 静态装配器(curio_pick_header/node_curio_header/adventure_summary_text——产品字串唯一产生点,无旁路);V4/V5/V6 经 open_career/UiKit 常量/open_camp 实例化;V7/wr3 走批量模拟真实输出;wr2/sl2 走场景实例化喂入数据。diff 内未发现换下层直调或 mock 顶替。
## 二 · 撤销证明抽查复现(三条,全部临时改动已精确复原)
| # | 复现操作 | 结果 | 与实现报告记录比对 |
|---|---|---|---|
| a1 | battle_narrator.gd:50 临时改回「关卡通关!」→跑 battle_narrator_test | 红:`[FAIL] 关卡收束态恒为「关卡完成!」(实际「关卡通关!」)`→复原后 17 checks 全绿 | 逐字吻合记录表 a1 行 |
| a8 | save_select_overlay.gd:87 临时改回「通关 %d 局 · 冒险 %d 局」→跑 ui_struct_probe | 红(exit 1):`[savesel] 槽1 摘要读数失守(got=通关 2 局 · 冒险 7 局 应=冒险成功 2 局 · 冒险 7 局)〔sl2〕`→复原后 PASS | 逐字吻合记录表 a8 行「got=通关 2 局」 |
| a12 | hud_presenter.gd:194 临时改回「通关剩余 HP 均」→跑 ui_tune_probe | 红(exit 1):`模拟输出缺关卡完成剩余 HP 行〔口径换代 V7〕`;**wr3(关卡完成率)同跑仍绿**→复原后 PASS | 吻合记录表 a12 行,并实证「wr3+V7 双红独立钉住」声明 |
复原精度:三次复原后 `git diff` 零 hunk(a1/a8 首轮复原因编辑器换行符差异出现 autocrlf 幻影 M 态,已由 `git checkout --` 精确还原,见 §五.3)。
## 三 · 测试削弱检查
机器对账(验证集清单 cond 列基线 0b501743 vs 尖 ed49a7a5 多重集对比):
- cond 表达式删除=**恰 1 条**:`level == "关卡通关!"`(wr1 换代,同结构等强度换串,非削弱);
- cond 新增=9 条:wr1 新串+V1–V6 八条(清单内 1583→1591)+V7(探针面,不入清单——与卡片 B5 事实一致);
- 数值阈值/比较结构:零改动;扫描面:ui_struct_probe sl3 禁项 needle 列表 `["断点","种子","继续","停点"," 关"]` 未动(:446 仅注释改写);ui_click 7 处仅注释/print 文本。
diff 逐文件复核(13 测试文件+3 探针全部 hunk 过目):条件表达式改动仅 wr1(battle_narrator_test:41)/wr2(ui_struct_probe:436)/wr3(ui_tune_probe:75)三处,其余全部为 Kit.check 首参标签文本或注释——**与申报完全一致,零超范围**。V3/V4/V6 负断言(`not contains("通关")`)属净收紧。
结论:零断言删除、零阈值放宽、零扫描面缩小、零换下层直调;V 断言净增 9 条。
## 四 · 附加核对
1. **S11 豁免单 ↔ grep 残留一一对应**:`grep -rn "通关" game/`(四扩展名)现命中 20 行,全部落位——豁免单新增 13 行(.tres 12+level_autoplay.gd:560)各恰命中一次=14 行;其余 6 行=被检字形态(验证集清单 .md 三行负断言 cond 文本〔S11 层① 自指豁免〕+battle_narrator_test:101/theme_projection_test:819/:873 代码行负断言〔非注释面不取〕+theme_projection_test:866 注释「零「通关」残留」〔层①〕)。非豁免残留=零。既有豁免 7 行本批零误伤(enemy.tres:653 等不再含本批词面,属其他退役词)。豁免行的「去豁免即红」结构性成立:上述 14 命中行现仍含退役词,豁免即 S11 红的充分条件在位(q4 撤销证明未复跑,据此采信)。
2. **A 表 12 条逐字核对**:卡片 §1 q1 表 a1–a12 的新文案与 worktree 实码(§一行列位置)逐字一致,含 a2/a3 的全角括号与空格、a5 的 `\n` 后首词去「通关」、a8 的「冒险成功 %d 局 · 冒险 %d 局」、a9 的常量值「冒险成功」、a11/a12 的读数前缀——全部吻合。
3. **C 面零行为改动机器核验**:core 六件/schema 两件/sim 四件/七张表全部改动行,去注释段后代码部分与基线逐字节相同。
4. **批末回执**:`game/tests/.gates/receipt.json`=commit ed49a7a5、domain full、00–10 闸全 green;11 号 MC 对拍 NEED-APPROVAL skip——与「零数值改动=零 MC」红线一致。
5. **随批三件套**:实现报告/代拍板追认单/反哺点清单均在 reports/ 在位;卡片与待办条目已删(git 可考)。
## 五 · 例外与观察(均不构成缺漏)
1. **V 尾缀「口径换代 Vn」≠卡片原文「通关换代 Vn」**——例外成立,依据充分:断言尾缀进验证集清单 .md 整行扫描面,「通关换代」含退役词必自撞 S11;实现报告 §五.2 已记录根因与改法,且后续批次命名避退役词。卡片 §8 表用的旧尾缀名在清单里不可检索属预期形态,审计按「口径换代 V」检索全数命中。
2. **game/README.md:15 落「非末关的关卡完成发大奖励」**——卡片 q3 README 行写作「非末关卡完成」,实施取「非末关的关卡完成」。依据:同卡 q3 换法映射表通行规则+§13 grilling 应修④(防「末关卡」连读歧义)优先于行内简写,且属代拍板白名单 1(标签/注释面两可选词)。判例外采纳,非越权。
3. **审计会话 run_all.gd 一次异常**:统一入口跑至 49/61 套件后外层宿主进程静默退出(exit 1,无套件红)。处置:13 个受影响套件中 11 个在该次运行内 PASS,其余 2 个(battle_narrator/theme_projection)+4 个重载件(adventure_feats 138/level_shop_economy 186/meta_progression_forge 135/save_manager_domain 101)+runner 未及的 6 个尾件(hud_presenter/player_copy_sweep/minesweeper_rules/config_db_resolver/config_db_cross_validator/type_glow)全部单独实跑绿——61/61 全数覆盖;批末回执 02 闸在同 commit 全绿。判环境瞬时异常(本机资源态),非本批缺陷;留档备查。
4. **探针不进验证集清单**(B5)——V7 与 wr2/wr3 的机器对账面缺位于清单属机制常态,本次以 diff 逐 hunk+探针实跑输出双重核对补齐。
## 六 · 审计过程文件声明
审计全程只读,唯一写入=本报告;撤销证明的三次临时改动均已精确复原(收尾 `git status` 于 game/ 与全仓(除本报告外)零改动——见下方回执行)。
- 收尾 git status:仅 `?? reports/iter-supplement-通关换代-验收审计-2026-10-26.md` 一个新文件,零修改零删除。
@@ -1,325 +0,0 @@
# 需求卡片:通关口径换代批(game 面玩家可见字串 13 条+断言随动+注释清扫)
- 版本:**v1.1**(v1.0 成文→grilling 一轮复核零阻断、四应修折入:q3 计数口径注明+估时对齐+映射表补帧预算语境行+「非末关的关卡完成」防连读歧义;复核记录见 §13)
- 来源:[待办清单](../待办清单.md)「game 面玩家可见字串『通关』退役换代」条目(2026-10-26 登记,状态=文档设计已闭合+待写卡)(唯一事实源:[术语表](../design/gdd/术语表.md) v1.23「关卡完成」「冒险成功」「冒险落幕」三行+退役词对照「通关」「冒险终结」两行+用户 2026-10-26 开工指令 A/B/C 三面清单——A 表 12 条措辞已核、改动即停呈报)
- 归属:`game/presentation` 六件产品字串+13 测试文件+3 探针+`game/tests/验证集清单.md`+core 六件注释+七张表头注+schema 两件+sim 四件+`game/README.md`+呈现层注释件+`game/tests/static_scan.sh`(S11 豁免单)+根件`体验验证清单.md`(目检项引语随批同步——用户 B 表指令)
- 排期:提前实施(用户 2026-10-26 开工令「写需求卡片并实施」,先例=quotaface-20261026 提前实施卡批)
- 开工门:U0 已随开工令下达;grilling 复核门过(§13);零待裁点
- 估时:≈1.1 个工作日(T0–T5 合计 0.1+0.25+0.25+0.25+0.1+0.15)
## 0. 前置依赖与并行性
- **第〇步核对(2026-10-26 写卡会话)**:来源条目设计线闭合成立——凭据=术语表 v1.23 三行(termsweep2-20261026 批落,合并提交 6af36382)+用户 2026-10-26 裁决与开工令;状态=文档设计已闭合+待写卡,放行。
- 与迭代 38 七卡零文件交叠(levelmap/结构小件/效果引擎/明细行/奖励节点/结算行缓冲/调试入口批均不触碰本卡文件面;`battle_narrator.gd` 仅本卡触碰——奖励节点清扫批(迭代 38)动 battle_narrator.gd:15 的「节点内容待定——直通」行,本卡动 :50/:90/:92/:154/:156/:159/:166——行面不相交,先合者为准、后合者零冲突)。
- 与 2026-10-26 在途文档批(双层 BD 条目迁移 content-archive、奇物.md 修订——工作区未提交件)零交叠:本批 worktree 内不触碰待办清单 BD 条目区与奇物.md。
## 1. 目标与完成定义
**目标**:「通关」从 game/ 面全量退役——玩家可见字串换为主体明确短语族(单关=关卡完成、整局=冒险成功)、断言与探针随动、注释面清扫、带日期历史读数注释入 S11 留档豁免单;退役后非豁免残留由 S11(注释面+.md 整行)与 doc_term_audit(文档域 .md)双闸兜住,误用即红。
**改动单元清单**(🔴 契约 1——A 表措辞经用户逐条核定,实施逐字照行;超出本清单的新玩家可见「通关」字串=L3 停机呈报):
### q1 · A 面产品字串 12 条(行为改动——每条须断言钉住;任务 A 表编号至 12,标题计数「13 条」系含 :159 注释同步项,一并随 a5 落)
| # | 位置 | 旧 → 新 | 断言 |
|---|---|---|---|
| a1 | `battle_narrator.gd:50` | 「关卡通关!」→「关卡完成!」 | 既有 battle_narrator_test:41 随动(wr1) |
| a2 | 同 `:90` | 「通关回血 +%d(生命 %d → %d)」→「关卡完成回血 +%d(生命 %d → %d)」 | 新增 V1 |
| a3 | 同 `:92` | 「生命已满——通关回血 0(合法态)」→「生命已满——关卡完成回血 0(合法态)」 | 新增 V1(同函数两分支) |
| a4 | 同 `:156` | 「与通关专长三选一同库分流」→「与关卡完成专长三选一同库分流」 | 新增 V2 |
| a5 | 同 `:166` | 「%s\n通关 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d」→「%s\n完成 %d 关 · 止步第 %d 层 · 钥石合计 %d · 主种子 %d」 | 新增 V3 |
| a6 | `career_overlay.gd:42` | 标签「通关局数」→「冒险成功局数」 | 新增 V4 |
| a7 | 同 `:43` | 标签「各关通关次数」→「各关完成次数」 | 新增 V4 |
| a8 | `save_select_overlay.gd:87` | 「通关 %d 局 · 冒险 %d 局」→「冒险成功 %d 局 · 冒险 %d 局」 | 既有 ui_struct_probe:436 随动(wr2) |
| a9 | `ui_kit.gd:403` | TREASURE_REQ_LABELS 的 `adventure_cleared` 标签「通关」→「冒险成功」 | 新增 V5 |
| a10 | `camp_overlay_view.gd:545` | 「最近冒险:%s · 通关 %d 关 · 钥石 +%d · 种子 %d」→「最近冒险:%s · 完成 %d 关 · 钥石 +%d · 种子 %d」 | 新增 V6 |
| a11 | `hud_presenter.gd:191` | 调试浮层读数「通关率 …%」→「关卡完成率 …%」 | 既有 ui_tune_probe:75 随动(wr3) |
| a12 | 同 `:194` | 「通关剩余 HP 均 x」→「关卡完成剩余 HP 均 x」 | 新增 V7(ui_tune_probe 同报告补检) |
(同文件注释随动:battle_narrator:159「通关几关」→「完成几关」、career_overlay:11/:12/:36、save_select_overlay:2/:18、ui_kit:211——归 q3 呈现层注释清单,非独立行为改动。)
### q2 · B 面断言随动+新增断言+探针+验证集清单
**必红三处**(产品字串改后断言必红,先改产品后改断言同轮落地):
| wr | 断言位置 | 随动改法 |
|---|---|---|
| wr1 | `tests/unit/presentation/battle_narrator_test.gd:41` | 条件 `level == "关卡通关!"` 与标签/注释(:39/:41 两处引语)同换「关卡完成!」 |
| wr2 | `probe/ui_struct_probe.gd:436` | 条件 `sum1.text != "通关 2 局 · 冒险 7 局"` 与标签/注释(:434/:436/:437)同换「冒险成功 2 局 · 冒险 7 局」 |
| wr3 | `probe/ui_tune_probe.gd:75` | 条件 `out.contains("通关率")` 与标签(:75/:76)同换「关卡完成率」 |
**13 测试文件 60 处断言/注释随动**(按 §1 换法映射逐处套用;全部为标签文本或注释换代,**断言条件表达式除 wr1 外零触碰**):
| 文件 | 处数 | 逐处换法 |
|---|---|---|
| battle_narrator_test.gd | 2 | :39 注释+:41 标签/条件(wr1) |
| save_manager_domain_test.gd | 8 | :81「通关次数」→「关卡完成次数」、:83「随通关关卡置位」→「随关卡完成置位」、:85「通关局数」→「冒险成功局数」、:98「通关次数表」→「关卡完成次数表」、:268/:272/:324/:328「通关局」→「冒险成功局」「通关局数」→「冒险成功局数」 |
| save_slot_summary_test.gd | 1 | :49 标签「钥石/通关/总局」→「钥石/冒险成功/总局」 |
| adventure_feats_test.gd | 15 | :102/:106 注释「满配通关」→「满配冒险成功」、:132 注释同、:135「满配通关」→「满配冒险成功」、:136「通关几关 = 3」→「完成几关 = 3」、:154「通关三选一」→「关卡完成三选一」、:245/:364「通关部分回血封顶」→「关卡完成部分回血封顶」、:274「未打穿 · 通关 0 关」→「未打穿 · 完成 0 关」、:354/:355 注释「L2 通关」→「L2 关卡完成」、:409 注释「通关回血」→「关卡完成回血」、:417「满血通关回血 0」→「满血关卡完成回血 0」、:512 注释+:522「通关渠道」→「关卡完成渠道」 |
| level_shop_economy_test.gd | 12 | :3/:326 注释「满配通关」→「满配冒险成功」、:332 注释同、:334 标签「通关」→「关卡完成」(单关 victory)、:342/:343/:345/:353「通关路径」→「关卡完成路径」、:374 注释「难以通关」→「难以完成」、:491/:504「路径通关」→「路径完成」、:976 注释「快速通关档」→「快速完成档」 |
| meta_progression_forge_test.gd | 9 | :162 注释「逐通关关次」→「逐关卡完成关次」、:189「通关两关」→「完成两关」、:195「两关通关逐关归因」→「两关完成逐关归因」、:198/:203「零通关」→「零关卡完成」、:220「远程镐=通关 1 次」→「远程镐=冒险成功 1 次」(adventure_cleared 整局语境)、:223「C6:通关=打通整局冒险」→「C6:冒险成功=打通整局冒险」、:233「通关≥3」→「冒险成功≥3」(adventure_cleared 语境)、:270「通关 1 关」→「完成 1 关」 |
| theme_projection_test.gd | 3 | :840「通关局数投影」→「冒险成功局数投影」、:845「各关通关次数投影」→「各关完成次数投影」、:869「通关局数显示 0」→「冒险成功局数显示 0」 |
| reward_overlay_test.gd | 1 | :3 注释「与通关三选一共用」→「与关卡完成三选一共用」 |
| cross_system_flow_test.gd | 3 | :119「满配通关」→「满配冒险成功」、:121「集成通关次数表」→「集成关卡完成次数表」、:126「通关次数全 2」→「关卡完成次数全 2」 |
| breakpoint_resume_test.gd | 1 | :99「非末关通关三选一停点」→「非末关的关卡完成三选一停点」 |
| save_boot_no_activate_test.gd | 1 | :30「通关数全零值」→「冒险成功数全零值」(adventure_cleared 语境) |
| curio_refresh_paid_test.gd | 2 | :26 注释「白盒造通关停点」→「白盒造关卡完成停点」、:28「通关停点在途」→「关卡完成停点在途」 |
| table_structure_guard_test.gd | 2 | :96「通关部分回血比例」→「关卡完成部分回血比例」、:467「〔节点/通关〕」→「〔节点/关卡完成〕」 |
**三探针 13 处**:ui_struct 4 处(wr2 三行+:446 注释改写——「关数形——『通关』无空格不误伤」→「关数形——摘要『冒险成功 X 局 · 冒险 Y 局』不含『关』字」);ui_tune 2 处(wr3);ui_click 7 处(:34「L2 通关收口」→「L2 关卡完成收口」、:371「保 L1 可通关」→「保 L1 可完成」、:402「常态通关远低于此」→「常态跑完远低于此」、:414「通关兑付共用面」→「关卡完成兑付共用面」、:478「非末关通关恰一次/L2 已通关」→「非末关的关卡完成恰一次/L2 已关卡完成」、:494 print「(L2 通关)」→「(L2 关卡完成)」、:642「保可通关」→「保可完成」)。
**验证集清单 38 处**:`python tools/extract_checks.py --extract --force` 重生成(断言标签换代自动同步)→ `--diff` 对账零差异(quotaface 先例流程)。
**新增断言七组**(真实入口=被测装配器/视图的公开装配函数或 open 面;全部带尾缀〔通关换代 Vn〕,进验证集清单):
| # | 断言 | 落点 | 内容(撤销证明=回退对应产品行 hunk→断言红→复原复绿) |
|---|---|---|---|
| V1 | curio_pick_header 回血两分支文案 | battle_narrator_test(随 battle_end 断言组) | 直调 `BattleNarrator.curio_pick_header({"level_index":1,"heal_gained":5,"hp_before":10,"hp_after":15})` 断言输出含「关卡完成回血 +5(生命 10 → 15)」;`heal_gained:0` 载荷断言含「生命已满——关卡完成回血 0(合法态)」〔通关换代 V1〕 |
| V2 | node_curio_header 渠道句 | 同上 | 直调 `node_curio_header()` 断言含「与关卡完成专长三选一同库分流」〔通关换代 V2〕 |
| V3 | adventure_summary_text 完成关数行 | 同上 | 直调 `adventure_summary_text({"cleared":false,"result":"failed","levels_cleared":1,"stopped_at":2,"keystone_total":4,"master_seed":42})` 断言含「完成 1 关 · 止步第 2 层」〔通关换代 V3〕 |
| V4 | 生涯页标签换代+零残留 | theme_projection_test(career 组尾) | open_career(既有 data) 后收集树内全部 label 文本,断言含「冒险成功局数」「各关完成次数」且全集不含「通关」〔通关换代 V4〕 |
| V5 | TREASURE_REQ_LABELS 冒险成功标签 | theme_projection_test(V4 后) | 断言 `UiKit.TREASURE_REQ_LABELS["adventure_cleared"] == "冒险成功"`〔通关换代 V5〕 |
| V6 | 营地最近冒险行完成关数 | theme_projection_test(camp 组) | 实例化 camp_overlay_view → `open_camp(最小 data)`(recent=[{result:"completed",levels_cleared:2,keystone_total:5,master_seed:42}],其余键缺省)→ 断言 `_recent_list.text` 含「完成 2 关」;**T0 先验证各 _rebuild 对缺省键健壮,若不健壮改按 camp_shop_presenter 投影完整构造**〔通关换代 V6〕 |
| V7 | 模拟报告关卡完成剩余 HP 行 | ui_tune_probe(wr3 检查旁) | 报告输出断言 `out.contains("关卡完成剩余 HP")`——高加成参数下确定性有胜局(T0 验证;若该参数下胜局为零则呈报改设计)〔通关换代 V7〕 |
### q3 · C 面注释清扫(非行为改动——同位替换,零行数变化)
**换法映射**(语境规则——B 面标签同用):
| 旧措辞 | 新措辞 | 语境 |
|---|---|---|
| 通关(单关语境:关/层/level/停点/兑付/回血/渠道/三选一/路径) | 关卡完成 | 主体=单关 |
| 通关(整局语境:局/冒险/cleared/满配打通三关) | 冒险成功 | 主体=整局 |
| 「通关 %d 关」 | 「完成 %d 关」 | 用户 A 表 a5 定稿形态 |
| 通关率 | 关卡完成率 | 文档面先例(难度曲线与Boss「关卡完成率带」);hud_presenter a11 同 |
| 通关几关 | 完成几关 | a5 同款 |
| 非末关通关/末关通关 | 非末关的关卡完成/末关的关卡完成 | 防「末关卡」连读歧义(grilling 一轮应修 4) |
| 通关(帧预算/流程跑完语境——非游戏术语) | 跑完 | ui_click_probe:402「常态通关远低于此」一例;白名单 1 同源 |
| 冒险终结 | 冒险落幕 | 退役词对照(game/ 面实查零命中,防御性登记) |
**逐文件清单**(除豁免单所列 13 行外全换):
- core 六件 29 处:adventure_runner.gd:10/:12/:269/:342/:393/:486/:501/:565/:654/:671/:782/:794/:808/:813(14)、save_manager.gd:10/:157/:288/:420/:434/:435/:561/:569/:572(9)、level_runner.gd:564/:745/:775(3)、meta_progression.gd:274、config_db.gd:257、config_db_cross_validator.gd:43——单关语境一律「关卡完成」、局语境「冒险成功」(如 save_manager:10「通关局数」→「冒险成功局数」、:434「通关局数(口径=三关全通」→「冒险成功局数(口径=三关全通」、:561「逐关通关次数合计」→「逐关完成次数合计」、level_runner:564「Boss 胜利 = 关卡通关」→「Boss 胜利 = 关卡完成」、:745「通关/倒下/放弃」→「冒险成功/倒下/放弃」(冒险落幕三态)、config_db:257「通关累加域」→「关卡完成累加域」)
- 七张表头注 17 处减豁免:hero.tres:21/:36 豁免;enemy.tres:751/:891 豁免、:768「0 加成通关 ≤40%」→「0 加成关卡完成 ≤40%」、:871「≈5 次 L1 通关锻造后」→「≈5 次 L1 关卡完成锻造后」;action_economy.tres:15/:16/:26/:27 豁免(4);status_effects.tres:91 豁免;treasure_items.tres:11「生涯通关局数(C6 裁:通关=打通整局冒险)」→「生涯冒险成功局数(C6 裁:打通整局冒险)」;adventure.tres:51「末关通关不发」→「末关的关卡完成不发」;meta.tres:48/:56/:62 豁免、:55「≈5 次 L1 通关开门」→「≈5 次 L1 关卡完成开门」
- schema 两件 4 处:treasure_item_table.gd:12「通关口径=C6(通关=打通整局冒险)」→「解锁口径=C6(冒险成功=打通整局冒险)」、:13「生涯通关局数」→「生涯冒险成功局数」;adventure_table.gd:4「重扫通关率带」→「重扫关卡完成率带」、:39「通关部分回血(固定型,非末关通关兑付)」→「关卡完成部分回血(固定型,非末关兑付)」
- sim 四件 12 处减豁免:auto_player.gd:36「通关率带」→「关卡完成率带」;level_autoplay.gd:2「通关结束」→「关卡完成」、:4「自动通关策略」→「自动完成策略」、:8「通关率/难度类…通关率脱节」→「关卡完成率/难度类…关卡完成率脱节」、:13「通关兑付停点」→「关卡完成兑付停点」、:306「通关停点」→「关卡完成停点」、:560 豁免;verifier_batch.gd:5/:111「逐关条件通关率」→「逐关条件关卡完成率」、:9「累计几次通关冒险后」→「累计几次冒险成功后」、:14「通关率差」→「关卡完成率差」;adventure_monte_carlo.gd:9「通关率/败因分布」→「关卡完成率/败因分布」
- game/README.md 7 处::5/:6「通关期望」→「冒险成功期望」(整局元进度语境)、:7「通关攻击期望」→「冒险成功攻击期望」、:15「非末关通关发大奖励」→「非末关卡完成发大奖励」、:16「输出通关率」→「输出关卡完成率」、:18「满配通关」→「满配冒险成功」、:38「通关率/止步分布」→「关卡完成率/止步分布」
- 呈现层注释 15 处(清单列全 16 个位置供实施对照——battle_narrator:159 计数归 A 面口径〔q1 注〕,q3 面实计 15):battle_narrator.gd:154(「与通关三选一共享同库」→「与关卡完成三选一共享同库」)+:159(「通关几关」→「完成几关」,计数归 q1)、career_overlay.gd:11/:12/:36(「通关局数」→「冒险成功局数」「各关通关次数」→「各关完成次数」)、save_select_overlay.gd:2/:18(「通关局数」→「冒险成功局数」「通关/冒险两值」→「冒险成功/冒险两值」)、ui_kit.gd:211(「模拟报告通关率同档」→「模拟报告关卡完成率同档」)、curio_interact_controller.gd:28/:85/:114/:180/:231(「通关停点/通关三选一/非末关通关」→「关卡完成停点/关卡完成三选一/非末关的关卡完成」)、curio_pick_overlay.gd:2、tune_sim_controller.gd:38(「通关全量 JSONL」→「关卡完成全量 JSONL」)、main_adventure_flow.gd:54
### q4 · S11 留档豁免单扩充(static_scan.sh,7→20 处)
带日期历史读数注释 13 行按豁免惯例保留原文,逐行登记进 `game/tests/static_scan.sh` 两段 awk 的豁免单(.tres 段 12 行+.gd 段 1 行):
| 文件:行 | 豁免理由(留档口径随 commit) |
|---|---|
| hero.tres:21 | 压缩前基线读数(N=30 normal 基线档通关率 3%)——历史读数 |
| hero.tres:36 | L1 条件通关 2%、0/100 出第一关——历史实测 |
| enemy.tres:751 | 基线满血全通关(模拟 80%/真人 100%)——历史试玩读数 |
| enemy.tres:891 | L1 条件通关 1-2%、0/100 出第一关——历史实测 |
| action_economy.tres:15 | AP 压速批(2026-09-16 用户拍板)无伤通关根因诊断实录——带日期历史批记录 |
| action_economy.tres:16 | 同段延续(零承伤通关实录) |
| action_economy.tres:26 | 上批 8→6 所杀退化形态叙述——历史批记录 |
| action_economy.tres:27 | 同段延续 |
| status_effects.tres:91 | 真人局 12/12 轮零承伤满血通关——历史读数 |
| meta.tres:48 | 旧「总通关次数达阈值」六行历史记载(旧文名引述) |
| meta.tres:56 | 实测 L1 通关局收入 ≈29-35 GP——历史实测读数 |
| meta.tres:62 | 通关率差实测每级带平移(bootstrap SE)——历史实测读数 |
| level_autoplay.gd:560 | 完美档 41% 通关全败实证——历史实证读数 |
(行号=实施后实测;C 面同位替换零行数变化,预期与基线一致——不符即 L2 记录按实测定。)
### q5 · 体验验证清单目检项引语(根件,随批同步——用户 B 表指令)
- :193 生涯页目检项:「通关局数〔括注「三关全通」〕 / 各关通关次数」→「冒险成功局数〔括注「三关全通」〕 / 各关完成次数」、句尾「三关全通才计通关」→「三关全通才计冒险成功」
- :198「『通关局数』口径如与体感冲突」→「『冒险成功局数』口径如与体感冲突」
- :107「真人通关率+k 系数」→「真人关卡完成率+k 系数」
- :258 历史读数记载行(四代锚、T19 读数等带批名日期引述)——豁免保留原文
**完成定义**:
1. q1–q5 全落地;三条 grep 判据过(§8 wr 判据)+验证集清单对账零差异;
2. 计数器键名(`adventure_cleared`/`level_clear_counts`)与测量数据键、sim-data 锚文件名零触碰(wr1 读数对拍);断言条件表达式除 wr1/wr2/wr3 三处随动外零触碰;
3. 撤销证明=A 面 13 条逐条(回退产品行 hunk→对应断言红→复原复绿)记录成表;三份产出物写齐(T5)。
## 2. 设计事实源
| # | 出处 | 取用条款 |
|---|---|---|
| D1 | [术语表](../design/gdd/术语表.md) v1.23:「关卡完成」「冒险成功」「冒险落幕」三行+「退役词对照」通关/冒险终结两行 | 全部换法的第一依据 |
| D2 | 用户 2026-10-26 开工指令(本卡来源 prompt):A 表 12 条措辞逐字核定+:159 注释同步项+B/C 面清单+红线 | q1 逐字措辞、q5 随批范围、零触碰红线 |
| D3 | termsweep2-20261026 批(合并提交 6af36382):文档面 25 文件 145 行已完成 | 「通关率」→「关卡完成率」文档面先例(难度曲线与Boss「关卡完成率带」) |
| D4 | [test-conventions](../design/engineering/test-conventions.md) §8.3(验证集清单同步义务)+ quotaface-20261026 先例(S11 豁免单 4→7 处流程、清单重生成对账、卡片与条目随批删除) | q2 清单重生成、q4 豁免单、批内删卡删条目 |
## 3. 现状事实(2026-10-26 写卡会话实查于 master 1ca76f16;开工日 T0 复核行号)
| # | 事实 | 出处 |
|---|---|---|
| B1 | game/ 全域「通关」208 处/47 文件(A 面 13+B 面 111=13 测试 60+探针 13+验证集清单 38+C 面 84);「冒险终结」零命中 | grep 实查(任务基线 208 处一致;文件数 46→47 为本会话按现行扩展名集实测) |
| B2 | 必红断言三处=battle_narrator_test:41(条件 `level == "关卡通关!"`)/ui_struct_probe:436(条件 `sum1.text != "通关 2 局 · 冒险 7 局"`)/ui_tune_probe:75(条件 `out.contains("通关率")`);其余 57 处为标签/注释(条件位是数值判定,改产品字串不红) | 三文件 grep+条件位扫描实查 |
| B3 | A 面其余 10 条产品字串零逐字断言(theme_projection 断的是值文本「1 局(三关全通)」非行标签;TREASURE_REQ_LABELS/curio_pick_header 回血行/node_curio_header 渠道句/adventure_summary_text 关数行/camp 最近冒险行/ui_tune「通关剩余 HP」行均无断言) | theme_projection_test:840-871、grep TREASURE_REQ_LABELS/最近冒险/通关回血 实查 |
| B4 | S11 词表动态解析自术语表「退役词对照」——termsweep2 批入表后「通关」「冒险终结」已自动进入扫描;豁免三层=①闸门自指 ②显式留档豁免单 7 处 ③非注释面(字符串字面量)不取;A 面产品字串属③、注释面与 game/ 内 .md 整行属扫描面 | `game/tests/static_scan.sh:122-230` |
| B5 | 验证集清单由 extract_checks.py 机器提取冻结(标签+cond+计数三要素对账;--extract --force 重生成、--diff 对账);探针不进清单 | `tools/extract_checks.py` 头注;quotaface 先例 |
| B6 | hud_presenter:191/:194 属 tune_sim_report(调试浮层批量模拟报告——DS_DEBUG 门控玩家可见面);camp 最近冒险行装配=`camp_overlay_view._rebuild_recent`(open_camp 全量重建路径,data 键缺省走 .get 兜底);career 行标签与值 label 分体(值断言不断行标签) | hud_presenter.gd:186-195、camp_overlay_view.gd:376-382/:534-547、theme_projection_test:835-855 |
| B7 | ui_struct_probe:446 禁项检查「 关」现存形态注释引「通关」作不误伤说明——产品字串换代后摘要零「关」字,注释须改写不再引旧词(否则撞 S11) | ui_struct_probe.gd:446-451 |
## 4. 设计契约(红线,违反 = L3 停机呈报)
🔴 **契约 1 · A 表措辞逐字照行**:q1 的 13 条新文案逐字按 §1 表落(用户已核);实施中发现 A 表之外的玩家可见「通关」字串(含新发现文件)=停卡呈报,禁自行拟措辞。
🔴 **契约 2 · 零触碰面**:计数器键名 `adventure_cleared`/`level_clear_counts`、存档域键、sim-data 锚文件名与 JSONL 数据键、RNG 流名、全部数值行——逐字不动;断言条件表达式除 wr1/wr2/wr3 外零触碰(B 面只改标签文本与注释)。
🔴 **契约 3 · 清单跟上**:断言标签改动后 `tools/extract_checks.py --diff` 对账零差异;禁为免对账保留旧标签。
🔴 **契约 4 · 残留清零双闸兜底**:`grep -rn "通关" game/` 非豁免残留清零(豁免=q4 清单 13 行+S11 既有 7 行+字符串字面量位非注释面);S11 全绿。清单外新发现的注释残留按映射表同批清(非行为改动,L1 自主),**新发现的玩家可见字串仍走契约 1 停卡**。
🔴 **契约 5 · 撤销证明全量**:A 面 13 条逐条「回退产品行 hunk→最窄断言红→复原复绿」;禁以「代码能跑通」代替。
🔴 **契约 6 · 禁为机检文本保留无效成员**(req-card 第 13 条):禁为避免 S11/清单红而保留旧词文本或新增别名/无效成员;机检键与卡片契约互斥时停卡呈报。
## 5. 范围边界
**做**:q1–q5;三条 grep 判据;清单重生成对账;撤销证明 13 条;S11 豁免单扩充;产出物三份;卡片与待办条目随批删除(quotaface 先例,用户开工令内含「实施」)。
**不做**:
- 计数器/存档域/数据键/数值任何调整(零数值改动=零 MC,11 号对拍不补跑——quotaface 同款红线);
- design/ 面与根规则文件除体验验证清单目检项引语外的任何修订(roadmap 不动——提前实施批无迭代行;待办清单仅删本条目);
- 体验验证清单 :258 历史读数记载行(豁免保留);
- 任何未被 A/B/C 表覆盖的新措辞裁量。
## 6. 关联文档同步面
| 文档 | 关联点 | 同步义务 |
|---|---|---|
| `体验验证清单.md`(根件) | :193/:198 生涯页目检项引语随 a6/a7 换代、:107 关卡完成率口径 | **随批同步**(用户 B 表指令;q5——批内落地,全量档闸覆盖) |
| `待办清单.md`(根件) | 本条目随批删除(活干完登记即毁——quotaface 先例) | **随批同步**(批内删除) |
| [术语表](../design/gdd/术语表.md) v1.23 | 退役词对照已收两行,无需改动 | 反哺点 · 预期零改动注记(T0 核对) |
| [架构文档](../design/engineering/architecture/architecture.md)「测试与质量域」 | S11 豁免单 7→20 处的现状登记(若该节登记了豁免计数) | 反哺点 · 列清单(T0 查该节是否载计数,载则列换代行) |
| `game/tests/README.md`(测试工具手册) | 若载 S11 豁免计数/清单说明 | 反哺点 · 列清单(T0 查,载则列) |
## 7. 任务卡
### 卡前 · 文件结构图
| 文件 | 职责 | 行为改动 |
|---|---|---|
| `game/presentation/battle_narrator.gd` | a1–a5 产品字串+:154/:159 注释(复用既有·修改) | 行为改动 → 对照表 a1–a5 |
| `game/presentation/views/career_overlay.gd` | a6/a7 标签+:11/:12/:36 注释 | 行为改动 → a6/a7 |
| `game/presentation/views/save_select_overlay.gd` | a8 摘要+:2/:18 注释 | 行为改动 → a8 |
| `game/presentation/views/ui_kit.gd` | a9 标签值+:211 注释 | 行为改动 → a9 |
| `game/presentation/views/camp_overlay_view.gd` | a10 最近冒险行 | 行为改动 → a10 |
| `game/presentation/hud_presenter.gd` | a11/a12 调试报告文案 | 行为改动 → a11/a12 |
| `game/tests/unit/presentation/battle_narrator_test.gd` | wr1 随动+V1/V2/V3 新增 | 断言随动+新增 |
| `game/tests/unit/presentation/theme_projection_test.gd` | 3 处标签随动+V4/V5/V6 新增 | 断言随动+新增 |
| 其余 11 测试文件 | 60 处减 battle_narrator_test 2 处=58 处标签/注释随动 | 非行为改动(标签文本) |
| `game/probe/ui_struct_probe.gd` | wr2+:446 注释改写 | 断言随动 |
| `game/probe/ui_tune_probe.gd` | wr3+V7 | 断言随动+新增 |
| `game/probe/ui_click_probe.gd` | 7 处注释/print | 非行为改动 |
| `game/tests/验证集清单.md` | 重生成 38 处 | 非行为改动(机器生成同步) |
| core 六件+七张表+schema 两件+sim 四件+`game/README.md`+呈现层注释件 | q3 注释同位替换 | 非行为改动(注释面) |
| `game/tests/static_scan.sh` | q4 豁免单 7→20 处 | 非行为改动(闸件豁免清单;判据口径零变化) |
| `体验验证清单.md` | q5 目检项引语 4 行 | 非行为改动(文档引语) |
(零新增文件、零新增函数、零新增数据件。)
### T0 · 开工核对(0.1d)
- [ ] 建 worktree `git worktree add ../g-plan2-termsweep-game -b termsweep-game-20261026`+拷 `.godot/` 导入缓存与字体 .import 边车(坑册三件套)
- [ ] 复核 B 面行号(迭代 38 若有先合回的批改了行号则以实查为准——行面零交叠预期成立)
- [ ] V6 前置验证:camp_overlay_view.open_camp 最小 data 健壮性直跑(30 秒级孤立验证——新断言先 standalone 走通再并入套件)
- [ ] V7 前置验证:ui_tune_probe 现行参数下模拟必有胜局(读一次探针输出确认「通关剩余 HP」行恒在)
- [ ] 术语表 v1.23 三行在位核对(§6 零改动注记);架构文档/README 是否载 S11 豁免计数核对
### T1 · A 面产品字串+必红断言(0.25d)
- [ ] a1–a12 十二处字串逐字落(q1 表);wr1/wr2/wr3 断言条件+标签+注释同轮换
- [ ] V1–V3 battle_narrator_test 三组新断言落(孤立验证:单套件直跑绿)
- [ ] 撤销证明 a1–a5(回退 battle_narrator hunk→battle_narrator_test 红→复原复绿)
**接口块**:取用=无前置卡;产出=产品字串新文案(对照表 a1–a12 文本,V 组断言引用)。
### T2 · B 面其余断言随动+新增断言+清单(0.25d)
- [ ] 11 测试文件 58 处标签/注释随动(q2 逐处换法表)+theme_projection 3 处+V4/V5/V6 落位
- [ ] 三探针随动+V7(ui_click 7 处、ui_struct wr2+:446 注释改写、ui_tune wr3+V7)
- [ ] `python tools/extract_checks.py --extract --force` 重生成 → `--diff` 对账零差异
- [ ] 撤销证明 a6–a10、a12(theme_projection/ui_struct/ui_tune 各自最窄套件红→复绿)
**接口块**:取用=T1 产品字串(a1–a12 文本);产出=验证集清单新基线(T4 件核对账)。
### T3 · C 面注释清扫+S11 豁免单(0.25d)
- [ ] core 29+表头注 5(17−12 豁免)+schema 4+sim 11(12−1)+README 7+呈现层 15——按映射表同位替换
- [ ] static_scan.sh 豁免单 13 行登记(.tres 段 12+.gd 段 1,行号按实施后实测)+头部豁免说明句更新
- [ ] 撤销证明 a11 复核(wr3 已在 T1;此处 S11 直跑绿作护栏确认)
### T4 · 件核+体验验证清单+冻结(0.1d)
- [ ] q5 体验验证清单 4 行改(:107/:193/:198;:258 不动)
- [ ] 三条 grep 判据(§8)逐条零输出
- [ ] 冻结:代码与断言改完即冻结,全仓件核 grep 查残留
- [ ] 并 master(若 master 被推进——先并后闸铁律)
### T5 · 产出物落位卡(0.15d)
- [ ] 轻闸全绿后、合并前 `reports/` 三份写齐:`iter-supplement-通关换代-实现报告-2026-10-26.md`(各卡状态+撤销证明记录表 13 条+实机目检总清单+遗留读数义务)、代拍板追认单(§10 白名单逐条)、反哺点清单(§6 所列每条落点)
- [ ] 卡片与待办条目删除(quotaface 先例;唯一引用=条目自身)
- [ ] 批末全量域闸(改动含代码面+game/ 内 .md+根件+reports/ → 不带参数全量档)全绿、回执对齐分支尖 → 主区 `git merge --no-ff` → worktree 清理
## 8. 验证闸(改动—验收对照表)
| 需求点 | 改动(文件:行) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 撤销证明 |
|---|---|---|---|---|---|
| a1 关卡完成文案 | battle_narrator.gd:50 | 「关卡收束态恒为「关卡完成!」」battle_narrator_test:41〔随 wr1〕 | 单元 | 直调 battle_end_text | 回退 :50 → wr1 红 → 复绿 |
| a2/a3 回血两分支 | battle_narrator.gd:90/:92 | V1 curio_pick_header 断言〔通关换代 V1〕 | 单元 | 直调 curio_pick_header | 回退 :90/:92 → V1 红 → 复绿 |
| a4 渠道句 | battle_narrator.gd:156 | V2〔通关换代 V2〕 | 单元 | 直调 node_curio_header | 回退 :156 → V2 红 |
| a5 完成关数行 | battle_narrator.gd:166 | V3〔通关换代 V3〕 | 单元 | 直调 adventure_summary_text | 回退 :166 → V3 红 |
| a6/a7 生涯标签 | career_overlay.gd:42/:43 | V4 生涯页标签零残留〔通关换代 V4〕 | 接口 | open_career+树内 label 收集 | 回退 :42/:43 → V4 红 |
| a8 槽摘要 | save_select_overlay.gd:87 | wr2 ui_struct_probe:436〔sl2 随动〕 | 探针 | 场景实例化+喂入数据 | 回退 :87 → wr2 红 |
| a9 解锁标签 | ui_kit.gd:403 | V5〔通关换代 V5〕 | 单元 | UiKit 常量直读 | 回退 :403 → V5 红 |
| a10 最近冒险行 | camp_overlay_view.gd:545 | V6〔通关换代 V6〕 | 接口 | open_camp+_recent_list 读数 | 回退 :545 → V6 红 |
| a11 完成率读数 | hud_presenter.gd:191 | wr3 ui_tune_probe:75〔随动〕 | 探针 | 场景实例化+批量模拟输出 | 回退 :191 → wr3 红 |
| a12 剩余 HP 行 | hud_presenter.gd:194 | V7〔通关换代 V7〕 | 探针 | 同上报告 | 回退 :194 → V7 红 |
| q2 断言随动 | 13 文件 60 处 | extract_checks --diff 对账零差异+各套件批末闸 | — | 机器对账 | 由 a1–a12 行撤销证明连带覆盖 |
| q3 注释清扫 | C 面 84 处 | grep 判据②③+S11 全绿 | — | 静态扫描 | 非行为改动(判断依据:注释/头注文本,无运行时语义) |
| q4 豁免单 | static_scan.sh 13 行 | S11 直跑绿且豁免行不误伤相邻残留(抽一行去豁免验证红) | — | 静态扫描 | 去豁免行 → S11 红 → 还原 |
| q5 目检项引语 | 体验验证清单 4 行 | --docs 域闸 doc_term_audit 绿 | — | 文档域闸 | 非行为改动(文档引语) |
**grep 判据**(三条;豁免面机械判定由 S11 承载——注释面+game/ 内 .md 整行,豁免单承载 q4 13 行):
- wr判①:S11 直跑绿(零命中)——即「game/ 注释面与 .md 非豁免残留清零」的机械形态;辅以人工抽查 `grep -rn "通关" game/` 输出逐行比对 q4 豁免单(每命中行必在豁免单或字符串字面量位)
- wr判②:`grep -rn "冒险终结" game/` → 零输出
- wr判③:对账 `python tools/extract_checks.py --diff game/tests/验证集清单.md` → 退出码 0
**测试削弱申报**:本批改动的既有断言=wr1/wr2/wr3 条件文本随产品字串换代(非削弱——判据随动);其余 57 处仅标签文本;零断言删除、零阈值放宽、零扫描面缩小。
**独立验收审计**:合回前由未参与实现的子代理按 test-conventions §8.7 审(报告落 `reports/iter-supplement-通关换代-验收审计-2026-10-26.md`):对照表逐行核、撤销证明抽三条复现、削弱扫描。
**闸口径**:批末 `tools/run_all_gates.sh` 不带参数全量档(改动含代码面+文档件两类);批内回归措辞=里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增断言先行孤立验证再并入全套。
## 9. 异常分级
- L1 自救(一次重试):行号漂移(迭代 38 先合回导致)——以实查重定位;豁免行号与预期不符——按实测定。
- L2 记录继续:V6 最小 data 不健壮——改完整构造照 camp_shop_presenter 投影;V7 零胜局——呈报改设计(同轮记录)。
- L3 停机呈报:A 表外新玩家可见「通关」字串;机检键与契约互斥;S11 豁免单机制不足以表达某豁免形态;闸红且修法超出本卡范围。
## 10. 代拍板白名单
1. B/C 面标签与注释的措辞在映射表(q3)两可时的具体选词(如「路径完成」vs「完成路径」)——限标签/注释面,产品字串不适用。
2. 豁免单行号按实施后实测微调。
3. V1–V7 断言的夹具数据具体取值(保持确定性即可)。
## 11. 待裁点
零待裁点,开工门=U0(用户 2026-10-26 开工令已下达)。
## 12. 落位与可复用性预检
- **复用面**:零新增文件/函数/数据件——全部为既有文件的文本替换与既有测试文件的断言追加;V4 的树内 label 收集复用 ui_struct_probe `_collect_labels` 同型写法(theme_projection 内新写四行等价物——不跨层引探针私有件,避免测试件间耦合);V6 落 theme_projection_test 延续 career_overlay 直调先例。无「不能复用既有件」的新增申报。
- **唯一面**:产品字串唯一落点=各装配器/视图现文件(无第二处定义);「关卡完成」等新词的规范名唯一登记处=术语表(已立,本批零文档新增)。
- **分层与交互面**:V1–V3 直调 BattleNarrator 静态装配器(单元层);V4–V6 实例化视图 open 面后读 label(接口层,theme_projection 先例同型);V7/wr3 探针走批量模拟真实输出。零 Core 改动、零数值读取新增、零 RNG 触碰。
## 13. 复核记录
- v1.0 成文,grilling 一轮对抗复核(独立子代理,2026-10-26):**零阻断**;四应修全折入——①q3 呈现层计数口径注明(:159 归 A 面)+全文统一「A 表 12 条+:159 注释同步项」②估时对齐 T 段合计 ≈1.1d ③映射表补帧预算/流程语境行(「跑完」)④「非末关通关/末关通关」→「非末关的关卡完成/末关的关卡完成」防「末关卡」连读歧义(ui_click:478、breakpoint_resume:99、adventure_runner:808、adventure.tres:51、curio_interact 注释族)。
- 复核通过面(子代理实查证据):措辞与 A 表逐字一致;208 处全数对上(12 产品+111 随动+84 注释);豁免 13 行无漏判;V1–V7 落点全真(V6 open_camp 最小 data 全链 .get 兜底健壮性经子代理读码确认);语境抽查 8+ 处全对;契约完备;q5 恰 4 行覆盖。
- v1.1 定稿,零待裁点。
+3 -3
View File
@@ -104,7 +104,7 @@
**2026-10-01 数值修复批后试玩注记**(master 4b4a011 起的正式标定局):本批已落 hero 血池 20→26 / 直伤雷三行 4/5/5→6/8/8 / L1 系敌 atk 2→1(轨迹与读数见 numfix 报告(已退役,git 历史可查))。试玩时对号新增两点:
1. **踩雷疼痛感**(本批主刀):「踩很多雷还是轻松活」应是过去式——单雷 6-8 直伤、两三连雷应明显肉疼;若仍无痛感=回调信号,记一句话即可;
2. **整体难度印象**:用户 2026-10-01 已阶段性验收「令人稍微满意」——本批试玩读数作正式标定(真人通关率+k 系数),若体感偏难/偏易也照实记,不因单局顺逆回调。
2. **整体难度印象**:用户 2026-10-01 已阶段性验收「令人稍微满意」——本批试玩读数作正式标定(真人关卡完成率+k 系数),若体感偏难/偏易也照实记,不因单局顺逆回调。
模拟侧对照读数(普通档画像 L1 13%/战役 0%;完美档 78.5%)见同报告 §四。
### ⑩ 迭代 10 奖励重构体验项(2026-10-01 顺带登记——实机目检一轮已过 1/3/4/5/6/9/14,余项人工参与)
@@ -190,12 +190,12 @@
### ⑲ 生涯页目检(迭代 18 卡④生涯界面实装批·期一顺带登记——主菜单新增一级只读页+存档 v6)
- **入口与开合**:〔入口已换代——迭代 33 卡② 撤主菜单生涯钮后无玩家可见入口,待迭代 34 卡④ 营地笔记本接入;接入前本项验到页内容即可〕点开全屏页、点「返回」回上层(开始层钮可见性随开合正确恢复)。
- **页呈现两类六项**:组一·冒险战绩(冒险总局数 / 通关局数〔括注「三关全通」〕 / 各关通关次数〔一行三关,关名带层号〕 / Boss 首杀〔X/3 关〕 / Boss 总击杀);组二·资源(钥石余额徽)——数值与存档实况一致(对照:玩几局后总局数含放弃与失败、三关全通才计通关)。〔原「属性锻造已购合计」「天赋树已购概览」两观测项随生涯页三投影删除摘除——2026-10-12 用户裁⑧,天赋树实装批顺带〕
- **页呈现两类六项**:组一·冒险战绩(冒险总局数 / 冒险成功局数〔括注「三关全通」〕 / 各关完成次数〔一行三关,关名带层号〕 / Boss 首杀〔X/3 关〕 / Boss 总击杀);组二·资源(钥石余额徽)——数值与存档实况一致(对照:玩几局后总局数含放弃与失败、三关全通才计冒险成功)。〔原「属性锻造已购合计」「天赋树已购概览」两观测项随生涯页三投影删除摘除——2026-10-12 用户裁⑧,天赋树实装批顺带〕
- **旧档载入**:v5 现役档(升级前存档)载入后生涯页正常显示,总局数/冒险成功局数从 0 起计(终身计数域 v6 新增,缺省补零),钥石等既有进度原样。
- **零跳转与局内零入口**:页内除「返回」外无任何前往他处的按钮;冒险进行中(战斗屏/关卡地图屏/状态面板)无生涯入口。
- **对比度与零动效**:页文字亮墨族在深棕底上可读(机检 3 对已入 ui_contrast 闸);开页/关页无过渡动画。
- **红灯信号**:计数与实玩局数对不上(收束入账重复或漏计);旧档载入返回错误或计数域显示异常;页内出现购买/跳转入口;入口在局内可点。
- **结论回流**:呈现问题归生涯批后续修正(本批需求卡片 v1.2 在案);「通关局数」口径如与体感冲突=P1 甲裁决复核(三关全通才计 1,用户已裁);期二内容(历史回看/图鉴)缺位属分期计划非缺陷。
- **结论回流**:呈现问题归生涯批后续修正(本批需求卡片 v1.2 在案);「冒险成功局数」口径如与体感冲突=P1 甲裁决复核(三关全通才计 1,用户已裁);期二内容(历史回看/图鉴)缺位属分期计划非缺陷。
### ⑳ 营地统一四柜台目检(迭代 21 天赋树实装批顺带登记——V22 人闸;2026-10-12)
-13
View File
@@ -2,19 +2,6 @@
本清单是流水线的唯一登记处:要做的事先在此登记一条,随流水线段推进改状态,做完整条删除;各系统文档的「待办」节只留一行指路存根,不再自建条目。条目格式(标题=登记元数据含可选优先级,正文=实况/缺口/改动面/状态四属性)与状态机以 [doc-conventions §12.1](design/engineering/doc-conventions.md) 为权威。
#### game 面玩家可见字串「通关」退役换代——A 面文案 13 条+B 面断言随动+C 面注释清扫(2026-10-26 登记,来源=批次移交)
**实况**:文档面已由 termsweep2-20261026 批完成(合并提交 6af36382,25 文件 145 行),本条承接 game/ 面。2026-10-26 用户裁「通关」全库退役+开工令(A 表 13 条玩家可见字串措辞已核,改动即停呈报):单关=关卡完成、整局=冒险成功、一局终结统称=冒险落幕;「通关率」随文档面先例(难度曲线与Boss「关卡完成率带」)换代「关卡完成率」。写卡会话实查基线:game/ 全域「通关」208 处/47 文件(A 面产品字串 13 条=battle_narrator 6+career_overlay 2+save_select_overlay 1+ui_kit 1+camp_overlay_view 1+hud_presenter 2;B 随动面=13 测试文件 60 处+ui_click/ui_struct/ui_tune 三探针 13 处+验证集清单 38 处,其中逐字断言必红三处=battle_narrator_test:41/ui_struct_probe:436/ui_tune_probe:75;C 注释面=core 六件 29+七张表头注 17+schema 两件 4+sim 四件 12+game/README 7+呈现层注释 15);带日期历史读数注释按豁免惯例保留原文(S11 留档豁免单登记)。S11 已随术语表收词自动开始扫「通关」「冒险终结」——本条实施后非豁免残留即闸红。
关联文档清单:
- design/gdd/术语表.md v1.23(「关卡完成」「冒险成功」「冒险落幕」三行+退役词对照「通关」「冒险终结」两行——通关口径统一批,2026-10-26)
- 用户 2026-10-26 开工指令(A/B/C 三面清单+红线——计数器键名 adventure_cleared/level_clear_counts 与测量数据键、sim-data 锚文件名零触碰)
- 需求卡片 reports/需求卡片-通关口径换代批-2026-10-26.md(2026-10-26 v1.1 定稿,grilling 一轮复核零阻断、四应修折入,零待裁点;估时 ≈1.1d;用户开工令裁准提前实施,先例=quotaface-20261026)
**改动面**:游戏代码
**状态**:文档设计已闭合+已写卡
#### AdventureRunner 职责真拆分——三宿主共 25 条转发属性属 S13 计数迎合装置,须按变化原因重立协作件(2026-10-25 登记,来源=批次移交,优先级=P0)
**实况**: