Compare commits

..
Author SHA1 Message Date
DKChan e0d52051e7 迭代46收尾文档卡C(审计结论): 独立验收审计通过可合并(五项全过——对照表逐行/自跑探针两档与单元74checks实读/判据绑生产写入值/削弱全对申报/无阈值回归无变异撤销取证)·审计报告落reports·实现报告§九结论回填+§三行1补受控例外句(审计非阻断备注处置——审计方自跑读数与本会话当轮实跑逐值一致) 2026-11-01 03:07:14 +08:00
DKChan 8eaf7d8762 迭代46收尾文档卡C(实现报告+反哺点): T0改前基线四读数(gdstyle零恶化/单测62checks/探针两档/取档对账10-12-13-14)与改后对拍全录·实际对照表五行全绿(等价断言V1/算式单点V2/四档冒烟V3/推定留痕V4/批末闸V5——all_green=true七闸receipt在案)·锚点读数留档两档四板型(40/33/30/28·46/46/46/46逐值合卡§2.7)·目检一项登记不阻塞(14x14格面数字可辨·用户已背书)·实现决定三条备案(_board null前置/探针两成员/锚点全表读数件)·反哺点两条(battle.md:144下限句退役+10-31清单第8条作废声明)·审计结论节待审计落笔 2026-11-01 02:59:52 +08:00
DKChan e3e206d0f5 迭代46卡C(棋盘格边长口径收口批v2.0): 格边长算式单点化——board_view抽static _cell_for逐位同式(SEP/DEPTH_PX读本文件常量·clampi[26,46]留函数内·S13顶格27/31持平零新增成员)+set_cell_area改调·副作用尾链零动(行为零变化)·探针阈值判据退役(MIN_CELL常量整行删除·阈值断言换等价断言cell==_cell_for(n,bv.size)·_board内部字段直查受控例外=N无公开出口·PASS行含基座实读+13/14推定留痕打印不作判红·零尺寸前置记红)·单元冒烟扩四档板型((L,D)取样10/12/13/14·板型一致+算式单点等价两断言·既有网格高与26-46区间断言逐字保留·62→74checks全绿)·探针两档绿(基座实读40px·13/14推定30/28与卡§2.7锚点逐值一致·全屏46/46·可用面积1022x454/1662x734)·grep MIN_CELL/可读门槛/可读下限全仓零命中·probe_gate默认段绿 2026-11-01 02:53:31 +08:00
DKChan 5269ed4080 合并(fv25退役与欠账登记批): 〔fv25〕表内注释存在性断言退役——守载体不守信息,行留任仍由fv12从解析数据守,02套件67→66项;待办清单登记「hobgoblin 内容三件套补齐」条目(2026-11-01/来源=审计发现/状态=待文档设计,凭据=敌人系统.md v1.1)——欠账登记迁待办清单唯一登记处,禁再以表内注释+机检承载;用户令工程改动经worktree车道合回,合回后post-merge钩子自动跑CI全量域闸 2026-11-01 02:11:16 +08:00
DKChan 5ecfabc251 Merge branch 'master' into fv25退役 2026-11-01 02:11:05 +08:00
DKChan 57084e0bd3 工程+登记(fv25退役与欠账登记批·用户令): 删 table_structure_guard_test〔fv25〕断言——守「表内注释存在性」与游戏行为无关(行留任仍由fv12从解析数据守,注释机检守载体不守信息,用户裁无验证价值),连带摘fv12消息尾缀〔fv25〕与②块注释「缺口登记随行」句,02套件67→66项;登记待办清单「hobgoblin 内容三件套补齐」条目(2026-11-01/来源=审计发现/状态=待文档设计,凭据=敌人系统.md v1.1「hobgoblin缺口登记」节2026-10-13迭代24反哺④)——欠账登记迁待办清单唯一登记处,禁再以表内注释+机检承载;GDD正本登记不动 2026-11-01 02:11:04 +08:00
DKChan 12af4fe00c 合并(合回即删改回批·用户令): 删区时机改回「合回即删」2026-09-19原裁——「CI全量绿→删区」系10-30本地CI化批夹带改动未经用户单独裁断;红态修复车道换轨新开fix-修复worktree(免重塑免数卡),追绿复验落post-merge(post-commit降兜底);规则文本五文件十一处随动+iter/.qoder技能豁免面直改;件核旧语义零残留;合并落地即删区=新规首次实操 2026-11-01 02:09:46 +08:00
DKChan 22809f9b79 工程+文档(合回即删改回批·用户令): 删区时机改回2026-09-19原裁「合回即删」——「CI全量绿→删区」系10-30本地CI化批(c91bbddd)重写§1时夹带、未经用户单独裁断,10-31车道恢复批(94628fca)沿用未复位;恢复收口=冻结→件核→并master→重塑→主区merge --no-ff→合回落地即git worktree remove+删分支,CI由post-merge钩子在合回落地时自动触发、与删区解耦;红态修复车道换轨=新开修复worktree(fix-<任务名>分支,非iter<N>形免重塑免数卡,修复归属原批卡片)修复合回追绿,追绿复验落post-merge,post-commit钩子降为红态主区直提兜底触发(机件零动);规则文本五文件十一处随动:AGENTS车道铁律行/运行与验证首条/改动四类行,process-conventions §1.2收口序列与红态句/§1.3/§1.9①,test-conventions §4,iter技能两处+.qoder镜像两处(.zcode/.qoder豁免面主区直改不入git);件核=旧语义全库grep零残留,守卫数卡提示「回任务worktree」语义仍准(拦截时任务区尚在)零动 2026-11-01 02:09:29 +08:00
DKChan 94dfa7e79b 合并(编排批): 卡C棋盘格边长口径收口批v2.0回编迭代46——迭代行卡列补卡C路径/编入时间换代2026-11-01/预计周期≈8.625不变/状态保持进行中(用户裁);待办条目状态追加已编迭代46;当前位换代=卡C v2.0待/iter发令回worktree iter46续工+迭代47已编排待开工;battle.md:144退役归文档批待发令;文档域闸绿(7fd50fc4) 2026-11-01 02:08:41 +08:00
DKChan 7fd50fc485 编排(卡C v2.0回编迭代46): 棋盘格边长口径收口批v2.0(2026-11-01用户裁锚点换代后重立,写卡批e7090bc9)重编入迭代46——迭代行卡列补卡C路径、编入时间换代2026-11-01、预计周期≈8.625不变;状态保持进行中(用户裁,卡A/卡B已合回34bb62a5、剩余卡C约0.5日);待办条目状态追加已编迭代46(已写卡段保留);当前位下一步换代=卡C v2.0待/iter发令回worktree iter46续工(WIP两件复用一件返工)+battle.md:144退役归文档批待发令+迭代47已编排待开工;当前位措辞避R3修订注记形态(gdd_lint);流水线闸PASS 2026-11-01 02:08:06 +08:00
DKChan e7090bc9c1 合并(写卡批): 卡C棋盘格边长口径收口批v2.0重立合回——2026-11-01用户裁锚点换代(可读性锚点=用户验收的现行渲染值,基座档14×14=28px达标;可读下限30px句与探针36px人造阈值全退役,MIN_CELL手抄常量删除);探针判据换锚=实读与单点算式_cell_for严格相等+结构钳制+13/14推定留痕;grilling独立复核四项打回全落(S13实测更正max-class-variables=31/推定死断言删除/重构类两契约条款补齐/对拍不一致归L3#2);文档域闸绿(3a8634ab);battle.md:144退役落文归文档批待发令 2026-11-01 01:53:44 +08:00
DKChan 3a8634ab92 写卡(卡C v2.0·grilling复核修正): 独立复核四项打回全落——①§3补两条重构类必填条款(禁为机检文本保留无效成员/拆分边界纪律四要件,判据权威=code-conventions §1.1);②S13数字簇实测更正max-class-variables 32→31(baseline:11),连带§1/§9L3#1升32/§13/T4四处;③删对照表行4推定est≤0死断言(clampi兜底下数学不可达,正中test-conventions§8.4多层防御禁则),推定有效性归零尺寸前置;④推定读数对拍不一致处置句=§9L3#2呈报禁实现侧改锚 2026-11-01 01:53:22 +08:00
DKChan 92e9c70a8d 合并(表批注误删恢复批·用户令): 18 张数值表批注全量恢复——10-31 清理批系对用户指令的误读(文档内容误读为代码配置文件批注),经用户令回滚;恢复源=263050ad,feats 以现行内容回插原位 11 行,逐表校验内容行与现行零变化 2026-11-01 01:42:57 +08:00
DKChan 4a2ed96182 工程(表批注误删恢复批·用户令): 18 张数值表 ; 批注全量原样恢复——10-31 表批注清理批系对用户指令的理解错误(用户「表批注相关的东西放到 git 记录」指文档内容,误读为代码配置文件批注并经错误框架的选项确认执行),经用户令全量回滚;恢复源=263050ad(删除前末版),其中 feats.tres 以现行内容(迭代46卡A换图系七件新增行)为基底回插原位 11 行批注,adventure/battle_layout 原本无批注零恢复;逐表校验=内容行与合回现行零变化、仅批注行差异 2026-11-01 01:42:22 +08:00
DKChan 33525d19de 写卡(棋盘格边长口径收口批·v2.0重立): 2026-11-01用户裁锚点换代——可读性锚点=用户验收的现行渲染值(基座档14×14=28px达标),battle.md可读下限30px句与探针36px人造阈值全部退役(MIN_CELL手抄常量删除,全仓禁第二份阈值抄本);探针判据换锚=实读cell_size与单点算式_cell_for严格相等+结构钳制[26,46]+13/14推定留痕(≤0退化判据,非阈值);v1的探针门槛36→30方向作废,WIP三件复用判定=算式单点/四档冒烟原样复用+探针件返工;§2现状重锚2026-11-01(算例表已随1cdef6ba退役/布局事实源=battle_layout.tres五字段/实测读数40/33/30/28与46顶格为锚点);同步面battle.md:144退役走反哺点通道(归文档批);零待裁点,U0开工门 2026-11-01 01:39:59 +08:00
DKChan 3499638dee merge(迭代47编排): 营地交互重做卡④营地场景图新建批+卡⑤ESC浮窗与营地顶栏批同编迭代47——整批≈14.25个工作日经用户裁授带日期特批标记;待办清单条目状态追加已编迭代47;流水线闸PASS 2026-11-01 01:18:21 +08:00
DKChan e075f92e0f 编排(迭代47·营地交互重做卡④⑤整批): 卡④营地场景图新建批(≈10.5日)+卡⑤ESC浮窗与营地顶栏批(≈3.75日)同编迭代47,执行序卡④→卡⑤,合计≈14.25个工作日超10日经用户裁整批并授带日期特批标记;待办清单「营地交互重做·五张需求卡片」条目状态追加「已编迭代47」(已写卡段与卡片路径原样保留);流水线闸PASS(迭代总表47行/卡片路径8条全在场) 2026-11-01 00:13:11 +08:00
DKChan 34bb62a55b 合并(迭代46·拆批): 卡A换图系双基座七件批+卡B站桩名字行提档与地台色合并批合回——独立验收审计通过零缺漏(对照表V1-V12与卡B六行逐行核·八套件自跑全绿·削弱全申报面);卡C棋盘格边长口径收口批经用户裁拆批退回写卡段(触发卡片L3#2停机点,停机呈报+待办缺口在案);合回后post-merge钩子自动跑CI全量域闸 2026-10-31 18:36:13 +08:00
DKChan ceb816cd39 merge(master): 车道恢复批并入(worktree铁律回归+pre-commit直改闸退役,94628fca) 2026-10-31 18:34:33 +08:00
DKChan eb5dc1e7cb iter46-收尾 2026-10-31 15:13:32 +08:00
DKChan aba54ab228 iter46-卡2:名字行提档FS_HINT14+地台色合并: 退役两常量改指BRICK/PLAZA_EDGE+golden三张0.00%留痕 2026-10-31 15:13:32 +08:00
DKChan 3fa99583fb iter46-卡1:换图系双基座七件批: 七件实装+全局damage_taken统一+BattleRefreshFlow拆件+词表扩签+数据37行——代码域闸全绿 2026-10-31 15:13:32 +08:00
53 changed files with 2323 additions and 203 deletions

No files matched your search

+3 -3
View File
@@ -16,7 +16,7 @@
## 运行与验证(工作目录 = 含 project.godot 的切片目录)
- **合回验证 = 本地 CI(2026-10-30 用户裁)**:git merge 合回 master 落地后,post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh` 跑一次全量域闸(调 `tools/run_all_gates.sh` 不带参数档,02 全套件),结果落 `game/tests/.gates/ci-status.json`(commit/绿红/红闸名单+关键日志行/耗时)+`ci-history.jsonl` 流水账;CI 红态下每个修复提交经 post-commit 钩子(`tools/hooks/post-commit`)自动追绿复验,绿态与无状态零触发——批内提交零钩子检查、合回只验一次,同一份字节禁止第二次人手跑。CI 红不回滚合并,红项在案、修复提交追绿;开工先读 CI 状态,红未修不开新批。跑批起手自动 `--editor --quit` 刷类名缓存(.godot 缓存不入 git,防合并后失配假红)。`tools/run_all_gates.sh` 仍是全部验证闸的**唯一入口与唯一清单,域划分与选闸算法只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单;`--code`/`--docs`/`--quick` 手动档只服务开发期按域跑批(开发期窄测纪律见 process-conventions §6),不构成验收;全量回归由用户自发发起
- **合回验证 = 本地 CI(2026-10-30 用户裁)**:git merge 合回 master 落地后,post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh` 跑一次全量域闸(调 `tools/run_all_gates.sh` 不带参数档,02 全套件),结果落 `game/tests/.gates/ci-status.json`(commit/绿红/红闸名单+关键日志行/耗时)+`ci-history.jsonl` 流水账;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(红态修复走新开修复 worktree,`fix-<任务名>` 分支,免重塑免数卡;post-commit 钩子保留为红态主区直提兜底触发,绿态与无状态零触发)——批内提交零钩子检查、合回只验一次,同一份字节禁止第二次人手跑。CI 红不回滚合并,红项在案、修复合回追绿;开工先读 CI 状态,红未修不开新批。跑批起手自动 `--editor --quit` 刷类名缓存(.godot 缓存不入 git,防合并后失配假红)。`tools/run_all_gates.sh` 仍是全部验证闸的**唯一入口与唯一清单,域划分与选闸算法只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单;`--code`/`--docs`/`--quick` 手动档只服务开发期按域跑批(开发期窄测纪律见 process-conventions §6),不构成验收;全量回归由用户自发发起
- 单件直跑(调试期最窄确认用,**不构成验收**):冒烟测试 `python tools/run_smoke.py`(`game/tests/smoke_boot.tscn` 极小启动场景)· 系统测试 `python tools/run_system_test.py`(宿主 `game/tests/system_test.gd`)· 场景探针 `…console.exe --headless --path . res://probe/ui_probe.tscn` · 单元与集成 `… --script res://tests/run_all.gd`
- 调参批量:`--script res://sim/adventure_monte_carlo.gd`(全冒险期望)· `res://sim/reward_expectation.gd`(奖励期望);关卡完成率/难度类测量一律走全冒险
- 数值验证:`python tools/balance_verifier.py`(采集端 `res://sim/verifier_batch.gd`,报告与原始数据落 `sim-data/`)
@@ -93,11 +93,11 @@
## 硬性红线(勿违反;操作细则在 process-conventions.md 与各规约)
- 车道铁律(2026-10-31 用户裁恢复)——主工作区只作合并落点,一切开发/设计改动走独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`(建区即跑 `tools/setup_worktree_userdir.sh`),收口=冻结→件核→并 master→重塑→主区 `git merge --no-ff`→CI 全量绿→删区,CI 红回任务 worktree 修复合回;主区唯一合法写动作=合并,agent 侧由 worktree-guard 拦主区 Edit/Write 与非 merge 提交;豁免面仅 `.zcode/`、`.qoder/`(不入 git,主区直改);commit 点零钩子检查(pre-commit 直改闸已退役),全量验证归合回后本地 CI(`tools/hooks/post-merge` 触发,见「运行与验证」首条);`--no-verify` 仅属用户,agent 禁用(细则见 process-conventions §1)
- 车道铁律(2026-10-31 用户裁恢复)——主工作区只作合并落点,一切开发/设计改动走独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`(建区即跑 `tools/setup_worktree_userdir.sh`),收口=冻结→件核→并 master→重塑→主区 `git merge --no-ff`→删区(合回即删,2026-09-19 原裁),CI 红不回滚合并——新开修复 worktree(`fix-<任务名>` 分支,免重塑免数卡)修复合回追绿;主区唯一合法写动作=合并,agent 侧由 worktree-guard 拦主区 Edit/Write 与非 merge 提交;豁免面仅 `.zcode/`、`.qoder/`(不入 git,主区直改);commit 点零钩子检查(pre-commit 直改闸已退役),全量验证归合回后本地 CI(`tools/hooks/post-merge` 触发,见「运行与验证」首条);`--no-verify` 仅属用户,agent 禁用(细则见 process-conventions §1)
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
- 合回后验证 = 本地 CI 全量域闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。文档面批与代码面批验收同走合回 CI 全量(`--docs` 仅属开发期手动档,process-conventions §1)
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复提交追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令走独立 worktree 直改+合回(车道铁律,commit 点零钩子检查);大体量改动(含触及代码面的工程改动如纯测试维护)同车道无特殊通道;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复合回追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令走独立 worktree 直改+合回(车道铁律,commit 点零钩子检查);大体量改动(含触及代码面的工程改动如纯测试维护)同车道无特殊通道;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI(车道细则=process-conventions §1)
- MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起
- 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6)
+3 -3
View File
@@ -10,8 +10,8 @@
主工作区(master 检出)只作合并落点,一切开发/设计改动禁止直接发生在主工作区(2026-10-31 用户裁恢复;2026-10-30 的「主区直改+直提」车道与 commit 点 pre-commit 直改闸随之退役;验证模型不动——合回后本地 CI 仍是唯一强制验证点):
1. **worktree 车道(一切面)**:每批先建独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`;建区后立刻跑 `tools/setup_worktree_userdir.sh`——给该区独立 Godot user 目录(测试夹具、试玩日志、截图产物不与其他区共用;主区不生成,真实档保持原位)。所有改动与提交都在 worktree 内自由进行——commit 点零钩子检查(`tools/hooks/pre-commit` 已删除)。豁免面仅 `.zcode/`(钩子与配置)与 `.qoder/`(本机 harness 配置)允许主工作区直改——两份实测均不入 git(走 worktree 反而带不进去,改动只对本机生效)
2. **收口顺序(固定)**:**冻结 → 件核 → 并 master → 重塑 → 主区合回 → CI 验证 → 删区**——代码与断言改完即冻结,全仓件核 grep 查残留;master 已被别的批次推进时先在 worktree 内 `git merge master` 解冲突(必须在合回之前,合回后才并=CI 验的是中间态);**重塑**=迭代分支(iter<N> 形)合回前执行 `python tools/reshape_cards.py`,把分支线性史按卡末态快照压成一卡一笔(可选追加一笔纯文档收尾),master 台账由此每卡一行——工具自带「重塑前后树逐字节一致」断言与 backup 分支,不改任何文件内容;非迭代批免重塑;然后主工作区 `git merge --no-ff <任务分支>`——这是主区唯一合法写动作(合回守卫对迭代分支数卡校验,未归卡提交拦下)。**CI 全绿后才 `git worktree remove`+删分支;CI 红不删区——回任务 worktree 修复提交再合,追绿后删区**。游戏类测试(冒烟/单测/MC/探针)批内默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
3. **合回后验证(本地 CI,2026-10-30 用户裁)**:git merge 合回 master 落地后 post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh merge` 对 master 尖跑一次全量域闸——唯一强制全量验证点;CI 红态下每个修复提交经 post-commit 钩子(`tools/hooks/post-commit`)自动追绿复验(钩子仅主工作区生效——修复合回落地即触发),绿态与无状态零触发。重跑判据:同一份字节禁止第二次人手跑;有意义的重跑只剩「验证修复」一类。CI 红不回滚合并——红项记 `game/tests/.gates/ci-status.json`,开工先读 CI 状态,红未修不开新批。跑批起手 `--editor --quit` 刷类名缓存(在册坑:.godot 缓存不入 git,合并后失配假红假绿)。并跑语义=跑完补跑(coalesce):跑批期间新触发不中断当前轮,轮尾对最新尖端补跑,绝不逐笔各跑一轮
2. **收口顺序(固定)**:**冻结 → 件核 → 并 master → 重塑 → 主区合回 → 删区**——代码与断言改完即冻结,全仓件核 grep 查残留;master 已被别的批次推进时先在 worktree 内 `git merge master` 解冲突(必须在合回之前,合回后才并=CI 验的是中间态);**重塑**=迭代分支(iter<N> 形)合回前执行 `python tools/reshape_cards.py`,把分支线性史按卡末态快照压成一卡一笔(可选追加一笔纯文档收尾),master 台账由此每卡一行——工具自带「重塑前后树逐字节一致」断言与 backup 分支,不改任何文件内容;非迭代批免重塑;然后主工作区 `git merge --no-ff <任务分支>`——这是主区唯一合法写动作(合回守卫对迭代分支数卡校验,未归卡提交拦下)。**合回落地即 `git worktree remove`+删分支(合回即删,2026-09-19 原裁);post-merge 钩子已在合回落地时自动触发 CI,红态不回滚合并——新开修复 worktree(`fix-<任务名>` 分支,非 iter<N> 形免重塑免数卡)修复提交再合,落地复验追绿后删修复区**。游戏类测试(冒烟/单测/MC/探针)批内默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
3. **合回后验证(本地 CI,2026-10-30 用户裁)**:git merge 合回 master 落地后 post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh merge` 对 master 尖跑一次全量域闸——唯一强制全量验证点;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(红态修复走新开修复 worktree,`fix-<任务名>` 分支,非 iter<N> 形免重塑免数卡;post-commit 钩子保留为红态主区直提兜底触发,绿态与无状态零触发)。重跑判据:同一份字节禁止第二次人手跑;有意义的重跑只剩「验证修复」一类。CI 红不回滚合并——红项记 `game/tests/.gates/ci-status.json`,开工先读 CI 状态,红未修不开新批。跑批起手 `--editor --quit` 刷类名缓存(在册坑:.godot 缓存不入 git,合并后失配假红假绿)。并跑语义=跑完补跑(coalesce):跑批期间新触发不中断当前轮,轮尾对最新尖端补跑,绝不逐笔各跑一轮
4. **执法分层(全机械,零自觉依赖)**:①`.zcode/hooks/worktree-guard.sh`(PreToolUse,本机件不入 git)——agent 侧:主工作区 Edit/Write 与主区非 merge `git commit` 拦截(豁免 `.zcode/`、`.qoder/`;worktree 内目标快速放行;合并收尾提交凭 MERGE_HEAD 豁免)、`git commit --no-verify` 拦截(该旗标仅属用户)、iter<N> 分支合回的一卡一提交数卡校验、规则闸(写 design//game/ 前核对会话规约读取记录)。②post-merge/post-commit 钩子——本地 CI 触发(仅主工作区;任务 worktree 内的合并不触发)。③git 层不设车道钩子(pre-commit 直改闸已退役)——用户手工改动不受限;ZCode 层只约束 agent(子代理不经守卫层)
## 2. 五段流水线
@@ -49,7 +49,7 @@
**完成**
8. **合回后 CI 验证(唯一强制验证点)**——主工作区 `git merge --no-ff <任务分支>` 落地后 post-merge 钩子自动跑全量域闸(不带参数档,02 全套件),判定与红项落 `game/tests/.gates/ci-status.json`。全量是唯一强制档:跨卡触面、探针与未申报套件的消费漏网只有全量闸能抓(迭代 44 实证),关联档选测的盲区由它兜底。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分(含选闸算法与套件族映射表)**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。开发期手动跑批按需选档(按文件性质判域:代码面→`--code`,文档件→`--docs`,两类都有→全量;`--code` 关联档选测——02 只跑「需求卡片申报 ∪ diff 推导」并集套件、00 冒烟/01 系统测试/09 静态扫描三件保险恒跑、横切面回落全量——算法见闸脚本),手动跑不构成验收。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占 CI 全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;闸真崩溃时 CI 判红在案(跑不出回执=verdict 红,fail-closed 语义保留)
9. **重跑语义(2026-10-30 用户裁,取代原「每批一次/合并后零复跑」口径)**——同一份字节禁止第二次人手跑;允许的重跑只剩两类:① CI 红后的修复提交(post-commit 钩子自动追绿复验,每笔修复至多触发一轮);② 用户自发发起的全量回归。「跑都跑了再跑一遍保险」「顺手改点再补一跑」都不构成重跑理由。类名缓存刷新与开机确认由 CI 跑批起手接管(§1 第 2 条),合并后无手工动作
9. **重跑语义(2026-10-30 用户裁,取代原「每批一次/合并后零复跑」口径)**——同一份字节禁止第二次人手跑;允许的重跑只剩两类:① CI 红后的修复合回(post-merge 钩子复验,每轮修复至多触发一轮);② 用户自发发起的全量回归。「跑都跑了再跑一遍保险」「顺手改点再补一跑」都不构成重跑理由。类名缓存刷新与开机确认由 CI 跑批起手接管(§1 第 2 条),合并后无手工动作
10. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §12
11. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,**另禁写 `game/tests/README.md`(能力清单,归写卡段维护,test-conventions §8.9)**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务+实现决定记录——超白名单/超卡片明列范围事项逐条记「决了什么/为什么/代价/撤销方式」,全部按卡执行时只写一行「零拍板」)、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由工程/文档设计改动批(直改类)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归工程/文档设计改动批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注
12. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置
+1 -1
View File
@@ -27,7 +27,7 @@
## 4. 验证时机(合回后本地 CI 制,2026-10-30 换代)
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6)。全量验证归合回后的本地 CI:git merge 合回 master 触发 post-merge 钩子跑 `tools/run_all_gates.sh` 不带参数档(两域全量,02 全套件——跨卡触面与关联档选测的盲区由全量兜底),结果落 `game/tests/.gates/ci-status.json`;CI 红态下修复提交经 post-commit 钩子自动追绿。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6)。全量验证归合回后的本地 CI:git merge 合回 master 触发 post-merge 钩子跑 `tools/run_all_gates.sh` 不带参数档(两域全量,02 全套件——跨卡触面与关联档选测的盲区由全量兜底),结果落 `game/tests/.gates/ci-status.json`;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(修复走新开 `fix-<任务名>` 修复 worktree,process-conventions §1)。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
行为探针使用随机化玩家行为(封印/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。
## 5. 测试函数怎么写——验收清单条目的落码规范
+29 -12
View File
@@ -93,11 +93,29 @@ func request_dig(chain_size: int, clears_map: bool) -> bool:
_reject("phase")
return false
var cost := _cost("dig")
# dig_paid 发射+施加(付费开掘发起、动作经济闸收对价前:
# dig_paid 发射+施加(付费开掘发起、动作经济闸收对价前——施加单点 _apply_dig_paid_hits):
# counter_spend 命中=尝试加收对价并积计数;资源不足走 fallback/耗尽即止=只付常规基础对价。
# counter_add〔双管镐 付费挖格计数〕/ grant_ap〔每第 2 次返 1 AP——对价减免,
# mod_eq 闸在行 when〕同面施加)
var apply_res := _apply_dig_paid_hits(cost)
cost = int(apply_res["cost"])
if _ap < cost:
_reject("resource_face")
return false # 拒绝轮零入池(矿脉储能只在扣费受理后兑付——C2 成功才登记同款纪律)
if int(apply_res["refund"]) > 0:
cost = maxi(0, cost - int(apply_res["refund"])) # 返还=对价减免(净费不低于 0——免费开格不产生负对价)
var accepted := _spend_cost_and_arbitrate(cost, clears_map)
if accepted and int(apply_res["rap_gain"]) > 0:
grant_reserve_ap(int(apply_res["rap_gain"])) # 每次受理的付费挖格产 RAP(一次性入池——每动作恰一挖,连锁不二次)
return accepted
# dig_paid 命中施加单点(迭代 46 卡A T3 自 request_dig 迁出——分支本体逐字保留:
# counter_spend 加收对价并积计数/counter_add 施加〔双管镐 付费挖格计数〕/
# grant_ap 对价减免〔每第 2 次返 1 AP——mod_eq 闸在行 when〕/grant_reserve_ap 矿脉储能累计;
# 命中序=声明序不变。返回 {cost, refund, rap_gain} 交受理面消费)
func _apply_dig_paid_hits(base_cost: int) -> Dictionary:
var cost := base_cost
var dig_refund := 0
var rap_gain := 0
if _fx != null:
for h in _fx.dispatch(_ctx, "dig_paid", {"ap_cost": cost, "ap_surplus": _ap - cost}):
if String(h.get("kind", "")) == "counter_spend":
@@ -110,12 +128,9 @@ func request_dig(chain_size: int, clears_map: bool) -> bool:
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
elif String(h.get("kind", "")) == "grant_ap":
dig_refund += int(h.get("value", 0))
if _ap < cost:
_reject("resource_face")
return false
if dig_refund > 0:
cost = maxi(0, cost - dig_refund) # 返还=对价减免(净费不低于 0——免费开格不产生负对价)
return _spend_cost_and_arbitrate(cost, clears_map)
elif String(h.get("kind", "")) == "grant_reserve_ap":
rap_gain += int(h.get("value", 0))
return {"cost": cost, "refund": dig_refund, "rap_gain": rap_gain}
func _spend_cost_and_arbitrate(cost: int, clears_map: bool) -> bool:
@@ -196,13 +211,15 @@ func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
# 轮内刷新:唯一入口 =
# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算
# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。
func request_refresh() -> bool:
# cost_override=折后价入参(迭代 46 卡A T3——BattleRefreshFlow 折价读数传入;缺省 -1=表基线逐位不变)
func request_refresh(cost_override: int = -1) -> bool:
if _phase != Phase.ACTIVE:
_reject("phase")
return false
if _rap < db.run.action_economy.rap_refresh_cost:
var cost := int(db.run.action_economy.rap_refresh_cost) if cost_override < 0 else cost_override
if _rap < cost:
return false # EC13 资源不足拒绝——合法相位内资源拒绝,非编排错误
_rap -= db.run.action_economy.rap_refresh_cost
_rap -= cost
return true
+9 -17
View File
@@ -31,8 +31,9 @@ signal settlement_snapshot(snap: Dictionary) ## 每结算末尾的全量状态
const SOURCE_EXEMPT_TYPES := ["decay", "verdict"]
# 行来源断言豁免集合(来源类别层)——事实来源是单位 / 清图而非棋盘格;
# fx_reveal=专长揭示掷格(落空行无真实格);
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指)
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "fx_reveal", "fx_grant_gp"]
# fx_grant_gp=专长·GP 入账(钱包面而非棋盘面,行无格子可指);
# fx_board_refresh=成功刷新事实族(迭代 46 卡A——崩壁冲击群伤行源=换图动作而非棋盘格)
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "fx_reveal", "fx_grant_gp", "fx_board_refresh"]
var _facts: BattleFacts # 战斗事实件(battle_facts.gd——事实族 15 成员单点)
var _rounds: RoundCycle # 轮循环驱动件(settlement/round_cycle.gd——五段持有/段序/重入闸/摇号)
@@ -596,26 +597,17 @@ func _exit_distribution() -> void:
round_started.emit(round_index)
# 呈现层查询面(HUD 刷新按钮可用性)——挖掘期唯一通道
# 呈现层查询面(HUD 刷新按钮可用性)——挖掘期唯一通道(折价成本同源:判据归 BattleRefreshFlow)
func can_refresh() -> bool:
return battle_result == "" \
and _facts.refresh_used < int(db.run.action_economy.refresh_cap_per_round) \
and action_economy.phase == action_economy.PHASE_ACTIVE \
and action_economy.rap >= int(db.run.action_economy.rap_refresh_cost)
return BattleRefreshFlow.can_refresh(self)
# 玩家经 HUD框架发起、战斗结算 代理调用(禁止 RAP 满自动刷)。
# 挖掘期唯一通道:Active 相位立即换图
# 继续挖(RAP -8;AP 保留、相位不动、不触发结算);非挖掘期请求一律拒绝——非清图轮恒同图。
# 挖掘期唯一通道:Active 相位立即换图继续挖(RAP 按折后价扣;AP 保留、相位不动、不触发结算);
# 非挖掘期请求一律拒绝——非清图轮恒同图。刷新门/折价扣费/换图/成功事实归 BattleRefreshFlow
# (迭代 46 卡A T3 拆出——编排器入口与唯一通道不变,C10 结构预算只降不升)。
func player_request_refresh() -> bool:
if not can_refresh():
return false
if not action_economy.request_refresh():
return false
_facts.refresh_used += 1
_reward_economy.retire_mapping() # 旧图批次退役(地图战斗资源 Ready→retired;E6 新批重置前提)
minesweeper.retire_board(true) # 旧板退役(轮内通道)
return _install_and_reset(true) # 当场换图——失败即编排违例 fail-fast
return BattleRefreshFlow.execute(self)
func _battle_over(result: String) -> void:
+28 -10
View File
@@ -460,8 +460,8 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary
# 敌方伤害技:掷点 → 折扣/暴击 → 克制点数(attack_type vs 主角护甲类型——
# 无类型恒中性 delta 0,写法同 hero_attack 的 _counter_delta 消费位)→ hero_defense×enemy_output
# 池乘区 → 下限 → +rider(后置必中附加)→ 交付 → commit_action(作废单不 commit——死亡即不动手)。
# 掷点算术归 _enemy_roll;damage_taken 施加面归 _enemy_shield_apply(发射在本函数——
# 与减伤落快照的先后序固定)。
# 掷点算术归 _enemy_roll;damage_taken 统一发射+消费归 _emit_hero_damage_taken(发射在本函数——
# 与减伤落快照的先后序固定;「掷点后、交付前」消费时点不变)。
func _resolve_enemy_skill(t: Dictionary) -> void:
var hero: Dictionary[Variant, Variant] = units.hero_unit()
var roll := _enemy_roll(t)
@@ -469,9 +469,8 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
# damage_taken 发射(kind=enemy_skill;铁壁整备减伤订阅面:shield 加法族合计 value×per(charge_layers),
# 稳健拓荒树规则同面(rebuild 合成订阅);减后过下限保底。damage_reduce〔背水 hp_ratio when〕/
# counter_add〔保险丝/石肤〕/ac_bonus〔石肤〕同面施加;hp_ratio fact 由 _fx_fact 补充事实面回落提供)
var dt_hits := _fx_hits("damage_taken", {"kind": "enemy_skill", "amount": total,
"source": String(t.context.get("skill_id", "")), "round": int(t.round_index)})
total = int(_enemy_shield_apply(dt_hits, total)["total"])
var dt_res := _emit_hero_damage_taken(total, "enemy_skill", String(t.context.get("skill_id", "")), t.origin_cell, int(t.round_index))
total = int(dt_res["total"])
units.apply_damage(hero, total)
var payload: Dictionary[Variant, Variant] = {"target": "hero", "skill_id": String(t.context.get("skill_id", "")), "baseline": int(t.context.get("baseline", 0)),
"roll": int(roll["roll"]), "ac": int(roll["threshold"]), "tier": int(roll["discount"]), "attack_type": String(t.attack_type)}
@@ -502,9 +501,22 @@ func _enemy_roll(t: Dictionary) -> Dictionary:
return {"roll": int(roll_read.roll), "threshold": threshold, "crit": crit, "discount": discount, "total": total}
# damage_taken 施加面(shield 加法族合计;damage_reduce 逐条减量〔背水〕;ac_bonus 转编排器单点
# 〔石肤 AC 累积归 set_hero_ac_override 消费面〕;counter_add 施加)。返回 {total, shield}。
func _enemy_shield_apply(hits: Array, total: int) -> Dictionary:
# 全局受伤发射单点(迭代 46 卡A T2——所有主角承伤路径唯一发射口:敌方攻击/地形/危险格/错误封印
# 资源直伤(后三者经 board_flat_hero 单据进入)都经此 dispatch 一次 damage_taken 并消费一次;
# 同一伤害单据重复发射由 context.hero_damage_consumed 标记闸拦(地形路径在开单点标记)。
# payload 必含 kind/amount/source/cell/round/hp_ratio;返回 {total, hits} 交调用侧按减后值开单/交付)。
func _emit_hero_damage_taken(amount: int, damage_kind: String, source: String, cell: Vector2i, ri: int) -> Dictionary:
var hu: Dictionary[Variant, Variant] = units.hero_unit()
var payload: Dictionary[Variant, Variant] = {"kind": damage_kind, "amount": amount, "source": source,
"cell": cell, "round": ri, "hp_ratio": int(round(100.0 * float(int(hu.get("hp", 0))) / float(maxi(1, int(hu.get("max_hp", 1))))))}
var hits := _fx_hits("damage_taken", payload)
return {"total": int(_apply_hero_damage_effects(hits, amount)["total"]), "hits": hits}
# damage_taken 唯一消费族(组合次序冻结:shield 加法族合计→damage_reduce 逐条减量〔背水/石层缓冲〕→
# ac_bonus 转编排器单点〔石肤 AC 累积归 set_hero_ac_override 消费面〕→counter_add 施加〔保险丝/石肤/
# 换图系临时计数夹具〕。返回 {total, shield})。
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary:
var shield := 0
for h in hits:
match String(h.get("kind", "")):
@@ -565,9 +577,15 @@ func _resolve_board_blast(t: Dictionary) -> void:
_outcomes[int(t.ticket_id)] = "redeemed"
# 棋盘主角直伤(反噬/治疗包负向/地形变异):无类型必中,下限保底;context 键并入行载荷
# 棋盘主角直伤(反噬/治疗包负向/危险格/错误封印资源直伤):无类型必中,下限保底;context 键并入行载荷。
# damage_taken 全局发射点(迭代 46 卡A T2——单据未标 hero_damage_consumed 时在下限前发射并消费一次;
# 地形路径已在授予段开单点发射并标记,此处跳过防重复)
func _resolve_board_flat_hero(t: Dictionary) -> void:
var dealt: int = maxi(int(db.combat.settlement.damage_roll.damage_floor), int(t.base_value)) # 伤害下限全来源
var dealt: int = int(t.base_value)
if not bool((t.get("context", {}) as Dictionary).get("hero_damage_consumed", false)):
var res_flat := _emit_hero_damage_taken(dealt, "board_flat_hero", String(t.get("source_class", "")), t.origin_cell, int(t.round_index))
dealt = int(res_flat["total"])
dealt = maxi(int(db.combat.settlement.damage_roll.damage_floor), dealt) # 伤害下限全来源(减后过下限)
units.apply_damage(units.hero_unit(), dealt)
var extra: Dictionary[Variant, Variant] = {"target": "hero"}
for k in t.context.keys():
+12 -4
View File
@@ -12,11 +12,15 @@ extends RefCounted
const EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "attack_commit", "clamp_check",
"armor_roll", "kill", "damage_taken", "enemy_hp_threshold", "round_start", "seal_settle",
"round_end", "reward_roll", "treasure_used", "charge_full", "dig_stop", "battle_won", "seal_missed",
"board_installed"]
"board_installed",
# 换图系双事件(refresh_request=玩家请求刷新〔省力周转 apply_discount 订阅面——折扣只改门槛
# 与扣费不改拒绝判据〕;board_refreshed=成功刷新事实〔换图系七件订阅主挂点——装新板后才发射〕)
"refresh_request", "board_refreshed"]
## 扫雷侧事件集(🔴 流派扫雷触发铁律新形态:每流派 ≥1 行订阅本集事件——schema 加载校验。
## 原 MINE_HOOKS 的同义迁移:挖掘/封印/轮末结算面事件)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_settle", "round_end", "reward_roll"]
## 原 MINE_HOOKS 的同义迁移:挖掘/封印/轮末结算面事件+轮内刷新面〔换图系〕)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_settle", "round_end", "reward_roll",
"refresh_request", "board_refreshed"]
## 效果类型词汇(解释器 switch 闭集——词表实数以 EFFECT_KINDS 常量为准,含共享行拆型:
## grant_gp/bias_wallet、counter_add/counter_spend、reveal_tiles/remove_mines/refresh_pick、
@@ -40,7 +44,11 @@ const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open
"mine_count_preview",
# forgive_seal_miss=完美保险(本场首次封印落空不计入完美
# 封印判定——订阅 seal_missed 的豁免效果,消费点在授予段;feats.tres 首例 domain=battle 行)
"forgive_seal_miss"]
"forgive_seal_miss",
# 换图系两效果(grant_reserve_ap=矿脉储能·付费挖格产 RAP〔消费点在行动经济 dig_paid
# 受理后一次性入池〕;apply_discount=省力周转·刷新成本折扣〔mode=refresh cost_mult 百分比,
# floor 取整——只改门槛与扣费、不改拒绝判据〕)
"grant_reserve_ap", "apply_discount"]
## fact 条件词汇(封闭枚举——payload 同名键直读面〔fact 取值序 payload
## 优先——effect_engine._fact_value 既有机制〕。new_resource_type=首开资源类型〔地质学家〕;
+6
View File
@@ -365,6 +365,12 @@ func outcome_of(ticket_id: int) -> String:
return "redeemed" if ticket_id < 0 else damage.outcome(ticket_id)
# 延迟单据入队(迭代 46 卡A T4——成功刷新的崩壁冲击群伤单走案C 同队列:授予段闸外唯一入口,
# 战斗段末 _board_deferred_settle 按开单序补提交统一结账;类变量零增员——复用 defer.queue)
func defer_ticket(t: Dictionary) -> void:
(defer.queue as Array).append(t)
# 主角直伤单据(反噬/治疗包负向——无类型必中,下限保底在控制器管线内)
func _submit_flat_hero(display: String, power: int, att: Dictionary, sync: bool) -> String:
var t := damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, String(att.get("source_class", "")), display,
@@ -0,0 +1,43 @@
class_name BattleRefreshFlow
extends RefCounted
## 换图刷新流程件(迭代 46 卡A T3/T4 自编排器拆出):刷新可用性门、折价扣费、换图执行与成功事实登记。
## 拆分边界(需求卡片 C13):本件只负责可用性、扣费、换图与成功事实登记——七件效果的算法留在
## 效果引擎、授予段与伤害控制器。调用路径=呈现层→编排器单行转发→本件→行动经济/装板壳/授予段。
## 反自动刷新(C11):只经玩家入口发起,禁 RAP 满自动刷——编排器公开面单行转发,无自动调用点。
## 成功序钉死(T4):折价扣 RAP→refresh_used+1→旧资源退役→旧板退役→装新板→授予段成功事实发射;
## 装板失败返回 false 且零效果——成功事实只在新板就位后发射。
## 折价成本读数(refresh_request 命中省力周转 apply_discount——只认 kind==apply_discount 且
## mode==refresh,取声明序第一命中,floor(base×cost_mult/100);无命中=表基线逐位不变。
## can_refresh 与 execute 同源取数〔V3——can 与执行同源判断〕;折扣只改变门槛与扣费,
## 不改变拒绝判据〔C2——每轮上限与相位判据照旧〕)
static func _effective_refresh_cost(oc: BattleOrchestrator) -> int:
var base := int(oc.db.run.action_economy.rap_refresh_cost)
for h in oc.fx_host().fx_emit("refresh_request", {"mode": "refresh", "base": base}):
if String(h.get("kind", "")) == "apply_discount" and String(h.get("mode", "")) == "refresh":
return int(floor(float(base) * float(int(h.get("cost_mult", 100))) / 100.0))
return base
## 可用性门(HUD 刷新按钮同源判据):战斗未终局、每轮上限未满、Active 相位、RAP 抵折后价。
static func can_refresh(oc: BattleOrchestrator) -> bool:
return oc.battle_result == "" \
and oc._facts.refresh_used < int(oc.db.run.action_economy.refresh_cap_per_round) \
and oc.action_economy.phase == oc.action_economy.PHASE_ACTIVE \
and oc.action_economy.rap >= _effective_refresh_cost(oc)
## 执行(玩家请求刷新唯一通道体):扣费受理后继续;扣费拒绝/门拒零效果零事实。
static func execute(oc: BattleOrchestrator) -> bool:
if not can_refresh(oc):
return false
if not oc.action_economy.request_refresh(_effective_refresh_cost(oc)):
return false
oc._facts.refresh_used += 1
oc._reward_economy.retire_mapping() # 旧图批次退役(地图战斗资源 Ready→retired;E6 新批重置前提)
oc.minesweeper.retire_board(true) # 旧板退役(轮内通道)
if not oc._install_and_reset(true): # 当场换图——失败即编排违例 fail-fast(零成功事实)
return false
oc._rounds._grant_seg.apply_board_refreshed(oc.round_index) # 成功事实:新板就位后一次发射
return true
@@ -0,0 +1 @@
uid://d2kjxlt5b2eqv
+50 -24
View File
@@ -6,7 +6,8 @@ extends RefCounted
# 效果施加归 BattleFxHost(段持三引用直调,零转发壳)——
# 本段只搬运时序,零算法改动(🔴 零行为变更契约:同种子完整局输出逐位一致)。
# run 编排骨架+批私有族(_reveal_batch/_terrain_batch/_seal_batch/_round_end_apply,段内施加面
# 再拆 _reveal_safe_apply/_reveal_mine_apply/_terrain_damage_reduce/_seal_hit_effects 等单面函数);
# 再拆 _reveal_safe_apply/_reveal_mine_apply/_seal_hit_effects 等单面函数);地形 damage_taken
# 消费随迭代 46 卡A T2 并入伤害控制器全局消费族(本段零本地减伤函数);
# kind 分派单点化(P1-1 同批):counter_add 五位三行组 / grant_material 三位兑付块
# 收 _apply_counter_hit / _apply_material_hit 两助手(分支本体逐字保留在助手内)。
@@ -32,6 +33,27 @@ func run(ri: int) -> void:
_round_end_apply(ri)
# 成功刷新事实发射+施加(迭代 46 卡A T4——BattleRefreshFlow 装新板后单点调用,每成功刷新恰一次;
# 拒绝刷新零事实〔C2〕。命中消费三面:counter_add→既有单点落账〔蓄力周转/石层缓冲/坑道小憩 计数〕;
# grant_gp→战斗内缓冲 escrow 入账+fx_grant_gp 行〔矿道红利——不绑清图奖励,C7〕;
# open_ticket channel=blast→崩壁冲击群伤单延迟入队〔案C 同队列,战斗段末按存活目标结账〕)
func apply_board_refreshed(ri: int) -> void:
var br_hits := fx_host.fx_emit("board_refreshed", {"round": ri})
for h in br_hits:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
for h in br_hits:
if String(h.get("kind", "")) == "grant_gp":
reward_grant.escrow += int(h.get("value", 0))
oc._emit_row({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
for h in br_hits:
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "blast":
var bb_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_board_refresh", "崩壁冲击",
{"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": Vector2i(-1, -1)})
reward_grant.defer_ticket(bb_t)
# 开格/踩雷兑付批(轮 facts_reveals 资源事件序:safe 正向乘区兑付 / mine_hit 减半负向兑付)
func _reveal_batch(ri: int) -> void:
for evt in oc._facts.reveals:
@@ -167,37 +189,22 @@ func _terrain_batch(ri: int) -> void:
if String(vrow.get("effect", "")) != "flat_damage":
continue # 触发词表按 effect 路由:flat_damage=on_open 兑现;hide_number/on_open 外组合 = 数据行新词表未接线(显式忽略面)
# 地形变异伤害单据——无类型必中,下限保底在控制器管线内
# damage_taken 发射在单据开立前:damage_reduce〔背水——hp_ratio when
# 面〕先收集再按减后值开单;counter_add〔保险丝/石肤〕+ ac_bonus〔石肤〕照常施加;
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化)
# damage_taken 统一发射(迭代 46 卡A T2——地形路径并入全局消费族 _emit_hero_damage_taken:
# 发射在单据开立前、减后值开单,组合消费含 shield〔全局受伤语义〕;
# hero_damage_consumed 标记入单据 context——战斗段末 _resolve_board_flat_hero 跳过重复发射;
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化))
var dt_amount := int(vrow.get("effect_value", 0))
var dt_hits := fx_host.fx_emit("damage_taken", {"kind": "terrain", "amount": dt_amount,
"source": String(vid), "cell": evt.cell, "round": ri, "terrain_kind": String(vid),
"hp_ratio": int(round(100.0 * float(int(oc._enemy_system.hero_unit().get("hp", 0))) / float(maxi(1, int(oc._enemy_system.hero_unit().get("max_hp", 1))))))})
dt_amount = _terrain_damage_reduce(dt_hits, dt_amount)
var dt_res := oc._damage._emit_hero_damage_taken(dt_amount, "terrain", String(vid), evt.cell, ri)
dt_amount = int(dt_res["total"])
var vt := oc._damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)),
{"channel": "board", "base_value": dt_amount, "target_rule": DamageController.TARGET_HERO,
"ri": ri, "origin_cell": evt.cell, "context": {"variant_id": String(vid)}})
"ri": ri, "origin_cell": evt.cell, "context": {"variant_id": String(vid), "hero_damage_consumed": true}})
reward_grant.submit(vt, false)
for h in dt_hits:
for h in (dt_res["hits"] as Array):
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, evt.cell, ri)
# damage_taken 施加面(damage_reduce 减量收集〔背水——定值〕/ac_bonus〔石肤〕/counter_add〔保险丝/石肤〕;
# 返回减后 amount 交开单消费)
func _terrain_damage_reduce(dt_hits: Array, dt_amount: int) -> int:
for h in dt_hits:
match String(h.get("kind", "")):
"damage_reduce":
dt_amount = maxi(0, dt_amount - int(h.get("value", 0)))
"ac_bonus":
fx_host.apply_ac_bonus(h)
"counter_add":
_apply_counter_hit(h)
return dt_amount
# 封印判定批+战封印军规开单段(封印流:seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
# 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌——
# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。
@@ -259,6 +266,7 @@ func _seal_strike_value(fs_fx: Array, fs_hits: int, fs_misses: int) -> Dictionar
func _round_end_apply(ri: int) -> void:
var re_fx := fx_host.fx_emit("round_end", {"round": ri})
_round_end_blast_tickets(re_fx, ri)
_round_end_heal_value_counter(re_fx, ri) # 迭代 46 卡A T5——坑道小憩恢复兑付(先于清零行消费层值,声明序=heal 行在 counter set 行前)
for h in re_fx:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
@@ -278,6 +286,24 @@ func _round_end_apply(ri: int) -> void:
oc._damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
# round_end heal value_counter 兑付(迭代 46 卡A T5——坑道小憩:层值=本轮成功刷新累计恢复点,
# units.heal 封顶、满血发未生效行;层清零由同事件 counter_add set 行承载〔声明序在后〕——本面
# 先于 counter_add 遍历消费层值即保序。行源=成功刷新事实族(无棋盘格——fx_board_refresh 豁免类))
func _round_end_heal_value_counter(re_fx: Array, ri: int) -> void:
for h in re_fx:
if String(h.get("kind", "")) != "heal" or String(h.get("value_counter", "")) == "":
continue
var layers := EffectEngine.counter(fx_host.counters_ctx, String(h.get("value_counter", "")))
if layers <= 0:
continue
var hu: Dictionary[Variant, Variant] = oc._enemy_system.hero_unit()
if int(hu.get("hp", 0)) >= int(hu.get("max_hp", 1)):
oc._emit_row({"type": "reward_ineffective", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 未生效(满血)", 0, {"batch_key": String(h.get("value_counter", ""))})
continue
var healed: int = oc._enemy_system.heal(hu, layers)
oc._emit_row({"type": "heal", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 回复", healed, {"target": "hero"})
# round_end blast 通道反打单(per-counter 积层读数×value=反打总量;source_class 用中性名,
# per-counter 反打通道保留泛用面;案C 暂存战斗段末兑付)
func _round_end_blast_tickets(re_fx: Array, ri: int) -> void:
+15
View File
@@ -11,3 +11,18 @@ rap_refresh_cost = 8
refresh_cap_per_round = 2
move_ap_cost = 0
chain_ap_cost = 0
; AP 压速批(2026-09-16 用户拍板):ap_per_round 8→6——无伤通关根因诊断(playtest_5678/1234 实录:
; 第 1 轮 8 AP 全封印 = 8 份正向兑付 + 16 魔法充能,减伤/削弱单轮堆满 → "被减伤归零"零承伤通关)
; 的主阀门:削每轮兑付吞吐(封印 rush 墙/归零轮覆盖率 ~50%→~35%)。单格兑付语义零改动(保留封印命中
; 正向兑付——危险格/安全格概率差异化设计的前提)。rap_refresh_cost 8→6 随动 = 保持 registry 登记
; "默认 = ap_per_round,满停一轮 = 恰好一刷基线节拍"耦合不变式;若有意解耦(刷新相对变贵推清图)
; 须单独立批。减伤幅度下调(-30 → 下调)为同根因配套项,待用户拍板数值后另行落表;
; 终局方案 = 减伤换装护甲等级 AC+1(随 AC 掷点定稿批 EV 中性换装)。
; 观察口径:AP 6 不够再降 4(墙覆盖率 ~19%,代价 = 12×12 板挖掘节奏拖长)。
;
; Package B 数值优化批·首版(2026-09-22——qa-data/实现计划书_PackageB.md §4.2):ap_per_round 6→8、
; rap_refresh_cost 6→8 随动(维持 registry 登记"刷新成本 = 每轮 AP,满停一轮恰一刷"不变式;schema
; 非硬约束,系设计不变式)。上批 8→6 所杀"首轮全封印→减伤削弱堆满→零承伤通关"退化,在 B1 敌人 HP
; 大幅抬升后单轮堆满已无法零承伤通关多轮整场,风险缓解;复测须盯 analyze.py 零承伤轮指标,明显回升
; 则按计划书 §6.5 AP 8→7 回调。dig/seal/move/chain cost 与 refresh_cap 未动(move/chain 结构冻结 0)。
+45
View File
@@ -408,3 +408,48 @@ rows = [{
"shave_ring_radius": 1,
"r0_max_cap": 59
}]
; 切片调参登记(SLICE-TUNED provisional,调参所有权仍归 C3):
; A1 GDD 参考行(d0.10/0.12)经"先布后删"环内移除后仅幸存 2-3 雷、R0/|S|约0.97、残余挖掘 2-4 格——
; 掘近退化板(A2 反例 B 机制的低密度放大)。实验数据(8 种子均值,smoke/experiment_density.gd):
; d0.25 → L1 幸存雷 11/残余 13/inf 0.925/att 2.9 · L2 雷 19/残余 21/inf 0.908/att 7.9(seed42 实测 inf 0.889→target 定 0.88 首过),零降级告警。
; target 收窄至 0.88/0.88(不低于 0.85 绝对锚)为密度上移的对价——B1 条款③⑤对账重跑义务随调参触发。
; 2026-09-11 关卡扩展批:L1 增 D1–D4 行(Boss=Dmax=4)。
; Boss 板以 12×12 区分(d0.28/t0.87 实测达不到推理率门槛——best≈0.81 降级告警,回退 0.25/0.88;
; 密度轴上调留给 C3 与求解器能力一起解)。
;
; 分支节点图批(2026-09-17——L2 幽暗地域板行铺满,全值 provisional 归 难度曲线与Boss):
; L2 战斗/Boss 节点 (2, 0/1/4/5/6/7/8) 七行精确铺满(跨表校验强制——ConfigDB ④ level_node_dangling);
; 奖励点 (2,2)/恢复点 (2,3) 不开战斗、无板行(跨表校验只查 battle/boss 节点)。
; 板面参数与 L1 高档同值(12×12 d0.25 t0.88)——密度/推理率轴差异化留给 难度曲线与Boss 调参批;
; board_type 词表 v1.0(2026-09-19 用户拍板):L1=dungeon 地牢基线 / L2=underdark 幽暗地域(玩家侧显示名「地图类型」);域校验在 board_gen_table.gd 词表常量。
;
; 第三关内容批(2026-09-20):板面 13×13 / Boss 14×14 兑现「Boss 板 = 本关最大板形」纪律;
; 密度维持 0.25(0.28 破推理率门槛有失败先例——切片调参登记 best≈0.81 降级告警,加难主杠杆 = 板面 + 地形);
; 词表第三值 deep_underdark(灰矮人矿道方向——v1.1 2026-09-20 L3 内容批,显示名归呈现侧);
; min_safe_count 按既有 ~0.42 格占比外推(13×13 → 70 / 14×14 → 82)。
; 降档登记:Boss max_gen_attempts 批内拟值 36 越 schema 域 [1,32](config_fatal 修行值纪律——不许放宽 schema),
; 按域上限取 32(批内降档梯子「Boss 回 13×13 时 attempts 32」同款值)。行值全 provisional 归 难度曲线与Boss。
;
; 商店房批连带(2026-09-21——level.tres 商店房批 Boss 重编号 8→9):新增 (2,9)/(3,9) 精确行
; (跨表校验强制——ConfigDB ④ level_node_dangling),行值照抄原 Boss (L,8) 行;
; 商店房 (L,8) 不开战斗、无板行(跨表校验只查 battle/boss 节点)。行值 provisional 归 难度曲线与Boss。
; 陈旧行清理(GP+商店房实施批收尾 2026-09-21):并行表代理曾把 Boss 板行照抄到 (2,8)/(3,8)——节点 8 现为
; 商店房、无板行需求,原 (2,8)/(3,8) 行成陈旧冗余已删(删前核 (·,9) 行在位且内容与被删行逐字段一致);
; 本行集迁移 = Boss 重编号 8→9 的商店房批连带。
;
; Package B 迭代·第 6 轮·d3 改板试点(2026-09-22——用户提出并授权,破 §4.4 板面冻结的单行试点):
; (1,3) 第一关·节点3·熊地精双雄 板 12×12→10×10(min_safe_count 60→50、max_gen_attempts 24→16,
; 对齐 (1,0)/(1,1) 的 10×10 行)。依据:第 5 轮读数 d3 完成度 84% 中 83.2pp 系 R₀ 免费展开、实挖仅
; ~18 格/3.7 轮——总血 28-42 撑不起 121 格精英板;缩板后同战斗量读数 (68.6+18)/87≈100% 回带,
; 清图率升(打完近全翻开,清图尖峰 24-40 伤可兑现),轮数/承伤/胜率预计不动(难度刀≠设计意图修复)。
; 连带义务:board_calibration.csv 全量重跑 calibrate_boards(§4.4 预告)。provisional 未提交。
;
; 板尺寸策略改判批(2026-09-24——用户拍板「小怪板统一 10×10」原则,取代第 6 轮「板随战斗时长」原则):
; 第 6 轮单行缩板在 L1 内造出 10→10→12→10→12 锯齿——弱怪长战配大板、强怪速战配小板,节奏倒挂
; (用户实测定罪:第三场单只熊地精 50 血占 12×12、第四场三只强怪占 10×10)。本批 L1 小怪场统一 10×10:
; d2 12×12→10×10(min_safe_count 60→50、max_gen_attempts 24→16,对齐本关其余小怪行);
; **boss d4 维持 12×12 不动**(首次实施时误将 boss 板一并缩小,用户纠正后回滚——boss 场更大=
; 战斗逐渐变难的视觉收束,板序列 10/10/10/10/12 单调递增)。
; 战斗轮数由怪血量与玩家输出决定、与板大小无关=难度中性;完成度分母/清图率变化连带 board_calibration
; 全量重跑。L2 全 12×12 / L3 全 13×13(boss 14×14 单调收尾)本原则下无需动。provisional 未提交。
+2
View File
@@ -5,3 +5,5 @@
[resource]
script = ExtResource("1")
rows = []
; C3 GDD Core Rule 1:全局修饰结构位——MVP 空表(零行为)。首个修饰行 = 精英节点上线(C1 联动)。
+172
View File
@@ -754,4 +754,176 @@ rows = [{
"armor_class": 12
}]
; 切片调参登记 v3(2026-09-12 试玩批次 1 触发——F7 流程;C3 GDD F5 对照):
; 试玩反馈 = 基线满血全冒险成功(模拟 80%/真人 100%)——难度严重不足,主杠杆 = atk 曲线上调(承伤侧):
; 4/4/6/4/7 → 7/5/9/6/9(约 +50%);HP 结构调整(D0 24→20 缩短战斗、Boss 130→150 用户拍板可上调)。
; D0 注:20 HP 会被首轮盲挖粉尘秒杀(12+9)——回 24(4 轮战斗/承伤 42%,有过程有痛感)。
; DND 命名(用户指令):goblin 地精(单拍杂鱼)→ bugbear 熊地精(三拍伏击环——DND 原型即伏击手)→
; ogre_chief 食人魔酋长(经典低阶 Boss)。enemy_id 同步更名(内部键,无外部引用)。
; L2 锚行同步 v3 语义(bugbear 30/7)。
; 遭遇随机化批(2026-09-12 用户拍板):D0 [1,2]/D1 [2,3] hp13/D3 [2,3] hp18——数量区间骰(encounter 流,B1 掷骰);
; D2/Boss 固定(新敌种首遇教学位不加骰——C3 GDD Core Rule 8)。期望总量 ≈304(+8%),F6 带内。
;
; 护甲类型批(2026-09-12 用户拍板:攻击类型 × 护甲类型克制;2026-09-15 命名统一批——原位置键/位置名作废):
; armor_type ∈ {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲(缺省 = light 向后兼容;原 {none, a, b});
; 克制矩阵本体在 settlement.tres.armor_effectiveness。
; 映射(教学梯度:轻甲→魔法护甲→重甲):goblin=light · bugbear=ward · ogre_chief=heavy(L2 bugbear 同 ward)——候选方案,
; 数值与映射均 provisional 待用户调参。
; 敌方攻击区间批(2026-09-14 用户拍板②第二步——"上限掺运气"敌方侧:atk 上阶/下阶,中点恒等行值;
; roll 时机 = 遭遇骰同流〔encounter〕spawn 期整战持久,每拍抖动否决〔数字可读性锚〕;B2 零随机纪律保持
; ——收 override 即确定值。宽度 ±3 provisional 归 C3 测量迭代;验收 = 完美策略档 0 加成关卡完成 ≤40%。
; 护甲等级掷点批(2026-09-16 用户拍板):armor_class 字段入列(缺省 0 向后兼容)——防守方掷点判定阈值基数,
; 与 armor_type(克制属性轴)两概念严格分离、不做映射。首关六行暂定 0(正式行值归怪物/敌人系统设定批——
; 登记待办:第一关怪物 AC 相对较低,值待怪物设定投值;投值所有权归 难度曲线与Boss)。
; 敌人技能池批(2026-09-16 用户拍板——设计权威 docs/proposal-skill-pool.md v1.0):
; pattern 行为环数组 + heavy_multiplier 整体退役,改池模板 pool = [{skill_id, weight}](技能行本体在 skills.tres;
; 实例池快照与冷却状态在 单位容器 spawn)。建议池(全部 provisional,投值归 难度曲线与Boss;EV 锚 = proposal §2.5):
; 地精 普攻 9/突刺 1(名义 EV 1.05;**整数基线 EV 1.10**——atk1 突刺落地 2:〔9×1+1×2〕÷10;2026-10-05 技能池执行批 D4 整数口径重登记);
; 熊地精 普攻 6/蓄力 2/重击 2(EV 1.00 中性);食人魔酋长 普攻 6/重击 2/硬化 2(EV 1.00 中性;
; 硬化 = Boss 池独占增益技——增益 ≤1 纪律下旧蓄力拍退役不进 Boss 池)。;
; 酋长 EV 重锚(2026-10-05 技能池执行批 T5——震地怒击半血解锁入池:前半场池 6/2/2/3,
; 后半场 6/2/2/2+震地怒击 2=每拍 4.33;含闸长期速率抽样实测 1.1948,smoke EV 抽样带重锚 [1.10, 1.30];
; 卓尔遏毒/石君王岩崩同批入池——读数判读归 T7 节奏重测〔后置挂 07,跑批待用户批准〕)
; 含闸长期速率(冷却/保底)EV 锚按实施批 EV 抽样复核登记(proposal §2.5)。
; 地精 EV 重锚(2026-10-28——触发条件批孤注守势入池(w1,血量 ≤35% 情境入池):池 9/1/1;
; EV 抽样模拟器取无情境口径(trigger 行不过滤、每拍可摇)实测 0.9148,smoke EV 抽样带重锚 [0.82, 1.02];
; 旧带 [1.00, 1.20] 系触发器前唯一特殊技=突刺的池结构下登记,随池构成换代作废)。
;
; 分支节点图批(2026-09-17——L2 幽暗地域内容批,行值全部 provisional,投值归 难度曲线与Boss):
; 每战斗/Boss 节点 (2, node_depth) 精确行(跨表校验强制,违例加载致命——ConfigDB ④);锚行 (2,0) 保留。
; L2 熊地精 = 熊地精 bugbear 复用(幽暗地域部族设定——第二关三行):
; 池差异化改造 = 蓄力拍退役换突刺(复用共享技能行 lunge——共享技能表验证面),display_name 沿用
; 熊地精系译名(熊地精/熊地精猎群/熊地精重击手——行值与池与 L1 错开)。节点 5 重击手轮廓:普攻 5/突刺 2/重击 2(名义 EV 1.333;atk1 整数口径 1.333、atk3 每拍 ≈4.11——2026-10-05 整数口径重登记;权重期望
——冷却闸实际拉低,抽样复核归实施批口径)。
; 巨蜘蛛 giant_spider(新敌种——关间台阶纪律的"新敌种"半边):群行倾向(数量区间 [2,3],首遇位节点 4
; 固定 2 不加骰——新敌种首遇教学位纪律同 L1 D2);池 = 普攻 6/撕咬 2/蓄力 1(增益 ≤1 纪律——charge 复用
; empower;EV 名义 1.00——批注旧值 1.11 = **atk1 整数口径**〔6+2×2〕÷9=1.111,非混用(2026-10-05 T2 消歧登记);护甲 light(与 L2 熊地精 ward 错开克制面;L1 三甲 goblin=light/
; bugbear=ward/ogre_chief=heavy 全覆盖,蜘蛛取 light 与同关敌种错开)。
; 蛛化卓尔精灵 drider(Boss——用户 2026-09-17 拍板定名,蜘蛛主题呼应):
; 专属池 = 普攻 6/重击 2/毒爆 2(独占技 venom_burst 权重轮廓;EV ≈1.67 权重期望,冷却闸拉低后待抽样),
; armor heavy,血量 175 = r_boss 口径 provisional 锚(定位本关最大单场消耗)。
; L2 敌种首遇位绿档(节点 0 熊地精/节点 4 巨蜘蛛):任何掷骰结果稳过——完美策略档扫描验证,读数另批登记。
;
; 数值压缩批(2026-09-17 用户拍板暂定——整体绝对值轴 ÷3,四舍五入):
; 全部 12 行 hp/atk/atk_min/atk_max ÷3(24/7→8/2 · 13/5→4/2 · 40/9→13/3 · 18/6→6/2 · Boss 150/9→50/3 ·
; 30/7→10/2 · 15/5→5/2 · 14/5→5/2 · 38/8→13/3 · 12/4→4/1 · 13/5→4/2 · drider 175/8→58/3)。
; armor_type / armor_class / unit_count 区间 / 技能池权重不动(相对轴与概率轴不压缩)。
; 粒度告警登记:atk 1~3 域下掷点折扣 25% 档大量触 damage_floor=1,地板伤害相对权重上升——
; 属压缩的已知效应,难度复测(monte carlo 压缩前后对比)随批产出。行值 provisional 归 难度曲线与Boss。
;
; 护甲类型重配批(2026-09-18 用户拍板):关卡 1 全部轻甲(食人魔酋长 heavy→light、熊地精两行 ward→light);
; 关卡 2 除 Boss(蛛化卓尔精灵 heavy→ward 魔法甲)外全部轻甲(熊地精四行 ward→light,巨蜘蛛既有 light 不动)。
; hero 侧两轴(armor_type/attack_type)同批入列(hero.tres 批注),敌方技能攻击类型轴仍未开(全部 normal 档)。
;
; 甲效矩阵加法核换装批(2026-09-18 用户拍板——settlement.tres armor_effectiveness 量纲 percent→整数点数,
; 三档 克制 +1 / 中性 0 / 被克 −1,五档百分比词表随批退役)。
;
; 新效应词汇批(2026-09-19 用户拍板——设计权威 docs/proposal-status-vocabulary.md v1.0):卓尔池
; 6/2/2 → 6/2/2/2(+蛛网束缚 web_snare 2——妨害技,对主角施 off_guard;每池妨害 ≤1 纪律入跨表校验)。
; 主分量权重期望 1.67 → 1.25(零伤害拍占 2/12),加毒爆必中附加分量(max(1,floor(atk×0.75)),atk 区间
; 2–4 → 1–3 点)后净期望待抽样复核归 难度曲线与Boss;实装面 = 仅卓尔,其余敌种不动。
; 现行读数效果:L1/L2 小怪全 light → 穿刺类(开格充能/穿刺弹)被克 −1(穿刺弹 2→1、充能 3 点→2)、普通/魔法中性;
; L2 仅 Boss 战非中性(魔法甲)——基础攻击与穿刺弹克制 +1、魔法矢被克 −1(原威力 + delta 自行核算后落笔)。
; 原重配批注「魔法矢克 Boss ×150、穿刺弹被克 ×50」与当批矩阵正反互倒(系笔误),随本批订正为上行读数。
;
; 第三关内容批(2026-09-20):新敌种 duergar 灰矮人(单体重甲中坚——关间台阶 ≈L2 单体 ×1.5,落 [1.3,1.6] 域)
; 与 hook_horror 钩爪恐魔(群行捕食者,复用 bite 共享技能行——lunge 已有 L2 熊地精复用先例);两敌种首遇位
; (节点 0 / 节点 1)固定数量不加骰——新敌种首遇教学位纪律同 L1 D2 / L2 节点 4;克制面重轻错开
; (重甲灰矮人 = 重甲首次登台,轻甲钩爪恐魔保穿刺/普通分量有用武之地),Boss duergar_king 灰矮人石君王
; 重甲收束「轻甲→魔法甲→重甲」教学梯度;Boss 血量 75 = Σ普通战生命期望 91 × r_boss≈0.82
; (provisional 锚,域 [0.5,1.1]);Boss 专属池独占技 earth_pulse(第二妨害词汇 sapped 首发);
; armor_class 恒 0(正式投值仍归「敌方护甲等级正式行值」在案条目全表同批)。行值全 provisional
; 归 难度曲线与Boss。
;
; 商店房批连带(2026-09-21——level.tres 商店房批 Boss 重编号 8→9):新增 (2,9)/(3,9) 精确行
; (跨表校验强制——ConfigDB ④ level_node_dangling),行值照抄原 Boss (L,8) 行(drider / duergar_king);
; 商店房 (L,8) 不开战斗、无行集。行值 provisional 归 难度曲线与Boss。
; 陈旧行清理(GP+商店房实施批收尾 2026-09-21):并行表代理曾把 Boss 行照抄到 (2,8)/(3,8)——节点 8 现为
; 商店房、无战斗行需求,原 (2,8)/(3,8) 行成陈旧冗余已删(删前核 (·,9) 行在位且内容与被删行逐字段一致);
; 本行集迁移 = Boss 重编号 8→9 的商店房批连带。
;
; Package B 数值优化批·首版(2026-09-22——qa-data/实现计划书_PackageB.md §4.1,worktree
; adventure-mc-playtest,provisional 未提交):B1 敌人 HP 阶梯整体重锚(完成度主旋钮——零加成基线
; naive 完成度普遍过低,见 qa-data/分析报告_零加成全流程.md)。只改 hp 字段;atk/atk_min/atk_max/
; 池/armor_type/armor_class/unit_count 全部不动(atk 系 §6 承伤迭代旋钮,改时须三值同步保中点恒等)。
; 改动清单:L1 d2 熊地精 13→50 · d3 双雄 6→20 · d4 酋长 50→72(d0/d1 弱档不动);
; L2 d0 10→45 · d1 5→18 · d4 巨蜘蛛 5→20 · d5 13→68 · d6 巢群 4→18 · d7 巢群 4→18 · d9 卓尔 58→85;
; L3 d0 灰矮人 15→50 · d1 钩爪 5→25 · d4 钩爪 5→20 · d5 16→76 · d6 巢群 6→20 · d7 小队 8→20 ·
; d9 石君王 75→105。以 MC 复测读数为准(runbook 计划书 §5;迭代记录 qa-data/iteration_log.md)。
;
; Package B 迭代·第 3 轮(2026-09-22——计划书 §6.2 承伤带守卫,用户拍板):8 行 atk-3 精英/boss
; 三值同步 3(2/4)→2(1/3)。第 2 轮 max_hp/heal 组合拳后单场承伤% 仍 49-62%(战斗变长吃掉血池收益,
; heal 加强主导经济修复)——§6.2「AP 已到顶 → 长战斗档 atk −1」条件成立。行清单:L1 d2 熊地精 /
; d4 酋长 · L2 d5 重击手 / d9 卓尔 · L3 d0 灰矮人 / d5 重装手 / d7 小队 / d9 石君王。
; atk-1/2 行(弱怪/群怪/L1 d3 双雄)不动。预期单场承伤 ~35%(20% 带在 Package B 框架内结构性
; 不可达,带位全达标归 Package A)。provisional 未提交。
;
; Package B 迭代·第 4 轮(2026-09-22——用户拍板 A):L1 d3 熊地精双雄 hp 20→16/单位
; (总量 [32,48])。第 3 轮后唯一未回调的 L1 高压行——双单位每轮双判定,单场承伤 51%、
; 胜率 70%,拖住 L1 通过率(40.7% vs 靶 70-78%)。其余行不动。provisional 未提交。
;
; Package B 迭代·第 5 轮(2026-09-22——用户拍板「继续」,修理刀三处,胜率带优先、完成度让路):
; ① 第一关·节点3·熊地精双雄 hp 16→14/单位(冲 ≥90 胜率靶;该行胜率/完成度两靶互斥已登记);
; ② 第二关·节点9·蛛化卓尔精灵 85→80(L2 boss 胜率 59% 回 60-75 带);
; ③ 第三关·节点9·灰矮人石君王 105→92(L3 boss 胜率 38% 向 50-70 带靠;该行完成度靶未设带,让路合法)。
; 否决项登记:食人魔酋长 +10% 方案经测算否决——boss 完成度 >130 需 HP +~50-75%(98→104 买不回靶),
; 会把刚入带的 66% 胜率打回带外,纯亏。provisional 未提交。
;
; 数值设计批·关卡成长×永久成长对齿(2026-09-22——设计权威 qa-data/数值设计_关卡成长×永久成长_v1.md,
; 用户三拍板 1A/2A/3A,第 7 轮):第二/三关阶段拔高 14 行(L1 一行不动)。血轴=每关上一档;
; 伤害轴=atk 2→3 三值同步 3(2/4)【L2 d5 重击手/d9 卓尔;L3 d0 灰矮人/d1 钩爪/d4 钩爪/d5 重装手/
; 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 快照
; 攻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 未提交。
;
; 实体名修正批(2026-09-24——用户裁决,DND 阶梯对齐):(1,3) 熊地精双雄 → 大地精
; (enemy_id bugbear→hobgoblin,仅本行;L2 bugbear 三行熊地精系不动)。依据:①DND 阶梯
; 地精(goblin)→大地精(hobgoblin)→熊地精(bugbear) 与实测每只血量 4-8→14→50 严格同构——
; 单只熊地精 50 血在前、三只 14 血在后,同族每只血量倒挂的观感由阶名归位化解;
; ②「双雄」名硬编码数量、数量却是 [2,3] 变量骰——摇出 3 只即成「三个双雄」自相矛盾
; (用户截图实证),集体名(猎群/巢群/小队/大地精)为正解。hp/atk/池/数量区间零改动。
; M-3 迭代 5 判读锁值批(2026-09-28):(2,9) 卓尔 hp 130→100(杠杆扫描 C3 组合——Boss 杠杆对普通档 +4pp;
; r_boss 锚漂移加重 100/389≈0.26 低于域 [0.5,1.1] 下限,收锚裁决挂迭代 5 出口人闸;读数见
; design/qa/m3-iter5-lever-scan-2026-09-28.md)。
; M-3 迭代 5 T4 AC 投值批·终版 Boss-only(2026-09-28:v1 硬梯度普通 L2 14.3% 越带/v2 软梯度 15.7% 仍越带——杂鱼税对普通画像〔普通分量为主力输出〕系统性超压;终版=杂鱼全 0〔带 0~5 下缘,掷点戏剧性集中 Boss 战〕+Boss 台阶 8/9/12 严格递增;三版演化链读数见 design/qa/m3-iter5-t4-ac-2026-09-28.md,随出口人闸追认)。
; M-3 迭代 5 返工批·U2 裁 v1 硬梯度重投(2026-09-29 用户推翻 Boss-only:重击手 8/重装手 9〔逐行手设值,非精英编码位——解读 A 追认,精英 P2 承载〕+Boss 台阶 8/10/12;杂鱼行按 T4 报告 §一 软梯度恢复〔v1 中间态未入 git,v1↔v2 唯一差异=重击手/重装手,代拟面记台账 247〕;卓尔 hp 100→130 随 C3 回退复原;组合读数 C1+v1 无先测,全量确认归返工批——裁断全录 design/qa/m3-iter5-u2-gate-2026-09-29.md)。
;
; 数值修复批·调参二(2026-10-01 新尺 MC 驱动):L1 系八行 atk 2→1(行 15/35/54/77/99/121/144/166;
; 行 234 巨蜘蛛巢群主体=L2 域维持)——新尺读数:普通档 v3.4 死因=L1 四连战无治疗夹缝(雷伤 6/8/8
; + 杂兵垫刀双压,L1 条件关卡完成率 1-2%,0/100 出第一关);完美档战役 50%(L1 93%)。刀位语义=
; 「危险来自雷,不来自杂兵」:杂兵垫刀减半,雷直伤 6/8/8 维持痛感;L2/L3(atk 3 系)本刀不动。
; 全旧带读数随新尺作废重测归本批。
; 调参二补充:上述八行 atk 区间 1-3→0-2(中点恒等期望当量锚不变式——atk=1 需区间中点同步 1;0=掷点空刀,保随机手感)。
;
; 混编遭遇首批投放批(2026-10-07 迭代 18 卡⑤——需求卡片 v1.2 裁点全裁 P1-P4 甲;机制前置=敌人表行集多行批
; 已提交 9cc4b8b):首个混编遭遇行落表 = (2,5) 双行行集〔R6 行序契约:同坐标两行文件内相邻、
; 行序=声明序=生成序——resolver 返回序/生成序/断言面全依赖文件行序〕:
; 行 1 = 既有 熊地精重击手 (2,5,bugbear)(行值零变更);
; 行 2 = 新增 (2,5,giant_spider) 巨蜘蛛巢群——行值整行复制 (2,6) 巢群行(P4 甲零数值新增:unit_count 2/
; unit_count_max 3/hp 26/atk 1/atk [1,2]/池 普攻6·撕咬2·蓄力1/light/armor_class 2),
; 行序=重击手在前(默认目标=前排坦克 ac 8,蜘蛛后排输出——强弱搭配形态)。
; 设计意图(P1 甲单点试水):「巨蜘蛛+熊地精」同场混编——生产内容实打卡①「每行一骰数量骰」交付面
; (重击手定值不骰、蜘蛛掷 [2,3],同场两种数量语义并存);静态最坏骰和 = 1+3 = 4 ≤ ENEMY_ROWSET_INSTANCE_MAX
; (spawn 期守卫内)。
; 读数预期:(2,5) 场敌人 3-4 只(3=重击手 1+蜘蛛 2,4=蜘蛛掷 3);L2 难度带位移读数随 Alpha 收官 M4 数值批
; 统一执行(07 在册义务——MC 取证跑批届时另报批,本批零跑)。禁区排查:巨蜘蛛首遇位 (2,4)「固定 2 不加骰」
; 零触碰、Boss d9 零触碰、跨关混编禁令过(两敌种首遇关均 ≤L2)、既有 19 行现值零变更。
; 敌人表内容修批 N1(2026-10-13 用户令「按推荐方式」,迭代 24 卡①):(1,2) 熊地精降档 hp 50>>22、armor_class 3>>2——
; 2026-09-24 阶名化解方案((1,3) 改名大地精以阶名归位化解同族血量倒挂观感)实机验证未达预期,按体验反馈重裁:
; 第一关总量曲线 12/10/50/35/72 的 5 倍墙消除(22 后曲线 12/10/22/35/72 相邻比最大 2.2);敌种 bugbear/池/数量/atk 区间零改动。
; 曲线标定其余行归 M4 数值批。
; N3 敌方攻击类型轴开列(2026-10-13 迭代 24 卡①,承接锻造工坊实装批迁移面⑥):20 行全填 normal 挂敌种行——
; 不做差异化(pierce/magic 属新设计另立项)。全局平移效应=主角穿重甲受伤 −1 / 魔法护甲 +1 / 轻甲 0,
; 锻造甲路线首次具备战斗意义;克制矩阵数值零触碰(settlement.tres armor_effectiveness 不改)。
;
; 行键 = (level_index, row_kind, depth_band, enemy_id);row_kind ∈ {battle, boss}——battle=战斗节点配池,boss=终点行集;
; depth_band=节点深度所在的带键,请求落带取首个 ≥ 深度的键(偏难一侧);越顶带由 resolver clamp、跨表校验判为调参事故。
; hobgoblin 行为缺口行:无立绘、不在敌种表——留任在役,补齐三件套(立绘+语义模板+敌种表行)归敌种内容批。
+221
View File
@@ -4,6 +4,17 @@
[resource]
script = ExtResource("1")
; 迭代 20 专长实装批 T3——专长重设计 27+2 件换代(奇物.md 基础件表 27 件+迁入登记 2 件=29 行;
; 件数口径=卡文 §10.4 推荐案:29 行=基础 27+迁入 2,V2/V3 断言按落表实数)。旧 27 件随本批整体
; 退役(素材库表二);订阅行 on/when/do 词表=效果引擎封闭集;rarity 四级直映射令牌档;cap=已拿
; 上限(案②解耦——拿了才计刷出不计);数值全 provisional 归调参批;desc 禁「雷/旗」字面
; (契约 6 命名纪律:旗〔旧称〕=封印、危险格=陷阱/巢穴风味层)。dispatch 预评估口径注记:「每第 N 次/积
; 满触发」系 when 于命中前读计数器现值——行 when 按 N-1 落值(双管镐 mod_eq ref 1/地质学家 eq 3/
; 跳格 eq 2/封印匠 mod_eq ref 2/保险丝 eq 1——effect_words_t1_test mod_eq 断言钉住该口径)。
; 迭代 31 卡② 凭证件裁削批 T5——完美保险自资源类迁入专长表(第 30 行):rarity rare/cap 省略
; (承 twin_pick/seal_burst 惯例)/domain=battle 显式写出(现役 29 行全为 adventure 或不写,本行
; 系战斗域首例——豁免计数每战清零)。限次落法=单订阅行:when counter(insurance_used) lt 1 承载
; 「本场首次」,do 自带 counter_id 记账由授予段消费点落账(dispatch 不代执行计数器)。
rows = [
{
"feat_id": "twin_pick",
@@ -858,5 +869,215 @@ rows = [
}
}
]
},
{
"feat_id": "vein_reserve",
"display_name": "矿脉储能",
"rarity": "excellent",
"cap": 1,
"desc": "挖格储能:每次受理的付费挖格 +1 储备点(RAP)",
"triggers": [
{
"on": "dig_paid",
"do": {
"kind": "grant_reserve_ap",
"value": 1
}
}
]
},
{
"feat_id": "easy_swap",
"display_name": "省力周转",
"rarity": "excellent",
"cap": 1,
"desc": "刷新半价:弃图刷新成本为基线 50%",
"triggers": [
{
"on": "refresh_request",
"do": {
"kind": "apply_discount",
"mode": "refresh",
"cost_mult": 50
}
}
]
},
{
"feat_id": "wall_blast",
"display_name": "崩壁冲击",
"rarity": "excellent",
"cap": 1,
"desc": "刷后群伤:成功刷新后全体存活敌各受 2 点",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "open_ticket",
"channel": "blast",
"value": 2
}
}
]
},
{
"feat_id": "mine_profit",
"display_name": "矿道红利",
"rarity": "rare",
"cap": 1,
"desc": "刷后GP:每次成功刷新固定 +1 GP",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "grant_gp",
"value": 1
}
}
]
},
{
"feat_id": "stone_mitigation",
"display_name": "石层缓冲",
"rarity": "common",
"cap": 1,
"desc": "当轮减伤:本轮成功刷新后,每次受伤固定 −2(多刷不叠加)",
"triggers": [
{
"on": "board_refreshed",
"do": {
"kind": "counter_add",
"counter_id": "round_swap_guard",
"value": 1
}
},
{
"on": "damage_taken",
"when": [
{
"fact": "counter",
"id": "round_swap_guard",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "damage_reduce",
"value": 2
}
}
]
},
{
"feat_id": "charge_rotation",
"display_name": "蓄力周转",
"rarity": "excellent",
"cap": 1,
"desc": "刷后蓄力:每次刷新 +1 层(上限 3),下一次攻击消费全部层数",
"triggers": [
{
"on": "board_refreshed",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "lt",
"value": 3
}
],
"do": {
"kind": "counter_add",
"counter_id": "swap_charge_layers",
"value": 1
}
},
{
"on": "attack_commit",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "attack_bonus",
"value_counter": "swap_charge_layers"
}
},
{
"on": "attack_commit",
"when": [
{
"fact": "counter",
"id": "swap_charge_layers",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "counter_add",
"counter_id": "swap_charge_layers",
"set": 0
}
}
]
},
{
"feat_id": "tunnel_rest",
"display_name": "坑道小憩",
"rarity": "common",
"cap": 1,
"desc": "当轮恢复:每次刷新累计 1 点(上限 3),本轮轮末合计回复",
"triggers": [
{
"on": "board_refreshed",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "lt",
"value": 3
}
],
"do": {
"kind": "counter_add",
"counter_id": "round_swap_rest",
"value": 1
}
},
{
"on": "round_end",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "heal",
"value_counter": "round_swap_rest"
}
},
{
"on": "round_end",
"when": [
{
"fact": "counter",
"id": "round_swap_rest",
"op": "gte",
"value": 1
}
],
"do": {
"kind": "counter_add",
"counter_id": "round_swap_rest",
"set": 0
}
}
]
}
]
+11
View File
@@ -85,3 +85,14 @@ routes = [{
"prereq_route": "light",
"unlock_price": 200
}]
; 锻造工坊实装批 T1(2026-10-12——迭代 23;设计权威 design/gdd/systems/跨局循环/锻造工坊.md v0.6,
; 需求卡片=锻造工坊实装批(迭代 23,卡片已按「吸收即死」退役,git 历史可考)T1 锚表):
; 六路线 provisional 投值——cap 5/每级增量统一 +1(v1.2 用户裁:provisional 线性起步,4a 前缓后陡为
; 终值形状方向归 M4)/甲型基础 AC 10-12-9(现役结构值 settlement.tres 矩阵同行)/基础价 20/公比 150%
; (历史考古参数 meta.tres:82-84 退役批注)/各级价格 20/30/45/67/101/一次性解锁价 200(中段三级 213 九折)。
; 初始持有=长剑+轻甲(语义继承自已退役属性锻造行——迭代 21 T5 行族删除后的新建行族,非改造)。
; 并联双通道(4b-1 裁):长枪/法杖←长剑练满、重甲/魔法护甲←轻甲练满,或一次性解锁价直购,互不影响。
; 默认两轴(契约 2 所有权移交):hero.tres 静态表两轴随本批删除,默认值归本表常驻持有——normal/light
; = 裸体态类型(Q1:未解锁/未锻造=0 加值)。全部数值 provisional 终值归 M4 数值批。
; 表运行期只读(铁律):永久强化只写存档 forge_routes 域,本表零写面。
+28
View File
@@ -8,3 +8,31 @@ max_hp = 26
atk = 2
armor_class = 10
display_name = "探险者"
; 〔2026-10-12 迭代 23 锻造工坊实装批 T2——两根类型轴删除(契约 2 所有权移交):armor_type/
; attack_type 两字段随本批从静态表删除,默认值归锻造工坊配置常驻持有(forge.tres
; default_attack_type=normal/default_armor_type=light),选中态写存档域 forge_routes、冒险开始
; 经参数面注入;单位快照读取面改读注入值(缺省回退锻造配置默认值——行为连续)。原 2026-09-18
; 营地扩展前置批「两根可配置轴」登记随字段删除退役,历史记载归 git 考古。〕
; 数值压缩批(2026-09-17 用户拍板暂定——整体绝对值轴 ÷3):
; max_hp 50→17(16.67 四舍五入)、atk 6→2。armor_class 10 不动(掷点阈值轴,相对量纲不压缩——
; 用户同日裁决:本体自带 AC 不算减伤统计)。恢复奖励削弱随批在 reward.tres(概率 1/4 + 量再减半)。
; 全部行值 provisional 归 难度曲线与Boss;压缩前基线读数(N=30 normal):基线档通关率 3%。
; 护甲等级掷点批(2026-09-16 用户拍板):armor_class = 10(暂定基准,归 难度曲线与Boss;
; EV 参考数按判定式复算 = 期望减免 21.25%);ac_up 增益 +1 AC/层(×2 层 = AC+2)在其上加算。
;
; 〔营地扩展前置批(2026-09-18 用户指令)两根可配置轴 armor_type/attack_type 已于 2026-10-12
; 迭代 23 锻造工坊实装批 T2 删除——所有权移交锻造工坊配置(见顶部注记);甲型基础 AC 随甲路线
; 行承载(轻 10/重 12/魔法护甲 9),本表 armor_class=10 保留为甲路线缺失时的兜底基准。〕
;
; Package B 迭代·第 2 轮(2026-09-22——计划书 §6.2,用户拍板一B):max_hp 17→20。第 1 轮读数
; 触发 §6.2(L1 精英承伤 43-55% vs 靶 ≤20%、L1 净消耗 17.7/17=104% 破产、normal L1 通过率
; 8.3% vs 靶 70-78%)的生存侧修复。单变量归因原则:同轮第二变量(heal_minor 加强)在 reward.tres。
; 血线 17/27/37→20/30/40(关间大奖励 +10 不变)。provisional 未提交。
; 数值修复批(2026-10-01——模拟档位重构后新尺首调):max_hp 20→26(调参二再 +2:普通档 L1 7% 差 2-4 血量级,+8% 池把 b3/b4 压线死改写险胜;完美档 62% 上界余量健康)。依据=新尺 MC 读数:普通档 v3.4
; (完美+失误注入,踩雷 ~2.5/战)L1 四连战无治疗夹缝全灭(L1 条件通关 2%,0/100 出第一关);
; 完美档战役 36-41%(L2 53% 弱环)。+20% 血池抬夹缝生存;配套直伤雷当量同步上调(reward.tres
; 4/5/5→6/8/8,雷痛回归用户口径)双向对冲。全旧带读数随新尺作废重测归本批。
+4
View File
@@ -10,3 +10,7 @@ rows = [
{"level_index": 2, "display_name": "第二层 · 幽暗地域", "rest_heal_pct": 30},
{"level_index": 3, "display_name": "第三层 · 灰矮人矿城", "rest_heal_pct": 30}
]
; 节点图源已换代为参数化生成器(关卡节点图生成 v1.1)——本表逐关行只承载关卡序号/显示名/恢复比例,
; 不再携带节点数据;图 = f(参数档, 种子),参数档见 level_gen_params.tres 三行(消费侧按 level_index 解析后交 generate)。
; 旧 15 张模板池(3 关 × 5)已清出,历史查 git 提交史与 tools/t5_build_templates.py、t17_fanout2_rebuild.py 留档。
+3
View File
@@ -44,3 +44,6 @@ rows = [{
"fork_ways_min": 2,
"fork_ways_max": 3
}]
; 节点图生成器核心批 卡① §8.1 参数档 provisional 取值表逐行填列(定值批落终值,数值全 provisional)。
; 层深/出口/岔口去路数为已锁域;商店实数由摆放约束钉死=出口数(shop_ratio 为名义值,配比填余次序见卡① §8.1 注)。
+40
View File
@@ -36,3 +36,43 @@ attune_refresh_upgrade = [{
"refresh_charges": 2,
"keystone_cost": 60
}]
; 元进度实现批(2026-09-21,设计权威 design/gdd/systems/冒险循环/元进度.md 定稿 v1.2):
; 数值全 provisional 归元进度设计批配读数(骨架先行——元进度实现批 2026-09-21,先例 = GP+商店房批)。
; 〔forge_products 属性锻造行族已退役——迭代 21 T5 攻防所有权切分:生命唯一入口=树脊柱淬炼、
; 攻/AC 唯一入口=锻造工坊路线(契约 9 红线),meta_forge_row 访问器与 buy_forge 购买面随批删除;
; 历史投值记载(2026-09-22 对齿批 base 20/ratio 150、2026-09-23 标定批 6/9/1)系 conversion 行
; 与 ForgeGDD 考古用,行族本体不再在表。〕
; 〔workshop_items 道具工坊行族已退役——迭代 22 T2 秘宝实装批:解锁换代混合制(钥石购买价
; unlock_keystone_cost + 条件谓词 unlock_requires 全挂 treasure_items.tres 行内单一事实源),
; 旧「总通关次数达阈值」六行(阈值 1/1/2/2/3/3)与渠道面随批删除;历史记载归 git 考古。〕
; boss 两档 L1 5/1 · L2 8/2 · L3 12/3
; (首杀全量、重复杀固定小额恒定不递减);conversion 全 0 = 未标定占位(标定归设计批,护甲行强条目:验收读数必须按
; 解锁后参数重测)。
;
; 数值设计批·关卡成长×永久成长对齿(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 关卡完成开门」节奏
; (实测 L1 通关局收入 ≈29-35 GP)。换算率表维持全 0(墙/钥匙闭环不依赖该轴,留扩展)。
; 配套怪表改动见 enemy.tres 同日批注。provisional 未提交。
;
; 换算率标定批(2026-09-23——用户指令「现在标定」;读数报告 production/qa/conversion-calibration/
; calibration.raw.md,判定端 tools/balance_verifier.py ④ 段可复判):conversion 三行全 0 → 6/9/1 投值。
; 方法 = OFAT 冒险注入档(每属性 +1/+2 级 × 双画像 × na=100,与白板同种子流配对)逐关条件
; 通关率差实测每级带平移 生命 +6.0±0.7 / 攻击 +8.8±0.8 / 护甲 +1.2±0.4 点(配对 bootstrap SE,
; 逆方差加权合并)→ 取整投 6/9/1。测量面 = 冒险条件口径(同日单关直入口口径退役后重测——
; 首轮直入口面读数作废)+ 当日 L2 收紧/关间大奖励 10→5 后的现行数值包。
; 护甲行强条目重测义务随批履行:重测暴露并修复护甲断链(begin_battle hero_armor_class 参数此前
; 零消费——_defender_ac 主角分支本次接线,缺参/-1 = 表值兜底);修复后甲轴读数 +1.2±0.4 为
; 解锁后参数实测,非基线外推。战役轨迹三花费序(生命/攻击/护甲优先)随批采集验证。
;
; 迭代 10 奖励重构批(2026-10-01——boss_keystone 重锚,P-B 乙拍板):首杀 3 三关统一;
; 复杀按关 2/2/3 递进(**用户 2026-10-01 裁「a 用备选」**——原代拍板「复杀统一 2」被推翻;
; L3 首杀=复杀=3 系 first_kill ≥ repeat_kill 结构约束下的合法端点)。
; 钥石渠道自本批起 =
; Boss/精英击杀逐杀掉落(大奖励 keystone_per_level 渠道退役;精英掉 1 预留行随怪物批实装落)。
; 产出结构变化 → 元进度标定义务重开(06-待办清单 2026-10-01 登记条)。provisional 归曲线调参批。
;
; 迭代 11 第①批 同调实施批 T4(2026-10-03):attune_refresh_upgrade 行族 = Y 刷新升级行(需求卡片 P1 甲——
; 每冒险开局给 Y 次、冒险周期池、用完即无;等级 L → Y=L 次刷新,钥石直购)。数值全 provisional 归
; 难度曲线与Boss(需求卡片 §三 7)。
+13
View File
@@ -155,3 +155,16 @@ rows = [{
"economy_spend": 1
}
}]
; 迭代 21 天赋树实装批 T1 换代(2026-10-12,设计权威 design/gdd/systems/跨局循环/天赋树.md v0.8):
; 12 节点 = 脊柱 2(生命淬炼/命中淬炼——一线一节点 cap4,2026-10-11 用户裁,原 4 层×4 线 16 行收编;
; 攻/护淬炼线随攻防所有权切分砍除——攻唯一入口=锻造工坊武器路线、AC 唯一入口=甲路线〔契约 9 红线〕)
; + 锻造脉 2(forge_workshop 钥匙 free 无前置 + easter_ley 彩蛋——effects 只持生命/命中两键,
; :402/:405 旧攻/护分量已摘除)+ 钥石脉 2(秘宝匣 ks_workshop〔display 旧名「道具工坊」退役〕/
; 刷新升级 ks_refresh)+ 同调脉 6(同调位 1/2/3——socket1 改根节点无前置 + Fork 3 单行双态节点,
; 原 8 行 A/B 双行结构与 fg_mastery 克制专精组退役)。
; 行门槛=一线一节点主语:脊柱节点第 N 级需全树总等级 ≥4×(N−1)(SPINE_ROW_GATE=4 公式平移,
; row 字段退役);requires=点亮前置(R1 裁撤案执行,schema 无等级阈值形态)。
; 🔴 层级纪律:局外树只放全局规则件——零 archetype 键(schema 校验强制);规则改写型特性只走局内随机渠道。
; 数值全 provisional 归 M4 数值批(契约 7——价格沿旧量纲锚:脊柱 10=旧 r1 价、socket 20/40/60、
; Fork 25、刷新 30 沿旧;生命 +5/级、命中 +1/级沿旧量纲);A/B 两侧差 <~3pp 验收=V19 MC。
+59
View File
@@ -201,3 +201,62 @@ rows = [{
"pool_tag": "cell",
"tier": 1
}]
; 格面资源批(2026-09-15 用户拍板——registry reward_table 条目为现行 schema 事实源):
; 十行格面池(五型 × 正负各一行)+ 清图槽一行。安全格与危险格同池同分布(雷安同池),
; 装板期一次摇定;摇号只消费正向行权重(类型频次 = attack 20 / defense 16 / blast 16 / heal 3 / weaken 12,合计 67;
; 头段"heal 15 / weaken 12,合计 79"为历史注记——数值压缩批后 heal 3、合计 67,见本文件尾部 2026-09-17 批注);
; 负向行 weight=0 不参与摇号,纯(类型, 极性)兑付字典——heal 负向 = 主角直伤 4(不经增益状态),
; blast 负向 = 警觉(敌方 AC +1,2026-09-20 伏击退役换装)。
; 可选类三行(knife_t1/knife_adv/heal_pack)与注入池 active_inject 随可选类道具整体删除(供给控制器/保底/窗口同步下线)。
; 格面资源八型批(2026-09-20 用户拍板):① blast 负向 ambush_double 退役 → enemy_alert(警觉,effect_id=alert
; 敌方 AC +1 percent 行);② 新增第 8 型 action 两行——ap_up(正向,下轮行动点 +1)/ ap_drain(负向,下轮
; 行动点 −1),排队兑现于下一轮开始(action_economy.queue_ap_delta);③ off_guard 数值 −2→−1 归 status_effects.tres。
; 区间行(伤害量值区间批 2026-09-14):heal_minor [2,6] · clear_blast [25,55]——中点锚 power 恒等期望当量,
; roll 时机 = 身份预分配批内追加取号(既有取号序零扰动);spark_aoe 定值行无区间字段。
; 减伤墙调参批(2026-09-13):defense_up 权重 24→16——攻击成为池内最高行(历史注记)。
; 护甲等级掷点批(2026-09-16 用户拍板):defense 型正向行换装——reward_id/effect_id defense_up → ac_up
; (显示名"盾牌加护"——2026-10-12 由"护甲强化"改名;AC +1/层 ×2 层);cell_type defense / 权重 16 / tier 2 零改动(类型频次合计 79 不变——历史注记,数值压缩批后现合计 67);
; 百分比减伤行无双轨退役——status_effects.tres defense_up 行同批删除。
;
; 数值压缩批(2026-09-17 用户拍板暂定):
; ① 整体绝对值轴 ÷3:spark_aoe 4→1 · heal_backlash 4→1 · clear_blast [25,55]→[8,18](中点 power 40→13,
; 区间中点恒等期望当量的锚口径不漂移)。
; ② 恢复奖励削弱(用户拍板两条):出现概率下调到当前的 1/4——权重 15→3(当前频率 15/79≈19.0%,目标
; 4.75%,整数权重最接近 = 3/67≈4.5%);恢复量在 ÷3 后再 ÷2(现值 ×1/6)——[2,6]→定值 1
; (0.33~1 取整,区间字段保留 min=max=1 占位,量级再调时恢复区间结构)。
; ③ 雷安同池连带(结构冻结,自动同侧):heal 权重下调同比例降低踩中 heal 型雷(反噬)的频率——
; 负向侧反噬量同步 ÷3(4→1)。
; 类型频次合计 79→67(attack 20 / defense 16 / blast 16 / heal 3 / weaken 12)。行值 provisional 归 难度曲线与Boss。
;
; 伤害道具批(2026-09-18 用户指令):① 七型字典——五型增补 pierce(穿刺)/ magic(魔法)两型,各正负一行:
; 正向 = 单目标克制直伤道具(穿刺弹/魔法矢 power 2,attack_type 字段载攻击类型轴,走克制矩阵、
; 必中全额不掷点——棋盘来源伤害既有口径);负向 = 主角直伤(穿刺陷阱/魔法反噬 power 2,复用反噬通道不经增益状态)。
; ② 权重再平衡(保 spark_aoe 16 不动、总 67 不变):attack_up 20→14 · ac_up 16→11 · heal_minor 3→2 · weaken 12→8;
; 新行各 8。等比基准 = 其余四行 51→35(×0.686),整取后 14/11/2/8,微差登记已知偏差。
; 类型频次合计仍 67:attack 14 / defense 11 / blast 16 / heal 2 / weaken 8 / pierce 8 / magic 8。
;
; 九型批(2026-09-20 用户拍板——GP 进第九型,获取来源=战斗结束掉落+雷图低概率惊喜获取〔量与战斗掉落相当〕;
; GP 袋=挖开安全格入战斗内缓冲、战斗完成统一入账;破财=危险格摇到 gp 面的字典负向行〔weight 0 不入池,
; 挖危险格兑付=缓冲 -2 钱包侧 clamp ≥0〕;正向权重合计 74→76);行值 provisional 归 难度曲线与Boss。
; gp 两行 tier=1(provisional 占位);effect_id gp_gain/gp_loss = 战斗内缓冲(escrow)通道路由键
;(战斗结算 _apply_percent_row 分流,不入增益容器——ConfigDB ① status_effects 悬空豁免归 core 面)。
;
; Package B 数值优化批·首版(2026-09-22——qa-data/实现计划书_PackageB.md §4.3):clear_blast
; 区间 [8,18]→[18,35](中点 power 13→26)——清第一图回归真正伤害尖峰,撑起 boss"必清图才打得动"
; 节奏(L1 boss 清图≥1 靶 ≥80%);其余行全部不动。provisional,以 MC 复测读数为准
;(runbook 计划书 §5;迭代记录 qa-data/iteration_log.md)。
; ⚠️ 计划书笔误代拍板:原文"power 25"违反 schema 中点锚硬校验(config_fatal)——power ≡ floor((min+max)/2),
; (18+35)/2=26 故落 26(期望当量 +1 vs 计划书 25,掷点区间 18–35 与计划一致,玩法值零偏离)。
;
; Package B 迭代·第 2 轮(2026-09-22——计划书 §6.2,用户拍板一B):heal_minor 恢复量 1→2
;(区间三值同步 power/min/max = 2/2/2 过中点锚校验)+ 出现权重 2→4(正向池合计 76→78)。
; 与 hero.tres max_hp 17→20 同轮双变量(用户拍板组合拳,归因混在一起由下轮分摊)。provisional 未提交。
;
; Package B 迭代·第 4 轮(2026-09-22——计划书 §6.4,用户拍板 A):clear_blast 26→32
;(区间 [18,35]→[24,40],(24+40)/2=32 恰为整中点锚)。boss 面三缺同解(normal 胜率 62% 贴下沿 /
; 清图率 49% vs ≥80 / 完成度 99 vs >130)——清图尖峰加大让"先清一图"成为 boss 战最优路径。
; 副作用已知:perfect L1 boss 胜率 97% 越 75% 上界加剧。provisional 未提交。
; 数值修复批(2026-10-01):直伤雷三行 4/5/5→6/8/8(迭代 8 雷当量 provisional 的再抬档)——用户口径
; 「踩雷必须痛」;配套 hero max_hp 20→24 双向对冲(见 hero.tres 批注)。新尺 MC 复测归本批。
+54
View File
@@ -63,3 +63,57 @@ script = ExtResource("1")
segment_order = PackedStringArray("grant", "buff_debuff", "combat", "decay", "verdict")
core = SubResource("SettlementCoreTable_core")
damage_roll = SubResource("DamageRollTable_roll")
; 攻击类型 × 护甲克制批(2026-09-12 用户拍板结构;2026-09-15 命名统一批 + 抽象分离批,数值 provisional 待用户/C3 调参):
; 主角攻击 = 单一基础分量(2026-09-19 充能退役批·用户拍板——原三分量模型退役,克制策略归冒险前配置:
; 类型 = hero 表 attack_type,营地换型 = 改字段;局内无挖掘攒弹药的第二供给)。
; 【抽象分离纪律保留】攻击类型 {normal, pierce, magic} = 普通 / 穿刺 / 魔法 = 伤害的属性轴,
; 只决定查矩阵哪一行,定义里不含任何棋盘来源;护甲类型 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲是敌单位属性。
; 格面资源批(2026-09-15 用户拍板):段序七段 → 五段(item_window/item 两段随可选类道具删除下线);
; source_label_map 删 item 键;effect_pool_map 增 sapped→hero_offense(weaken 型负向——乏力)。
; enrage(敌方暴走 +25%,enemy_output 池)/ off_guard(我方失衡 +25%,hero_defense 池)/
; sapped(我方乏力 -25%,hero_offense 池)= attack/defense/weaken 三型负向字典行
; (极性路由兑付:挖危险格 / 标错安全格按该格 cell_type 取负向行;heal 负向 = 主角直伤不经状态、blast 负向 = 警觉(敌方 AC +1,2026-09-20 八型批换装))。
; 减伤墙调参批(2026-09-13):pool_clamp 域 [-100,+100] 结构冻结不动;减伤杠杆走 status_effects.tres(批注在该表)。
; 护甲等级掷点批(2026-09-16 用户拍板,数字即规范——战斗结算 GDD 护甲等级掷点节定稿同批):
; roll_faces=20 / roll_tier_bounds=[3,7] / roll_tier_discounts=[75,50,25] / crit_roll=20 / crit_multiplier=200 /
; damage_floor=1 / armor_roll_stream=armor_roll;attack_bonus=0 结构位(元进度/武器附魔未来投值)。
; 判定式:掷点 ≥ 防守方 AC + attack_bonus → 全额;缺口 1-3 → ×0.75、4-7 → ×0.5、8+ → ×0.25;无 miss 无零收益档;
; nat20 = 暴击 ×2 恒全额(双方同规);任何攻击落地伤害 ≥ damage_floor(全局 max(1,·))。
; 掷点只吃基础攻击分量(主角普通分量 / 敌方攻击拍与重击拍基线);穿刺/魔法充能与棋盘来源伤害必中全额。
; EV 参考数(按判定式复算):AC10 期望减免 21.25% / AC11 25% / AC12 28.75%;微调旋钮 = bounds/discounts,不动 AC 值。
; effect_pool_map 同批删 defense_up 键(减伤百分比行整体退役、无双轨)——ac_up 是 AC 阈值轴效果,不入任何池;
; off_guard(hero_defense 池)保留不动,池结构与乘区公式零改动。
; 敌人技能池批(2026-09-16 用户拍板):effect_pool_map 增 empower→enemy_output(蓄力技载体——敌方输出 ×2
; 经既有 enemy_output 乘区兑付)与 fortify→enemy_defense(硬化技载体——敌方防御池新词汇)。
; enemy_defense 池乘区消费面 = 主角攻击落地段(Σ三分量后 ×(100+clamp(enemy_defense))/100)——
; 四池对称乘区:敌打主角 = hero_defense×enemy_output,主角打敌 = hero_offense×enemy_defense
; (hero_offense 现行走主角普通分量 attack_up 直乘、切片未改——登记遗留)。
;
; 数值压缩批(2026-09-17 用户拍板暂定):
; equivalence_ref_damage 12→4(当量参考敞口,绝对值轴 ÷3 取整);
; roll_faces/折扣档/暴击/damage_floor/克制矩阵/pool_clamp 全部不动(相对轴)。clamp 域 [-100,+100] 共用。
;
; 战斗数值结构修复批(2026-09-18 盘问会话用户拍板):
; effect_pool_map 删 off_guard 键——off_guard(失衡)换装 AC 轴(unit percent→ac、+25→−2,provisional
; 归 状态层重设计批;消费走 _defender_ac 既有 ac 型读法零代码),退离 hero_defense 池(池清空、结构保留;
; 上方 2026-09-15 attack/defense/weaken 三型负向字典行的极性路由注记不变——defense 型兑付行仍是 off_guard,
; 变的只是池归属)。hero_offense 乘区接线同步落地(battle_orchestrator 主角攻击段):Σ三分量后
; ×(100+clamp(hero_offense))/100×(100+clamp(enemy_defense))/100——attack_up 普通分量直乘旁路退役,
; 增伤/乏力覆盖面 = 普通+穿刺+魔法 合计;上方"hero_offense 现行走直乘——登记遗留"注记随之销项。
;
; 甲效矩阵用户调参批(2026-09-18 用户逐格给值,五格改值、四格不动):
; normal ward 100→120 · normal heavy 100→50 · pierce light 100→80 · pierce ward 150→120 · pierce heavy 50→80;
; magic 全行与 normal light 不动。上方「克制语义 = 克制 ×150 / 中性 ×100 / 被克 ×50(对策三角)」三档措辞退役——
; 现行五档:克制 ×150 / 弱克制 ×120 / 中性 ×100 / 弱被克 ×80 / 被克 ×50,普通行不再恒中性。
; 键集 3×3 满格结构冻结不动;矩阵消费面 = 主角侧(道具直伤 / 主角攻击三分量 / 充能分量),
; 敌打主角现行不查矩阵——主角 armor_type 轴仍休眠,轴生效待敌方技能攻击类型批。
; 实战读数(现行敌种甲配置):L1/L2 小怪全轻甲 → 穿刺类(开格充能/穿刺弹)×80 小幅折损、普通/魔法中性;
; L2 Boss 魔法甲 → 基础攻击与穿刺弹 ×120、魔法矢 ×50(不变)。重甲现行无消费敌种(零消费格)。
;
; 甲效矩阵加法核换装批(2026-09-18 用户拍板数值语言——小整数加法核,与 01-核心循环 数值语言条款同批):
; 量纲 percent→整数点数,三档 克制 +1 / 中性 0 / 被克 −1,九格 normal [0,+1,−1] / pierce [−1,+1,0] / magic [0,−1,+1];
; pierce×heavy = 0 中性为用户特别拍板(重甲挡刀刃不排斥穿刺;现行无重甲敌种,纯结构位)。
; 上方 2026-09-18 甲效矩阵用户调参批(五档 ×150/×120/×100/×80/×50)与更早三档三角批注的百分比量纲全部随批退役(历史留痕不删)。
; 键集 3×3 满格结构冻结不动;缺项回退 = 0 中性(battle_orchestrator._counter_delta);域 [−3,+3](schema 校验)。
; 奖励直伤行 payload 键 effectiveness 保留键名、值语义改点数(JSONL 契约稳定——analyze_playtest 等分析工具不破坏)。
+15
View File
@@ -94,3 +94,18 @@ price_a = 1.8
price_b = 1.1
relic_base_price = 15.0
spend_discount = 0.8
; GP+商店房实施批(2026-09-20 用户拍板,设计权威 design/research/proposal-gp-shop-room-2026-09-20.md 裁决记录):
; 掉落=均值±方差掷定(gp_drop 封闭流)+关级加值;精英行为预留行(当前无消费方,随精英节点内容批启用);
; 价格公式 = base_price + price_level_bonus(关卡) + price_increment × 该商品已购档数(已购档数在 冒险结构——
; 冒险内累计、跨冒险清零);商品三件全陈列无刷新无摇号(2026-09-20 拍板,提案 §2.4 的三选一+刷新方案退役)。
; 期望账:L2 商店前可得 GP 期望 ≈ 35–40(L1 全程 22 ± 惊喜 + L2 商店前五战 21 ± ),三件首购总价 L2 =
; (4+2)+(2+2)+(4+2)=16、全购欲望 ≈ 26–34——取舍空间成立;量级与方差全部 provisional-pending-曲线调参批
;(用户拍板骨架先行、数值整体后置,行值归 难度曲线与Boss)。
; GP 商店房重构批(M-4 迭代 11 第②批 T1,2026-10-03——需求卡片 v1.3 全裁 P1 共享池/P4 ceil/P5 甲):
; pool_shelf 行族 = 共享池货架(kind_id 词表 attack/max_hp/armor/ap/feat_ticket/heal 六行——heal 系 T6 商店补回复〔默认原则:分类内成员都要在它该出现的渠道池里,现行缺〕、GP 袋负交易切边计价物不自售、feat_ticket 系 T5 专长凭证行〔非 Boss 渠道专长入口,价与其他商品相当=契约④口径②〕、
; 规则改写型特性三轴 doctrine 切边;库存=上限即库存,账本在 冒险结构 _bonus_taken 单点共享,行族不记库存);
; 价格公式换代(pool 模式)= ceil(base_price × b^(关序−1) × A^(本次进店已购次数)),price_a=1.8/price_b=1.1
; provisional 投值(零人工令改 provisional+后置追认,原 P5 甲研究拍板后置);products 行族 + 加法定价
; 随 legacy 模式(shop=off 对照承载)保留不退役,退役归 off 锚换代批。货架行值全部 provisional 归
; 难度曲线与Boss。
+59
View File
@@ -112,3 +112,62 @@ rows = [{
"damage_mult": 1.7,
"cooldown_beats": 3
}]
; 敌人技能池批(2026-09-16 用户拍板——设计权威 docs/proposal-skill-pool.md v1.0,全值 provisional 投值归 难度曲线与Boss):
; 五行 = 普通攻击 / 突刺(新行:伤害 ×1.5 冷却 2)/ 蓄力(增益技)/ 重击 / 硬化(新行:独占增益技——Boss 专属池载体)。
; 普通攻击两条域校验锁死:倍率恒 1、冷却恒 0(schema 拒绝配置)——池恒非空的结构前提。
; 蓄力 = 纯"攻击力增加"增益技(0 伤害、施加 empower 状态),与重击无任何组合语义(纠偏二——旧"蓄力→猛击"
; 固定配对随行为环退役);增益与倍率同拍只是两层乘法共存(×2 遇 ×2 = ×4)。
; buff_duration = 2 的口径:容器时限纲 = remaining_settlements(先兑付后衰减、授予当轮衰减段即 −1)——
; 施放发生在第 N 结算战斗段,需存活到第 N+1 结算战斗段才兑付("下一拍"),故持续 1 个有效结算 ⇔ 表值 2
; (提案"攻击×2 持续 1 结算"的容器语义落地;有效增益拍恰 1 拍,归 增益状态/难度曲线 复核)。
; 硬化 = 自防增益(fortify 状态 → 敌方防御池),Boss 池独占——增益 ≤1 纪律下旧蓄力拍不进 Boss 池(proposal §2.5)。
; 保底 M = pity_threshold = 4:连续 4 拍执行普攻 → 下拍必出特殊技(特殊技内按权重、不受冷却限制)。
;
; 分支节点图批(2026-09-17——L2 幽暗地域新技能行,全值 provisional 投值归 难度曲线与Boss):
; 撕咬 bite = 伤害 ×1.5 冷却 1(高频小爆发轮廓,与突刺 ×1.5 冷却 2 同倍率错开冷却轴——巨蜘蛛池载体;
; 复用技能行纪律下 lunge 仍归 L2 熊地精,两行不共用是有意的权重/冷却独立投值位)。
; 毒爆 venom_burst = 伤害 ×2.5 冷却 4(蛛化卓尔精灵 drider 专属池独占技——"Boss=专属池独占技+权重轮廓"
; 纪律的本批兑现;池引用唯一性 = 唯一 enemy_id: drider)。
; 两行均为 damage 型(存倍率不存固定值——伤害 = 施放者攻击基线 × 技能倍率,三乘法链第一乘)。
;
; 新效应词汇批(2026-09-19 用户拍板——设计权威 docs/proposal-status-vocabulary.md v1.0,全值 provisional
; 投值归 难度曲线与Boss):三词收敛两词——多段命中退役(无防御磨损体系,无对照机制);固定值伤害改形
; 必中附加分量(rider_mult,域 (0,1]):后置加法、必中全额、不进乘区不出暴击,分量值 =
; max(damage_floor, floor(攻击基线 × rider_mult))——随基线缩放保持共享技能表前提;
; ×0.75 价位锚 = AC 折扣档最轻档"不掉档的稳定伤害"(可掷分量期望 AC10≈0.80 / AC12≈0.76 对表)。
; ① 毒爆 + rider_mult 0.75(必中毒分量:atk3 → 毒 2 点,atk 区间 2–4 → 1–3 点随开战掷定值缩放)。
; ② 蛛网束缚 web_snare(新行,妨害型 debuff——对主角施加 off_guard 失衡):0 伤害施放行(不掷不折不兑付
; 伏击),字段复用 buff_ref/buff_duration;duration 2 = 1 个有效结算(empower/fortify 先兑付后衰减含
; 施放当轮口径);冷却 4;首发载荷 = 失衡(用户拍板),乏力 sapped 为第二词汇暂不实装;
; 池纪律 +:每池妨害 ≤1(ConfigDB 跨表校验);实装面 = 仅卓尔池,其余敌种不动。
;
; 第三关内容批(2026-09-20):大地脉动 earth_pulse = 灰矮人石君王 duergar_king 专属池独占妨害技
; (0 伤害施放行,对主角施加 sapped 乏力——第二妨害词汇首发,状态行 status_effects.tres 既有);
; 字段口径同 web_snare(duration 2 = 1 个有效结算);每池妨害 ≤1 纪律由跨表校验覆盖。
; provisional 归 难度曲线与Boss。
;
; Boss 半血解锁技三行(2026-10-05 技能池执行批 T5,迭代14④——D7 用户裁案甲:每 Boss 一行新独占伤害技,
; 工作值 provisional 归调参;pool 侧 半血解锁(trigger 收编)=50 门槛随 enemy.tres 三 Boss 行):
; quake_smash 震地怒击 ×2.5/CD4(食人魔酋长)· venom_curb 遏毒 ×2.0/CD3(蛛化卓尔精灵)·
; rockslide 岩崩 ×2.5/CD4(灰矮人石君王)。解锁语义/双面过滤/一次性弹出规格 = 敌人系统.md 意图呈现节。
;
; 有效倍率表(2026-10-05 技能池执行批 T2——D4 整数基线登记:敌技基线=int(round(atk×mult)) 四舍五入半值上取,
; 全伤害管线唯一非 floor 取整点〔豁免登记见 registry skill_table〕;半点恒上取保住 atk=1 特殊技辨识度):
; atk=0(区间下界):全技塌缩下限 1(突刺==普攻==重击==1)
; atk=1(L1 全系+L2 四行):attack 1 / lunge·bite 2(实际 ×2.0 +33%)/ heavy 2(==突刺)/
; venom_burst 3(×3.0 +20%)/ venom_curb 2(×2.0 =名义)/ quake_smash 3 / rockslide 3(×3.0 +20%)
; atk=2:attack 2 / lunge·bite 3 / heavy 4 / venom_burst 5 / venom_curb 4 / quake_smash 5 / rockslide 5(全精确)
; atk=3(L2 精英/L3 全系):attack 3 / lunge·bite 5(×1.667 +11pp)/ heavy 6 / venom_burst 8(×2.667 +6.7pp)/
; venom_curb 6(=名义)/ quake_smash 8 / rockslide 8
; atk=4(区间上界):全精确(4/6/6/8/10/8/10)
;
; 敌人表内容修批 N2(2026-10-13 用户令「按推荐方式」,迭代 24 卡①)——普通敌错峰两行(12→14 行):
; venom_spit 毒吐 = 伤害 ×1.6 冷却 2(巨蜘蛛族普通敌池载体,挂 (2,5)/(2,6)/(2,7),首遇教学位 (2,4) 不投——
; 「独占技随关错峰」首轨只做在 Boss 侧的补齐;venom_spit 自 (2,5) 起投)。
; shield_bash 盾击 = 伤害 ×1.7 冷却 3(灰矮人族普通敌池载体,挂 (3,0)/(3,5)/(3,7))。
; 两行均不带 rider_mult:L2/L3 普通敌 atk 1–3,floor(atk×r) 恒被 damage_floor 兜成 1,与 0.75 档效果
; 完全相同——白占字段(毒绕开护甲由 damage 直伤表达已足够)。
; 有效倍率(int(round) 半值上取):毒吐 atk1→2/atk2→3/atk3→5;盾击 atk1→2/atk2→3/atk3→5。
; 倍率 provisional 归 M4 数值批。池权重均 2;逐池「恰一普攻/增益≤1/妨害≤1」纪律复查过
; (巨蜘蛛族增益仍仅 charge、灰矮人族 (3,0)/(3,7) 仅 charge·(3,5) 无增益、两族妨害 0)。
+44
View File
@@ -95,3 +95,47 @@ rows = [{
"stack_policy": "stack",
"max_stack": 16
}]
; 减伤墙调参批(2026-09-13,playtest-2026-09-12 报告 Q1 触发——defense_scan 实测墙轮 39%、
; 真人局 12/12 轮零承伤满血冒险成功):defense_up magnitude -50 → -30——叠满两层 = -60 减伤帽
; (原拟 pool_clamp_lo=-60 被 schema 冻结域拦截,杠杆移至本表);石肤权重 24→16 随批(reward.tres)。
; 承伤带对 C3 F4(≤20%/普通场、≤50%/Boss 场)复验责任随批;B3 GDD "-50" 文字同步随下次 B3 修订批。
; 格面资源批(2026-09-15):新增 sapped(乏力——weaken 型负向,hero_offense 池,attack_up 镜像;
; 进 effect_pool_map 同批登记)——状态表六行→七行;enrage/off_guard 语义 = attack/defense 型负向
; (标错安全格 / 踩到危险格经极性路由兑付,不再绑"标错反噬"单一来源)。
; 护甲等级掷点批(2026-09-16 用户拍板——百分比减伤增益整体换装为 AC,无双轨):
; defense_up(减伤 −30 percent ×2 层)整行退役(上方 2026-09-13 减伤墙批注随之留档)——
; "叠满池触底 ×0"免伤墙的结构面随 AC 掷点从根杜绝;新增 ac_up(盾牌加护 +1 ac ×2 层——2026-10-12 由"护甲强化"改名)——
; unit=ac 新量纲(AC 阈值轴效果,不入 effect_pool_map 任何池),消费面 = 战斗结算掷点判定
; (防守方 AC = 主角表值 armor_class + Σac 型效果层数×幅度);off_guard(失衡 +25 percent,hero_defense 池)
; 保留不动,其余六行零改动——状态表仍七行(一行换装)。
; 敌人技能池批(2026-09-16 用户拍板——增益技新状态行,七行→九行;敌方增益走既有增益状态容器):
; empower(蓄力:敌方攻击 +100 percent,enemy_output 池——蓄力技 buff_ref 载体)与
; fortify(硬化:敌方防御 +100 percent,enemy_defense 池——敌方防御池新词汇载体,Boss 池独占硬化技)。
; 两行 duration = 2 的口径:先兑付后衰减含施放当轮 → 表值 2 ⇔ 有效增益拍 1 拍(施放后下一结算兑付,
; 提案"持续 1 结算"的容器语义落地——详见 skills.tres 批注);refresh ×1 层(冷却闸下不可能自叠加)。
; enemy 侧池语义 = side-wide(registry 既有口径):蓄力 +100 入 enemy_output 与 weaken/enrage 共池 clamp;
; 硬化 +100 入 enemy_defense 新池(乘区消费面 = 战斗结算 主角攻击落地段)。值全部 provisional 归 难度曲线与Boss。
;
; 数值压缩批(2026-09-17 用户拍板暂定):ambush magnitude 3→1(伏击附加 = 层数×幅度,3 整除 ÷3)。
; percent 全族(attack_up 50 / weaken −65 / enrage 25 / off_guard 25 / sapped −25 / empower·fortify 100)
; 与 duration/max_stack 全部不动(相对轴与结构轴不压缩)。
;
; 战斗数值结构修复批(2026-09-18 盘问会话用户拍板——本批三行全部临时值,provisional 归 状态层重设计批):
; off_guard(失衡)换装 AC 轴:unit percent→ac、magnitude +25→−2(失衡 = 主角 AC −2,更易被命中;
; 消费走 _defender_ac 既有 ac 型读法零代码;settlement effect_pool_map 同批删 off_guard 键——
; hero_defense 池清空、池结构保留);fortify(硬化)符号纠正:+100→−50(乘区 = enemy_defense 池 −50
; → 主角攻击 ×0.5 折半——旧 +100 实际 ×2 翻倍,与"硬化=减伤"语义相反,用户确认系符号 bug);
; weaken(削弱)叠层改续时:stack_policy stack→refresh、duration_settlements 1→2(同名重复授予
; 只刷新剩余结算数、层数恒 1——结构性消饱和,×2 叠满池触底 ×0 免伤轮从根杜绝;refresh 实现与
; empower/fortify 同款零代码)。duration 终值、off_guard N 值、当量锚复算全部归 状态层重设计批。
;
; 格面资源八型批(2026-09-20 用户拍板):① off_guard magnitude −2→−1(护甲型奖励/惩罚数值对齐 = ±1);
; ② ambush(伏击)行整体退役——群伤负向不再放大敌方伤害;③ 新增 alert(警觉:敌方护甲等级 +1,
; target_side enemy、unit ac、1 结算、stack 上限 16)——blast 型负向新载体,消费 = _defender_ac 敌方侧
; ac 型读法(本批新接线)。状态表九行→九行(伏击出、警觉进)。数值全部 provisional 归 难度曲线与Boss。
;
; 主角增益只续时不叠层批(2026-09-20 用户拍板「主角的其实也一样,都是按照叙事的方式,不要有叠层;只续时不叠层」——
; 对齐削弱 2026-09-18 续时定案):attack_up / ac_up 两行 stack_policy stack→refresh;
; duration_settlements=1 与 max_stack=2 表值不动(refresh 下层数恒 1、max_stack 不消费——weaken 先例;
; dur=1 下续时表现为重复拿重置当轮倒数,跨轮延续随将来 duration 拉长自然显形——数值归状态层重设计批)。
+11
View File
@@ -4,6 +4,17 @@
[resource]
script = ExtResource("1")
; 迭代 22 秘宝体系实现重构落地批 T1(2026-10-12):新目录 12 行全量换代(设计权威=秘宝.md「目录(重设计 v1)」;
; 旧 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=
; 真视类工具累计使用(本批新增计数域)。全部数值 provisional 归调参批(契约 7)。
; 常时参数面件(无订阅行——效果=开局/装板参数注入,非事件订阅):阔视野带/储备罐/远程镐/信标网络/刷新练习/
; 刷新执照六件 effect_kinds 挂参数 kind,由注入面按装配集 kind 分发读取(_attune_refresh_x_total 先例)。
; 主动件:线香(battle 级充能)/天眼(level 级充能)charge_reset 扩签;试炼镐=自动兑付(charges=0 无上限,
; ap_charge_safe 满 charge_n=8 自动当轮 grant_ap——2026-10-12 反哺批⑤用户裁:无玩家发起入口、
; 旧行 counter_spend 受理闸随裁删除;挖格扣 AP 后、轮末判定前兑付;战斗级重置=账即时清空自然无感)。
rows = [{
"item_id": "morning_order",
"display_name": "晨操令",
+31
View File
@@ -74,3 +74,34 @@ rows = [{
"board_types": ["deep_underdark"],
"counter_items": []
}]
; 地形变异 首批类型定稿批(2026-09-19 用户拍板)——四行两轴×两关:
; 第一关 dungeon(平原/密林基线):荆棘丛(伤害型:挖开扣 1 点生命,轮末结算兑现)+ 藤蔓缠绕
; (信息型:挖开且显示数字时数字被遮蔽,呈现层读取);第二关 underdark(幽暗地域):岩浆
; (伤害型 2 点,登记名沿用)+ 烬覆(信息型)。unlock_level 门控 + board_types 交集门控双闸,
; 首关候选池恒 {荆棘丛, 藤蔓缠绕},第二关候选池恒 {岩浆, 烬覆}。
; counter_items 恒空:反制道具配套义务随框架契约保留,兑现归 Alpha 反制批(2026-09-19 用户拍板
; 反制道具改排 Alpha——切片期变异暂无反制手段,纯税状态已知悉接受)。
; 全部行值 provisional 归 难度曲线与Boss 调参批(effect_value/布置参数均为加法核小整数轴)。
;
; 第三关内容批(2026-09-20):伤害型关间递进 1(荆棘)→ 2(岩浆)→ 3(落石)+ 信息型矿尘;
; counter_items 恒空(反制归 Alpha 反制批);布量走既有公式
; (5% + per_level_bonus × 2——percent_of_cells 表头字段驱动,不加新配置)。
; 行值全 provisional 归 难度曲线与Boss。
;
; 基数修正批(2026-09-21 用户拍板):数量公式百分比基数由总格数改为候选池格数
; (安全格 − 初始展开区 = 实际可挖格)——总格数基数下初始展开占全板 56–67%,
; 有效密度膨胀 4–6 倍(L1 10×10 板实测 5%→30%,极端板全池覆盖);percent 语义 =
; 占可挖格比例,关卡递增由 per_level_bonus 绝对格数承载。表值不变归 调参批 重标。
; M-3 迭代 5 判读锁值批(2026-09-28):岩浆 effect_value 2→1 · per_level_bonus 2→0 · percent_of_cells 5→4
; (杠杆扫描 C3 组合收带 L2——配对种子读数见 design/qa/m3-iter5-lever-scan-2026-09-28.md;L3 侧效应=
; 变异密度 9%→4%、完美 L3 0→34%,L3 无带记观察态;行值 provisional→正式随迭代 5 出口人闸追认)。
; M-3 迭代 5 返工批(2026-09-29 U2 推翻 C3 裁 C1 两刀):percent_of_cells 4→5 回退;岩浆 1 与 per_level_bonus 0 维持——C1 终态;
; C1+v1 AC 组合读数归返工批全量确认(裁断全录 design/qa/m3-iter5-u2-gate-2026-09-29.md)。
;
; 屏蔽记录(2026-10-16 用户令):skewed_veins 贫矿脉行已删除——用户裁「地形变异特异关卡后续重新做
; 设计,先屏蔽」。删除后 Boss/精英图全局池空 → _resolve_global_variant 空行回落 → region 布置原路
; 兜底(battle_orchestrator.gd 既有回落路径),玩家面零横幅零角标零资源偏斜。删除行内容与首发批
; 裁决依据(2026-10-04 迭代 14 ③号卡:案A 资源面微贫向偏斜、effect_value=30 provisional、域 [0,50]、
; 0=中性 off 锚)见 git 历史;resource_face 机制代码(RewardEconomy 偏斜投影/schema 互锁/负样本
; 断言)全部保留,重建设计直接加行即可。
+13 -6
View File
@@ -203,15 +203,11 @@ func set_world_font(f: Font) -> void:
# D 区可用面积(AP 条之外)→ 格边长 = floor((min(宽, 高) − 预留 − (N−1)×格间距) / N)(battle.md 格尺寸公式)
# 公式须补算 (N−1)×2px 网格间距与垂直预留 24px,否则 12×12 板实高溢出可用区 ~6px,下缘被 clip_contents 裁掉。
# 算式唯一落点 = _cell_for(迭代 46 卡C——探针与单元测试共用该静态函数求值,禁复写算式造第二口径)。
func set_cell_area(avail: Vector2) -> void:
if _board == null or _board.width == 0:
return
var n: int = maxi(_board.width, _board.height)
var sep := SEP # 格间距(_init 同值)
var d := DEPTH_PX
var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出)
var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))
_cell = float(clampi(cell, 26, 46))
_cell = float(_cell_for(maxi(_board.width, _board.height), avail))
_hero_sprite.set_cell_size(_cell, float(SEP))
var stride := _cell + float(SEP)
for c in _cells:
@@ -224,6 +220,17 @@ func set_cell_area(avail: Vector2) -> void:
_update_world_size()
# 格边长算式唯一落点(迭代 46 卡C T1 自 set_cell_area 抽出——逐位同式:SEP/DEPTH_PX 读本文件
# 既有常量、clampi [26,46] 留本函数内;名字带下划线是 S13 max-public-methods 27 顶格的约束,禁改公开名。
# 探针(非当前板型 13/14×实板可用面积)与单元测试(四档板型)经本函数求值,复写算式即造第二口径)
static func _cell_for(n: int, avail: Vector2) -> int:
var sep := SEP # 格间距(_init 同值)
var d := DEPTH_PX
var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出)
var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))
return clampi(cell, 26, 46)
# 悬崖参数下发(网格像素尺寸/步距/列数/材质锚色——BoardWorld._draw 消费)
func _refresh_slab() -> void:
if _board == null or _board.width == 0:
+1 -1
View File
@@ -173,7 +173,7 @@ func _draw_south_facade(canvas: CanvasItem, rect: Rect2) -> void:
var y_far := rect.size.x - far_h
var y_near := y_far - near_h
var y_hl := y_near - FACADE_HIGHLIGHT_PX
canvas.draw_rect(Rect2(0.0, y_hl, rect.size.x, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点)
canvas.draw_rect(Rect2(0.0, y_hl, rect.size.x, FACADE_HIGHLIGHT_PX), UiKit.BRICK, true) # 口沿高光(合并归掩尘枚 BRICK——art-bible §4.1 换色合并①同值单点)
canvas.draw_rect(Rect2(0.0, y_near, rect.size.x, near_h), FACADE_NEAR_COL, true)
canvas.draw_rect(Rect2(0.0, y_far, rect.size.x, far_h), FACADE_FAR_COL, true)
+16 -13
View File
@@ -59,7 +59,8 @@ func intent_probe() -> Dictionary:
# 名字行探针出口(ui_probe 呈现口径 + 本批 V6/V8 断言面:armor_icon = 绑定纹理路径〔缺位回退字形〕、
# 几何 = 素底牌矩形与文字基线、素底色——V6 判据 = baseline_y < plinth_top_y)
# 几何 = 素底牌矩形与文字基线、素底色——V6 判据 = baseline_y < plinth_top_y;
# name_font_size = 名字行字号档实读(迭代 46 卡B——提档 FS_HINT 14 的几何单点出口))
func name_line_probe() -> Dictionary:
var f := _efont()
var tex := UiKit.load_tex(_armor_tex_path(st._armor))
@@ -69,7 +70,8 @@ func name_line_probe() -> Dictionary:
"armor_tex_bound": tex != null,
"plate": plate, "baseline_y": _name_baseline(plate).y,
"plinth_top_y": _pedestal_rect().position.y,
"plate_color": UiKit.HP_BAR_BACK}
"plate_color": UiKit.HP_BAR_BACK,
"name_font_size": UiKit.FS_HINT}
# ---- setter 面(presenter/组合根零改排) ----
@@ -332,15 +334,16 @@ func _name_plate_rect(f: Font, tex: Texture2D) -> Rect2:
var icon_w := 24.0
if tex == null:
icon_w = f.get_string_size(String(ARMOR_ICONS.get(st._armor, "◇")),
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
var name_w := f.get_string_size(st._display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
var name_w := f.get_string_size(st._display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
var total_w := icon_w + 3.0 + name_w
return Rect2((box.x - total_w - 8.0) * 0.5, box.y - 18.0 - 26.0, total_w + 8.0, 26.0)
# 名字行文字基线(牌内几何单点——图标 24px 垂直居中牌内〔中心=牌顶+13〕,12px 字形基线=中心+4)
# 名字行文字基线(牌内几何单点——图标 24px 垂直居中牌内〔中心=牌顶+13〕,14px 字形基线=中心+5
# (0.35h 规则按字号档推导 14×0.35≈4.9→5,T0 实测校验——迭代 46 卡B §7.4 提档))
func _name_baseline(plate: Rect2) -> Vector2:
return plate.position + Vector2(4.0, 17.0)
return plate.position + Vector2(4.0, 18.0)
# 绘制内容矩形(世界局部;ui_struct_probe ⑥standee「不出框+下余量≥8px」断言读数面——
@@ -362,8 +365,8 @@ func _draw() -> void:
## 地面域:椭圆落影(圣经 §6.1 地台落影压脚:方向稿椭圆 (0,0,0,70),压在地台
## 上缘周围;24 段多边形近似椭圆,中心=台面上方 6px、横半径=台宽一半)
## + 石板地台(§6.1 近景地台独立件:TEX_STAGE_PLAZA 源 48×12 九宫格横向拉伸、顶缘受光线 2px
## 不随拉伸变形、跨三关恒定;素材缺位回退程序化三段绘制〔STAND_STONE 族方向稿取色,几何同源
## _pedestal_rect——与 panel_skinned 缺位回退同构〕)
## 不随拉伸变形、跨三关恒定;素材缺位回退程序化三段绘制〔板身 STAND_STONE/描边 PLAZA_EDGE/
## 顶缘线 BRICK——art-bible §4.1 换色合并①②改指目标色,几何同源 _pedestal_rect〕)
func _draw_ground(f: Font) -> void:
var sh_center := Vector2(box.x * 0.5, box.y - 24.0)
var sh_rx := (box.x - 36.0) * 0.5
@@ -379,8 +382,8 @@ func _draw_ground(f: Font) -> void:
_draw_plinth_stretched(plaza, ped)
else:
draw_rect(ped, UiKit.STAND_STONE, true)
draw_rect(ped, UiKit.STAND_STONE_EDGE, false, 1.0)
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.STAND_STONE_RIM, true)
draw_rect(ped, UiKit.PLAZA_EDGE, false, 1.0)
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.BRICK, true)
## 地台九宫格横向拉伸域(循环体提函数——左右 4px 端头切片 + 中段拉伸;纵向恒 12px 源档,
@@ -464,13 +467,13 @@ func _draw_armor_name_line(f: Font) -> void:
draw_texture_rect(tex, Rect2(baseline.x, plate.position.y + 1.0, 24.0, 24.0), false)
else:
var armor_txt := String(ARMOR_ICONS.get(st._armor, "◇"))
icon_w = f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION).x
icon_w = f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT).x
var col: Color = UiKit.WARM_FAINT # 轻甲 = 暖白弱化(v3.5 暗底双色墨制)
match st._armor:
"ward":
col = UiKit.HERO_BLUE_BRIGHT # 魔法护甲 = 小人蓝亮档
"heavy":
col = UiKit.GOLD_BRIGHT # 重甲 = 金族亮档
f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION, col)
f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col)
f.draw_string(get_canvas_item(), baseline + Vector2(icon_w + 3.0, 0.0), st._display_name,
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_CAPTION, UiKit.WARM) # v3.5 暗底双色墨制
HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM) # v3.5 暗底双色墨制
+1 -2
View File
@@ -185,8 +185,7 @@ const TIP_BG := Color(0.16, 0.12, 0.09, 0.96) # tooltip 深底
const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 render_battle_screen——battle_stage_view 面板底)
const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层)
const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色)
const STAND_STONE_EDGE := Color8(90, 76, 58) # 站桩石板地台·描边
const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线
const PLAZA_EDGE := Color8(76, 64, 56) # 小石台描边(art-bible §4.1 换色合并②合并目标——贴图已落此值,缺位回退同值)
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色;对齐 demo --wood-deep)
const CAMP_ROW_PLATE := Color8(240, 230, 206) # 营地行板暖米(同上)
const CAMP_BADGE_PANEL := Color8(40, 32, 24) # 营地钥石徽暗底(同上)
+31 -6
View File
@@ -6,15 +6,16 @@
extends Node
const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 隔离样板单点
const BoardViewScript := preload("res://presentation/views/board_view.gd") # 算式单点静态调用载体(迭代 46 卡C)
# 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动)
const VIEWPORTS := [
{"label": "基座 1280x800", "size": Vector2i(1280, 800)},
{"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)},
]
const MIN_CELL := 36.0 # 格子可读门槛(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px)
var _problems: PackedStringArray = []
var _cell_reads: Dictionary = {} # 逐档格边长实读留痕(label→cell_size;PASS 行引基座值)
var _est_reads: Dictionary = {} # 逐档 13/14 板推定读数留痕(label→{13,14,avail};PASS 后打印对拍)
func _ready() -> void:
@@ -41,8 +42,12 @@ func _ready() -> void:
# 复原本项目基座(探针不该把进程留在末档尺寸)
get_tree().root.content_scale_size = Vector2i(1280, 800)
if _problems.is_empty():
print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)"
% [VIEWPORTS.size(), MIN_CELL])
print("[layout_probe] PASS — %d 档几何不变量全过+格边长等价成立(基座实读 %.0fpx)〔格边长 V1〕"
% [VIEWPORTS.size(), float(_cell_reads.get("基座 1280x800", -1.0))])
for label in _est_reads:
var e: Dictionary = _est_reads[label]
print("[layout_probe] [%s] 13 板推定 %dpx/14 板推定 %dpx(实板可用面积 %s)〔格边长 V4〕"
% [label, int(e["13"]), int(e["14"]), str(e["avail"])])
get_tree().quit(0)
else:
for p in _problems:
@@ -91,5 +96,25 @@ func _check_one(label: String, main: Node) -> void:
_problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)"
% [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]])
var cell: float = main.ui.battle.board_zone.board_view.get("cell_size")
if cell < MIN_CELL:
_problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])
_cell_reads[label] = cell
# 格边长接线守卫——判据=等价(生产写入值与单点算式严格相等,生产接线断开即红;
# 全仓禁任何人造格边长可读阈值)。
# 板型 N 无公开出口→`bv.get("_board")` 内部字段直查=受控例外(改动—验收对照表行 1 注明理由)。
var b = bv.get("_board")
if b == null:
_problems.append("[%s] _board 为 null(板型 N 无从取值,等价断言无依据)〔格边长 V1〕" % label)
elif cell != float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)):
_problems.append("[%s] 格边长等价破——生产写入 %.1fpx ≠ _cell_for(n=%d, avail=%s) 返回 %.1fpx(实板可用面积 %s)〔格边长 V1〕"
% [label, cell, maxi(int(b.width), int(b.height)), str(bv.size),
float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)), str(bv.size)])
# 最大板推定留痕(迭代 46 卡C T2——实板可用面积代入 13/14 板型求算式单点,读数打印不作判红:
# 锚点=用户验收的现行渲染值;对拍归实现报告,对拍不一致=卡 §9 L3 #2 呈报,禁实现侧改锚;
# 零尺寸前置=可用面积不可信时推定无依据)
if bv.size.x <= 0.0 or bv.size.y <= 0.0:
_problems.append("[%s] board_view 零尺寸(可用面积不可信,格边长推定无依据)〔格边长 V4〕" % label)
else:
_est_reads[label] = {
"13": BoardViewScript._cell_for(13, bv.size),
"14": BoardViewScript._cell_for(14, bv.size),
"avail": str(bv.size),
}
+1 -1
View File
@@ -188,7 +188,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/presentation/pacing_queue_test.gd", "expect": 0}, # AC⑤/T9——排空零分叉/零丢行/延迟双态
{"path": "res://tests/unit/presentation/standee_fx_sweep_test.gd", "expect": 0}, # 呈现域缺陷扫掠(d2 蓄力档/d4 庆祝句柄/d5+d8 互斥/d12 站桩按 id 解析)
{"path": "res://tests/unit/presentation/tune_sim_batch_parity_test.gd", "expect": 0}, # 测速循环批双实现对拍(帧索引/运行态/进度串/stats/报告五面——信号步进 vs await 基线)
{"path": "res://tests/unit/presentation/standee_present_values_test.gd", "expect": 0}, # 战斗一屏样张批 V5–V9(血条危险红/名字行素底不压地台/徽标 FS_HINT/护甲 PNG 绑定/素材入包)
{"path": "res://tests/unit/presentation/standee_present_values_test.gd", "expect": 0}, # 战斗一屏样张批 V5–V12(血条危险红/名字行素底不压地台与提档 FS_HINT/徽标/护甲 PNG 绑定/素材入包/地台色合并退役)
{"path": "res://tests/unit/presentation/reward_overlay_test.gd", "expect": 0}, # 奖励节点 UI 直验(盲区补漏)
{"path": "res://tests/unit/presentation/level_map_info_fog_test.gd", "expect": 0}, # 信息迷雾——呈现显隐/Boss 豁免/详情零显示/大图拟合
{"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面)
+4 -2
View File
@@ -314,7 +314,7 @@ fi
# get_tree());排除注释行(行首 #/##)与字符串字面量(双引号包裹段内的 await 非代码),
# 口径仿 S1/S2 的 grep 排除手法。
# 自检:清单内文件缺位(路径写错/被改名)即判红——防扫面为空静默绿。
S8_FILES="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd"
S8_FILES="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd"
S8_MISS=0
for f in $S8_FILES; do
if [ ! -f "$f" ]; then
@@ -356,7 +356,9 @@ fi
# 自检:扫描面为空即判红(require_scan_face);B1 文件集与三族自家文件缺位即判红
# (文件被改名=豁免名单失效,宁可红不可静默放行)。
if require_scan_face "S7" "$DIR_CORE"; then
S7_B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd"
# B1 域文件集(迭代 46 卡A 增补 battle_refresh_flow——换图刷新流程件=C13 拆分边界的 B1 域拆件,
# 承载旧资源退役/装板编排调用;architecture.md「模块归属」BattleRefreshFlow 行随反哺点落文)
S7_B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd"
S7_MISS=0
for f in $S7_B1_SET game/core/unit_container.gd game/core/status_container.gd game/core/reward_economy.gd; do
if [ ! -f "$f" ]; then
@@ -0,0 +1,650 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 换图系双基座七件专项宿主(迭代 46 卡A——对照表 V1–V12 按轮落地:V5=T2 全局受伤链路、
# V2/V3=T3 两基座、V4/V10=T4 成功事实与 GP、V6–V9=T5 五件后续、V11=T6 数据与池;
# V12 无专长基线回归归 round_flow/seal_polarity 既有断言承载)。
# 临时承伤计数专长经现行 ConfigDB 注入(表行追加+持有集传 id——正常数据配置路径,卡片 §12);
# 真实入口驱动:AutoPlayer 盲挖循环+停手/封印动作;判据绑可观测提交面(damage_taken 命中计数器
# 与 target=hero 明细行)。
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/refresh_feats_test.gd(expect 0)
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const ConfigDBScript = preload("res://core/config_db.gd")
const EffectEngineScript = preload("res://core/effect_engine.gd")
const AutoPlayer := preload("res://sim/auto_player.gd")
# 直接伤型资源类型(负向极性兑付=主角直伤单据的三型——reward 表 pierce/magic/heal 负向行)
const DIRECT_TYPES := ["pierce", "magic", "heal"]
var _db
func _init() -> void:
_db = ConfigDBScript.new()
if not _db.load_tables("res://data/tables", true):
print("[refresh_feats_test] FAIL — 数值表加载失败")
quit(1)
return
_install_dt_probe()
test_all_hero_damage_paths_emit_once()
test_no_duplicate_damage_taken_event()
test_reserve_on_accepted_dig()
test_refresh_discount_gate_and_charge()
test_double_base_stack()
test_no_feats_baseline()
test_board_refresh_fact_only_on_success()
test_mine_profit_gp()
test_stone_mitigation_all_damage()
test_rest_rotation_cap_and_clear()
test_charge_layers_consumed_same_round()
test_blast_defers_to_combat_segment()
test_seven_feats_validate_and_pool()
quit(Kit.finish("refresh_feats_test"))
# ---- 夹具族(真实入口驱动——settle_batch/stop/封印动作;直构只用于编排器受理)----
# 临时承伤计数专长(damage_taken → counter_add battle 桶——每张主角承伤单据恰 +1)
func _install_dt_probe() -> void:
if _db.crossrun.feats.row_by_id("probe_dt_counter").is_empty():
(_db.crossrun.feats.rows as Array).append({
"feat_id": "probe_dt_counter", "display_name": "承伤计数探针", "rarity": "common",
"desc": "测试夹具:每次主角承伤计数 +1",
"triggers": [{"on": "damage_taken", "do": {"kind": "counter_add", "counter_id": "dt_probe_hits", "value": 1}}],
})
func _orch(seed_v: int):
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(seed_v)
return load("res://core/battle_orchestrator.gd").new(_db, rng)
func _enc(feats: Array, voucher_rap: int = 0, armor: int = 10, hero_hp: int = 200) -> Dictionary:
return {"level_index": 1, "node_depth": 4, "hero_hp": hero_hp, "hero_atk": 8, "hero_max_hp": 200,
"hero_armor_class": armor, "hero_ap_bonus": 0, "attunement": [], "feats": feats, "tree_rules": [],
"battle_index": 1, "voucher_rap": voucher_rap, "voucher_time_sand": 0, "node_type": "boss", "adv_bucket": {}}
func _probe_count(orch) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, "dt_probe_hits")
# 主角承伤明细行收集(type=damage 且 target=hero——敌方技与棋盘主角直伤两类单据的行面)
func _collect_hero_rows(orch) -> Array:
var rows: Array = []
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "damage" and String(row.get("target", "")) == "hero":
rows.append(row))
return rows
# 按来源类分桶计数(rows 收集器的只读后处理)
func _count_by_source(rows: Array, source: String) -> int:
var n := 0
for r_v in rows:
if String((r_v as Dictionary).get("source_class", "")) == source:
n += 1
return n
# AutoPlayer 盲挖循环直到 cell_revealed 谓词命中(真实挖掘入口;返回 {"cell","evt"} 或空)
func _dig_until(orch, pred: Callable) -> Dictionary:
var ms = orch.minesweeper
var hit := {}
ms.cell_revealed.connect(func(evt):
if hit.is_empty() and bool(pred.call(evt)):
hit["cell"] = evt.cell
hit["evt"] = evt)
var guard := 40
while hit.is_empty() and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(ms)
if plan.is_empty():
break
if plan.hero_cell != ms.hero_pos():
ms.settle_batch([{"action": "move", "cell": plan.hero_cell}])
ms.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
return hit
# 直接伤型快照判定(未揭示格可查——reward_economy 装板期摇定身份,示警烟火同源读面)
func _is_direct_type(orch, c: Vector2i) -> bool:
var snap_v = orch.reward_grant().reward_economy.query_full_snapshot(c)
return snap_v is Dictionary and DIRECT_TYPES.has(String((snap_v as Dictionary).get("cell_type", "")))
# 可封印的直接伤型安全格扫描(未揭示+非雷+直接伤型+邻接可达 revealed 格——封印动作真实落点位)
func _find_sealable_direct(orch) -> Vector2i:
var ms = orch.minesweeper
var b = ms.current_board()
var revealed := {}
for c_v in ms.revealed_cells():
revealed[c_v] = true
for y in b.height:
for x in b.width:
var c := Vector2i(x, y)
if ms.is_revealed(c) or b.is_mine(c) or ms.has_seal(c) or not _is_direct_type(orch, c):
continue
for n_v in b.neighbors8(c):
var n := Vector2i(n_v)
if revealed.has(n) and (ms.compute_move_path(n).size() > 0 or n == ms.hero_pos()):
return c
return Vector2i(-1, -1)
# 盲挖扩板直至出现可封印直接伤型安全格(有界;返回格位或 (-1,-1))
func _seal_direct_with_expansion(orch) -> Vector2i:
var c := _find_sealable_direct(orch)
var guard := 12
while c.x < 0 and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if plan.is_empty():
break
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
c = _find_sealable_direct(orch)
return c
# ---- V5 全局受伤链路(迭代 46 卡A T2)----
# 四条真实路径(敌方攻击/危险格/错误封印/地形)逐路驱动:每条至少一张主角承伤单据在案,
# 且承伤计数器 == 主角承伤单据行数(每单据恰发射一次的全量判据——重复发射即计数器>行数)。
func test_all_hero_damage_paths_emit_once() -> void:
print("--- V5 全局 damage_taken:四路径逐路恰一次 ---")
# ① 敌方攻击路径(Boss 战零挖掘连停三轮——干净样本:零地形零资源单据;多轮保证伤害技在案)
var orch1 = _orch(46001)
var rows1 := _collect_hero_rows(orch1)
Kit.check("V5① 前置:战斗受理", orch1.begin_battle(_enc(["probe_dt_counter"])))
for _i in 3:
orch1.minesweeper.settle_batch([{"action": "stop"}])
var enemy_rows := _count_by_source(rows1, "enemy_action")
Kit.check("V5① 敌方攻击路径:承伤单据在案且逐单恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [enemy_rows, _probe_count(orch1)],
enemy_rows >= 1 and _probe_count(orch1) == enemy_rows)
# ② 地形路径(AutoPlayer 真挖至伤害型变异格开出——发射在授予段开单点、单据标 hero_damage_consumed)
var orch2 = _orch(46002)
var rows2 := _collect_hero_rows(orch2)
Kit.check("V5② 前置:战斗受理", orch2.begin_battle(_enc(["probe_dt_counter"])))
var terr := _dig_until(orch2, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("V5② 前置:伤害型变异格已挖开(%s)" % str(terr.get("cell", Vector2i(-1, -1))), not terr.is_empty())
orch2.minesweeper.settle_batch([{"action": "stop"}])
var terrain_rows := _count_by_source(rows2, "地形变异")
Kit.check("V5② 地形路径:地形单据在案且全单据逐张恰一次(行 %d=计数 %d——盲挖途经单据一并计入)〔换图系批 V5〕" % [rows2.size(), _probe_count(orch2)],
terrain_rows >= 1 and _probe_count(orch2) == rows2.size())
# ③ 危险格路径(AutoPlayer 真挖直接伤型危险格——负向直伤单据)
var orch3 = _orch(46003)
var rows3 := _collect_hero_rows(orch3)
Kit.check("V5③ 前置:战斗受理", orch3.begin_battle(_enc(["probe_dt_counter"])))
var mine_hit := _dig_until(orch3, func(evt):
return String(evt.get("kind", "")) == "mine" and _is_direct_type(orch3, evt.cell))
Kit.check("V5③ 前置:直接伤型危险格已挖开(%s)" % str(mine_hit.get("cell", Vector2i(-1, -1))), not mine_hit.is_empty())
orch3.minesweeper.settle_batch([{"action": "stop"}])
var danger_rows := _count_by_source(rows3, "mine_hit")
Kit.check("V5③ 危险格路径:直伤单据在案且全单据逐张恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [rows3.size(), _probe_count(orch3)],
danger_rows >= 1 and _probe_count(orch3) == rows3.size())
# ④ 错误封印路径(扩板→封直接伤型安全格不挖→停手轮末判标错——负向直伤单据)
var orch4 = _orch(46004)
var rows4 := _collect_hero_rows(orch4)
Kit.check("V5④ 前置:战斗受理", orch4.begin_battle(_enc(["probe_dt_counter"])))
var scell := _seal_direct_with_expansion(orch4)
Kit.check("V5④ 前置:可封直接伤型安全格在位(%s)" % str(scell), scell.x >= 0)
if scell.x >= 0:
var plan4 = AutoPlayer.choose_dig(orch4.minesweeper)
# 封印动作真实落点:先就位到目标邻接可达格(封印距离同挖掘距离)
var seal_plan := _plan_seal(orch4, scell)
if not seal_plan.is_empty():
if seal_plan["hero_cell"] != orch4.minesweeper.hero_pos():
orch4.minesweeper.settle_batch([{"action": "move", "cell": seal_plan["hero_cell"]}])
var seal_out: Array = orch4.minesweeper.settle_batch([{"action": "seal_place", "cell": scell}])
Kit.check("V5④ 前置:封印受理", not seal_out.is_empty() and bool((seal_out[0] as Dictionary).get("accepted", false)))
orch4.minesweeper.settle_batch([{"action": "stop"}])
var sealwrong_rows := _count_by_source(rows4, "seal_miss")
Kit.check("V5④ 错误封印路径:直伤单据在案且全单据逐张恰一次(行 %d=计数 %d)〔换图系批 V5〕" % [rows4.size(), _probe_count(orch4)],
sealwrong_rows >= 1 and _probe_count(orch4) == rows4.size())
# 封印落点计划(目标格邻接可达 revealed 位——AutoPlayer._plan_on 同构最小私有形)
func _plan_seal(orch, target: Vector2i) -> Dictionary:
var ms = orch.minesweeper
for n_v in ms.current_board().neighbors8(target):
var n := Vector2i(n_v)
if ms.is_revealed(n) and (ms.compute_move_path(n).size() > 0 or n == ms.hero_pos()):
return {"hero_cell": n}
return {}
# 同一伤害不得出现两次消费(重复发射=计数器>行数):敌方路径与地形路径两样本逐位核对——
# 地形单据带 hero_damage_consumed 标记,战斗段末 _resolve_board_flat_hero 跳过二次发射由此钉住。
func test_no_duplicate_damage_taken_event() -> void:
print("--- V5 全局 damage_taken:无重复消费 ---")
var orch1 = _orch(46011)
var rows1 := _collect_hero_rows(orch1)
Kit.check("ND 前置:战斗受理", orch1.begin_battle(_enc(["probe_dt_counter"])))
for _i in 2:
orch1.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("ND 敌方路径零重复(行 %d=计数 %d——重复消费即计数>行)〔换图系批 V5〕" % [rows1.size(), _probe_count(orch1)],
rows1.size() >= 1 and _probe_count(orch1) == rows1.size())
var orch2 = _orch(46012)
var rows2 := _collect_hero_rows(orch2)
Kit.check("ND 前置:战斗受理", orch2.begin_battle(_enc(["probe_dt_counter"])))
var terr2 := _dig_until(orch2, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("ND 前置:伤害型变异格已挖开(%s)" % str(terr2.get("cell", Vector2i(-1, -1))), not terr2.is_empty())
orch2.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("ND 地形路径零重复(行 %d=计数 %d——context 标记闸失效即计数翻倍)〔换图系批 V5〕" % [rows2.size(), _probe_count(orch2)],
rows2.size() >= 1 and _probe_count(orch2) == rows2.size())
# ---- V2/V3 两件基座(迭代 46 卡A T3)----
# 表驱动停手攒 RAP(每轮 1:1 转存——无转存率订阅缺省)
func _bank_rap(orch, rounds_n: int) -> void:
for _i in rounds_n:
orch.minesweeper.settle_batch([{"action": "stop"}])
# AutoPlayer 单次付费挖格(真实挖掘入口——移位+dig 一动作)
func _auto_dig(orch) -> void:
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if not plan.is_empty():
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
# V2 矿脉储能:受理的付费挖格 RAP 恰 +1、AP 按表扣、板资源不变
func test_reserve_on_accepted_dig() -> void:
print("--- V2 矿脉储能:付费挖格产 RAP ---")
var orch = _orch(46021)
Kit.check("V2 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve"])))
_bank_rap(orch, 1)
var rap_before: int = orch.action_economy.rap
var ap_before: int = orch.action_economy.ap
_auto_dig(orch)
var dig_cost: int = int(_db.run.action_economy.dig_ap_cost)
Kit.check("V2 受理付费挖格 RAP 恰 +1(%d→%d)〔换图系批 V2〕" % [rap_before, orch.action_economy.rap],
orch.action_economy.rap == rap_before + 1)
Kit.check("V2 AP 按表扣(%d→%d,dig 价 %d)〔换图系批 V2〕" % [ap_before, orch.action_economy.ap, dig_cost],
orch.action_economy.ap == ap_before - dig_cost)
Kit.check("V2 受理记账闭合:AP 耗尽轮 RAP 增量恰等于受理挖数(AP 消耗数——每受理恰 +1,未受理零入池)〔换图系批 V2〕",
_drain_round_rap_accounting(orch))
# V2 辅证:挖到 AP 耗尽——RAP 增量 == 受理挖数(每受理恰 +1;移位耗 AP 不入账故按挖计数)
func _drain_round_rap_accounting(orch) -> bool:
var rap0: int = orch.action_economy.rap
var digs := 0
var guard := 24
while orch.action_economy.phase == "Active" and guard > 0:
guard -= 1
var plan = AutoPlayer.choose_dig(orch.minesweeper)
if plan.is_empty():
break
if plan.hero_cell != orch.minesweeper.hero_pos():
orch.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
if orch.action_economy.phase != "Active":
break # 移位耗尽 AP 收轮——本轮不再有受理挖
orch.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
digs += 1
return digs > 0 and orch.action_economy.rap - rap0 == digs
# V3 省力周转:折价门槛与扣费(can 与执行同源;base 8→扣 4)
func test_refresh_discount_gate_and_charge() -> void:
print("--- V3 省力周转:折价门槛与扣费 ---")
var orch = _orch(46022)
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["easy_swap"])))
_bank_rap(orch, 1)
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
var discounted: int = int(floor(float(base_cost) * 50.0 / 100.0))
var rap0: int = orch.action_economy.rap
Kit.check("V3 门同源:can_refresh 与折后价同源(RAP %d ≥ 折后 %d)〔换图系批 V3〕" % [rap0, discounted],
orch.can_refresh())
Kit.check("V3 第 1 次刷新受理", orch.player_request_refresh())
Kit.check("V3 折后单次扣费(%d→%d,−%d)〔换图系批 V3〕" % [rap0, orch.action_economy.rap, discounted],
orch.action_economy.rap == rap0 - discounted)
Kit.check("V3 第 2 次刷新受理(RAP=%d)" % orch.action_economy.rap, orch.player_request_refresh())
Kit.check("V3 两次折后扣费累计(RAP=%d=%d−2×%d)〔换图系批 V3〕" % [orch.action_economy.rap, rap0, discounted],
orch.action_economy.rap == rap0 - 2 * discounted)
Kit.check("V3 门槛拒:RAP 不足折后价零受理(can=false 且执行拒)〔换图系批 V3〕",
orch.action_economy.rap < discounted and not orch.can_refresh() and not orch.player_request_refresh())
# V3 双基座叠加:挖格产 RAP 与刷新折价同场生效
func test_double_base_stack() -> void:
print("--- V3 双基座叠加 ---")
var orch = _orch(46023)
Kit.check("V3 前置:战斗受理", orch.begin_battle(_enc(["vein_reserve", "easy_swap"])))
_bank_rap(orch, 1)
var rap_before: int = orch.action_economy.rap
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
_auto_dig(orch)
Kit.check("V3 双基座:挖格产 RAP +1(%d→%d)〔换图系批 V3〕" % [rap_before, orch.action_economy.rap],
orch.action_economy.rap == rap_before + 1)
var rap_after_dig: int = orch.action_economy.rap
Kit.check("V3 双基座:刷新按折后价扣(−%d)〔换图系批 V3〕" % int(base_cost / 2),
orch.player_request_refresh() and orch.action_economy.rap == rap_after_dig - int(base_cost / 2))
# V12(refresh_feats 侧):无专长基线——挖格零产 RAP、刷新按表基线扣、AP 保留、上限照旧
func test_no_feats_baseline() -> void:
print("--- V12 无专长基线(refresh_feats 侧) ---")
var orch = _orch(46024)
Kit.check("V12 前置:战斗受理", orch.begin_battle(_enc([])))
_bank_rap(orch, 3)
var rap_before: int = orch.action_economy.rap
var ap_before: int = orch.action_economy.ap
var base_cost: int = int(_db.run.action_economy.rap_refresh_cost)
_auto_dig(orch)
Kit.check("V12 无基座:挖格零产 RAP(%d 不变)〔换图系批 V12〕" % rap_before, orch.action_economy.rap == rap_before)
Kit.check("V12 刷新受理并按表基线扣(−%d)〔换图系批 V12〕" % base_cost,
orch.player_request_refresh() and orch.action_economy.rap == rap_before - base_cost)
Kit.check("V12 AP 保留、相位 Active(基线刷新不重置)〔换图系批 V12〕",
orch.action_economy.ap == ap_before - int(_db.run.action_economy.dig_ap_cost) and orch.action_economy.phase == "Active")
Kit.check("V12 每轮上限照旧:第 3 次拒(RAP 足额仍拒)〔换图系批 V12〕",
orch.player_request_refresh() and not orch.player_request_refresh())
# ---- V4/V10 成功刷新事实与命中施加(迭代 46 卡A T4)----
# 成功刷新事实探针专长安装(board_refreshed → counter_add——成功事实的可观测计数面)
func _install_br_probe() -> void:
if _db.crossrun.feats.row_by_id("probe_br_counter").is_empty():
(_db.crossrun.feats.rows as Array).append({
"feat_id": "probe_br_counter", "display_name": "刷新事实探针", "rarity": "common",
"desc": "测试夹具:每次成功刷新计数 +1",
"triggers": [{"on": "board_refreshed", "do": {"kind": "counter_add", "counter_id": "br_probe_hits", "value": 1}}],
})
func _br_count(orch) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, "br_probe_hits")
# V4 成功事实只在新板就位后发射:成功恰一次;非 Active/每轮上限/RAP 不足三拒面全零事实零换图
func test_board_refresh_fact_only_on_success() -> void:
print("--- V4 成功刷新事实:拒绝零事实 ---")
_install_br_probe()
var orch = _orch(46031)
var swaps := {"n": 0}
var waiting_rejected := {"v": false}
orch.board_changed.connect(func(): swaps["n"] += 1)
# 非 Active 探针(round_flow 同款:借衰减行在结算段内同步探测一次——Waiting 相位刷新必拒)
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "decay" and not bool(waiting_rejected["v"]):
waiting_rejected["v"] = not orch.player_request_refresh())
Kit.check("V4 前置:战斗受理", orch.begin_battle(_enc(["probe_br_counter"])))
_bank_rap(orch, 3) # RAP=3×AP;首轮结算衰减行触发 Waiting 探针
var swaps0: int = swaps["n"]
Kit.check("V4 非 Active 拒零事实(结算段内探测拒绝)〔换图系批 V4〕",
bool(waiting_rejected["v"]) and _br_count(orch) == 0 and int(swaps["n"]) == swaps0)
# 成功 ×2:事实计数与换图信号逐次 +1
Kit.check("V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕",
orch.player_request_refresh() and _br_count(orch) == 1 and int(swaps["n"]) == swaps0 + 1)
Kit.check("V4 第 2 次成功(事实 +1 换图 +1)〔换图系批 V4〕",
orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2)
# 每轮上限拒:used=2 满门、RAP 足额——零事实零换图
Kit.check("V4 上限拒零事实(RAP %d 足额仍拒)〔换图系批 V4〕" % orch.action_economy.rap,
not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2)
# RAP 不足拒:停手进新轮(used 重置、停手再入 RAP)→ 消费至 RAP 0 → 拒(零事实零换图;
# 纯 RAP 不足拒绝面已由 V3 门槛拒单测钉住,此处并拦形态)
orch.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("V4 新轮受理两次(used 重置、RAP 复入)〔换图系批 V4〕",
orch.action_economy.phase == "Active" and orch.player_request_refresh() and orch.player_request_refresh() and _br_count(orch) == 4)
Kit.check("V4 RAP 不足拒零事实(RAP %d < 价 %d)〔换图系批 V4〕" % [orch.action_economy.rap, int(_db.run.action_economy.rap_refresh_cost)],
orch.action_economy.rap < int(_db.run.action_economy.rap_refresh_cost) and not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 4 and int(swaps["n"]) == swaps0 + 4)
# V10 矿道红利:每次成功刷新 escrow 恰 +1 且 fx_grant_gp 行在案;拒绝零变化
func test_mine_profit_gp() -> void:
print("--- V10 矿道红利:刷新固定 GP ---")
var orch = _orch(46032)
var gp_rows := {"n": 0}
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "effect" and String(row.get("effect_id", "")) == "fx_grant_gp":
gp_rows["n"] += 1)
Kit.check("V10 前置:战斗受理", orch.begin_battle(_enc(["mine_profit"])))
_bank_rap(orch, 2)
var escrow0: int = orch.reward_grant().escrow
Kit.check("V10 第 1 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [escrow0, orch.reward_grant().escrow],
orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 1)
Kit.check("V10 fx_grant_gp 行在案(%d 行)〔换图系批 V10〕" % int(gp_rows["n"]), int(gp_rows["n"]) == 1)
Kit.check("V10 第 2 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [orch.reward_grant().escrow, orch.reward_grant().escrow + 1],
orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2)
# 拒绝零变化:RAP 不足
Kit.check("V10 拒绝零变化(escrow=%d)〔换图系批 V10〕" % orch.reward_grant().escrow,
not orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2 and int(gp_rows["n"]) == 2)
# ---- V6/V8/V9/V7 五件后续效果(迭代 46 卡A T5)----
func _counter(orch, counter_id: String) -> int:
return EffectEngineScript.counter(orch.fx_host().counters_ctx, counter_id)
# 伤害明细行泛收集(type=damage 全量——按来源类与值断言)
func _collect_damage_rows(orch) -> Array:
var rows: Array = []
orch.step_row.connect(func(row):
if String(row.get("type", "")) == "damage":
rows.append(row))
return rows
func _damage_rows_by_source(rows: Array, source: String) -> Array:
var out: Array = []
for r_v in rows:
var r := r_v as Dictionary
if String(r.get("source_class", "")) == source:
out.append(r)
return out
# V6 石层缓冲:全局固定减伤(危险格 pierce 8→6 两档钉死:单刷减 2·同轮多刷不叠加·未刷新轮不减;
# 地形路径减后值=max(下限, effect_value−2)——含下限保持;敌方路径照常单次发射)
func test_stone_mitigation_all_damage() -> void:
print("--- V6 石层缓冲:全局固定减伤 ---")
# S1 同轮双刷 → 危险格 pierce 8 减 2=6(叠加即 4——多刷不增钉死)
var orch1 = _orch(46041)
var rows1 := _collect_damage_rows(orch1)
Kit.check("V6 S1 前置:战斗受理", orch1.begin_battle(_enc(["stone_mitigation"])))
_bank_rap(orch1, 3)
Kit.check("V6 S1 同轮双刷受理", orch1.player_request_refresh() and orch1.player_request_refresh())
Kit.check("V6 S1 guard 累计 2(同轮双刷)", _counter(orch1, "round_swap_guard") == 2)
var mine1 := _dig_until(orch1, func(evt):
return String(evt.get("kind", "")) == "mine" and _is_direct_type(orch1, evt.cell))
Kit.check("V6 S1 前置:直接伤型危险格已挖开(%s)" % str(mine1.get("cell", Vector2i(-1, -1))), not mine1.is_empty())
orch1.minesweeper.settle_batch([{"action": "stop"}])
var pierce_rows1 := _damage_rows_by_source(rows1, "mine_hit")
Kit.check("V6 S1 危险格减伤恰 2 且多刷不叠加(行值 %s∈[6]——叠加态应为 4)〔换图系批 V6〕" % str(pierce_rows1.map(func(r): return int(r.value))),
pierce_rows1.size() >= 1 and int((pierce_rows1[0] as Dictionary).get("value", -1)) == 6)
# S2 未刷新轮:guard 随 round_start 清空 → 同型危险格不减(pierce/magic 表幂 8——heal 型幂 6 不同锚勿混)
var mine2 := _dig_until(orch1, func(evt):
var snap_v2 = orch1.reward_grant().reward_economy.query_full_snapshot(evt.cell)
return String(evt.get("kind", "")) == "mine" and snap_v2 is Dictionary \
and ["pierce", "magic"].has(String((snap_v2 as Dictionary).get("cell_type", ""))))
Kit.check("V6 S2 前置:第二直接伤型危险格已挖开(%s)" % str(mine2.get("cell", Vector2i(-1, -1))), not mine2.is_empty())
orch1.minesweeper.settle_batch([{"action": "stop"}])
var pierce_rows2 := _damage_rows_by_source(rows1, "mine_hit")
Kit.check("V6 S2 未刷新轮不减伤(新轮行值 %s 含 8——round 桶轮始清空)〔换图系批 V6〕" % str(pierce_rows2.map(func(r): return int(r.value))),
pierce_rows2.size() >= 2 and int((pierce_rows2[pierce_rows2.size() - 1] as Dictionary).get("value", -1)) == 8)
# S3 地形路径:减后值 = max(下限, effect_value−2)——下限保持与定值减一并钉死
# (种子 46002=V5② 已证含 flat_damage 变异板;刷新换板后变异重布—— digging guard 内可达)
var orch3 = _orch(46002)
var rows3 := _collect_damage_rows(orch3)
Kit.check("V6 S3 前置:战斗受理", orch3.begin_battle(_enc(["stone_mitigation"])))
_bank_rap(orch3, 2)
Kit.check("V6 S3 单刷受理", orch3.player_request_refresh())
var terr3 := _dig_until(orch3, func(evt):
for vid_v in (evt.get("variant_ids", []) as Array):
if String(_db.combat.variation.row_by_id(String(vid_v)).get("effect", "")) == "flat_damage":
return true
return false)
Kit.check("V6 S3 前置:伤害型变异格已挖开(%s)" % str(terr3.get("cell", Vector2i(-1, -1))), not terr3.is_empty())
orch3.minesweeper.settle_batch([{"action": "stop"}])
var terrain_rows := _damage_rows_by_source(rows3, "地形变异")
var floor_v: int = int(_db.combat.settlement.damage_roll.damage_floor)
var ev_ok := true
for tr_v in terrain_rows:
var tr := tr_v as Dictionary
var vid := String(tr.get("variant_id", ""))
var ev: int = int(_db.combat.variation.row_by_id(vid).get("effect_value", 0))
if int(tr.get("value", -1)) != maxi(floor_v, ev - 2):
ev_ok = false
Kit.check("V6 S3 地形减伤=max(下限,effect_value−2) 逐单闭合(%d 单)〔换图系批 V6〕" % terrain_rows.size(),
terrain_rows.size() >= 1 and ev_ok)
# V9 坑道小憩:轮末合计回复(白盒预置承载上限 3——计数器直改属数据面注入;
# 自然累计 2 与满血未生效两形态分战场钉死)
func test_rest_rotation_cap_and_clear() -> void:
print("--- V9 坑道小憩:轮末恢复 ---")
# R1 白盒预置 3 层 + 刷新闸(when<3 拒第 4 层)→ 停手回复 min(3, 缺血)——护甲降档保缺血 ≥3
var orch1 = _orch(46051)
var rows1: Array = []
orch1.step_row.connect(func(row): rows1.append(row))
Kit.check("V9 R1 前置:战斗受理", orch1.begin_battle(_enc(["tunnel_rest"], 0, 10, 100)))
_bank_rap(orch1, 1) # 开局血差 100(hero_hp 100/max 200)——缺血 ≥3 恒成立,回复面确定
var missing: int = 200 - int(orch1._enemy_system.hero_unit().get("hp", 0))
EffectEngineScript.counter_add(orch1.fx_host().counters_ctx, "round_swap_rest", 3)
Kit.check("V9 R1 预置 3 层后刷新被闸(when<3——上限 3)", orch1.player_request_refresh() and _counter(orch1, "round_swap_rest") == 3)
orch1.minesweeper.settle_batch([{"action": "stop"}])
var heal_rows1: Array = []
var idle_rows1 := 0
for r_v in rows1:
var r := r_v as Dictionary
if String(r.get("type", "")) == "heal" and String(r.get("source_class", "")) == "fx_board_refresh":
heal_rows1.append(r)
if String(r.get("type", "")) == "reward_ineffective" and String(r.get("batch_key", "")) == "round_swap_rest":
idle_rows1 += 1
Kit.check("V9 R1 轮末回复=min(3,缺血%d) 且层清 0(heal 行值 %s)〔换图系批 V9〕" % [missing, str(heal_rows1.map(func(r): return int(r.value)))],
missing >= 3 and heal_rows1.size() == 1 and int((heal_rows1[0] as Dictionary).get("value", -1)) == 3 and _counter(orch1, "round_swap_rest") == 0)
# R2 满血未生效:首轮刷新后停手——授予段 heal 时点主角仍满血(combat 段还手在其后)
var orch2 = _orch(46052)
var rows2: Array = []
orch2.step_row.connect(func(row): rows2.append(row))
Kit.check("V9 R2 前置:战斗受理", orch2.begin_battle(_enc(["tunnel_rest"], 8)))
Kit.check("V9 R2 首轮刷新受理(voucher_rap 入池)", orch2.player_request_refresh())
orch2.minesweeper.settle_batch([{"action": "stop"}])
var heal_rows2 := 0
var idle2 := 0
for r2_v in rows2:
var r2 := r2_v as Dictionary
if String(r2.get("type", "")) == "heal" and String(r2.get("source_class", "")) == "fx_board_refresh":
heal_rows2 += 1
if String(r2.get("type", "")) == "reward_ineffective" and String(r2.get("batch_key", "")) == "round_swap_rest":
idle2 += 1
Kit.check("V9 R2 满血未生效行在案且零 heal 行(未生效 %d·heal %d)〔换图系批 V9〕" % [idle2, heal_rows2],
idle2 == 1 and heal_rows2 == 0 and _counter(orch2, "round_swap_rest") == 0)
# R3 自然累计:同轮双刷 → 回复 2(上限 3 的闸面由 R1 白盒钉住,此处证自然路径)
var orch3 = _orch(46053)
var rows3: Array = []
orch3.step_row.connect(func(row): rows3.append(row))
Kit.check("V9 R3 前置:战斗受理", orch3.begin_battle(_enc(["tunnel_rest"])))
_bank_rap(orch3, 2)
Kit.check("V9 R3 同轮双刷受理", orch3.player_request_refresh() and orch3.player_request_refresh())
Kit.check("V9 R3 自然累计 2 层", _counter(orch3, "round_swap_rest") == 2)
orch3.minesweeper.settle_batch([{"action": "stop"}])
var heal3 := 0
for r3_v in rows3:
if String((r3_v as Dictionary).get("type", "")) == "heal" and String((r3_v as Dictionary).get("source_class", "")) == "fx_board_refresh":
heal3 += 1
Kit.check("V9 R3 轮末 heal 行值 2(自然累计路径)〔换图系批 V9〕",
heal3 == 1 and _counter(orch3, "round_swap_rest") == 0)
# V8 蓄力周转:当轮攻击消费全部层数(上限 3 闸=白盒预置;战斗新建自然清空)
func test_charge_layers_consumed_same_round() -> void:
print("--- V8 蓄力周转:攻击消费层 ---")
var orch1 = _orch(46061)
var rows1 := _collect_damage_rows(orch1)
Kit.check("V8 前置:战斗受理", orch1.begin_battle(_enc(["charge_rotation"], 16)))
# 上限 3 闸:白盒预置 3 层后刷新被拒层(when<3)
EffectEngineScript.counter_add(orch1.fx_host().counters_ctx, "swap_charge_layers", 3)
Kit.check("V8 上限 3 闸(预置 3 刷新不加层)", orch1.player_request_refresh() and _counter(orch1, "swap_charge_layers") == 3)
# 当轮攻击消费:停手 → 主角先手攻击 → 层数全消费清零
orch1.minesweeper.settle_batch([{"action": "stop"}])
var attack_rows := 0
for r_v in rows1:
if String((r_v as Dictionary).get("source_class", "")) == "hero_attack":
attack_rows += 1
Kit.check("V8 当轮攻击消费全部层(攻击行在案 %d·层清 0)〔换图系批 V8〕" % attack_rows,
attack_rows >= 1 and _counter(orch1, "swap_charge_layers") == 0)
# 自然累计:双刷 2 层(voucher_rap 16 入池)→ 停手消费归零
var orch2 = _orch(46062)
Kit.check("V8 前置:战斗受理", orch2.begin_battle(_enc(["charge_rotation"], 16)))
Kit.check("V8 战斗新建层 0(引擎重建自然清空)", _counter(orch2, "swap_charge_layers") == 0)
Kit.check("V8 双刷累计 2 层", orch2.player_request_refresh() and orch2.player_request_refresh() and _counter(orch2, "swap_charge_layers") == 2)
orch2.minesweeper.settle_batch([{"action": "stop"}])
Kit.check("V8 攻击后层清 0(消费面闭合)〔换图系批 V8〕", _counter(orch2, "swap_charge_layers") == 0)
# V7 崩壁冲击:延迟群伤单(刷新时点零伤害行——战斗段末按存活目标结账)
func test_blast_defers_to_combat_segment() -> void:
print("--- V7 崩壁冲击:延迟群伤 ---")
var orch = _orch(46071)
var rows := _collect_damage_rows(orch)
Kit.check("V7 前置:战斗受理", orch.begin_battle(_enc(["wall_blast"], 8)))
Kit.check("V7 刷新受理(voucher_rap 入池)", orch.player_request_refresh())
Kit.check("V7 刷新时点零伤害行(授予段不结账——案C 延迟)〔换图系批 V7〕",
_damage_rows_by_source(rows, "fx_board_refresh").is_empty())
orch.minesweeper.settle_batch([{"action": "stop"}])
var blast_rows := _damage_rows_by_source(rows, "fx_board_refresh")
Kit.check("V7 战斗段末群伤单恰一单(全体存活敌 ×2——行值 2 count≥1)〔换图系批 V7〕",
blast_rows.size() == 1 and int((blast_rows[0] as Dictionary).get("value", -1)) == 2
and int((blast_rows[0] as Dictionary).get("count", 0)) >= 1)
# ---- V11 数据与池(迭代 46 卡A T6)----
# 七件 validate/在册/稀有度 cap 对表/跨表计数器引用闭合/desc 禁词
func test_seven_feats_validate_and_pool() -> void:
print("--- V11 七件数据与池 ---")
var seven := {"vein_reserve": "excellent", "easy_swap": "excellent", "wall_blast": "excellent",
"charge_rotation": "excellent", "stone_mitigation": "common", "tunnel_rest": "common", "mine_profit": "rare"}
Kit.check("V11 feats.validate 零违例(七行全过词表/行键白名单/前缀校验)〔换图系批 V11〕",
_db.crossrun.feats.validate().is_empty())
var all_in := true
var desc_bad := 0
for fid in seven.keys():
var row: Dictionary = _db.crossrun.feats.row_by_id(String(fid))
if row.is_empty() or String(row.get("rarity", "")) != String(seven[fid]) or int(row.get("cap", 0)) != 1:
all_in = false
if String(row.get("desc", "")).contains("雷") or String(row.get("desc", "")).contains("旗"):
desc_bad += 1
Kit.check("V11 七行在册且稀有度/cap 逐件对表〔换图系批 V11〕", all_in)
Kit.check("V11 七件 desc 禁词零命中(雷/旗字面)〔换图系批 V11〕", desc_bad == 0)
# 跨表计数器引用闭合(判据域=换图系七件自身:value_counter/when-id 读取全部有 counter_add 写入方;
# 预存行的消费点记账形态〔如完美保险 insurance_used 由授予段落账〕不在此判据域)
var written := {}
for row_v in (_db.crossrun.feats.rows as Array):
for tv in ((row_v as Dictionary).get("triggers", []) as Array):
var do_d: Dictionary = (tv as Dictionary).get("do", {})
if String(do_d.get("kind", "")) == "counter_add":
written[String(do_d.get("counter_id", ""))] = true
var refs_ok := true
for fid2 in seven.keys():
var row2: Dictionary = _db.crossrun.feats.row_by_id(String(fid2))
for tv2 in ((row2.get("triggers", []) as Array)):
var tr2 := tv2 as Dictionary
var do2: Dictionary = tr2.get("do", {})
for reader in [String(do2.get("value_counter", "")), String(do2.get("value_from", ""))]:
if reader != "" and not written.has(reader):
refs_ok = false
for c_v in (tr2.get("when", []) as Array):
var cid := String((c_v as Dictionary).get("id", ""))
if (c_v as Dictionary).get("fact", "") == "counter" and cid != "" and not written.has(cid):
refs_ok = false
Kit.check("V11 七件跨表计数器引用闭合(value_counter/when-id 全部有 counter_add 写入方)〔换图系批 V11〕", refs_ok)
Kit.check("V11 换图系三计数器在写入集(swap_charge_layers/round_swap_guard/round_swap_rest)〔换图系批 V11〕",
written.has("swap_charge_layers") and written.has("round_swap_guard") and written.has("round_swap_rest"))
@@ -0,0 +1 @@
uid://bmsh0idfmpw3j
+3 -3
View File
@@ -461,8 +461,8 @@ func _smoke_adventure() -> void:
func _smoke_feats() -> void:
print("--- 专长重设计实装(feat_table 四级 · 专长带池接线 · 订阅面逐件)---")
# ① 现行表牌库构成:30 件全量入池——102 数值令牌+30 relic 令牌(四级 rarity 直映射)
Kit.check("feat_table 已加载(30 件=基础 27+迁入 2+卡② 完美保险 1——迭代 31 卡② 换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 30)
# ① 现行表牌库构成:37 件全量入池——102 数值令牌+37 relic 令牌(四级 rarity 直映射)
Kit.check("feat_table 已加载(37 件=基础 27+迁入 2+完美保险 1+迭代 46 换图系七件——换代)", _db.crossrun.feats != null and (_db.crossrun.feats.rows as Array).size() == 37)
Kit.check("feat_table.validate 零违例(四级 rarity/cap 域/trigger 词表/行键白名单全过)", _db.crossrun.feats.validate().is_empty())
var a_r0 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(71))
Kit.check("专长带场景:冒险启动(seed 71)", a_r0.start_adventure(71))
@@ -473,7 +473,7 @@ func _smoke_feats() -> void:
feat_tokens += 1
var tk := String((t as Dictionary).get("tier", ""))
tier_counts[tk] = int(tier_counts.get(tk, 0)) + 1
Kit.check("专长带入池:30 件四级直映射(普通 12/优秀 9/稀有 9/传说 0)——牌库 102+30=132〔专长批 V2+卡② 完美保险迁入〕", feat_tokens == 30 and int(tier_counts.get("common", 0)) == 12 and int(tier_counts.get("excellent", 0)) == 9 and int(tier_counts.get("rare", 0)) == 9 and a_r0.deck().deck.size() == 132)
Kit.check("专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕", feat_tokens == 37 and int(tier_counts.get("common", 0)) == 14 and int(tier_counts.get("excellent", 0)) == 13 and int(tier_counts.get("rare", 0)) == 10 and a_r0.deck().deck.size() == 139)
# ② 旧 27 件零残留钉面(V2——arcane_ember 系旧件表首行;全仓 grep 零残留归静态核查)
Kit.check("旧 27 件已退役:arcane_ember/magma_body 表内零残留〔专长批 V2〕", _db.crossrun.feats.row_by_id("arcane_ember").is_empty() and _db.crossrun.feats.row_by_id("magma_body").is_empty())
Kit.check("迁出收尾:treasure_items 无 venom_edge/seal_burst 行(同调目录自然收窄)〔专长批 V13〕", _db.crossrun.treasure_items.row_by_id("venom_edge").is_empty() and _db.crossrun.treasure_items.row_by_id("seal_burst").is_empty())
+13 -5
View File
@@ -51,21 +51,29 @@ func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator:
# 公式 N×cell ≤ 可用高−12 必须计入 (N−1)×2px 网格间距,否则网格实高溢出被 clip_contents 裁掉;
# 现公式 = 计入间距 + 垂直预留 24px〔上 12/下 12——底部不贴 D 区边框〕)----
func _smoke_layout_cell_formula() -> void:
print("--- 棋盘布局格边长公式(底部预留回归)---")
print("--- 棋盘布局格边长公式(底部预留回归·四档板型)---")
var board_generator = load("res://core/board_generator.gd").new()
var board_view = load("res://presentation/views/board_view.gd").new()
for L in [1, 2]:
var b = board_generator.generate(L, 0, _mk_stream("board_gen", 555), _db)
var board_view_script := load("res://presentation/views/board_view.gd")
# 四档取样表(board_gen.tres 实表取档——(L,D)→尺寸:10/12/13/14;13×13 与 14×14 首次入断言)
var tiers := [{"L": 1, "D": 0, "n": 10}, {"L": 2, "D": 0, "n": 12}, {"L": 3, "D": 0, "n": 13}, {"L": 3, "D": 9, "n": 14}]
for t_v in tiers:
var t: Dictionary = t_v
var b = board_generator.generate(int(t["L"]), int(t["D"]), _mk_stream("board_gen", 555), _db)
board_view.rebuild(b, null)
var avail := Vector2(1036, 460) # 受约束 D 区可用面积样本(1280×800 档量级——用户实测裁切场景)
board_view.set_cell_area(avail)
var n: int = maxi(b.width, b.height)
var cell: float = board_view.get("_cell")
var grid_h := n * int(cell) + (n - 1) * 2
Kit.check("L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" % [L, b.width, b.height, grid_h, int(avail.y), int(cell)],
Kit.check("L%d D%d 板型一致(%dx%d = 取样 %d——表改动致取样落错档即红)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, int(t["n"])],
n == int(t["n"]))
Kit.check("L%d D%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, grid_h, int(avail.y), int(cell)],
grid_h <= int(avail.y) - 24)
Kit.check("L%d 格边在 26–46 档内(%dpx)" % [L, int(cell)],
Kit.check("L%d D%d 格边在 26–46 档内(%dpx)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), int(cell)],
cell >= 26.0 and cell <= 46.0)
Kit.check("L%d D%d 算式单点等价(set_cell_area 写入 %d == _cell_for 返回 %d——同一事实两路读取)〔格边长 V2〕" % [int(t["L"]), int(t["D"]), int(cell), board_view_script._cell_for(n, avail)],
int(cell) == board_view_script._cell_for(n, avail))
board_view.free()
@@ -14,6 +14,11 @@ func _init() -> void:
# ① 词表在册断言(扩签面:2 事件+8 事实+7 效果类型+1 算子)
Kit.check_quiet(EffectEngineScript.EVENTS.has("dig_stop") and EffectEngineScript.EVENTS.has("battle_won"),
"新事件在册:dig_stop/battle_won〔专长批 V1〕")
# ①-a 换图系两事件两效果在册(迭代 46 扩签——旧出域负例全部保留)
Kit.check_quiet(EffectEngineScript.EVENTS.has("refresh_request") and EffectEngineScript.EVENTS.has("board_refreshed"),
"换图系新事件在册:refresh_request/board_refreshed〔换图系批 V1〕")
Kit.check_quiet(EffectEngineScript.EFFECT_KINDS.has("grant_reserve_ap") and EffectEngineScript.EFFECT_KINDS.has("apply_discount"),
"换图系新效果在册:grant_reserve_ap/apply_discount〔换图系批 V1〕")
Kit.check_quiet(EffectEngineScript.FACTS.has("hp_ratio") and EffectEngineScript.FACTS.has("is_edge_cell")
and EffectEngineScript.FACTS.has("new_resource_type") and EffectEngineScript.FACTS.has("dug_under_seal")
and EffectEngineScript.FACTS.has("ap_exhausted_last") and EffectEngineScript.FACTS.has("ap_left")
@@ -199,7 +199,7 @@ func _test_negative_cases() -> void:
# 现行 31;本套件零扩签,钉现行数(将来任何加词即转红提示复核)。
func _test_effect_kinds_closed() -> void:
Kit.check("效果引擎效果类型闭集 32 种(锻造工坊批零新增口径随迭代 31 卡② 扩签 forgive_seal_miss 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EFFECT_KINDS.size() == 32)
Kit.check("效果引擎事件词表在册 20 种(本批零扩签参照面)〔锻造工坊批 V11〕",
EffectEngineScript.EVENTS.size() == 20)
Kit.check("效果引擎效果类型闭集 34 种(迭代 46 换图系扩签 grant_reserve_ap/apply_discount 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EFFECT_KINDS.size() == 34)
Kit.check("效果引擎事件词表在册 22 种(迭代 46 换图系扩签 refresh_request/board_refreshed 换代)〔锻造工坊批 V11〕",
EffectEngineScript.EVENTS.size() == 22)
@@ -228,16 +228,14 @@ func _smoke_enemy_bands() -> void:
int(row.get("depth_band", -1)), String(row.get("enemy_id", ""))])
Kit.check("敌人表 28 行且键序 = 覆盖表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕",
_db.combat.enemy.rows.size() == 28 and got_keys == expect_keys)
# ② hobgoblin 缺口行留任随迁(契约 9:不删行不补立绘——缺口登记随行)
# ② hobgoblin 缺口行留任随迁(不删行不补立绘——补齐归敌种内容批)
var hob := {}
for row in _db.combat.enemy.rows:
if String(row.get("enemy_id", "")) == "hobgoblin":
hob = row
Kit.check("hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕〔fv25〕",
Kit.check("hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕",
int(hob.get("level_index", -1)) == 1 and String(hob.get("row_kind", "")) == "battle" \
and int(hob.get("depth_band", -1)) == 3)
Kit.check("hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕",
FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行"))
# ③ Boss 行集带键 = 覆盖表(旧判据「各恰一行=图 Boss 节点深度」属单终点模板时代;
# 生成图多终点要求每终点深度解析到互异带键 ⇒ 每关 Boss 行必然多于一行——
# 迭代 35 覆盖批随动改写为带键清单,「带键互异」的运行时把关在 level_shop_economy_test〔v11〕)
@@ -1,12 +1,16 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 战斗一屏样张批 V5–V9 断言面(2026-10-29 迭代 43)——五项实装定值的真实入口断言:
# 战斗一屏样张批 V5–V12 断言面(2026-10-29 迭代 43 立五项;2026-10-31 迭代 46 卡B 增 V10–V12)——
# 实装定值的真实入口断言:
# V5 血条填充 = 危险红(UiKit.HP_BAR_FILL 值单点 + 绘制点源码锚:全仓零 #689CE8 与零裸
# Color(0.41,0.61,0.91);条底收 HP_BAR_TRACK 常量);
# V6 站桩名字行垫 HP_BAR_BACK 素底且不压地台(名字行基线 y < 地台顶 y——name_line_probe 几何出口);
# V7 顺位徽标提档 UiKit.FS_HINT(值断言 + 绘制点源码锚,§7.4 清单外 11–13px 提档);
# V8 护甲图标三键(light/ward/heavy)绑对应 24×24 PNG(ResourceLoader 装载结果 + name_line_probe
# 绑定面);
# V9 地台件 scene_stage_plaza.png + 护甲三枚入包可加载且尺寸 48×12 / 24×24(§5.2/§5.3 工单)。
# V9 地台件 scene_stage_plaza.png + 护甲三枚入包可加载且尺寸 48×12 / 24×24(§5.2/§5.3 工单);
# V10 名字行提档 FS_HINT 14(探针 name_font_size 实读 + 两绘制点源码锚 + FS_CAPTION 计数锚==1);
# V11 退役常量不在册 + 掩尘 BRICK 逐位 + 顶缘线/口沿高光两绘制点改指源码锚;
# V12 PLAZA_EDGE 逐位 #4C4038 + 回退描边改指源码锚。
# headless 无渲染面:像素采样以常量单点 + 源码锚 + 探针几何承载(观测面注记,验收对照表 V5 行)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd(expect 0)
extends SceneTree
@@ -48,14 +52,39 @@ func _init() -> void:
Rect2(nl.plate).end.y <= float(nl.plinth_top_y) + 0.01)
Kit.check("名字行素底 = HP_BAR_BACK 深档 #282220(挂载底色口径)〔样张批 V6〕",
Color(nl.plate_color).to_html(false) == "282220")
Kit.check("基线读数与实装几何同源(基线 = 地台顶上方 9px 档:149 < 158)〔样张批 V6〕",
absf(float(nl.baseline_y) - 149.0) < 0.51 and absf(float(nl.plinth_top_y) - 158.0) < 0.51)
Kit.check("基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕",
absf(float(nl.baseline_y) - 150.0) < 0.51 and absf(float(nl.plinth_top_y) - 158.0) < 0.51)
# ---- V7 顺位徽标提档 FS_HINT ----
Kit.check("FS_HINT = 14(§7.4 六档)〔样张批 V7〕", UiKit.FS_HINT == 14)
Kit.check("顺位徽标绘制点取 UiKit.FS_HINT(源码锚)〔样张批 V7〕",
body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT"))
# ---- V10 名字行提档 FS_HINT 14(迭代 46 卡B——五取值点+基线随档联动) ----
Kit.check("名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕",
int(nl.name_font_size) == UiKit.FS_HINT)
Kit.check("名字与护甲两绘制点取 FS_HINT(源码锚——沿 V7 先例)〔名字行 V10〕",
body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM")
and body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col"))
Kit.check("全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕",
body_src.count("UiKit.FS_CAPTION") == 1)
# ---- V11/V12 地台色合并(迭代 46 卡B——常量面载体=Script 常量表,源码锚正证改指目标色) ----
var uikit_script: Script = load("res://presentation/views/ui_kit.gd")
var cmap: Dictionary = uikit_script.get_script_constant_map()
var cell_src := _read_source("res://presentation/views/cell_base_layer.gd")
Kit.check("退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕",
not cmap.has("STAND_STONE_RIM") and not cmap.has("STAND_STONE_EDGE"))
Kit.check("掩尘 BRICK 值逐位 #C9AF83(合并目标①)〔名字行 V11〕",
UiKit.BRICK.to_html(false) == "c9af83")
Kit.check("顶缘受光线与口沿高光两绘制点改指 BRICK(源码锚)〔名字行 V11〕",
body_src.contains("ped.size.x, 2.0), UiKit.BRICK, true")
and cell_src.contains("FACADE_HIGHLIGHT_PX), UiKit.BRICK, true"))
Kit.check("PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕",
UiKit.PLAZA_EDGE.to_html(false) == "4c4038")
Kit.check("回退描边绘制点改指 PLAZA_EDGE(源码锚)〔名字行 V12〕",
body_src.contains("UiKit.PLAZA_EDGE"))
# ---- V8 护甲图标三键绑 24×24 PNG ----
var armor_paths := {"light": UiKit.TEX_ARMOR_LIGHT, "ward": UiKit.TEX_ARMOR_WARD,
"heavy": UiKit.TEX_ARMOR_HEAVY}
+25
View File
@@ -0,0 +1,25 @@
# 迭代 46 GDD 反哺点清单(2026-10-31)
> 接收方:待办清单「反哺点专条」通道——用户会话逐条裁决后由文档批写入 `design/`,写入后本清单条目清掉(吸收即死)。
> 依据:迭代 46 实现报告(同目录)与实测评读数。实现批禁写 `design/`,以下为待裁决落文面。
## 卡A 换图系双基座七件批
1. **[奇物.md]** 基础件表(或专长实装状态句):七件(矿脉储能/省力周转/崩壁冲击/蓄力周转/石层缓冲/坑道小憩/矿道红利)由设计声明改为**已实装**(feats.tres 37 行、稀有度 14/13/10/0、cap 均 1);「双基座叠加、后续效果本轮五段结算内兑付」已在案零改述。依据=实现报告 §二 V2–V11。
2. **[行动经济.md]** 两件基座由「待实现」改为在册:付费挖格 +1 RAP(受理后一次性入池、拒绝轮零入池)、刷新成本折价 50%(floor 取整、can 与执行同源、只改门槛与扣费不改拒绝判据)。依据=V2/V3。
3. **[战斗结算.md]** 全局 `damage_taken` 落码状态补记:敌方攻击/地形/危险格/错误封印资源直伤四路径统一经唯一发射口与唯一消费族;**地形路径消费面自三件扩为五件(+shield)——铁壁自此对地形伤害生效;危险格/错误封印直伤路径自此纳入 damage_taken 订阅面**(用户已裁全局语义,实现报告 §六);成功刷新事实 `board_refreshed` 为七件订阅主挂点(装新板后恰一次发射)。依据=V5/V4。
4. **[地图战斗资源.md]** 清图奖励语义确认句:矿道红利不绑清图奖励(每次成功刷新固定 +1 GP 走战斗内缓冲,不读旧图探索进度、不触发清图事件;刷新图零清图奖励守卫不变)——若现行正文已含此意则零改(本行核对性反哺)。依据=V10/V12。
5. **[architecture.md]** 「模块归属」增补/现状化三行:①**BattleRefreshFlow**(新 B1 域拆件——刷新可用性门/折价扣费/换图执行/成功事实登记,调用路径呈现层→编排器单行转发→流程件→行动经济/装板壳/授予段;static_scan S7_B1_SET 已随批增补该文件,判据口径不变);②**damage_controller** 行现状化(damage_taken 唯一发射口 `_emit_hero_damage_taken`+唯一消费族 `_apply_hero_damage_effects`(原名 _enemy_shield_apply 改名)+board_flat_hero 结账前统一发射);③**action_economy** 行现状化(dig_paid 命中施加抽私有 `_apply_dig_paid_hits` 单点;request_refresh 收折后价入参)+**grant_segment** 行现状化(apply_board_refreshed 成功事实消费+round_end heal value_counter 兑付面)。依据=实现报告 §五/§八.6。
## 卡B 站桩名字行提档与地台色合并批
6. **[art-bible.md §4.1]** 换色合并登记①②句的代码指称换代:①「代码 `STAND_STONE_RIM`」②「代码 `STAND_STONE_EDGE`」两源常量已删除(全仓零引用——唯一残留=退役断言测试串,预期例外);②合并目标 `#4C4038` 补代码名 **`PLAZA_EDGE`**(ui_kit 唯一落点)。依据=卡B 对照表 3/4 行+grep 取证(实现报告 §三)。
7. **[art-bible.md §8.2]** 高墙瓦片体系条口沿高光行:「Color8(196,172,132)」值随换值换代为 **`#C9AF83`(201,175,131)**(并档掩尘枚 BRICK 单点);程序化回退顶缘线与口沿高光两绘制点已改指。依据=对照表 3 行。
## 卡C 棋盘格边长口径收口批(停摆——随裁决落文)
8. **[battle.md §棋盘格尺寸公式]** 算例表(:148-152)与实机偏差实测落点:1280 档实板可用面积 **(1022, 454)**(算例假设 486 可用高)→ 实测 L1 格边 40px、13×13 推定 30px(恰达标)、**14×14 推定 28px(低于可读下限 30)**;1080 档 (1662, 734) → L1=46、13/14 推定=46(钳制顶格)。落文方向随「30px 下限适用域」裁决(读法甲/乙见停机呈报 §三):读法乙需补「下限按视口档分级」条+算例表填实测值或标注「按旧布局口径」。依据=停机呈报 §一/§四。
## 术语表
9. 零新增术语——本批新名(BattleRefreshFlow/_cell_for/PLAZA_EDGE/counter_id 三枚)均属代码标识符与数据行键,不入术语表(登记口径=概念规范名)。
@@ -0,0 +1,12 @@
# 迭代 46 GDD 反哺点清单——卡C(2026-11-01)
> 接收方:待办清单「反哺点专条」通道——用户会话逐条裁决后由文档批写入 `design/`,写入后本清单条目清掉(吸收即死)。
> 卡A/卡B 反哺条见同目录 `iter46-反哺点-2026-10-31.md`;本文件只收卡C v2.0。
1. **[battle.md §棋盘格尺寸公式 :144]** 「可读下限 30px——与结构钳制分开:钳制管算式不越界,下限管玩家能否看清」整句**退役**:整句删除,或改写为「格边长可读性以用户验收的现行画面为基准;结构钳制 [26,46] 管算式不越界」(措辞由文档批裁)。依据=2026-11-01 用户三项裁定(锚点=用户验收的现行渲染值,基座档 14×14=28px 达标;30 无实测背书)+停机呈报读数链(`reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`)+实现报告 §四锚点留档(`iter46-实现报告-卡C-2026-11-01.md`)。随句建议同节载明判据现行形态:全域禁人造格边长可读阈值,接线守卫=生产写入值与单点算式严格相等,最大板推定读数留痕不作判红。
2. **作废声明**:`iter46-反哺点-2026-10-31.md` 第 8 条(battle.md 算例表改数——随「30px 下限适用域」读法甲/乙裁决落文)**作废**——`:148-152` 算例表已随布局几何单点化批(1cdef6ba)退役,无落点;读法甲/乙之争已被 2026-11-01 用户三项裁定取代(下限句退役,不再有适用域问题)。文档批落文时以本条为准,10-31 清单第 8 条不再执行。
## 术语表
零新增术语——本批新名 `_cell_for` 属代码标识符,不入术语表(登记口径=概念规范名)。
+109
View File
@@ -0,0 +1,109 @@
# 迭代 46 实现报告(2026-10-31)
> 批次:迭代 46 三卡——卡A 换图系双基座七件批 · 卡B 站桩名字行提档与地台色合并批 · 卡C 棋盘格边长口径收口批。
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,自 b5b2c555 检出)。
> **批内状态总览:卡A 收口 · 卡B 收口 · 卡C 停摆(L3 #2 预设停机点,呈报见主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`)**。合并阻塞等裁决。
## 一、各卡状态
| 卡 | 状态 | 出口证据 |
|---|---|---|
| 卡A 换图系双基座七件批 | **收口**(T0–T8 全落) | `tools/run_all_gates.sh --code` 7 闸全绿(回执 commit 45b53d68 时点);此后 S9/S7 修正与卡B 提交待迭代级复跑 |
| 卡B 站桩名字行提档与地台色合并批 | **收口**(T0–T3 全落) | standee_present_values_test 26 checks 绿;golden 板面三张 0.00% 留痕;gdstyle 零新增键 |
| 卡C 棋盘格边长口径收口批 | **停摆**(T0 完成;T1–T3 改动已验未提交;T4 未走) | 卡C §9 L3 #2:实板可用面积代入 14×14 得 28px < 30px(1280 档)——停卡呈报等裁决 |
## 二、卡A 实际对照表(改动—验收)
| 需求点 | 改动(文件:行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 结果 |
|---|---|---|---|---|---|---|
| V1 词表扩签 | effect_engine.gd EVENTS 20→22/MINE_EVENTS 同步换图刷新面/EFFECT_KINDS 32→34;forge_table/effect_words 计数换代 | `effect_words_t1_test.gd:_init`(新增①-a 两断言);`forge_table_test.gd:_test_effect_kinds_closed` | 单元 | validate 与整表加载 | EVENTS=22、EFFECT_KINDS=34、出域仍拒(旧负例全保留) | 绿(20/41 checks) |
| V2 矿脉储能 | action_economy.gd dig_paid 命中环累计 grant_reserve_ap(扣费受理后一次性入池·拒绝轮零入池);feats.tres vein_reserve 行 | `refresh_feats_test.gd:test_reserve_on_accepted_dig` | 接口 | `settle_batch(dig)`(AutoPlayer 真实挖掘) | RAP 恰+1(8→9)、AP 按表扣(8→7)、耗尽轮增量==受理挖数 | 绿 |
| V3 省力周转 | BattleRefreshFlow `_effective_refresh_cost`(声明序第一命中 floor(base×mult/100),can 与执行同源);request_refresh 收折后价入参;easy_swap 行 | `test_refresh_discount_gate_and_charge`;`test_double_base_stack` | 接口 | `player_request_refresh()` | base 8→扣 4;两次折后扣尽 8→0;门槛拒零受理;双基座同场生效 | 绿 |
| V4 成功事实 | battle_refresh_flow execute 成功序(折价扣RAP→used+1→旧资源退役→旧板退役→装新板→apply_board_refreshed);编排器两公开方法单行转发 | `test_board_refresh_fact_only_on_success` | 集成 | `player_request_refresh()` | 非 Active/每轮上限/RAP 不足三拒面零事实零换图(probe_br_counter 计数+board_changed 双观测);成功恰一次 | 绿 |
| V5 全局受伤 | damage_controller `_emit_hero_damage_taken` 唯一发射口+`_apply_hero_damage_effects` 唯一消费族(改名自 _enemy_shield_apply,组合次序冻结);敌方攻击/棋盘直伤/地形三路径接入;grant_segment 删本地 _terrain_damage_reduce | `test_all_hero_damage_paths_emit_once`;`test_no_duplicate_damage_taken_event` | 集成 | 敌方攻击、dig danger、seal wrong、terrain 四真实入口 | 每路径承伤单据在案且计数器==行数(逐单恰一次);context 标记闸防地形二次发射 | 绿 |
| V6 石层缓冲 | round_swap_guard 计数+damage_taken when≥1 damage_reduce 2(round 桶轮始自清) | `test_stone_mitigation_all_damage` | 集成 | 刷新→危险格(pierce 8)/地形两真实受伤路径 | pierce 8→6(双刷不叠加)、未刷新轮不减(8)、地形 max(下限, effect_value−2) 逐单闭合 | 绿 |
| V7 崩壁冲击 | apply_board_refreshed open_ticket blast→reward_grant.defer_ticket(案C 同队列);SOURCE_EXEMPT_CLASSES+fx_board_refresh;wall_blast 行 | `test_blast_defers_to_combat_segment` | 集成 | 刷新→停手 | 刷新时点零伤害行;战斗段末恰一单 ×2 全体存活敌 | 绿 |
| V8 蓄力周转 | swap_charge_layers 战斗桶+attack_bonus value_counter+set0(既有消费面零新码) | `test_charge_layers_consumed_same_round` | 接口 | 两次刷新→停手(主角先手攻击) | 双刷 2 层→攻击后 0;白盒预置 3 层时刷新被闸(上限 3);战斗新建 0 | 绿 |
| V9 坑道小憩 | round_swap_rest+round_end heal value_counter(grant_segment 新增 `_round_end_heal_value_counter`——先于 set0 行消费层值) | `test_rest_rotation_cap_and_clear` | 集成 | 刷新→停手(轮末授予段) | 回复 min(3,缺血)==3、层清 0、满血发未生效行、自然累计 2 路径 | 绿 |
| V10 矿道红利 | board_refreshed grant_gp→escrow+fx_grant_gp 行(apply_board_refreshed 消费);mine_profit 行 | `test_mine_profit_gp` | 集成 | `player_request_refresh()` | escrow 每成功+1(0→1→2)、fx_grant_gp 行在案、拒绝零变化 | 绿 |
| V11 数据与池 | feats.tres 七行(rarity 14/13/10/0·cap 1);adventure_feats 30→37/牌库 132→139 | `test_seven_feats_validate_and_pool`;adventure_feats `_smoke_feats` | 单元 | ConfigDB 整表与冒险牌库 | rows 37、relic 37、14/13/10、deck 139、validate 空、禁词 0、七件计数器引用闭合 | 绿(85/138 checks) |
| V12 无专长回归 | round_flow/seal 旧基线零改动(刷新体迁移编排器单行转发) | `_smoke_active_refresh`;`_smoke_refresh_provenance_guard`;`test_no_feats_baseline` | 单元+集成 | 现行刷新/清图入口 | RAP8、AP 保留、上限 2、刷新图零清图、挖格零产 RAP、表基线扣费 | 绿(134/70 checks) |
**窄测实跑记录**:refresh_feats 85 · adventure_feats 138 · round_flow 134 · seal_polarity 70 · damage_controller 44 · effect_words 20 · forge_table 41 · config_db 29——全绿。批末 `--code` 闸 00/01/02/03/05/09/10 全绿(回执 45b53d68)。
## 三、卡B 实际对照表(改动—验收)
| # | 需求点 | 改动 | 验收测试 | 结果 |
|---|---|---|---|---|
| 1 | 名字行字号提档 FS_HINT 14 | standee_body.gd 五取值点+name_line_probe 增 name_font_size | 〔名字行 V10〕探针实读==FS_HINT+两绘制点源码锚+FS_CAPTION 计数锚==1 | 绿 |
| 2 | 名字行几何随档联动 | _name_baseline 偏移 17→18+注释换代 | V6 换代读数 150±0.51(8px 档)且 < plinth_top_y | 绿 |
| 3 | 受光线常量退役改指 BRICK | ui_kit 删 STAND_STONE_RIM;standee_body:386+cell_base_layer:176 改指 | 〔名字行 V11〕常量表零键+BRICK==#C9AF83+两绘制点源码锚;grep 反证(仅剩测试断言串——预期例外) | 绿 |
| 4 | 描边常量退役改指新 token | ui_kit 删 STAND_STONE_EDGE+增 PLAZA_EDGE=Color8(76,64,56);standee_body:385 改指 | 〔名字行 V12〕PLAZA_EDGE 逐位+常量表零旧键+描边源码锚 | 绿 |
| 5 | 板面 golden 与换值一致 | (预期重锚——实际无需) | render_gate 板面三张 0.00%(改后跑,基线未动) | 绿(见实现决定 #4) |
| 6 | 批末收口 | 全批 | 迭代级 --code 闸(待卡C 裁决后统一走) | 待迭代出口 |
卡B 改前读数(T0):standee_present_values_test 18 checks 绿 · gdstyle 5 键全基线内 · 渲染闸绿(--code 回执 45b53d68 内 05 闸)· 字体度量 ts(14px)=(14,20)。
## 四、卡C 停摆实况(改动已验未提交)
- T0 完成:行号重锚(board_view set_cell_area 现 :206-224、layout_probe MIN_CELL :15、冒烟 :53-69);取档对账 resolve_row(1,0)/(2,0)/(3,0)/(3,9)=10/12/13/14 全符;改前读数 board_gen_reward_test 62 绿+layout_probe 两档绿+gdstyle board_view max-public-methods 27 持平。
- T1 算式单点:`BoardView._cell_for(n, avail)` 提取(逐位同式)+set_cell_area 改调——等价性由 board_gen_reward_test 既有断言承载(62 checks 绿)+gdstyle 持平。
- T2 探针:MIN_CELL 36→30+注释改指 battle.md+BoardViewScript preload+零尺寸前置+13/14 推定断言——**推定断言触发 L3 #2(14×14=28<30)**。
- T3 冒烟四档:取样表 [{1,0,10},{2,0,10→12},{3,0,13},{3,9,14}]+板型一致断言+算式单点等价断言——74 checks 绿(单元面全过:钳制 [26,46] 面无越界)。
- 三件改动**未提交**(停摆卡不提交),随裁决处置(可复用——行为零变化)。
- 停机呈报:主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`(分歧读法/建议案/待办回退拟稿)。
## 五、结构预算(S13 前后)
| 触面 | 前测 | 后测 | 判定 |
|---|---|---|---|
| battle_orchestrator.gd | 811 行/类变量 15(卡A 写 38 系笔误) | 803 行(−8 达标)/类变量 15 | 只降不升 ✓ |
| reward_grant.gd | 类变量 15 | 15(defer_ticket 为方法零增员) | ✓ |
| grant_segment.gd | 类变量 3/公开方法 1 | 3/2(apply_board_refreshed);私有新增 1(_round_end_heal_value_counter)≤4 | ✓ |
| damage_controller.gd | 公开方法 9 | 9(私有净增 1:_emit_hero_damage_taken)≤2 | ✓ |
| action_economy.gd | 公开方法 15 | 15(命中环抽私有 _apply_dig_paid_hits——S13 分支超限重构,分支本体逐字迁入) | ✓ |
| battle_refresh_flow.gd(新件) | — | 公开函数 2(can_refresh/execute)/类变量 0 | ≤20/≤15 ✓ |
| gdstyle_gate | 5 键全基线内 | 5 键全基线内零新增零恶化 | ✓ |
## 六、旧 damage_taken 专长覆盖面变化
全局统一后,订阅 `damage_taken` 的既有专长(背水 hp_ratio when/石肤 ac_bonus+counter_add/保险丝 counter_add)与换图系石层缓冲的覆盖面变化:
- **敌方攻击路径**:不变(原即经 _enemy_shield_apply 全组合消费)。
- **地形路径**:消费族从本地三件(damage_reduce/ac_bonus/counter_add)扩为全局五件(+shield)——**铁壁(shield)自此对地形伤害生效**(用户已裁全局语义;卡A §十二削弱申报第 2 条在案)。
- **危险格/错误封印资源直伤路径(board_flat_hero)**:原零事件,现统一发射——上述专长自此对这些路径生效(同上申报面)。
- 无专长基线逐位零漂移(V12 全绿;同一伤害恰一次由 V5 钉死)。
## 七、实机目检总清单(登记不阻塞)
1. 〔卡A〕玩家持基座与收益件后,是否主动规划刷新而非只在坏图刷新(行为观感)。
2. 〔卡B〕站桩名字行 14px 清晰可读且长名不截字(验收底线句)。
3. 〔卡B〕墙格口沿亮线换值后观感无异(#C4AC84→#C9AF83)。
4. 〔卡B〕贴图缺位回退描边与顶缘线观感无异(PLAZA_EDGE/BRICK 程序化回退)。
5. 〔卡C·停摆批登记〕第三关 14×14 板格面数字在基座档的可辨读性(28px 实况——随裁决)。
## 八、实现决定记录(超白名单/偏差备案)
1. **卡A 自洽扫描笔误三处(不改合同)**:「类变量 38」实为 15;gdstyle_baseline.txt:9 引用实无 battle_orchestrator 键(该文件现无 S13 键);稀有度断言实挂 :476 非 :465。T0 按实数重锚记录,合同(只降不升/换代判据)不受影响。
2. **卡A T0 工具名**:卡写 `bash tools/gdstyle.sh game`——该文件不存在,实际工具 `python tools/gdstyle_gate.py`(与卡B/C 一致),按实跑。
3. **MINE_EVENTS 同步口径**:卡A T1「同步 :19 的 MINE_EVENTS」取「两新事件入集」读法——弃图刷新属扫雷侧动作面,流派铁律订阅集随之认列(零机械消费面差异,mine_covered 生产无强制点)。
4. **卡B golden 预期红未现**:卡预期「板面三张红→重锚」,实测三张 0.00%——口沿高光换值 #C4AC84→#C9AF83 每通道差 ≤5,低于逐像素容差(≤16 差/≤2%),golden 判定零变化;重锚步骤无需执行(基线未动、零差留痕达成 V5 判据;禁借重锚夹带红线零触碰)。
5. **S9 修正**:四处显式非参数化容器注解改 `:=` 推断(R1)——零行为改动。
6. **S7 域集增补**:`static_scan.sh` S7_B1_SET 增补 battle_refresh_flow.gd——换图刷新流程件=卡A C13 拆分边界的 B1 域拆件(承载 retire_mapping/装板编排调用,卡A §八明列新结算族件);判据口径不变(B2/B3/B4 对外 API 仍限 B1 域调用)。**architecture.md「模块归属」需补 BattleRefreshFlow 行——已入反哺点清单**。
7. **V11 引用闭合判据域**:全表扫描发现预存行完美保险 when 读 insurance_used 无 counter_add 写入方(消费点落账合法形态——feats.tres 注记在案)——判据域收窄为「换图系七件自身引用闭合」,预存形态不入判据。
8. **apply_board_refreshed 访问形态**:BattleRefreshFlow 经 `oc._rounds._grant_seg` 直调(round_cycle 不在卡A §八改动文件清单内,不加转发壳——沿结算族直调惯例)。
9. **卡A T8「代拍板追认单」不另立**:iter 技能与 process-conventions §2 定「超白名单记实现报告实现决定记录,不另立追认单」——本批全部决定在上列备案,白名单内决定零追认面。
10. **测试断言口径修正(开发期自纠)**:V5②③④ 聚合判据从「两来源行合计」修正为「全 hero 行数」(盲挖途经的其他直伤单据一并入账);V2 辅证改「RAP 增量==受理挖数」(移位耗 AP 不入账);V4 时序修正(停手再入 RAP)——均为测试侧修正,生产码零回退。
11. **V9 上限 3 承载形态**:每轮刷新上限 2(表值)使自然路径层值至多 2——「上限 3」判据由白盒计数器预置+真实刷新入口承载(test-conventions §5.4 计数器直改属数据面注入),自然累计 2 另设战场钉死。
## 九、独立验收审计结论
- **审计方**:未参与实现的子代理(独立会话上下文),2026-10-31 执行。
- **报告**:`reports/iter46-验收审计-2026-10-31.md`(本目录)。
- **结论**:**审计通过(零缺漏)**——卡A V1–V12 与卡B 6 行对照表逐行通过;尾缀全可 grep;真实入口零绕过(V2 真挖/V3·V4·V10 经 player_request_refresh/V5 四路径真实战斗驱动);自跑复核 refresh_feats 85/standee 26/adventure_feats 138/round_flow 134/seal_polarity 70/effect_words 20/forge_table 41 全绿+gdstyle 5 键零新增+render_gate 三张 0.00% 实跑;削弱核对全部落在两卡申报面内(round_flow/seal 零进 diff,禁删面原样在案);无变异/撤销类取证。
- **审计附加发现处置**:①两枚新件 .uid 边车未随批提交——已补提交(.gitignore 要求);②static_scan S8_FILES 与 S7_B1_SET 未同步增补新件——已同步(新件零 await,扫描面实跑绿);③V10 断言消息在求值前格式化的显示瑕疵(判据本体正确,零处置);④V9 私有读前置守卫附注(审计方判例外不阻塞——判据主面走公共 step_row 信号)。
## 十、遗留读数义务
- 卡C 裁决后:探针两档 bv.size/L1 格边/13·14 推定读数已录本报告第四节与停机呈报——重写卡可直接引用,无需重采。
- 无 MC/数值验证义务(本批无数值改动,feats.tres 七行为卡内 provisional 值——归调参批)。
@@ -0,0 +1,70 @@
# 迭代 46 实现报告——卡C 棋盘格边长口径收口批 v2.0(2026-11-01)
> 批次:迭代 46 卡C(v2.0 重立回编——2026-11-01 用户三项裁定后写卡批重立,合并提交 e7090bc9)。卡A/卡B 已于 2026-10-31 收口合回,见同目录 `iter46-实现报告-2026-10-31.md`。
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,卡C 段基点=master 5269ed40,WIP 三件按 v2 卡 §12 判定复用两件返工一件)。
> 需求卡片:`reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md`(v2.0 定稿,零待裁点)。
## 一、卡C 状态
**收口**(T0–T4 全落)。游戏行为零变化:算式逐位同式、`set_cell_area` 副作用尾链零动、两条生产调用路径不改、画面像素零变化。本批改动全部落在守卫面=**人造阈值退役+算式单点+13/14 两档入卫**。
## 二、T0 改前基线读数(2026-11-01,master 5269ed40 态实测)
| 读数 | 结果 |
|---|---|
| `python tools/gdstyle_gate.py` | 扫描面 142 件、命中 5 条全在基线、基线外新违规 0、较登记值恶化 0(board_view.gd `max-public-methods=27`/`max-class-variables=31` 持平) |
| 单件跑 `board_gen_reward_test` | all green(**62 checks**——改前基线值) |
| 单件跑 `layout_probe` | 2 档 PASS(旧阈值形态「≥36px」消息,即本批退役对象) |
| 四档取档对账(`resolve_row(1,0)/(2,0)/(3,0)/(3,9)`,临时对账件跑后即删) | 10×10/12×12/13×13/14×14,与卡 §2 第 6 条全符 |
| `battle_layout.tres` 五字段 | stage_h=252/ap_bar_h=44/info_panel_w=240/log_band_h_dig=24/log_band_h_settle=84,与卡 §2 第 8 条全符 |
行号重锚:卡 §2 行号在 5269ed40 上逐一核实(`layout_probe.gd:15` MIN_CELL/`board_view.gd:206-215` 内联算式/`board_zone_view.gd:91-92` 生产调用路径+`board_view.rebuild` 末尾调用/`board_gen_reward_test.gd:53-69` 冒烟)——卡成文时点(3499638d)至 T0 时点间零漂移。`tools/gdstyle_baseline.txt` 两键登记值与卡 §2 第 5 条一致(:11=31、:22=27)。
## 三、实际对照表(改动—验收)
| # | 需求点 | 改动 | 验收测试(名称+实际落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 结果 |
|---|---|---|---|---|---|---|---|
| 1 | 探针阈值退役+格边长接线守卫 | `layout_probe.gd` MIN_CELL 常量整行删除;阈值断言换等价断言(:106-109) | 「两档几何不变量+格边长等价」`layout_probe.gd:_ready`(PASS 行 :45 尾缀〔格边长 V1〕;失败路径 :107) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 实读 `cell_size`(生产经 `set_cell_area` 真实写入)与 `_cell_for(n, bv.size)` 同输入严格相等——生产停止调单点算式即红;`_board` 内部字段直查=受控例外(板型 N 无公开出口,卡 §14.2);`grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` 零命中(已跑,exit=1 零命中) | 绿(两档全过,基座实读 40px) |
| 2 | 算式单点(`_cell_for`) | `board_view.gd:210` 改调 `static _cell_for`(:226,逐位同式);:206 注释补唯一落点句 | 「算式单点等价」`board_gen_reward_test.gd:75-76`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 绿(四档 4 断言全过;既有四套断言全绿=等价性证明,62→74 checks) |
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:58` 循环集改四档取样表 [(1,0,10),(2,0,12),(3,0,13),(3,9,14)];:69-74 板型一致+既有两断言(前缀 L%d D%d) | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边 ∈ [26,46](既有断言逐字保留) | 绿(四档 12 断言全过,10/12/13/14 实测 41/33/31/28px) |
| 4 | 最大板推定守卫(留痕型) | `layout_probe.gd:_check_one` 零尺寸前置(:112-114 记红)+13/14 推定留痕(:116-119 采集,:47-50 PASS 后打印) | 「最大板推定留痕」同探针(尾缀〔格边长 V4〕) | 探针 | 同行 1 | ①零尺寸前置(size ≤ 0 记红);②推定读数打印并与本报告 §四留档锚点读数逐值对拍,不一致=卡 §9 L3 #2 呈报(禁实现侧改锚) | 绿(基座 30/28、全屏 46/46——与锚点逐值一致,零对拍偏差) |
| 5 | 批末收口 | 全批 | 代码域闸 `tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 绿(`all_green=true`,七闸 00/01/02/03/05/09/10,commit e3e206d0,域=code·scope=related[presentation],`changed_vs_baseline=[]`) |
**测试削弱申报核对**(卡 §8 申报全集四条,实际执行逐条对上):①探针阈值断言删除=申报 1 的执行面,补偿三件全落(等价断言/四档结构钳制/推定留痕对拍);②PASS 消息改写=申报 2;③循环集 2→4 档纯增量、样本值 (1036,460) 未改、既有两断言逐字保留=申报 3;④未发生恢复阈值、删旧断言、放宽区间、换读取入口任一项=申报 4 成立。另:既有几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)与视口档位表零改动(卡 §14.1 核对通过)。
## 四、锚点读数留档(两档视口 × 四板型——未来批次对照基准)
锚点=用户验收的现行渲染值(2026-11-01 用户裁,卡 §2 第 7 条)。本批探针实跑复核:
| 视口 | board_view.size(实读) | 10 板 | 12 板 | 13 板 | 14 板 |
|---|---|---|---|---|---|
| 基座 1280×800 | (1022.0, 454.0) | **40px(实读)** | 33px(单点算式读) | **30px(推定打印)** | **28px(推定打印)** |
| 16:9 全屏 1920×1080 | (1662.0, 734.0) | 46px(实读·钳制顶格) | 46px(单点算式读) | **46px(推定打印)** | **46px(推定打印)** |
基座档 10 板实读经等价断言与 `_cell_for(10, (1022,454))` 严格相等(V1 守卫当轮执行);13/14 推定由探针 V4 打印(当轮留痕);12 板与全屏 10/12 三格由单点算式读数件在两档真实可用面积上读出(一次性临时件,当轮跑批留痕后即删):基座 12 板=33px、全屏 10/12=46px。全表与卡 §2 第 7 条锚点逐值一致,零对拍偏差。
## 五、实机目检总清单(登记不阻塞)
1. **第三关 14×14 板格面数字清楚可辨**——2026-11-01 用户已实际背书现行画面(锚点裁定),登记照留(卡 T4)。
## 六、实现决定记录
代拍板白名单 6 项全部按卡执行(`_cell_for` 局部变量名与注释措辞/四档取样表用字典数组/探针消息文案与 PASS 措辞/尾缀〔格边长 V1–V4〕编排/`BoardViewScript` 常量名/`bv.get("_board")` 取值形态),零超范围裁决。超白名单备案三件(均属测量面承载,判据零扩张):
1. **探针 `_board == null` 前置**(`layout_probe.gd:104-105`)——卡片明列判据只有零尺寸前置与等价断言;null 前置防「内部直查遇 null 即脚本错误静默跳过剩余断言」(test-conventions §7 失败模式),属测量面防御。撤销方式=删该分支(等价断言改回无条件求值)。
2. **探针新增两成员 `_cell_reads`/`_est_reads`**(`layout_probe.gd:17-18`)——PASS 行基座实读与推定留痕打印的承载面;探针不在 S13 扫描面,无预算约束。撤销方式=删成员与两处打印(PASS 行退回无读数形态)。
3. **单点算式锚点全表读数件**(本报告 §四 12 板与全屏 10/12 三格补读)——卡 §2 第 7 条锚点表含四板型,探针打印只覆盖 13/14(卡定死),全表留档用一次性临时脚本对两档实读可用面积逐板型读 `_cell_for`(当轮跑批留痕后即删)。撤销方式=无(纯读数,不落码)。
## 七、遗留读数义务
零。本批无数值改动,MC/verifier 零跑(卡 §4 范围边界 6)。§四全表读数已当轮留痕,无待补。
## 八、S13 结构预算与批末闸对账
- `board_view.gd`:`max-public-methods` **27→27**(`_cell_for` 私有名不计数)、`max-class-variables` **31→31**(零新增成员——钳制界未外提)、无 NEW 键;gdstyle 全批「基线外新违规 0、较登记值恶化 0」。
- 探针与测试件不在 S13 扫描面(扫描面=产品码,测量面直构为既有惯例)。
- 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(回执落 `game/tests/.gates/receipt.json`,T4 当轮执行)。
## 九、独立验收审计结论
独立验收审计由未参与实现的子代理执行,报告=`reports/iter46-验收审计-卡C-2026-11-01.md`。结论:**通过,可合并**——五项全过(对照表五行逐行核对/自跑最窄测试实读:探针基座实读 40px·推定 30/28·46/46·单元 74 checks 全绿/判据绑定面钉生产写入值/测试削弱逐处对上卡 §8 申报全集/全仓无人造阈值回归且无变异·撤销取证)。一处非阻断备注已处置:实现报告 §三 行 1 判据绑定面格补「受控例外」句(审计报告原文备注定格于补句前)。审计方自跑读数与本实现会话当轮实跑逐值一致。未过不得合并红线未触发。
+130
View File
@@ -0,0 +1,130 @@
# 迭代 46 独立验收审计报告
> 审计方:独立验收审计子代理(未参与本批实现——迭代 loop 内启动,符合 test-conventions §8.7 两入口铁规)。
> 日期:2026-10-31。
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,基点 b5b2c555)。Godot:`Godot_v4.7.2-stable_win64_console.exe`。
> 审计范围:合并候选=卡A(换图系双基座七件批)+卡B(站桩名字行提档与地台色合并批)已提交 diff=`git diff b5b2c555..c10bbc11`(10 笔提交=9 实现+1 收尾文档;20 文件,+1231/−96)。卡C(棋盘格边长)停摆不计。
> 审计方式:不采信实现报告「已全绿」自述——逐行核对对照表两端、自跑最窄套件与闸件、对照 diff 逐处核对削弱申报。本审计只写本报告,零源码/测试/数据文件改动。
## 一、审计自跑记录(当轮真实执行)
| 套件/闸 | 结果 | 与报告申报对照 |
|---|---|---|
| refresh_feats_test | **all green (85 checks)**,exit 0 | 与报告§二 申报 85 一致 |
| standee_present_values_test | **all green (26 checks)**,exit 0 | 与报告§三 申报 26 一致 |
| adventure_feats_test | **all green (138 checks)**,exit 0 | 与报告§二 申报 138 一致 |
| round_flow_skill_pool_test | **all green (134 checks)**,exit 0 | 与报告§二 申报 134 一致 |
| seal_polarity_test(附加) | all green (70 checks),exit 0 | 与报告§二 申报 70 一致 |
| effect_words_t1_test(附加,V1) | all green (20 checks),exit 0 | 与报告§二 申报 20 一致 |
| forge_table_test(附加,V1) | all green (41 checks),exit 0 | 与报告§二 申报 41 一致 |
| gdstyle_gate | 5 键全基线内、基线外新违规 0、恶化 0 | 与报告§五「零新增零恶化」一致 |
| static_scan.sh | exit 0——S7 ok(B2/B3/B4 符号收敛 B1 文件集)/S8 ok/S13 ok | S7 新域集生效见§六① |
| render_gate.sh(窗口化实跑) | 板面三张+menu_boot 全 **changed=0.00%**,全绿 | 与报告§八.4 一致,见§三行 5 |
## 二、卡A 对照表逐行核对(V1–V12)
| 需求点 | 测试存在 | 尾缀可 grep | 真实入口无绕过 | 判定 |
|---|---|---|---|---|
| V1 词表扩签 | `effect_words_t1_test.gd:_init` 新增①-a 两断言+`forge_table_test.gd:_test_effect_kinds_closed` 22/34 换代 | 〔换图系批 V1〕×2+〔锻造工坊批 V11〕×2 | ConfigDB 整表加载+词表常量直读 | **通过**(实跑 20/41 checks 绿;diff 核对 effect_words 纯增量 5 行,旧出域负例零删) |
| V2 矿脉储能 | `test_reserve_on_accepted_dig` | 〔换图系批 V2〕×3 | 经 `_auto_dig`→`settle_batch([move]+[dig])` 真实付费挖格;辅证 `_drain_round_rap_accounting` 仍走 settle_batch 逐挖计数 | **通过**(RAP 8→9、AP 按表扣、耗尽轮增量==受理挖数三面全钉) |
| V3 省力周转 | `test_refresh_discount_gate_and_charge`+`test_double_base_stack` | 〔换图系批 V3〕×6 | 经 `orch.player_request_refresh()` 唯一入口;can 与执行同源(`_effective_refresh_cost` 单点,diff 核实编排器两公开方法单行转发) | **通过**(折后 −4×2、门槛拒 can=false 且执行拒、双基座同场生效) |
| V4 成功事实 | `test_board_refresh_fact_only_on_success` | 〔换图系批 V4〕×6 | 经 `player_request_refresh()`;非 Active 拒=decay 行内同步探测(round_flow_skill_pool_test:161 先例同款),上限拒/RAP 不足拒均在 Active 相位真入口 | **通过**(三拒面 probe_br_counter==0+board_changed 计数冻结双观测;成功恰一次×2+新轮×2) |
| V5 全局受伤 | `test_all_hero_damage_paths_emit_once`+`test_no_duplicate_damage_taken_event` | 〔换图系批 V5〕×8 | **四路径全真实战斗驱动、零直调 `_emit`**:①敌方攻击=boss 战零挖掘连停三轮(enemy_action 行在案);②地形=AutoPlayer 盲挖 flat_damage 变异格+stop;③危险格=真挖直接伤型雷格;④错误封印=真 `seal_place`+stop | **通过**(每路径「承伤行数==计数器」恰一次+无重复消费两样本;实跑读数 行2=计2/行9=计9/行2=计2/行2=计2) |
| V6 石层缓冲 | `test_stone_mitigation_all_damage` | 〔换图系批 V6〕×3 | 刷新经 `player_request_refresh`;危险格/地形经真挖+stop 结算 | **通过**(pierce 8→6 双刷不叠=叠加态 4 反证钉死;未刷轮不减=round 桶轮始清空;地形 max(下限, effect_value−2) 逐单闭合) |
| V7 崩壁冲击 | `test_blast_defers_to_combat_segment` | 〔换图系批 V7〕×2 | 刷新+停手(轮末授予段→战斗段末) | **通过**(刷新时点零伤害行=授予段不结账;停手后恰一单 value 2 全体存活敌) |
| V8 蓄力周转 | `test_charge_layers_consumed_same_round` | 〔换图系批 V8〕×2 | 双刷+停手(主角先手攻击)真实消费 | **通过**(白盒预置 3 层上限闸=实现决定 #11 申报面;双刷 2 层攻击后清 0;战斗新建 0) |
| V9 坑道小憩 | `test_rest_rotation_cap_and_clear` | 〔换图系批 V9〕×3 | 刷新经真入口;轮末恢复在授予段(stop 触发) | **通过(附未注例外,见§五)**(回复 min(3,缺血)==3 且层清 0;满血未生效行+零 heal 行;自然累计 2 路径) |
| V10 矿道红利 | `test_mine_profit_gp` | 〔换图系批 V10〕×4 | 经 `player_request_refresh()`;拒绝=RAP 不足真拒 | **通过**(escrow 每次+1、fx_grant_gp 行恰 1、拒绝零变化;消息文案时点瑕疵见§七.3,不涉判据) |
| V11 数据与池 | `test_seven_feats_validate_and_pool`+adventure_feats `_smoke_feats` | 〔换图系批 V11〕×5+〔专长批 V2〕换代句 | ConfigDB 整表+冒险牌库真实启动(seed 71) | **通过**(validate 空/七行稀有度 cap 对表/desc 禁词/七件计数器引用闭合/三计数器在册;rows 37、14/13/10/0、deck 139;feats.tres 七行 trigger 结构逐行对照卡§十.T6 零出入) |
| V12 无专长回归 | `test_no_feats_baseline`+round_flow `_smoke_active_refresh`+seal `_smoke_refresh_provenance_guard` | 〔换图系批 V12〕×4 | 挖格/刷新/停手全真入口 | **通过**(挖格零 RAP、表基线扣 8、AP 保留、第 3 次拒;round_flow/seal 两文件**零进 diff**——RAP8/上限2/AP保留/刷新图零清图/五段序/回放断言原样在案,134/70 checks 实跑绿) |
## 三、卡B 对照表逐行核对(6 行)
| # | 需求点 | 判定 |
|---|---|---|
| 1 | 名字行字号提档 FS_HINT 14 | **通过**——diff 核对五取值点(`:334-336` 两测量+`:467` 测量+`:474` 护甲绘制+`:476` 名字绘制)全改 `UiKit.FS_HINT`;`:114` 消散提示零触碰(FS_CAPTION 计数锚==1 断言在案且实跑绿);`name_line_probe` 增 `name_font_size` 键(探针实读==FS_HINT 绿) |
| 2 | 名字行几何随档联动 | **通过**——`_name_baseline` 偏移 17→18+注释换代(diff 核实);V6 读数 150±0.51 与「8px 档」句换代=卡B§8 申报第 1 条面内;`baseline_y < plinth_top_y` 文字不压地台判据零改动且绿 |
| 3 | 受光线常量退役改指 BRICK | **通过**——ui_kit 真删 `STAND_STONE_RIM` 无别名占位;`standee_body` 顶缘线与 `cell_base_layer:176` 口沿高光两绘制点改指 `BRICK`;V11=常量表零键+BRICK 逐位 c9af83+两处源码锚全绿;grep 全仓退役常量仅剩测试断言串(与报告§三「预期例外」申报一致) |
| 4 | 描边常量退役改指新 token | **通过**——ui_kit 删 `STAND_STONE_EDGE`+原位增 `PLAZA_EDGE := Color8(76, 64, 56)`;`standee_body` 回退描边改指;V12=PLAZA_EDGE 逐位 4c4038+源码锚全绿 |
| 5 | 板面 golden 与换值一致 | **通过**——审计方自跑 `bash tools/render_gate.sh`(窗口化):board_L1/L2/L3 与 menu_boot 全 0.00%;golden 五文件 git 零改动=基线未动、重锚未执行(与实现决定 #4 备案一致——#C4AC84→#C9AF83 灰度差约 2,低于容差 ≤16,0.00% 为必然);level_map.png 不在 render_gate 对比域、git 零改动 |
| 6 | 批末收口 | **通过(遗留义务明示)**——迭代级 `--code` 闸未跑(卡B 行 6 申报「待迭代出口」);批内证据=commit 点 docs 域回执(fde1795d)全绿含 08c 流水线闸+审计方补跑 gdstyle/static_scan/render_gate/五套件全绿。合回后本地 CI 全量域闸为唯一强制验证点(process-conventions §1),本行遗留义务随合回 CI 承接 |
## 四、抽查断言原样输出行(当轮实跑摘录)
```
[refresh_feats_test] all green (85 checks)
[PASS] V5① 敌方攻击路径:承伤单据在案且逐单恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V5② 地形路径:地形单据在案且全单据逐张恰一次(行 9=计数 9——盲挖途经单据一并计入)〔换图系批 V5〕
[PASS] V5③ 危险格路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V5④ 错误封印路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
[PASS] V2 受理付费挖格 RAP 恰 +1(8→9)〔换图系批 V2〕
[PASS] V3 折后单次扣费(8→4,−4)〔换图系批 V3〕
[PASS] V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕
[PASS] V6 S1 危险格减伤恰 2 且多刷不叠加(行值 [6]∈[6]——叠加态应为 4)〔换图系批 V6〕
[PASS] V9 R1 轮末回复=min(3,缺血102) 且层清 0(heal 行值 [3])〔换图系批 V9〕
[PASS] V10 第 1 次成功 escrow +1(0→0)〔换图系批 V10〕
[PASS] V12 刷新受理并按表基线扣(−8)〔换图系批 V12〕
[standee_present_values] all green (26 checks)
[PASS] 基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕
[PASS] 名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕
[PASS] 全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕
[PASS] 退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕
[PASS] PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕
[adventure_feats_test] all green (138 checks)
[PASS] 专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕
[round_flow_skill_pool_test] all green (134 checks)
[seal_polarity_test] all green (70 checks)
[effect_words_t1] all green (20 checks)
[forge_table_test] all green (41 checks)
```
```
[render_gate] [golden] board_L1_dungeon.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] board_L2_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] board_L3_deep_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] [golden] menu_boot.png — changed=0.00%(容差 ≤16 差/≤2%)OK
[render_gate] 全绿
```
注:V10 消息「escrow +1(0→0)」的两个读数均在 `player_request_refresh()` 求值前格式化——显示瑕疵;断言本体绑刷新后 `escrow == escrow0 + 1`,判据面正确(绿即真值)。
## 五、内部字段直查受控例外核对(test-conventions §80/§8.7 第 4 条)
| 直查行 | 性质 | 判定 |
|---|---|---|
| `orch.fx_host().counters_ctx`(refresh_feats_test 5 处:读计数器×3+白盒预置×2) | `counters_ctx` 是 BattleFxHost **公开成员**(无下划线),读经公共 API `EffectEngine.counter` | 非私有直查;存量套件 adventure_feats_test 同款先例 |
| `EffectEngineScript.counter_add(...)` 白盒预置(V9/V8 上限 3 闸) | 计数器直改=数据面注入 | **已申报**——实现决定 #11(test-conventions §5.4 引用)+对照表 V8/V9 行「白盒预置」在案 |
| `orch1._enemy_system.hero_unit()`(refresh_feats_test:518,V9 缺血读数) | **私有面直查 1 处** | **例外(手续缺注,不阻塞)**——对照表 V9 行与实现决定 #11 均未注该行理由。依据:①判据主面(heal 行/未生效行)经公共 `step_row` 信号在案,私有读只作前置量(`missing >= 3` 守卫),行为并非钉在私有面上;②存量套件 roll_multiplier_test.gd:51 同款先例;③公开替代面存在(`presentation_snapshot()` 含 hero.hp)。建议实现报告 V9 行补注一句(随合回流程,本审计不代改) |
| 生产码 `oc._damage._emit_hero_damage_taken`/`oc._rounds._grant_seg` 直调 | 结算族内部协作 | 实现决定 #8 备案(结算族直调惯例)——非测试面,不在本节判域 |
## 六、附加核对(实现决定复核三项+卡C 脏面)
1. **S7_B1_SET 增补(实现决定 #6)——判据口径未变,通过**:static_scan.sh diff 仅 1 行清单+2 行注释;B2/B3/B4 三族符号交替式(14/5/9 符号)逐字未动;豁免语义(自家文件、注释/字符串排除、s7-b1-exempt)未动;`battle_refresh_flow.gd` 调 `oc._reward_economy.retire_mapping()` 命中 B4 符号,入 B1 域集与 C13 拆分边界自洽。审计实跑 static_scan:S7 ok(50 文件扫描面)。
2. **S9 修正(f498d2ec)——零行为变化,通过**:4 处显式非参数化容器注解(`damage_controller.gd` 3 处 `: Dictionary`/`: Array`、`grant_segment.gd` 1 处)改 `:=` 推断,静态注解改写,无逻辑分支变化。
3. **卡C 停摆脏面核对——申报一致,另见§七.1**:`git status` 修改面恰为申报的三件(`board_view.gd`/`layout_probe.gd`/`board_gen_reward_test.gd`),均未提交、不在审计 diff 内;另有 2 个未跟踪 `.uid` 文件(§七.1)。
4. **golden 申报制核对**:golden 五文件(三张板面+level_map+menu_boot)git 零改动=重锚申报制零触发(实现决定 #4「无需重锚」与实跑 0.00% 互证);不存在借重锚夹带。
## 七、测试削弱核对清单(diff 被改/被删旧断言逐处 → 申报面)
| 位置 | 改动 | 申报依据 | 判定 |
|---|---|---|---|
| adventure_feats_test :464/:476 | 行数 30→37、稀有度 12/9/9→14/13/10、牌库 132→139 | 卡A§十二第 1 条(计数换代申报) | **通过**——零放宽:validate/禁词/退役钉面零删(diff 核对) |
| forge_table_test :202 附近 | EFFECT_KINDS 32→34、EVENTS 20→22 | 卡A§十二第 1 条 | **通过** |
| effect_words_t1_test :17-21 | 纯增量 5 行(两事件两效果在册) | 增量非削弱 | **通过**(旧负例全保留,diff 核对) |
| standee_present_values_test V6 | 读数 149→150+「9px 档→8px 档」句 | 卡B§8 申报第 1 条 | **通过**(文字不压地台判据零改动) |
| standee_present_values_test V10–V12 | 纯增量 8 断言+文件头注释换代 | 卡B§8 申报第 2 条 | **通过**(V5/V7/V8/V9 判据零改动,diff 核对) |
| run_all.gd :191 | 套件注释 V5–V9→V5–V12 | 非行为改动(卡B§6 预告) | **通过** |
| 禁删面(卡A§十二):RAP8 基线/每轮上限 2/AP 保留/刷新图零清图/伤害下限/五段序/同种子回放 | round_flow/seal 两文件零进 diff;伤害下限在 `_resolve_board_flat_hero` 保位(`maxi(下限, dealt)` 减后过下限=卡C4 同款) | 卡A§十二禁删令 | **通过**(134/70 checks 实跑绿承载) |
| 生产面删除项 | `_terrain_damage_reduce` 删/编排器旧刷新体删/`_enemy_shield_apply` 改名/ui_kit 两常量删 | 卡A T2/T3 步骤明列+卡B§1/§3 | **通过**(全部卡面内,无未申报删改) |
| 变异/撤销类取证 | 全 diff 无回退 hunk/注入破坏证明 | 卡A§十二+卡B§3 | **通过**(临时专长经 ConfigDB 表行追加=卡§十二「正常数据配置路径」申报一致) |
## 八、附加发现(不属验收对照面、不阻塞合并——处置建议随合回流程)
1. **批内新件两枚 `.uid` 未随批提交**:`game/core/settlement/battle_refresh_flow.gd.uid` 与 `game/tests/unit/battle_settlement/refresh_feats_test.gd.uid` 未跟踪(mtime 07:49,早于 T3 提交 07:52——非审计跑测所生成)。`.gitignore` 第 6 行明文「Godot 4 的 .uid 文件必须提交,不要忽略」,全仓其余 `.gd` 均有 `.uid` 入库伴件。已核零 UID 引用(grep 两 uid 串零命中),风险低;建议合回前补提交(本审计受只读约束不代提交)。
2. **S8_FILES 未与 S7_B1_SET 同步增补**:await 零命中扫描集仍为 10 文件,新 B1 拆件不在其内。实查新件零 await(grep 证),当日无漏扫;但两清单同源 B1 域集出现分叉,建议 architecture「模块归属」BattleRefreshFlow 行落文(反哺点 #5)时同批补 S8_FILES。
3. **V10 消息文案时点**(§四注):纯文案瑕疵,判据不受影响。
4. **V9 私有读未注理由**(§五):建议补注。
## 九、结论
**审计通过(零缺漏)**——卡A 对照表 V1–V12 与卡B 对照表 6 行逐行通过(例外 1 处=V9 私有读手续缺注,附依据不阻塞);四套件+三附加套件+gdstyle+static_scan+render_gate 当轮自跑全绿;测试削弱全部在两卡申报面内,无未申报删改,无变异/撤销类取证;结构预算申报(803 行/类变量 15/各件增员)经实数核对全部相符。§八附加发现四条均不属验收对照面,随合回流程处置。
@@ -0,0 +1,91 @@
# 迭代 46 验收审计——卡C 棋盘格边长口径收口批 v2.0(2026-11-01)
> 审计方:独立子代理(未参与实现,只读证据+自跑最窄测试,不采信实现报告转述)。
> 审计对象:commit `e3e206d0`(代码三件)+`8eaf7d87`(收尾文档两件);卡C 段基点 master `5269ed40`。
> 依据:需求卡片 v2.0(§8 改动—验收对照表+测试削弱申报全集)、实现报告 §三 实际对照表、批末闸回执。
> 审计工作区:`D:\code\g-plan2-iter46`(分支 iter46)。审计全程只读,唯一写入=本报告。
## 一、对照表逐行核对——通过
实现报告 §三 五行逐行核对,每行的验收测试真实存在、尾缀可 grep、真实入口未绕过改动点。
行 1(探针阈值退役+接线守卫):
- 测试真实存在:`game/probe/layout_probe.gd:98-109`(读 `cell_size` → 等价断言),失败路径 `:107`,PASS 行 `:45`。
- 尾缀 grep:`grep -n "格边长 V1" game/probe/layout_probe.gd` → `:45`/`:105`/`:107` 三处。
- 真实入口:`layout_probe.tscn:6` 挂载 `res://probe/layout_probe.gd`(场景进,非脚本直调);探针断言对象=`main.tscn` 实例化的生产 UI 链(`main.ui.battle.board_zone.board_view`),非测试替身。
- 报告附带的 grep 读数(MIN_CELL 零命中)由审计方独立复跑证实(见第五节)。
行 2(算式单点 V2):
- 测试真实存在:`game/tests/unit/core/board_gen_reward_test.gd:75-76`,断言条件 `int(cell) == board_view_script._cell_for(n, avail)`。
- 改动点行号核实:`board_view.gd:210`=`_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`、`:226`=`static func _cell_for`——与报告所记 `:210`/`:226` 逐一相符。
- 真实入口:`--script res://tests/unit/core/board_gen_reward_test.gd` 直跑(文件头 `:10` 自述同入口)。
行 3(四档冒烟 V3):
- 测试真实存在:`board_gen_reward_test.gd:59` 四档取样表 `[(1,0,10),(2,0,12),(3,0,13),(3,9,14)]`,`:69`/`:71`/`:73` 三条 V3 断言。
- 真实入口同行 2(同一宿主 `_init:33` 调 `_smoke_layout_cell_formula`)。
行 4(推定留痕 V4):
- 测试真实存在:零尺寸前置 `layout_probe.gd:113-114`,推定采集 `:116-119`,PASS 后打印 `:47-50`,尾缀 `:49`/`:114`。
- 真实入口同行 1。
行 5(批末收口):
- `game/tests/.gates/receipt.json` 实读:`all_green=true`、`commit=e3e206d0f5646ecb8ece22ac08018c26e5591e1d`、七闸(00/01/02/03/05/09/10)exit 全 `0`、`domain=code`、`scope=related[presentation]`、`changed_vs_baseline=[]`——与报告行 5 所记一致。审计方未重跑全量闸(按审计约束,回执承载)。
## 二、自跑最窄测试抽查——通过
a. 探针两档(工作目录 `D:\code\g-plan2-iter46\game`):
- 命令:`/d/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path . res://probe/layout_probe.tscn`
- 实测 exit 0。关键输出原文:
- `[layout_probe] PASS — 2 档几何不变量全过+格边长等价成立(基座实读 40px)〔格边长 V1〕`
- `[layout_probe] [基座 1280x800] 13 板推定 30px/14 板推定 28px(实板可用面积 (1022.0, 454.0))〔格边长 V4〕`
- `[layout_probe] [16:9 全屏 1920x1080] 13 板推定 46px/14 板推定 46px(实板可用面积 (1662.0, 734.0))〔格边长 V4〕`
- 读数与卡 §2 第 7 条锚点(基座 30/28、全屏 46/46,可用面积 1022×454/1662×734)逐值一致,零对拍偏差。审计方另按公式手工复核:基座 13 板 `(422−24)/13=30.6→30`、14 板 `(422−26)/14=28.28→28`、全屏 14 板 `(702−26)/14=48.3→钳制 46`——打印值与单点算式自洽。
b. 单元四档(同工作目录):
- 命令:`…console.exe --headless --path . --script res://tests/unit/core/board_gen_reward_test.gd`
- 实测 exit 0,收尾行 `[board_gen_reward_test] all green (74 checks)`。
- 四档各四断言全 PASS:L1D0(41px)/L2D0(33px)/L3D0(31px)/L3D9(28px)——每档「板型一致〔格边长 V3〕」「网格高 ≤ 可用高−24〔格边长 V3〕」「格边 ∈ 26–46〔格边长 V3〕」「算式单点等价〔格边长 V2〕」各一条,16 条尾缀齐全。62→74 增量=每档净增 3 条 × 4 档,账目相符。
c. gdstyle(工作目录 `D:\code\g-plan2-iter46`):
- 命令:`python tools/gdstyle_gate.py`
- 实测 exit 0:`扫描面 142 个 .gd;命中 5 条,基线内 5 条,基线外新违规 0 条,较登记值恶化 0 条`。
- 基线两键实读:`tools/gdstyle_baseline.txt:11`=`board_view.gd|quality/max-class-variables = 31`、`:12`=`board_view.gd|quality/max-public-methods = 27`——持平,无 NEW 键。
## 三、判据绑定面核查——通过(一处非阻断措辞备注)
1. 绑定的是生产写入值,不是多层防御后的终态:
- 探针取值面=`cell_size` 公开只读属性(`board_view.gd:89-91` getter 直返 `_cell`),`_cell` 由 `set_cell_area` 在 `:210` 直接写入;生产写入路径两条未动(`board_zone_view.gd:92` resized 信号→`set_cell_area(_board_area.size)`、`board_view.gd:273` rebuild 末尾)。等价断言 `layout_probe.gd:106` 对 `bv.size`(实板可用面积)严格相等(`!=` 记红)——生产停调单点算式即红(基座档默认值 46 与算式值 40 分叉可捕)。
- 单元取值面=真实调用 `set_cell_area(avail)` 后直读 `_cell`(`board_gen_reward_test.gd:65,67`),与同输入 `_cell_for(n, avail)` 逐档 int 严格相等(`:76`)——同一事实两路读取。
2. 内部字段直查两处的例外依据:
- 探针读 `_board`(`layout_probe.gd:103`):理由「板型 N 无公开出口」已载于代码注释 `:102`、卡 §7.3 T2 与卡 §14.2、报告 §六 白名单第 6 项。
- 测试读 `_cell`(`board_gen_reward_test.gd:67`):批前既有取值面(卡 §2 第 4 条记载改前即如此),卡 §8 申报 4 明列 `cell_size`/`_cell` 为两合法入口、禁换别的入口——属守住的旧面,非新开直查。
3. 非阻断措辞备注:卡 §14.2 与代码注释 `layout_probe.gd:102` 均称例外理由「对照表行 1 注明」,但实现报告 §三 行 1 判据绑定面格(以及卡 §8 行 1)未逐字带「N 无公开出口/受控例外」句——交叉引用指针落空。例外事实与理由已在他处完整承载(本节第 2 点所列四处),断言零削弱、无申报外改动,不判红;建议实现会话顺手把该句补进报告 §三 行 1(可选,不阻塞合并)。
## 四、测试削弱核查——通过
e3e206d0 负向行(删除行)全集逐处对账,全部落进卡 §8 申报四条,无申报外删改:
| 删除内容 | 对应申报 |
|---|---|
| `const MIN_CELL := 36.0` 整行 | 申报 1(阈值常量退役) |
| `if cell < MIN_CELL:` +「< 可读门槛」记红两行 | 申报 1(阈值断言删除) |
| 旧 PASS 消息(「格子 ≥36px」形态)两行 | 申报 2(文案随判据换代) |
| `for L in [1, 2]` 与 `generate(L, 0, …)` 两行 | 申报 3(循环集 2→4 档)+白名单第 2 项(字典数组形态) |
| 两条旧 Kit.check 的消息行 | 申报 3(前缀 L%d→L%d D%d——卡 §7.4 明许)+白名单第 4 项(尾缀编排) |
| 冒烟 print 头行(「底部预留回归」→「·四档板型」) | 申报 3 同批日志头随档数换代(非断言,无条件面) |
| `board_view.gd` 内联算式六行 | 非断言——逐字搬入 `_cell_for`(`board_view.gd:226-231` 对拍:SEP/DEPTH_PX 同源、reserve 24.0+d 同式、clampi(26,46) 留函数内),行为零变化由单元 74 checks 全绿承载 |
补偿三件全在位:等价断言(V1)、结构钳制四档(V3)、推定留痕(V4)。既有两断言条件逐字保留(`grid_h <= int(avail.y) - 24`、`cell >= 26.0 and cell <= 46.0`),样本 `Vector2(1036, 460)` 未改。既有几何不变量与视口档位表零改动(diff 负向行中无其一)。申报 4(禁恢复阈值/删旧断言/放宽区间/换读取入口)成立——四处均未发生。
## 五、全仓无人造阈值回归——通过
- `grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` → 零命中(exit 1);扩到 `tools/`、`design/` 查 `MIN_CELL` 亦零命中。
- 30/36 阈值形态穷举:三件改动文件中 30 仅两处且均非格边长判据(`board_gen_reward_test.gd:203`「板面 30% 锚」=雷数比例锚、`board_view.gd:659`「南部 30%」=墙面口径注释);36 零出现。全 `game/` 断言形态 grep(`(>|>=|<|<=)\s*(30|36)`)命中皆为无关域(RNG 位移 `>> 30`、商店 `1 << 30` 哨兵价、护甲 `<= 30` 属性域检);`clampi` 无 30/36 形态。格边长唯一区间判据仍是公式自带 `clampi(26, 46)`。
- 变异类测试与撤销类取证:diff 负向行无「人为改错值验抓取」形态;无回退 hunk;测试代码无注入破坏分支(探针 `_board == null` 前置属测量面防御,报告 §六 第 1 项已备案,非变异)。
- design/ 现状:`battle.md:144`「可读下限 30px」句仍在位=预期(实现批禁写 `design/`,退役走反哺点通道——`reports/iter46-反哺点-卡C-2026-11-01.md` 第 1 条已登记,卡 §5 明示不阻塞本批)。
- 禁写面核查:卡C 段全区间 `5269ed40..8eaf7d87` 触碰文件恰为三件代码+两件 reports,`待办清单.md`/`体验验证清单.md`/`game/tests/README.md`/`design/` 零触碰。
- 工作区漂移核查:三件代码文件 `git status` 干净——审计实跑的即 e3e206d0 提交的字节。
## 六、总结论
**通过。** 五项审计全过:对照表五行真实入口与尾缀齐全;自跑探针两档绿(基座实读 40px、推定 30/28 与 46/46 合锚点)、单元 all green 74 checks(四档 41/33/31/28px,V2/V3 全 PASS)、gdstyle 基线外新违规 0 恶化 0;判据绑生产写入值与 `_cell_for` 严格相等;删改逐处对上申报四条;全仓无人造阈值回归、无变异/撤销形态。唯一遗留=第三节第 3 点的交叉引用措辞备注(不判红、不阻塞合并,补句归实现会话可选动作)。
@@ -1,57 +1,61 @@
# 需求卡片——棋盘格边长口径收口批(探针门槛纠偏+格边长算式单点+冒烟扩四档板型)
# 需求卡片——棋盘格边长口径收口批 v2(阈值退役+格边长算式单点+冒烟扩四档板型)
状态行:v1.0 定稿——零待裁点(2026-10-30 用户裁:①探针不扩到 L3 推关;②最紧档 14×14 的可读断言落单元四档+探针实板推定两处;③算式单点提取用私有静态名,依据见 §2 第 5 条)
状态行:v2.0 定稿——零待裁点(2026-11-01 用户裁:①可读性锚点=用户验收的现行渲染值——基座档 14×14 板实测 28px 即达标,battle.md「可读下限 30px」判据句退役(落文归文档批,走反哺点通道),探针旧值 36 一并废弃;②探针格边长判据换锚=实读值与单点算式严格相等+结构钳制保护+全档读数留痕,全仓禁任何人造格边长阈值;③阈值手抄常量 `MIN_CELL` 删除,禁第二份阈值抄本再入代码或测试)
接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交)」(`待办清单.md` 在册,状态「待写卡」)
接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交)」(`待办清单.md` 在册)
来源:**设计事实源**=`design/gdd/ux/battle.md`「棋盘格尺寸公式」节(Last Updated 2026-10-29;格边长口径的**唯一事实源**——公式、可读下限 30px、结构钳制 [26,46] 三项都在此节)。指路文档(2026-10-26 已归一、不再存算例)=`design/art/art-bible.md` §8.1/§8.2、`design/gdd/systems/表现与支撑/HUD框架.md`。四档板型事实源=`game/data/tables/board_gen.tres`。
来源:**设计事实源**=`design/gdd/ux/battle.md`「棋盘格尺寸公式」节(2026-11-01 实查——公式与结构钳制 [26,46] 在 `:137-146`;原 `:148-152` 算例表已于 2026-10-31 布局几何单点化批退役(commit 1cdef6ba),现行 `:148`=「算例不入文档」句;`:144`「可读下限 30px」句经 2026-11-01 用户裁退役,落文走 §5 反哺点通道,本卡判据零引用)。布局事实源=`game/data/tables/battle_layout.tres` 五字段(2026-10-31 布局几何单点化批起为分区几何唯一事实源)。四档板型事实源=`game/data/tables/board_gen.tres`。锚点裁决过程与实测读数链=[停机呈报-棋盘格边长口径收口批-2026-10-31](停机呈报-棋盘格边长口径收口批-2026-10-31.md)。
## 1. 目标与完成定义
这批做什么:把「棋盘格边长」在代码侧残留的两处旧口径收干净。文档侧已于 2026-10-26 归一(口径只剩 battle.md 一节),代码侧还差两件——探针的可读门槛仍是 12×12 时代的 36px(比 battle.md 的可读下限 30px 严 6px,属用旧值当新标准),格边长冒烟只跑 L1/L2(13×13 与 14×14 两档板型无断言)。本批顺带把格边长算式收成单点(现在它内联在 `set_cell_area` 里,任何「在别的板型/别的面积上求值」的需求都只能复写算式=造第二口径)。
这批做什么:把「棋盘格边长」在代码侧的残留旧口径收干净——**方向经 2026-11-01 用户裁换代**:不是把探针阈值 36 改成 30(v1 方向,已废),而是**把人造阈值整个退役**——可读性的锚点=用户验收过的现行渲染画面(基座档四档板型 40/33/30/28px),检查工具不再拿任何编造的下限给画面打分,只做两件事:①验证生产写入值与单点算式严格相等(接线守卫);②把四档板型在实板可用面积上的推定值留痕(回归对照基准)。同时完成 v1 已裁的两件:格边长算式收成单点(现在内联在 `set_cell_area` 里,任何「在别的板型/别的面积上求值」的需求都只能复写算式=造第二口径)、13/14 两档板型进入测试守卫。
**行为零变化**:算式逐位不变,`set_cell_area` 的副作用顺序(英雄脚锚重置 → `_refresh_slab` → `_update_world_size`)零改动,玩家可见画面零变化。
完成定义(全部满足才算完):
1. 代码侧可读门槛=30px,与 battle.md「可读下限 30px」一致;旧值 36 在 `game/` 下零残留。
1. 代码侧人造阈值归零:`layout_probe.gd` 的 `MIN_CELL` 常量删除,`grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` 零残留;30 与 36 均不得以任何形态回到代码或测试判据。
2. 格边长算式只剩一处落点(`BoardView._cell_for`),`set_cell_area` 只调它;探针与单元测试不再各自复写算式。
3. 13×13 与 14×14 两档进入断言并在当轮真实跑批中执行通过(单元四档+探针实板面积推定两条)。
4. 代码域闸全绿;`tools/gdstyle_baseline.txt` 中 board_view.gd 各键只许持平或下降(MATCH——`max-public-methods` 保持 27)。
3. 13×13 与 14×14 两档进入守卫并在当轮真实跑批中执行通过(单元四档冒烟+探针等价断言与推定留痕)。
4. 代码域闸全绿;`tools/gdstyle_baseline.txt` 中 board_view.gd 各键只许持平或下降(MATCH——`max-public-methods` 保持 27、`max-class-variables` 保持 31)。
5. 产出物三份落 `reports/`(实现报告/反哺点清单/验收审计报告),独立验收审计未过不合并。
**编队注记与估时**:估时 **0.5 个工作日**(T0 0.1/T1 0.15/T2 0.1/T3 0.1/T4 0.05),远低于迭代冻结线(5 个工作日),可编批;编入哪个迭代由迭代编排段定。本批触面=`game/presentation/views/board_view.gd`+`game/probe/layout_probe.gd`+`game/tests/unit/core/board_gen_reward_test.gd`+`game/tests/README.md`(写卡段已同批入表),与在飞批次无文件交叠需编排段复核。
**编队注记与估时**:估时 **0.5 个工作日**(T0 0.1/T1 0.15/T2 0.1/T3 0.1/T4 0.05),远低于迭代冻结线(5 个工作日),可编批;编入哪个迭代由迭代编排段定(worktree iter46 的未提交 WIP 三件中,T1 与 T3 两件按 v2 原样复用,T2 探针件按本卡新判据返工——见 §12)。本批触面=`game/presentation/views/board_view.gd`+`game/probe/layout_probe.gd`+`game/tests/unit/core/board_gen_reward_test.gd`,与在飞批次无文件交叠需编排段复核。
## 2. 现状事实
全部行号为 **2026-10-30 实查**——本批编入后须由 T0 按 T0 时点 master 重测重锚。
全部行号为 **2026-11-01 实查**(master 3499638d)——本批编入后须由 T0 按 T0 时点 master 重测重锚。
1. 算式内联在 `game/presentation/views/board_view.gd:191-209`(`set_cell_area`):`:194` N=`maxi(width,height)`、`:195` `sep := SEP`(`SEP := 2`,`:34`)、`:196-197` `d := DEPTH_PX`(`8.0`,`:37`)与 `reserve := 24.0 + d`、`:198` 一条 `int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))`、`:199` `clampi(cell, 26, 46)`。副作用尾链 `:200-209`(英雄脚锚重置/`_refresh_slab`/`_update_world_size`)。
2. 生产调用路径两条:`game/presentation/views/board_zone_view.gd:91-92`(`board_area.resized` → `set_cell_area(_board_area.size)`)与 `game/presentation/views/board_view.gd:250`(`rebuild` 末尾 `set_cell_area(size)`)——传入的都是棋盘视图实际可用区尺寸,`board_view` 为 `PRESET_FULL_RECT` 子级,故 `board_view.size` 即生产传入值。
3. 探针门槛:`game/probe/layout_probe.gd:15` `const MIN_CELL := 36.0`,注释自陈「battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px」;`:92-94` 断言 `cell < MIN_CELL` 记红。探针只进 L1(`:27` 起始冒险)、`:31` `press_candidate(0)`,故实跑只会遇到 10×10(格边约 45px),门槛 36 从未在 30–35px 区间生效过。
4. 格边长冒烟:`game/tests/unit/core/board_gen_reward_test.gd:53-69`(`_smoke_layout_cell_formula`):`:57` `for L in [1, 2]`、`:58` `generate(L, 0, ...)`、`:60` 样本 `avail := Vector2(1036, 460)`、`:63` 读 `board_view.get("_cell")`、`:64-66` 网格高 ≤ 可用高−24、`:67-68` 格边长 ∈ [26,46]。13×13 与 14×14 两档从未进入断言。
5. **S13 顶格事实(本机 gdstyle v0.4.0 实测)**:`tools/gdstyle_baseline.txt:22` 登记 `game/presentation/views/board_view.gd|quality/max-public-methods = 27`,与实测公开方法数 27 相等。对同一文件副本加一个公开静态函数后实测升到 **28**(WORSENED 红);改名为下划线前缀(`_cell_for`)后实测仍为 **27**(零新发现)。故算式提取必须用私有名。
6. 四档板型取档(`game/data/tables/board_gen.tres` 实查 (level_index, node_depth) → 尺寸):L1 depth 0–3=10×10、L1 depth 4–6=12×12、L2 depth 0–9=12×12、L3 depth 0–8=13×13、L3 depth 9–13=14×14。即 `generate(1,0)`=10、`generate(2,0)`=12、`generate(3,0)`=13、`generate(3,9)`=14。
7. 规格侧:`battle.md:137-146` 公式与两项判据——结构钳制区间 [26,46]px(`:143`,管算式不越界)、**可读下限 30px**(`:144`,管玩家能否看清,与钳制分开);`:148-152` 算例表按「1280 档可用高 486px」推算 43/36/33/30,文内明标「随布局与板型变化,不是契约值」。
8. 实机可用面积与算例不同源:`game/presentation/ui_assembler.gd:20-28` 结构为 B 区 252px + C/D 两列占余高(该文件 `:24` 注释写明 548px),D 列内 `board_zone_view.gd:24-36` 再减 AP 条 44px 与 4px 间距——与 battle.md 算例假设的 486 可用高存在偏差(与待办清单在册的「B 区高度 270 vs 252」同源)。本卡不把算例当契约,实板可用面积一律以探针实读 `board_view.size` 为准。
1. 算式内联在 `game/presentation/views/board_view.gd:206-215`(`set_cell_area`):`:209` N=`maxi(width,height)`、`:210` `sep := SEP`(`SEP := 2`)、`:211-212` `d := DEPTH_PX` 与 `reserve := 24.0 + d`、`:213` 一条 `int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))`、`:214` `clampi(cell, 26, 46)`。副作用尾链自 `:215` 起(英雄脚锚重置/`_refresh_slab`/`_update_world_size`)。
2. 生产调用路径两条:`game/presentation/views/board_zone_view.gd:91-92`(`board_area.resized` → `set_cell_area(_board_area.size)`)与 `board_view.gd` `rebuild` 末尾 `set_cell_area(size)`——传入的都是棋盘视图实际可用区尺寸,`board_view` 为 `PRESET_FULL_RECT` 子级,故 `board_view.size` 即生产传入值。
3. 探针阈值手抄件:`game/probe/layout_probe.gd:15` `const MIN_CELL := 36.0`,注释自陈出处为旧算例表「1280 档 12×12 → 36px」;`:93-95` 断言 `cell < MIN_CELL` 记红;`:44-45` PASS 消息引用 MIN_CELL。探针只进 L1(`:27` 起始冒险)、`:31` `press_candidate(0)`,实跑只遇 10×10(实读 40px),36 阈值从未在 28–35px 区间生效过——**该常量与旧算例表同源,旧算例表已退役(1cdef6ba),此抄本成为无源之值**。
4. 格边长冒烟:`game/tests/unit/core/board_gen_reward_test.gd:53-69`(`_smoke_layout_cell_formula`):`:57` `for L in [1, 2]`、`:60` 样本 `avail := Vector2(1036, 460)`、`:63` 读 `board_view.get("_cell")`、`:64-66` 网格高 ≤ 可用高−24、`:67-68` 格边长 ∈ [26,46]。13×13 与 14×14 两档从未进入断言。
5. **S13 顶格事实(2026-11-01 实查,gdstyle v0.4.0)**:`tools/gdstyle_baseline.txt` 登记 `board_view.gd|quality/max-public-methods = 27`(`:22`)与 `max-class-variables = 31`(`:11`),与静态清点逐一相符。对同一文件副本加一个公开静态函数后实测升 28(WORSENED 红);改名为下划线前缀(`_cell_for`)后持平 27。**故算式提取必须用私有名,且本批禁给 board_view.gd 新增任何成员(31 同样不容新增——钳制界不得提取为公开常量)**。
6. 四档板型取档(`board_gen.tres` 实查 (level_index, node_depth) → 尺寸):L1 depth 0–3=10×10、L1 depth 4–6=12×12、L2 depth 0–9=12×12、L3 depth 0–8=13×13、L3 depth 9–13=14×14。即 `generate(1,0)`=10、`generate(2,0)`=12、`generate(3,0)`=13、`generate(3,9)`=14。
7. **锚点读数(2026-10-31 探针实读,停机呈报在案;2026-11-01 用户裁定为现行锚点)**:基座 1280×800 档 `board_view.size`=(1022.0, 454.0),10/12/13/14 板 → 40/33/30/28px;16:9 1920×1080 档 (1662.0, 734.0) → 四档全部 46px(钳制顶格)。代入复核(1280 档 14 板):`min(1022−12−8, 454−32) = 422`;`(422 − 13×2) / 14 = 28.28 → floor 28` → 钳制 [26,46] 内 → 28px。
8. 布局事实源换代(2026-10-31 布局几何单点化批,1cdef6ba):分区几何唯一事实源=`battle_layout.tres` 五字段(实查 `stage_h=252`/`ap_bar_h=44`/`info_panel_w=240`/`log_band_h_dig=24`/`log_band_h_settle=84`);容器内部结构(VBox 间距 4px、常驻操作提示条、面板边距)在代码中,实板可用面积以探针实读 `board_view.size` 为准,不静态推算。
9. 旧判据毒性记录(v1 停机的根因,防复发):「可读下限 30px」与「36px 门槛」均无实测背书——30 系旧算例表(按假设可用高 486px 推算)的产物被写成规则,36 是探针从该表错抄的第二份手抄件;两者互相打架且与实机脱节,是 2026-10-31 停机的直接原因。本批后此类「文档句子↔代码常量」双写形态在本域不得再现(§3 契约②③)。
## 3. 设计契约(违反即返工,标 🔴 触碰=L3 停机呈报)
- 🔴 **算式单点**:格边长算式只在 `BoardView._cell_for` 一处;`set_cell_area` 只调它。探针、测试、任何新调用方都不得复写这条算式(复写=造第二口径,本批立项原因就是三处口径打架)。
- 🔴 **两个数不得合并**:可读下限 30px(battle.md 判据)与结构钳制 [26,46]px(算式不越界)是两个概念。前者只出现在探针门槛与断言里,后者只出现在 `_cell_for` 内;禁止用钳制区间替代下限判据,也禁止把下限塞进 `cell_for` 的 clamp。
- 🔴 **私有名固定**:提取函数名必须是 `_cell_for`(下划线前缀)。依据=§2 第 5 条的实测:公开名即令 `max-public-methods` 27→28 恶化。实现期「顺手改成公开方法更好看」=L3。
- 🔴 **锚点唯一**:格边长的判据锚=①用户验收的现行渲染值(§2 第 7 条读数表,只作记录与对照,不作判红阈值)+②公式自身结构钳制 [26,46](battle.md `:143`,单元冒烟既有断言承载)。**本批起全仓禁任何人造格边长可读阈值**——30、36 及其变体不得出现在任何代码、测试、探针判据中;发现即 L3。
- 🔴 **阈值手抄禁令**:探针与测试不得新增阈值常量。与算式相关的判据一律经 `BoardViewScript._cell_for(...)` 等价比较或既有公开出口(`cell_size`)承载;唯一例外=单元冒烟既有的 [26,46] 区间断言(v1 前已存在,逐字保留)。
- 🔴 **算式单点**:格边长算式只在 `BoardView._cell_for` 一处;`set_cell_area` 只调它。探针、测试、任何新调用方都不得复写这条算式(复写=造第二口径)。
- 🔴 **私有名固定**:提取函数名必须是 `_cell_for`(下划线前缀)。依据=§2 第 5 条实测:公开名即令 `max-public-methods` 27→28 恶化;同理禁新增任何成员(`max-class-variables` 31 顶格)。实现期「顺手改成公开方法更好看」=L3。
- 🔴 **行为零变化**:`set_cell_area` 的副作用顺序与数值逐位不变;`rebuild`/`resized` 两条调用路径不改;画面像素零变化。
- 🔴 **禁为机检文本保留无效成员**(重构类卡条款,2026-10-25 立):禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名;机检键与本卡契约互斥时,实现段一律停卡呈报,不得自造第三条路。
- 🔴 **拆分边界纪律**(结构类卡条款,2026-10-28 立,判据权威=code-conventions §1.1):①拆出单元 `_cell_for` 职责语句=「按 battle.md 格尺寸公式,由板型 N 与可用面积算格边长的无状态纯函数」,单一变化原因=公式本身,实现报告逐件一行承载;②集体身份=「算式唯一落点」(命得出名字,非凑计数袋);③「过阈值」只许作为发现信号,作为拆分或收拢的动机即违规;④按纪律拆不出内聚单元 → 停卡呈报走豁免/还债通道,禁硬拆凑绿(进 §9 L3)。
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8「测试削弱申报」,未申报的删改由独立验收审计判红。
- 🔴 **禁变异类测试与撤销类取证**:不得靠回退 hunk 或注入破坏证明断言有效。
## 4. 范围边界
做:算式提取为 `_cell_for` 并改调;探针门槛 36→30 与注释改指 battle.md;探针新增「实板可用面积下 13/14 板格边长推定」断言与 board_view 零尺寸前置;格边长冒烟扩到四档板型并加算式单点等价断言;`game/tests/README.md` 能力行入表(写卡段同批,见 §5)。
做:算式提取为 `_cell_for` 并改调;探针删除 `MIN_CELL` 常量与阈值断言、改等价断言+推定留痕+零尺寸前置;格边长冒烟扩到四档板型并加算式单点等价断言。
不做(明示去向):
1. **探针扩到 L3 推关**——2026-10-30 用户裁不做:成本是探针要真打穿两关(main 无跳关入口),且一旦红因是推关而非布局会污染该闸;最大板可读面改由 §7.3 的推定断言承担。
2. **battle.md 算例表改数**——实现批禁写 `design/`;实测与算例的偏差走 §5 反哺点通道。
3. **四档板型数值调整**——`board_gen.tres` 板尺寸由设计侧定,本批只读不写。
4. **B 区高度 252 vs 270**——另一条在册待办(迭代 43 批后待裁三点),本批不碰。
1. **探针扩到 L3 推关**——v1 已裁不做(推关成本与红因混淆);维持。
2. **battle.md「可读下限 30px」句的落文**——实现批禁写 `design/`;退役措辞走 §5 反哺点通道,文档批落文。本批判据不依赖该句(退役与否不阻塞实现)。
3. **四档板型数值调整**——`board_gen.tres` 由设计侧定,本批只读不写。
4. **布局表字段调整**——`battle_layout.tres`(含 `stage_h`)刚经布局几何单点化批落地,本批只读;任何为格边长而动的布局调整(如加高棋盘区)=另一张设计批的卡。
5. **`set_cell_area` 副产物顺序调整**——零触碰。
6. **MC/数值验证**——无数值改动零跑。
@@ -59,112 +63,114 @@
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/gdd/ux/battle.md(设计权威——实现批禁直改) | ①`:148-152` 算例表按「可用高 486px」推算 43/36/33/30,而实机 D 列结构为 548px(§2 第 8 条)——探针实测格边长若与算例不符,是算例随布局漂移而非代码错;②「可读下限 30px」(`:144`)条本身不变,本批正是向它对齐 | 反哺点通道:把探针 L1 实测格边长与 13/14 推定读数逐条列进 `reports/iter<N>-反哺点-<日期>.md`(改哪一节=`:148-152` 算例表、改成什么=填实测值或标注「按旧布局口径」、依据=实测读数),用户会话裁决后归文档批落文 |
| game/tests/README.md(机械面——归写卡段维护) | 新增能力行「战斗屏三区几何与棋盘格边长(分辨率两档×四档板型)」,覆盖域表 HUD框架.md 行同步补名 | 写卡段已于本卡成文同批入表,实现批零增删零改行 |
| design/gdd/systems/表现与支撑/HUD框架.md、design/art/art-bible.md §8.1/§8.2(已归一为指路) | 本批不改二者的任何陈述(2026-10-26 归一后它们只写规则与指针,不存算例与定值档) | 无(本行占位——按规约查不出也要写明) |
| design/gdd/ux/battle.md(设计权威——实现批禁直改) | ①`:144`「可读下限 30px——与结构钳制分开:钳制管算式不越界,下限管玩家能否看清」句——2026-11-01 用户裁退役;依据=锚点裁决+停机呈报读数链(30 无实测背书、36 错抄自已退役的算例表)。推荐落文方向(措辞由文档批裁):整句删除,或改写为「格边长可读性以用户验收的现行画面为基准;结构钳制 [26,46] 管算式不越界」 | 反哺点通道:列进 `reports/iter<N>-反哺点-<日期>.md`(改哪节=`:144`、改成什么=删除或上述改写、依据=停机呈报+用户裁),用户裁决后归文档批落文。②v1 的「算例表改数」反哺条**作废**——`:148-152` 表已随 1cdef6ba 退役,无落点 |
| game/tests/README.md(机械面——归写卡段维护) | 能力行「战斗屏三区几何与棋盘格边长(分辨率两档×四档板型)」v1 写卡段已入表;v2 判据换代不改能力名与真实入口 | 无增删(本行占位核对:批末核对表上名字与实况一致) |
| design/gdd/systems/表现与支撑/HUD框架.md、design/art/art-bible.md §8.1/§8.2(已归一为指路) | 本批不改二者的任何陈述 | 无(本行占位——按规约查不出也要写明) |
## 6. 卡前文件结构图
| 文件 | 职责 | 新增/复用 | 行为改动? |
|---|---|---|---|
| game/presentation/views/board_view.gd | 修改:`set_cell_area` 内联算式抽成 `static func _cell_for(n, avail) -> int`,`set_cell_area` 改调;注释补「算式唯一落点」句 | 复用既有(抽自本文件既有代码,不新增文件/类) | 否(算式逐位相同——对照表行 2 的等价断言承载) |
| game/probe/layout_probe.gd | 修改:门槛 36→30+注释改指 battle.md;新增 board_view 零尺寸前置与 13/14 板格边长推定断言 | 修改 | 是(守卫面变化——对照表行 1/4) |
| game/tests/unit/core/board_gen_reward_test.gd | 修改:格边长冒烟循环扩四档板型+板型一致断言+算式单点等价断言 | 修改 | 是(测试面——对照表行 2/3) |
| game/tests/README.md | 修改:能力清单加一行+覆盖域表 HUD框架行补名 | 修改 | 否(机械面文档,写卡段同批完成) |
| game/presentation/views/board_view.gd | 修改:`set_cell_area` 内联算式抽成 `static func _cell_for(n, avail) -> int`,`set_cell_area` 改调;注释补「算式唯一落点」句 | 复用既有(抽自本文件既有代码,不新增文件/类/成员) | 否(算式逐位相同——对照表行 2 的等价断言承载) |
| game/probe/layout_probe.gd | 修改:删 `:15` MIN_CELL 常量;`:93-95` 阈值断言换等价断言;`:44-45` PASS 消息改写并附 13/14 推定读数;新增 board_view 零尺寸前置与推定有效性判据 | 修改(v1 WIP 的探针件按本卡返工——v1 的「30 阈值+推定 ≥30」形态作废) | 是(守卫面换代——对照表行 1/4) |
| game/tests/unit/core/board_gen_reward_test.gd | 修改:格边长冒烟循环扩四档板型+板型一致断言+算式单点等价断言 | 修改(v1 WIP 原样复用) | 是(测试面——对照表行 2/3) |
不触碰:`board_zone_view.gd`、`ui_assembler.gd`、`data/tables/board_gen.tres`、全部 `core/` 与 `data/` 代码、其余探针与测试套件。
不触碰:`board_zone_view.gd`、`ui_assembler.gd`、`data/tables/board_gen.tres`、`data/tables/battle_layout.tres`、全部 `core/` 与 `data/` 代码、`game/tests/README.md`(v1 已入表)、其余探针与测试套件。
## 7. 任务卡 T0..T4
### 7.1 T0——读数重锚与取档对账(估时 0.1 个工作日)
- [ ] 按 T0 时点 master 重测重锚 §2 全部行号:`layout_probe.gd:15`/`:92-94`/`:27`/`:31`、`board_view.gd:34`/`:37`/`:191-209`/`:250`、`board_zone_view.gd:91-92`、`board_gen_reward_test.gd:53-69`。
- [ ] 按 T0 时点 master 重测重锚 §2 全部行号:`layout_probe.gd:15`/`:31`/`:44-45`/`:93-95`、`board_view.gd:206-215`、`board_zone_view.gd:91-92`、`board_gen_reward_test.gd:53-69`。
- [ ] 四档取档复核:对 `db.combat.board_gen.resolve_row(L, D)` 逐组取 (1,0)(2,0)(3,0)(3,9),实测返回 `board_width` 依次为 10/12/13/14——不符=L3 停卡退回写卡段。
- [ ] `battle_layout.tres` 五字段读数与 `battle.md:137-146` 现状句确认(`:144` 30px 句仍在位=预期,本批不碰,§5 反哺承载;若已被文档批提前退役亦不阻塞——本卡判据零依赖该句)。
- [ ] 改前基线读数(孤立验证):`python tools/gdstyle_gate.py`(board_view.gd `max-public-methods` 实测 27 持平)+单件跑 `board_gen_reward_test`(绿)+单件跑 `layout_probe`(两档绿)。三条读数进实现报告 T0 节。
- 取用:本卡 §2。产出:重锚后的行号与三份改前读数(T1–T4 对拍基准)。
### 7.2 T1——算式单点提取(估时 0.15 个工作日)
- [ ] `board_view.gd` 新增 `static func _cell_for(n: int, avail: Vector2) -> int`,函数体=`:195-199` 五行算式逐字搬入(`SEP`/`DEPTH_PX` 仍读本文件既有常量,`clampi(..., 26, 46)` 留在该函数内),返回 `int`。
- [ ] `set_cell_area` 的 `:194-199` 六行替换为 `_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`;`:200-209` 副作用尾链一字不动。
- [ ] `:189-190` 注释块补一句:算式唯一落点=`_cell_for`(探针与单元测试共用);名字带下划线是 S13 `max-public-methods` 27 顶格的约束,禁改公开名。
- [ ] `board_view.gd` 新增 `static func _cell_for(n: int, avail: Vector2) -> int`,函数体=`:210-214` 五行算式逐字搬入(`SEP`/`DEPTH_PX` 仍读本文件既有常量,`clampi(..., 26, 46)` 留在该函数内),返回 `int`。
- [ ] `set_cell_area` 的 `:209-214` 六行替换为 `_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`;`:215` 起副作用尾链一字不动。
- [ ] `:204-205` 注释块补一句:算式唯一落点=`_cell_for`(探针与单元测试共用);名字带下划线是 S13 `max-public-methods` 27 顶格的约束,禁改公开名。
- [ ] 孤立验证:单件跑 `board_gen_reward_test`(既有四套断言全绿=算式等价性证明)+`python tools/gdstyle_gate.py`(board_view.gd 各键持平,`max-public-methods` 仍 27)。
- 取用:T0 读数。产出:`BoardView._cell_for(n: int, avail: Vector2) -> int`(公开静态调用面,T2/T3 逐字消费)。
- 取用:T0 读数。产出:`BoardView._cell_for(n: int, avail: Vector2) -> int`(调用面,T2/T3 逐字消费)。
### 7.3 T2——探针门槛与最大板推定(估时 0.1 个工作日)
### 7.3 T2——探针阈值退役与判据换锚(估时 0.1 个工作日)
- [ ] `layout_probe.gd:15` 改 `const MIN_CELL := 30.0`,注释改为「可读下限 30px(battle.md §棋盘格尺寸公式;结构钳制 [26,46] 管算式不越界,是另一回事)」。
- [ ] 顶部加 `const BoardViewScript := preload("res://presentation/views/board_view.gd")`(静态调用载体)。
- [ ] `_check_one` 在既有格子断言之后追加两段:①board_view 零尺寸前置——`bv.size.x <= 0.0 or bv.size.y <= 0.0` 时记问题「board_view 零尺寸(可用面积不可信,格边长推定无依据)」;②推定断言——对 `n in [13, 14]` 取 `BoardViewScript._cell_for(n, bv.size)`,`< int(MIN_CELL)` 即记问题,消息含实板可用面积、板型、算出的格边长与下限。
- [ ] 收口 PASS 消息补一句「13/14 板推定 ≥30px」。
- [ ] 孤立验证:单件跑 `layout_probe`(两档全绿)+`tools/probe_gate.sh` 默认段(含本探针在内的默认段退出码 0)。
- 取用:T1 `_cell_for`。产出:绿的两档探针日志(T4 对拍基准)。
- [ ] 删除 `layout_probe.gd:15` `const MIN_CELL := 36.0` 整行。
- [ ] `:93-95` 阈值断言替换为**等价断言**:读 `var b = bv.get("_board")`(内部字段直查——受控例外,对照表行 1 注明理由:实跑板型 N 无公开出口),`var n := maxi(b.width, b.height)`;断言 `cell == float(BoardViewScript._cell_for(n, bv.size))`,失败消息含实板可用面积、n、两值(生产写入 vs 单点返回)。文件顶部加 `const BoardViewScript := preload("res://presentation/views/board_view.gd")`(静态调用载体)。
- [ ] `:44-45` PASS 消息改写:去 MIN_CELL 引用,改为「三区几何不变量全过+格边长等价成立(基座实读 %.0fpx)」。
- [ ] `_check_one` 追加两段:①board_view 零尺寸前置——`bv.size.x <= 0.0 or bv.size.y <= 0.0` 时记问题「board_view 零尺寸(可用面积不可信,推定无依据)」;②**推定留痕**:对 `n2 in [13, 14]` 取 `var est := BoardViewScript._cell_for(n2, bv.size)`,随 PASS 打印「13 板推定 %dpx/14 板推定 %dpx」留痕(实现报告对拍 §2 第 7 条锚点读数 30/28;对拍不一致=§9 L3 #2 呈报,禁实现侧改锚)。
- [ ] 孤立验证:单件跑 `layout_probe`(两档全绿,PASS 行含推定读数)+`tools/probe_gate.sh` 默认段退出码 0+`grep -rn "MIN_CELL" game/` 零命中。
- 取用:T1 `_cell_for`。产出:绿的两档探针日志含四档推定读数(T4 对拍基准)。
### 7.4 T3——格边长冒烟扩四档(估时 0.1 个工作日)
- [ ] `_smoke_layout_cell_formula` 的 `:57` 循环集改为四档取样表:`[{L:1,D:0,n:10}, {L:2,D:0,n:12}, {L:3,D:0,n:13}, {L:3,D:9,n:14}]`(按 §2 第 6 条的实表取档)。
- [ ] 每档追加两条断言:①板型一致——`maxi(b.width, b.height) == 取样 n`(表改动导致取样落错档时红);②算式单点——`int(cell) == BoardViewScript._cell_for(n, avail)`(`set_cell_area` 写入值与纯函数返回同值)。
- [ ] 既有两条断言(网格高 ≤ 可用高−24、格边长 ∈ [26,46])逐字保留,仅把消息前缀从 `L%d` 扩成 `L%d D%d`;`:60` 样本 `Vector2(1036, 460)` 不改(本卡把「真实可用面积」的可读面交给探针,样本只承担算式不溢出面)。
- [ ] 既有两条断言(网格高 ≤ 可用高−24、格边长 ∈ [26,46])逐字保留,仅把消息前缀从 `L%d` 扩成 `L%d D%d`;`:60` 样本 `Vector2(1036, 460)` 不改(样本只承担算式不溢出面,真实可用面积守卫归探针 V1/V4)。
- [ ] 孤立验证:单件跑 `board_gen_reward_test`(四档全绿)。
- 取用:T1 `_cell_for`、T0 取档对账。产出:四档冒烟日志(T4 对拍基准)。
### 7.5 T4——批末收口与产出物落位(估时 0.05 个工作日)
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体 → 代码域;02 单元与集成按关联档选跑 `board_gen_reward` 套件,冒烟/系统测试/静态扫描三件恒跑)。里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
- [ ] S13 键账对账:board_view.gd 各键持平(`max-public-methods` 27 不变、`max-class-variables` 32 不变、无 NEW 键);探针与测试不在 S13 扫描面。
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体 → 代码域)。里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
- [ ] S13 键账对账:board_view.gd 各键持平(`max-public-methods` 27 不变、`max-class-variables` 31 不变、无 NEW 键);探针与测试不在 S13 扫描面。
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与各卡改后读数对拍+探针实读的 `board_view.size`(两档视口)与 L1 格边长实测值+13/14 推定值+实机目检总清单(登记不阻塞:「第三关 14×14 板格面数字清楚可辨」一项,时间由用户自定)。
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面第 1 行(battle.md 算例表 vs 实测)逐条有落点。
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与各卡改后读数对拍+**锚点读数留档**(两档视口 × 四板型实读/推定全表=§2 第 7 条,未来批次对照基准)+实机目检总清单(登记不阻塞:「第三关 14×14 板格面数字清楚可辨」一项——2026-11-01 用户已实际背书现行画面,登记照留)。
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面第 1 行(battle.md `:144` 退役)有落点。
- `reports/iter<N>-验收审计-<日期>.md`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
- 取用:T1–T3 全部产出。产出:代码域闸回执+三份 reports/ 产出物。
## 8. 验证闸(改动—验收对照表)
本批三处改动全部落在守卫面(探针门槛/测试覆盖/算式落点),游戏行为零变化,故无新玩法验收;用户目检一项登记不阻塞(T4)。对照表 5 行对应 §1 完成定义第 1–5 条。
本批三处改动全部落在守卫面(阈值退役/算式落点/测试覆盖),游戏行为零变化,故无新玩法验收;用户目检一项登记不阻塞(T4)。对照表 5 行对应 §1 完成定义第 1–5 条。
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| 1 | 探针可读门槛与 battle.md 对齐(36→30) | `layout_probe.gd:15` 常量与注释;`:92-94` 断言文本沿用 | 「layout_probe 两档几何不变量」`layout_probe.gd:_ready`(尾缀〔格边长 V1〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 探针经真实战斗屏装配读到 `board_view.cell_size` 且 ≥30px(下限判据直接作用于实机取值) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 2 | 算式单点(`_cell_for`) | `board_view.gd:191-209` 抽函数并改调;`:189-190` 注释补唯一落点句 | 「格边长算式单点等价」`board_gen_reward_test.gd:_smoke_layout_cell_formula`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:53-69` 循环集改四档取样+板型一致断言 | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边长 ∈ [26,46](此前从未执行) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 4 | 最大板可读推定(实板可用面积) | `layout_probe.gd:_check_one` 新增 board_view 零尺寸前置+`n∈{13,14}` 推定断言 | 「最大板格边长推定」同探针(尾缀〔格边长 V4〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 以探针实读 `board_view.size` 代入 `_cell_for(13/14, size)` 得 ≥30px(真实容器尺寸下的最大板可读面——不推关即可覆盖) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 5 | 批末收口 | 全批 | 代码域闸:`tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 1 | 探针阈值退役+格边长接线守卫(等价判据) | `layout_probe.gd:15` 删常量;`:93-95` 阈值断言换 `cell == _cell_for(n, bv.size)` | 「layout_probe 两档几何不变量」`layout_probe.gd:_ready`(尾缀〔格边长 V1〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 实读 `cell_size`(生产经 `set_cell_area` 真实写入)与 `_cell_for` 同输入返回严格相等——生产停止调单点算式即红;`grep -rn "MIN_CELL" game/` 零命中 | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 2 | 算式单点(`_cell_for`) | `board_view.gd:206-215` 抽函数并改调;`:204-205` 注释补唯一落点句 | 「格边长算式单点等价」`board_gen_reward_test.gd:_smoke_layout_cell_formula`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:53-69` 循环集改四档取样+板型一致断言 | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边长 ∈ [26,46](公式自身结构钳制——既有断言逐字保留) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 4 | 最大板推定守卫(实板可用面积,留痕型) | `layout_probe.gd:_check_one` 新增零尺寸前置+`n∈{13,14}` 推定(读数打印) | 「最大板推定留痕」同探针(尾缀〔格边长 V4〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | ①零尺寸前置(size ≤ 0 记红);②推定读数打印并与实现报告留档的锚点读数(基座档 30/28px)逐值对拍,不一致=§9 L3 #2 呈报(禁实现侧改锚) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
| 5 | 批末收口 | 全批 | 代码域闸:`tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
**测试削弱申报**(实现期允许的旧断言改动全集,超出即审计判红):
1. **探针门槛 36→30=阈值放宽**,依据 battle.md `:144`「可读下限 30px」(旧值 36 是 12×12 旧锚点的误用,探针实跑从未触及 30–35px 区间,等于该门槛对大板无效)。补偿=第 4 行新增推定断言与第 3 行四档冒烟——原门槛守不住的面由两者接管。
2. 第 3 行循环集从 2 档扩到 4 档=纯增量;`avail` 样本值不改;既有两条断言(网格高、区间)逐字保留。
3. 探针新增断言=纯增量,既有格子断言与两档视口表不改。
4. 显式禁止:删任何旧断言、放宽区间 [26,46]、把格子读取从 `cell_size`/`_cell` 换成别的入口。
1. **删除探针阈值断言**(`:93-95` `cell < MIN_CELL` 记红)=人造阈值退役的执行面(v1 申报的「36→30 放宽」作废,v2 直接删除)。补偿=行 1 等价断言(同测量对象上严格相等,强于区间阈值——生产接线断开即红)+行 3 结构钳制四档+行 4 读数留痕对拍;目检项「第三关 14×14 板格面数字清楚可辨」继续登记(2026-11-01 用户已实际背书现行画面)。
2. `:44-45` PASS 消息改写=文案随判据换代,非削弱。
3. 行 3 循环集从 2 档扩到 4 档=纯增量;`avail` 样本值不改;既有两条断言逐字保留。
4. 显式禁止:恢复任何人造阈值(30/36 及变体)、删任何旧断言、放宽区间 [26,46]、把格子读取从 `cell_size`/`_cell` 换成别的入口。
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面是否绑在被测改动特有的可观测面(`cell_size`/`_cell` 取值与 `_cell_for` 返回)、核对削弱申报、确认全文无变异/撤销类取证;未过不得合并。
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面是否绑被测改动特有的可观测提交面(`cell_size`/`_cell` 取值与 `_cell_for` 返回)、核对削弱申报、确认全文无变异/撤销类取证、**并核查全仓无人造阈值回归**(grep 30/36 阈值形态);未过不得合并。
## 9. 异常分级
- **L1 自救**:单件跑红→定位该件最小单元修后重跑;探针单件超时→孤立复跑该件;`--only` 选跑零命中(用法错 exit 2)→修正选词重跑。
- **L2 记录继续**:同一红连续 3 次同因→在实现报告「实现决定记录」记因后继续最窄确认。
- **L3 停机写报告等用户**:
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或 `board_view.gd|quality/max-public-methods` 升到 28(即 `_cell_for` 的下划线被去掉);本批零新增文件、零新增成员,任何键恶化都不许改基线换绿,禁开豁免。
2. **实板可读面本身不达标**——探针实测 L1 `cell_size` < 30,或实板可用面积代入 13/14 后 < 30。这是产品面问题(不是断言错),停卡呈报,禁调低下限、禁换样本凑绿。
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或 board_view.gd `max-public-methods` 升 28/`max-class-variables` 升 32(即 `_cell_for` 的下划线被去掉或钳制界被提取为成员);禁改基线换绿,禁开豁免。
2. **锚点冲突复现**——实现期任何一方(用户目检/新读数)推翻 §2 第 7 条现行渲染锚点,或有人再提人造阈值方案,或探针推定读数与 §2 第 7 条留档读数对拍不一致且复测排除环境因素;停卡呈报,禁实现侧自裁。
3. **四档取档对不上**——`resolve_row(1,0)/(2,0)/(3,0)/(3,9)` 实测尺寸与 §2 第 6 条不符(表已改而本卡未跟上)。
4. **算式等价性破**——T1 后既有四套断言出现改前不存在的红,说明提取改变了算式语义。
5. **battle_layout.tres 被并发批改动**——T0 实读五字段与本卡 §2 第 8 条不符(布局几何单点化批之后又有布局批落地),读数锚点须重测重锚,停卡呈报。
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
1. `_cell_for` 内部局部变量名与注释措辞(算式、常量、clamp 数值不变)。
2. 四档取样表用字典数组还是三元组数组(取值与断言不变)。
3. 探针推定断言与零尺寸前置的失败消息文案、PASS 消息措辞(判据不变)。
3. 探针推定留痕与零尺寸前置的失败消息文案、PASS 消息措辞(判据不变)。
4. 尾缀〔格边长 V1〕–〔格边长 V4〕的字样编排(含义不变)。
5. `BoardViewScript` 常量名(preload 载体命名)。
6. 探针内读取板型 N 的取值写法(`bv.get("_board")` 后取 `maxi(width, height)` 的具体表达式形态——直查事实与判据不变)。
## 11. 待裁点
**零待裁点,开工门=U0。** 2026-10-30 用户三项裁定已定案:①探针不扩到 L3(推关成本与红因混淆,最大板改走推定断言);②最紧档 14×14 的断言落两处(单元四档+探针实板推定);③算式提取用 `_cell_for` 私有名(§2 第 5 条 S13 实测依据)。编批由迭代编排段定。
**零待裁点,开工门=U0。** 2026-11-01 用户三项裁定已定案:①锚点=用户验收的现行渲染值——基座档 14×14=28px 达标,battle.md「可读下限 30px」与探针 36px 退役;②探针判据换锚=等价+结构钳制+读数留痕,禁人造阈值;③手抄常量 `MIN_CELL` 删除、禁第二份阈值抄本。v1 的「探针不扩到 L3」「算式提取用 `_cell_for` 私有名」两项裁定沿用。battle.md `:144` 句退役的落文走反哺点通道(文档批,用户发令),不阻塞本批。编批由迭代编排段定。
## 12. 落位与可复用性预检
**复用面**:本批零新增文件、零新增类。`_cell_for` 抽自 `board_view.set_cell_area` 既有内联算式——不能只留在原处内联,是因为探针要在「非当前板型(13/14)× 真实可用面积」上求值、单元测试要在四档板型上求值,内联形态无法复用,复写算式就是造第二口径(正是本卡要消灭的缺陷形态)。探针与测试的既有工具件(`probe_ui_util.gd`、`test_kit.gd`)与本批职责无关,不复用也不扩展。
**复用面**:本批零新增文件、零新增类、零新增成员。`_cell_for` 抽自 `board_view.set_cell_area` 既有内联算式——不能只留在原处内联,是因为探针要在「非当前板型(13/14)× 真实可用面积」上求值、单元测试要在四档板型上求值,内联形态无法复用,复写算式就是造第二口径。**worktree iter46 未提交 WIP 三件的复用判定**:`board_view.gd` 算式提取与 `board_gen_reward_test.gd` 四档冒烟两件=按本卡原样复用(等价性已证:单元 62→74 checks 全绿);`layout_probe.gd` 件=**返工**(其 v1 形态「MIN_CELL→30+推定 ≥30 断言」带有人造阈值,与本卡契约①③冲突——按 §7.3 重做)。探针与测试的既有工具件(`probe_ui_util.gd`、`test_kit.gd`)与本批职责无关,不复用也不扩展。
**唯一面**:算式唯一落点=`BoardView._cell_for`;可读下限 30px 的唯一代码判据面=`layout_probe.MIN_CELL`(文本出处=battle.md `:144`);四档板型唯一事实源=`board_gen.tres`(测试按 (L,D) 取行,不复制取值)。2026-10-30 实查 `game/` 下无 `cell_for`/`_cell_for` 同名或同义函数,无第二落点。
**唯一面**:算式唯一落点=`BoardView._cell_for`;四档板型唯一事实源=`board_gen.tres`(测试按 (L,D) 取行,不复制取值);分区几何唯一事实源=`battle_layout.tres` 五字段(本批只读);**格边长无阈值判据面**——2026-11-01 起本域不存在可读下限常数,唯一结构性界限=公式内 `clampi(..., 26, 46)`(不外提)。2026-11-01 实查 `game/` 下无 `cell_for`/`_cell_for` 同名或同义函数,无第二落点;`MIN_CELL` 仅 `layout_probe.gd:15` 一处(本批删除后 grep 零残留为证)。
**分层与交互面**:`_cell_for` 是无状态纯函数——零成员读写、零信号、零 RNG、零 UI 依赖、零 `load()`;调用路径=`set_cell_area`(生产)/探针 `_check_one`(经脚本常量静态调用)/单元测试(同前)。preservation:不新增跨层依赖,Core 零 UI 红线零触碰(本批不触 `core/`)。
@@ -172,12 +178,13 @@
**新件三查**:本批零新增文件——三查不适用(写「无新增件」)。
**存量触面一查**:触碰的存量件为 `board_view.gd`(S13 扫描面内)与探针/测试两件(不在 S13 扫描面)。`board_view.gd` 预期:`quality/max-public-methods` 保持 **27**(私有静态不计数,§2 第 5 条实测)、`quality/max-class-variables` 保持 32、`quality/max-branches|_paint_cell` 等函数级键持平(本批不增分支、不增成员、不增局部变量——`set_cell_area` 局部变量反而减少)。达成手段:算式整体搬出,函数体只留一行调用。
**存量触面一查**:触碰的存量件为 `board_view.gd`(S13 扫描面内)与探针/测试两件(不在 S13 扫描面)。`board_view.gd` 预期:`quality/max-public-methods` 保持 **27**(私有静态不计数,§2 第 5 条实测)、`quality/max-class-variables` 保持 **31**(零新增成员——钳制界禁提取为常量,§3 契约④)、`quality/max-branches|_paint_cell` 等函数级键持平(本批不增分支、不增成员、不增局部变量——`set_cell_area` 局部变量反而减少)。达成手段:算式整体搬出,函数体只留一行调用。
**指标例外申报**:无。
## 14. 源码级断言与探针契约面的随批义务(防漏项清单)
1. `layout_probe` 的既有两档视口表(`VIEWPORTS`)与三条几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)本批零改动——T2 只追加断言,不改判据。
2. `board_view` 的对外只读出口(`cell_size`,`board_view.gd:93-95`)是探针取值面,本批不动;测试内部读 `_cell`(测试惯例已如此,`board_gen_reward_test.gd:63`),不改为公开出口。
3. `game/tests/README.md` 能力行由写卡段同批入表(本卡成文时已落),实现批不写该文件;批末核对表上名字与实况一致。
1. `layout_probe` 的既有两档视口表(`VIEWPORTS`)与三条几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)本批零改动——T2 只换代格边长判据,几何判据与档位表一字不动。
2. `board_view` 的对外只读出口(`cell_size`)是探针取值面,本批不动;测试内部读 `_cell`、探针内部读 `_board`(板型 N 取值——受控例外,对照表行 1 已注明理由:N 无公开出口),不为此新开公开出口。
3. `battle_layout.tres` 五字段为分区几何唯一事实源(1cdef6ba 起),本批只读;任何布局字段改动归设计批。
4. `game/tests/README.md` 能力行由 v1 写卡段同批入表,v2 名字与入口不变,实现批不写该文件;批末核对表上名字与实况一致。
+3 -2
View File
@@ -6,7 +6,7 @@
- **阶段**:M3——Alpha 版本的完整实现(2026-10-09 立规:未明令宣布完成前停留 M3,迭代只排 M3 需求,M4 不排期;根目录 stage.txt 为旧阶段开关仅存档)。
- **迭代位**:迭代 1–45 全部已完成——迭代 45(呈现层状态件+gdstyle 尾波补件两卡)2026-10-31 合回(合并提交 f817ab2d,独立验收审计通过零缺漏,行为零变化),批后收尾闭合(2026-10-31):反哺点七条处置闭合(architecture 六条落文=breakpoint_store/board_hover/单格两件/站桩两件四行现状化+构造句改述与 7 项引用束/15 贴顶实况+gdstyle 截断缺陷节清偿结果;第 7 条工具观察迁待办新条)、目检三项登记体验验证清单 ㊸、产出物退役九件(两报告/反哺点/审计/两停机呈报+两卡+两设计文档)、worktree 两件清理(iter45+assert-slim-b2)。迭代 44 收尾记录见 git 提交 9eedf896。
- **下一步**:迭代 46 进行中〔用户裁拆批 2026-10-31〕——卡A 换图系双基座七件批+卡B 站桩名字行提档与地台色合并批已收口,代码域闸全绿且独立验收审计通过,合回与 CI 待发令;卡C 棋盘格边长口径收口批停摆退回写卡段:触发卡片 L3 #2 停机点,14×14 板基座档实测 28px 低于 battle.md 可读下限 30px、系存量事实首测非本批回归,读数与读法甲/乙裁决点见 reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md,裁决后重立卡。待编池:营地交互重做卡⑤(排卡④之后)、卡④(美术设计已闭合=样张过审 2026-10-31+art-bible v3.24 §6.2,待用户发令重立卡)、冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
- **下一步**:迭代 46 进行中〔用户裁拆批 2026-10-31〕——卡A 换图系双基座七件批+卡B 站桩名字行提档与地台色合并批已收口合回(合并提交 34bb62a5,独立验收审计通过,CI 全量绿);卡C 棋盘格边长口径收口批 **v2.0 重立回编**(用户裁锚点换代:可读性锚点=用户验收的现行渲染值,基座档 14×14=28px 达标,battle.md 可读下限 30px 句与探针 36px 人造阈值全退役,探针判据换锚=实读与单点算式严格相等+结构钳制+13/14 推定留痕;v2.0 卡经 grilling 独立复核,写卡批合并提交 e7090bc9,CI 全量绿),待 `/iter` 发令回 worktree iter46 续工(WIP 算式单点与四档冒烟两件复用、探针件返工)。battle.md `:144` 可读下限句退役归文档批待发令。迭代 47(营地交互卡④⑤)已编排待开工。待编池:冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
## 里程碑时间线
@@ -79,7 +79,8 @@
| 迭代 43 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈6.5 个工作日 | 已退役(git 历史可考) |
| 迭代 44 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈7.875 个工作日 | 已退役(git 历史可考) |
| 迭代 45 | 已完成(2026-10-31) | 已退役(git 历史可考) | 2026-10-29 | ≈7 个工作日 | 已退役(git 历史可考) |
| 迭代 46 | 进行中 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md | 2026-10-31 | ≈8.625 个工作日 | — |
| 迭代 46 | 进行中 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md<br>reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md | 2026-11-01 | ≈8.625 个工作日 | — |
| 迭代 47 | 待开工 | reports/需求卡片-营地场景图新建批-2026-10-18.md<br>reports/需求卡片-ESC浮窗与营地顶栏批-2026-10-18.md | 2026-11-01 | ≈14.25 个工作日〔用户特批 2026-11-01 超 10 个工作日〕 | — |
- 迭代 38 收口为带红合并〔用户特批 2026-10-26〕:09 静态扫描 S13 gdstyle 恶化 2 条经批准记入基线存量红(git 提交 c4c69701),卡② 停卡退上游,处方已于 2026-10-26 修订定稿 v1.3(git 提交 b3ea7532),随「gdstyle 存量债全量清偿」条目波 7 待编排。
- 迭代 39 收口为带红合并〔用户特批 2026-10-27〕:02 EV 锚 goblin 0.9148∉[1.00,1.20](触发器批有意行为改动,fp f74b9a48ef4a)已随 EV 锚重锚批换带归绿(git 提交 ba5105aa);08d 生涯.md:55 行号漂移已随 2026-10-28 批后收尾反哺落文修复归绿。三卡拆分后终局 gdstyle 恶化 2 条(battle_orchestrator max-file-length、level_map_screen max-class-variables)归迭代 40 卡①还债与迭代 41 存量债序列。
+14 -4
View File
@@ -2,6 +2,16 @@
本清单是流水线的唯一登记处:以后才做的事在此登记一条,随流水线段推进改状态,做完整条删除(条目记进度,不是各段的开工前置);各系统文档的「待办」节只留一行指路存根,不再自建条目。条目格式(标题=登记元数据含可选优先级,正文=实况/缺口/改动面/状态四属性)与状态机以 [doc-conventions §12.1](design/engineering/doc-conventions.md) 为权威。
#### hobgoblin 内容三件套补齐(2026-11-01 登记,来源=审计发现)
**实况**:敌人表 hobgoblin 行(寻址键 (1, battle, 3))数值完整在役、战斗正常运行,但内容三件套未交付——立绘、敌种语义模板、敌人系统.md 敌种表行均缺,呈现面因语义模板未成文无定义可依。登记凭据=[敌人系统](design/gdd/systems/战斗循环/敌人系统.md) v1.1「hobgoblin 缺口登记」节(2026-10-13 迭代 24 反哺④)。
**缺口**:还差敌种语义模板的文档设计讨论闭合(呈现面定义、池构成口径),闭合后立绘走美术线。
**改动面**:文档+美术素材+游戏代码
**状态**:待文档设计
#### 状态合并改累加制——同名状态重复授予改加时长(2026-10-31 登记,来源=会话提及)
**实况**:2026-10-31 用户立案并四点裁决闭合:六行 refresh 全改累加/时长不封顶/只加时长、层数与强度不变/累加量=本次授予的生效时长(技能施放行带覆盖时长时按覆盖值)。设计定稿=[增益状态](design/gdd/systems/战斗循环/增益状态.md) v1.3(2026-10-31,合并策略「刷新」改「累加」)+[地图战斗资源](design/gdd/systems/战斗循环/地图战斗资源.md) 削弱行引用句随改。查证记录:全仓设计文档、reports 与 git 全历史均无既往累加制决策,本次为新裁决、非补同步。本裁决消解「空刷新的可见性」条目(累加制下重复授予恒有变化,无空操作授予面)。
@@ -281,12 +291,12 @@
**实况**:扩板已落地(四档板型 10/12/13/14 在跑),2026-10-26 文档面已按单点维护归一:①格边长口径的唯一出处收敛到 [战斗屏规格](design/gdd/ux/battle.md)「棋盘格尺寸公式」——公式含格间距与垂直预留,可读下限 30px,1280 档算例 43/36/33/30 标注为随布局变化的非契约值;②[美术圣经](design/art/art-bible.md) §8.1 改为指路 battle.md、只锁资产侧两条比例,§8.2 甲类整数倍纪律已剔除砖面 tile(瓦片随格边长整格铺满,允许非整数倍);③[HUD框架](design/gdd/systems/表现与支撑/HUD框架.md) 只留装板可行性不等式,删去 36/45 定值档与旧算例;④demo 原型 44px 那一路随 `src/` 退役消失。剩余为代码面两处与口径不同步:`game/probe/layout_probe.gd:15` 的格子可读门槛 `MIN_CELL := 36.0` 仍取 12×12 的旧锚点值,比 battle.md 的可读下限 30px 严——现探针只进 10×10 的 L1 故仍全绿,一旦覆盖 13×13(33px)与 14×14(30px)即会误报红;`game/tests/unit/core/board_gen_reward_test.gd:53` 的格边长冒烟只跑 L1/L2,13×13 与 14×14 无覆盖。断言改动后按 `game/tests/README.md`「能力清单」核对受影响行(验证集清单已退役——test-conventions §8.3)。
- 需求卡片:[需求卡片-棋盘格边长口径收口批-2026-10-30](reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md)(2026-10-30 已写卡,零待裁点——算式单点提取经 `BoardView._cell_for`/探针门槛 36→30 对齐 battle.md 可读下限/冒烟扩 13×13 与 14×14;探针不扩到 L3,最大板可读面改由实板面积推定断言承担)。
- 停机呈报:[停机呈报-棋盘格边长口径收口批-2026-10-31](reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md)(迭代 46 实现会话触发卡片 §9 L3 #2 预设停机点,2026-10-31 用户裁拆批退回写卡段;14×14 板基座档实测 28px 低于可读下限 30px 的适用域裁决待定——读法甲/乙见呈报 §三,裁决后重写卡C;算式单点提取/探针门槛对齐/四档冒烟三件已验改动可复用,worktree iter46 未提交 WIP 在案)。
- 需求卡片:[需求卡片-棋盘格边长口径收口批-2026-10-30](reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md)(2026-10-30 写 v1.0;迭代 46 触发 L3#2 停机退回写卡段后,2026-11-01 用户裁重立 **v2.0**——可读性锚点=用户验收的现行渲染值(基座档 14×14=28px 达标)、人造阈值退役(battle.md 可读下限 30px 句与探针 36px 全废、`MIN_CELL` 手抄常量删除)、探针判据换锚=实读与单点算式严格相等+结构钳制+13/14 推定留痕;v1 的「探针门槛 36→30 对齐」方向作废)。
- 停机呈报:[停机呈报-棋盘格边长口径收口批-2026-10-31](reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md)(迭代 46 实现会话触发卡片 §9 L3 #2 预设停机点,2026-10-31 用户裁拆批退回写卡段。**2026-11-01 裁决已落**:14×14 基座档 28px 不再是「低于下限的不达标值」——用户裁可读性锚点=现行渲染画面本身,30/36 属无实测背书的历史编造数全部退役;battle.md `:144` 句退役归文档批落文(反哺点通道)。卡C v2.0 按此重立:算式单点与四档冒烟两件 WIP 复用,探针件返工)。
**改动面**:游戏代码
**状态**:已写卡
**状态**:已写卡+已编迭代 46
#### 立绘清晰度回退评估——是否改用高清图(2026-10-13 登记,来源=playnote)
@@ -445,4 +455,4 @@
**改动面**:文档+游戏代码+美术素材
**状态**:文档设计已闭合+美术设计已闭合+已写卡
**状态**:文档设计已闭合+美术设计已闭合+已写卡+已编迭代 47