merge: 分支并入 master(推进 84 提交,2026-10-21)——research/ 治理批按现master重取四处冲突
解冲突取法=master 现文为准、本批新增语义打上去,不照抄分支旧文本: - doc-conventions §11:活文档清单与「暂存地非档案库」行取本批新增全文(content-archive 退役内容留底/research/ 无常驻例外/跨会话交接文件禁入库/根 playnote 定性),清单称呼按 docmove 批现文写作「待办清单」,「落档必写接收方」「迭代实现报告」两行照收 master 版(旧日期注记不复活)。 - 08-Alpha版本标准:Alpha 出口裁决与营地五卡入 Alpha 的全部现行行照收 master,仅把 playnote 链接按新位置改指 ../../playnote.md。 - tools/repo_structure_audit.py:ROOT_FILES 取 master 全集(含待办清单/体验验证清单两件根文件)后追加 playnote.md。 - design/research/Alpha未完成事项梳理-2026-10-13.md:modify/delete 冲突按删除解——master 侧自分叉只改过两处指路句(f100e192/281d471e),其结论已由 08 v1.11-v1.12 与待办清单承载,吸收判据今日重核成立。 随批补两处 master 前进后暴露的同步面:AGENTS.md Project Layout 根目录段补 playnote.md 一行(白名单收了新根文件而目录说明未跟上);技能层 batch-closure 两份副本(.zcode 原件→整文件覆盖 .qoder)补「research/ 无常驻例外」的候选呈报口径与 playnote 不入退役候选。
This commit is contained in:
commit
224a947559
132 files changed
+2162
-2565
No files matched your search
@@ -16,7 +16,7 @@
|
||||
|
||||
## 运行与验证(工作目录 = 含 project.godot 的切片目录)
|
||||
|
||||
- **交付前验收 = 跑 `tools/run_all_gates.sh`,按本批改动域选档(`--code` 只跑代码域/`--docs` 只跑文档域/不带参数=两域全量),所选域全绿才合回 master**——它是全部验证闸的唯一入口与唯一清单,**域划分也只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单。一次改动同批混触 `game/` 与文档面时跑全量档。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推出该验哪个域再校验回执(缺回执/quick 模式/回执 commit ≠ HEAD/域不覆盖本次改动/含新增红,五况一律拦截)
|
||||
- **交付前验收 = 跑 `tools/run_all_gates.sh`,按本批改动域选档(`--code` 只跑代码域/`--docs` 只跑文档域/不带参数=两域全量),所选域全绿才合回 master**——它是全部验证闸的唯一入口与唯一清单,**域划分也只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单。一次改动同批混触 `game/` 与文档面时跑全量档。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推出该验哪个域再校验回执(五况拦截细则见 process-conventions §3.10)
|
||||
- 单件直跑(调试期最窄确认用,**不构成验收**):系统级断言面 `python tools/run_smoke.py`(名带 smoke 但不止存活检查,术语见 `game/tests/README.md`)· 场景探针 `…console.exe --headless --path . res://smoke/ui_probe.tscn` · 单元 `… --script res://tests/run_all.gd`
|
||||
- 调参批量:`--script res://smoke/adventure_monte_carlo.gd`(全冒险期望)· `res://smoke/reward_expectation.gd`(奖励期望);通关率/难度类测量一律走全冒险
|
||||
- 数值验证:`python tools/balance_verifier.py`(采集端 `res://smoke/verifier_batch.gd`,报告与原始数据落 `sim-data/`)
|
||||
@@ -32,9 +32,9 @@
|
||||
- `artwork/` — 在途图像素材唯一工作区(文档归文档/在途图归一处/终稿归游戏);整目录不入 git(.gitignore 强制),收口归档 `archive/`
|
||||
- `game/assets/art/` — 已验收终稿(入包被游戏加载);`game/` — 游戏工程本体(代码 + smoke/ 冒烟 + tests/ 单元集成)
|
||||
- `sim-data/` — 机器读数据产物;数据件不入 git(入库例外 = README+analyze.py+growth_model.py);MC 锚与读数常驻留盘,无数值调整禁重跑
|
||||
- `tools/` — python 工具脚本;`archive/` — 收口归档 zip(gitignored)
|
||||
- `tools/` — python 工具脚本;`archive/` — 批次完结归档 zip(gitignored)
|
||||
- `reports/` — 过程件容器:**需求卡片**(写卡段产出,实现批的输入合同)+迭代批产出物(实现报告/代拍板追认单/GDD 反哺点清单);**实现批唯一可写目录**,只放 .md,不是事实源(细则见 `reports/README.md`)
|
||||
- 根目录:`README.md`(纯指路牌:设计/流程/当前位/待办四个链接,零规则重述)、`roadmap.md`(里程碑+迭代总表六列+顶部「当前位」节=现在做什么)、`stage.txt`/`review-mode.txt`(运行开关);仓库根禁止再设会话状态文件(2026-10-18 用户裁,细则见 doc-conventions 第 10 节)
|
||||
- 根目录:`README.md`(纯指路牌:设计/流程/当前位/待办四个链接,零规则重述)、`roadmap.md`(里程碑+迭代总表六列+顶部「当前位」节=现在做什么)、`待办清单.md`(流水线唯一登记处,条目级待办与状态)、`体验验证清单.md`(需真人试玩才能下结论的验证项)、`stage.txt`/`review-mode.txt`(运行开关)、`playnote.md`(用户本人试玩随手记,只由用户增删,不属任何文档族);仓库根禁止再设会话状态文件(细则见 doc-conventions 第 10 节)
|
||||
|
||||
## 规范导航(动手前先查表)
|
||||
|
||||
@@ -49,28 +49,30 @@
|
||||
| 系统设计 / 增删文档 | `design/gdd/02-系统清单.md`——根索引 + 中文规范名↔英文代号对照 |
|
||||
| 架构(唯一事实源) | `design/engineering/architecture/architecture.md`——历史决策沿革查 git commit log,不另设决策文档 |
|
||||
| 术语与已锁数值 | 规范名与定义 = `design/gdd/术语表.md`(全库唯一对照表,仅 owner 文档可修订);现行数值 = `game/data/tables/*.tres`;规则与取值域 = 各系统 owner 文档。冲突须显式提出,禁默默改值 |
|
||||
| 现在做什么 / 待办与待裁 | 根 `roadmap.md` 顶部「当前位」节+迭代总表六列 · 待办唯一登记处 `design/gdd/06-待办清单.md` · 批次过程与结论查 git 提交信息与 `reports/` |
|
||||
| 现在做什么 / 待办与待裁 | 根 `roadmap.md` 顶部「当前位」节+迭代总表六列 · 待办唯一登记处 `待办清单.md` · 批次过程与结论查 git 提交信息与 `reports/` |
|
||||
|
||||
## 硬性红线(勿违反;操作细则在 process-conventions.md 与各规约)
|
||||
|
||||
- 主工作区禁止直接改动与提交——一切改动走独立 worktree(豁免:`.zcode/` 与 `.qoder/` 两份本机 harness 配置,实测均不入 git)
|
||||
- **仓库根禁止设会话状态文件**(2026-10-18 用户裁废除 `active.md`)——现状看 roadmap「当前位」,待办看 06,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
|
||||
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
|
||||
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
|
||||
- 交付前验收 = 跑 `tools/run_all_gates.sh` **按改动域**全绿(唯一入口、唯一闸清单、唯一域划分,禁在文档另列);合回 master 由 worktree-guard 按 diff 推域校验回执强制(process-conventions §3)
|
||||
- **两类批**——**迭代批**(代码实现,`/iter` 发射):**禁写 `design/` 整个目录**(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;产出物只准写 `reports/`;批末只跑代码域闸。**非迭代批**(工具/规则/文档改动):不占 roadmap 迭代号,按改动域跑闸。文档内容的落地走五段流水线相应段(文档设计段入口=doc-design 技能;登记条目格式由流水线闸强制)
|
||||
- 五段流水线铁律——一切工作按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点,流水线闸 `tools/pipeline_gate.py` 强制(工具并行开发中,注册进 run_all_gates.sh 文档域归第二批);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)
|
||||
- **两类批**——**迭代批**(代码实现,`/iter` 发射):**禁写 `design/` 整个目录与两份根流程清单(`待办清单.md`/`体验验证清单.md`)**(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;批末只跑代码域闸。**非迭代批**(工具/规则/文档改动):不占 roadmap 迭代号,按改动域跑闸。文档内容的落地走五段流水线相应段(文档设计段入口=doc-design 技能;登记条目格式由流水线闸强制)
|
||||
- 五段流水线铁律——一切工作按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点,流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)
|
||||
- MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起
|
||||
- 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6)
|
||||
- 变异验证——新增/改写验收函数必须变异变红才算验收成立;无变异红记录只写「已实现,拦截能力未证」(test-conventions §6)
|
||||
- 中间产物吸收即死——结论先吸收进权威文档、未决先迁 06,文件即删且永不提交(doc-conventions 第 11 节)
|
||||
- **验收闭合**——每个需求点与每个行为改动都要有从真实入口进入的验收断言,撤销该改动能让对应断言变红;禁删断言/放宽阈值换绿;合回前独立验收审计未过则禁止合并(细则=test-conventions §9+iter 技能第 5 步)
|
||||
- 规则评估——没在真实任务上评估过的规则修订,不算完成。
|
||||
- 中间产物吸收即死——结论先吸收进权威文档、未决先迁待办清单,文件即删且永不提交(doc-conventions 第 11 节)
|
||||
- **禁写变更记录**——文档与代码文件内一律禁设「版本历史/修订记录/变更记录/修改记录/更新记录/更新日志」节或表格,禁写「本版改了哪些条款」式逐版本叙述与 `# 2026-xx-xx 缺陷修批——` 式行内修订注记;沿革只进 git commit log。版本号可作文档现行身份标记留在文首状态行与正文引用中(doc-conventions 第 5 节,机检闸 gdd_lint R4 反转拦截)
|
||||
- 用语红线——一切对用户输出禁自造黑话,需要作者解释的词必须展开成完整白话句(doc-conventions 第 4 节三形态表)
|
||||
- 呈报六问——What 现状差异 / Why 动机与不做代价 / How 方案与全部备选 / Where 影响面 / 验收判据 / 不确定点单列收尾;Who/When 不进正文;仅首次描述需求/bug/方案适用
|
||||
- 真人检测只登记不阻塞——登记进 07 清单/实现报告即完成义务,禁以等目检为停工理由或收口前置
|
||||
- 文件头 = `##` 文档注释,介绍本模块,≤8 行两类白名单(①职责摘要 ②使用约束;禁一切文档反向链接;code-conventions §1.4.1,规范权威 = Google pyguide §3.8 + PEP 257;机器闸 tools/audit_file_headers.py)
|
||||
- 呈报四问(读后自检制)——向用户首次完整呈报一件事(需求/缺陷/方案/结论)时,写完先自查:读者只读这一段,能否不看原文答出——① 现状差在哪:现在是什么、缺什么;② 你建议做什么:做完预期什么效果、会动到哪些面;③ 为什么选这个做法:考虑过、放弃过哪些别的路;④ 做完怎么验证:哪些还有把握不足。哪问答不出就补哪问,禁靠加小标题凑数;三句话能说清的小件合并为三句:现状一句、建议与验证一句、没把握的一句;后续追问与推进不必重跑全框架
|
||||
- 真人检测只登记不阻塞——登记进体验验证清单/实现报告即完成义务,禁以等目检为停工理由或当作合回的前置条件
|
||||
- 文件头 = `##` 文档注释,介绍本模块是什么;禁写变更叙事(改动史查 git log);禁文档反向链接(code-conventions §1.4.1)
|
||||
- 数值单一事实源——玩法数值只存 `data/tables/`(.tres)经 ConfigDB 单点读取;禁数值字面量与自建 `load()` 表路径
|
||||
- RNG 单点——禁 `randomize()`/自建 RandomNumberGenerator,随机只经 `RngService` 统一流
|
||||
- 架构事实唯一源——架构事实只认 `design/engineering/architecture/architecture.md`;禁立第二架构文档或决策文档体系,历史决策记录只进 git commit log
|
||||
- Core 层零 UI 依赖——完整战斗必须可 `--headless --script` 跑通;fail-fast——数据校验错误一律致命中止,无软回退
|
||||
- spec-AC 一致性——规约与验收标准不得在同一输入上给出不同答案;评审隔离——`/design-review` 必须独立会话跑(与 `/design-system` 隔离)
|
||||
- spec-AC 一致性——规约与验收标准禁止在同一输入上给出不同答案;评审隔离——`/design-review` 必须独立会话跑(与 `/design-system` 隔离)
|
||||
- 像素结构件——先抄后造、验收写成像素级判据、±1px 坐标推导(process-conventions 第 7 节)
|
||||
@@ -9,4 +9,4 @@
|
||||
| 这个游戏设计成什么样 | [design/gdd/02-系统清单.md](design/gdd/02-系统清单.md)(GDD 根索引) |
|
||||
| 开发流程怎么走(五段流水线/批次纪律/验收闸) | [design/engineering/process-conventions.md](design/engineering/process-conventions.md)+[AGENTS.md](AGENTS.md) 规范导航表(每段一个技能入口) |
|
||||
| 现在做什么 | [roadmap.md](roadmap.md) 顶部「当前位」节 |
|
||||
| 待办与待裁 | [design/gdd/06-待办清单.md](design/gdd/06-待办清单.md) |
|
||||
| 待办与待裁 | [design/gdd/待办清单.md](待办清单.md) |
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **性质**:表现层升级批与战斗表现批的生产侧清单——把散在战斗表现 / HUD框架 / 关卡节点地图 / 敌人系统里的资产需求收拢成一张可执行的工单视图,**兼资产维度单一登记处**。**规范效力归各 owner 文档**(战斗表现、美术圣经、04-美术音频方向),本文只补「生产视角的需求规格」,冲突时以 owner 文档为准。
|
||||
> **建立**:2026-09-18 场景一(战斗画面呈现)讨论产物。**2026-09-22 GDD 过期账清偿批**:原 asset-checklist.md 并入本文后删除,本文自此为两份清单合并后的唯一清单;同批修正敌人键集/徽章数量/样张状态等过期事实(详见 art-bible 的 git 提交史)。**2026-09-22 §5 角色设计落地批**:剪影引用迁 art-bible §5.1、护甲区分方案落 §5.3(清偿两处待设计项)、提示词卡 v2.0 重生成(详见 art-bible 的 git 提交史)。**2026-09-23 复用盘点批**:立素材站复用渠道与复用闸(§0);新增 §3.4–3.6 三条此前漏登记的需求(增益状态效果图标 / 营地与元进度件 / 字体);各条目标注来源渠道。**2026-09-23 §5 抽离批**:角色设计内容迁独立文档 `character-design.md`(art-bible §5 自此为引用存根,小节号不变),本文 §5.x 剪影/护甲引用经存根仍可达;主角特征批(直剑→挖掘铲、加旗朱背旗)顺带,主角形象锚见 character-design.md §5.4。**2026-09-23 §6–9 补全批**:art-bible 四章成文(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考);本文 §0 交付格式与像素尺寸行、参考图行、§6 依赖链顺带改指 §8/§9 现行。
|
||||
> **更新纪律**:各条目「现状」行顺带更新(未开始 / 候选已出 / 已裁决 / 已交付);做完不删条目,改状态。与 06-待办清单的联动:本文是资产维度明细,06 只留指路存根。
|
||||
> **更新纪律**:各条目「现状」行顺带更新(未开始 / 候选已出 / 已裁决 / 已交付);做完不删条目,改状态。与待办清单的联动:本文是资产维度明细,待办清单只留指路存根。
|
||||
|
||||
## 0. 生产渠道、规格与风格宪法
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
**通用规格纪律**:
|
||||
|
||||
- 交付格式 PNG 透明底;动画用序列帧或精灵表(SpriteFrames),帧数规格随样张批定(候选档已成文 art-bible §8.4)。命名规范候选草案见 art-bible §8.3。
|
||||
- **分辨率档位已定案(2026-09-22 用户确认):1280×800 + 1920×1080 两档**,窗口不可调、格边长按档位计算,后续调整走修订;像素画布已定暂定基准(**art-bible §8.2**:主角原图与战斗状态图 48×72、棋盘小人 32×32 二头身〔用户暂定〕;敌种立绘三档 48/64/96 候选;1× 基准整数倍缩放纪律;1920 档缩放策略与帧数规格待样张批,06-待办清单已登记)。
|
||||
- **分辨率档位已定案(2026-09-22 用户确认):1280×800 + 1920×1080 两档**,窗口不可调、格边长按档位计算,后续调整走修订;像素画布已定暂定基准(**art-bible §8.2**:主角原图与战斗状态图 48×72、棋盘小人 32×32 二头身〔用户暂定〕;敌种立绘三档 48/64/96 候选;1× 基准整数倍缩放纪律;1920 档缩放策略与帧数规格待样张批,待办清单已登记)。
|
||||
- 程序化占位是现行结构冻结条款:资产未就位的槽位保持占位渲染,不空窗。
|
||||
- **复用闸(♻ 渠道四道验收,一道不过不入包)**:① 色板量化——改色对齐 art-bible §4.1 token,不保留原包杂色;② 吸色检查——无 HSV 色相 300°–345°(紫赤禁入带);③ 灰度/色弱可辨——去色相后仍可辨且不与语义件混淆;④ 授权干净——CC0 优先,CC-BY 署名记入设置/关于页,license 不明的一律不用。
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 美术圣经(Art Bible)— Dungeon Sweeper
|
||||
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归状态记录 125);现行为 v3.15,历次修订明细见 git 提交史
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归状态记录 125);现行为 v3.17,历次修订明细见 git 提交史
|
||||
>
|
||||
> **Art Director Sign-Off (AD-ART-BIBLE)**:历次批均 SKIPPED(v1.8 §6–9 补全亦 SKIPPED)— Lean/Solo mode(非 PHASE-GATE;§6–9 已成文,full 门禁签核时机待用户表态)
|
||||
> 风格基调(2026-09-10 用户定稿):地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身
|
||||
> 参考池(用户审阅选定,三者相融):**Into the Breach**(网格信息分层;雷图棋盘呈现基准 = 其战棋地块质感 × 正俯视角,见 §3.2)× **Moonlighter**(明亮像素地牢质感)× **Peglin**(明快全年龄地牢色彩);参考截图存于 `design/art/references/`(三款×2 张,与点名池一一对应;gungeon 两张经 2026-09-22 裁决退场;正面取/弃与反面条款已成文 §9,反面图证待素材生成后另批增补)
|
||||
> 约束(2026-09-10 用户强调):本圣经是**纯表现层文档**——明亮轻快是美术风格,不是玩法成分。圣经禁止引入、暗示或预设任何玩法语义,不改变任何 GDD 已锁机制;凡表现层需要玩法钩子才能表达之处(如完美排雷检测、Boss 预警),一律标注「待 GDD 定夺」,圣经只规定"若有,则如何呈现"。GDD 机制与圣经冲突时,机制语义以 GDD 为准,呈现方案以本圣经为准
|
||||
> **条款验收标注纪律(2026-10-06 P2-4 用户拍板,全文书写规范)**:条款尾部按三档标注验收方式——**`←机检: <断言>`**(脚本可自动判定)/ **`←目检: <判据>`**(机器测不了,但看什么、算过有明确判据)/ **`←方向性`**(气质与方向原则,不承担验收);**新条款一律按此书写**,存量口号型条款分章补标(约 55%,06 挂账「口号型条款机检化批」分章推进、每章交付抽验);数值类条款推荐「**待定现用**」态(现行即执法、修订走登记,释义见 §4.1)
|
||||
> 约束(2026-09-10 用户强调):本圣经是**纯表现层文档**——明亮轻快是美术风格,不是玩法成分。圣经禁止引入、暗示或预设任何玩法语义,不改变任何 GDD 已锁机制;凡表现层需要玩法钩子才能表达之处(如完美封印检测、Boss 预警),一律标注「待 GDD 定夺」,圣经只规定"若有,则如何呈现"。GDD 机制与圣经冲突时,机制语义以 GDD 为准,呈现方案以本圣经为准
|
||||
> **条款验收标注纪律(2026-10-06 P2-4 用户拍板,全文书写规范)**:条款尾部按三档标注验收方式——**`←机检: <断言>`**(脚本可自动判定)/ **`←目检: <判据>`**(机器测不了,但看什么、算过有明确判据)/ **`←方向性`**(气质与方向原则,不承担验收);**新条款一律按此书写**,存量口号型条款分章补标(约 55%,待办清单挂账「口号型条款机检化批」分章推进、每章交付抽验);数值类条款推荐「**待定现用**」态(现行即执法、修订走登记,释义见 §4.1)
|
||||
|
||||
---
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
**判定与回退**:第 1 项不过 → 按原则二设计测试回退——先调格底明度档,复杂格底垫色牌兜底,禁止缩字号换字形逃避;第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增图类型/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;《HUD框架》以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。
|
||||
|
||||
**整屏样张闸(2026-09-29 v3.0 新增)**:格子样张验"格",整屏样张验"屏"——战斗屏/关卡屏/菜单屏/营地屏各出一张整屏样张,验收四项:①全部文字(含菜单/说明/tooltip/按钮)对其直接背景 ≥4.5:1(WCAG AA;大字〔≥18px 或加粗档〕底线 3:1、目标 4.5:1);②明度分层三档带成立(信息面层 > 世界中层 > 世界底层,层间灰度可分);③整屏焦点预算(§3.4:首焦唯一——数字区/立绘/主按钮,无第二焦点竞争;负空间服务焦点,不属空窗);④文字禁直接压纹理底(垫容器或罩层)。闸形 = **方向稿目检先行**(防"机检过闸但气质否决"式返工),目检过后机检冻结。执行时机 = 屏级视觉落码开工前;过闸前一切屏级视觉为占位临时态(§7.1 完成条款)。现行 §1.4 格子样张机检断言集中涉色牌/明度档条目随样张 v3 重跑同步改造(登记 06-待办清单)。
|
||||
**整屏样张闸(2026-09-29 v3.0 新增)**:格子样张验"格",整屏样张验"屏"——战斗屏/关卡屏/菜单屏/营地屏各出一张整屏样张,验收四项:①全部文字(含菜单/说明/tooltip/按钮)对其直接背景 ≥4.5:1(WCAG AA;大字〔≥18px 或加粗档〕底线 3:1、目标 4.5:1);②明度分层三档带成立(信息面层 > 世界中层 > 世界底层,层间灰度可分);③整屏焦点预算(§3.4:首焦唯一——数字区/立绘/主按钮,无第二焦点竞争;负空间服务焦点,不属空窗);④文字禁直接压纹理底(垫容器或罩层)。闸形 = **方向稿目检先行**(防"机检过闸但气质否决"式返工),目检过后机检冻结。执行时机 = 屏级视觉落码开工前;过闸前一切屏级视觉为占位临时态(§7.1 完成条款)。现行 §1.4 格子样张机检断言集中涉色牌/明度档条目随样张 v3 重跑同步改造(登记待办清单)。
|
||||
|
||||
> **样张 v2 记录(2026-09-28,四冻结生效)**:机检 16/16 全绿(扩展断言集:A3 图底分离 Δ≥55/F6 墙格顶面立面双明度带 Δ≥18/F7 整板悬崖 DEPTH_PX 与游戏渲染常量同源/A6 观感密度梯度均值 ≥4.70=v1 实测 4.09×1.15)。**四冻结**——①砖面形态:方案甲 21 瓦片+高墙感 D 投影档+整板悬崖(§3.2 高墙瓦片体系条定稿参数);②色板:小人蓝 #2E86DE/接触带微光 #D6ECFF/类型微光五组定稿(§4.1/§4.2);③动画方案:§8.4 帧数档冻结;④数字形制:色牌形制定稿(#F5EAD3 素色直角小牌+Fusion Pixel+字号 max(18, ceil(格边×0.5))+墨三档,§7.4)。产物=`artwork/mockups/sample-batch-v2-2026-09-28.png`,机检报告=git 提交 f9b0765;气质终审=批末用户目检(闸形 A,2026-09-27 拍板)。**(v3.0 注:四冻结之①砖面形态的数字承载面与④数字形制随色牌降级解冻,待样张 v3 重冻;②色板③动画维持冻结不变)**
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
### 2.2 轮末自动战斗结算
|
||||
|
||||
- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;完美排雷(概念定义与兑付结构见《地图战斗资源》)的高光兑现——结算带高光行,以其为全场喝彩级的最高反馈档
|
||||
- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;完美封印(概念定义与兑付结构见《地图战斗资源》)的高光兑现——结算带高光行,以其为全场喝彩级的最高反馈档
|
||||
- **色彩性格**:情绪不走明度(§2 禁令),色彩转向金橙主导、饱和拉高;数字色转译为弹道色沿行推进
|
||||
- **氛围形容词**:酣畅、递进、一气呵成
|
||||
- **能量**:7/10,随连击爬升
|
||||
@@ -164,7 +164,7 @@
|
||||
- **装饰变体(未开格氛围层)**:覆尘完整砖之上叠加裂纹/碎石斑/物件级点缀等装饰;2026-09-29 v3.0 物件级解禁——物件级装饰(小石块/陶片/骨殖等)须走**专属装饰形状家族**(与信息元素形状语言隔离、灰度下不与任何逻辑元素同形),数量配额按图类型定(候选 3–5 种,样张 v3 冻结),一律残缺、内凹、低对比而自然后退(§3.4);零信息纪律(分布随机、与雷/安/类型零相关、灰度下禁止制造新分类特征、分布每板一次跨轮不复掷)以原则三为准,此处不赘。←原三
|
||||
- **结果整格表达(已开格)**:已开格按当格结果整格表达(结果清单见原则三);画法——产资源格的家族物件纹样用 §3.1 道具点剪影语言(直线几何体)角嵌于砖缘,砖心与数字字形占位区留白(已开格仍承载数字);危险格巢穴纹较已开格更凶一档,形状+颜色双通道不变。←原三;P3
|
||||
- **未开格预读通道(微光单通道)**:类型预读 = 类型微光单通道(色相+明度双码,色带 §4.2);色弱类型主通道 = 微光明度梯度(组粒度——组间互辨;§2 微光明度例外);格内零角嵌/箭头/加号类附加形(2026-09-28 用户裁「微光格只留微光」,退役史见 git 提交史)。←原一;原三
|
||||
- **旗(封印印记)**:插旗动作的叙事呈现 = **封印**(玩家可见专名,术语定义见 [术语表](../gdd/术语表.md)「封印」条;插旗动作面见《排雷玩法》)——旗 = 封印印记;封印色(§4.1,现行封印黑)与格内优先级栈位(§1.2)不变。封印特效(施印瞬间 + 印记常显形态)资产归清单 §4 演出类;旗印记纹理已落位、字形占位退役(迭代 9 卡⑬兑现;原 06 债务条目已完成移除)。←原一;P2;术语批 2026-09-25;封印紫批 2026-09-26;封印黑批 2026-09-28
|
||||
- **封印印记**:封印动作的叙事呈现 = **封印**(玩家可见专名,术语定义见 [术语表](../gdd/术语表.md)「封印」条;动作面见《排雷玩法》)——封印动作落在格上的印记称封印印记;封印色(§4.1,现行封印黑)与格内优先级栈位(§1.2)不变。封印特效(施印瞬间 + 印记常显形态)资产归清单 §4 演出类;封印印记纹理已落位(现行件=`seal_mark.png`,32×32 可选件)、字形占位退役(迭代 9 卡⑬已实现;原待办清单债务条目已完成移除)。←原一;P2;术语批 2026-09-25;封印紫批 2026-09-26;封印黑批 2026-09-28
|
||||
- **可挖域接触带微光(交互反馈层,2026-09-25 用户拍板)**:挖掘期,英雄格周界与可挖域(概念见《排雷玩法》动作合法性节;呈现义务见《HUD框架》可挖域指示)的接触面画呼吸微光——正交邻墙=该侧整条边,斜向邻墙=共享角点+向两侧各延伸半条边;光只画英雄格(空间)一侧、不进墙格(与装饰变体零冲突,数字砖心不涉);微光=空间的灯光边界(画法口径)。色相=蓝族浅一档(§4.2 蓝条;与主角蓝框的分层归样张批);呼吸=§2.1 挖掘期唯一在动元素,减动效停格。角点为斜墙与正交墙共享、不逐格归因,逐格指认=悬停金框(呈现交互归《HUD框架》悬停指示域)。←§2.1;§4.2;P1
|
||||
- **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰禁止伪装逻辑差异。←原三
|
||||
- **高墙瓦片体系(2026-09-26 定稿,样张批追认)**:未挖格=**一整块 32×32 自含凸起岩体**(方案甲核心定义);素材语义名——未开格素材=**墙瓦片**,已开格素材=**地面瓦片**(术语 2026-09-26 用户立)。**边界=墨线纪律**:墙瓦片与相邻格分界一律 1px 深墨线、直线贴格边、零锯齿;墨线职责=分隔,不追求与地面融合(Tiny Dungeon 样图实证调研后否决出局——TD 拼块套不进方案甲自含岩体定义,2026-09-25 终局维持方案甲;记录=git 提交 70d2320)。**光照=单一北受光**:北缘=墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘=墨线后直进纹理、不加修饰;南缘=墨线+立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。**凹角**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出(v9.1/v9.2 两版返工实证)。现行定稿=`game/assets/art/board/terrain_*`(16 唯一墙瓦片+5 地面瓦片变体;词表与查表逻辑见 `高墙瓦片词表(已落码 board_world.gd 查表,文件已退役)`)。**高墙感 D 投影档+整板悬崖(样张 v2 定稿 2026-09-28,用户 2026-09-27 四变体挑档)**:南临空墙格=立面口沿高光 2px `Color8(196,172,132)`+双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);北邻墙地面格=三段渐隐投影(顶带 0.22 格边,α0.28 起);整板悬崖=板底/右侧 `DEPTH_PX 8px` 暗面+列分隔竖缝+岩层横纹+口沿高光+接触 AO+底线收边+悬浮影(游戏 `board_world.gd` 同款结构件,样张脚本与游戏渲染常量同源——机检 F7 承载)。←原三;状态记录 164;样张 v2 批 2026-09-28
|
||||
@@ -460,7 +460,7 @@
|
||||
|
||||
**(三)氛围层平滑件与抖动派并存(迭代 26 反哺④——Q5 已裁保平滑)**
|
||||
|
||||
- vignette / gradient_base / beams 三件程序化平滑渐变件**保平滑连续 alpha**(2026-10-13 grilling Q5 裁),与卡顶类型色带 DitherBand 的 Bayer 有序抖动派**并存不一致**——两派统一裁决留**整屏样张批**(§1.4;战斗一屏样张 P0 未开工,06 在册),本节只登记并存事实与裁决去向,不提前统一。
|
||||
- vignette / gradient_base / beams 三件程序化平滑渐变件**保平滑连续 alpha**(2026-10-13 grilling Q5 裁),与卡顶类型色带 DitherBand 的 Bayer 有序抖动派**并存不一致**——两派统一裁决留**整屏样张批**(§1.4;战斗一屏样张 P0 未开工,待办清单在册),本节只登记并存事实与裁决去向,不提前统一。
|
||||
|
||||
> **主题色值双源注记(迭代 25 反哺④)**:主题色值现存两处——`main_theme.tres` 字面量与 ui_kit 常量(卡片立项时已知代价,README 溯源表缓解)。长期收敛候选 = 程序化 Theme 生成回代码单点,属重构面、未排期;新增色值仍按现状双落并同批登 README 溯源表。
|
||||
|
||||
@@ -470,12 +470,12 @@
|
||||
|
||||
> **白话**:这一章是给所有出图者(人与脚本)的技术底线——多大的画布、什么格式、怎么命名。屏级格边长定值的裁决权在 battle.md;本节锁资产侧:分辨率档位、像素基准与缩放纪律、导出与命名、帧数规格。生产工单视图 = art-assets-checklist。
|
||||
|
||||
> **定值单点纪律(2026-10-06 审计 §07-1 落规)**:全文数字定值只落在**两张表**——**§4.1 主色板**(全部色值+明度/饱和边界)与 **§8.2**(画布/体量/立绘源档/圆角率/缩放);其余章节一律**指路、禁复述数字**(修订改值只动单点处);character-design §5.5 为档位**语义**表(普通/精英/Boss + 例外),数值权威 = §8.2;存量双写复述随整备批(06 挂账)逐步收编。
|
||||
> **定值单点纪律(2026-10-06 审计 §07-1 落规)**:全文数字定值只落在**两张表**——**§4.1 主色板**(全部色值+明度/饱和边界)与 **§8.2**(画布/体量/立绘源档/圆角率/缩放);其余章节一律**指路、禁复述数字**(修订改值只动单点处);character-design §5.5 为档位**语义**表(普通/精英/Boss + 例外),数值权威 = §8.2;存量双写复述随整备批(待办清单挂账)逐步收编。
|
||||
|
||||
### 8.1 分辨率档位与格边长口径
|
||||
|
||||
- **两档定案**:1280×800(基座)+ 1920×1080,窗口不可调;后续调整走修订。
|
||||
- **格边长口径**:按档位与板形计算——固定档位下格子量增加则格边长相应变小;现行 1280×800 基座锚点值 = 45px(10×10 板)/36px(12×12 板),扩板(13×13/14×14)换算值未定稿(1920 档已裁决全局等比缩放——迭代 9 卡⑮落码;原 06 分辨率债务条目已完成移除);装板可行性校验规则见《HUD框架》。
|
||||
- **格边长口径**:按档位与板形计算——固定档位下格子量增加则格边长相应变小;现行 1280×800 基座锚点值 = 45px(10×10 板)/36px(12×12 板),扩板(13×13/14×14)换算值未定稿(1920 档已裁决全局等比缩放——迭代 9 卡⑮落码;原待办清单分辨率债务条目已完成移除);装板可行性校验规则见《HUD框架》。
|
||||
|
||||
### 8.2 像素基准与画布
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|---|---|---|
|
||||
| `character/` | 角色棋盘帧 + B 区立绘 | character_hero_idle/walk(棋盘 32×32 朝左基准)、character_hero_portrait(B 区立绘 46×48)+ character_enemy_<键> 八敌 |
|
||||
| `fx/` | 特效帧 | shovel_swing_f0/f1/f2(挥铲三帧,握把锚 (18,12)) |
|
||||
| `board/` | 砖面/旗印记(三关换皮 + 封印旗;2026-10-06 对账更正——旧 `board_floor/wall/pillar_dungeon_*` 命名退役) | terrain_floor_<board_type>_<n>.png(三关地面系)、flag_seal.png |
|
||||
| `board/` | 砖面/封印印记(三关换皮 + 封印印记件;2026-10-06 对账更正——旧 `board_floor/wall/pillar_dungeon_*` 命名退役) | terrain_floor_<board_type>_<n>.png(三关地面系)、seal_mark.png |
|
||||
|
||||
## 4. 坐标/朝向等工程侧约定备查
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
硬规则:**所有角色自带轮廓通道**——亮色描边(暖白族,禁黑描边)+落影底盘,识别不依赖所在背景。敌人不占格,剪影禁止借格底色成立;同一套剪影因此在棋盘、结算画面与任一战斗舞台通用,不为战斗表现未决的舞台问题付重画成本。←已锁事实;「禁黑描边」= 2026-09-20 提示词卡实践顺带固化
|
||||
|
||||
**主角描边例外(2026-10-06 待决项 B 用户拍板——显式例外,非漂移)**:主角四态硬描边 = **深褐 `(40,19,13)`**,不入暖白族——深描边在浅砖面格底上对比更高、主角更突出;暖白达标率 0–28% 属例外内允许(敌人仍暖白族,实测达标 27–69%,不变)。素材不随本条修改;样张批(圣经 §1.4)终审复核例外成立性,复核义务登记 06 待办。
|
||||
**主角描边例外(2026-10-06 待决项 B 用户拍板——显式例外,非漂移)**:主角四态硬描边 = **深褐 `(40,19,13)`**,不入暖白族——深描边在浅砖面格底上对比更高、主角更突出;暖白达标率 0–28% 属例外内允许(敌人仍暖白族,实测达标 27–69%,不变)。素材不随本条修改;样张批(圣经 §1.4)终审复核例外成立性,复核义务登记待办清单。
|
||||
|
||||
四类角色 archetype 以高宽比/轮廓凹凸数/锐角数一眼区分(道具点——唯一非生物剪影——留在圣经 §3.1):
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## 文档状态
|
||||
|
||||
- 版本:1.8
|
||||
- 最近更新:2026-10-17(迭代 27/28 GDD 反哺点写入——新增「呈现层装配与会话生命周期」节(视觉层装配合同/三屏壳/RunScope 生命周期与存活性矩阵/ModalFlow tags 协议)、战斗域现状补结算五段与 RunLedger 四账、初始化顺序补启动后置条件六条,均 2026-10-17 用户裁按推荐案写入;实现实证行读数换代)
|
||||
- 版本:1.9
|
||||
- 最近更新:2026-10-21(迭代 32 卡②反哺点写入——effect_engine 行与战斗域装配节改无状态化 ctx 形态、架构原则新增「系统无状态」条;2026-10-21 用户裁)
|
||||
- 引擎:Godot 4.7.2-stable(GDScript 单语言;2026-09-07 锁定)
|
||||
- 评审模式:solo(`review-mode.txt`,2026-09-12 用户拍板——TD/LP 门禁按 solo 跳过,用户授权架构产出自主推进、迭代期回调)
|
||||
- 覆盖 GDD:现行全集——`design/gdd/` 顶层 00–08 共 9 件 + `systems/` 五域 25 件(排雷循环 4 / 战斗循环 5 / 冒险循环 5 / 跨局循环 5 / 表现与支撑 6)+ `ux/` 8 件;原英文命名 12 份系统 GDD 已全部迁入中文域目录,原「未设计」预留(A5 扫雷秘宝 / C1–C4 / B5 / D3–D6)均已立项成文
|
||||
@@ -112,8 +112,8 @@
|
||||
|
||||
| 模块 | 独占职责 | 对外暴露 | 消费依赖 | 引擎 API |
|
||||
|---|---|---|---|---|
|
||||
| **battle_orchestrator**(B1) | 段序骨架(七段结构冻结)、轮 facts 缓冲、可回放步骤流、[明细行协议](../../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(15 类行 + 字段分层)、条款③⑤闭合对账、**战斗域全部跨系统调用权**、效果引擎宿主(每战 rebuild/战末 adventure 计数器导出回写)+ 树规则挂点施加面(posture 窗/sense 限次/守侧 flag_missed 发射/聚乘算入账前) | `begin_battle(encounter)`;窗口/刷新意图提交面(扮演 D1);`level_clear()`;步骤流(输出契约) | B2/B3/B4 全调用面(**唯一调用方**);A3 request_refresh 代理 + 回发;A2 install/retire + 三事件订阅;A1 generate(attempt 非 scope 事件);D2 SettlementTable + set_reload_gate | 无(零随机、禁 await——切片 88 冒烟实证可回放) |
|
||||
| **effect_engine**(核心层——内容数据驱动扩展批 iter17 建域) | 事件/事实/效果类型/算子四词表封闭(迭代 21 T3 扩签:EVENTS 18→19 +flag_missed 标错判定、FACTS +battle_index 关卡域战斗序)+ 树规则合成订阅六键(posture 每关首轮窗/sense 两组限次 counter 闸·domain=adventure 跨战斗持久) | rebuild(每战重建)/dispatch/validate_trigger_row/counters_export·counters_seed | ConfigDB(feats/treasure 行集) | 无(零随机全确定) |
|
||||
| **battle_orchestrator**(B1) | 段序骨架(七段结构冻结)、轮 facts 缓冲、可回放步骤流、[明细行协议](../../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(15 类行 + 字段分层)、条款③⑤闭合对账、**战斗域全部跨系统调用权**、效果引擎宿主(每战 rebuild;round/battle 桶为编排器自有字典、经 EffectContext 引用挂载)+ 树规则挂点施加面(posture 窗/sense 限次/守侧 seal_missed 发射/聚乘算入账前) | `begin_battle(encounter)`;窗口/刷新意图提交面(扮演 D1);`level_clear()`;步骤流(输出契约) | B2/B3/B4 全调用面(**唯一调用方**);A3 request_refresh 代理 + 回发;A2 install/retire + 三事件订阅;A1 generate(attempt 非 scope 事件);D2 SettlementTable + set_reload_gate | 无(零随机、禁 await——切片 88 冒烟实证可回放) |
|
||||
| **effect_engine**(核心层——内容数据驱动扩展批 iter17 建域) | 零运行时可变状态的系统(见「架构原则」条 8):事件/事实/效果类型/算子四词表封闭(迭代 21 T3 扩签:EVENTS 18→19 +seal_missed 标错判定、FACTS +battle_index 关卡域战斗序)+ 树规则合成订阅六键(posture 每关首轮窗/sense 两组限次 counter 闸·adv_ 前缀走冒险桶跨战斗持久) | rebuild(每战重建订阅表)/`dispatch(ctx, event, payload, facts)`/`validate_trigger_row` + 静态计数口 `counter`·`counter_add`·`counter_spend`·`bucket_of`·`counter_id_legal`(计数器读写只经 EffectContext 引用束,无导出/灌入接口) | ConfigDB(feats/treasure 行集)、EffectContext(四桶引用由宿主持有:round/battle 桶归编排器、level 桶归关卡域、adventure 桶归 RunLedger) | 无(零随机全确定) |
|
||||
| **level_runner**(C1-lite,切片已建) | 关卡域生命周期(节点链推进、共享 B4 + HP 携带、关卡末 level_clear) | 节点推进接口;LevelTable(`level.tres`——节点 enum {battle,reward,rest,boss}、末节点必须 Boss、ConfigDB 跨表校验⑧) | B1 begin_battle、B4 grant_node_active、ConfigDB LevelTable | 无 |
|
||||
| **run_structure**(C2) | 冒险/关卡序列、失败重开、**master_seed 唯一接线方** | (契约规范源:`../../gdd/systems/冒险循环/冒险结构.md`) | RngService.set_master_seed、C3/D2 | 无 |
|
||||
| **difficulty**(C3) | DifficultyTable 回填、全部曲线数值调参所有权、战斗期望表(切片 monte_carlo 为工具原型) | 调参输出(经 D2 R1 解析/编排器传递,禁反向调用生成) | ConfigDB、B1 对账输出(调参约束清单)、A1 报告 C | 无 |
|
||||
@@ -122,7 +122,7 @@
|
||||
### 战斗域装配现状(迭代 27——2026-10-17 用户裁写入)
|
||||
|
||||
- **结算五段**:B1 编排器的每轮结算序列拆为 `game/core/settlement/` 五个段类——GrantSegment(①授予段,含封印判定与 counter/grant 助手单点)→ AuraSegment(②增益/减益段)→ CombatSegment(③战斗段)→ DecaySegment(④衰减段)→ VerdictSegment(⑤判定段,返回裁定,终局早退语义留 B1)。段序骨架冻结于 `SettlementTable.segment_order` 语义,B1 退为段序编排+接线;段类可直接 `new()` 单测(`settle_segments_test` 在役)。
|
||||
- **RunLedger 四账**:`game/core/run_ledger.gd` 单类持四本共享账——同调充能账/真视使用计数/冒险域计数器/统一冒险加算区。所有权:AdventureRunner 开局构造、全程唯一宿主;LevelRunner 经 `set_run_ledger` 下传装配 encounter;战斗侧(B1)经 `encounter["ledger"]` 拿引用、只走 read/append 原语族。审计 P1-4「共享可变字典跨三层三处可写」由「函数签名上不可见的共享引用」换成签名可见的显式类,时点语义(战末导出/开局回灌)逐字不变。
|
||||
- **RunLedger 四账**:`game/core/run_ledger.gd` 单类持四本共享账——同调充能账/真视使用计数/冒险域计数器/统一冒险加算区。所有权:AdventureRunner 开局构造、全程唯一宿主;LevelRunner 经 `set_run_ledger` 下传装配 encounter;战斗侧(B1)经 `encounter["ledger"]` 拿引用、只走 read/append 原语族。审计 P1-4「共享可变字典跨三层三处可写」由「函数签名上不可见的共享引用」换成签名可见的显式类。冒险桶计数器无导出/灌入时点:EffectContext 持 `run_ledger.adventure_counters` 的同一字典引用,引擎静态计数口就地直写,跨战持久随 RunLedger 生灭(2026-10-21 迭代 32 卡②无状态化写入)。
|
||||
|
||||
### 呈现层
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
| 域 | 闸 | 本批改动落在这些路径即属该域 |
|
||||
|---|---|---|
|
||||
| **代码域** | run_smoke、run_all、probe_gate `--click`、probe_gate `--full`、render_gate、static_scan、回放确定性对拍(条件件) | **唯一触发=`game/`**(游戏工程代码、测试、探针、场景、数据表)。2026-10-18 用户裁「跨领域执法禁绝」(提交 `7bdf53e3`)后,原 `tools/`、`artwork/` 两前缀不再把改动拉进代码域 |
|
||||
| **文档域** | gdd_lint、doc_link_audit、doc_term_audit、流水线闸 | 一切非 `game/` 的路径:`design/`、根规则文件(`AGENTS.md`/`roadmap.md`)、`tools/` 工具脚本、`.zcode/` 配置、`sim-data/`、`reports/` |
|
||||
| **文档域** | gdd_lint、doc_link_audit、doc_term_audit、流水线闸 | 一切非 `game/` 的路径:`design/`、根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)、`tools/` 工具脚本、`.zcode/` 配置、`sim-data/`、`reports/` |
|
||||
| **两域都含** | repo_structure_audit(熵增闸:顶层目录/根文件/图像白名单/退役路径/design 子目录五查——文档批新增或挪动 `.md` 同样会绊它;纯文本扫描秒级) | — |
|
||||
|
||||
口径三条:**域划分的唯一出处是 `tools/run_all_gates.sh`**(`--code`/`--docs`/不带参数=全量),本表是它的现状登记、不是第二份清单;改闸件本身或一次改动跨两域 → 跑全量档(§6.2 共享件例外);合回闸按 merge diff 路径推出应验的域,再校验回执的 `domain` 字段与新增红。代码域跑前先过**素材齐盘预检**——`game/assets/art/**` 与一切 `*.import` 边车都不入 git(2026-09-29 裁定),worktree 冷检出必空盘,空盘态冒烟 V1 成片假红、渲染闸更是一段 golden 都不比。
|
||||
@@ -206,7 +206,7 @@
|
||||
| 硬闸 | run_smoke | run_all | probe_gate | render_gate | static_scan |
|
||||
|---|---|---|---|---|---|
|
||||
| **退出码优先** | 已落实(闸①) | 已落实(套件 manifest expect 协议) | 已落实(`verdict` 函数逐段判退出码,PASS 串不作判据) | 已落实(段级退出码 + 取证双检) | 已落实(fail 聚合退出) |
|
||||
| **SCRIPT ERROR 扫描** | 已落实(闸③,外部扫描——GDScript 无脚本错误陷阱 API,闸设在外部包装) | 已落实(入口内扫子套件输出;子进程的 stdout/stderr 须先落盘——`OS.execute` 管道在本机恒空) | 已落实(逐探针段 `tools/error_scan.py`,标记与 run_smoke 同源) | **缺失**(本批未收——判定主体是 golden 容差与取证存在性;登记 06) | 不适用(静态域无运行时错误面) |
|
||||
| **SCRIPT ERROR 扫描** | 已落实(闸③,外部扫描——GDScript 无脚本错误陷阱 API,闸设在外部包装) | 已落实(入口内扫子套件输出;子进程的 stdout/stderr 须先落盘——`OS.execute` 管道在本机恒空) | 已落实(逐探针段 `tools/error_scan.py`,标记与 run_smoke 同源) | **缺失**(本批未收——判定主体是 golden 容差与取证存在性;登记待办清单) | 不适用(静态域无运行时错误面) |
|
||||
| **断言数下限** | 无独立下限(闸②摘要行必须在案,防「跑一半仍 PASS」;脚本错误吞断言路径由闸③兜住) | 已落实(`Kit.finish` 零断言返回专用退出码 2——与「断言失败=1」区分,防致命类套件的 expect 协议放过) | 已落实(退出码 0 但无该探针签名标记亦判红——防静默退出) | 不适用(golden 容差 + 取证存在性判定) | 不适用(逐规则命中判定;另有扫描面为空即判红的零命中自检) |
|
||||
|
||||
背景事实(run_smoke 头注在案):GDScript 运行时脚本错误不改进程退出码、不计入冒烟失败数、且静默中断所在函数剩余断言——2026-09-19 状态记录 83 实证「309 项 0 失败」实为断言未跑全。这是 SCRIPT ERROR 扫描必须存在于判定层的原因。
|
||||
@@ -350,6 +350,7 @@ CJK 字体随包发行并设为 theme 默认字体——运行时默认字体无
|
||||
5. **fail-fast + 零特判**——数据错误一律致命中止(错误数值上线比崩溃更伤);API 非法 = 拒绝 + 诊断不静默;变异零运行时分支(行为差异全部由生成端约束保证)。
|
||||
6. **依赖单向 + 组合根模式**——层间单向依赖、唯一回边 = A2 事件订阅面;核心写调用唯一发生在组合根;禁上帝对象(切片 1100 行 main.gd 反面案例 → 9 单元拆分)。
|
||||
7. **呈现层契约成文**——面向呈现的信号载荷必须有成文 schema;模态覆盖层必须有"打开时遮挡物隐藏"场景级断言;场景级 UI 探针测试为标配(切片缺陷 ①②④⑤ 教训沉淀)。
|
||||
8. **系统无状态**——效果引擎这类系统零运行时可变状态:数据由宿主对象持有并随宿主生灭,状态按参数传入系统,系统只做判定(立项判据=2026-10-19 用户会话裁定;落地=迭代 32 卡②,2026-10-21 用户裁写入)。系统实例不持计数器——计数器住桶(round/battle/level/adventure 四桶,EffectContext 引用束),桶住宿主。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -21,17 +21,13 @@
|
||||
### 1.4 命名 = 领域命名,禁止系统 ID 前缀
|
||||
文件名与类名禁止 `a1_`/`b1_`/`c1_` 等系统 ID 前缀(`a1_board_generator.gd` → `board_generator.gd`)。设计追溯不靠前缀:代码↔设计的追溯由 GDD 文档承担,系统 ID 只作为文档的追溯锚;文件头注不承担追溯职责,不写任何文档链接(§1.4.1)。豁免:D2 schema 类与 autoload;文档/夹具登记名(如 b4_golden)属文档层。
|
||||
|
||||
### 1.4.1 文件头 = `##` 文档注释,介绍本模块,≤8 行,两类白名单制
|
||||
### 1.4.1 文件头 = `##` 文档注释,介绍本模块
|
||||
|
||||
头注的存在性与内容范围以 Python 规范为权威(GDScript 语法近 Python,官方风格指南对文件头零条款,不自造标准):Google Python Style Guide §3.8.2 明文要求——"Files should start with a docstring describing the contents and usage of the module"(文件应以描述模块内容与用法的 docstring 开头);PEP 257——模块一般都应有 docstring,摘要压一行。
|
||||
头注以 Python 规范为权威(GDScript 语法近 Python,官方风格指南对文件头零条款,不自造标准):Google Python Style Guide §3.8.2——文件应以描述模块内容与用法的 docstring 开头;PEP 257——模块一般都应有 docstring,摘要压一行。载体只用 `##` 文档注释(Godot 官方载体,进编辑器 F1 帮助),头注以说清模块为度,长度不设硬限。
|
||||
|
||||
载体与上限:只用 `##` 文档注释(Godot 官方载体,进编辑器 F1 帮助),总行数 ≤8(不含空行,从文件首个非空行到首个非注释代码行)。
|
||||
黑名单(出现即违规):日期与变更叙事——改动史查 `git log`/`git blame`,变更背景读迭代实现报告;与其他文档或其他文件头重复的内容——零新信息不写;一切文档反向链接——「实现 GDD:xxx」「指路:架构见 xxx」一类全部废止,代码↔设计追溯由 GDD 文档承担。
|
||||
|
||||
白名单(只许两类):①职责摘要——这是什么,一句话;项目语境(垂直切片状态/验证目标)以括号并入本行。②使用约束——使用本文件前必须知道的禁令与边界,每条一行。
|
||||
|
||||
黑名单(出现即违规):日期与变更叙事——改动史查 `git log`/`git blame`,变更背景读迭代实现报告;与其他文档或其他文件头重复的内容——零新信息不写(pyguide §3.8.2.1);场景结构、生命周期等代码机制描述——"Never describe the code"(pyguide §3.8.5);一切文档反向链接——「实现 GDD:xxx」「指路:架构见 xxx」一类全部废止,代码↔设计追溯由 GDD 文档承担。
|
||||
|
||||
机器闸:`tools/audit_file_headers.py`(static_scan S10:行数上限+黑名单机械可判子集)。存量头注按白名单清偿(约 90 文件,清偿批进行中);清偿落地后闸增补「头注必须 `##` 开头」锁死载体;此间新增/改动的文件头必须即合规。
|
||||
机器闸:`tools/audit_file_headers.py`(static_scan S10:变更叙事黑名单机械可判子集)。行数上限与类别白名单已停用。
|
||||
|
||||
### 1.5 禁止事实约定
|
||||
任何"大家默认这么做"的惯例必须成文进本文件(或 AGENTS.md)才算存在。写代码时发现未成文但必须遵守的约定 → 当场补条文,不许靠惯性延续。
|
||||
@@ -41,7 +37,7 @@
|
||||
## 2. 契约纪律——跨层/跨类调用的语义
|
||||
|
||||
### 2.1 信号与事件契约成文
|
||||
每个跨类信号:参数语义、发送时点、消费方依赖,必须在发送方 `##` 文档注释成文,每信号一行;计入 §1.4.1 的 8 行上限,装不下时迁契约文档。状态机的每次迁移路径都必须发出对应事件——**缺一条路径的事件 = 消费方永久等待 = 软锁**。
|
||||
每个跨类信号:参数语义、发送时点、消费方依赖,必须在发送方 `##` 文档注释成文,每信号一行;装不下时迁契约文档。状态机的每次迁移路径都必须发出对应事件——**缺一条路径的事件 = 消费方永久等待 = 软锁**。
|
||||
|
||||
### 2.2 跨层标识符语义单一且成文
|
||||
位置(index)/ 句柄(handle)/ 键(key)是三种不同的东西。跨类传递标识符时,传递方的语义必须与接收方契约逐字一致,禁止凭"当前恰好相等"传值。
|
||||
@@ -68,7 +64,7 @@
|
||||
4. **lambda 捕获循环变量按创建时值**——循环内先落地局部变量再捕获,或用 `bind`。
|
||||
5. **重命名脚本必须同步 `.uid` 文件**;新增 `class_name` 后必须跑一次编辑器扫描刷新全局类缓存,否则 `--headless --script` 模式解析失败。
|
||||
6. **`print` 格式占位符与实参类型必须匹配**(`%d` 配 String = 静默输出字面量)。
|
||||
7. **语言纪律已机器化为 S9 类型闸**(迭代 27——2026-10-17 用户裁写入)——`tools/audit_type_hints.py`,执行载体=静态扫描域 S9(`game/tests/static_scan.sh`),退出码 0=归零。扫描面=产品码(`game/core`+`game/data`+`game/presentation`;smoke/tests 豁免——测量面容器直构是既有惯例)。三规则:R1 无类型容器(显式非参数化 `Dictionary`/`Array` 注解)/R2 有值返回缺返回类型(纯副作用缺 `-> void` 属写法面不入闸)/R3 参数缺类型(`_init` 豁免——构造注入面形态自由)。
|
||||
7. **语言纪律已机器化为 S9 类型闸**——`tools/audit_type_hints.py`,执行载体=静态扫描域 S9(`game/tests/static_scan.sh`),退出码 0=归零。扫描面=产品码(`game/core`+`game/data`+`game/presentation`;smoke/tests 豁免——测量面容器直构是既有惯例)。三规则:R1 无类型容器(显式非参数化 `Dictionary`/`Array` 注解)/R2 有值返回缺返回类型(纯副作用缺 `-> void` 属写法面不入闸)/R3 参数缺类型(`_init` 豁免——构造注入面形态自由)。
|
||||
8. **`:= {}` 推断形态不算债**——`var x := {}` 类赋值推断属类型推断合法面,不入 R1 扫描;只有显式写的非参数化容器注解才算无类型容器。
|
||||
9. **行级白名单通道 `# type-hint: ignore`**——豁免该行不入 S9;豁免理由必须随行注明(现行例:`board_generator` 四函数的 db 参鸭子型——typed 参拒冒烟夹具 `_BoardRowStub` 注入)。
|
||||
10. **`# rng-seed-exempt` 标记豁免通道**——S1/S2 随机函数扫描对该行豁免;限定用途=非玩法 RNG 消费面的引擎熵(现行例:组合根种子编辑框空态取 `randi()`——建种子不进玩法封闭流)。
|
||||
@@ -96,5 +92,5 @@
|
||||
## 6. 流程纪律
|
||||
|
||||
1. **大改动三段走**:设计稿/伪代码 → 用户审阅 → 实现;实现禁止先于审阅。改动明细只进 git 提交信息,README 与代码内不写修改记录(doc-conventions 第 5 节「禁写变更记录」)。
|
||||
2. **实现批完成后的登记义务**:06 条目按 doc-conventions §12.1 推进状态,批次过程与结论进 git 提交信息与 `reports/`(2026-10-18 用户裁:根目录会话状态文件已废除,禁止再在其中登记条目)。
|
||||
2. **实现批完成后的登记义务**:待办清单条目按 doc-conventions §12.1 推进状态,批次过程与结论进 git 提交信息与 `reports/`(根目录会话状态文件已废除,禁止再在其中登记条目)。
|
||||
3. **呈现层缺陷的教训回写 D1 GDD 设计输入**(信号载荷 schema / 场景级测试 / 时点状态可见性)。
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
**切分正例**(现行排雷循环即按此切分):棋盘是地图级对象,包含多个格;格是成员对象,危险格/安全格是它的枚举属性;格上可能挂的奖励或惩罚(格面资源)是独立对象,本体与规则归地图战斗资源与战斗结算,格只保留"有无资源资格"一个挂载位。反面基准:把奖励的类型、数值、极性全摊开写成格子的属性字段——奖励规则一改,棋盘文档就得跟着改,这便是没封装。
|
||||
|
||||
**无权威文档,不下定义**:一个概念还没有权威文档时,任何其他文档都禁止替它下定义——只能引用概念名,并在 `design/gdd/06-待办清单.md` 登记「待落位」;谁先立权威文档,谁拿走全部定义权。(反例:某概念长期无权威文档,art-bible 复述、06 登记、需求卡片摘要三处各自给出口径,范围限定词在层层复述中丢失,最终放大成「全板常显」的错误事实。)
|
||||
**无权威文档,不下定义**:一个概念还没有权威文档时,任何其他文档都禁止替它下定义——只能引用概念名,并在 `待办清单.md` 登记「待落位」;谁先立权威文档,谁拿走全部定义权。(反例:某概念长期无权威文档,art-bible 复述、待办清单登记、需求卡片摘要三处各自给出口径,范围限定词在层层复述中丢失,最终放大成「全板常显」的错误事实。)
|
||||
|
||||
**与引用纪律的关系**:第 1 节管单个概念的边界与归属(概念级封装),第 2 节管概念之间、文档之间的引用(跨文档解耦)——先有封装,第 2 节的单点定义、反向边与删除 SOP 才有干净的作用对象。
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
| 依赖逐条列出 | 系统文档的「依赖与输出」节逐条列出本系统依赖的他人概念(规范名 + 链接)——这份清单就是删除概念时的影响面清单 |
|
||||
| 引用可查 | 需要某概念的影响面时,全仓搜索它的规范名即得;被引方在自己的「依赖与输出」节写明谁依赖它。人工维护的反向边清单已废止(2026-10-19 核出旧登记漏 7 份、多 1 份) |
|
||||
| 链接断了也好读 | 引用处的句子在链接失效后仍通顺完整(写"变异规则见《地形变异》",不把半截语义挂在链接里) |
|
||||
| 待办单点登记 | 全项目待办事项单点登记于 `design/gdd/06-待办清单.md`:系统文档的「待办」节只留指路存根,禁止自建条目表;新增待办条目直接登记清单,裁决后改「状态」列留痕、不删条目 |
|
||||
| 待办单点登记 | 全项目待办事项单点登记于 `待办清单.md`:系统文档的「待办」节只留指路存根,禁止自建条目表;新增待办条目直接登记清单,裁决后改「状态」列留痕、不删条目 |
|
||||
|
||||
**引用格式**:一律 Markdown 相对链接,指向文件、必要时带节锚点(例:`[地形变异 · 变异类型](systems/排雷循环/地形变异.md#变异类型)`)——机械可查,死链可被工具检出。
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
**读者测试是每个小节的验收标准**:只读这一节,回答三个问题——这节管的是谁/什么?在什么情况下?会发生什么?答不出来,就重写这一节,而不是给看不懂的地方补注释。
|
||||
|
||||
旧体系 lean 格式六条硬规则已停用(其解读对象 `design/archive/` 旧档已删除,现无适用文档;原表格占比 30% 口径随之作废)。`tools/gdd_lint.py` 现为现行文档机检三闸之一(gdd_lint / doc_link_audit / doc_term_audit,分级周期口径见 AGENTS.md);可读性仍是第一内容质量门,lint 是格式机检面,两者并行。
|
||||
`tools/gdd_lint.py` 现为现行文档机检三闸之一(gdd_lint / doc_link_audit / doc_term_audit,分级周期口径见 AGENTS.md);可读性是第一内容质量门,lint 是格式机检面,两者并行。
|
||||
|
||||
## 2. 白话写作规则
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
|
||||
**通行检验标准**:读者是业内人但没读过项目历史——需要作者解释才懂的词不合格,必须展开成完整白话句(换新名词不算改,展开才算改)。**写后自查**:凡自己都要停顿才能想起意思的词,一律展开。
|
||||
|
||||
**词表冻结**:doc_term_audit 与术语表「用语规范」节的禁用词表不再扩充;扩表唯一入口=用户实读反馈并经用户裁决,由非迭代批写入,禁 agent 自行增词。
|
||||
|
||||
## 5. 结构纪律
|
||||
|
||||
| 规则 | 内容 |
|
||||
@@ -70,10 +72,10 @@
|
||||
|
||||
**数值纪律**:正文定规则、取值域、单位;平衡值进 `data/tables/`,provisional 必须标注;已锁数值事实源 = `game/data/tables/*.tres`(现行值)与 owner 文档正文(规则、取值域、单位)。
|
||||
|
||||
**登记处唯一性(2026-10-19 用户裁)**:`design/registry/`(`entities.yaml`/`architecture.yaml`)已注销删除,禁再建第二处事实登记——术语进 术语表、数值进 `.tres`、架构进 架构文档、影响面靠全仓搜索概念规范名得出。强制收口条款:
|
||||
**登记处唯一性**:`design/registry/`(`entities.yaml`/`architecture.yaml`)已注销删除,禁再建第二处事实登记——术语进 术语表、数值进 `.tres`、架构进 架构文档、影响面靠全仓搜索概念规范名得出。强制收口条款:
|
||||
|
||||
1. **双写失同步禁止**——任何数值变更必须同时同步引用该数值的 GDD 表格正文、`data/tables/` 行值与 schema 校验域、相关待办清单现状——三类之一未更新即视为变更未完成。
|
||||
2. **调参表锁数后删现行值列**——仅留 schema 模板与取值域,只留 schema 模板与取值域,指针指向 `.tres` 对应行键。
|
||||
2. **调参表锁数后删现行值列**——仅留 schema 模板与取值域,指针指向 `.tres` 对应行键。
|
||||
3. **覆盖范围** = 奖励/状态/敌人/技能/充能/护甲等级/区间掷点/难度曲线/D_ref 等所有数值字段。
|
||||
4. **维护通道** = 「设计批 → 同步批」两段式;数值表改完不等于修订完成。
|
||||
5. **压缩/换装批** = 「`.tres` 落批 + 切片 smoke 跑通 + GDD 表格改为只留取值域与 `.tres` 指针 + 待办清单刷新」四步全收才记版本号。
|
||||
@@ -90,7 +92,7 @@
|
||||
| `03-内容规模.md` | 几个关卡、几种敌人、几种道具——做多少砍多少 |
|
||||
| `04-美术音频方向.md` | 参考图、参考游戏、风格关键词 |
|
||||
| `05-不做清单.md` | 明确写:这些我不要 |
|
||||
| `06-待办清单.md` | 流水线唯一登记处:条目=登记元数据标题+四属性正文,状态随流水线段推进(格式与状态机见第 12.1 节);各系统文档「待办」节只留指路存根 |
|
||||
| `待办清单.md`(仓库根) | 流水线唯一登记处:条目=登记元数据标题+四属性正文,状态随流水线段推进(格式与状态机见第 12.1 节);各系统文档「待办」节只留指路存根 |
|
||||
| `roadmap.md`(根目录) | 里程碑表+迭代总表,一行一迭代,记录编排与实现进度(六列格式见第 12.2 节) |
|
||||
|
||||
**系统文档骨架**(`systems/` 下统一采用):文首引言(一句话定位+状态)/设计目的(2-4 句白话)/系统范围(负责/不负责两表)/在核心循环中的位置(字符画一行+两句白话)/核心规则(领域章节 ×2–6,每节一页纸)/依赖与输出(表格;引用纪律见 concept-conventions)/P0 最小闭环与 P1·P2 后置/调参表(取值域+provisional 标注)/验收标准(可验证判据)/待办(指路存根)。
|
||||
@@ -101,37 +103,37 @@
|
||||
|
||||
## 10. 会话状态——仓库根不设状态文件
|
||||
|
||||
**2026-10-18 用户裁:根目录 `active.md` 连同「会话状态唯一状态源」这条规矩一起废除。** 判据来自对 869 个历史会话的读写统计:整文件被读 448 次、平均每次 14 KB 进上下文,但会话里关于它的思考九成花在「追加条目前先查尾部编号防撞号」与「并行会话同时改它导致的合并冲突与丢弃」上,明确写「从它所载内容得知某事实」的只有 20 个会话;文件 45 KB 里约 95% 是别处已有权威源的重复说法、已失真陈述或已完结批次的历史叙述。体量闸只能测条目数与死链,测不出语义失真:10-15 清到 8.6 KB/3 条,10-17 已回到 40 KB,用户为「只写不删」连续三次发火,规矩照样失效。
|
||||
**根目录 `active.md` 连同「会话状态唯一状态源」这条规矩一起废除。** 判据来自对 869 个历史会话的读写统计:整文件被读 448 次、平均每次 14 KB 进上下文,但会话里关于它的思考九成花在「追加条目前先查尾部编号防撞号」与「并行会话同时改它导致的合并冲突与丢弃」上,明确写「从它所载内容得知某事实」的只有 20 个会话;文件 45 KB 里约 95% 是别处已有权威源的重复说法、已失真陈述或已完结批次的历史叙述。体量闸只能测条目数与死链,测不出语义失真:10-15 清到 8.6 KB/3 条,10-17 已回到 40 KB,用户为「只写不删」连续三次发火,规矩照样失效。
|
||||
|
||||
废除后的分工:**现在做什么**看根目录 `roadmap.md` 顶部「当前位」节;**待办与待裁**看 [06-待办清单](../gdd/06-待办清单.md)(唯一登记处);**批次过程与结论**看 git 提交信息与 `reports/`;**跨会话教训**看记忆库。禁止在仓库根新建同类状态文件,也禁止在任何文档里另设「当前状态快照」节——两处记同一件事必然漂移,这正是本次统计查出七条失实陈述的成因。
|
||||
废除后的分工:**现在做什么**看根目录 `roadmap.md` 顶部「当前位」节;**待办与待裁**看 [待办清单](../../待办清单.md)(唯一登记处);**批次过程与结论**看 git 提交信息与 `reports/`;**跨会话教训**看记忆库。禁止在仓库根新建同类状态文件,也禁止在任何文档里另设「当前状态快照」节——两处记同一件事必然漂移,这正是本次统计查出七条失实陈述的成因。
|
||||
|
||||
## 11. 文档生命周期:吸收即死
|
||||
|
||||
**活文档**=唯一事实源长期维护:GDD 树、art-bible 等定稿规范、术语表、架构文档(`design/engineering/architecture/architecture.md`)、退役内容留底(`design/content-archive/`,2026-10-20 用户裁新立:只装「内容已退役、留底供日后重新过准入」的档案,落内文件须在文档头写明所属线)、本规范、06-待办清单、roadmap。**中间文档**=脚手架,寿命=迭代寿命:批需求卡片(死期=所属迭代实现批合回 master 那一刻;roadmap 对应行的卡片/报告列改填「已退役(git 历史可考)」保留字,凭据语义从文件指针换代为状态标记)、机检/验收过程报告、调研/审计报告、模拟读数、迭代计划书、目检清单。为什么:了解游戏看 GDD,了解变更看 git;增量中间文档让版本管理失去意义,原始数据 dump 是化石不是证据。
|
||||
**活文档**=唯一事实源长期维护:GDD 树、art-bible 等定稿规范、术语表、架构文档(`design/engineering/architecture/architecture.md`)、退役内容留底(`design/content-archive/`,2026-10-20 用户裁新立:只装「内容已退役、留底供日后重新过准入」的档案,落内文件须在文档头写明所属线)、本规范、待办清单、roadmap。**中间文档**=脚手架,寿命=迭代寿命:批需求卡片(死期=所属迭代实现批合回 master 那一刻;roadmap 对应行的卡片/报告列改填「已退役(git 历史可考)」保留字,凭据语义从文件指针换代为状态标记)、机检/验收过程报告、调研/审计报告、模拟读数、迭代计划书、目检清单。为什么:了解游戏看 GDD,了解变更看 git;增量中间文档让版本管理失去意义,原始数据 dump 是化石不是证据。
|
||||
|
||||
| 规则 | 内容 |
|
||||
|---|---|
|
||||
| 吸收即死 | 中间文档结论写进权威文档(GDD 对应节/art-bible/术语表)那一刻,文件即删 |
|
||||
| 未决先迁再死 | 未决内容先迁现行事实源(06/roadmap 卡/GDD 对应节),然后文档即死 |
|
||||
| 未决先迁再死 | 未决内容先迁现行事实源(待办清单/roadmap 卡/GDD 对应节),然后文档即死 |
|
||||
| 永不入库 | 迭代出口后中间产物一律删除且不许提交;迭代进行中可暂存工作区,出口批一并清 |
|
||||
| 引用面口径 | 活文档对中间文档的指路阻塞删除:先改写指路句为结论内联或 git 提交号再删 |
|
||||
| 删除不留死链 | 中间文档删除后,任何现行文档禁止再留指向它的文件路径;改指结论或提交号 |
|
||||
| sim-data 白名单 | 只留 README、日志、分析工具 .py、现行 MC 对拍锚、设计文档 md;可再生原始数据不入库 |
|
||||
| 出口清理项 | 每迭代出口检查单必含:本迭代中间产物逐份核对(结论已吸收、未决已迁移)后删除 |
|
||||
| 暂存地非档案库 | 中间文档有两处暂存地,入内文件默认到期即死:`reports/`(需求卡片+迭代产出物)、`design/research/`(调研/提案/评估/历史验收)。**`design/research/` 无常驻例外**——任何文件不得长期驻留:要留用的参考档迁出 `design/` 活文档区转正身份,用后即弃的到期删除;禁止文件在自身页眉自称「常驻/已追认/不受吸收即死约束」之类的豁免(豁免只进规约正文,而本条就是不给)。跨会话交接文件(会话提示/handoff/任务书/试运行报告)**禁入库**——给另一个会话的开工输入在会话里交付即可(与仓库根禁设会话状态文件同族判据,见第 10 节)。根目录 `playnote.md`=用户本人试玩随手记,只由用户本人增删,不属于任何文档族、不受吸收即死管辖,不进 §11 任何一格。`design/` 各子目录不留验证截图——截图属一次性凭据,结论写进 06 或需求卡片后图像即弃(在途图放 `artwork/`,整目录不入 git);只有需要机器逐像素比对的基线图长期入库,落 `game/tests/golden/` |
|
||||
| 落档必写接收方 | 新报告落档时头部必须写明接收方——`06-待办清单.md` 第几条,或哪张需求卡片。写不出接收方的不落文件,内容在会话里交付即可 |
|
||||
| 迭代实现报告 | 实现批收口的迭代级报告(各卡状态/代拍板追认单/GDD 反哺点清单),属中间文档,**落 `reports/` 不落 `design/`**(2026-10-17 用户裁:实现批禁写 `design/`,`reports/` 是其唯一可写目录):反哺点裁决落文吸收后按「证据只留最新」退役 |
|
||||
| 暂存地非档案库 | 中间文档有两处暂存地,入内文件默认到期即死:`reports/`(需求卡片+迭代产出物)、`design/research/`(调研/提案/评估/历史验收)。**`design/research/` 无常驻例外**——任何文件禁止长期停留:确实要留用的参考档迁入 `design/` 活文档区(退役内容留底落 `design/content-archive/`)并按活文档身份维护,用后即弃的到期删除;禁止文件在自身页眉自称「常驻/已追认/不受吸收即死约束」之类的豁免——豁免只写进规约正文,本条就是不给。跨会话交接文件(会话提示/handoff/任务书/试运行报告)禁止入库——给另一个会话的开工输入在会话里交付即可(与仓库根禁设会话状态文件同族判据,见第 10 节)。根目录 `playnote.md`=用户本人试玩随手记,只由用户本人增删,不属于任何文档族、不受吸收即死管辖,不进 §11 任何一格。`design/` 各子目录不留验证截图——截图属一次性凭据,结论写进待办清单或需求卡片后图像即弃(在途图放 `artwork/`,整目录不入 git);只有需要机器逐像素比对的基线图长期入库,落 `game/tests/golden/` |
|
||||
| 落档必写接收方 | 新报告落档时头部必须写明接收方——`待办清单.md` 第几条,或哪张需求卡片。写不出接收方的不落文件,内容在会话里交付即可 |
|
||||
| 迭代实现报告 | 实现批收口的迭代级报告(各卡状态/代拍板追认单/GDD 反哺点清单),属中间文档,**落 `reports/` 不落 `design/`**(实现批禁写 `design/`,`reports/` 是其唯一可写目录):反哺点裁决落文吸收后按「证据只留最新」退役 |
|
||||
| 证据只留最新 | 测试证据只保留最新一次迭代或最新一轮同类审计版本;新证据落位后同批删上一份;待人清单过人闸后同规退役 |
|
||||
|
||||
**删除方式**:按 `design/engineering/concept-conventions.md` 第 2 节删除概念 SOP 口径——先查引用面(grep 文件名于阻塞域),改写或删除指路句,再删文件,收尾跑死链检查全绿。执行入口=项目技能 batch-closure(批后收尾与中间文档退役)。
|
||||
|
||||
## 12. 流程文档格式
|
||||
|
||||
五段流水线(登记 → 文档设计/美术设计 → 写卡 → 迭代编排 → 实现,流程纪律见 `design/engineering/process-conventions.md` §2)靠流程文档运转:06-待办清单登记要做的事,roadmap 记录迭代编排结果,artwork 目录承载美术在途素材。本节是这些流程文档的格式与状态机权威,也是流水线闸(`tools/pipeline_gate.py`,自动检查 06 与 roadmap 两文件格式及互相引用的机检闸)的判据依据。
|
||||
五段流水线(登记 → 文档设计/美术设计 → 写卡 → 迭代编排 → 实现,流程纪律见 `design/engineering/process-conventions.md` §2)靠流程文档运转:待办清单登记要做的事,roadmap 记录迭代编排结果,artwork 目录承载美术在途素材。本节是这些流程文档的格式与状态机权威,也是流水线闸(`tools/pipeline_gate.py`,自动检查待办清单与 roadmap 两文件格式及互相引用的机检闸)的判据依据。
|
||||
|
||||
### 12.1 06-待办清单条目格式与状态机
|
||||
### 12.1 待办清单条目格式与状态机
|
||||
|
||||
06-待办清单是流水线的唯一登记处:要做的事先登记一条,随流水线段推进改状态,做完整条删除。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、走哪条设计轨,登记后不再变),**正文=四个属性**(随进程演进)。
|
||||
待办清单是流水线的唯一登记处:要做的事先登记一条,随流水线段推进改状态,做完整条删除。一条条目分两层——**标题=登记元数据**(何时登记、从哪来、走哪条设计轨,登记后不再变),**正文=四个属性**(随进程演进)。
|
||||
|
||||
标题格式(尖括号处按实际填写):
|
||||
|
||||
@@ -142,37 +144,34 @@
|
||||
正文四属性模板:
|
||||
|
||||
```
|
||||
**现状**:现在缺什么、为什么是问题(关键处带 file:line 或文档指针)。工程直入条目必须在现状里指认证据(bug 证据或工程债条目)。
|
||||
**现状**=关联文档清单,一行一件:它是什么(设计定稿/需求卡片/相关文档)+文件路径+版本号与日期(闭合类文件必带);必要时在清单前配一句现状说明。机制解释与论证过程不写在这里,归权威文档。路径用普通文本或行内代码书写,机器可搜索。工程直入条目必须在现状里指认证据(bug 证据或工程债条目)。
|
||||
**缺口**:还差哪一次设计讨论/哪份设计产物才能写卡。尚有未闭合的设计轨时此属性必须存在;全部设计轨闭合后删除。
|
||||
**归属**:主责系统文档或线别。
|
||||
**状态**:按状态机取词。
|
||||
```
|
||||
|
||||
状态机按标题里的设计面分轨,状态一律取下例用词。状态行按**状态段累积制**书写:条目每推进一段,就把新状态段接在状态行末尾,段与段用+号连接,书写顺序=推进顺序;已过去的段一律保留、禁删——闭合凭据要留在行内,编排四查与开工核验拿它比对。每段各自按状态词白名单与凭据规则校验,+号只作状态段连接符。
|
||||
状态机按标题里的设计面分轨,状态一律取下例用词。状态行按**状态段累积制**书写:条目每推进一段,就把新状态段接在状态行末尾,段与段用+号连接,书写顺序=推进顺序;已过去的段一律保留、禁删——状态履历供编排四查与开工核验比对。每段各自按状态词白名单校验,+号只作状态段连接符。
|
||||
|
||||
```
|
||||
各轨推进序列(双轨条目设计阶段两段并列推进,一文档轨一美术轨):
|
||||
文档轨:待文档设计 → 文档设计中 → 文档设计已闭合(凭据=文档路径+版本号,
|
||||
例:文档设计已闭合(design/gdd/systems/xxx.md v1.2))
|
||||
美术轨:待美术设计 → 美术设计中 → 美术设计已闭合(凭据=样张过审、art-bible 条款版本,
|
||||
例:美术设计已闭合(样张过审、art-bible v3.x))
|
||||
双轨条目设计阶段示例:文档设计已闭合(design/gdd/systems/xxx.md v0.6)+美术设计中
|
||||
进入下游段(待写卡及之后)的前置条件——双轨:两个设计轨段都在场且都已闭合(带凭据);单轨:本轨闭合段(带凭据)在场
|
||||
下游共用序列:待写卡 → 已写卡(reports/ 下的需求卡片路径)→ 已编迭代 N → 完成即整条删除
|
||||
单轨累积示例(已编迭代):文档设计已闭合(design/gdd/systems/xxx.md v1.2)+已写卡(reports/需求卡片-xxx.md)+已编迭代 2
|
||||
文档轨:待文档设计 → 文档设计中 → 文档设计已闭合
|
||||
美术轨:待美术设计 → 美术设计中 → 美术设计已闭合
|
||||
进入下游段(待写卡及之后)的前置条件——双轨:两个设计轨闭合段都在场;单轨:本轨闭合段在场
|
||||
下游共用序列:待写卡 → 已写卡 → 已编迭代 N → 完成即整条删除
|
||||
单轨累积示例(已编迭代):文档设计已闭合+待美术设计+已写卡+已编迭代 2
|
||||
```
|
||||
|
||||
凭据括号内禁用+号——+只作状态段连接符,凭据内需要并列多项时用顿号(如上例「样张过审、art-bible v3.x」),用+会被流水线闸误判为状态段。已编迭代 N 段的交叉核依据=同状态行内「已写卡」段括号里的卡片路径。
|
||||
状态行只写状态词:去掉特批标记与「(备选)」后缀后,残留任何括号即违规。闭合类状态词(文档设计已闭合/美术设计已闭合/已写卡/已写入)的证据——设计定稿路径+版本、art-bible 条款版本、需求卡片路径、落点文档+版本——一律写在现状的关联文档清单里,由流水线闸到正文核对;已编迭代 N 段的交叉核依据=现状清单里的需求卡片路径。
|
||||
|
||||
三条特殊通道:
|
||||
|
||||
| 设计面 | 通道规则 |
|
||||
|---|---|
|
||||
| 工程直入 | 缺陷修复/工程债/测试基建/重构四类工程条目:登记即「待写卡」,凭现状里指认的证据跳过设计轨 |
|
||||
| 反哺点专条 | 待裁决 → 已写入(落点=文档+版本)→ 删除 |
|
||||
| 反哺点专条 | 待裁决 → 已写入 → 删除 |
|
||||
| 备选库条目 | 暂不排期、留在备选库的条目:状态词后缀「(备选)」 |
|
||||
|
||||
「已闭合」不带凭据不算合法状态词。特批标记见 12.4。
|
||||
特批标记见 12.4。
|
||||
|
||||
### 12.2 roadmap 迭代行格式
|
||||
|
||||
@@ -216,7 +215,7 @@ roadmap 由顶部里程碑表+迭代总表组成,迭代总表一行一个迭
|
||||
|
||||
### 12.4 特批标记
|
||||
|
||||
特批标记是流程格式例外的唯一放行凭证:`〔用户特批 YYYY-MM-DD 跳过××〕`。只有用户明示才能给,用于 06 条目状态与 roadmap 格(见 12.1、12.2)。
|
||||
特批标记是流程格式例外的唯一放行凭证:`〔用户特批 YYYY-MM-DD 跳过××〕`。只有用户明示才能给,用于待办清单条目状态与 roadmap 格(见 12.1、12.2)。
|
||||
|
||||
执行技能一律两段式:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
主工作区(master 检出)只作合并落点,一切开发/设计改动禁止直接发生在主工作区:
|
||||
|
||||
1. **开工**:每批次先建独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`;所有改动与验证都在 worktree 内进行(未跟踪文件不会出现在新 worktree,需手工搬运)
|
||||
2. **收尾**:迭代 loop 批次验证全绿(冒烟+场景探针)后在主工作区 `git merge --no-ff <任务分支>`,再 `git worktree remove`+删分支;合回前若 master 已被别的批次推进,先在分支里 `git merge master` 解冲突、重跑本域闸再合回。非 loop 批次默认完成不跑闸(两入口铁规),用户点名或当场问
|
||||
2. **收尾**:迭代 loop 批次验证全绿(冒烟+场景探针)后在主工作区 `git merge --no-ff <任务分支>`,再 `git worktree remove`+删分支;合回前若 master 已被别的批次推进,先在分支里 `git merge master` 解冲突、重跑本域闸再合回。非 loop 批次合回前同样按本批改动域跑闸(见 §3.10:纯文档批跑 `--docs`);游戏类测试(冒烟/单测/MC/探针)对它默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
|
||||
3. **豁免面**:仅 `.zcode/`(钩子与配置)与 `.qoder/`(本机 harness 配置)允许主工作区直改——两份实测均不入 git(走 worktree 反而带不进去,改动只对本机生效);其余一律走 worktree,主工作区 git commit 被钩子拦截
|
||||
4. 本铁律由 PreToolUse 钩子 `.zcode/hooks/worktree-guard.sh` 强制兜底;钩子只约束 agent,用户手工改动不受限
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
|
||||
| 段 | 入口技能 | 产出 | 出入口铁律 |
|
||||
|---|---|---|---|
|
||||
| 一·登记 | 无(闸管格式) | 06-待办清单条目 | 条目=标题登记元数据+正文四属性(现状/缺口/归属/状态);标题登记后不再变,正文随进程演进。格式与状态机权威=doc-conventions 第 12 节 |
|
||||
| 二·文档设计/美术设计 | doc-design(文档轨,兼任反哺点写入)/asset-ingest(美术录入;样张素材由用户主导产出) | GDD 文档或 art-bible/过审样张素材 | 用户提起或从 06 取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档;完成判据=待裁点清零,带未裁点的是讨论稿;轨闭合凭据记入 06 状态(文档轨=文档路径+版本号,美术轨=样张过审+art-bible 条款版本) |
|
||||
| 三·写需求卡片 | req-card | 需求卡片 | 轨闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=工程直入四类(缺陷修复/工程债/测试基建/重构)凭证据跳过设计轨;卡片必经 grilling 多轮复核、零待裁点才成立;写完在 06 状态记「已写卡」+卡片路径(防遗忘) |
|
||||
| 一·登记 | 无(闸管格式) | 待办清单条目 | 条目=标题登记元数据+正文四属性(现状/缺口/归属/状态);标题登记后不再变,正文随进程演进。格式与状态机权威=doc-conventions 第 12 节 |
|
||||
| 二·文档设计/美术设计 | doc-design(文档轨,兼任反哺点写入)/asset-ingest(美术录入;样张素材由用户主导产出) | GDD 文档或 art-bible/过审样张素材 | 用户提起或从待办清单取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档;完成判据=待裁点清零,带未裁点的是讨论稿;轨闭合凭据记入待办清单现状清单(文档轨=文档路径+版本号,美术轨=art-bible 条款版本) |
|
||||
| 三·写需求卡片 | req-card | 需求卡片 | 轨闭合后用户发令;新玩法/新系统/新内容必须有设计定稿前置,例外=工程直入四类(缺陷修复/工程债/测试基建/重构)凭证据跳过设计轨;卡片必经 grilling 多轮复核、零待裁点才成立;写完在待办清单状态记「已写卡」,卡片路径记入现状清单(防遗忘) |
|
||||
| 四·迭代编排 | iter-plan | roadmap 迭代行 | 编排四查:①每卡卡片文件存在 ②每卡设计闭合凭据齐(或工程直入证据)③总估时 ≤2 周 ④写完 roadmap 行后跑流水线闸确认绿;编入未完成迭代,新开迭代行经用户确认 |
|
||||
| 五·实现 | iter | 代码与配置,可运行的游戏 | 需求卡片永不单独施工,统一由用户指定迭代号触发(=U0 开工令),按 iter 技能与本文 §3 执行;**本段是代码实现批:禁写 `design/` 与根规则文件,产出物只落 `reports/`(见 §3 第 13 条)**;设计权威文档(GDD/art-bible)批内一律不更新;完成人闸=实机目检清单+代拍板追认单登记交用户即完成批内义务 |
|
||||
|
||||
**实现段零裁断**:实现段的输入在编排段出口已锁死(卡片零待裁点+设计闭合凭据齐+编排四查过)。实现中冒出卡片没写清的分歧=上游缺陷——停该卡、06 状态回退+缺口补记,退回上游;禁自答、禁内联提问求裁断;该卡停摆不阻塞同迭代其他卡。环境故障与测试红走异常分级(自救重试/记录继续/停机呈报)。代拍板白名单保留:授权发生在卡片审问阶段,实现只执行预授权;批后追认单=备案确认。
|
||||
**实现段零裁断**:实现段的输入在编排段出口已锁死(卡片零待裁点+设计闭合凭据齐+编排四查过)。实现中冒出卡片没写清的分歧=上游缺陷——停该卡、待办清单状态回退+缺口补记,退回上游;禁自答、禁内联提问求裁断;该卡停摆不阻塞同迭代其他卡。环境故障与测试红走异常分级(自救重试/记录继续/停机呈报)。代拍板白名单保留:授权发生在卡片审问阶段,实现只执行预授权;批后追认单=备案确认。
|
||||
|
||||
**流水线闸**:`tools/pipeline_gate.py` 机检 06-待办清单与 roadmap 两文件的格式与互相引用;将注册进 `run_all_gates.sh` 文档域(注册归第二批,本批不注册)。流程文档的条目格式、迭代行格式与状态机细则见 doc-conventions 第 12 节。
|
||||
**流水线闸**:`tools/pipeline_gate.py` 机检待办清单与 roadmap 两文件的格式与互相引用;已注册进 `run_all_gates.sh` 文档域。流程文档的条目格式、迭代行格式与状态机细则见 doc-conventions 第 12 节。
|
||||
|
||||
## 3. 批次工作流标准清单
|
||||
|
||||
@@ -45,17 +45,18 @@
|
||||
6. **涉数值改动先采改前读数**——与改后同随机种子配对比较并写明逐项比对口径;无数值改动禁跑模拟
|
||||
|
||||
**实施**
|
||||
7. **新功能必配新测试**——新增玩法/机制必须配套新测试套件进 `tests/run_all`;没有对应测试的变更不许合并
|
||||
7. **新功能必配新测试**——新增玩法/机制必须配套新测试套件进 `tests/run_all`;没有对应测试的变更不许合并;并按 test-conventions §9 挂进改动—验收对照表,没有对照表行的行为改动同样不许合并
|
||||
8. **验收改动先行声明**——验收变化(基线换代/探针增改/旧断言改判)预先写进需求卡片,禁事后补
|
||||
9. **验收函数变异验证**——新增/改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证:故意把被测件改坏成该类缺陷形态 → 跑最窄那道闸确认变红 → `git checkout` 精确还原;变异点/跑的闸/红输出摘要/还原结论四件记入实现报告变异验证记录表;**无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」**(细则见 test-conventions §6)
|
||||
9. **验收函数变异验证**——新增/改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证:故意把被测件改坏成该类缺陷形态 → 跑最窄那道闸确认变红 → `git checkout` 精确还原;变异点/跑的闸/红输出摘要/还原结论四件记入实现报告变异验证记录表;**无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」**(细则见 test-conventions §6);行为改动另做撤销证明(test-conventions §9.4),两类证明都记入实现报告
|
||||
|
||||
**完成**
|
||||
10. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:本批改动触及 `game/`(游戏工程代码、测试、探针、场景)→ `--code`;不触及 `game/` 的一切改动(`design/`、根规则文件、`tools/` 工具脚本、`.zcode/` 配置等)→ `--docs`;同批混触 `game/` 与文档面 → 全量档不带参数。所选域全绿才合回 master。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占合回全量——其质量义务=落批时的变异验证(test-conventions §6),闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以合回全量为兜底
|
||||
10. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:本批改动触及 `game/`(游戏工程代码、测试、探针、场景)→ `--code`;不触及 `game/` 的一切改动(`design/`、根规则文件、`tools/` 工具脚本、`.zcode/` 配置等)→ `--docs`;同批混触 `game/` 与文档面 → 全量档不带参数。所选域全绿才合回 master。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验(五况拦截:缺回执/quick 模式/回执 commit ≠ HEAD/域不覆盖本次改动/含新增红)。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占合回全量——其质量义务=落批时的变异验证(test-conventions §6),闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以合回全量为兜底
|
||||
11. **合并后主区按域复跑**——合并可能引入冲突或环境差异,落回 master 后按本批改动域复跑:触及 `game/` 的批=全量复跑(维持);纯文档域批=复跑文档域档(秒级),不再全量
|
||||
12. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进 06 条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(2026-10-18 用户裁,见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`)实现批禁写,见 §13
|
||||
13. **文档同步闸(2026-10-17 改口径:实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter<N>-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由非迭代批(工具/规则/文档批)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归非迭代批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与 06;需求卡片必列「关联文档同步面」按两层标注
|
||||
14. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与 07-体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置
|
||||
12. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §13
|
||||
13. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter<N>-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由非迭代批(工具/规则/文档批)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归非迭代批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注
|
||||
14. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置
|
||||
15. **中间产物吸收即死**——细则见 AGENTS.md 硬性红线与 doc-conventions 第 11 节
|
||||
16. **改动级验收闭合**——写卡期列需求侧对照表,实现期补实际对照表(测试文件:行),每个行为改动做撤销证明;合回前独立验收审计。细则=test-conventions §9
|
||||
|
||||
## 4. 测试执行两入口铁规(细则)
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
|
||||
## 5. 测试函数怎么写——验收清单条目的落码规范
|
||||
|
||||
需求卡片 §7「逐需求点验收清单」每一条都要落成可执行的验证;本文定统一写法,卡里定每批的具体内容(测什么/注入哪里)。写卡期查不出执行载体的条目=不可测,当场呈报,禁拖到实现期。
|
||||
需求卡片 §8「改动—验收对照表」每一条都要落成可执行的验证;本文定统一写法,卡里定每批的具体内容(测什么/注入哪里)。写卡期查不出执行载体的条目=不可测,当场呈报,禁拖到实现期。
|
||||
|
||||
1. **执行载体五选一,逐条声明**:单元测试(`tests/run_all`)/冒烟断言(`smoke_all.gd` 对应场景段)/场景探针(`smoke/` 下 `*_probe`)/MC 同种子逐项比对/人工核对(仅限无法自动化的纪律类需求点,如命名锁定、数值占位)。行为类条目以自动化载体为主。
|
||||
2. **粒度与对应关系**:每条清单条目至少一个自动断言;多条条目可合用一个测试函数。断言失败消息末尾带编号尾缀〔卡短名 Vn〕(如〔秘宝批 V3〕)——断言与卡片清单可互相 grep 查找。
|
||||
2. **粒度与对应关系**:每条清单条目至少一个自动断言,且断言必须从该条目的真实入口进入;多条条目可合用一个测试函数,但撤销对应改动时每条都要能单独变红。断言失败消息末尾带编号尾缀〔卡短名 Vn〕(如〔秘宝批 V3〕)——断言与卡片清单可互相 grep 查找。
|
||||
3. **断言词汇按验证对象五选一**:
|
||||
|
||||
| 词汇 | 适用场景 |
|
||||
@@ -52,6 +52,8 @@
|
||||
|
||||
测试绿只证明「当前代码没犯这个错」,不证明「犯了这个错会被抓住」。新增或改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证,把拦截能力自证一遍。**无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」。**
|
||||
|
||||
行为改动的撤销证明见 §9.4。新增或改写断言用变异验证;修改既有行为用撤销证明。两者不互相代替:变异证明断言能抓这一类缺陷,撤销证明断言真的钉在这次改动上。
|
||||
|
||||
**操作四步(逐条落实现报告变异验证记录表)**:
|
||||
|
||||
1. **注入**——把被测件改坏成该类缺陷的最小形态(翻装配序、注释一行 `add_child`、把状态源取反、把层 `visible` 置 false)。一次只注入一处;可直接改源码,不必留长期开关。
|
||||
@@ -66,7 +68,7 @@
|
||||
|
||||
**判据强弱分层**:同一缺陷面往往需要多件配合——**可见性/状态源类**缺陷由 `is_visible_in_tree`、树序、状态源取值的**运行期断言**抓;**内容/观感类**缺陷由 **golden 逐像素比对**抓;**占比/统计类**判据(墨色占比、纹理幅度)只能兜「整框空白/整框糊/压 busy 纹理」,是补充不是主力。写卡时按此分层配齐,别指望一条断言包打。
|
||||
|
||||
## 7. 判定三硬闸——闸承载的构造义务(迭代 29 三硬闸 2026-10-17 用户裁升格为纪律)
|
||||
## 7. 判定三硬闸——闸承载的构造义务(迭代 29 起)
|
||||
|
||||
任何**新增或改写的闸承载**(闸脚本/批跑编排/探针入口)必须三闸齐备;现行各闸状态登记归架构文档「测试与质量域」节(本文不另列闸件清单):
|
||||
|
||||
@@ -76,11 +78,68 @@
|
||||
- 负面套件(测试目标就是产生错误输出)仅可豁免第 2 闸,且须在 manifest 显式标记,其余两闸照判。
|
||||
- 子进程输出须经系统命令解释器重定向落盘再读——`OS.execute` 的输出管道在本机恒空。
|
||||
|
||||
**新写测试件全类型标注**(2026-10-17 用户裁写法纪律)——新写的测试件(冒烟断言/探针/单测)参数、返回值、容器一律带类型标注,与 S9 对产品码的要求同型;存量不强制清扫,被批次触碰时顺带换代。暂不机器闸(S9 扫描面=产品码,smoke/tests 豁免是测量面直构惯例的既有裁量)。
|
||||
**新写测试件全类型标注**——新写的测试件(冒烟断言/探针/单测)参数、返回值、容器一律带类型标注,与 S9 对产品码的要求同型;存量不强制清扫,被批次触碰时顺带换代。暂不机器闸(S9 扫描面=产品码,smoke/tests 豁免是测量面直构惯例的既有裁量)。
|
||||
|
||||
## 8. 判定可靠性优先于覆盖率(迭代 29 核心命题 2026-10-17 用户裁成节)
|
||||
## 8. 判定可靠性优先于覆盖率(迭代 29 核心命题)
|
||||
|
||||
- 「跑一遍没崩=通过」无效:测试绿只证明**当前代码没犯这个错**,不证明**犯了这个错会被抓住**;断言存在不等于断言执行。
|
||||
- **脚本错误会静默中断所在函数的剩余断言,而套件仍报全绿**(机制事实):GDScript 无脚本错误陷阱 API,错误不改进程退出码、不计失败数。三处实证:①2026-09-19「309 项 0 失败」实为断言未跑全(状态记录 83);②敌人表行集纪律 10 条断言因定型数组赋值错误从未执行(迭代 29 修复后两条正向见红=夹具本缺锚行);③`Callable` 形参类型写错致解析函数返回 null、后续断言整段蒸发。
|
||||
- 由此产生两条义务:**写闸件**按 §7 配三硬闸;**写验收函数**必须自证断言全数执行——变异验证(§6)为主证,反例实证(构造缺陷态改前判绿/改后判红)与零命中自检(扫描面为空即判红)为辅证。
|
||||
- **覆盖率不是目标**:行覆盖率度量缺失(Godot 无内建,第三方属新基建,是否引入 06 在册待裁);现行补洞=零指名单测清单+人工判定,判定可靠性靠上列自证手段,不靠覆盖率数字。
|
||||
- **覆盖率不是目标**:行覆盖率度量缺失(Godot 无内建,第三方属新基建,是否引入待办清单在册待裁);现行补洞=零指名单测清单+人工判定,判定可靠性靠上列自证手段,不靠覆盖率数字。
|
||||
|
||||
## 9. 改动级验收闭合(2026-10-21 立)
|
||||
|
||||
### 9.1 验收单位是行为改动
|
||||
|
||||
一个行为改动 = 新增或修改一条函数、一条数据、一处接线、一个生命周期行为、一条玩家可见文案。纯改名、注释、格式化不算行为改动,但必须在对照表里单独列一行写「非行为改动」及判断依据。禁止用「重构」两个字把行为改动藏起来。
|
||||
|
||||
### 9.2 四层与真实入口
|
||||
|
||||
- **单元**:不依赖场景树和真实宿主的纯逻辑;测试直接调用被测函数。
|
||||
- **接口**:模块边界的公开契约——数据表加载、存档与断点、事件载荷、命令接口;测试从公开入口进出。
|
||||
- **集成**:跨宿主的接线、持久、生命周期——例如 LevelRunner 把 level_counters 传给 BattleOrchestrator、两场战斗共享同一个桶、断点续玩;测试必须实例化真实宿主链,禁止用裸 EffectEngine 代替。
|
||||
- **探针/golden**:玩家可达性与观感;真实实例化场景、驱动真实输入。
|
||||
|
||||
分层不是形式。改动发生在哪一层,验收就必须从那一层的真实入口进入。测试绕过了改动点,就不算这个改动的验收——这正是「断言存在、接线坏了却不红」的来源。
|
||||
|
||||
### 9.3 改动—验收对照表
|
||||
|
||||
需求卡片列需求侧对照表;实现报告列实际对照表。每行至少六格:
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+文件:行+尾缀) | 层级 | 真实入口 | 撤销或变异证明 |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
实现报告另加「结果」格(绿/红/未过及依据)。两端都要对上:
|
||||
|
||||
- **正向**:每个需求点至少一行;每行必须有真实入口和证明方式。缺任一项=该需求点未验收。
|
||||
- **反向**:diff 里每个行为改动至少一行;没有对应行的改动,必须写明「非行为改动」或「由第 X 行覆盖」。
|
||||
- **测试侧**:新增或改写的每条断言至少挂一行;挂不上的孤儿断言删掉或补挂到需求点。
|
||||
|
||||
### 9.4 撤销证明
|
||||
|
||||
- **修改既有行为**:只撤销该改动(回退该 hunk 或该处接线)→ 跑能覆盖它的最窄测试 → 对应断言必须变红 → 还原 → 复绿。
|
||||
- **新增行为**:用 §6 的变异办法把新代码改坏成最小缺陷形态 → 对应断言必须变红。
|
||||
- **撤销后仍然绿**:说明断言没钉在这个改动上,判缺漏;不得用「代码能跑通」代替。
|
||||
- 每张任务卡至少一条撤销或变异记录;持久化、生命周期、存档、跨战共享这类接线逐行做。
|
||||
|
||||
### 9.5 禁止削弱测试
|
||||
|
||||
实现期不得为了让测试变绿而删除断言、放宽阈值、缩小扫描面、把真实入口换成下层直调。确需改判据=需求卡片没写清=上游缺陷:停该卡、呈报退回写卡段,不在实现期自行决定。卡片可预先声明允许改动的旧断言及理由;未声明的削弱由独立审计判红。
|
||||
|
||||
### 9.6 测试时机
|
||||
|
||||
不要求 TDD,不要求先红后绿。允许实现后补测试;但补的测试必须通过 §9.4 的撤销证明。实现完再补、补完就绿,不算验收。
|
||||
|
||||
### 9.7 独立验收审计
|
||||
|
||||
合并前由没有参与实现的会话或子代理做一次审计。审计只读需求卡片、实际对照表、diff、测试文件和实现报告,自己跑最窄测试抽查,不采信报告里的「已全绿」。审计至少做三件事:
|
||||
|
||||
1. 逐行核对对照表:测试存在、真的执行、尾缀能 grep 到、真实入口没有绕过改动点;
|
||||
2. 抽查撤销证明:每张任务卡至少一条,按记录复现红/绿;
|
||||
3. 检查测试削弱:删除断言、放宽阈值、缩小扫描面、换下层直调,逐处要卡片申报或用户批准。
|
||||
|
||||
审计报告落 `reports/iter<N>-验收审计-<日期>.md`,逐行写「通过/缺漏/例外及依据」。未通过不得合并。测试执行两入口铁规不变:审计在迭代 loop 内启动的子代理算 loop;另开会话须用户点名。
|
||||
|
||||
### 9.8 生效与旧卡
|
||||
|
||||
本规自 2026-10-21 起对文件名日期不早于该日的需求卡片生效;旧卡不回头改。旧卡在开工前缺对照表的,由写卡段补,补完再开工。
|
||||
@@ -13,8 +13,8 @@
|
||||
| `04-美术音频方向.md` | 参考图、参考游戏、风格关键词 |
|
||||
| `05-不做清单.md` | 明确写:这些我不要 |
|
||||
| `术语表.md` | 全库专用术语的唯一对照表——危险格/安全格/同调位/RAP/GP/真视等定义+退役词对照(2026-10-11 建立,v1.0) |
|
||||
| `06-待办清单.md` | 全项目待办事项的唯一登记处;各系统文档的「待办」节只留指路存根 |
|
||||
| `07-体验验证清单.md` | 需要真人试玩观测的验证项登记处(与 06 分工:06 是"要拍板的事",本篇是"要玩过才知道的事");每条注明结论回流路径 |
|
||||
| `待办清单.md`(仓库根,不属本树) | 全项目待办事项的唯一登记处;各系统文档的「待办」节只留指路存根 |
|
||||
| `体验验证清单.md`(仓库根,不属本树) | 需要真人试玩观测的验证项登记处(与待办清单分工:待办清单是"要拍板的事",本篇是"要玩过才知道的事");每条注明结论回流路径 |
|
||||
| `08-Alpha版本标准.md` | Alpha 版本包含哪些东西的清单——里程碑 M3 的完成标准;只列条目不写内容,定义一律指路各权威文档(**已定稿 v1.4**,2026-10-13) |
|
||||
|
||||
## systems/ 五个循环
|
||||
@@ -43,7 +43,7 @@
|
||||
| 文件 | 一句话概要 | 状态 |
|
||||
|---|---|---|
|
||||
| `关卡节点地图.md` | 单关内的节点图选路与推进 | 已定稿 |
|
||||
| `关卡节点图生成.md` | 单关节点图的逐局随机来源:图模板池、抽签与实例化(实施排队统一流裁决后) | 设计草案·拍板点全部已写入 |
|
||||
| `关卡节点图生成.md` | 一关的节点图从哪来——生成器按参数产出合法图 | 已定稿 |
|
||||
| `冒险结构.md` | 整局冒险的关卡序列、失败与大奖励 | 已定稿·自旧体系迁移 |
|
||||
| `难度曲线与Boss.md` | 难度曲线、期望值带与 Boss 设计 | 已定稿·自旧体系迁移 |
|
||||
| `奇物.md` | 一次冒险内各渠道给予玩家的增益总称(专长=触发器句式的机制件 / 属性值提升 / 补给=给系统资源加一笔的转换源凭证 × 三条渠道:奖励节点 / 商店 / 关底 Boss 击败) | 已实现·迭代 20 专长实装批落地(29 行=基础 27+迁入 2,流派机制退役;池结构案D 四级 104,通关兑付=专长三选一按档配比加权) |
|
||||
@@ -55,7 +55,7 @@
|
||||
| `元进度.md` | 跨局成长(旧体系未单独成文) | 已定稿·已实现 |
|
||||
| `天赋树.md` | 跨局成长的树形承载(**结构/节点/数值/效果一律以该文档为唯一事实源——本表不复制其内容,只作指路**) | v0.8 换代定稿待实装(迭代 21 天赋树实装批,2026-10-11 用户令编入,开工门=U0 发令;需求卡片 v1.7) |
|
||||
| `生涯.md` | 主菜单一级的局外只读界面——跨局汇总、历史冒险回看、图鉴承接(冒险中数据回看的唯一承接处,P-C7 裁决) | 范围草案·待评审 |
|
||||
| `锻造工坊.md` | 营地武器与护甲成长——武器锻造区(长剑/长枪/法杖)与护甲锻造区(轻甲/重甲/魔法护甲)各三条独立等级路线,钥石几何定价,一次冒险各选一件,满级特效留扩展位本期不做 | v0.5·**设计定案待实装(17 裁点已清零**——2026-10-12 B 组逐裁:1 丙/1a 一冒险一件武器+一件甲/2a 伪裁点消解/3b 敌方攻击缺省普通·克制反向当下生效/6 满级特效本期不做/8a 两区定名;2c·5a 于 v0.4 结案);开工门=17 裁点清零+用户发令,裁点门已满足——现状见 06-待办清单 |
|
||||
| `锻造工坊.md` | 营地武器与护甲成长——武器锻造区(长剑/长枪/法杖)与护甲锻造区(轻甲/重甲/魔法护甲)各三条独立等级路线,钥石几何定价,一次冒险各选一件,满级特效留扩展位本期不做 | v0.5·**设计定案待实装(17 裁点已清零**——2026-10-12 B 组逐裁:1 丙/1a 一冒险一件武器+一件甲/2a 伪裁点消解/3b 敌方攻击缺省普通·克制反向当下生效/6 满级特效本期不做/8a 两区定名;2c·5a 于 v0.4 结案);开工门=17 裁点清零+用户发令,裁点门已满足——现状见待办清单 |
|
||||
| `秘宝.md` | 营地装配、整局冒险生效的角色机能件——操作包络加强(视野/行动点/刷新/挖掘距离等),不出角色战斗属性值、不改战斗结算机制 | 已定稿·跨局件(2026-10-10 自排雷循环迁入——解锁/装配/存档持久全在跨局侧) |
|
||||
|
||||
### 表现与支撑(`systems/表现与支撑/`)
|
||||
|
||||
+29
-33
@@ -2,7 +2,7 @@
|
||||
|
||||
> **一句话定位**:本文定义这个项目的 **Alpha 版本包含哪些东西**——它是里程碑 M3 的完成标准,也是判断"还差什么"的对照表。
|
||||
> **本文只列条目,不写内容**:每样东西是什么、规则怎么走,一律归各自的权威文档,本文只给指路链接。想知道某样东西的定义,请点链接,不要在本文里找解释。
|
||||
> **状态**:**已定稿(v1.10)**——2026-10-13 用户令定稿,此后十次升版的明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **状态**:**已定稿(v1.12)**——2026-10-13 用户令定稿,此后十二次升版的明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **成文**:2026-10-13。
|
||||
|
||||
## 本文怎么用
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
四个循环下的全部子系统。分界与职责见 [02-系统清单](02-系统清单.md),本文不重复。
|
||||
|
||||
Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战斗);跨局循环等非玩法面不新增内容,新内容排 Beta。例外=营地交互重做五卡,2026-10-21 用户裁列入 Alpha(见「三 · 界面」表)。
|
||||
|
||||
### 排雷循环
|
||||
|
||||
| 条目 | 事实源 | 现状 |
|
||||
@@ -37,7 +39,7 @@
|
||||
| 敌人系统 | [敌人系统](systems/战斗循环/敌人系统.md) | 在役 |
|
||||
| 增益状态 | [增益状态](systems/战斗循环/增益状态.md) | 在役 |
|
||||
| 地图战斗资源(格面资源) | [地图战斗资源](systems/战斗循环/地图战斗资源.md) | 在役 |
|
||||
| 战斗表现(动画与演出) | [战斗表现](systems/战斗循环/战斗表现.md) | 待裁 |
|
||||
| 战斗表现(动画与演出) | [战斗表现](systems/战斗循环/战斗表现.md) | 在役(Alpha 判据=受击闪白/死亡不瞬间消失/高光行/逐行排播四条可读;正式演出设计与资产终稿排 Beta,不属 Alpha 完成判据) |
|
||||
|
||||
### 冒险循环
|
||||
|
||||
@@ -45,8 +47,8 @@
|
||||
|---|---|---|
|
||||
| 冒险结构 | [冒险结构](systems/冒险循环/冒险结构.md) | 在役 |
|
||||
| 关卡节点地图 | [关卡节点地图](systems/冒险循环/关卡节点地图.md) | 在役(待重构,见下一条) |
|
||||
| 关卡节点图重构(增加可选节点) | [关卡节点地图](systems/冒险循环/关卡节点地图.md) | 待做(须先做文档设计,不可直接开发) |
|
||||
| 关卡节点图生成 | [关卡节点图生成](systems/冒险循环/关卡节点图生成.md) | 在役 |
|
||||
| 关卡节点图重构(增加可选节点) | [关卡节点地图](systems/冒险循环/关卡节点地图.md)+[关卡节点图生成](systems/冒险循环/关卡节点图生成.md) | 待做(文档设计已闭合,待写卡;单独成批,2 周时间盒;优先全量 v2.0,第一周结束若多终点与地图屏明显超载则分两步:先三关基础分叉+单终点可玩;出口判据见两份文档的验收标准,另加旧模板清理与真人目检选路有取舍) |
|
||||
| 关卡节点图生成 | 参数化长图:产出规格与生成参数 | 待做(规格 v1.0 已定稿;生成器实现与上一条同一批) |
|
||||
| 难度曲线与 Boss | [难度曲线与Boss](systems/冒险循环/难度曲线与Boss.md) | 在役 |
|
||||
| 奇物 | [奇物](systems/冒险循环/奇物.md) | 在役 |
|
||||
|
||||
@@ -58,7 +60,7 @@
|
||||
| 天赋树 | [天赋树](systems/跨局循环/天赋树.md) | 在役 |
|
||||
| 秘宝 | [秘宝](systems/跨局循环/秘宝.md) | 在役 |
|
||||
| 锻造工坊 | [锻造工坊](systems/跨局循环/锻造工坊.md) | 在役(实装批 2026-10-12 入库;满级特效按 2026-10-12 裁 6 留扩展位、本期不做,不碍在役判定) |
|
||||
| 生涯 | [生涯](systems/跨局循环/生涯.md) | 在役(期一;期二、期三待裁) |
|
||||
| 生涯 | [生涯](systems/跨局循环/生涯.md) | 在役(期一;期二、期三排 Beta,不属 Alpha) |
|
||||
|
||||
### 表现与支撑
|
||||
|
||||
@@ -91,7 +93,6 @@
|
||||
| 天赋树节点集 | `meta_tree.tres` | 在役 |
|
||||
| 锻造工坊路线行族 | `forge.tres` | 在役(定价 provisional 归 M4;表名订正与入库依据见 git 提交史) |
|
||||
| 混编遭遇行集 | `enemy.tres` | 在役(首批;第二批移挂 Beta,裁决与明细见 git 提交史) |
|
||||
| 第四关 | —— | 待裁 |
|
||||
|
||||
---
|
||||
|
||||
@@ -101,7 +102,8 @@
|
||||
|---|---|---|
|
||||
| 主菜单 | —— | 在役 |
|
||||
| 开始覆盖层 | —— | 在役 |
|
||||
| 营地(天赋树 / 锻造工坊 / 秘宝匣 / 图鉴) | [camp](ux/camp.md) | 在役(锻造柜台占位面板注已解除;柜台贴图占位归「五 · 表现」UI 贴图重构批行) |
|
||||
| 营地(天赋树 / 锻造工坊 / 秘宝匣 / 图鉴) | [camp](ux/camp.md) | 在役(锻造柜台占位面板注已解除;柜台贴图占位随营地交互重做列入 Alpha,见下行) |
|
||||
| 营地交互重做(五卡:存档制数据层/主菜单三入口/选档界面/营地场景图/Esc 浮窗与营地顶栏) | [camp](ux/camp.md)+[需求澄清](../research/需求澄清-营地交互重做-2026-10-18.md) | 待做(2026-10-21 用户裁:五卡整体列入 Alpha,含营地表现优化——可点击场景图/顶栏/Esc 浮窗。五卡已写、待编排;卡④营地场景图为最大件,挂三个用户侧前置未销号:美术渠道 A/B、点选反馈设计稿、参照物。出口=五卡各自验收标准通过+营地屏用户目检通过) |
|
||||
| 生涯页 | [生涯](systems/跨局循环/生涯.md) | 在役(期一) |
|
||||
| 关卡地图屏 | [level-map](ux/level-map.md) | 在役 |
|
||||
| 战斗屏 | [battle](ux/battle.md) | 在役 |
|
||||
@@ -109,13 +111,13 @@
|
||||
| 关卡结算屏 | [level-clear](ux/level-clear.md) | 在役 |
|
||||
| 三选一(奖励节点 / 通关) | [node-reward](ux/node-reward.md) | 在役 |
|
||||
| 商店 | [shop](ux/shop.md) | 在役 |
|
||||
| 状态面板(局内) | [06-待办清单](06-待办清单.md) | 部分在役(在役半=面板本体——迭代 17 卡③实装、2026-10-13 由「暂停层」改名;未结半=2026-10-16 用户令瘦身——去跨关解锁内容〔秘宝匣解锁态 12 行概览 / 天赋树已购概览〕、只留本局五件〔带的秘宝 / 已获专长 / 当前属性 / 现有 GP / 钥石总量〕;登记面=06「状态面板瘦身」专条) |
|
||||
| 状态面板(局内) | [待办清单](../../待办清单.md) | 部分在役(在役半=面板本体——迭代 17 卡③实装、2026-10-13 由「暂停层」改名;未结半=2026-10-16 用户令瘦身——去跨关解锁内容〔秘宝匣解锁态 12 行概览 / 天赋树已购概览〕、只留本局五件〔带的秘宝 / 已获专长 / 当前属性 / 现有 GP / 钥石总量〕;登记面=待办清单「状态面板瘦身」专条;出口判据=只显示本局五件、两段跨关解锁退役、文案更新、界面探针或断言覆盖、用户目检通过) |
|
||||
| 冒险终局面板 | —— | 在役 |
|
||||
| 图例面板 | —— | 在役 |
|
||||
| 设置面 | [设置与本地化](systems/表现与支撑/设置与本地化.md) | 在役 |
|
||||
| 调试浮窗 | —— | 在役 |
|
||||
| UI 全局主题资源(引擎原生控件接管皮肤) | —— | 在役(迭代 25 · 2026-10-13 入库:game/assets/theme/main_theme.tres 挂 gui/theme/custom) |
|
||||
| UI 按钮状态矩阵(悬停 / 按下 / 禁用 / 键盘焦点补齐) | [06-待办清单](06-待办清单.md) | 在役(迭代 26 入库:五态矩阵逐实例 override 补齐——悬停 modulate 提亮/禁用 α0.45 幽灵化/焦点 INK 2px 内缩 1px 环/危险钮本族按压态;键盘导航批量面归 Beta「棋盘战斗键盘/手柄输入」条连带点③) |
|
||||
| UI 按钮状态矩阵(悬停 / 按下 / 禁用 / 键盘焦点补齐) | [待办清单](../../待办清单.md) | 在役(迭代 26 入库:五态矩阵逐实例 override 补齐——悬停 modulate 提亮/禁用 α0.45 幽灵化/焦点 INK 2px 内缩 1px 环/危险钮本族按压态;键盘导航批量面归 Beta「棋盘战斗键盘/手柄输入」条连带点③) |
|
||||
|
||||
---
|
||||
|
||||
@@ -132,7 +134,7 @@
|
||||
| 机器可读的呈现断言(对比度 / 结构) | `game/tests/` | 在役 |
|
||||
| 分辨率两档(1280×800 / 1920×1080) | [HUD框架](systems/表现与支撑/HUD框架.md) | 在役 |
|
||||
| 绘制调用数达标 | —— | 在役 |
|
||||
| 真人试玩轮次 | [07-体验验证清单](07-体验验证清单.md) | 在役(义务维持;当前轮次未清) |
|
||||
| 真人试玩轮次 | [体验验证清单](../../体验验证清单.md) | 在役(义务维持;当前轮次未清) |
|
||||
|
||||
---
|
||||
|
||||
@@ -143,35 +145,33 @@
|
||||
| 像素美术风格与色板 | [美术圣经](../art/art-bible.md) | 在役 |
|
||||
| 棋盘瓦片与格面资源图标 | [美术圣经](../art/art-bible.md) | 在役 |
|
||||
| 未开格类型微光(五组色相+明度档) | [HUD框架](systems/表现与支撑/HUD框架.md) | 在役(明度档 provisional 值登记见 art-bible 的 git 提交史,复核转正归战斗一屏样张) |
|
||||
| 棋盘观感(第二 / 三关亮度与对比度) | [06-待办清单](06-待办清单.md) | 待做(2026-10-16 用户令编入迭代 30,序位③) |
|
||||
| 立绘清晰度回退评估(是否改用高清图) | [角色设计](../art/character-design.md) | 待做(评估已结——2026-10-14 用户裁维持像素风、升级精细手绘像素、不回退高清图,裁决录=[美术审计-cursor会话存档](../research/美术审计-cursor会话存档-2026-10-14.md) §2.2;余=立绘按新口径重产+大小比例子题用户复核) |
|
||||
| 棋盘观感(第二 / 三关亮度与对比度) | [待办清单](../../待办清单.md) | 待做(先做素材轴/UI 轴对照截图定性,再按定轴修复;出口=用户实机目检第二、三关不再判「看着费劲」) |
|
||||
| 立绘清晰度回退评估(是否改用高清图) | [角色设计](../art/character-design.md) | 待做(评估已结——2026-10-14 用户裁维持像素风、升级精细手绘像素、不回退高清图,裁决录=[美术审计-cursor会话存档](../research/美术审计-cursor会话存档-2026-10-14.md) §2.2;Alpha 范围=主角+三个 Boss 按新口径重产,其余敌种当前可读则保留到 Beta;出口=用户目检八敌体量关系通过且主角与三个 Boss 重产完成) |
|
||||
| 敌人立绘(全敌种) | [角色设计](../art/character-design.md) | 在役 |
|
||||
| 主角立绘三态 | [角色设计](../art/character-design.md) | 在役 |
|
||||
| 状态图标 | [美术圣经](../art/art-bible.md) | 在役 |
|
||||
| 战斗演出链(弹道 / 受击 / 血条 / 灰化) | [战斗表现](systems/战斗循环/战斗表现.md) | 在役 |
|
||||
| 逐行结算排播 | [settlement-summary](ux/settlement-summary.md) | 在役 |
|
||||
| 减动效开关 | [设置与本地化](systems/表现与支撑/设置与本地化.md) | 在役 |
|
||||
| 站桩帧动画 | [06-待办清单](06-待办清单.md) | 待裁 |
|
||||
| 素材升档重产 | [06-待办清单](06-待办清单.md) | 待裁 |
|
||||
| 全局纹理过滤设为邻近采样 | [06-待办清单](06-待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:default_texture_filter=0+平滑渐变件三处显式 LINEAR 白名单,翻转面全枚举闭合) |
|
||||
| UI 贴图重构批(营地与元进度件出终稿) | [美术资产清单](../art/art-assets-checklist.md) | 待做 |
|
||||
| UI 字体补完(拉丁像素字专件 + 字重两档) | [美术圣经](../art/art-bible.md) §7.4 | 待做 |
|
||||
| 战斗一屏样张(界面美术的终稿闸) | [美术圣经](../art/art-bible.md) §1.4 | 待做(2026-10-16 用户令编入迭代 30,序位②) |
|
||||
| UI 氛围层与九宫格切片观感修 | [06-待办清单](06-待办清单.md) | 部分在役(迭代 26 · 2026-10-14:光柱硬边半已修=连续 alpha 渐变;九宫格切片错位半未结——待素材尺寸核对,属美术线连带面) |
|
||||
| UI 元素细节修(滑轨填充 / 图标垫块 / 留白 / 开发件外泄) | [06-待办清单](06-待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:①滑轨填充随迭代 25 注销;余②图标垫块明度差降档/③商店+营地布局修/④诊断钮迁调试浮窗 全落地) |
|
||||
| 站桩帧动画 | [待办清单](../../待办清单.md) | 待做(列入 Alpha 定版前置清单:定版前完成最小版,路线预选拆件驱动;未完成则顺延 Beta;不占功能迭代) |
|
||||
| 全局纹理过滤设为邻近采样 | [待办清单](../../待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:default_texture_filter=0+平滑渐变件三处显式 LINEAR 白名单,翻转面全枚举闭合) |
|
||||
| UI 字体补完(拉丁像素字专件 + 字重两档) | [美术圣经](../art/art-bible.md) §7.4 | 待做(Alpha 只做标题/正文层级调整:字号档差+墨色深浅;正式拉丁像素字专件与第二字重排 Beta) |
|
||||
| 战斗一屏样张(界面美术的终稿闸) | [美术圣经](../art/art-bible.md) §1.4 | 待做(Alpha 硬前置:站桩帧动画的定版前置与主角+三 Boss 重产都依赖它;出口=用户目检通过的战斗屏样张,并产出尺寸档/色板/明度档清单) |
|
||||
| UI 氛围层与九宫格切片观感修 | [待办清单](../../待办清单.md) | 部分在役(光柱硬边已修=连续 alpha 渐变;未结半=九宫格切片错位——Alpha 内做最小修复:核对素材尺寸,必要时重出九宫格素材,全套素材重出排 Beta;出口=结算屏与图例屏错位消失,渲染闸+用户目检通过) |
|
||||
| UI 元素细节修(滑轨填充 / 图标垫块 / 留白 / 开发件外泄) | [待办清单](../../待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:①滑轨填充随迭代 25 注销;余②图标垫块明度差降档/③商店+营地布局修/④诊断钮迁调试浮窗 全落地) |
|
||||
|
||||
---
|
||||
|
||||
## 六、Alpha 不含
|
||||
|
||||
下面这些明确不进 Alpha,写在这里防止回潮。逐条的理由见 [05-不做清单](05-不做清单.md) 与 [06-待办清单](06-待办清单.md)。
|
||||
下面这些明确不进 Alpha,写在这里防止回潮。逐条的理由见 [05-不做清单](05-不做清单.md) 与 [待办清单](../../待办清单.md)。
|
||||
|
||||
**与游戏内容无关的三项**(2026-10-13 用户裁:一律排 Beta)——它们不影响核心循环是否成立,缺了照样能联调、能通关,属打磨层:
|
||||
|
||||
| 条目 | 理由出处 |
|
||||
|---|---|
|
||||
| 音频(音乐与音效) | 排 Beta(用户 2026-10-13 裁:与游戏内容无关);现行零音频资源、零播放代码 |
|
||||
| 教学引导(含教学引导面) | 排 Beta([06-待办清单](06-待办清单.md) 教学引导节,2026-10-04 用户裁改排 Beta;2026-10-13 再确认) |
|
||||
| 教学引导(含教学引导面) | 排 Beta([待办清单](../../待办清单.md) 教学引导节,2026-10-04 用户裁改排 Beta;2026-10-13 再确认) |
|
||||
| 多语言本地化 | 排 Beta(用户 2026-10-13 裁:与游戏内容无关);Alpha 只做最小设置面,不做多语言 |
|
||||
|
||||
**其余不进 Alpha 项**:
|
||||
@@ -189,23 +189,19 @@
|
||||
| 遗物式构筑协同 | [05-不做清单](05-不做清单.md) |
|
||||
| 战后自动回复 | [05-不做清单](05-不做清单.md) |
|
||||
| 反雪球机制 | [05-不做清单](05-不做清单.md) |
|
||||
| 键盘 / 手柄输入 | [06-待办清单](06-待办清单.md)(排期归 Beta) |
|
||||
| 数值统一标定(M4 数值批) | 2026-10-09 里程碑立规:M4 = Beta 阶段、M3 未由用户明令关闭前不排期(用户 2026-10-07 令移出迭代 18;2026-10-16 确认此裁长期有效)——06 各条「已编入 Alpha 收官 M4」措辞系旧口径残留 |
|
||||
| 键盘 / 手柄输入 | [待办清单](../../待办清单.md)(排期归 Beta) |
|
||||
| 数值统一标定(M4 数值批) | 2026-10-09 里程碑立规:M4 = Beta 阶段、M3 未由用户明令关闭前不排期(用户 2026-10-07 令移出迭代 18;2026-10-16 确认此裁长期有效)——待办清单各条「已编入 Alpha 收官 M4」措辞系旧口径残留 |
|
||||
| 连刷 / 换图资金跨战斗携带 | [05-不做清单](05-不做清单.md) |
|
||||
| 战斗节点地图类型差异化(更多地图主题) | 排 Beta 版本([playnote](../../playnote.md),2026-10-13 裁) |
|
||||
| 第四关 | 排 Beta;全库无设计承载,N>3 走扩展批流程([03-内容规模](03-内容规模.md)) |
|
||||
| 生涯期二(历史冒险回看)与期三(图鉴) | 排 Beta;Alpha 只保留期一([生涯](systems/跨局循环/生涯.md)) |
|
||||
| 素材升档重产 | 排 Beta;原始动机已由八敌逐键定档解决,剩余提灯候选随 Beta 资产批重议([待办清单](../../待办清单.md)) |
|
||||
| 元进度件贴图终稿 | 排 Beta;元进度图标在 Alpha 保留占位,不碍在役判定([美术资产清单](../art/art-assets-checklist.md))——营地柜台贴图已随营地交互重做列入 Alpha(见「三 · 界面」表) |
|
||||
|
||||
---
|
||||
|
||||
## 七、未决项
|
||||
|
||||
下面这些**要不要进 Alpha 还没定**,按上面的表逐条对应。每一项都需要用户裁定「进 / 不进 / 换成什么」,裁定后填回对应表格的现状列,本节的该行随即删除。
|
||||
|
||||
| # | 未决项 | 出现在 |
|
||||
|---|---|---|
|
||||
| 1 | 战斗表现(动画与演出)算不算已经达标 | 一 · 战斗循环 |
|
||||
| 2 | 生涯页期二(历史冒险回看)与期三(图鉴承接)进不进 Alpha | 一 · 跨局循环 |
|
||||
| 3 | 第四关进不进 Alpha | 二 · 内容量 |
|
||||
| 4 | 站桩帧动画进不进 Alpha | 五 · 表现 |
|
||||
| 5 | 素材升档重产进不进 Alpha | 五 · 表现 |
|
||||
无。
|
||||
|
||||
---
|
||||
@@ -1,214 +1,128 @@
|
||||
# 关卡节点图生成
|
||||
|
||||
> 一句话:单关节点图怎么从固定配置变成逐局随机——图模板池、抽签与实例化
|
||||
> 状态:**v0.5 台阶一已实施**(迭代 5 T5,2026-09-28 落 master 前挂 worktree m3-iter5-20260928——15 张骨架+node_map 扩签+首次冒险固定脚本+池内全模板校验+MC 分层出数,冒烟 576/0;文档本节规则与实现一致性以本批落地为准,台阶二/三仍后置)
|
||||
> 旧版参考:无(旧体系未单独成文;事项自 2026-09-20 立项,池重生成已落地〔2026-09-29 返工批,二轮目检 15/15 过〕,原 06 条目已完成移除)
|
||||
> 一句话:一关的节点图从哪来——生成器按参数长出一张合法图
|
||||
> 状态:现行为 v1.1
|
||||
|
||||
## 设计目的
|
||||
|
||||
现行每关的节点图是关卡表里写死的一行行集——玩家每一局走的是完全相同的图。对照参照游戏:杀戮尖塔每层抽一张新地图,哈迪斯的房间编排逐局变化——布局变化本身就是"再来一局"的驱动力的一部分,而本作这块目前是零。
|
||||
|
||||
本文立项把单关节点图从固定配置升级为逐局随机。边界先说死:本文只管**图从哪里来**;图怎么走、走到每个节点发生什么、图合法性怎么校验,全部仍是[关卡节点地图](关卡节点地图.md)的现行规则,本设计对它们零改动——生成侧的职责止于"生产一张合法的图",生产出来的图进的是既有校验、既有行进、既有兑付。
|
||||
固定模板的图太小:第一关一条链零选择,第二三关网状但岔路兑付差不多,「可选节点太少」换画法解决不了。本文把图的来源换成参数化生成——图更大、岔口更多、每条路通向不同的东西,走哪条路决定打谁;规模随关卡推进生长,层高、宽度、节点数逐关递增且增幅变大。改形态=改参数不改代码。
|
||||
|
||||
## 系统范围
|
||||
|
||||
边界这样划:图模板与模板池是内容资产(与敌人表行、棋盘参数同级的配置内容),抽签是玩法随机(消费主种子派生流),实例化的产物就是关卡表行集——行集内容的责任方仍是[冒险结构](冒险结构.md),本文管的是行集**怎么被生产出来**。
|
||||
|
||||
负责:
|
||||
|
||||
| 负责 | 说明 |
|
||||
|---|---|
|
||||
| 图模板的结构定义 | 一张模板有哪些组成部分、满足什么条件才算一张合法模板 |
|
||||
| 模板池与关卡表接线 | 每关从哪个池抽、池怎么随关卡表配置 |
|
||||
| 抽签时机与随机流 | 什么时候抽、消费哪条封闭流、可复现域怎么覆盖 |
|
||||
| 实例化与校验交接 | 模板落成行集,交给既有的图合法性校验 |
|
||||
| 台阶路线与测量对账 | 三级台阶的推进条件;模拟读数不被随机污染的口径 |
|
||||
| 图从哪来 | 按本关参数档长出一张节点图,交付给关卡节点地图 |
|
||||
| 产出规格 | 什么算一张合法的图——产出约束的唯一陈述处 |
|
||||
| 生成参数 | 五个旋钮、三套关卡参数档与生长曲线 |
|
||||
| 节点类型配比 | 节点类型分布怎么掷、商店位这类摆放约束 |
|
||||
|
||||
不负责:
|
||||
|
||||
| 不负责 | 归谁 |
|
||||
|---|---|
|
||||
| 节点图怎么走、选路、兑付触发、收束点 | [关卡节点地图](关卡节点地图.md)(现行规则零改动) |
|
||||
| 图合法性校验的检查项与算法 | [关卡节点地图](关卡节点地图.md)(LevelTable.validate 现行——生成侧只是它的上游供给方) |
|
||||
| 池里放几张模板、每张模板里放什么遭遇数值 | [冒险结构](冒险结构.md)(行集内容责任方)与[难度曲线与Boss](难度曲线与Boss.md)(数值所有权) |
|
||||
| 战斗节点内部的敌人配置与初始化 | 战斗循环(节点编号寻址口径零改动) |
|
||||
| 节点图的画面呈现 | [HUD框架](../表现与支撑/HUD框架.md)(节点态照旧读图,零新呈现语义) |
|
||||
| 图怎么走、每类节点到达时兑付什么 | 关卡节点地图 |
|
||||
| 节点上具体摆什么料(敌人行集、奖励内容) | 敌人系统、冒险结构等各 owner 文档 |
|
||||
| 图的画面呈现与多终点标记 | level-map、HUD 框架 |
|
||||
| 生成算法本身 | 代码——本文只给产出规格与执行边界,图怎么长出来是实现的事 |
|
||||
|
||||
## 在核心循环中的位置
|
||||
|
||||
```
|
||||
冒险结构 ──开始关卡──▶ 关卡节点图生成(抽模板 → 实例化行集)──行集──▶ 关卡节点地图(校验 → 选路行进)
|
||||
```
|
||||
冒险结构 ──开始关卡(关卡序号+参数档)──▶ 关卡节点图生成:长出本关的图
|
||||
──图交付──▶ 关卡节点地图:选路推进 ──关卡结束──▶ 冒险结构
|
||||
|
||||
[核心循环](../../01-核心循环.md)里"关卡:节点链逐节点推进"这一段之前,多了一步"这一关的图是哪张"的抽签——本系统就站在这一步。它向上接冒险结构的开始关卡请求,向下把实例化好的行集交给关卡节点地图;生成之后的一切(校验、行进、兑付、收束)与本系统无关。
|
||||
生成站在冒险结构与关卡节点地图之间:接「第几关、用哪套参数档」,交「一张合法图」。图一旦交付生成即退场,走图的事全归关卡节点地图。
|
||||
|
||||
## 核心规则
|
||||
|
||||
### 概念三件套:图模板、模板池、图模板抽签
|
||||
### 图的规格:什么算一张合法的图
|
||||
|
||||
**人话阶段**:本系统只有三个概念,弄清它们谁包含谁,整个系统就读懂了。
|
||||
合法图是一张岔路发散式道路系统:主路+支路,宽度由分叉自然长出,多终点时提前承诺归属。产出约束五条,每张产出图都必须满足:
|
||||
|
||||
**图模板**(本文含义:一张手工编写的合法节点图骨架——一个节点集合〔每个节点带类型标注与关卡内唯一编号〕加一个连线集合)是内容资产,不是运行期生成物。**实例化**(本文含义:把模板原样照抄落成该关的行集——节点集合与连线进关卡表行,零变换)之后,它就是关卡节点地图要加载的那张图。
|
||||
|
||||
| 概念 | 一句话定义 | 归谁 |
|
||||
|---|---|---|
|
||||
| 图模板 | 手工编写的合法节点图骨架;实例化即行集 | 本文(结构规则);内容归冒险结构 |
|
||||
| 模板池 | 关卡表逐关行配置的同关可抽模板集合,池大小 N(N≥1) | 本文(规则);配置承载归数据配置 |
|
||||
| 图模板抽签 | 关卡开始时从池中均匀抽一张 | 本文(时机与流);流基础设施归数据配置 |
|
||||
|
||||
模板池挂在关卡表逐关行上:行集从"单一节点集合"扩为"模板池"。**N=1 的池里只有现行那张图,逐局抽签退化回固定图**——这是**回滚档**(本文含义:任何一关把池缩回一张模板,该关行为就与今日固定配置逐位等价的保险形态)。整个设计因此可增量落地:先全关 N=1,再逐关扩池。
|
||||
|
||||
抽签消费主种子派生的统一随机流,走扩签通道新立用途标签 `node_map`(统一流口径已定——2026-09-27 两轮复裁落地;标签不参与派生、只进事件日志)。
|
||||
|
||||
**出度上限与层宽(2026-10-01 用户拍板 T17 目检)**:单节点出边数(出度)**最大为 2**——加载校验第七检(`fanout_violation` 加载致命)。规则连带(数学约束):出度 ≤2 + 入口唯一下,BFS 层宽上限 = **4**(层宽序列上限 1,2,4;2026-09-29 旧规则「层宽上限 5」与出度约束不相容,随本拍板废止)。现行池 = `tools/t17_fanout2_rebuild.py` 按两规则重建(五骨架:宽廊双干〔现行〕/三干中廊/宽廊斜岔/三干宽门/窄廊短支——层宽序列 1-2-4-1-1 / 1-2-3-2-1 / 1-2-2-2-1,最大层宽 4 应用足)。
|
||||
|
||||
**内容纪律**:每张新模板等于一个小内容批——模板里每个战斗与 Boss 节点都必须在敌人与棋盘生成的数值表里有精确行,义务从冒险结构加关纪律的"每关一行集"细化到"每模板一行集";冒烟覆盖义务同批扩为遍历池内全部模板校验,不只抽中的那张。
|
||||
|
||||
### 抽签时机与可复现
|
||||
|
||||
**人话阶段**:什么时候抽、抽完怎么保证复盘对得上,是本系统接入现有随机纪律的两件事。抽签时点 = **开始关卡处理内、加载节点图之前**,逐关各抽一次(不是冒险开始一次抽齐三关)——抽签时机贴着"这一关的图要用"的时点,主种子的复现域天然覆盖。
|
||||
|
||||
```
|
||||
冒险结构(开始关卡:关卡序号、当前生命、生效上限)
|
||||
→ 关卡节点图生成:按关卡序号取模板池 → 抽一张(图模板抽签流,恰一 scope)
|
||||
→ 实例化为行集 → 交图合法性校验(关卡节点地图现行,零改动)
|
||||
→ 关卡节点地图加载行集 → 呈现选路停点(现行流程原样续跑)
|
||||
```
|
||||
|
||||
可复现域沿用既有口径:主种子 + 全局取数计数器(2026-09-27 统一流;旧表述系并流前口径)(语义权威归[冒险结构](冒险结构.md)「失败重开与主种子」节,本文不复述)。抽签每次消费恰一个带编号的 scope,计数器随既有快照契约入档——[存档序列化](../表现与支撑/存档序列化.md)零新增域。同主种子同操作序列的两局,逐位同一张图。
|
||||
|
||||
教学位随骨架走:各骨架的"第一场战斗节点"沿用教学位纪律(首遇位固定数量不加骰)——教学位属性是骨架的内容标注,不跟节点编号走(拍板点①裁决后第一关也参与抽签,例外见下节)。
|
||||
|
||||
### 首次冒险的固定脚本(拍板点①裁决写入文档)
|
||||
|
||||
**人话阶段**:教学关不该第一局就随机——玩家第一次玩,地图必须是设计好的那一张。拍板点①裁决(2026-09-27):**全部关卡都参与抽签,唯独玩家的第一次冒险全程走固定脚本**。这也是参照游戏的通用做法(首次冒险特殊脚本)。
|
||||
|
||||
| 事项 | 规则 |
|
||||
| 约束 | 内容 |
|
||||
|---|---|
|
||||
| 判定标志 | 存档域新增布尔位「首次冒险已进行」(owner=存档序列化,实施批落 schema) |
|
||||
| 判定时机 | 开始新冒险时读标志:false = 首次脚本模式;true = 正常抽签 |
|
||||
| 脚本模式内容 | 全关零抽签——每关直接取池内的现行固定模板(即回滚档成员),逐位等价今日行为,无需新做脚本内容 |
|
||||
| 置位时机 | 冒险收束点置位(完成/失败/放弃三结局同写)——存档级属性冒险期只读、写入只在收束挂点(既有纪律) |
|
||||
| 道路系统 | 图=主路+支路;除岔口节点(两条以上去路的节点)外每个节点恰一条去路;岔口去路 2–3 条 |
|
||||
| 宽度=分叉生长 | 不摆平行层;同深度节点数由岔口自然长出(1→2→3→4……) |
|
||||
| 层高=沿路步数 | 入口到终点的最长路径步数;第一关 ≥6,逐关递增且增幅变大 |
|
||||
| 出口归属锁定 | 多终点时由大岔口把图分成互不连达的分支区域,各区域只通向自己的终点;每个终点前一层节点的去路集合恰含一个终点——选路=提前承诺打谁 |
|
||||
| 战斗节点保底 | 奖励/恢复/商店占位后战斗节点量不被挤没(保底数进调参表) |
|
||||
|
||||
两条边界:首次冒险中途退出游戏(未到任何收束点)则标志未写,下次新冒险仍走脚本——教学重放,可接受;首次冒险内死亡或放弃即结算置位,第二次冒险起抽签。
|
||||
结构铁律:连线只朝前进方向(无环);入口、终点都可多个(个数=参数档),每条路径恰好从一个入口出发、恰好到达一个终点。
|
||||
|
||||
测量口径:模拟试玩与数值验证夹具恒置标志为 true(永远走抽签路径)——首次脚本分支不进任何读数。
|
||||
执行边界(实现只须遵守这几条,怎么长图是代码的事):
|
||||
|
||||
### 三级台阶路线
|
||||
| 边界 | 内容 |
|
||||
|---|---|
|
||||
| 不合格产出禁止进玩法 | 产出不过产出规格即无效,无效图绝不上给玩家 |
|
||||
| 无兜底图 | 不设固定兜底图——软回退一律禁止 |
|
||||
| 参数无解即中止 | 参数档本身给不出合法图=配置错误,致命中止 |
|
||||
| 同种子同图 | 同种子+同参数档产出同一张图;随机只经 RngService 统一流 |
|
||||
|
||||
**人话阶段**:2026-09-21 呈报的三级台阶(图模板抽签 → 骨架固定+属性随机 → 全程序生成)随本批立项成文为正式路线。每一级都比上一级多一点随机,每一级都必须过同样两条不变量:①产物过图合法性校验(检查项口径归关卡节点地图,本文不复述);②商店房"每关恰 1、关底 Boss 前一位必经位"的编排约束在生成侧成立——冒烟断言照旧把关,校验零新增。
|
||||
### 生成参数:五个旋钮与三套关卡参数档
|
||||
|
||||
| 台阶 | 内容 | 前置条件 |
|
||||
|---|---|---|
|
||||
| 一:图模板抽签 | 每关模板池 N≥1,逐局从池中抽一张手工模板实例化 | 测量对账条款先行(见下节);每张新模板按内容纪律配齐数值行 |
|
||||
| 二:骨架固定+属性随机 | 模板拓扑定型,节点类型在**候选槽位**(本文含义:模板给某个可变位列出的允许节点类型集合,实例化时从集合内均匀掷一个——例:某槽位允许{奖励,恢复},掷中哪个就是哪个)内掷定 | 台阶一完成;商店房所在位排除出候选槽位(保必经位) |
|
||||
| 三:全程序生成 | 读参数(深度、分支密度、节点类型配比)直接构造图,构造失败重掷(重掷预算同棋盘生成 max_gen_attempts 纪律) | 台阶二完成;图合法性校验作最终守门 |
|
||||
| 旋钮 | 管什么 |
|
||||
|---|---|
|
||||
| 层深 | 图有多高(沿路步数) |
|
||||
| 分支密度 | 岔口多少、宽度长多快 |
|
||||
| 配比 | 节点类型分布(见下节) |
|
||||
| 入口个数 | 图从几个点进场 |
|
||||
| 出口个数 | 图通向几个终点(1=单出口;2 及以上=多出口+归属锁定;大岔口自第二关起进入参数档,第一关出口恒为 1) |
|
||||
|
||||
台阶二的随机量入账:候选槽位掷定消费同一条图模板抽签流的后续 scope,计数器域照旧覆盖,可复现性零损失。台阶三的参数取值域届时随本文修订登记,本草案不预设。
|
||||
每关一套参数档(L1/L2/L3),生成器只吃参数档、不认关卡号。生长曲线:层高、宽度、节点总数、路径长度逐关递增且增幅变大;决策点密度逐关递增;第一关即含岔口——第一关也要有选择。单链是分支密度取下限的合法退化形态,不作为三关参数档的取值。
|
||||
|
||||
台阶一用一句话说:**给每关手工做几张不同的地图骨架,每局开始时随机抽一张用**(用户裁决路线确认:骨架不完全固定、多份随机调取)。玩家看到的是同一关每局长得不一样——这局岔路多、下局一条窄道——但每张都是人工画好、审过合法性的图,不是程序乱长。台阶一不做两件事:房间类型不掷骰(某房间是奖励房还是恢复房仍由骨架定死——那是台阶二),程序不算图(那是台阶三)。首版池规模 = 每关 5 张(拍板点④),其中 1 张 = 现行固定图。
|
||||
### 节点类型配比:类型分布怎么掷、内容归谁
|
||||
|
||||
### 测量对账:别让随机污染读数
|
||||
配比是生成旋钮:战斗/奖励/恢复/商店各占多少由参数档定,生成器掷出分布落在图上成为节点类型;配比只管非终点节点,终点恒为 Boss、不进配比。节点类型在可见范围内对玩家公开(可见性规则归关卡节点地图)——看得见的类型才谈得上提前规划。
|
||||
|
||||
**人话阶段**:2026-09-21 呈报暂缓的三理由之一是"污染 MC 读数"——模拟试玩与数值验证的通关率是按固定图测的,图一随机,读数波动就混进了布局差异。本节是消解条款,三条:
|
||||
摆放约束(三条):
|
||||
|
||||
1. **分层出数**:模拟试玩与数值验证工具的读数按「关卡 × 图模板」分层记录;混合池的整体通关率 = 各模板条件通关率的加权平均(权重 = 抽中概率,均匀池即算术平均)。
|
||||
2. **基线可比**:基线批(与基线数据逐项比对、重投)跑固定模板口径——把池临时缩到 N=1(回滚档)或固定抽签 scope,保证新旧基线在同一张图上对话。
|
||||
3. **实施门**:随机量(扩池)上线排在[难度曲线与Boss](难度曲线与Boss.md)曲线调参批完成之后——现行行值 provisional 收带前叠加布局随机,归因会失真;抽签机制本身可先行落地(池保持 N=1 行为零变化,见拍板点③选项 C)。
|
||||
| 约束 | 内容 |
|
||||
|---|---|
|
||||
| 商店位 | 每个终点前一位恰一家商店——每名玩家一局必经恰一家店 |
|
||||
| 岔路取舍·下一跳 | 一个岔口分出的各条去路,下一节点类型互不相同——下一跳必在可见范围,是决策的可见锚 |
|
||||
| 岔路取舍·支路构成 | 一个岔口分出的各条支路,整条支路的节点类型构成互不相同——每条支路至少有一个别的支路没有的类型(例:一路有恢复节点、一路有奖励节点) |
|
||||
|
||||
另两条暂缓理由顺带消解:**类型集未稳**——五类节点封闭枚举已随商店房批定稿,扩类走文档修订的既有通道对模板同样适用(模板 schema 跟随枚举走);**内容量不足**——台阶一的模板就是内容批,按内容批节奏逐张走,不设批量生产目标。
|
||||
内容归各 owner:战斗节点打谁=敌人系统按深度带配池;奖励/恢复/商店的兑付语义=关卡节点地图与各 owner 文档。
|
||||
|
||||
## 拍板点(登记 [06-待办清单](../../06-待办清单.md),顺带更新条目)
|
||||
|
||||
| # | 拍板点 | 状态 |
|
||||
|---|---|---|
|
||||
| ① | 第一关(教学关)是否参与抽签 | **已裁决(2026-09-27)**:参与——仅首次冒险走固定脚本,见「首次冒险的固定脚本」节 |
|
||||
| ② | `node_map` 随机流增流 | **已裁决(2026-09-27 两轮复裁)**:统一用一个流——全游戏 rng 架构批先行(评估书已交付,2026-09-27),本特性实施排队其后 |
|
||||
| ③ | 台阶一实施时点 | **已裁决(2026-09-27)**:机制先行骨架随后——特性内先落抽签机制(池暂 N=1 行为零变化)、骨架内容批随后扩池;整体仍排在架构批落地之后 |
|
||||
| ④ | 首版池规模 | **已裁决(2026-09-27)**:每关 5 张起步(调参表已投值) |
|
||||
| ⑤ | 抽签时点 = 关卡开始逐关抽 | **已裁决(2026-09-27)**:确认 |
|
||||
|
||||
#### 证据面 A:随机流与「增流」是什么(拍板点②的背景)
|
||||
|
||||
**人话**:本项目的随机数只有一个总入口(随机服务,代码名 rng_service),硬性红线禁止任何代码绕开它自己掷骰——"同一个种子+同样操作序列 = 逐位相同的一局"靠这条保证,调试复盘和模拟对账都依赖它。总入口内部按用途分账:每种用途占一栏,叫一条**流**(本文含义:随机服务内部按用途分开记账的一栏独立随机序列,各有各的编号,互不串账)。现行清单七条:雷图生成、奖励抽取、敌人数量掷骰、命中掷点、出招摇号、变异布置、GP 掉落——清单是封闭的,新用途必须在清单里开新栏。
|
||||
|
||||
图模板抽签是一笔新用途的随机账,要在清单里新开一栏(拟名 `node_map`)——「增流」就是这个动作:**给封闭流清单加一条新流**。它之所以要拍板:加流有固定成本(改代码 + 补一组校验用黄金向量 + 加冒烟断言,纪律规定的三件套),不是随手配置。
|
||||
|
||||
| 选项 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| A 增流(原荐) | 清单加 `node_map` 栏,抽签独立记账 | 三件套成本;图的抽签可独立复现,与其它系统零耦合 |
|
||||
| B 借现有流 | 从某条现有流取号 | 省三件套;但这笔账和那个系统的账搅在一起——重放对方系统的随机序列会被抽签挤位,复现口径变含糊,不推荐 |
|
||||
|
||||
**裁决注记(2026-09-27)**:用户两轮复裁后终选「统一用一个流」(全面执行),执行路径 = 全游戏 rng 架构批先行、本特性排队其后。评估书已交付(2026-09-27,git 历史可查)——核心发现:字面目标「单一随机源、逐位可复现」现行架构已满足(七条流系同一主种子的七个盐值分栏而非七个随机源);待裁决项 = 是否进一步并掉分栏计数器(方案甲维持分栏 / 乙物理并流 / 丙并流+全事件日志)。
|
||||
|
||||
#### 证据面 B:台阶一详解与实施时点(拍板点③的背景)
|
||||
|
||||
台阶一(图模板抽签)落地后的样子:
|
||||
|
||||
- **玩家看到什么**:同一关,每局地图的房间连接方式不一样(这局岔路多、下局一条窄道),但每张都是人工画好、审过合法性的图——不是程序乱长。
|
||||
- **做三件事**:①每关 5 张骨架(拍板点④,其中 1 张 = 现行固定图);②开始关卡时抽一张实例化;③首次冒险走固定脚本(拍板点①)。
|
||||
- **不做两件事**:房间类型不掷骰(那是台阶二);程序不生成图(那是台阶三)。
|
||||
- **内容形态**:一个内容批 = 15 张骨架(3 关 × 5)+ 池内全模板加载校验 + 冒烟断言。骨架里只有房间和连线,**不新增敌人数值**——遭遇表按节点编号共用:两张骨架若都在 3 号位开战,3 号位打的敌人是同一套(表键不含骨架维);骨架差异体现在拓扑与房型排布,不体现在"同编号打不同敌人"。要打破这条需给遭遇表键加骨架维,归实施批 schema 拍板。
|
||||
|
||||
实施时点三个选项:
|
||||
|
||||
| 选项 | 内容 | 后果 |
|
||||
|---|---|---|
|
||||
| A 机制+骨架立即全上 | 现在做完整内容批(含扩池到 5 张) | 曲线调参批未完成,数值基线在动;地图一随机,模拟读数的波动归因变难(数值动了还是图动了分不清) |
|
||||
| B 全部等调参批完成(v0.1 原荐) | 机制与内容都排在曲线调参批后 | 读数对账最干净;机制落地要等 |
|
||||
| C 机制先行、骨架随后(原荐) | 先落抽签机制但每关池暂放 1 张现行图(行为零变化),骨架内容批画好后一次扩到 5 张——扩池时点守测量对账实施门 | 机制与内容解耦:扩池前读数零污染,扩池后分层出数条款接管;不用等调参批 |
|
||||
扩展节点类型位:现行四类之外预留扩展位,内容未设计、登记见待办清单「扩展节点类型」条。扩展通道四步=加枚举类型+落兑付实现+调生成配比+走本文档修订。
|
||||
|
||||
## 依赖与输出
|
||||
|
||||
这张表就是删除任何一方时的影响面清单。
|
||||
|
||||
| 方向 | 系统 / 文档 | 接口要点 |
|
||||
|---|---|---|
|
||||
| 上游 | [冒险结构](冒险结构.md) | 开始关卡流程是抽签时点的宿主;主种子与派生流的语义权威;行集内容责任方(模板内容归它) |
|
||||
| 上游 | [数据配置](../表现与支撑/数据配置.md) | 关卡表模板池列的承载与统一读取;node_map 统一流用途标签(扩签已落地 v0.5)与流种子派生公式的基础设施 |
|
||||
| 上游 | [难度曲线与Boss](难度曲线与Boss.md) | 模板内遭遇数值的所有权;曲线调参批完成是台阶一的实施门 |
|
||||
| 下游 | [关卡节点地图](关卡节点地图.md) | 实例化行集的供给方;其图合法性校验与全部行进规则零改动 |
|
||||
| 下游 | [存档序列化](../表现与支撑/存档序列化.md) | 抽签 scope 计数器入既有快照契约(主种子 + 流计数器),零新增域;「首次冒险已进行」布尔位为唯一新增存档字段(实施批落 schema) |
|
||||
| 下游 | [HUD框架](../表现与支撑/HUD框架.md) | 零新语义——节点态照旧读图,模板差异对呈现层透明 |
|
||||
|
||||
## P0 最小闭环(台阶一实施批)——✅ 已落地(v0.5,2026-09-28)
|
||||
|
||||
> 实施前置已解除:统一随机流批已落地(2026-09-27 两轮复裁——统一流+全事件日志);抽签接入形态 = 统一流扩签新用途标签 `node_map`(扩签通道 = 改代码 + 评审)。
|
||||
|
||||
1. 关卡表逐关行扩模板池结构(承载归数据配置,schema 细节随实施批定)。
|
||||
2. 池内全部模板加载期预校验(违规致命,fail-fast 不变)。
|
||||
3. 存档域加「首次冒险已进行」布尔位:开始新冒险读标志(false = 首次脚本模式,全关零抽签取现行固定模板),冒险收束点置位(三结局同写,写入只在收束挂点)。
|
||||
4. 开始关卡处理内抽签实例化(`node_map` 流恰一 scope——拍板点②)。
|
||||
5. 既有图合法性校验照跑零改动;关卡节点地图与战斗侧全链零改动。
|
||||
6. 冒烟断言:抽签确定性(同种子逐位同图)、池内全模板校验、N=1 池与现行固定图逐位等价、首次脚本分支(标志 false = 固定图 / true = 抽签)。
|
||||
|
||||
## P1 / P2 后置
|
||||
|
||||
| 优先级 | 内容 | 后置原因 |
|
||||
|---|---|---|
|
||||
| P1 | 台阶二:骨架固定+属性随机(候选槽位掷定) | 台阶一完成后另批立项;商店必经位的槽位排除规则顺带成文 |
|
||||
| P2 | 台阶三:全程序生成(参数化构造) | 台阶二完成后另批立项;参数取值域与重掷预算顺带修订本文 |
|
||||
| 上游 | 冒险结构 | 开始关卡给出关卡序号与本关参数档 |
|
||||
| 下游 | 关卡节点地图 | 图的消费方:选路、兑付、加载校验(结构规则以本文产出规格为准) |
|
||||
| 协作 | 敌人系统 | 战斗节点遭遇按深度带配池,遭遇坐标=关卡序号+深度带;多终点各配 Boss 行 |
|
||||
| 协作 | 难度曲线与Boss | 图规模(节点数/战斗量)进它的投放节奏账;深度带配池的难度坡归它,本文零数值 |
|
||||
| 上游 | 数据配置 | 参数档数据的承载与统一读取,数值事实源=data/tables |
|
||||
| 协作 | level-map | 呈现只读:图形态随参数变化,画布自适应;多终点各带 Boss 标记 |
|
||||
|
||||
## 调参表
|
||||
|
||||
本系统无已锁数值。台阶一实施后进表:
|
||||
数值事实源=game/data/tables(定值批落表);本表只留取值域与设计意图。
|
||||
|
||||
| 参数 | 域 | 现行值 | 说明 |
|
||||
| 参数 | 取值域 | 设计意图 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 模板池大小 N | int [1,+∞),关卡表逐关行 | 每关 5(2026-09-27 拍板点④;2026-09-28 台阶一实施批落地,3 关 × 5) | N=1 = 回滚档;扩池 = 内容批 |
|
||||
| 重掷预算(台阶三) | 未立项不设值 | — | 台阶三立项时随本文修订登记 |
|
||||
| 层深 L1 | ≥6(步) | 第一关不再是零选择短链——够长才有岔口与取舍 | 已锁下限 |
|
||||
| 层深 L2/L3 | >前一关且增幅变大 | 后期关显著更长,推进感随冒险加深 | provisional |
|
||||
| 岔口去路数 | 2–3 | 每次取舍在 2–3 条里选:少了没选择,多了不过脑 | 已锁 |
|
||||
| 分支密度 | 待定值 | 一局遇到多少次取舍(目标量级=一局十几次,待设计验证) | provisional |
|
||||
| 入口个数 | 1 起 | 多入口给「从哪进场」的第一层选择 | provisional |
|
||||
| 出口个数 | L1 恒 1;L2/L3 1–3 | 多终点让「走哪条=打谁」成立;大岔口自第二关起 | 已锁范围 |
|
||||
| 配比 | 四类占比合计 1,战斗保底优先 | 攻击性/兑付/恢复的节奏配比 | provisional |
|
||||
| 战斗节点保底数 | ≥ 调参值 | 成长位不挤没战斗——战斗是本作核心量 | provisional |
|
||||
| 商店数 | =出口数 | 每局决战前恰一次补给,成长保底是结构事实 | 已锁 |
|
||||
|
||||
## 验收标准(草案——随拍板点裁决完成,实施批落地时按本节验收)
|
||||
## 验收标准
|
||||
|
||||
- 池内全模板加载期通过图合法性校验,违规致命(校验面不只抽中的那张)。
|
||||
- 抽签确定性:同主种子同操作序列逐位同一张图;抽签 scope 可独立回放。
|
||||
- 回滚档成立:N=1 池的关卡与现行固定图逐位等价。
|
||||
- 首次冒险脚本:存档标志 false 时全程固定图(逐位等价现行行集),冒险收束点置位标志恰一次;标志 true 后每局抽签。
|
||||
- 商店房编排约束在每张模板上成立:每关恰 1 个商店房、关底 Boss 前一位必经位。
|
||||
- 零新面:关卡节点地图行进/兑付/收束断言与战斗侧寻址断言全绿,零改动。
|
||||
- 测量分层:模拟读数含「关卡 × 图模板」维度;基线批可固定模板复现。
|
||||
- 内容纪律:每张新模板的战斗与 Boss 节点在敌人与棋盘生成的数值表中有精确行(冒烟遍历池内全模板)。
|
||||
|
||||
## 待办
|
||||
|
||||
本系统无未决待办(结构随机生成池已落地);后续新增待办统一登记 06-待办清单,本节不再逐条登记。
|
||||
| 判据 | 内容 |
|
||||
|---|---|
|
||||
| 产出合格 | 任意参数档产出的图必过产出约束五条+结构铁律;任意入口出发的任意路径都恰好到达一个终点 |
|
||||
| 生长递增 | L1/L2/L3 读数:最长路径步数与节点总数均逐关递增;节点总数增幅 Δ(L2→L3)>Δ(L1→L2) |
|
||||
| 归属锁定 | 多出口图每个终点前一层节点的去路集合恰含一个终点;各分支区域互不连达 |
|
||||
| 必经恰一店 | 任意入口→终点路径经过且恰经过一家店 |
|
||||
| 岔路取舍 | 任意岔口各去路下一节点类型互不相同;各条支路的节点类型构成互不相同 |
|
||||
| 可复现 | 同种子+同参数档=同一张图 |
|
||||
| 无软回退 | 不存在兜底图路径;参数无解即致命中止 |
|
||||
| 战斗保底 | 任意合法产出的战斗节点数 ≥ 保底数 |
|
||||
@@ -1,33 +1,34 @@
|
||||
# 关卡节点地图
|
||||
|
||||
> 一句话:单关内的节点图选路与推进
|
||||
> 状态:已定稿
|
||||
> 状态:现行为 v2.1
|
||||
|
||||
## 设计目的
|
||||
|
||||
一关不是一场战斗,而是由关卡表配置的一组**节点**(本文含义:关卡内移动的步进单位,画在图上就是一个格)连成的一张**节点图**。玩家从一个入口出发,在每个路口看着几条路选一条走——这条路上有要打的仗、有开奖励的房间、有能歇脚的篝火——最后所有路线汇到关底 Boss。关卡节点地图管的就是这张图上的"走":怎么选路、走到每种节点上发生什么、Boss 分出胜负后怎么把这一关的结局交回去。
|
||||
一关不是一场战斗,而是由关卡表配置的一组**节点**(本文含义:关卡内移动的步进单位,画在图上就是一个格)连成的一张**节点图**。玩家从一个入口出发,在每个路口看着几条路选一条走——这条路上有要打的仗、有开奖励的房间、有能歇脚的篝火——最后走到关底 Boss(终点可以有多个,走哪条路决定打谁)。关卡节点地图管的就是这张图上的"走":怎么选路、走到每种节点上发生什么、Boss 分出胜负后怎么把这一关的结局交回去。
|
||||
|
||||
用循环的层次说:冒险是外层骨架,关卡是**中循环**,战斗是**小循环**。本文管中循环——图怎么编排、怎么走、每类节点到达时触发什么;一场战斗内部发生什么全部归小循环(战斗结算与敌人系统),本文把控制流移交给它之后就不再看里面。它是[冒险结构](冒险结构.md)(序列、大奖励、重开)与战斗循环(结算与敌人)之间的行进层。
|
||||
|
||||
## 系统范围
|
||||
|
||||
边界这样划:图上**有哪些节点**(具体摆什么、摆几个)是关卡表的内容,归冒险结构;本文管图**怎么走、走到每步发生什么**,以及一张关卡表行集要满足什么结构规则才算合法的图。战斗内部的一切(敌人怎么配、几只、什么技能池、踩雷负向怎么兑)归战斗循环文档(原"伏击怎么叠"随 2026-09-20 八型批伏击退役改述);本文只负责把"第几关、哪个节点"递过去。
|
||||
边界这样划:图**从哪来**(按参数长图)与**什么算合法图**(产出规格)归[关卡节点图生成](关卡节点图生成.md);图上**具体摆什么料**(敌人行集、奖励内容)归各 owner 系统;本文管图**怎么走、走到每步发生什么**,以及加载期的合法性校验动作(规则语义以关卡节点图生成的产出规格为准)。战斗内部的一切(敌人怎么配、几只、什么技能池、踩雷负向怎么兑)归战斗循环文档(原"伏击怎么叠"随 2026-09-20 八型批伏击退役改述);本文只负责把"第几关、哪个节点"递过去。
|
||||
|
||||
负责:
|
||||
|
||||
| 负责 | 说明 |
|
||||
|---|---|
|
||||
| 节点图结构规则 | 什么算一张合法的关卡节点图(唯一编号、唯一入口、唯一终点、不可回头)——结构校验的规则面 |
|
||||
| 图合法性校验 | 加载期校验动作;合法图的规则语义以[关卡节点图生成](关卡节点图生成.md)的产出规格为准 |
|
||||
| 选路与行进 | 玩家亲手选路前进;兑付后确认、零自动推进(交互形态定稿) |
|
||||
| 节点兑付触发 | 行进到各类型节点时触发对应兑付:战斗移交、奖励入库、恢复请求、商店覆盖层开闭 |
|
||||
| 战斗发起组装 | 组装定位信息(关卡序号 + 节点编号),移交战斗结算开战 |
|
||||
| 战斗发起组装 | 组装定位信息(关卡序号 + 节点深度),移交战斗结算开战 |
|
||||
| 关卡收束点 | Boss 战胜利或英雄死亡时定局:先调战斗结算的关卡收束清空库存,再向冒险结构宣布关卡结束 |
|
||||
|
||||
不负责:
|
||||
|
||||
| 不负责 | 归谁 |
|
||||
|---|---|
|
||||
| 关卡序列与图上摆哪些节点(行集内容、关数) | [冒险结构](冒险结构.md) |
|
||||
| 关卡序列与关数 | [冒险结构](冒险结构.md) |
|
||||
| 图从哪来、节点类型构成与合法图规则 | [关卡节点图生成](关卡节点图生成.md) |
|
||||
| 关卡大奖励、跨战斗生命、失败重开 | [冒险结构](冒险结构.md) |
|
||||
| 战斗内部的一切:敌人配置、数量、技能池、踩雷负向 | [敌人系统](../战斗循环/敌人系统.md)与[战斗结算](../战斗循环/战斗结算.md) |
|
||||
| 同场多敌种(同一节点配多种敌人)的表结构与取值口径 | 敌人系统(战斗侧改键批) |
|
||||
@@ -47,9 +48,9 @@
|
||||
|
||||
### 关卡的形状:分支节点图与五类节点
|
||||
|
||||
一关内部是一张**有方向的节点图**:节点之间用连线(本文含义:节点之间允许前进的通道)连接,玩家沿连线从一个节点走到下一个节点。图有三条结构铁律:连线只朝前进方向(没有回头路,整张图无环);图有一个唯一的入口节点;**关底 Boss 恒为全图唯一的终点**——所有路线最终都汇到它,打赢它这一关就通了。违反任何一条的行集是配置违约,加载期拦截。
|
||||
一关内部是一张**有方向的节点图**:节点之间用连线(本文含义:节点之间允许前进的通道)连接,玩家沿连线从一个节点走到下一个节点。结构铁律的规则语义以[关卡节点图生成](关卡节点图生成.md)的产出规格为准,走图视角三句:连线只朝前进方向(没有回头路,整张图无环);每条路径恰好从一个入口出发、恰好到达一个终点;终点恒为 Boss(终点可以有多个,走哪条路决定打谁)。违反任何一条的行集是配置违约,加载期拦截。
|
||||
|
||||
图上只有五种节点,封闭枚举,扩类一律走文档修订:
|
||||
图上现行五种节点,**可扩展枚举**(扩展通道=加枚举类型+落兑付实现+调生成配比+本文档与[关卡节点图生成](关卡节点图生成.md)同批修订;扩展位见关卡节点图生成配比节):
|
||||
|
||||
| 节点类型 | 机制承诺 |
|
||||
|---|---|
|
||||
@@ -59,13 +60,13 @@
|
||||
| 恢复 | 行进至此向冒险结构发治疗封顶回报;反馈呈现后回到选路 |
|
||||
| 商店 | 行进至此打开商店覆盖层,用 GP(战斗掉落的冒险内货币)购买属性提升;离开后兑付确认回选路;不携带战斗行。**v1.5 兑付换代(pool 模式现行)**:进店从共享池抽 3 展示、买 1 后三件全重抽(未买不保留)、货架买空=空架合法态。**v1.6(2026-10-04 P-C2+Q2 裁/MC-1)**:新增**独立刷新**——基础免费 0 次(天赋/营地侧刷新次数增益位预留)+ GP 付费刷新 ceil(20×1.8^已付费刷) provisional;重抽=共享池同源(shop_draw 同签),推翻本行旧「无独立刷新动作——重抽绑购买」口径;库存与定价归[冒险结构](冒险结构.md)共享池节 |
|
||||
|
||||
商店房的编排规则(2026-09-20 用户拍板):每关**恰 1 个**商店房,位置 = **关底 Boss 前一位**——哈迪斯式的"决战前最后一个补给点":买到的属性还有整段战斗可消费,错过的成长也没有第二家店可补。Boss 前一位同时是**必经位**:图拓扑收束使所有入口→Boss 的路径都穿过它,成长保底成为图结构事实而非玩家自觉。这条位置约束不由加载校验承载——图合法性六检零新增,商店房的"每关恰 1 + 必经"由冒烟断言把关。
|
||||
商店房的编排规则:**每个终点前一位恰一家店**——哈迪斯式的"决战前最后一个补给点":买到的属性还有整段战斗可消费,错过的成长也没有第二家店可补。终点前一位同时是**必经位**:每名玩家一局恰好必经一家店,成长保底是图结构事实而非玩家自觉。这条位置约束不由加载校验承载,由冒烟断言把关(摆放规则语义见[关卡节点图生成](关卡节点图生成.md)配比节)。
|
||||
|
||||
每个节点在关卡内领一个**节点编号**(本文含义:关卡内不重复的序号,用来当战斗的定位地址;数据键沿用切片字段 `node_depth`)。Boss 的编号恒为全图最大——"最深处的房间是关底"这条直感由此保留。单链特例:一张每步只有一条连线的图就是一条链,此时节点编号等于它前面走过的战斗场数。第一关行集就是单链特例,切片代码零改动运行;分支图行集已落地(v1.0.1 起;模板池见[关卡节点图生成](关卡节点图生成.md))。
|
||||
每个节点在图上有**节点深度**(本文含义:从入口沿路走过来的步数),用来当战斗的定位地址——遭遇坐标=关卡序号+节点深度,敌人表按**深度带**(深度区间)配池,寻址语义见[敌人系统](../战斗循环/敌人系统.md)。"最深处是关底"的直感由深度承载:终点恒在各路径最深处。单链(每步只有一条连线)是合法退化形态,节点深度等于它前面走过的战斗场数;三关参数档都含岔口,第一关不再是单链。图的来源见[关卡节点图生成](关卡节点图生成.md)。
|
||||
|
||||
呈现侧口径(2026-09-17 用户拍板):节点图的画面表达走**方块地牢观感**——节点画成地牢房间的方块、连线画成通道——但逻辑拓扑就是本文的节点图,不产生任何额外选路或探索语义。关卡画面本体即节点链进度的呈现载体(选路覆盖层的常驻形态,节点态直接读图:已过暗金 / 当前金 / 候选下一跳通道亮金 / Boss 封印黑描边(旗朱→封印紫→封印黑 #000000,2026-09-28 用户拍板现行)),不另设进度条组件;视觉规格归 [HUD 框架](../表现与支撑/HUD框架.md)。
|
||||
|
||||
**商店覆盖层呈现**(呈现 spec:[design/gdd/ux/shop.md](../../ux/shop.md) v1.0,2026-09-30;**v1.5 注记**——pool 模式货架面=共享池三选一同构卡〔进店抽 3/买 1 全重抽/空架售罄态〕,与三选一奖励同一交互语言;legacy 固定三件面随对照模式保留。**v1.5-a 注记(2026-10-04 用户裁)**:~~库存余量就近~~ **库存余量不呈现**(货架卡不带「库存余 N/∞」字样——库存语义仍在共享池令牌库,仅呈现退役;独立刷新机制另见 06「商店独立刷新机制」条):兑付 = 打开商店覆盖层(模态)——货架卡陈列、GP 余额读数、购买/离开;离开后兑付确认回选路。本节锁编排面(何时开/何时关/回选路),呈现细节归 spec;定价数值归[难度曲线与Boss](难度曲线与Boss.md)(shop.tres)。
|
||||
**商店覆盖层呈现**(呈现 spec:[design/gdd/ux/shop.md](../../ux/shop.md) v1.0,2026-09-30;**v1.5 注记**——pool 模式货架面=共享池三选一同构卡〔进店抽 3/买 1 全重抽/空架售罄态〕,与三选一奖励同一交互语言;legacy 固定三件面随对照模式保留。**v1.5-a 注记(2026-10-04 用户裁)**:~~库存余量就近~~ **库存余量不呈现**(货架卡不带「库存余 N/∞」字样——库存语义仍在共享池令牌库,仅呈现退役;独立刷新机制另见待办清单「商店独立刷新机制」条):兑付 = 打开商店覆盖层(模态)——货架卡陈列、GP 余额读数、购买/离开;离开后兑付确认回选路。本节锁编排面(何时开/何时关/回选路),呈现细节归 spec;定价数值归[难度曲线与Boss](难度曲线与Boss.md)(shop.tres)。
|
||||
|
||||
### 行进与选路:兑付后确认,选路就是行进
|
||||
|
||||
@@ -89,13 +90,25 @@
|
||||
|
||||
Boss 兑付(胜利或英雄死亡)触发关卡收束点,此后不再有选路——关卡已定局,后续流程归冒险结构。
|
||||
|
||||
### 选路信息可见性:近处见类型,远处起雾
|
||||
|
||||
看图选路时玩家知道什么,三条:
|
||||
|
||||
| 层 | 可见什么 |
|
||||
|---|---|
|
||||
| 图结构 | 全图可见:节点、连线、入口、终点位置——路线形状永远明牌 |
|
||||
| 节点类型 | 可见范围(邻近 N 层)内可见;范围外信息迷雾,只见结构、不见类型 |
|
||||
| 节点内详情 | 图上从不显示——打谁、给什么到兑付才揭晓;内容生成时已定,不是到达随机 |
|
||||
|
||||
终点恒为 Boss,**Boss 标记与终点身份不入雾**——「走哪条=打谁」是提前承诺,承诺必须有信息前提:雾遮的是途中有什么,不遮终局打谁。N 为本系统自有参数(见调参表)。
|
||||
|
||||
### 节点兑付:把控制流移交给小循环
|
||||
|
||||
**人话阶段**:节点兑付就是"走到这个房间里,这个房间的事开始发生"。本文按节点类型触发兑付,然后把属于别家领域的事交还给别家——本文自始至终不生产任何数值。
|
||||
|
||||
| 类型 | 兑付动作 |
|
||||
|---|---|
|
||||
| 战斗 / Boss | 组装定位信息(关卡序号 + 节点编号),移交[战斗结算](../战斗循环/战斗结算.md)开战;战斗结果(胜负与战后生命)经战斗结果上报回流冒险结构(契约语义归[冒险结构](冒险结构.md),本文不复述) |
|
||||
| 战斗 / Boss | 组装定位信息(关卡序号 + 节点深度),移交[战斗结算](../战斗循环/战斗结算.md)开战;战斗结果(胜负与战后生命)经战斗结果上报回流冒险结构(契约语义归[冒险结构](冒险结构.md),本文不复述) |
|
||||
| 恢复 | 向冒险结构发治疗封顶回报(载荷 = 该关恢复比例,读本关配置行);按比例回血并封顶的执行归冒险结构 |
|
||||
| 奖励 | 触发节点奖励兑付:提升三选一(与通关三选一共享同一令牌库——七种令牌洗牌不放回抽 3 点选一件,池耗即上限;见下节) |
|
||||
| 商店 | 打开商店覆盖层(pool 模式:进店抽 3〔shop_draw 封闭流恰一 scope〕+买 1 全重抽;legacy 对照模式:商品陈列读商店表);购买即时生效、经共享池写统一冒险加算区(与关卡大奖励同一笔——库存语义归[冒险结构](冒险结构.md),本文不复述);离开覆盖层即兑付确认。商店房不携带战斗行:零敌人、零棋盘、零战斗初始化 |
|
||||
@@ -103,7 +116,7 @@ Boss 兑付(胜利或英雄死亡)触发关卡收束点,此后不再有选
|
||||
**遭遇构成的编排面**——本文对"放什么敌人"的全部职责只有三条:
|
||||
|
||||
1. 行进到战斗或 Boss 节点,兑付动作就是开战:本文递出定位信息,移交战斗结算。
|
||||
2. 定位信息只有两样:**关卡序号 + 节点编号**。战斗侧拿编号查表、配敌、掷数量、跑技能池、兑踩雷负向——一切战斗内部初始化归战斗循环文档(原"叠伏击"随 2026-09-20 八型批伏击退役改述),图的初始化不为战斗初始化做任何决定。
|
||||
2. 定位信息只有两样:**关卡序号 + 节点深度**。战斗侧按深度带配池查表、配敌、掷数量、跑技能池、兑踩雷负向——一切战斗内部初始化归战斗循环文档,图的初始化不为战斗初始化做任何决定。
|
||||
3. 本文不预掷任何战斗数值、不读敌人数量;教学位节点同样掷骰、没有结构豁免("任何掷骰结果下都稳过"的保证是[难度曲线与Boss](难度曲线与Boss.md)的数值责任)。
|
||||
|
||||
第二关的"新内容 = 既有池新技能"(技能池构成已定稿入[敌人系统](../战斗循环/敌人系统.md)技能池节)投放时,改的是战斗侧行里引用的技能池内容——节点图结构原样不动,本文无需为投放留任何新结构位。
|
||||
@@ -143,7 +156,7 @@ Boss 兑付(胜利或英雄死亡)触发关卡收束点,此后不再有选
|
||||
3. 战斗 / Boss 节点:组装关卡序号 + 节点编号,移交战斗结算开战;战斗结束向冒险结构上报战斗结果。
|
||||
4. 奖励节点:触发节点奖励兑付 = 提升三选一(与通关三选一共享令牌库);恢复节点:向冒险结构发治疗封顶回报;商店节点:打开商店覆盖层,购买即时生效,离开后兑付确认。
|
||||
5. Boss 战胜利或任意战斗倒下 = 关卡收束点:先调战斗结算的关卡收束清库,再宣布关卡结束。
|
||||
6. 第一关行集 = 单链特例(图的最简形态),切片按单链运行零改动;分支图行集与图版实现已落地(v1.0.1 起)。
|
||||
6. 单链是合法退化形态;三关参数档都含岔口,第一关即有选择(图的来源见[关卡节点图生成](关卡节点图生成.md))。
|
||||
|
||||
## P1 / P2 后置
|
||||
|
||||
@@ -156,22 +169,22 @@ Boss 兑付(胜利或英雄死亡)触发关卡收束点,此后不再有选
|
||||
|
||||
## 调参表
|
||||
|
||||
暂无本系统自有的参数——选路与行进无数值可调。图结构合法性(唯一编号、唯一入口、唯一终点、无环)是规则不是参数,由加载校验强制;链上节点构成归冒险结构的关卡表,战斗数值归难度曲线与Boss。
|
||||
本系统自有参数一项——**信息可见层数 N**(int ≥ 1,provisional,定值批落数据表):节点类型可见距离(层),范围外信息迷雾只见结构。图结构合法性是规则不是参数(规则语义见[关卡节点图生成](关卡节点图生成.md)的产出规格),由加载校验强制;图的来源与节点构成归[关卡节点图生成](关卡节点图生成.md),战斗数值归难度曲线与Boss。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 图合法:行集通过加载校验——节点编号关卡内唯一、入口唯一、Boss 为唯一终点、连线无环、所有节点沿连线可达 Boss;违规配置加载期致命。
|
||||
- 图合法:行集通过加载校验——每条路径恰好从一个入口出发、恰好到达一个终点,终点恒为 Boss(可多个)、连线无环、任意入口出发的任意路径都到达某一个终点;违规配置加载期致命(规则语义见[关卡节点图生成](关卡节点图生成.md)产出规格)。
|
||||
- 行进自主:从入口到 Boss 的每一次前进都由玩家选路发起(含关卡开始后的第一跳——关卡开始停在图上,入口节点不自动进入);不存在任何自动推进路径;倒下时行进终止。
|
||||
- 兑付移交:每场开战调用携带(关卡序号 + 节点编号),与节点图上该节点位置一致;每场战斗结束恰一次战斗结果上报。
|
||||
- 兑付后确认:战斗节点的继续确认挂战斗总结结算(settlement-summary.md),奖励/恢复挂兑付反馈覆盖层;到达与兑付之间无确认步。
|
||||
- 恢复兑付:治疗封顶回报载荷 = 该关恢复比例(读关卡配置行);满血回复 0 是合法状态(执行语义归冒险结构)。
|
||||
- 奖励兑付:奖励节点兑付恰触发一次;池空直通空态合法,现行 = 提升三选一(与通关三选一共享令牌库)取一件按件类兑付(spec:node-reward.md v2.x;兑付路由归[冒险结构](冒险结构.md)提升池节)。
|
||||
- 商店房编排:每关恰 1 个商店房、Boss 前一位必经位(所有入口→Boss 路径都穿过它)——由冒烟断言把关,图合法性校验零新增检查;商店房不携带战斗行;行进至此打开商店覆盖层、离开后兑付确认回选路。
|
||||
- 商店房编排:每个终点前一位恰一家店、每名玩家一局必经恰一家(所有入口→终点路径都穿过且恰穿过一家店)——由冒烟断言把关,图合法性校验零新增检查;商店房不携带战斗行;行进至此打开商店覆盖层、离开后兑付确认回选路。
|
||||
- 收束有序且唯一:关卡清库发生在关卡结束宣布之前;每关恰宣布一次关卡结束;Boss 胜 = 通关、倒下 = 失败。
|
||||
- 四件接口全款:与冒险结构登记的开始关卡、战斗结果上报、治疗封顶回报、关卡结束逐件对上,不另起平行通道。
|
||||
- 单链特例合法:单链行集(每步恰一条连线)是合法图形态;第一关按单链运行,切片零改动。
|
||||
- 单链特例合法:单链行集(每步恰一条连线)是合法退化形态;三关参数档都含岔口,第一关即有选择。
|
||||
- 呈现只读:节点图结构、当前位置、可选下一节点对呈现层只读可见(读数契约归 HUD 框架)。
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -170,7 +170,7 @@ GP(显示名「GP」)是冒险内的一次性属性提升货币:战斗胜
|
||||
|
||||
**奖励节点三选一池(结构层——池机制事实源=[奇物](奇物.md)「池结构与抽取」节,2026-10-11 迭代 20 案D 换代)**:池 = 四级令牌库 70/25/9/0=104 张(案D——普通打底/优秀·稀有做翼/传说惊喜位空集;曝光槽 = 三选一 2 格全池 + 1 格优秀档以上子池,令牌 = 出现次数上限、概率与已拿上限解耦;构成数字与抽取细节全读[奇物](奇物.md)与 `adventure.tres`,provisional 归[难度曲线与Boss](难度曲线与Boss.md)曲线调参批,本节零复制)。**池为冒险级单例**(迭代 10 追加批):关卡内奖励节点的三选一与商店货架共享同一令牌库(同库同摘牌),库存 = 剩余令牌数;消费 reward_pick 封闭随机流(主种子派生、[数据配置](../表现与支撑/数据配置.md)口径),同种子同操作序列同三选项可复放;**MC 策略 316-f**:autoplay 1 级优先度=最稀有(rarest>rare>common)、2 级=最左。断点附随件承载池余库逐枚还原;兑付语义归[关卡节点地图](关卡节点地图.md)。
|
||||
|
||||
**秘宝件受同调位约束**(2026-10-09 注:道具件已随迭代 11 第①批 T7 出池——三选一/奖励节点/战斗内渠道现行零道具件投放,下述渠道约束与替换交互无现役触发面,保留作渠道恢复时的契约口径;同调位读数现行事实源=attunement_growth.tres 谓词表实时求值):道具持有上限 = 同调位数。读数源 = 同调位里程碑谓词表 attunement_growth.tres 实时求值(0 起步、硬上限 3、增长 = 通关次数 + Boss 击杀数——同调实施批 2026-10-03 落地,过渡读数源 transitional_sync_slots 已顺带退役;原提案八拍板 git 57da58c 已退役)。获取渠道约束形态(迭代 10 过渡承载):三选一道具件超位触发**替换交互**(现有件与新件 N+1 选 N——丢任一原有件腾位收新,或放弃新件不获取;终态恒 ≤ 同调位);奖励节点与战斗内渠道超位**不授予**(候选拒绝 + 掉落沉没,逐行归因留痕)——停点渠道全覆盖的替换交互随同调实施批统一完成(原 06 条目已随完成移除)。
|
||||
**秘宝件受同调位约束**(2026-10-09 注:道具件已随迭代 11 第①批 T7 出池——三选一/奖励节点/战斗内渠道现行零道具件投放,下述渠道约束与替换交互无现役触发面,保留作渠道恢复时的契约口径;同调位读数现行事实源=attunement_growth.tres 谓词表实时求值):道具持有上限 = 同调位数。读数源 = 同调位里程碑谓词表 attunement_growth.tres 实时求值(0 起步、硬上限 3、增长 = 通关次数 + Boss 击杀数——同调实施批 2026-10-03 落地,过渡读数源 transitional_sync_slots 已顺带退役;原提案八拍板 git 57da58c 已退役)。获取渠道约束形态(迭代 10 过渡承载):三选一道具件超位触发**替换交互**(现有件与新件 N+1 选 N——丢任一原有件腾位收新,或放弃新件不获取;终态恒 ≤ 同调位);奖励节点与战斗内渠道超位**不授予**(候选拒绝 + 掉落沉没,逐行归因留痕)——停点渠道全覆盖的替换交互随同调实施批统一完成(原待办清单条目已随完成移除)。
|
||||
|
||||
最后一关通关不发三选一与部分回血:没有下一战可受益——只做冒险完成结算,末关 Boss 钥石照常入账。设计意图:清库之后才兑付、末关不兑付,都是从「奖励只朝下一战生效」这一条推出来的。
|
||||
|
||||
@@ -261,7 +261,7 @@ GP(显示名「GP」)是冒险内的一次性属性提升货币:战斗胜
|
||||
|
||||
| 参数 | 域 | 现行值 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 关卡行集(关数与节点链模板) | 关卡表逐关行 | 三关现行(逐关 templates 池 3 关 × 5,模板 0 = 回滚档——见[关卡节点图生成](关卡节点图生成.md)) | 加关 = 内容批(链行 + 敌我数值 + 新元素同批);L1 回滚档模板 = 两战 → 奖励 → 两战 → 恢复 → 关底 Boss(7 节点,战斗深度 5) |
|
||||
| 关卡行集(关数与图的参数档) | 关卡表逐关行 | 三关现行(图由生成器按参数档产出,见[关卡节点图生成](关卡节点图生成.md);旧模板池随实现批清数据) | 加关 = 内容批(参数档 + 敌我数值 + 新元素同批);确定性 = 固定种子+固定参数档(首局教学与存档复现的固定图由此承载,无兜底模板) |
|
||||
| 恢复比例 | int [0,100],逐关行 | 见 level.tres rest_heal_pct(provisional) | 恢复点回复生效上限的百分比;随上限加成同步放大;100 = 恢复点变全满泉,0 = 死节点 |
|
||||
| 上限提升 | int [0,999],冒险表逐件列 | adventure_table bonus_attack_flat / bonus_max_hp_flat / bonus_armor_flat(provisional) | 三选一属性件各自兑付值;值锚与上调约束归难度曲线与Boss |
|
||||
| 三选一件值(其余) | int [0,999],冒险表 kind×tier 列族 | adventure_table curio_value_*(GP 袋 / AP 件 / 恢复 / 刷新,provisional) | 令牌件值读表;道具件授予件数随秘宝件出池退役 |
|
||||
@@ -272,7 +272,7 @@ GP(显示名「GP」)是冒险内的一次性属性提升货币:战斗胜
|
||||
| Boss 钥石掉落 | int [0,999],meta 表逐关行 | meta.tres boss_keystone 首杀 3 / 复杀 2(provisional) | 逐杀实时入账;精英怪掉 1 预留;消费方归元进度 |
|
||||
| GP 经济参数 | 商店表逐行 | 见 shop.tres gp_drops / level_gp_bonus / pool_shelf / price_a / price_b(provisional,以 shop.tres 为准;legacy products / price_level_bonus 随对照模式保留) | 掉落均值与区间(普通/精英/Boss)、关级掉落加值、货架基价、A/b 价格因子;公式语义归本文,数值所有权归难度曲线与Boss曲线调参批 |
|
||||
|
||||
结构冻结(非调参项):序列线性无跳关、关底 Boss 恒为末节点、倒下即冒险终、大奖励构成 = 通关部分回血 + 专长三选一(按档配比加权抽取——2026-10-11 迭代 20 换代;钥石退役出大奖励,改 Boss/精英击杀掉落——2026-09-30 拍板重构)、一冒险一主种子、收束点先清库后宣布的次序。
|
||||
结构冻结(非调参项):序列线性无跳关、终点恒为 Boss(终点可多个、各在路径最深处)、倒下即冒险终、大奖励构成 = 通关部分回血 + 专长三选一(按档配比加权抽取)、一冒险一主种子、收束点先清库后宣布的次序。
|
||||
|
||||
参数联动:恢复比例与通关回血比例越厚通关越有余量(对冲杠杆语义归难度曲线与Boss);三选一属性件/AP 件抬高后续战斗强度,件值/权重/上限改值须按难度曲线与Boss的口径重扫通关率带(回血比例自「血回满」改 20% 系难度上压方向,带判定顺带呈报)。
|
||||
|
||||
@@ -300,4 +300,4 @@ GP(显示名「GP」)是冒险内的一次性属性提升货币:战斗胜
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -63,7 +63,7 @@
|
||||
| 陷阱爆发 | on 踩雷:全体存活敌受 雷伤×倍率 直伤 | 角色与怪物互相吃伤害;倍率 >1 系结构锚(引爆有代价、回报须更高),数值归调参批(扩签:雷罚伤害量) |
|
||||
| 血蜜 | on 踩雷:回复 N 生命 | 引爆仍然净亏血、只是亏得慢;比值 <1 系结构锚,N 归调参批 |
|
||||
| 石肤 | on 受伤:AC +1(本场累计,上限 M) | 受击成长型护甲——越挨打越硬;恒定减伤形态被否(用户裁「太硬霸」),M 归调参批 |
|
||||
| 背水 | when 生命 ≤50%:受伤额外 −N | 风险联动减伤——低血不再是纯劣势;自爆流刀口舔血兜底(扩签:生命比例〔条件事实——实现前置需求,06 在册〕) |
|
||||
| 背水 | when 生命 ≤50%:受伤额外 −N | 风险联动减伤——低血不再是纯劣势;自爆流刀口舔血兜底(扩签:生命比例〔条件事实——实现前置需求,待办清单在册〕) |
|
||||
|
||||
**踩雷转化对账锚(自爆流 kill-switch,2026-10-11 用户裁顺带登记)**:踩雷的净血账期望必须为负——雷伤 > 血蜜回复 + 其余转化件的血当量;对账归调参批,两轮数值迭代压不住则砍血蜜。防「见雷就踩」退化(喂雷流 R5 甲教训同源);主动/误踩不判定(用户裁:不预判玩家意图,拿件自选收益最高的操作)。
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
惊巢反制=退役进素材库(不迁入)。
|
||||
|
||||
**资源类迁出件(2026-10-18 用户裁)**:完美保险自资源类迁入专长——理由:它给的是**规则豁免**而非可调动的余量(§1.1 判据「给规则的是专长」),效果=本场首次封印落空不计入完美封印判定。旧「资源类成员」归类系 2026-10-08 框架批写入、无用户裁断支撑,随本次改判注销。专长行的触发器规格与代码实现(含效果引擎词表扩签)归独立批,见 06 登记。
|
||||
**资源类迁出件(2026-10-18 用户裁)**:完美保险自资源类迁入专长——理由:它给的是**规则豁免**而非可调动的余量(§1.1 判据「给规则的是专长」),效果=本场首次封印落空不计入完美封印判定。旧「资源类成员」归类系 2026-10-08 框架批写入、无用户裁断支撑,随本次改判注销。专长行已于迭代 31 卡② 落表(合并 `3e076e22`):`feats.tres` 单行 `when counter(insurance_used) lt 1` 承载「本场首次」,效果类型 `forgive_seal_miss` 随引擎扩签;配额按净删重裁(rare 9→7、总量 104→102)。
|
||||
|
||||
### 扩签登记(迭代 20 实装已承载——2026-10-12 追认④写入文档换代)
|
||||
|
||||
@@ -231,6 +231,7 @@
|
||||
**补给类内部的异质性(统一表述)**:凭证全部即时消尽——不存在「持有凭证」这种状态;异质性不在凭证上,而在**被写入的系统资源的寿命**上:GP 钱包 / 生命 / 刷新存量 = 冒险级,储备池 = 节点级,时沙暂记 = 至下一战首轮兑付(系统资源清单对齐「凭证件表(新 3+留任 2)」节;「AP 上限」已归属性值提升轴,见 1.2)。
|
||||
|
||||
- 原在册的「秘宝充能」「完美资格」两笔随 2026-10-18 两件裁削注销——`attune_charges` 账本本身仍为秘宝基础设施保留(迭代 11,主动件充能每冒险重置),本次只删奇物侧写它的入口。
|
||||
- 完美资格的豁免走**订阅式自有累加账**:授予段在 `seal_missed` 命中时累加豁免数,判定取「误标总数 − 豁免数」得有效数;**不改写误标账本身、不拦截事件**(迭代 31 卡② 契约 2/3)。
|
||||
- 登记此节防止把凭证当成会持续生效的东西去结算。
|
||||
|
||||
#### 1.4 分类判据
|
||||
@@ -371,7 +372,7 @@
|
||||
|
||||
#### 2.5 在册事项
|
||||
|
||||
现行实现里**三条渠道共用同一个候选池**(商店货架按 2.4 可计算条件切边去掉计价物),三个分类出现条件一样、只有出现概率不同——上表渠道分工中的**凭证形式(非 Boss 渠道给凭证 + 保底限购)已随迭代15② 入表实装**(见 2.2 与调参表);**Boss 渠道直接给专长已裁随专长实装批本批落地**(2026-10-11 用户裁,不再分期)。相关的实施批次登记在 [06-待办清单](../../06-待办清单.md) 的 BD 分层条目下,本文件不复制。
|
||||
现行实现里**三条渠道共用同一个候选池**(商店货架按 2.4 可计算条件切边去掉计价物),三个分类出现条件一样、只有出现概率不同——上表渠道分工中的**凭证形式(非 Boss 渠道给凭证 + 保底限购)已随迭代15② 入表实装**(见 2.2 与调参表);**Boss 渠道直接给专长已裁随专长实装批本批落地**(2026-10-11 用户裁,不再分期)。相关的实施批次登记在 [待办清单](../../../../待办清单.md) 的 BD 分层条目下,本文件不复制。
|
||||
|
||||
**池结构选型澄清(2026-10-11)**:池结构正主=案 D(v3.3 写入文档,用户定夺在案);「概率与上限解耦」的实现路径(v3.4 在册三案)系**实装侧自选项,非用户裁定项**——按「实现设计不定于现在」的原裁定,实装批内依代码事实自选,不再上问。
|
||||
|
||||
@@ -424,7 +425,7 @@
|
||||
|
||||
- **P0(已实现)**:三个分类可定义 → 进入候选池 → 在渠道上被选中 → 当局生效 → 收束清零。
|
||||
- **P1 后置**:三条渠道按第 2 节分工——凭证形式(非 Boss 渠道给凭证+保底限购)已随迭代15② 入表实装(见调参表),其余分工(Boss 渠道直接给专长等)待实施。
|
||||
- **P2 后置**:商店里的攻击类型转换服务;喂雷玩法是否保留(尚未拍板,登记在 [06-待办清单](../../06-待办清单.md))。
|
||||
- **P2 后置**:商店里的攻击类型转换服务;喂雷玩法是否保留(尚未拍板,登记在 [待办清单](../../../../待办清单.md))。
|
||||
|
||||
## 调参表
|
||||
|
||||
@@ -460,4 +461,4 @@
|
||||
|
||||
## 待办
|
||||
|
||||
全部待办单点登记于 [06-待办清单](../../06-待办清单.md)(BD 分层条目、命名统一后续条目),本节不复制。
|
||||
全部待办单点登记于 [待办清单](../../../../待办清单.md)(BD 分层条目、命名统一后续条目),本节不复制。
|
||||
@@ -107,7 +107,7 @@ MVP 行值并不逐式现算,而是按此式量级直填经切片验证过的
|
||||
|
||||
### 期望值带:三条定稿的验收带
|
||||
|
||||
单战判定之上还有"整关长什么样"的验收基准。下面三条带是定稿口径;调参后任一条越带都必须回滚或登记,禁止静默出带。带子按模拟口径定义——即调参期批量模拟器的读数口径;真人与模拟者的偏差另行登记标定义务(登记 [06-待办清单](../../06-待办清单.md))。
|
||||
单战判定之上还有"整关长什么样"的验收基准。下面三条带是定稿口径;调参后任一条越带都必须回滚或登记,禁止静默出带。带子按模拟口径定义——即调参期批量模拟器的读数口径;真人与模拟者的偏差另行登记标定义务(登记 [待办清单](../../../../待办清单.md))。
|
||||
|
||||
| 带 | 定稿口径 | 含义 |
|
||||
|---|---|---|
|
||||
@@ -163,7 +163,7 @@ GP 与商店房随 2026-09-20 GP+商店房批落地,给难度引入第三族
|
||||
商品价格(legacy 对照) = 基价 + 关级价格加成 + 步长 × 已购档数
|
||||
```
|
||||
|
||||
定价锚定口径:商品价格按"正常运气可购档数 / 运气极好可购档数"两读数的数学期望制定(期望读数进商店表批注)——正常运气的玩家买得到成长保底档,运气极好也不至于全表买穿,两个失速态都不出现。**v0.22 换代注记(迭代 11 第②批)**:A=店内递增 ≫ b=跨关递增的方向裁决随 06「GP 商店房重构」条落定;A/b 具体系数原裁决=game-balancer 研究后用户拍板(P5 甲),迭代 11 第②批依零人工令改 provisional 投值 1.8/1.1+后置追认(追认单随迭代 11 出口包,研究归调参批复核);ceil 向上单一取整(玩家不利向)。
|
||||
定价锚定口径:商品价格按"正常运气可购档数 / 运气极好可购档数"两读数的数学期望制定(期望读数进商店表批注)——正常运气的玩家买得到成长保底档,运气极好也不至于全表买穿,两个失速态都不出现。**v0.22 换代注记(迭代 11 第②批)**:A=店内递增 ≫ b=跨关递增的方向裁决随待办清单「GP 商店房重构」条落定;A/b 具体系数原裁决=game-balancer 研究后用户拍板(P5 甲),迭代 11 第②批依零人工令改 provisional 投值 1.8/1.1+后置追认(追认单随迭代 11 出口包,研究归调参批复核);ceil 向上单一取整(玩家不利向)。
|
||||
|
||||
| 杠杆位 | 口径 | 说明 |
|
||||
|---|---|---|
|
||||
@@ -175,7 +175,7 @@ GP 与商店房随 2026-09-20 GP+商店房批落地,给难度引入第三族
|
||||
|
||||
公式语义与 GP 归[冒险结构](冒险结构.md)(GP 节),商店房编排归[关卡节点地图](关卡节点地图.md);行值读 `data/tables/shop.tres`,全部 provisional 归本文曲线调参批。
|
||||
|
||||
**分层验收 defer(2026-09-20 用户拍板成文)**:按"商店存在与否"分层的通关率验收——MC 双格(shop=off 现行基线 / shop=on 购买画像)、shop=on 的带平移量、商店属性提升的换算率登记(换算率强条目纪律同类)——**全部随曲线调参批执行**,与「基线通关率越带」同窗(原「L2 Boss 承伤微越带」「大奖励数值随难度曲线联动」两 06 条目已完成移除——前者 2026-09-28 读数自愈、后者随迭代 10 大奖励退役失义)。执行前 shop=off 续用现行带判读,shop=on 读数只登记不判读、禁止用 shop=off 读数外推 shop=on。L3 首遇绿档冒烟断言仍按成长代理档口径,不因商店批改判。**v0.22 注记(迭代 11 第②批)**:shop=off 对照 = legacy 固定三件模式(MC 逐项比对 off_n60 逐位,迭代 11 第②批 T5);shop=on 首锚 = 共享池货架面 N=60 双策略(`runsum_m4iter11b_t5shopon_n60_20261003.jsonl`,attune=off 同种子配对)——带平移判读仍归调参批,终局池读数(pool_stock)随行只测不判。
|
||||
**分层验收 defer(2026-09-20 用户拍板成文)**:按"商店存在与否"分层的通关率验收——MC 双格(shop=off 现行基线 / shop=on 购买画像)、shop=on 的带平移量、商店属性提升的换算率登记(换算率强条目纪律同类)——**全部随曲线调参批执行**,与「基线通关率越带」同窗(原「L2 Boss 承伤微越带」「大奖励数值随难度曲线联动」两待办清单条目已完成移除——前者 2026-09-28 读数自愈、后者随迭代 10 大奖励退役失义)。执行前 shop=off 续用现行带判读,shop=on 读数只登记不判读、禁止用 shop=off 读数外推 shop=on。L3 首遇绿档冒烟断言仍按成长代理档口径,不因商店批改判。**v0.22 注记(迭代 11 第②批)**:shop=off 对照 = legacy 固定三件模式(MC 逐项比对 off_n60 逐位,迭代 11 第②批 T5);shop=on 首锚 = 共享池货架面 N=60 双策略(`runsum_m4iter11b_t5shopon_n60_20261003.jsonl`,attune=off 同种子配对)——带平移判读仍归调参批,终局池读数(pool_stock)随行只测不判。
|
||||
|
||||
### Boss 设计:食人魔酋长与关底血量锚
|
||||
|
||||
@@ -221,10 +221,10 @@ Boss 战的棋盘用本关最大板形(板形行值读棋盘参数表),板
|
||||
| η 读数 ↔ 模拟画像定义 ↔ 可推理率 ι ↔ min_safe_count/生成尝试上限 | sim 档位、[棋盘生成](../排雷循环/棋盘生成.md) | η 是画像条件量:画像重构即锚漂移,重锚顺带;可推理率三元组既有联动扩一员(2026-10-05 判式换代批新增) |
|
||||
| β 带端点 β_lo/β_hi ↔ D_ref ↔ hero max_hp ↔ AC 折算档(5pp 系数) | [战斗结算](../战斗循环/战斗结算.md) | 带是 D_ref 的函数:换锚(D_ref/max_hp)须全带重推;AC 档值变更改 5pp 系数,同罪(2026-10-05 判式换代批新增) |
|
||||
| 直伤雷三行(6/8/8) ↔ 正向同轴行(heal_minor/pierce_strike/magic_bolt) | [地图战斗资源](../战斗循环/地图战斗资源.md) | **同轴镜像比入对账面**:负向/正向比超出 β 带内推导区间即越带;单边动负向三行今后必须带正向镜像行联合检算(2026-10-05 判式换代批新增——d70f6c3/数值修复批两先例的单边动教训成文) |
|
||||
| clear_blast 区间(24/40/32) ↔ p_perfect | [地图战斗资源](../战斗循环/地图战斗资源.md) | 完美排雷第二份的补贴基数进封印激励对账;单边动清图奖励 = 变相动封印(2026-10-05 判式换代批新增) |
|
||||
| clear_blast 区间(24/40/32) ↔ p_perfect | [地图战斗资源](../战斗循环/地图战斗资源.md) | 完美封印第二份的补贴基数进封印激励对账;单边动清图奖励 = 变相动封印(2026-10-05 判式换代批新增) |
|
||||
| 标中强化池权重 ↔ 正向池权重(77 分母) ↔ 关级恒等式奖励占比 [15,40%] | [地图战斗资源](../战斗循环/地图战斗资源.md) | 形态 (b) 专属池重摇启用时生效(现行不启):双池分布变动 = E_dig 与 W 同时漂移,须联合检算 + 恒等式占比复核(2026-10-05 判式换代批新增,条件生效行) |
|
||||
|
||||
五条经济契约的对账由[战斗结算](../战斗循环/战斗结算.md)闭合验收;挖掘风险不等式已于 2026-10-05 判式换代批重立(三层口径+β 带+条件二封印溢价——结构层接替与数值层带判读详见行动经济 F1 条款3 推导节),本系统的对账义务转为按 β 带逐 u 检算(常设重跑入口 `tools/clear_dominance_audit.py`,现行 u=3 β=1.17 微破下限登记态);清图支配性已于 2026-09-19 按现行值链重跑、裁决②"非支配设计态"维持(回执见 git 提交 2ca3e20,报告已退役)——判式换代后的全套判值重跑随本系统下一次调参批顺带执行。完美排雷乘数的调参权在本系统。
|
||||
五条经济契约的对账由[战斗结算](../战斗循环/战斗结算.md)闭合验收;挖掘风险不等式已于 2026-10-05 判式换代批重立(三层口径+β 带+条件二封印溢价——结构层接替与数值层带判读详见行动经济 F1 条款3 推导节),本系统的对账义务转为按 β 带逐 u 检算(常设重跑入口 `tools/clear_dominance_audit.py`,现行 u=3 β=1.17 微破下限登记态);清图支配性已于 2026-09-19 按现行值链重跑、裁决②"非支配设计态"维持(回执见 git 提交 2ca3e20,报告已退役)——判式换代后的全套判值重跑随本系统下一次调参批顺带执行。完美封印乘数的调参权在本系统。
|
||||
|
||||
元进度接口预留:**结论——元进度解锁对难度的影响,以生存链区间的平移换算率接入;接口启用形态已定稿(2026-09-18 元进度定稿批,用户拍板):永久强化影响折算为通关率带平移量,判读口径不变;换算率表首版三行(生命上限 / 基础攻击 / 护甲等级——护甲行强条目,验收读数须按解锁后参数重测)与键结构见 `data/schemas/meta_table.gd`,数值行读 `data/tables/`、标定随元进度设计批。** 元进度数值行落地前,本系统不为它预留任何数值行。
|
||||
|
||||
@@ -263,7 +263,7 @@ Boss 战的棋盘用本关最大板形(板形行值读棋盘参数表),板
|
||||
| P1 | 挖掘风险不等式与清图支配性对账重跑 | **挖掘风险不等式半边已完成(2026-10-05 判式换代批,迭代14②号卡)**——判式三层重立写入文档+条件二封印溢价机制闭合+β 带判读制度化(残留=u=3 β=1.17 微破下限登记态,不强制回滚;δ 敏感度判读顺带出口报告),本行对挖掘风险不等式的「重跑义务」改判读回执:后续调参按 β 带检算执行,不再立独立重跑批。清图支配性已于 2026-09-19 按现行值链重跑(裁决②"非支配设计态"维持——缺口收窄主读数 L1≈24/L2≈45 当量〔u=1〕,设计权威回执=git 提交 2ca3e20,重跑入口 tools/clear_dominance_audit.py);调参批执行时按当期行值顺带重跑 |
|
||||
| P1 | 真人通关率修正系数标定 | 带按模拟口径定义;试玩批次实测真人显著强于模拟者,系数待后续试玩批次定量 |
|
||||
| P1 | 迭代 10 奖励重构带重扫义务 | 恢复经济收紧(通关血回满→部分回血 20%,难度上压方向)+ 局内成长节奏变动(三选一件值/权重/次数上限 knob 全读冒险表;AP 件 = 每场战斗开始首轮 +1 AP 的强度敏感轴)——通关率带重扫随曲线调参批现行窗执行;Boss 钥石掉落重锚(首杀 3/复杀 2)连带元进度产出标定义务重开(归元进度设计批) |
|
||||
| P1 | 完美策略档的结构性加难杠杆 | 数值杠杆对完美档已被实测证伪,结构候选未裁决(登记 [06-待办清单](../../06-待办清单.md)) |
|
||||
| P1 | 完美策略档的结构性加难杠杆 | 数值杠杆对完美档已被实测证伪,结构候选未裁决(登记 [待办清单](../../../../待办清单.md)) |
|
||||
| P2 | 精英节点完整语义与修饰乘数表首行 | 随[关卡节点地图](关卡节点地图.md)定稿联动;首个使用场景 = 精英节点;精英怪设定三特征已登记(迭代 10 批,见「曲线雕刻」表注),钥石掉 1 个预留挂钩 meta.tres boss_keystone 精英行(随实装批落) |
|
||||
| P2 | 区间平移换算率定稿 | 接口启用形态已定稿(2026-09-18 元进度定稿批);剩余面 = 首版三行折算数值随[元进度](../跨局循环/元进度.md)设计批模拟读数标定 |
|
||||
| P2 | 全局作用域变异的投放计划 | **首发已落地(2026-10-04 全局作用域变异首发批,迭代 14 ③号卡)**——现行=L2/L3 Boss 图恒定贫矿脉(unlock_level=2 起,第 1 关 Boss 免疫=L1 教学保护);本行降级为「后续型扩展计划」:新全局型(精英图首行/案D 冻土/案B 诡雷远期)逐关固定演进,随关卡扩展登记;带 A(全局变异)收敛判读归全局难度批(Beta 阶段,未排期)杠杆表(Q4 甲——落地批读数只测不判) |
|
||||
@@ -295,7 +295,7 @@ Boss 战的棋盘用本关最大板形(板形行值读棋盘参数表),板
|
||||
| 池权重 | 权重即旋钮:抬特殊技权重 = 抬输出频率,EV 随之重锚;各敌种行集读表 |
|
||||
| 技能冷却(拍) | 逐技能行读表;普攻冷却恒 0 不可配(空池不可能的结构前提——域校验锁死);撕咬 = 与突刺同倍率错开冷却轴的高频小爆发轮廓;毒爆 = 蛛化卓尔精灵专属池独占技 |
|
||||
| 保底 M | 全局单旋钮,读技能表 pity_threshold;连续 M 拍执行普攻 → 下拍必出特殊技;单特殊技低权重池受保底支配 |
|
||||
| 池 EV 锚 | 静态份额与含闸长期速率实测读数登记于调参日志(EV 重锚与权重投值随本表修订)。**2026-09-19 节奏体验验证批登记**(60 distinct seeds × 双策略 × 两关,tools/skill_rhythm_audit.py——20 种子以下批次有种子偏置假象,读数须 ≥50 种子):特殊技执行份额 对表成立——地精 10.4%(静态 10%)/ 地精一伙 8.4% / 熊地精系 28.9–42.4% / 巨蜘蛛 34.8% / 酋长 28.9% / 卓尔 35.4%;**L2 池含闸速率抽样复核完成**(上项即含闸读数);保底越界 0/247(间隔 4 = M=4 合法上界);过涝信号 8/1440 场(最长连击 3);过旱按用户判读带集中在短命杂鱼(生存时长问题非池配置——见 06 清单 Alpha 条目移交注记) |
|
||||
| 池 EV 锚 | 静态份额与含闸长期速率实测读数登记于调参日志(EV 重锚与权重投值随本表修订)。**2026-09-19 节奏体验验证批登记**(60 distinct seeds × 双策略 × 两关,tools/skill_rhythm_audit.py——20 种子以下批次有种子偏置假象,读数须 ≥50 种子):特殊技执行份额 对表成立——地精 10.4%(静态 10%)/ 地精一伙 8.4% / 熊地精系 28.9–42.4% / 巨蜘蛛 34.8% / 酋长 28.9% / 卓尔 35.4%;**L2 池含闸速率抽样复核完成**(上项即含闸读数);保底越界 0/247(间隔 4 = M=4 合法上界);过涝信号 8/1440 场(最长连击 3);过旱按用户判读带集中在短命杂鱼(生存时长问题非池配置——见待办清单清单 Alpha 条目移交注记) |
|
||||
|
||||
主角表与棋盘参数表(行值分别读 `hero.tres` 与 `board_gen.tres`,provisional):
|
||||
|
||||
@@ -310,7 +310,7 @@ Boss 战的棋盘用本关最大板形(板形行值读棋盘参数表),板
|
||||
|---|---|---|
|
||||
| 血量锚比率 r_boss | [0.5, 1.1];加难走廊 [1.1, 1.35](扫描已执行,见「Boss 设计」节) | Boss 生命锚;**锚漂移登记(迭代 5 基线重测批)**:数值设计批 Boss 血轴拔高后 L2/L3 现值 r_boss≈0.33(全行集 Σ 口径)低于域下限 0.5——该指向已过期(迭代 5 已关闭未执行);r_boss 复算与收锚义务挂起,随数值批重开时核销 |
|
||||
| 克制矩阵 | 克制 / 中性 / 被克三档加法修正(+1/0/−1 点),读 `settlement.tres` 甲效表 | 消费归[战斗结算](../战斗循环/战斗结算.md) |
|
||||
| 完美排雷乘数 | 读表 | 调参权本文 |
|
||||
| 完美封印乘数 | 读表 | 调参权本文 |
|
||||
| 当量参考敞口 | 读表 | 变更触发全量对账重跑 |
|
||||
| 注入概率初值 / 衰减 | 读表 | 注入机制归[地图战斗资源](../战斗循环/地图战斗资源.md) |
|
||||
| 清障炸药威力区间 | 读表 | 清图槽当场引爆 |
|
||||
@@ -334,4 +334,4 @@ Boss 战的棋盘用本关最大板形(板形行值读棋盘参数表),板
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -152,7 +152,7 @@
|
||||
|
||||
### 清图奖励对象:整图封装,可配置可替换
|
||||
|
||||
清图奖励不属于任何一格的格面资源——它是作用域为整张板的独立奖励对象:把一张图的雷全部清完(全部安全格开完)时结算发的那一份。装板摇号时与格子资源同批判尾摇定身份;清图批兑付恰好一次;完美排雷(零误标且至少一次封印标中)追加第二份同身份兑付。
|
||||
清图奖励不属于任何一格的格面资源——它是作用域为整张板的独立奖励对象:把一张图的雷全部清完(全部安全格开完)时结算发的那一份。装板摇号时与格子资源同批判尾摇定身份;清图批兑付恰好一次;完美封印(零误标且至少一次封印标中)追加第二份同身份兑付。
|
||||
|
||||
它是可配置可替换的封装:数据面上是清图池里的一行(现行默认槽 clear_default),换行即换内容,机制零改动。按图类型差异化已留结构位——`clear_<图类型>` 命名空间预留,不同地图类型可以配不同的清图奖励;词表现行 = {dungeon, underdark, deep_underdark}(2026-09-19 定稿两值、第三关内容批 2026-09-20 增 deep_underdark,玩家侧「地图类型」),差异化内容启用随内容批。
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
1. 战斗结算装上新图后、玩家开挖前调用一次摇号:全部非封锁格的资源类型与清图对象身份摇定,映射进入只读。
|
||||
2. 玩家挖掘:挖开的格子当场揭晓类型身份;已开格的身份随时可读。
|
||||
3. 轮末授予段按极性矩阵逐格兑付:正向 percent 挂状态、heal 正向当场结算,非伤害负向当场兑付;伤害形态正负单据暂存,战斗段末统一落地。
|
||||
4. 清图轮兑付清图奖励对象,恒非空;完美排雷追加第二份。
|
||||
4. 清图轮兑付清图奖励对象,恒非空;完美封印追加第二份。
|
||||
5. 战斗结束:整图映射退役。换图按新板重摇。
|
||||
|
||||
## P1 / P2 后置
|
||||
@@ -202,7 +202,7 @@
|
||||
|---|---|---|
|
||||
| P1 | ~~功能道具渠道~~(已由秘宝体系承接——局外同调件:营地解锁 + 同调位装配,同调实施批 2026-10-03 落地;关卡节点渠道形态顺带退役) | 数值型伤害/治疗道具已删除(2026-09-15 用户定);本系统只留结构位不变 |
|
||||
| P1 | 清图池按图类型差异化 | 默认槽之外的结构位已留;词表现行三值({dungeon, underdark, deep_underdark}),差异化行随内容批配置 |
|
||||
| P1 | 未开格资源类型的呈现深度 | 基础档已定——微光只读类型不读危险格与安全格(2026-10-07 案甲,实施归呈现批、06 在案);更深档与推理的界线另议 |
|
||||
| P1 | 未开格资源类型的呈现深度 | 基础档已定——微光只读类型不读危险格与安全格(2026-10-07 案甲,实施归呈现批、待办清单在案);更深档与推理的界线另议 |
|
||||
| P1 | 增攻(attack)型行值与权重终调 | 内容决策,归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md) |
|
||||
| P2 | 音频挂钩(拾取音 / 负向兑付音 / 清图奖励音) | 归[音频](../表现与支撑/音频.md),Alpha |
|
||||
| P2 | 内容名录扩展(新资源行) | 机制与内容分离;现行行集收窄,扩展随内容批 |
|
||||
@@ -248,4 +248,4 @@
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -239,4 +239,4 @@ empower 与 fortify 是敌方技能池增益技在容器侧的数据落点(技
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -82,7 +82,7 @@
|
||||
| 蓄力行 | 敌人本拍蓄力不动手(攒下一击)时记一行 **(2026-09-16 敌人技能池批改型为施放行 `cast`:敌人本拍施放增益技——0 伤害动作、具名技能、不掷不折〔原"不兑付伏击"读怪奖励语义随 2026-09-20 八型批伏击退役作废〕;蓄力语义改写为纯攻击增益技,见[敌人系统](敌人系统.md))** |
|
||||
| 衰减行 | 到期临时效果移除一行 |
|
||||
| 判定行 | 胜 / 负 / 继续各一行;终局判定行同时是胜负画面的数据锚 |
|
||||
| 高光行 | 完美排雷成立时一行,只记实际有效兑付 |
|
||||
| 高光行 | 完美封印成立时一行,只记实际有效兑付 |
|
||||
|
||||
每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调封印标中、清图奖励这类值得高亮的结果)。没生效也要留痕:两类"未生效"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。
|
||||
|
||||
@@ -98,14 +98,14 @@
|
||||
|
||||
### 收益对照表:所有收益共用同一把尺子
|
||||
|
||||
开安全格给正向、封印标中给正向、踩雷与标错给负向、清图给清图奖励对象——来源不同、形态不同,但玩家必须能回答"挖这一格值不值",所以全部收益用一张效果档位对照表归一换算。折算单位是当量:1 当量等于 1 点主角生命的期望净变化。这张表是[行动经济](../排雷循环/行动经济.md)五条经济契约第一条"单一当量"的载体;本系统是全部五条契约的唯一对账责任方(契约本体见该文档,对账现状登记 [06-待办清单](../../06-待办清单.md))。
|
||||
开安全格给正向、封印标中给正向、踩雷与标错给负向、清图给清图奖励对象——来源不同、形态不同,但玩家必须能回答"挖这一格值不值",所以全部收益用一张效果档位对照表归一换算。折算单位是当量:1 当量等于 1 点主角生命的期望净变化。这张表是[行动经济](../排雷循环/行动经济.md)五条经济契约第一条"单一当量"的载体;本系统是全部五条契约的唯一对账责任方(契约本体见该文档,对账现状登记 [待办清单](../../../../待办清单.md))。
|
||||
|
||||
**结论**:单一线性换算全源共用,禁止分源改率;百分比效果按"每个结算的瞬时当量"口径折算。
|
||||
|
||||
```
|
||||
flat 类(直伤、治疗、行动点): E = 表值本身
|
||||
percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × 参考敞口
|
||||
完美排雷的清图奖励: E = 奖励表值 × 完美乘数 ÷ 100
|
||||
完美封印的清图奖励: E = 奖励表值 × 完美乘数 ÷ 100
|
||||
```
|
||||
|
||||
| 变量 | 含义 | 现行值 |
|
||||
@@ -113,7 +113,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 ×
|
||||
| E | 效果折算当量(主角生命点) | — |
|
||||
| 参考敞口 | 折算百分比效果所用的代表性伤害敞口,配置值 | 读 `settlement.tres` equivalence_ref_damage(provisional) |
|
||||
| 层数 × 行值 | 单条效果的叠层数与行值,读[增益状态](增益状态.md)的实例数据 | 行值归该系统 |
|
||||
| 完美乘数 | 完美排雷给清图奖励的乘数,配置值 | 读 `settlement.tres` p_perfect(现行值读表) |
|
||||
| 完美乘数 | 完美封印给清图奖励的乘数,配置值 | 读 `settlement.tres` p_perfect(现行值读表) |
|
||||
|
||||
**gp / ap 当量口径注记(2026-10-05 判式换代批 T3——口径空白登记)**:上式 flat 类枚举(直伤、治疗、行动点)不含 gp——现行对账按 **gp 当量 = flat power 代理**计(GP 袋 3 / 破财 2,方案报告假设 A5);ap 当量 = flat 表值 1,机会成本口径(+1AP ≈ 多挖一格 ≈ E_dig + 进度 + 信息价值)更高,差异仅登记不进主算(假设 A6)。gp 的精确口径应经商店定价换算(1 GP 的边际属性当量),归状态层/商店批复核后换代。封印溢价兑付(条款3 条件二形态 a,`settlement.tres` flag_hit_premium=125 provisional)按同型正向 ×(1+δ) 兑付——当量换算随同型行口径同步放大,整数量级取整四舍五入(与伤害管线 round_direction=floor 分域,registry 顺带登记)。
|
||||
|
||||
@@ -136,9 +136,9 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 ×
|
||||
|---|---|
|
||||
| 挖格批 | 开到的安全格逐格兑该格资源的正向(封印格直挖除外——四情况④零奖励);开到的危险格逐格兑该格资源的负向(封印危险格照常——四情况②) |
|
||||
| 封印判定批 | 标中则兑该危险格资源的正向,每颗雷的资格只兑一次;标空则记"封印未中"归因行,并兑该安全格资源的负向 |
|
||||
| 清图批 | 清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付 |
|
||||
| 清图批 | 清图奖励对象恰好发一次;完美封印追加第二份同身份兑付 |
|
||||
|
||||
授予段当场只兑非伤害形态(增益、治疗、行动点、GP 缓冲、警觉加层);**伤害形态兑付(爆裂、直伤、反噬)以伤害单据暂存,攒到战斗段末统一结算**(2026-10-06 用户裁——地图伤害账殿后,见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)与战斗段两节)。暂存单在授予段按「将兑付」回报——授予段内依赖兑付读数的流程决策(完美排雷第二份、高光份数)依据开单时点事实判定,该时点敌人存活有保证;战斗段末实际结账若目标已灭,控制器自记「奖励未生效」行。
|
||||
授予段当场只兑非伤害形态(增益、治疗、行动点、GP 缓冲、警觉加层);**伤害形态兑付(爆裂、直伤、反噬)以伤害单据暂存,攒到战斗段末统一结算**(2026-10-06 用户裁——地图伤害账殿后,见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)与战斗段两节)。暂存单在授予段按「将兑付」回报——授予段内依赖兑付读数的流程决策(完美封印第二份、高光份数)依据开单时点事实判定,该时点敌人存活有保证;战斗段末实际结账若目标已灭,控制器自记「奖励未生效」行。
|
||||
|
||||
**充能已退役(2026-09-19 用户拍板)**:原"挖格/标中旗给充能计数 → 战斗段折算穿刺/魔法分量"的弹药系统整体移除——主角攻击回归单分量,克制策略归冒险前配置(主角 attack_type 表值,营地换型 = 改字段;未来冒险前参数面承接策略位)。攻防两条抽象分离纪律保留:攻击类型是伤害的属性轴,棋盘事实只是奖励触发源。
|
||||
|
||||
@@ -158,7 +158,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 ×
|
||||
|
||||
标错的惩罚不再是独立机制:标空封印(标了但格不是雷)= 该安全格格面资源的负向兑付(极性矩阵的"标错"分支),类型化、确定性、零额外派生——旧三选一哈希反噬(暴走 / 失衡 / 伏击+2 三池,坐标×板序号哈希派生)随格面资源批(2026-09-15)整段下线,三池内容收编为 attack / defense / blast 三型的负向行。负向兑付与"封印未中"归因行同键去重,每危险格实例只触发一次;叠层已满授予被拒时记"奖励未生效"行,不静默吞掉。
|
||||
|
||||
完美排雷全有或全无:一张板零误标且至少一次封印标中,清图奖励加倍(现行倍数读 `settlement.tres` p_perfect)。完美计数从装板起算、换图重置;高光行只记实际有效兑付(首份加被接受的第二份),首份即零兑付则高光行不发。封印标中的兑付行自带高光标记,与完美排雷的独立高光行构成两个强制高光检查点。
|
||||
完美封印全有或全无:一张板零误标且至少一次封印标中,清图奖励加倍(现行倍数读 `settlement.tres` p_perfect)。完美计数从装板起算、换图重置;高光行只记实际有效兑付(首份加被接受的第二份),首份即零兑付则高光行不发。封印标中的兑付行自带高光标记,与完美封印的独立高光行构成两个强制高光检查点。
|
||||
|
||||
举证:兑付数值全部读配置表,本节无新公式;完美乘数的入账换算见收益对照表一节。
|
||||
|
||||
@@ -212,7 +212,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 ×
|
||||
|
||||
主角攻击 = 单一基础分量(2026-09-19 充能退役批):量 = 攻击力基线(表值),类型 = 主角表 `attack_type`(营地永久强化换型 = 改字段;未来冒险前参数面承接"按关配型"的策略位)。原"普通 + 穿刺/魔法充能三分量"模型整体退役——挖掘期与战斗期之间不再有弹药供给线,克制应对从局内攒桶改为冒险前配型 + 棋盘火力(穿刺弹/魔法矢等直伤奖励道具,消费面不变)。
|
||||
|
||||
**护甲等级掷点:必中、非归零,只吃基础攻击分量**(2026-09-16 定稿落地,判定式与全部数值已拍板;判定对象措辞随敌人技能池批改述)。防守方由此多了一条数值轴——护甲等级(数据键 `armor_class`):攻击永远命中,没有 miss、没有零收益白回合;只有吃掷点的分量在掷点低于防守方护甲等级时按减伤档位打折,档位再重也不归零。判定式:攻击结算时掷单骰(骰面读表),掷点 ≥ 防守方护甲等级 + 攻击加成 → 全额;掷点 < 护甲等级按缺口(护甲等级 − 掷点)查减伤档位表打折——边界与档值读 `settlement.tres` 减伤档位表,三档皆非零档。暴击:天然最大面(读表)使该次基础攻击分量 ×暴击倍率(读表),双方同规,且天然最大面恒全额(暴击与折扣互斥;天然最大面〔nat20〕暴击是掷点层概念,与技能重击〔池技能〕严格区分——两层禁止混述)。伤害下限:**任何来源的伤害落地**至少 damage_floor(读表,全局 max(damage_floor, ·)——2026-09-20 用户拍板扩全来源:攻击双方、棋盘直伤与爆裂、地形变异、负向反噬;伤害取值域 [1, +∞),**不设上限**)——"被减伤归零 v=0"零值伤害行语义顺带退役,重档折扣的攻击行在行文本记"被压制"。掷点范围:只有基础攻击分量吃掷点——主角先手攻击的基础分量,敌方技能的基础伤害分量(= 攻击基线 × 技能伤害倍率,普通攻击倍率 1 为特例);无伤害分量的技能(增益/妨害类)不掷不折;棋盘来源一律无视护甲等级——爆裂、清障炸药、奖励即时伤害、技能必中附加分量(rider,2026-09-19)全部必中全额不掷,这是本机制的边界切口,与"奖励/惩罚必中"同构。护甲等级(数值轴,掷点打折)与护甲类型(属性轴,克制修正)严格分离:护甲类型不做护甲等级映射,克制修正点数读表、消费面不变,两概念在文档、表字段与代码键中禁止混用。数值载体:主角与敌方护甲等级行值读 hero / enemy 表的 armor_class 列(正式行值归怪物设定批,登记[待办清单](../../06-待办清单.md)),ac_up(盾牌加护)在其上加算(幅度/层数读 `status_effects.tres`)。攻击加成(`attack_bonus`)为结构位预留字段,切片恒 0,未来归元进度 / 武器附魔投值。掷点时机在结算战斗段内、每次基础攻击伤害计算时判定,消费 `armor_roll` 封闭流(逐次掷逐次取号,可逐位回放)。
|
||||
**护甲等级掷点:必中、非归零,只吃基础攻击分量**(2026-09-16 定稿落地,判定式与全部数值已拍板;判定对象措辞随敌人技能池批改述)。防守方由此多了一条数值轴——护甲等级(数据键 `armor_class`):攻击永远命中,没有 miss、没有零收益白回合;只有吃掷点的分量在掷点低于防守方护甲等级时按减伤档位打折,档位再重也不归零。判定式:攻击结算时掷单骰(骰面读表),掷点 ≥ 防守方护甲等级 + 攻击加成 → 全额;掷点 < 护甲等级按缺口(护甲等级 − 掷点)查减伤档位表打折——边界与档值读 `settlement.tres` 减伤档位表,三档皆非零档。暴击:天然最大面(读表)使该次基础攻击分量 ×暴击倍率(读表),双方同规,且天然最大面恒全额(暴击与折扣互斥;天然最大面〔nat20〕暴击是掷点层概念,与技能重击〔池技能〕严格区分——两层禁止混述)。伤害下限:**任何来源的伤害落地**至少 damage_floor(读表,全局 max(damage_floor, ·)——2026-09-20 用户拍板扩全来源:攻击双方、棋盘直伤与爆裂、地形变异、负向反噬;伤害取值域 [1, +∞),**不设上限**)——"被减伤归零 v=0"零值伤害行语义顺带退役,重档折扣的攻击行在行文本记"被压制"。掷点范围:只有基础攻击分量吃掷点——主角先手攻击的基础分量,敌方技能的基础伤害分量(= 攻击基线 × 技能伤害倍率,普通攻击倍率 1 为特例);无伤害分量的技能(增益/妨害类)不掷不折;棋盘来源一律无视护甲等级——爆裂、清障炸药、奖励即时伤害、技能必中附加分量(rider,2026-09-19)全部必中全额不掷,这是本机制的边界切口,与"奖励/惩罚必中"同构。护甲等级(数值轴,掷点打折)与护甲类型(属性轴,克制修正)严格分离:护甲类型不做护甲等级映射,克制修正点数读表、消费面不变,两概念在文档、表字段与代码键中禁止混用。数值载体:主角与敌方护甲等级行值读 hero / enemy 表的 armor_class 列(正式行值归怪物设定批,登记[待办清单](../../../../待办清单.md)),ac_up(盾牌加护)在其上加算(幅度/层数读 `status_effects.tres`)。攻击加成(`attack_bonus`)为结构位预留字段,切片恒 0,未来归元进度 / 武器附魔投值。掷点时机在结算战斗段内、每次基础攻击伤害计算时判定,消费 `armor_roll` 封闭流(逐次掷逐次取号,可逐位回放)。
|
||||
|
||||
EV 参考数不入正文(派生读数,需要时按判定式与现行表值自行复算);旧 defense_up 的减伤墙已整体退役换装为 AC(无双轨)。微调旋钮 = 档位边界与档位值,不动护甲等级值。换装点锚定:基准 AC(读 hero 表)自带的方向性偏移与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)已登记的加难杠杆扫描合并处理;其生存链区间模型按掷点带宽口径复核
|
||||
|
||||
@@ -256,7 +256,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
|
||||
停手与耗尽收轮的回合恒为同图继续;换图指令只允许出现在清图轮,其他轮发换图属于程序错误、立即上抛。秘宝背包以关卡为界:战斗结束不清背包,跨战斗延续;关卡结束时一次性清空,清空前先返回快照计数,作为"剩余秘宝随关卡结束清空"的可见反馈。战斗道具背包已随可选类删除(2026-09-15)下线。(2026-10-09 注:清空对象 = 关卡域库存 treasure_inventory——秘宝过渡形态的机械惰性承载,其投放渠道已随迭代 11 第①批全退役、现行恒空;现行秘宝=营地同调装配件、整局冒险生效不经此域,拆线归实装批——见[秘宝](../跨局循环/秘宝.md)沿革节。)
|
||||
|
||||
挖掘期刷新是玩家在挖掘中途主动换图,经[行动经济](../排雷循环/行动经济.md)扣费:扣费成功后按"旧板退役、生成新板、装板、资源预分配恰一次、变异布置"编排,本轮不打断——没有结算段、没有轮次结束。储备不足被拒是玩家决策的合法结果,不是编排错误。刷新图不发清图奖励,完美排雷计数随装板重置。
|
||||
挖掘期刷新是玩家在挖掘中途主动换图,经[行动经济](../排雷循环/行动经济.md)扣费:扣费成功后按"旧板退役、生成新板、装板、资源预分配恰一次、变异布置"编排,本轮不打断——没有结算段、没有轮次结束。储备不足被拒是玩家决策的合法结果,不是编排错误。刷新图不发清图奖励,完美封印计数随装板重置。
|
||||
|
||||
战斗开始按固定序列开张:关上配置热重载的闸门、打开增益状态容器、掷骰定敌方实际数量、生成敌人、装板并预分配资源恰一次、变异布置([地形变异](../排雷循环/地形变异.md)——棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 → 战斗资源格设置 → 变异格设置,消费 `variant_place` 封闭流)、发开轮通知并执行开轮摇号(摇定首轮各敌本拍动作)。上一关的大奖励(如血上限提升)经战斗开始入参的覆盖值通道生效,内容归[冒险结构](../冒险循环/冒险结构.md)。
|
||||
|
||||
@@ -278,7 +278,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
| 上游 | [冒险结构](../冒险循环/冒险结构.md) | 战斗触发、遭遇上下文与主角入参覆盖值(覆盖值通道已随冒险结构实现批正式接线——接口四件套正式侧承载) |
|
||||
| 下游 | [HUD 框架](../表现与支撑/HUD框架.md) | 明细流水是结算明细屏的统一数据源(未生效类行须带形状冗余防误读);状态读取走下方呈现只读面 |
|
||||
| 下游 | [战斗表现](战斗表现.md) | 结算步骤流是战斗动画回放的脚本源 |
|
||||
| 下游 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | 五条经济契约的对账结论是其调参约束输入(对账现状登记 [06-待办清单](../../06-待办清单.md)) |
|
||||
| 下游 | [难度曲线与Boss](../冒险循环/难度曲线与Boss.md) | 五条经济契约的对账结论是其调参约束输入(对账现状登记 [待办清单](../../../../待办清单.md)) |
|
||||
|
||||
### 呈现只读面:呈现层的唯一状态读取出口
|
||||
|
||||
@@ -339,7 +339,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
- 每事实恰一次:每格受理、每封印判定恰好一次;重复投递不改变结果。
|
||||
- 极性路由正确:开安全格 / 封印标中只兑正向、开危险格 / 标错封印只兑负向;兑付效果与该格资源类型的字典行一一对应。
|
||||
- 警觉共享:敌方全体共享同一警觉状态值;blast 负向逐层直授无上限(伏击退役换装——2026-09-20 八型批)。
|
||||
- 完美排雷:零误标且至少一面标中时第二份兑付恰一次并发高光行;出现误标或零标中则无高光;计数换图重置。
|
||||
- 完美封印:零误标且至少一面标中时第二份兑付恰一次并发高光行;出现误标或零标中则无高光;计数换图重置。
|
||||
- 开轮摇号:每轮恰摇一次(首轮同样;轮内刷新换图不重摇);摇定后兑现前死亡 → 本拍技能作废(零敌方行动行)、不进冷却不进保底计数;已兑现技能进冷却、保底计数按执行结果更新。
|
||||
- 战斗段次序:主角先手后逐敌"按摇定技能行兑现、结算伤害、执行上报"各恰一次;主角倒下立即短路,剩余敌人不行动。
|
||||
- 护甲等级掷点:吃掷点分量(主角基础分量、敌方技能基础伤害分量)逐次掷单骰(骰面读表)——掷点 ≥ 防守方护甲等级 + 攻击加成全额兑付;低掷按缺口查减伤档位表(边界与档值读表),档位不设零档;天然最大面暴击该分量 ×暴击倍率(读表)且恒全额;穿刺/魔法分量与棋盘来源伤害(爆裂、清障炸药、奖励即时)、必中附加分量及增益/妨害施放不掷不折。
|
||||
@@ -356,4 +356,4 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
战斗屏是挖掘与结算两种形态共用的同一张屏:常驻双舞台(白话:战斗画面与扫雷图同屏常驻,像同一块屏上的两个舞台),结算期不切换场景,裁决与理由见 [battle.md 布局决策 1](../../ux/battle.md)。所以战斗表现的工作不是另做一个战斗场景,而是在既有呈现上叠加动画密度与演出——这是本系统范围边界的第一条。
|
||||
|
||||
"发生什么"与"怎么动"因此分了工:敌人死亡是血条归零后灰化加碎裂退场、禁止瞬间消失,结算明细带逐行滚动,完美排雷与封印标中有高光行强调——这些呈现语义已由 battle.md 与 HUD 框架定稿,本文不复述。本文将来承接的只是其上的动画密度:受击反馈、弹道、退场动画等类别。
|
||||
"发生什么"与"怎么动"因此分了工:敌人死亡是血条归零后灰化加碎裂退场、禁止瞬间消失,结算明细带逐行滚动,完美封印与封印标中有高光行强调——这些呈现语义已由 battle.md 与 HUD 框架定稿,本文不复述。本文将来承接的只是其上的动画密度:受击反馈、弹道、退场动画等类别。
|
||||
|
||||
资产侧同理,这也是双舞台带来的管线约束:若舞台形态悬而未决,同一份角色资产可能因舞台不同而重画。现行解法是剪影跨舞台通用——角色剪影按"棋盘、结算画面与任一战斗舞台通用"设计,不为舞台未决问题付重画成本(art-bible §5.1;原 §3.1,2026-09-22 迁 §5.1)。
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
| 奖励未生效行的呈现 | [增益状态](增益状态.md)定稿时登记(数据源是[战斗结算](战斗结算.md)的明细协议) | 让这类行在演出中可读、不被吞掉 |
|
||||
| 效果来源格的可读化 | 同上(候选形态:缩略图或格子高亮,坐标不直接呈现) | 把"效果来自哪一格"翻译成玩家可读的演出 |
|
||||
| 坍塌显形的连锁动画 | [游戏概念](../../00-游戏概念.md)的概念层拍板——坍塌显形是[连锁展开](../排雷循环/排雷玩法.md)的玩家可见名,与音频方向同批配对登记 | 连锁展开触发时的连锁演出 |
|
||||
| 高光兑现的演出强化 | 完美排雷与封印标中强调的机制及呈现位已由战斗结算与 battle.md 定稿 | 在既有高光行呈现之上加强演出密度 |
|
||||
| 高光兑现的演出强化 | 完美封印与封印标中强调的机制及呈现位已由战斗结算与 battle.md 定稿 | 在既有高光行呈现之上加强演出密度 |
|
||||
| 伤害数字 | 归档索引中本系统的旧范围一句话即含"伤害数字" | 落点未定:预告上格原则与"敌人不占格、兑现发生在战斗画面区"如何相容,见待设计问题清单 |
|
||||
|
||||
### 前置美术测试是硬前置
|
||||
@@ -74,13 +74,13 @@
|
||||
|
||||
附着在同一次样张上的还有一项已登记的裁决(登记出处 = 敌人系统的呈现契约):敌人层绘制优先级的插入位置——白话就是敌人的姿态精灵画在绘制顺序的哪一层,会不会、以及如何与棋盘上的数字、封印印记、小人发生遮挡。它随美术测试一并裁决,不在本文提前定。
|
||||
|
||||
两项相邻裁定已有结论,设计时直接继承:**敌人呈现形态已裁决(2026-09-20 用户看对照样张拍板)——全站桩式(无框立绘+底座),主角同样站桩、状态小图标挂血条正下方,卡片式整体退役**(06 待办「敌人卡形态」解决移除,状态记录 99;battle.md 起草条款已随裁决改写,对照样张存档 `production/mockups/`);art-bible 的图标键契约已冻结,动画设计不必等图标终稿。
|
||||
两项相邻裁定已有结论,设计时直接继承:**敌人呈现形态已裁决(2026-09-20 用户看对照样张拍板)——全站桩式(无框立绘+底座),主角同样站桩、状态小图标挂血条正下方,卡片式整体退役**(待办清单「敌人卡形态」解决移除,状态记录 99;battle.md 起草条款已随裁决改写,对照样张存档 `production/mockups/`);art-bible 的图标键契约已冻结,动画设计不必等图标终稿。
|
||||
|
||||
### 一屏样张的验收标准
|
||||
|
||||
样张是前置美术测试的判定物;本节把"怎么算过"在测试组织前写成文,避免临场现定标准。成文做法参照 [art-bible](../../../art/art-bible.md) §1.4 格子样张测试(内容清单、按序验收项、判定与回退),判据全部落到已冻结的现行口径上——本节不定任何新的视觉参数。
|
||||
|
||||
样张内容清单(一组齐备,缺项即无效):按 [battle.md 布局决策 1](../../ux/battle.md) 的常驻双舞台四区布局整屏出样;角色剪影按棋盘格、结算明细带、战斗舞台三个语境同图摆放;~~敌人卡两候选形态对照(卡片式与站桩式各一版,其余内容不变)~~——**本项已由 2026-09-20 形态裁决提前完成(全站桩定稿、卡片式退役),正式样张按站桩形态单版出样即可**(owner 修订,对照义务解除;见 06 待办「敌人卡形态」销项,状态记录 99);受击、退场、高光兑现三组动画关键帧条(每组至少起、中、末三帧);敌人姿态精灵与数字、封印印记、小人同格叠放的遮挡对照组至少一组。
|
||||
样张内容清单(一组齐备,缺项即无效):按 [battle.md 布局决策 1](../../ux/battle.md) 的常驻双舞台四区布局整屏出样;角色剪影按棋盘格、结算明细带、战斗舞台三个语境同图摆放;~~敌人卡两候选形态对照(卡片式与站桩式各一版,其余内容不变)~~——**本项已由 2026-09-20 形态裁决提前完成(全站桩定稿、卡片式退役),正式样张按站桩形态单版出样即可**(owner 修订,对照义务解除;见待办清单「敌人卡形态」销项,状态记录 99);受击、退场、高光兑现三组动画关键帧条(每组至少起、中、末三帧);敌人姿态精灵与数字、封印印记、小人同格叠放的遮挡对照组至少一组。
|
||||
|
||||
验收项按序判定,第一项不过 = 整张否决(继承格子样张"数字可读性定风格"的次序):
|
||||
|
||||
@@ -150,7 +150,7 @@ MVP 不要求本文的闭环:不做清单把战斗动画演出列为 MVP 打
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
|
||||
## 待设计问题清单
|
||||
|
||||
|
||||
@@ -64,14 +64,14 @@
|
||||
|
||||
### 开战生成:主角数据行与一场 1–4 个敌人(行集可混编多种敌种)
|
||||
|
||||
战斗开始时,编排方给出遭遇坐标(关卡序号 + 节点深度),本系统据此在敌人表里取出该坐标的整个行集(一行或多行),把本战实际数量的敌人逐行生成出来——每行各按自己的数量行值实例化(单行 1 到 3 个),行集实例总数(各行开战骰后数量求和)不超过 4。同坐标多行即混编遭遇:各敌种同场上阵,生成序 = 表文件行序(行序即契约——目标顺位、摇号子流派生、开战叙述取首行都按它,加载期校验兜底:同坐标多行必须文件内相邻)。单行行集 = 既有形态:一场 1 到 3 个同种同属性敌人,来自同一行表值。战斗中敌人只减不增,不做召唤、增援、分裂。主角同时以当前血量入参生成,血量越界(高于上限或不超过 0)整单拒绝、不留半实例。
|
||||
战斗开始时,编排方给出遭遇坐标(关卡序号 + 深度带——节点深度所在的区间),本系统据此在敌人表里取出该坐标的整个行集(一行或多行),把本战实际数量的敌人逐行生成出来——每行各按自己的数量行值实例化(单行 1 到 3 个),行集实例总数(各行开战骰后数量求和)不超过 4。同坐标多行即混编遭遇:各敌种同场上阵,生成序 = 表文件行序(行序即契约——目标顺位、摇号子流派生、开战叙述取首行都按它,加载期校验兜底:同坐标多行必须文件内相邻)。单行行集 = 既有形态:一场 1 到 3 个同种同属性敌人,来自同一行表值。战斗中敌人只减不增,不做召唤、增援、分裂。主角同时以当前血量入参生成,血量越界(高于上限或不超过 0)整单拒绝、不留半实例。
|
||||
|
||||
生成拿到的是当时的表值快照:开战那一刻把每行的数值和池模板各抄一份,之后改表不影响已生成的单位。池模板快照成每实例自己的实例池(各技能冷却清零),此后各自独立摇号、互不干涉。某行的数量或攻击基线若配成了区间,由战斗结算用封闭随机流在开战时**按行各掷定一次**(每行掷自己的区间;掷定次数随行数——单行行集的掷定次数与既有口径逐位一致,混编不新增随机源形态)后传入确定值——本系统自身始终零随机;掷定后整场持久,攻击基线不逐拍重掷(数字要可读)。
|
||||
|
||||
| 规则 | 含义 |
|
||||
|---|---|
|
||||
| 敌方效果对全体生效 | 范围效果作用于所有存活敌人——混编放开后(2026-10-07 行集多行批)场上可多种敌种共存,结算语义按存活单位逐一生效、不依赖"场上全同种"前提;"只打某一个"的单体寻址暂不开放(见 P1) |
|
||||
| 行集构成纪律 | 敌人表按(关卡序号 × 节点深度)存放,同一坐标行集允许多行、允许不同敌种混编。纪律三条,违规加载期返回错误并止动:①行集内 enemy_id 唯一(同一敌种在同行集仍只许一行);②同坐标多行必须表文件内相邻,行序 = 声明序 = 生成序(行序即契约);③行集实例总数(各行骰后数量求和)≤ 4,单行数量域 [1,3] 不变(UNIT_COUNT_MAX 语义 = 单种数)。行数不设上限;同关随深度换敌种合法 |
|
||||
| 行集构成纪律 | 敌人表按(关卡序号 × 深度带)存放;深度带=节点深度的区间——逐局生成使单点深度不再固定,配池按带不按点,难度坡随带递增。同一坐标行集允许多行、允许不同敌种混编;Boss 行按终点配置,多终点各配一行(走哪条路决定打谁)。纪律三条,违规加载期返回错误并止动:①行集内 enemy_id 唯一(同一敌种在同行集仍只许一行);②同坐标多行必须表文件内相邻,行序 = 声明序 = 生成序(行序即契约);③行集实例总数(各行骰后数量求和)≤ 4,单行数量域 [1,3] 不变(UNIT_COUNT_MAX 语义 = 单种数)。行数不设上限;同关随深度换敌种合法 |
|
||||
| 主角数据行 | 主角战斗数值单独放一张全局单行表:生命上限、基础攻击力、护甲等级三字段(见调参表) |
|
||||
| 基础攻击恒存在 | 基础攻击不许配 0(配置为 0 加载期拒绝)——零基线乘任何百分比增益恒为零,"胜利路径恒有"的保障从数据层杜绝死锁 |
|
||||
| 先手攻击不是敌方技能拍 | 每次结算战斗段主角先手攻击顺位最前的存活敌人,这是固定动作;奖励侧的道具与增益是基础攻击之外的规划面伤害,归[地图战斗资源](地图战斗资源.md) |
|
||||
@@ -91,7 +91,7 @@
|
||||
| 钩爪恐魔 | 普通攻击 + 撕咬 + 蓄力 | 行值读表 | 行值读表 | L2/L3 群行位(蜘蛛同构池) |
|
||||
| 灰矮人石君王 | 普通攻击 + 重击 + 大地脉动 + **岩崩**(半血解锁) | 行值读表 | 行值读表 | L3 关底 Boss;大地脉动独占 |
|
||||
|
||||
护甲类型(轻甲 / 魔法护甲 / 重甲)与攻击类型的克制结算矩阵归[战斗结算](战斗结算.md),本系统只提供每个敌种的护甲类型数据——**护甲列自 2026-10-05 技能池执行批起改读表口径**(本表轮廓列不再记具体甲型:现行 L1 全轻甲 / L2 仅 Boss 魔法甲 / L3 heavy 登台的错峰投放以 `enemy.tres` 行值为准;历史「本表写魔甲/重甲」与表失同步的欠账顺带清偿)。护甲等级是另一条数值轴(防守方字段,基础攻击掷点的折扣判定读数):敌人表与主角表各增 `armor_class` 一列(域 [0, 999]),掷点与折扣的结算归[战斗结算](战斗结算.md),行值投值归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)——三敌种首关与主角的护甲等级行值读配置表(第一关怪物 AC 相对较低,正式行值待怪物设定投值,登记[待办清单](../../06-待办清单.md)),两条轴的概念与字段禁止混用。举证:生成与护甲为数据面规则,无计算。
|
||||
护甲类型(轻甲 / 魔法护甲 / 重甲)与攻击类型的克制结算矩阵归[战斗结算](战斗结算.md),本系统只提供每个敌种的护甲类型数据——**护甲列自 2026-10-05 技能池执行批起改读表口径**(本表轮廓列不再记具体甲型:现行 L1 全轻甲 / L2 仅 Boss 魔法甲 / L3 heavy 登台的错峰投放以 `enemy.tres` 行值为准;历史「本表写魔甲/重甲」与表失同步的欠账顺带清偿)。护甲等级是另一条数值轴(防守方字段,基础攻击掷点的折扣判定读数):敌人表与主角表各增 `armor_class` 一列(域 [0, 999]),掷点与折扣的结算归[战斗结算](战斗结算.md),行值投值归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)——三敌种首关与主角的护甲等级行值读配置表(第一关怪物 AC 相对较低,正式行值待怪物设定投值,登记[待办清单](../../../../待办清单.md)),两条轴的概念与字段禁止混用。举证:生成与护甲为数据面规则,无计算。
|
||||
|
||||
### 技能池:每敌种一张带权技能池,每拍开轮摇号出招
|
||||
|
||||
@@ -213,20 +213,20 @@ Boss 专属身份 = 专属池承载:独占技能 + 权重轮廓(**技能错
|
||||
|
||||
| 优先级 | 内容 | 后置原因 |
|
||||
|---|---|---|
|
||||
| P1 | 情境条件触发器(低血触发防御、玩家停手时偷袭技等) | 载体 = 技能行触发条件字段加列;登记[待办清单](../../06-待办清单.md) |
|
||||
| P1 | 个体化变体池(A 型好斗 / B 型守势、精英专属池) | 共识点 A 的 A2 案;登记[待办清单](../../06-待办清单.md) |
|
||||
| P1 | 全局特殊间隔旋钮 | 特殊技背靠背实测仍嫌时的备用闸;登记[待办清单](../../06-待办清单.md) |
|
||||
| P1 | 新效应词汇批(妨害技 + 必中附加分量已实装 2026-09-19——设计权威 proposal-status-vocabulary.md〔提案已吸收退役 git a6833b6a——现行事实源=敌人系统.md 词汇表/增益状态.md〕 v1.0,实装面 = L2 Boss 卓尔;多段命中经用户拍板退役) | 妨害类型 + rider_mult 字段已落地;登记[待办清单](../../06-待办清单.md)(已解决移除,去向见状态记录) |
|
||||
| P1 | 情境条件触发器(低血触发防御、玩家停手时偷袭技等) | 载体 = 技能行触发条件字段加列;登记[待办清单](../../../../待办清单.md) |
|
||||
| P1 | 个体化变体池(A 型好斗 / B 型守势、精英专属池) | 共识点 A 的 A2 案;登记[待办清单](../../../../待办清单.md) |
|
||||
| P1 | 全局特殊间隔旋钮 | 特殊技背靠背实测仍嫌时的备用闸;登记[待办清单](../../../../待办清单.md) |
|
||||
| P1 | 新效应词汇批(妨害技 + 必中附加分量已实装 2026-09-19——设计权威 proposal-status-vocabulary.md〔提案已吸收退役 git a6833b6a——现行事实源=敌人系统.md 词汇表/增益状态.md〕 v1.0,实装面 = L2 Boss 卓尔;多段命中经用户拍板退役) | 妨害类型 + rider_mult 字段已落地;登记[待办清单](../../../../待办清单.md)(已解决移除,去向见状态记录) |
|
||||
| P1 | 敌种名录扩容与投放节奏(含第二关池与投放) | 投放节奏是关卡命脉,归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md) |
|
||||
| P1 | 单体寻址("只打某一个敌人"的目标指定) | 首个单体效果上线时顺带裁决归集语义与呈现 |
|
||||
| P2 | 敌方技能图鉴 / 学习呈现(玩家侧技能知识系统) | 池不公开口径下的进阶信息面;登记[待办清单](../../06-待办清单.md) |
|
||||
| P2 | 敌方技能图鉴 / 学习呈现(玩家侧技能知识系统) | 池不公开口径下的进阶信息面;登记[待办清单](../../../../待办清单.md) |
|
||||
| P2 | ~~精英 / Boss 遭遇编码位~~(已完成——不设编码位:同层多遭遇由节点编号消解,关卡节点地图 v1.0⑥ 用户裁决) | — |
|
||||
|
||||
## 调参表
|
||||
|
||||
配置表共三张。数值纪律(2026-09-17 降级批定稿):**本文只锁字段、取值域与规则,行值一律读配置表(切片 `game/data/tables/` 的 enemy / skills / hero 表),不在本文登记具体数值——防双写失同步**;曲线调参所有权归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)。
|
||||
|
||||
敌人表(按关卡坐标存放,每坐标行集一行或多行——多行即混编遭遇,构成纪律见核心规则「行集构成纪律」;单行行集 = 既有形态):
|
||||
敌人表(按深度带坐标存放,每坐标行集一行或多行——多行即混编遭遇,构成纪律见核心规则「行集构成纪律」;单行行集 = 既有形态):
|
||||
|
||||
| 参数 | 取值域 | 说明 |
|
||||
|---|---|---|
|
||||
@@ -262,7 +262,7 @@ Boss 专属身份 = 专属池承载:独占技能 + 权重轮廓(**技能错
|
||||
|
||||
各敌种行值、池权重与池 EV 读数(含闸长期速率实测)一律读配置表与调参日志,本文不登记具体数值;EV 重锚与权重投值归[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)。
|
||||
|
||||
结构冻结(非调参项,修订走本文流程):技能类型枚举封闭、每池恰一普攻、每池增益 ≤1、死亡即不动手(摇定后兑现前死亡作废不进账)、每结算每存活敌恰行动一次、行序、先作用后推进、固定顺位、**行集构成纪律(2026-10-07 行集多行批换代——原「每坐标恰一敌种」退役:行集内 enemy_id 唯一 + 同坐标多行文件内相邻且行序 = 声明序 = 生成序 + 行集实例总数 ≤ 4)**、零随机(本系统自身语义零随机不变——摇号与掷点随机一律在[战斗结算](战斗结算.md)结算换算层按封闭随机流掷定,可回放契约 = 同种子同输入逐位复现,单敌出招轨迹可独立复放)。联动纪律:技能倍率 × 攻击(伤害拍支配)、权重 × 冷却 × 保底(池节奏三联动)禁止独立调参,联调须重跑方向性预检并同步 registry(登记 [06-待办清单](../../06-待办清单.md))。
|
||||
结构冻结(非调参项,修订走本文流程):技能类型枚举封闭、每池恰一普攻、每池增益 ≤1、死亡即不动手(摇定后兑现前死亡作废不进账)、每结算每存活敌恰行动一次、行序、先作用后推进、固定顺位、**行集构成纪律(2026-10-07 行集多行批换代——原「每坐标恰一敌种」退役:行集内 enemy_id 唯一 + 同坐标多行文件内相邻且行序 = 声明序 = 生成序 + 行集实例总数 ≤ 4)**、零随机(本系统自身语义零随机不变——摇号与掷点随机一律在[战斗结算](战斗结算.md)结算换算层按封闭随机流掷定,可回放契约 = 同种子同输入逐位复现,单敌出招轨迹可独立复放)。联动纪律:技能倍率 × 攻击(伤害拍支配)、权重 × 冷却 × 保底(池节奏三联动)禁止独立调参,联调须重跑方向性预检并同步 registry(登记 [待办清单](../../../../待办清单.md))。
|
||||
|
||||
## 验收标准
|
||||
|
||||
@@ -281,4 +281,4 @@ Boss 专属身份 = 专属池承载:独占技能 + 权重轮廓(**技能错
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -239,4 +239,4 @@
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -257,4 +257,4 @@
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -137,11 +137,11 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
|
||||
| 信息内容 | 只表达格面资源的类型(九类格面资源归五组色相呈现,色相/明度/画法规格归 [art-bible](../../../art/art-bible.md) §4.2);不表达极性(安全/雷)、不表达具体收益档位 |
|
||||
| 生命周期 | 资源身份装板期摇定(微光只是显示,不改任何游戏数据)→ 未开 ∧ 在 8 格内 → 亮;格子被挖开/连锁开/踩雷 → 灭;角色走远出圈 → 灭,走回 → 复亮;换图随旧图全重置 |
|
||||
| 关联与边界 | 信息本体(格面资源身份)归[地图战斗资源](../战斗循环/地图战斗资源.md);棋盘格态与动作合法性归[排雷玩法](../排雷循环/排雷玩法.md);与「接触带微光」同圈不同职责——接触带微光=可挖域的交互反馈光(「这里能挖」),类型微光=相邻格的类型先读(「这格是什么类型」) |
|
||||
| 实装状态 | 呈现契约已定稿(五组色相经样张 v2 冻结 2026-09-28);**已实装(迭代 30 卡① 2026-10-17 入库,合并 `7ffd1dc0`)**——2026-09-30 核证的「呈现层仅接触带微光在跑,类型微光无独立实现」状态自此注销;在役实现(五组色相 = ui_kit TG_GLOW_*、格面微光子件 + 距离场 shader)与验收转正见本节末「验收标准」类型微光行,登记面归 [06-待办清单](../../06-待办清单.md)「未开格类型徽章呈现时机」条(已完成) |
|
||||
| 实装状态 | 呈现契约已定稿(五组色相经样张 v2 冻结 2026-09-28);**已实装(迭代 30 卡① 2026-10-17 入库,合并 `7ffd1dc0`)**——2026-09-30 核证的「呈现层仅接触带微光在跑,类型微光无独立实现」状态自此注销;在役实现(五组色相 = ui_kit TG_GLOW_*、格面微光子件 + 距离场 shader)与验收转正见本节末「验收标准」类型微光行,登记面归 [待办清单](../../../../待办清单.md)「未开格类型徽章呈现时机」条(已完成) |
|
||||
|
||||
口径史注记(防复发):本概念曾只活在 06-待办清单登记与 art-bible 复述里,「常显」「全部未开格」等无范围表述于 2026-09-30 酿成理解事故;用户裁决「英雄周围 8 格」为正确口径并落位本节。教训=概念必须落地权威文档,零散复述不是定义。
|
||||
口径史注记(防复发):本概念曾只活在待办清单登记与 art-bible 复述里,「常显」「全部未开格」等无范围表述于 2026-09-30 酿成理解事故;用户裁决「英雄周围 8 格」为正确口径并落位本节。教训=概念必须落地权威文档,零散复述不是定义。
|
||||
|
||||
两带分工注记(2026-10-17 迭代 30 卡①登记):类型微光实装后,棋盘外另有「卡顶色带」(徽章/条目顶边色带,悬赏与秘宝等面板格面条目顶边的类型色带)与本格面微光并存——卡顶带 = 条目级静态色带(一眼扫类型),格面微光 = 棋盘格内动态微光(相邻先读);两套载体不同、职责不同,**禁互为引用**。两带统一面(卡顶带取值改用 art-bible 五组色值、归组改 cell_type 精确值)归 [06-待办清单](../../06-待办清单.md)「卡顶色带与格面微光两带统一」条(待办)。
|
||||
两带分工注记(2026-10-17 迭代 30 卡①登记):类型微光实装后,棋盘外另有「卡顶色带」(徽章/条目顶边色带,悬赏与秘宝等面板格面条目顶边的类型色带)与本格面微光并存——卡顶带 = 条目级静态色带(一眼扫类型),格面微光 = 棋盘格内动态微光(相邻先读);两套载体不同、职责不同,**禁互为引用**。两带统一面(卡顶带取值改用 art-bible 五组色值、归组改 cell_type 精确值)归 [待办清单](../../../../待办清单.md)「卡顶色带与格面微光两带统一」条(待办)。
|
||||
|
||||
### 可挖域指示(接触带微光):「这里能挖」的交互反馈光
|
||||
|
||||
@@ -166,7 +166,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
|
||||
| 关联与边界 | 与「未开格类型微光」同边界——灯光区起步半径=微光环 8 格(同圈起步,实测后可调);格态数据归[排雷玩法](../排雷循环/排雷玩法.md);黑暗区域渲染规格(暗淡态明度、迷雾过渡)归 [art-bible](../../../art/art-bible.md) 修订随实施批;初始展开收窄不在本案(后置与 Alpha 差异化批窗口合并再议) |
|
||||
| 实装状态 | **已实装**(2026-10-03 迭代 11 第④批已入库;四裁决逐条对账与验收线转正见本节末「实施状态」注记;渲染规格 = [art-bible](../../../art/art-bible.md) §6.5) |
|
||||
|
||||
配套拍板(同日):魔法提灯=灯甲形态(光照圈外扩 +2 格,flat 整数档)随迷雾立项;**类型微光不随视野扩大**(类型先读仍只贴身 8 格,微光锚=可挖域);提灯载体归 aux_items 新行(登记原文——2026-10-07 用户令改道为独立卡,2026-10-08 裁案A=秘宝·同调位新行落位,见下方实施状态注记;**2026-10-09 用户裁退役**——新目录 15 件未收提灯,fog_lantern 数据行与卡④接线拆除归退役删行批〔实装批〕,功能由新目录普通档「阔视野带」承接,06 登记条在册),呈现面挂「资产升档重产批」(挂账不动)。
|
||||
配套拍板(同日):魔法提灯=灯甲形态(光照圈外扩 +2 格,flat 整数档)随迷雾立项;**类型微光不随视野扩大**(类型先读仍只贴身 8 格,微光锚=可挖域);提灯载体归 aux_items 新行(登记原文——2026-10-07 用户令改道为独立卡,2026-10-08 裁案A=秘宝·同调位新行落位,见下方实施状态注记;**2026-10-09 用户裁退役**——新目录 15 件未收提灯,fog_lantern 数据行与卡④接线拆除归退役删行批〔实装批〕,功能由新目录普通档「阔视野带」承接,待办清单登记条在册),呈现面挂「资产升档重产批」(挂账不动)。
|
||||
|
||||
**实施状态(2026-10-03 迭代 11 第④批已入库)**:迷雾遮蔽+菱形灯光区跟随+暗淡记忆态上线,探针断言顺带扩(ui_input_probe case⑮)。四裁决逐条对账——①案甲视野迷雾立项(纯呈现层):迷雾开关分层验证可用(呈现态布尔,off 态=无迷雾基线),同板同动作序列与 MC 逐项比对逐位一致;②暗淡记忆态:区外已开格灰显+数字可读,明度按 [art-bible](../../../art/art-bible.md) §6.5 写入文档值(记忆板 #3A3A3A/记忆墨 #C4C4C4,provisional);③灯光半径=微光环 8 格同圈:LIGHT_RADIUS=8 参数化(曼哈顿菱形,常量);④提灯 +2、微光不扩:FOG_LANTERN_BONUS=+2(落码常量实名——本条旧记 LIGHT_RADIUS_LANTERN_BONUS 系文档侧拟名,随卡④入库对齐),类型微光仍贴身 8 邻格零改动。**〔2026-10-05 迭代14⑤接线注记〕公共接线面已就位**——`board_view.set_fog_lantern(enabled)`(true=半径 8+2、false=复原 0;写入即 refresh 重算雾态),半径算式消费 `_fog_lantern` 迷雾批已在位;ui_input_probe case⑮ 增接线断言(开=dist 9 格转可见+lantern=2/关=复原)。**〔2026-10-08 迭代 19 卡④入库注记〕载体到位+接线闭环=功能可达**——载体=秘宝·同调位新行 `fog_lantern`「魔法提灯」(treasure_items.tres 表尾追加,desc「灯光区半径+2」,slot_cost 1、营地秘宝匣同调装配获渠,行字段=用户「按推荐」全收);生产路径=runner set_attunement→enc.attunement→begin_battle 注入 orchestrator 装配集→公共只读出口 `attune_has`→组合根呈现桥 `main._sync_fog_lantern` 每战首轮装配链**显式重算含否**(🔴 复原语义=禁战斗结束隐式复原——卸装/未装配写 0,断点续玩/跨关天然正确);未装配=false 写 0=缺省零扰动基线逐位不变。顺带测试:case⑮ 载体级段(装配→lantern=2+dist R+1 转可见+卸装复原)+smoke 行字段/同调目录投影(九件→十件)/装配贯通断言+存档回归 Part 6+单测 fog_lantern_wiring(run_all 17→18 套)。原「提灯载体归 aux_items 新行随 Alpha 信息件重定批」登记去向就此注销(改道留痕=需求卡片-提灯载体道具批-2026-10-07 §一)。验收线(本篇「验收标准」视野迷雾行)逐条转正式验收:区内完整渲染/区外已开格灰显可读/区外未开格不可见/灯光区随移动跟随/两态结算逐位一致;实机目检人闸(迷雾观感/记忆态可读性/移动跟随流畅度)随出口闸。黑暗区域渲染规格承载 = art-bible §6.5(v3.5)。
|
||||
|
||||
@@ -274,7 +274,7 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
|
||||
- 一帧内意图整批到达,停手点击终结当帧收集。
|
||||
- 每次拒绝可感知:棋盘面、资源面、相位三类各有呈现且前两类可区分;停手被改判(本轮已清图)玩家可感知;拒绝反馈图标加文案双通道。
|
||||
- 结算逐行实时滚出且每行带来源标注;未生效类行聚合计数并带形状冗余;零值行不被吞;效果行的单次贡献与池合成值分示、禁相加误读。
|
||||
- 高光三档:封印标中行金框、清图奖励行金框、完美排雷全屏喝彩级——庆祝不打在空兑现上(首份即零兑付则不发高光)。
|
||||
- 高光三档:封印标中行金框、清图奖励行金框、完美封印全屏喝彩级——庆祝不打在空兑现上(首份即零兑付则不发高光)。
|
||||
- 状态槽显示图标加层数加剩余结算次数,悬停见来源;极性按键级表呈现(金满框 / 红缺框),空态整排隐藏、数据框上移补位;战斗结束全清有微文案。
|
||||
- 敌人呈现(站桩立绘,2026-09-20 全站桩拍板后载体由敌人卡改为无框立绘+底座,义务面平移不重述):意图占位 = 本拍摇定结果的动画占位(敌人技能池批三档可见性——普通攻击零表示、特殊技类型级提示〔攻/防系两型〕,不预告技能名与数值、池与权重不公开)且挖掘期全程可见、映射跨局恒定、顺位可读、血量数字可读、敌方共享层数各立绘同值(血条悬浮头顶、状态小图标挂血条正下方);死亡灰化加碎裂退场(禁止瞬间消失)且就地更新不丢灰化态。
|
||||
- 终局:胜利金蜡封、失败暮蓝氛围层(明度不变);储备清零与状态全清两反馈不合并、时序不倒挂(关卡末清库反馈义务载体 = 秘宝背包〔关卡域库存恒空机械惰性承载,拆线归实装批——见「轮次生命周期」同款节注于战斗结算〕,随秘宝 P0 落地补验收)。
|
||||
@@ -287,4 +287,4 @@ HUD框架站在玩家与全部玩法系统之间:挖掘期它是输入的翻
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -180,4 +180,4 @@ MVP 不要求本系统的闭环([不做清单](../../05-不做清单.md)把单
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -64,7 +64,7 @@ HUD 框架定稿时已为教学留好两个承载位,本系统的内容将来
|
||||
|
||||
### 内容原则
|
||||
|
||||
- **整体后置**:教学引导是 MVP 之后的打磨项(见 [不做清单](../../05-不做清单.md):先验证核心循环,再谈好听好看);改排 **Beta 阶段**(2026-10-04 用户拍板——当前 Alpha 版不做教学相关工作,登记见 [06-待办清单](../../06-待办清单.md)教学引导节头注记)。
|
||||
- **整体后置**:教学引导是 MVP 之后的打磨项(见 [不做清单](../../05-不做清单.md):先验证核心循环,再谈好听好看);改排 **Beta 阶段**(2026-10-04 用户拍板——当前 Alpha 版不做教学相关工作,登记见 [待办清单](../../../../待办清单.md)教学引导节头注记)。
|
||||
- **一次性提示**:首次触发类提示每场冒险至多出现一次,不反复轰炸。
|
||||
- **只教规则,不替玩家玩**:教学解释规则与因果,不提供自动操作或"最优解"提示。
|
||||
|
||||
|
||||
@@ -209,4 +209,4 @@
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -112,7 +112,7 @@ MVP 不要求本系统的闭环:[不做清单](../../05-不做清单.md)把设
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
|
||||
## 待设计问题清单
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
|
||||
## 待设计问题清单
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
|
||||
### 解锁界面:双入口框架
|
||||
|
||||
(2026-09-18 拍板)主菜单设营地独立入口(消费钥石、查看与管理永久强化);局内状态面板(原称暂停层,2026-10-13 改名——游戏非实时无暂停语义)也可查看钥石余额与已解锁清单(只读)。钥石余额在 HUD 常驻展示。呈现 spec 已出生:[ux/camp.md](../../ux/camp.md) v1.0(2026-09-30 审计拍板点 4A 裁决写入文档——退役/封存件「已退役/已解锁·能力封存中」呈现语义随 spec 落位);视觉终稿已随迭代 9 9A 段落码(2026-09-30)。本节锁框架与需求,呈现细节归 spec;局内状态面板入口已实装(2026-10-06 迭代 17 卡③〔时名暂停层〕——实机目检待用户,06 登记条在册)。
|
||||
(2026-09-18 拍板)主菜单设营地独立入口(消费钥石、查看与管理永久强化);局内状态面板(原称暂停层,2026-10-13 改名——游戏非实时无暂停语义)也可查看钥石余额与已解锁清单(只读)。钥石余额在 HUD 常驻展示。呈现 spec 已出生:[ux/camp.md](../../ux/camp.md) v1.0(2026-09-30 审计拍板点 4A 裁决写入文档——退役/封存件「已退役/已解锁·能力封存中」呈现语义随 spec 落位);视觉终稿已随迭代 9 9A 段落码(2026-09-30)。本节锁框架与需求,呈现细节归 spec;局内状态面板入口已实装(2026-10-06 迭代 17 卡③〔时名暂停层〕——实机目检待用户,待办清单登记条在册)。
|
||||
|
||||
### 单局循环零依赖:排期边界的推论
|
||||
|
||||
@@ -156,4 +156,4 @@ MVP 不要求本系统的闭环([不做清单](../../05-不做清单.md)整体
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -54,7 +54,7 @@
|
||||
- **生命/命中淬炼唯一入口**——生命/命中的永久强化只在树脊柱淬炼节点购买;攻/AC 的永久强化唯一入口=锻造工坊武器/护甲路线(所有权切分,2026-10-11 裁——树零攻防来源=红线)。旧属性锻造柜台(forge_levels 三档直购)退役:生命档吸收进脊柱、攻/护档由[锻造工坊](锻造工坊.md)实装批路线化改造承接,`forge_levels` 旧存档域随实装批清退(开发阶段无迁移——旧档破坏性废弃,见存档域节)。
|
||||
- **工作间钥匙模型**——锻造工坊、秘宝匣是树上两枚免费解锁节点(钥匙):点亮后展开对应工作间柜台面板,工作间内的升级操作(武器/护甲路线升级、秘宝解锁与同调装配)不在树上重复成节点。钥匙节点本身零币、不可跳过——锻造工坊钥匙无前置、直接可点亮(2026-10-11 裁),秘宝匣钥匙前置=脊柱生命第 1 级;钥匙未点亮时对应柜台锁定态,没点亮就进不去工作间。
|
||||
- **图鉴常开**——奖励格图鉴是查看类功能,不设钥匙、不设门槛。
|
||||
- 实装前过渡态:旧柜台三档直购维持现役(与树淬炼在注入面叠加),退役动作随 06「天赋树实装批」执行。
|
||||
- 实装前过渡态:旧柜台三档直购维持现役(与树淬炼在注入面叠加),退役动作随待办清单「天赋树实装批」执行。
|
||||
|
||||
### 一脊三脉:节点构成
|
||||
|
||||
@@ -157,7 +157,7 @@ A/B 态只放规则面:节点行持 a/b 双效果键(封闭规则键词表 R
|
||||
|
||||
## P0 最小闭环
|
||||
|
||||
**P0 语义已随迭代 21 全量落地(2026-10-12 入库;2026-10-13 反哺写入换代——原「33 节点现役态维持」过渡态终结)**:12 节点结构(脊柱 = 生命/命中一线一节点、Fork = 三节点双态)+ 营地统一(树 = 营地本体、生命/命中唯一入口、攻防归锻造工坊、工作间钥匙、四柜台呈现)+ 三组 Fork 双态真效果 + 购买/切换/门槛/彩蛋/注入/旧档废弃全链,机检 = 冒烟 886/0 + run_all 22 套 + probe 全绿 + MC 三组配对差 0.0pp(迭代 21 出口闸读数);残留人闸 = 实机目检营地四柜台(07 ⑳)。旧 33 节点结构随 `TREE_STRUCT_VERSION=2` 废弃重置(禁迁移,Q3 裁)。
|
||||
**P0 语义已随迭代 21 全量落地(2026-10-12 入库;2026-10-13 反哺写入换代——原「33 节点现役态维持」过渡态终结)**:12 节点结构(脊柱 = 生命/命中一线一节点、Fork = 三节点双态)+ 营地统一(树 = 营地本体、生命/命中唯一入口、攻防归锻造工坊、工作间钥匙、四柜台呈现)+ 三组 Fork 双态真效果 + 购买/切换/门槛/彩蛋/注入/旧档废弃全链,机检 = 冒烟 886/0 + run_all 22 套 + probe 全绿 + MC 三组配对差 0.0pp(迭代 21 出口闸读数);残留人闸 = 实机目检营地四柜台(体验验证清单 ⑳)。旧 33 节点结构随 `TREE_STRUCT_VERSION=2` 废弃重置(禁迁移,Q3 裁)。
|
||||
|
||||
## P1 / P2 后置
|
||||
|
||||
@@ -166,7 +166,7 @@ A/B 态只放规则面:节点行持 a/b 双效果键(封闭规则键词表 R
|
||||
| P1 | 命中线 flat 入注入面 | 消费面 = 命中折扣档归数值批(代码批注在册) |
|
||||
| P2 | 彩蛋发现率监控 | 发现率过低改树内灰色预告的反例条件在册;监控读数归呈报线 |
|
||||
|
||||
(原 P1「Fork 效果面差异化」已定案归实装批;「同调位 / 刷新升级消费面接管」已定案归实装批〔刷新升级定性缺陷修复〕;「三柜台→一树」已由营地统一模型〔案甲〕承接——三者均并入 06「天赋树实装批」,不再单列。)
|
||||
(原 P1「Fork 效果面差异化」已定案归实装批;「同调位 / 刷新升级消费面接管」已定案归实装批〔刷新升级定性缺陷修复〕;「三柜台→一树」已由营地统一模型〔案甲〕承接——三者均并入待办清单「天赋树实装批」,不再单列。)
|
||||
|
||||
## 调参表
|
||||
|
||||
@@ -181,7 +181,7 @@ A/B 态只放规则面:节点行持 a/b 双效果键(封闭规则键词表 R
|
||||
|
||||
## 验收标准
|
||||
|
||||
> **落地实态(2026-10-13 反哺写入注)**:下列各条已随迭代 21 实装并过出口闸——机检 = 冒烟 886/0(V1-V10/V16 断言顺带)+ run_all 22 套(tree_rules_v11_test 六键闭集)+ probe --click/--full 八探针 + render_gate + MC 三组同种子配对差 0.0pp(V19);残留人闸 = 实机目检营地四柜台(07 ⑳)。命中线「暂不注入」括注 = 契约 6 纪律维持(非缺陷——消费面开启归数值批)。
|
||||
> **落地实态(2026-10-13 反哺写入注)**:下列各条已随迭代 21 实装并过出口闸——机检 = 冒烟 886/0(V1-V10/V16 断言顺带)+ run_all 22 套(tree_rules_v11_test 六键闭集)+ probe --click/--full 八探针 + render_gate + MC 三组同种子配对差 0.0pp(V19);残留人闸 = 实机目检营地四柜台(体验验证清单 ⑳)。命中线「暂不注入」括注 = 契约 6 纪律维持(非缺陷——消费面开启归数值批)。
|
||||
|
||||
- 结构封闭:meta_tree.tres 通过 schema 校验零违例——12 节点、脉 / 类型 / 币词表出域即拒、Fork 三组每组恰一节点且持 a/b 双效果键、彩蛋 ≤3、effects 键封闭、零 archetype 键。
|
||||
- 行门槛可判:全树已购总等级 < 4×(N−1) 时脊柱节点第 N 级不可购;总等级达标当刻该级开放购买。
|
||||
@@ -195,4 +195,4 @@ A/B 态只放规则面:节点行持 a/b 双效果键(封闭规则键词表 R
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md)「天赋树实装批」条,本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md)「天赋树实装批」条,本节不再逐条登记。
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **一句话定位**:主菜单里与营地平级的**局外只读界面**——营地管「花」(花钥石买永久强化),生涯管「看」(看自己跨局攒下的一切:累计战绩、历史冒险、图鉴收集)。冒险进行中不给任何数据回看(2026-10-04 用户裁决 P-C7 的延续),生涯是历史数据唯一的家。
|
||||
> **状态**:期一已实装(2026-10-07 迭代 18 卡④生涯界面实装批·期一——跨局汇总页+主菜单入口+终身计数域存档 v6 落地;期二/期三另行立项,本文不预占排期)。
|
||||
> **来源**:[06-待办清单「『生涯』界面——冒险中数据回看的唯一承接处」条](../../06-待办清单.md)(2026-10-04 用户裁决 P-C7——冒险中不给数据回看,历史战报与结算明细的查看去向 = 新「生涯」界面)。
|
||||
> **来源**:[待办清单「『生涯』界面——冒险中数据回看的唯一承接处」条](../../../../待办清单.md)(2026-10-04 用户裁决 P-C7——冒险中不给数据回看,历史战报与结算明细的查看去向 = 新「生涯」界面)。
|
||||
|
||||
## 一、信息架构
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
|
||||
1. **局内不放生涯入口**。冒险进行中玩家只看状态面板(迭代 17 卡③并行批——局内只读查看钥石与解锁进度);若历史回看能从局内到达,等于绕回 P-C7 退役前的形态,禁止。
|
||||
2. **生涯内零跳转**。不放「去营地」之类的操作引导,生涯纯只读。
|
||||
3. **与 P-C7 的关系**:生涯的成立正是 P-C7 裁决的补偿面——「冒险中无回看」的已知代价由生涯承接(06 条目原文口径),本文不提议在冒险中恢复任何回看入口。
|
||||
3. **与 P-C7 的关系**:生涯的成立正是 P-C7 裁决的补偿面——「冒险中无回看」的已知代价由生涯承接(待办清单条目原文口径),本文不提议在冒险中恢复任何回看入口。
|
||||
|
||||
## 二、承载内容清单(逐段标数据可行性)
|
||||
|
||||
生涯装三段内容:跨局汇总、历史冒险回看、图鉴。这三段是 06 条目给定的骨架,本节在其上逐项落到数据源。跨局汇总与图鉴的收集类数据大多现成;历史回看的数据面已在存(见下),缺口只有三件,已随 P2 甲裁决定方向。
|
||||
生涯装三段内容:跨局汇总、历史冒险回看、图鉴。这三段是待办清单条目给定的骨架,本节在其上逐项落到数据源。跨局汇总与图鉴的收集类数据大多现成;历史回看的数据面已在存(见下),缺口只有三件,已随 P2 甲裁决定方向。
|
||||
|
||||
### 跨局汇总(期一)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
| 各关通关次数 | `clear_count(level_index)`(`save_manager.gd:238`) | 现成 |
|
||||
| Boss 首杀与总击杀 | `boss_first_kill_marked`(`save_manager.gd:242`)/ `boss_kill_count`(`:250`) | 现成 |
|
||||
| 钥石余额 | `keystone_balance`(`save_manager.gd:202`) | 现成。累计收支流水不在存(加钥石与花钥石只动余额,`:206`/`:211`)——如要做属新增流水域,期一实装批另呈报,本文只登记 |
|
||||
| ~~三柜台解锁完成度概览~~ | (整行删——2026-10-12 用户裁:生涯页不统计天赋树与账号成长态,只统计终身计数;数据源 forge_level/workshop_status/tree_nodes 三投影随天赋树实装批退役,07⑲两观测项同步摘除) | — |
|
||||
| ~~三柜台解锁完成度概览~~ | (整行删——2026-10-12 用户裁:生涯页不统计天赋树与账号成长态,只统计终身计数;数据源 forge_level/workshop_status/tree_nodes 三投影随天赋树实装批退役,体验验证清单⑲两观测项同步摘除) | — |
|
||||
|
||||
### 历史冒险回看(期二)
|
||||
|
||||
@@ -58,9 +58,9 @@
|
||||
|
||||
### 图鉴(期三)
|
||||
|
||||
- **敌人图鉴(优先,2026-10-06 用户裁 P3 甲)**:直接承接 06 在册「敌方技能图鉴 / 学习呈现」条(P2——见过 → 认得 → 记谱;归属 [HUD 框架](../表现与支撑/HUD框架.md) 与 [敌人系统](../战斗循环/敌人系统.md))。「见过」记账面 = 新增 meta 域,涉存档 schema 迁移(与期一终身计数域同属 schema 义务面;版本号按实装时点的现行版本顺延)。
|
||||
- **敌人图鉴(优先,2026-10-06 用户裁 P3 甲)**:直接承接待办清单在册「敌方技能图鉴 / 学习呈现」条(P2——见过 → 认得 → 记谱;归属 [HUD 框架](../表现与支撑/HUD框架.md) 与 [敌人系统](../战斗循环/敌人系统.md))。「见过」记账面 = 新增 meta 域,涉存档 schema 迁移(与期一终身计数域同属 schema 义务面;版本号按实装时点的现行版本顺延)。
|
||||
- **秘宝 / 奇物图鉴(随后)**:无在册条目,立项时另行登记。
|
||||
- **奖励格图鉴:只登记不激活**(P3 甲)。营地「奖励格图鉴」柜台现况 = 常开独立位(迭代 21 T6 四柜台并排重排——树淬炼柜台升主界面,图鉴本体仍空置、内容排 Alpha);该柜台已随配比预设批整体挂起冻结(2026-10-05 用户令,06 在册),重启须用户另行发令。
|
||||
- **奖励格图鉴:只登记不激活**(P3 甲)。营地「奖励格图鉴」柜台现况 = 常开独立位(迭代 21 T6 四柜台并排重排——树淬炼柜台升主界面,图鉴本体仍空置、内容排 Alpha);该柜台已随配比预设批整体挂起冻结(2026-10-05 用户令,待办清单在册),重启须用户另行发令。
|
||||
|
||||
## 三、与既有界面的关系
|
||||
|
||||
@@ -80,9 +80,9 @@
|
||||
|---|---|---|---|
|
||||
| 期一 | 跨局汇总页 + 主菜单生涯入口落位。总局数/通关局数两个终身计数域随该批做存档 schema 迁移——**✅ 已实装(2026-10-07 迭代 18 卡④)**:汇总段六类九项+营地与系统组生涯钮+存档 v6 两终身计数域〔v5 升 v6 幂等迁移〕;闸读数与代拍板追认单原记于该批出口报告,该件已按「吸收即死」在提交 8e02dcf0 删除,读数以本行与该批提交信息为准 | 提纲评审通过。实装批前置义务 = 对齐迭代 16 UI 批后的现行基线:`FS_*` 六档字号 / `TG_*` 语义色 / `BUTTON_MIN_H=40`(`ui_kit.gd:437`)/ 色板单源 / `page_shell` 模态栈纪律(`ui_kit.gd:394`)——与卡③需求卡片同款红线,本文只登记、不展开 | Alpha 后最近一个 UI 窗口 |
|
||||
| 期二 | 历史冒险回看整段:N 扩容 + 用时埋点 + 旧档摘要迁移 + 逐局列表 + 单局明细(`settlement_review_overlay.gd` 复活承接)+ 营地条带处置(P6 裁案落地) | 期一。实装批开工前须向用户呈报两件:N 终值读数(20 / 50 / 维持 10 三选一)与「用时」埋点口径(计时起点、暂停是否计入——字段级细节本文不做) | 期一落地后独立批 |
|
||||
| 期三 | 图鉴承接(敌人优先,范围与顺序随 P3 甲);「见过」记账面 schema 义务随该批执行 | 期二或独立评估 | Beta 窗口候选(与教学引导同段评估——图鉴的「认得」轴与教学相关;06 教学引导节各条 2026-10-04 起已整体标 Beta 阶段) |
|
||||
| 期三 | 图鉴承接(敌人优先,范围与顺序随 P3 甲);「见过」记账面 schema 义务随该批执行 | 期二或独立评估 | Beta 窗口候选(与教学引导同段评估——图鉴的「认得」轴与教学相关;待办清单教学引导节各条 2026-10-04 起已整体标 Beta 阶段) |
|
||||
|
||||
本地化文案权:生涯界面本体按钮、页签、空态文案归[设置与本地化](../表现与支撑/设置与本地化.md)线(全仓无多语言实装——`game/` 内 grep TranslationServer 零命中;文案权先例 = 教学文案定稿权登记于 06),实装批承接。
|
||||
本地化文案权:生涯界面本体按钮、页签、空态文案归[设置与本地化](../表现与支撑/设置与本地化.md)线(全仓无多语言实装——`game/` 内 grep TranslationServer 零命中;文案权先例 = 教学文案定稿权登记于待办清单),实装批承接。
|
||||
|
||||
## 五、待裁点
|
||||
|
||||
|
||||
@@ -257,4 +257,4 @@ playtest 预警(预注册,归难度曲线与Boss):
|
||||
|
||||
## 待办
|
||||
|
||||
本系统待办条目已统一收编至 [06-待办清单](../../06-待办清单.md),本节不再逐条登记。
|
||||
本系统待办条目已统一收编至 [待办清单](../../../../待办清单.md),本节不再逐条登记。
|
||||
@@ -1,7 +1,7 @@
|
||||
# 锻造工坊
|
||||
|
||||
> 一句话:营地的武器与护甲成长——武器锻造区三条路线、护甲锻造区三条路线各练各的,钥石消费,一次冒险各选一件,满级特效留位本期不做
|
||||
> 状态:**v0.8 迭代 21 反哺④残余写入文档(2026-10-13)**——「树侧钥匙」句换代:钥匙节点 forge_workshop 已随迭代 21 实装在役(free 无前置、点亮展开本柜台、锁定态零购买路径),原「实装归 06」悬置指针终结。承 v0.7 迭代 23 反哺写入(2026-10-13)——正文补 Q1 等级语义衔接(0 = 未练合法态)、效果类型闭集 24→31 订正、实装边界 1 回填实装实态(已随迭代 23 入库)。承 v0.6 完备性检查批(承 v0.5 B 组定案——**17 裁点全数清零**:1 丙 / 1a 一次冒险一件武器+一件甲 / 1b 营地可换·战斗禁换 / 1c 免费 / 2a 伪裁点消解 / 2b 随 3a 消解 / 3a 魔法护甲统一命名 / 3b 敌方攻击缺省普通·克制反向当下生效 / 3c 每型独立路线 / 4a 前缓后陡 / 4b 锻造内自闭环 / 4b-1 并联双通道 / 5b 纯钥石 / 5c 零材料 / 6 满级特效本期不做留扩展位 / 8a 工作间两区定名;2c、5a 已于 v0.4 结案)。**v0.6 变更(2026-10-12 完备性检查批·grilling 用户逐条裁)**:现役事实基线换代(forge_products 行族已随迭代 21 T5 退役、定价参数改历史考古口径);**两根类型轴所有权移交**(默认值归锻造工坊配置常驻持有、hero.tres 静态表两轴删除、选中态写存档+参数面注入);「与秘宝混合制同构」与「激活穿刺系专长」两处无依据表述订正删除。**满级特效本期不做**(只留扩展位);全部数值 provisional,终值归 Alpha 收官 M4 数值批〔未排期〕;实装已随迭代 23 入库(2026-10-12)
|
||||
> 状态:**v0.8 迭代 21 反哺④残余写入文档(2026-10-13)**——「树侧钥匙」句换代:钥匙节点 forge_workshop 已随迭代 21 实装在役(free 无前置、点亮展开本柜台、锁定态零购买路径),原「实装归待办清单」悬置指针终结。承 v0.7 迭代 23 反哺写入(2026-10-13)——正文补 Q1 等级语义衔接(0 = 未练合法态)、效果类型闭集 24→31 订正、实装边界 1 回填实装实态(已随迭代 23 入库)。承 v0.6 完备性检查批(承 v0.5 B 组定案——**17 裁点全数清零**:1 丙 / 1a 一次冒险一件武器+一件甲 / 1b 营地可换·战斗禁换 / 1c 免费 / 2a 伪裁点消解 / 2b 随 3a 消解 / 3a 魔法护甲统一命名 / 3b 敌方攻击缺省普通·克制反向当下生效 / 3c 每型独立路线 / 4a 前缓后陡 / 4b 锻造内自闭环 / 4b-1 并联双通道 / 5b 纯钥石 / 5c 零材料 / 6 满级特效本期不做留扩展位 / 8a 工作间两区定名;2c、5a 已于 v0.4 结案)。**v0.6 变更(2026-10-12 完备性检查批·grilling 用户逐条裁)**:现役事实基线换代(forge_products 行族已随迭代 21 T5 退役、定价参数改历史考古口径);**两根类型轴所有权移交**(默认值归锻造工坊配置常驻持有、hero.tres 静态表两轴删除、选中态写存档+参数面注入);「与秘宝混合制同构」与「激活穿刺系专长」两处无依据表述订正删除。**满级特效本期不做**(只留扩展位);全部数值 provisional,终值归 Alpha 收官 M4 数值批〔未排期〕;实装已随迭代 23 入库(2026-10-12)
|
||||
> 成文底稿:`design/research/提案-武器锻造系统-2026-10-07.md`(提案过程稿——全部备选案与淘汰依据、用户原案逐条对照见彼;本文只收录定案口径;**未启用的满级特效候选池亦留彼 §八**,不进正文)
|
||||
|
||||
## 设计目的
|
||||
@@ -29,7 +29,7 @@
|
||||
|---|---|
|
||||
| 钥石与营地柜台结构 | [元进度](元进度.md)——本系统即属性锻造柜台攻击/护甲两档的改造,消费货币与柜台框架归彼 |
|
||||
| 数值终值(每级增量/等级上限/价格/基础 AC/特效值) | Alpha 收官 M4 数值批——本文全部数值只有量纲与锚点,标 provisional |
|
||||
| 代码与数据表落地 | 实装批(待办在册 06)——迁移面见下文,含数值验证义务 |
|
||||
| 代码与数据表落地 | 实装批(待办在册待办清单)——迁移面见下文,含数值验证义务 |
|
||||
| 克制矩阵与伤害结算 | [战斗结算](../战斗循环/战斗结算.md)——矩阵九格结构冻结是既有条款,本系统零改动 |
|
||||
| 局内属性奖励件 | [奇物](../冒险循环/奇物.md)——账号层永久强化与单冒险加算区分层并存,互不侵入 |
|
||||
| 存档读写 | [存档序列化](../表现与支撑/存档序列化.md)——实装后新增等级与当前选中域,旧档废弃口径随实装批(开发阶段无迁移) |
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
**工作间内两区并列**(A③ 裁):武器区/护甲区并列呈现,机制层面零顺序门槛;同屏分栏或页签切换=呈现细节归 HUD 批。
|
||||
|
||||
**树侧钥匙(2026-10-11 裁:无前置)**:本柜台由天赋树「锻造工坊」钥匙节点展开——free 节点、**无前置条件、直接可点亮**(原「攻击淬炼累计 ≥2 级 且 护甲淬炼累计 ≥2 级」等级阈值前置,随树脊柱砍攻/护两线作废);未点亮时本柜台锁定态,点亮后开放。树侧结构换代(脊柱砍攻/护两线 + 一线一节点 + Fork 一节点双态,三轮换代 **29→21→15→12**,地脉共鸣条件效果随之换代——2026-10-12 订正:原记「29→21」系 v0.6 时点旧数,树侧现行终态 12 节点)设计权威文档 = [天赋树](天赋树.md);**钥匙节点 forge_workshop 已随迭代 21 实装在役(2026-10-12 入库——free 无前置、点亮展开本柜台、锁定态零购买路径),2026-10-13 反哺写入换代原「实装归 06」悬置指针**。
|
||||
**树侧钥匙(2026-10-11 裁:无前置)**:本柜台由天赋树「锻造工坊」钥匙节点展开——free 节点、**无前置条件、直接可点亮**(原「攻击淬炼累计 ≥2 级 且 护甲淬炼累计 ≥2 级」等级阈值前置,随树脊柱砍攻/护两线作废);未点亮时本柜台锁定态,点亮后开放。树侧结构换代(脊柱砍攻/护两线 + 一线一节点 + Fork 一节点双态,三轮换代 **29→21→15→12**,地脉共鸣条件效果随之换代——2026-10-12 订正:原记「29→21」系 v0.6 时点旧数,树侧现行终态 12 节点)设计权威文档 = [天赋树](天赋树.md);**钥匙节点 forge_workshop 已随迭代 21 实装在役(2026-10-12 入库——free 无前置、点亮展开本柜台、锁定态零购买路径),2026-10-13 反哺写入换代原「实装归待办清单」悬置指针**。
|
||||
|
||||
**注入衔接(所有权切分,2026-10-11 裁)**:每根数值轴全游戏单一来源——生命/命中唯一入口=树脊柱;**攻击唯一入口=武器路线、AC 唯一入口=甲路线(本系统)**,树零攻防来源(红线)。锻造路线加值 = 当前武器攻击加值 + 当前甲路线 AC 加值,与树淬炼 flat、局内加算区在开局属性注入面分层相加(通道现行事实源 = [元进度](元进度.md)「跨局注入」节);锻造加值只按**当前选中**的武器/甲生效(长剑的练度在换带长枪后回家待命,不丢级、不生效)。
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
- **奇物属性件**:分层并存,互不侵入。武器/护甲锻造动的是**账号层基础值**(营地永久强化,冒险开始时经参数面传入);奇物属性件动的是**单次冒险加算区**(关间不清零、冒险结束清零)。本设计不扩奇物"生命/AC/攻击"三轴封闭分类、不动渠道开关表、不往局内池加任何东西。入战 AC 的三层语义清晰可加:甲型基础 AC + 营地甲路线等级加值 + 局内加算区累加。
|
||||
- **克制矩阵(2a 裁:伪裁点消解)**:**伤害结算只有一套,类型不是待仲裁的槽位**。每份伤害单据自带 `attack_type` 栏(`damage_controller.gd:19` 单据九栏之一)——主角基础攻击单据取 `hero.tres` 的 attack_type(由当前武器决定),棋盘格面直伤件(穿刺弹 / 魔法矢)取 reward 表自身的类型(预期 `reward.tres:131/142`)——各自按**同一张 3×3 矩阵** `armor_effectiveness` 取克制修正(`_counter_delta`)。因此"叠加 / 替代 / 互斥"三分法不成立:两根参数之间无冲突可裁,也不需要任何共存规则。二者的真实差异只在**单据形态与生命周期**(主角基础攻击走掷点折扣 + 暴击;棋盘克制直伤走必中全额不掷点 `_resolve_board_typed`;且前者 = 账号层永久、后者 = 局内一次性)——那是两条不同的伤害来源,不是两种类型语义。矩阵九格满格结构冻结(2b 随 3a 自动消解:魔法护甲 = 现役 ward,不存在第四甲型)。
|
||||
- **工作间两区定名 + 路线行名(8a 裁)**:锻造工坊工作间内先立两个分区,各装三条锻造路线(路线行名统一为"件名 + 锻造")——①**武器锻造区**:长剑锻造 / 长枪锻造 / 法杖锻造;②**护甲锻造区**:轻甲锻造 / 重甲锻造 / 魔法护甲锻造。两区并列、机制层面零顺序门槛(A③ 裁),区内路线各自独立升级。
|
||||
- **局内不存在武器/护甲的提升(8a 裁连带核结)**:用户指出"局内没有可以提升武器的地方"——**属实**。查实:局内商店货架的三行(现役旧名"攻击锻升 / 生命锻升 / 护甲锻升"——**2026-10-12 已改名,见下**),**正是奇物「属性值提升」三轴(攻击 / 生命上限 / AC)的货架前台标签**——`shop.tres` pool_shelf 六行用 `kind_id` 指向 `adventure_table.gd:29` 的 CURIO_KINDS 常量(其中 feat_ticket 为显式豁免种、不入该词表,5/6 命中——`config_db_cross_validator.gd:109-110`),`display_name` 只是这层的前台名;买到的东西进**冒险加算区**、冒险结束清零。所以它们是**奇物属性件**,既不是武器也不是护甲,局内不改变任何锻造等级。命名上的隐患确凿:行名里的"锻"字与营地锻造同字,是误读的直接来源。**2026-10-12 用户裁决(8a 连带·C 项)**:三行按「属性 + 后缀」改名,后缀取 **祝福** ⇒ **攻击祝福 / 生命祝福 / 护甲祝福**(D&D 参照调研:Blessing 为"神赐恩惠"义;用户同时裁定——参考书目里出现过「祝福」一词不构成禁用)。**改名后这三条在语感上彻底脱离"锻造"一词,与营地工作间不再有字面关联**,而结构关系不变(仍是奇物「属性值提升」三轴,机制分类名不动,只改玩家向显示名)。**执行归独立改名小批**(06 在册)——**已于 2026-10-12 执行完毕并合入 master**(commit `0c93a71` / `0144f60`:shop.tres 六行锻升→祝福;reward.tres / status_effects.tres 护甲强化→盾牌加护;smoke_all.gd:1199 断言同步;注记段四处残留肃清;冒烟两跑 864 项 0 失败)。
|
||||
- **局内不存在武器/护甲的提升(8a 裁连带核结)**:用户指出"局内没有可以提升武器的地方"——**属实**。查实:局内商店货架的三行(现役旧名"攻击锻升 / 生命锻升 / 护甲锻升"——**2026-10-12 已改名,见下**),**正是奇物「属性值提升」三轴(攻击 / 生命上限 / AC)的货架前台标签**——`shop.tres` pool_shelf 六行用 `kind_id` 指向 `adventure_table.gd:29` 的 CURIO_KINDS 常量(其中 feat_ticket 为显式豁免种、不入该词表,5/6 命中——`config_db_cross_validator.gd:109-110`),`display_name` 只是这层的前台名;买到的东西进**冒险加算区**、冒险结束清零。所以它们是**奇物属性件**,既不是武器也不是护甲,局内不改变任何锻造等级。命名上的隐患确凿:行名里的"锻"字与营地锻造同字,是误读的直接来源。**2026-10-12 用户裁决(8a 连带·C 项)**:三行按「属性 + 后缀」改名,后缀取 **祝福** ⇒ **攻击祝福 / 生命祝福 / 护甲祝福**(D&D 参照调研:Blessing 为"神赐恩惠"义;用户同时裁定——参考书目里出现过「祝福」一词不构成禁用)。**改名后这三条在语感上彻底脱离"锻造"一词,与营地工作间不再有字面关联**,而结构关系不变(仍是奇物「属性值提升」三轴,机制分类名不动,只改玩家向显示名)。**执行归独立改名小批**(待办清单在册)——**已于 2026-10-12 执行完毕并合入 master**(commit `0c93a71` / `0144f60`:shop.tres 六行锻升→祝福;reward.tres / status_effects.tres 护甲强化→盾牌加护;smoke_all.gd:1199 断言同步;注记段四处残留肃清;冒烟两跑 864 项 0 失败)。
|
||||
- **专长联动**:现役专长均为跨攻击类型通用件(不按 attack_type 分系——既无 normal 条件件、也无 pierce/magic 条件件,`feats.tres` 29 行实证),武器类型差异只经克制矩阵的 matchup 行体现;「选某类型激活某系专长」在本项目专长体系下不成立(2026-10-12 v0.6 订正:原「选长枪激活穿刺系专长的价值」为无现役对应物的表述,删除)。现有死条件 `magic_vs_magic`(魔法打"魔法甲"——现行甲域无此型,永不可触发 = 废卡):**改值复活案已作废(2026-10-11 D 组核结——所属件魔网织者在素材库表二专长 27 件退役删除名单)**,该行随专长退役删行批整体消解,本系统零触碰(2026-10-12 订正:本节原写"推荐复活"与实装边界第 3 条结案口径冲突,随本批对齐)。「魔法打魔法护甲」的克制语义由矩阵九格承载(magic vs ward = −1),新专长框架如需同类件 = 彼批的新卡设计题。
|
||||
- **天赋树**:同钥石经济;注入面分层叠加且**所有权切分**(2026-10-11 裁)——树脊柱只持生命/命中淬炼(树零攻防来源),攻/AC 唯一入口=本系统路线加值(只按当前选中武器/甲生效)。
|
||||
- **冒险前资源配比预设批(挂起冻结中)**:本设计不预设该批的重启口径、不占用其任何冻结裁决、不改其冻结文本。该批重启时须显式核对两处交汇面:①预算包络是否把武器类型计入克制注;②容量柜台与锻造工坊工作间(同在营地)的资源竞争关系。
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
> **Platform Target**: PC (Steam)——Keyboard/Mouse 主(全部操作纯鼠标可完成)、Gamepad Partial 非 MVP、Touch None(同 technical-preferences)
|
||||
> **上游修订依赖**: 弃图刷新挖掘期受理已随布局决策 4 落稿上游(2026-09-12:行动经济 Active 受理 + 每轮上限 2 次、排雷玩法 Active·仅刷新路径装板、战斗结算 `refresh_board` 编排 + 恒 same_board、registry `rap_rules` 同步——见 OQ11)
|
||||
> **Template**: UX Spec
|
||||
> **覆盖子屏**: 挖掘阶段主界面 · 轮末自动战斗结算(战斗结算明细屏) · 胜利/失败终局 · ~~结算明细回看~~(2026-10-04 P-C7 裁:冒险中回看退役归「生涯」——06 登记)
|
||||
> **覆盖子屏**: 挖掘阶段主界面 · 轮末自动战斗结算(战斗结算明细屏) · 胜利/失败终局 · ~~结算明细回看~~(2026-10-04 P-C7 裁:冒险中回看退役归「生涯」——待办清单登记)
|
||||
> **输入义务**: 排雷玩法×6 + 行动经济 + 战斗结算×3 + 敌人系统×4 + 增益状态×4 + 地图战斗资源×5(旧版 systems-index 门禁增补记录,缺项即 blocking——承接落点见 §13 义务对照表)
|
||||
|
||||
---
|
||||
@@ -48,7 +48,7 @@
|
||||
战斗屏是**上下文相关屏**(仅由战斗节点进入),非顶层常驻。关卡内节点进度常驻关卡画面(独立屏的常驻枢纽形态,节点态即进度呈现:已过暗金、当前金、候选通道亮金、Boss 紫赤描边),战斗屏零进度承载——战斗结束返回关卡画面,玩家始终知道自己身处图中第几环。
|
||||
|
||||
- 上游屏:关卡画面(选路枢纽,屏级 spec:[level-map.md](level-map.md)——2026-09-27 建档;分支节点图形态,切片第一关为单链特例)
|
||||
- 下游屏:关卡画面(选路枢纽,spec:[level-map.md](level-map.md))——战斗胜利后返回选路;落地奖励/恢复节点时由关卡画面域开节点覆盖层(NodeOverlay,恢复点全量服务、奖励点=三选一构筑点)确认兑付——节点奖励屏 spec 已出生:[node-reward.md](node-reward.md) v1.0(2026-09-29 迭代 7 T5 顺带出生,06 条目兑现;2026-09-27「不立独立 spec」拍板随之被取代)
|
||||
- 下游屏:关卡画面(选路枢纽,spec:[level-map.md](level-map.md))——战斗胜利后返回选路;落地奖励/恢复节点时由关卡画面域开节点覆盖层(NodeOverlay,恢复点全量服务、奖励点=三选一构筑点)确认兑付——节点奖励屏 spec 已出生:[node-reward.md](node-reward.md) v1.0(2026-09-29 迭代 7 T5 顺带出生,待办清单条目兑现;2026-09-27「不立独立 spec」拍板随之被取代)
|
||||
- 下游屏:关卡结算([level-clear.md](level-clear.md),末节点 Boss 胜利——2026-09-27 建档)
|
||||
- 平行屏:图例面板 overlay(本屏内域屏);~~结算明细回看 overlay~~(2026-10-04 P-C7 退役——冒险中回看归「生涯」)
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
1. **扫雷图本体**(第一注视点,挖掘期占视觉预算大头):两格态(已开格 / 未开格——排雷玩法×6② 一眼可辨)、数字、封印印记、小人、可挖域(8 向相邻常驻指示)、变异纹理
|
||||
2. **本轮行动资源**:AP 余量(决定还能挖几格)、RAP 池(弃图刷新货币)、停手/弃图刷新按钮——紧贴棋盘上缘(动作发生地)
|
||||
3. **战果兑现面**(结算期为第一注视点):敌人血条变化、逐行结算明细、高光(完美排雷/封印标中)
|
||||
3. **战果兑现面**(结算期为第一注视点):敌人血条变化、逐行结算明细、高光(完美封印/封印标中)
|
||||
4. **规划面**(边缘注视,常读):攻击值与修正来源(C 区数据框)、敌人意图徽标与顺位(B 区站桩);HP 与状态效果由单位本体(B 区站桩)唯一承载——数据框不重复展示
|
||||
5. **进度语境**:轮次/阶段指示、图例入口(关卡节点进度常驻关卡画面,战斗屏不承载——见 Navigation Position)
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
- 攻击值 + 修正来源角标(战斗在消费、且站桩上无展示)
|
||||
- 护甲 AC(2026-09-26 二轮拍板补入——我方 AC 的唯一展示面;掷点判定轴,受击侧可读性)
|
||||
- 秘宝槽(四槽壳在役、空槽占位——局内两步使用与关卡域库存随秘宝过渡形态退役〔迭代 11 第①批〕;现行秘宝 = 营地同调装配件,本区展示内容归装配面批次重接)
|
||||
- 奇物列表(**已实装**——现行标题「特性」,迭代 9 卡⑫ 接线 + 迭代 12 呈现补齐;锻造/大奖励/关卡奖励节点获得的属性提升;与战斗内状态效果分区呈现,防「战斗结束被清」误读。**2026-10-12 奇物呈现修复批整改落地**:原逐流派聚合行退役改逐件行「件名 ×N」,零内部分类字样上屏——原 06「内部分类禁写入玩家可见面」条顺带完成移除)
|
||||
- 奇物列表(**已实装**——现行标题「特性」,迭代 9 卡⑫ 接线 + 迭代 12 呈现补齐;锻造/大奖励/关卡奖励节点获得的属性提升;与战斗内状态效果分区呈现,防「战斗结束被清」误读。**2026-10-12 奇物呈现修复批整改落地**:原逐流派聚合行退役改逐件行「件名 ×N」,零内部分类字样上屏——原待办清单「内部分类禁写入玩家可见面」条顺带完成移除)
|
||||
- **D 区扫雷图 · AP 条**
|
||||
- AP 点阵 + 停手按钮(现行)
|
||||
- **RAP 读数(点阵+数字)**——自 C 区迁入,置于弃图刷新按钮旁(2026-09-26 拍板:RAP 唯一消费口 = 弃图刷新,资源数字贴消费按钮)
|
||||
@@ -134,7 +134,7 @@
|
||||
- **对战斗表现双舞台陷阱的输入**:game-concept 登记的"战斗发生在棋盘上还是独立场景"悬念,由本布局裁决为**第三形态:同屏双舞台**。战斗表现定稿前的"战斗表现美术测试"(一屏样张)应按此布局出样
|
||||
- 敌人不占格(敌人系统已锁)与本布局自洽:敌人实体只存在于 B 区,棋盘永远只有数字/封印印记/小人/变异
|
||||
|
||||
**棋盘格尺寸公式**:`cell = floor(min(D宽−padding, D高−AP条−padding) / N)`,N∈{10,12,13,14}(棋盘生成 D1–D4 行;13×13/14×14 随第三关内容批扩板)。1280×800 下 12×12 → 36px,10×10 → 45px(格边长三方取值不一致已登记 06,扩板定档时统一处置)。数字字号 ≥ 格高的 50%,像素字体整数倍缩放(canvas_items stretch 保证清晰)。
|
||||
**棋盘格尺寸公式**:`cell = floor(min(D宽−padding, D高−AP条−padding) / N)`,N∈{10,12,13,14}(棋盘生成 D1–D4 行;13×13/14×14 随第三关内容批扩板)。1280×800 下 12×12 → 36px,10×10 → 45px(格边长三方取值不一致已登记待办清单,扩板定档时统一处置)。数字字号 ≥ 格高的 50%,像素字体整数倍缩放(canvas_items stretch 保证清晰)。
|
||||
|
||||
### Component Inventory
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
| B | 敌人站桩立绘 ×1–3 | 站桩(无框立绘+落影) | 血条+顺位徽标落底盘(同主角排式,2026-10-06 方案二;**大体档〔显示 >150px 级:drider/duergar_king〕允许向上溢出盒顶 N px(N 由实装批 C0 实算,与背景/光柱无关——现行溢出区为临时光柱氛围层,正式舞台背景插画到位后该层退役,判定不变),血条仍落底**)、状态小图标挂血条正下方(敌方 side-wide 增益各立绘同值呈现——蓄力/硬化/削弱/暴走/警觉;伏击层数通道随 2026-09-20 八型批退役——数据源 = 战斗结算流水经增益状态投影,敌人系统×4④;2026-09-16 敌人技能池批改型:行为环点拆除〔proposal §2.4〕,意图占位 = 本拍摇定结果的动画占位——普通攻击零表示 / 特殊技类型级提示,**意图徽标悬浮头顶上方,探顶档压胸**,敌方增益〔蓄力/硬化〕入状态图标行);死亡灰化加碎裂退场直接演在立绘上(2026-09-20 全站桩拍板,敌人卡退役);血条下名字行 = 纯名字 + 护甲类型图标(形状+色相双通道,与状态图标同行共存,状态空时为纯名字;原「底座下」随石板退位改落底排),AC 数值不展示——2026-09-26 终裁(用户口径:HP 信息已足够,攻防数值不直接写上;护甲类型图标保留克制对策可读性——类型可读、数值隐藏) | 只读 | 新建 |
|
||||
| B | 轮次·阶段指示 | 标签 | "第 N 轮 · 挖掘中/结算中"(终局定格「战斗结束」;Boss 变体附 Boss 名) | 只读 | 新建 |
|
||||
| B | 结算明细带 | 滚动列表 | 结算期逐行流水([战斗结算](../systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋) 9 类行协议);挖掘期收起为上轮摘要 1 行 | 点击展开明细回看 overlay(2026-10-04 P-C7 退役——带保留单行摘要,回看归「生涯」) | battle_log_view.gd 改造 |
|
||||
| B | ~~结算明细回看~~(已退役) | —— | 2026-10-04 P-C7 裁:冒险中回看退役归「生涯」——settlement_review_overlay.gd 为未接线遗留件(06 登记) | —— | —— |
|
||||
| B | ~~结算明细回看~~(已退役) | —— | 2026-10-04 P-C7 裁:冒险中回看退役归「生涯」——settlement_review_overlay.gd 为未接线遗留件(待办清单登记) | —— | —— |
|
||||
| C | 攻击数据框 | 直角细线数据框 | 攻击值 + 修正来源角标 + 护甲 AC(AC 二轮拍板 2026-09-26 补入——我方 AC 唯一展示面;原「HP/攻击/RAP 显示」——HP 与 RAP 随信息归属拍板迁出本组件) | 只读 | HUDView 拆分 |
|
||||
| C | 秘宝槽 | 图标槽 ×4 | 四槽壳在役、空槽占位(标题「秘宝」,悬停「秘宝槽(空)」);局内两步使用与关卡域库存随秘宝过渡形态退役——现行秘宝 = 营地同调装配件(秘宝.md),本区展示重接归装配面批次 | 悬停 tooltip | hero_info_panel_view.gd |
|
||||
| C | 奇物列表(现行标题「特性」) | 列表/图标行 | 已实装(迭代 9 卡⑫ 接线、迭代 12 呈现补齐;2026-10-12 呈现修复批整改——逐件行「件名 ×N」,零内部分类字样);锻造/大奖励/关卡奖励节点获得的属性提升——中循环数据,因改变战斗数学且无其他展示面准入战斗屏;与战斗内状态效果(B 区站桩)分区,防「战斗结束被清」误读 | 只读 | hero_info_panel_view.gd |
|
||||
@@ -219,7 +219,7 @@
|
||||
| **挖掘阶段(默认态)** | 轮次开始 / 结算完毕回 Active 相位 | B 区敌人待机(意图徽标全程可见——敌人系统 Rule 8⑤),D 区棋盘可交互,AP 条点亮;能量 2/10,唯悬停呼吸微光在动(§2.1) |
|
||||
| **结算阶段** | round_end 触发(AP 尽/停手/清图) | D 区锁输入(棋盘去饱和 + 金橙色相偏移,明度不变,数字保持全对比);B 区播放五段编排演出(授予段 → 增益/减益段 → 战斗段 → 衰减段 → 判定段,战斗结算 段序冻结);明细带展开 84px 逐行滚动;阶段指示→"结算中";能量 7/10 |
|
||||
| **清图瞬间** | 全部安全格开完 | 金粒喷泉 + 彩纸 3–5s 即收(§2.3);清图奖励行强调呈现(H2,地图战斗资源×④——金框高亮行);随后换新图 install |
|
||||
| **完美排雷高光** | 战斗结算高光判定(全图标中/零误标类) | 全屏喝彩级反馈(§2.2 最高档):金光带加倍扫射 + 「完美排雷 ×2」横幅——P2 爆发兑现节点(创意风险 #2 落点) |
|
||||
| **完美封印高光** | 战斗结算高光判定(全图标中/零误标类) | 全屏喝彩级反馈(§2.2 最高档):金光带加倍扫射 + 「完美封印 ×2」横幅——P2 爆发兑现节点(创意风险 #2 落点) |
|
||||
| **封印标中高光** | flag_judged{hit=true} | 该印记化暴击弹道加重 + 明细行金框——chord 缺席的即时兑现补偿(创意风险 #3 落点) |
|
||||
| **敌人死亡呈现** | 敌 hp=0(流水行击杀标记) | 该敌人立绘血条归零 → 立绘灰化 + 碎裂退场(**禁瞬消**——敌人系统资产下限「死亡瞬间须可辨识、禁止直接消失」,同等 blocking);战斗段短路的重击被掐灭不产行动明细行,由行序可推断(战斗结算);动画密度归战斗表现,呈现语义在本 spec |
|
||||
| **胜利终局** | verdict=win | 金蜡封落于**扫雷图区中央**(§2.4 字面锚定),全屏暖白金 overlay;判定行 + 「下一节点」;明细回看入口;状态图标行全清可见反馈(增益状态×4④——B 区主角站桩图标逐枚化光收回 + 微文案「状态随战斗结束消散」,防「我的增益哪去了」误读) |
|
||||
@@ -389,7 +389,7 @@
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Player journey map——已完成(2026-09-27 欠账处置拍板:不建)**:实现前补建的前提随战斗屏实装失效;情绪曲线事实源 = art-bible §2 能量档,真人体验观测与结论回流 = [07-体验验证清单](../07-体验验证清单.md)
|
||||
1. **Player journey map——已完成(2026-09-27 欠账处置拍板:不建)**:实现前补建的前提随战斗屏实装失效;情绪曲线事实源 = art-bible §2 能量档,真人体验观测与结论回流 = [体验验证清单](../../../体验验证清单.md)
|
||||
2. **无障碍专项文档——已完成(2026-09-27 拍板:不另立 design/accessibility-requirements.md)**:基线(对比/色弱/字号/闪烁/减动效/纯鼠标/中文字体)权威 = [HUD框架](../systems/表现与支撑/HUD框架.md)「可访问性基线」节;正式分层(文本缩放/色盲模式/按键重映射)归 [设置与本地化](../systems/表现与支撑/设置与本地化.md) 待设计问题清单 #1(设置项全集裁决时定);本 spec §Accessibility 为呈现侧落地承诺
|
||||
3. **interaction-patterns.md——已建成**(2026-09-12 /team-ui 批当日落稿,本条「未建」系过期陈述):本 spec 引入的四 pattern(双源拒绝反馈/逐行兑现明细带/悬停预承诺光标/点阵资源显示)均已入库成文(pattern 1/3/4/5)
|
||||
4. **战斗表现美术测试**(game-concept 已登记,战斗表现批次前强制):本 spec 的常驻双舞台分屏(布局决策 1)是测试的布局输入——**敌人卡形态已裁决(2026-09-20 用户看对照样张拍板):全站桩式(无框立绘+底座),主角同样站桩、状态小图标挂血条下,卡片式整体退役**;本 spec 已随裁决改写(原"按卡片式起草"条款作废),站桩式不改变分区结构。对照样张存档 `production/mockups/enemy-card-vs-standee-mockup.html`
|
||||
@@ -419,7 +419,7 @@
|
||||
| 排雷玩法×6 | ⑤ 路径预览裁决 | 布局决策 3 |
|
||||
| 排雷玩法×6 | ⑥ 单意图帧+voided+拒绝可感知 | §Interaction Map + §States 拒绝子态 + AC |
|
||||
| 行动经济 | AP/RAP 常显+刷新入口+清零反馈+双源 | §Layout D 区 AP 条(RAP 自 C 区迁入——2026-09-26 信息归属拍板)+ §Transitions + AC |
|
||||
| 战斗结算×3 | ①高光行强调呈现(封印标中金框行/清图 H2 金框行)②完美排雷呈现形态 ③棋盘生成 EC5 零合法板提示/补偿/回退形态(toast 提示;补偿/回退不提供——三形态选型留痕 2026-09-12) | §States 高光/清图 + §States 拒绝子态 + §AC;注:结算明细屏 = 战斗结算 UX Flag 事项(本 spec 即其 /ux-design 产物),非 战斗结算×3 状态记录 |
|
||||
| 战斗结算×3 | ①高光行强调呈现(封印标中金框行/清图 H2 金框行)②完美封印呈现形态 ③棋盘生成 EC5 零合法板提示/补偿/回退形态(toast 提示;补偿/回退不提供——三形态选型留痕 2026-09-12) | §States 高光/清图 + §States 拒绝子态 + §AC;注:结算明细屏 = 战斗结算 UX Flag 事项(本 spec 即其 /ux-design 产物),非 战斗结算×3 状态记录 |
|
||||
| 敌人系统×4 | 意图映射/恒定/顺位/敌方池 | §Component Inventory 敌人站桩立绘 + **意图线索映射目录(敌人系统×4① 成文占位,资产定稿归战斗表现)** + AC |
|
||||
| 增益状态×4 | 常显+空态/来源标注/溢出/去留 | §States 空态 + §AC |
|
||||
| 地图战斗资源×5 | 身份恒读/明细行/清图强调(旧版状态记录五条中「窗口」「清空反馈」随格面资源批下线——清空反馈义务后载秘宝背包、复随秘宝过渡形态退役消解(现行 = 营地同调装配,秘宝.md)) | §Component Inventory BoardView 奖励徽章 + §States 清图/高光 + §Data Requirements query_identity + AC |
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- 呈现数据源 = MetaProgression 只读投影经组合根喂入;本层不持有系统资源、不算价。
|
||||
- 退役/封存件呈现语义已随旧目录整体退役消解(探宝罗盘/显影尘/地穴全图卷等状态行已删——迭代 22 旧 12 行删除后目录内零 deferred/retired 态,2026-10-12);
|
||||
- 双入口框架(主菜单入口+局内状态面板只读入口〔2026-10-13 由「暂停层」改名——游戏非实时无暂停语义〕)的需求面归元进度「解锁双入口框架」节;状态面板入口已实装(2026-10-06 迭代 17 卡③〔时名暂停层〕——实机目检待用户,06 登记条在册),其呈现面不归本 spec。
|
||||
- 双入口框架(主菜单入口+局内状态面板只读入口〔2026-10-13 由「暂停层」改名——游戏非实时无暂停语义〕)的需求面归元进度「解锁双入口框架」节;状态面板入口已实装(2026-10-06 迭代 17 卡③〔时名暂停层〕——实机目检待用户,待办清单登记条在册),其呈现面不归本 spec。
|
||||
|
||||
## 秘宝匣柜台呈现细则(迭代 22 实装态,2026-10-12)
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab
|
||||
- 数据源:[战斗结算](../systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)步骤流(9 类行封闭协议(现行);公共字段 `type/source_class/origin_cell/round_index/display_name/value/highlight?`);**无来源行 = 断言失败**
|
||||
- 行模板:「{行为} {值} → {目标}(来源)」——来源标注(第 N 轮 · 安全格式);行文案最长 token = 来源标签(4 汉字封闭枚举),模板预留 40% 膨胀宽度
|
||||
- 聚合纪律:未生效类行(奖励未生效——现行 9 类行协议唯一未生效型,2026-09-20 收敛)按聚合计数呈现(防逐行惩罚轰炸);衰减行聚合计数;效果行 value=单次贡献值、禁与 pool_value(池合成值)相加呈现;零值行可见不吞(免伤轮归因不断点)
|
||||
- 高亮:highlight 字段 {flag_hit, clear_reward_h2, perfect_broken} → 金框强调;完美排雷 = 独立高光行 + 全屏喝彩级反馈
|
||||
- 高亮:highlight 字段 {flag_hit, clear_reward_h2, perfect_broken} → 金框强调;完美封印 = 独立高光行 + 全屏喝彩级反馈。`perfect_broken` 的判据句读作「本板首次标错受检」,不等于完美资格已失——专长「完美保险」可在本场首次标错时豁免,实际资格按有效误标(误标总数 − 豁免数)判定
|
||||
- 布局:固定槽高 + clip_contents;正文 ≥14px;数值暗底高对比;未生效类行带 icon/划线形状冗余
|
||||
- 击杀标记呈现(H3 数据源 = 战斗结算流水击杀结果字段)
|
||||
|
||||
@@ -213,3 +213,4 @@ MVP 未使用标准控件(不入库):toggle / dropdown / input field / tab
|
||||
2. Gamepad 批次引入时,需为 pattern 4/12/15 增补手柄映射节(焦点序已在 battle.md OQ8 留位)
|
||||
|
||||
---
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
1. **GP 增量展示面归属——已完成(2026-09-27 用户拍板)**:战斗胜利出现战斗总结结算、展示获得的资源(GP 增量+总量;迭代 10 起追加 Boss/精英钥石增量行);战斗失败出现冒险总结结算、确认后回存档首页。本屏不承载 GP 与钥石,无遗留。两面呈现 spec:[settlement-summary.md](settlement-summary.md) v1.1
|
||||
2. **末关分支形态**:末关通关时直通冒险结算(现行维持)——随完整屏形态实装批一并定
|
||||
3. **钥石呈现三面化——已完成(迭代 10 契约 6 推荐案)**:战斗总结结算(Boss/精英击杀增量行)+ 冒险总结结算(收束明细)+ 营地存量——「进账时刻 / 收束明细 / 存量可用」不同义不构成重复展示;本屏旧「钥石增量+累计」呈现随大奖励重构退役
|
||||
4. **视觉终稿**:以表现层样张过审为前置(06 待办登记);本 spec 的 AC 为功能性判据,不含视觉终稿判据。**本批渲染层验证冻结(用户 2026-10-01)**——实装后目检清单顺带交付,实机目检待用户批准后补闸
|
||||
4. **视觉终稿**:以表现层样张过审为前置(待办清单登记);本 spec 的 AC 为功能性判据,不含视觉终稿判据。**本批渲染层验证冻结(用户 2026-10-01)**——实装后目检清单顺带交付,实机目检待用户批准后补闸
|
||||
5. **替换子交互视觉形态——已随渠道退役**:秘宝件出池后本屏无道具件候选,替换子交互不于本屏触发(原功能性判据留档)
|
||||
|
||||
---
|
||||
@@ -7,7 +7,7 @@
|
||||
> **Template**: UX Spec
|
||||
> **覆盖范围**: 独立屏形态(关卡停点全景)· 入口停点 · 岔路停点 · 单链停点 · 放弃冒险入口
|
||||
> **不覆盖**: 节点覆盖层(NodeOverlay——奖励/恢复兑付反馈,spec:[node-reward.md](node-reward.md))、商店覆盖层(spec:[shop.md](shop.md))、关卡结算屏([level-clear.md](level-clear.md))、战斗屏([battle.md](battle.md))
|
||||
> **机制权威**: [关卡节点地图](../systems/冒险循环/关卡节点地图.md)(选路、兑付、停点语义;节点图逐局随机的来源另见[关卡节点图生成](../systems/冒险循环/关卡节点图生成.md)——只换图的来源,不换本屏的呈现语义);**系统契约**: [HUD框架](../systems/表现与支撑/HUD框架.md)(订阅纪律、模态层级、可访问性基线)
|
||||
> **机制权威**: [关卡节点地图](../systems/冒险循环/关卡节点地图.md)(选路、兑付、停点语义);图的来源 = [关卡节点图生成](../systems/冒险循环/关卡节点图生成.md)(参数化长图——只换图的来源与形态,不换本屏的呈现语义;多终点时各终点均带 Boss 标记,标记语义不变、数量随图;图形态随参数变化,布局自适应,形态不进呈现代码);**系统契约**: [HUD框架](../systems/表现与支撑/HUD框架.md)(订阅纪律、模态层级、可访问性基线)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 定位
|
||||
|
||||
本 spec 承载两个**已实装**的结算总结呈现面(2026-09-27 用户拍板定名,接线登记 06 状态记录):
|
||||
本 spec 承载两个**已实装**的结算总结呈现面(2026-09-27 用户拍板定名,接线登记待办清单状态记录):
|
||||
|
||||
- **战斗总结结算**——冒险模式普通战斗胜利时的总结覆盖层(战斗小循环的出口呈现);
|
||||
- **冒险总结结算**——冒险终结(倒下 / 末关通关 / 放弃)时的终局覆盖层(冒险大循环的收束呈现)。
|
||||
@@ -24,13 +24,13 @@
|
||||
## 冒险总结结算(冒险终结面)
|
||||
|
||||
- 触发:主角倒下(verdict=lose)/ 末关通关 / 玩家放弃——三结局同面(`adventure_finished` 收束)。
|
||||
- 载体:与战斗面同一个 battle_end_overlay(2026-09-29 迭代 6 T7 接线)——末关通关走 `open_victory`(金蜡封 +「本局 GP 入账 · 合计 N」行,GP 收束清零前的呈现侧影子账);倒下 / 放弃走 `open_defeat`(暮蓝)。无独立「冒险终局面板」实体,06 状态记录旧称随本条完成。
|
||||
- 载体:与战斗面同一个 battle_end_overlay(2026-09-29 迭代 6 T7 接线)——末关通关走 `open_victory`(金蜡封 +「本局 GP 入账 · 合计 N」行,GP 收束清零前的呈现侧影子账);倒下 / 放弃走 `open_defeat`(暮蓝)。无独立「冒险终局面板」实体,待办清单状态记录旧称随本条完成。
|
||||
- 呈现内容:冒险摘要五件事(定义归[冒险结构](../systems/冒险循环/冒险结构.md))+ 钥石写入明细(= 本局逐杀 Boss 掉落合计;消费归[元进度](../systems/跨局循环/元进度.md) settle_adventure——迭代 10 起大奖励面不再产钥石)+ GP 合计行。
|
||||
- 出口:确认 → 回存档首页。
|
||||
|
||||
## 口径
|
||||
|
||||
- **命名(2026-09-27 拍板文本口径)**:战斗总结结算 / 冒险总结结算。历史变体退役清单(正文一律改用正名):①「战斗结算画面」——battle.md 旧口径(06 归属行已判登记偏差);②「结算画面」单用——两义词(level-clear 语境=冒险面、battle 旧语境=战斗面),禁用;③「战斗胜利总结结算」——level-clear 与 06 状态记录旧用四字变体;④「摘要结算画面」——冒险结构 旧称(冒险总结结算的摘要呈现位);⑤「冒险终局面板」——06 状态记录对同一 overlay 冒险面打开形态的俗称,无独立实体。06-待办清单 现状段按其「措辞不改」体例保留历史原文,以本清单为命名准绳。
|
||||
- **命名(2026-09-27 拍板文本口径)**:战斗总结结算 / 冒险总结结算。历史变体退役清单(正文一律改用正名):①「战斗结算画面」——battle.md 旧口径(待办清单归属行已判登记偏差);②「结算画面」单用——两义词(level-clear 语境=冒险面、battle 旧语境=战斗面),禁用;③「战斗胜利总结结算」——level-clear 与待办清单状态记录旧用四字变体;④「摘要结算画面」——冒险结构 旧称(冒险总结结算的摘要呈现位);⑤「冒险终局面板」——待办清单状态记录对同一 overlay 冒险面打开形态的俗称,无独立实体。待办清单现状段按其「措辞不改」体例保留历史原文,以本清单为命名准绳。
|
||||
- **GP / 钥石呈现分工**(battle.md OQ14 完成口径 + 迭代 10 扩):GP = 本 spec 战斗面(增量+总量)+ 关卡画面常驻总量;钥石 = 本 spec 战斗面(Boss/精英击杀增量行,迭代 10 起)+ 冒险面(收束明细)+ 营地存量。钥石多处并存系「进账时刻 / 收束明细 / 存量可用」不同义,不构成重复展示(level-clear.md OQ3 完成维持)。
|
||||
- **失败面不呈战利品**:冒险总结结算不呈现 GP 增量(倒下即终语义,失败侧恒不呈现)。
|
||||
- **禁重复展示**:本面不呈状态效果、不呈结算明细正文(明细回看 overlay 已随 P-C7 退役——冒险中明细回看归「生涯」)——[battle.md](battle.md) 信息归属两原则(循环限定 + 单一展示面)对面级呈现同样适用。
|
||||
@@ -13,7 +13,7 @@
|
||||
- 模态覆盖层(dim + 居中):标题「商店」/ GP 余额读数/ 令牌级货架卡(进店自共享池抽 3 张:显示名·效果·现价——六行池面含回复与专长凭证;买一件 = 池少一件、货架随即全架重抽)/ 购买按钮(余额不足禁用置灰——pattern 8 禁用态即理由)/ 刷新按钮(GP 付费刷新,价 = ceil(20×1.8^本次进店已付费刷) provisional,余额不足禁用)/ 离开按钮。
|
||||
- 购买意图 `buy_requested` 经组合根转发 runner(pool 模式载荷 = kind_id);成功后货架整架重建(买 1 全重抽反馈)。刷新意图 `refresh_requested` 经组合根转发 presenter。
|
||||
- 离开 → 关卡画面兑付确认回选路(零自动推进)。
|
||||
- 刷新与货架口径(2026-10-04 P-C2+Q2 裁决,SR-1 落码):基础免费 0 次 + GP 付费刷新 + 池源同源(与三选一同一令牌库本)——原「无独立刷新、全陈列」裁决随该批被推翻;天赋侧「刷新次数」增益预留 free_count 位(06 登记条在册)。
|
||||
- 刷新与货架口径(2026-10-04 P-C2+Q2 裁决,SR-1 落码):基础免费 0 次 + GP 付费刷新 + 池源同源(与三选一同一令牌库本)——原「无独立刷新、全陈列」裁决随该批被推翻;天赋侧「刷新次数」增益预留 free_count 位(待办清单登记条在册)。
|
||||
|
||||
## 口径
|
||||
|
||||
|
||||
+14
-4
@@ -11,7 +11,7 @@
|
||||
| 安全格 | 没有惩罚的格子——挖开安全并获得其上信息与收益 |
|
||||
| 整图 | 单张棋盘(的整个范围)——原「整图」退役(2026-10-11 用户裁)。例:整图叠加层、整图作用域 |
|
||||
| 叠加层(整图叠加层) | 秘宝产生的信息的自持容器:按效果分类的格集,随图消亡、不回写棋盘(见[秘宝](systems/跨局循环/秘宝.md)) |
|
||||
| 封印 / 控制 | 封印动作的机制名/玩家名:封印印记落在危险格上为「标中」(危险格被控制、化险为利),封错为「标空」(原「旗 / 控制」,2026-10-18 用户裁:「旗」概念不存在——动作=封印、印记=封印印记) |
|
||||
| 封印 / 控制 | 封印动作的机制名/玩家名:封印印记落在危险格上为「标中」(危险格被控制、化险为利),封错为「标空」(原「旗 / 控制」,2026-10-18 用户裁:「旗」概念不存在——动作=封印、印记=封印印记);代码契约键与标识符统一用 `seal_*`(原 `flag_*` 全族换代),板面单字字形=「封」 |
|
||||
| 真视 | 直接告诉玩家某范围内哪些格是危险格的效果(秘宝承重契约:一次最多 3×3,见[秘宝](systems/跨局循环/秘宝.md)核心规则) |
|
||||
| 类型微光 | 棋盘上按格子类型显示的提示光效(视野参数控制其显示范围) |
|
||||
| 换图 / 弃图刷新 | 清完一图进入下一图 / 花储备放弃当前图重开一张(代价见[行动经济](systems/排雷循环/行动经济.md)) |
|
||||
@@ -81,18 +81,25 @@
|
||||
|
||||
两类的判定与道具定义卡必答见[核心循环](01-核心循环.md)「结算目标分两类:总线与抽屉」节。
|
||||
|
||||
## 战斗与效果
|
||||
|
||||
| 术语 | 定义 |
|
||||
|---|---|
|
||||
| `seal_missed` | 封印落空事件(原 `flag_missed` 换代):封印动作落在安全格上时发出,订阅面据此做限次与豁免判定(迭代 31 卡② 落地) |
|
||||
| `forgive_seal_miss` | 本场首次封印落空豁免——专长「完美保险」的效果类型,`domain=battle` 首例;有效误标=误标总数 − 豁免数,豁免不改写误标账(迭代 31 卡② 落地) |
|
||||
|
||||
## 开发流程
|
||||
|
||||
| 术语 | 定义 |
|
||||
|---|---|
|
||||
| 逐需求点验收清单 | 需求卡片验证闸的必备清单:每个需求点列明测什么(具体断言或行为)/怎么测(测试落点与注入方式)/谁验(自动闸或用户目检);查不出验收条目的需求点=卡片不成立(规矩见 req-card 技能,测试落码写法见 [code-conventions](../engineering/code-conventions.md) §3.5) |
|
||||
| 测试方式两分法 | 能力验证与体验验证分开走(2026-10-11 用户定):能力验证=通过调试接口直接修改游戏数据(改钥石/槽位/解锁状态等,不走正常资源积累流程);体验验证=用户目检完整流程,登记 07-体验验证清单观测项 |
|
||||
| 测试方式两分法 | 能力验证与体验验证分开走(2026-10-11 用户定):能力验证=通过调试接口直接修改游戏数据(改钥石/槽位/解锁状态等,不走正常资源积累流程);体验验证=用户目检完整流程,登记体验验证清单观测项 |
|
||||
|
||||
## 用语规范(2026-10-15 用户令立)
|
||||
|
||||
词面纪律的登记处。通用禁用词与选词以**简明技术中文(STC)词表**为权威:外部技能文件 `C:\Users\DK\.zcode\skills\simplified-technical-chinese\词表.md`,约 170 条,配检查脚本 check.py。
|
||||
|
||||
本仓库的机器闸 = `tools/doc_term_audit.py`,已吸收本节项目裁项。本节只登记两类裁项:A 已裁替换、B 豁免条款。历史退役词(雷格/正主/落籍等 7 组)见下方「退役词对照」节,不与 A 表重复。
|
||||
本仓库的机器闸 = `tools/doc_term_audit.py`,已吸收本节项目裁项。**词表冻结**:本节与该闸的禁用词表不再扩充,扩表唯一入口=用户实读反馈并经用户裁决(由非迭代批写入,agent 禁自行增词)。本节只登记两类裁项:A 已裁替换、B 豁免条款。历史退役词(雷格/正主/落籍等 7 组)见下方「退役词对照」节,不与 A 表重复。
|
||||
|
||||
### A. 已裁替换(写新文本必须用「用」列)
|
||||
|
||||
@@ -113,11 +120,12 @@
|
||||
|
||||
注:「注销」仅用于清单条目办结义(销号/销账的替换词);「结束登录状态」义仍按 STC 用「退出登录」。
|
||||
|
||||
### B. 豁免条款(三类文本不改写)
|
||||
### B. 豁免条款(四类文本不改写)
|
||||
|
||||
1. **历史文本**:git 提交说明、裁决记录中引用用户原话的句子——保留原用词,不按 A 表追改。
|
||||
2. **对照性文本**:「退役词对照」表本身、「原 ××」式更名记载、禁令引举句——旧词在这些位置是内容本身,不是残留。
|
||||
3. **玩家可见专名与机制词**:按「对外命名纪律」节与各系统文档现行用语使用(标中/踩雷等机制词在文档内部照常用)。
|
||||
4. **被检查的禁字不算使用**:守卫代码里作为检查对象的禁字字面量——`game/smoke/smoke_all.gd` 的 `contains("旗")` 检查项、`game/tests/unit/presentation/ui_font_asset_test.gd` 的字形集负探针、`game/data/tables/feats.tres` 头注的「desc 禁『雷/旗』字面」、`tools/doc_term_audit.py` 的退役词表定义行——不属换代范围:被检查的字不是对该字的使用,改写等于自毁守卫。术语清扫的复扫口径按 **game/ 全部文件类型、不排除任何扩展名**执行(迭代 31 卡① 实测:金币/插旗/雷格/惩罚格/`flag` 均 0 命中,残留仅为上述保留面)。
|
||||
|
||||
存量口径:A 表生效前已入库的正文不做强制清扫;文件后续被批次触碰时,顺带按 A 表换代。
|
||||
|
||||
@@ -131,10 +139,12 @@
|
||||
| 巢穴(机制表述) | 危险格(世界观风味层的巢穴隐喻〔00-游戏概念/美术纹样名〕除外) |
|
||||
| 正主 | 现行事实源 |
|
||||
| 落籍 | 已入库 |
|
||||
| 完美排雷 | 完美封印(2026-10-19 用户裁:全库统一换代,含代码内部注释;玩家面正式名=完美封印,2026-10-21 迭代 32 卡①反哺入表) |
|
||||
| 工作日志 | 状态记录 |
|
||||
| 金币 | GP(2026-10-18 用户裁:GP 是唯一正式命名,「金币」系 AI 噪音全库禁用) |
|
||||
| 插旗 | 封印(2026-10-18 用户裁:封印=正式动作名,全库统一,非比喻非「对外表达」别名) |
|
||||
| 旗(游戏概念语境) | 封印印记(2026-10-18 用户裁:「旗」概念不存在——封印动作落下的印记称封印印记) |
|
||||
| flag_*(代码契约键/标识符) | seal_*(2026-10-18 用户裁全族换代;迭代 31 卡① 已清零) |
|
||||
|
||||
## 对外命名纪律(玩家可见命名,2026-10-11 用户令)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
注:「封印」现有 170 处命中(46 文件),其中大部分是正确用法(封印=正式术语),但部分语境把它当「比喻」或「对外表达」描述,需修正措辞。
|
||||
|
||||
**读数订正(2026-10-18 需求卡片 v1.4 实测,本表数字保留作历史读数不作施工依据)**:上表的 game/ 侧数字用现行代码复现不出。同一命令口径实测(`game/` 内、区分大小写逐处计数):含 `flag` 的 .gd 53 个文件 595 处 + .tres 4 个文件 22 处,剔除 `size_flags_*`(70 处/19 文件)与 `_fatal_flag`(4 处)后**净 543 处/42 个文件**;「金币」39 处、「插旗」62 处、「雷格」75 处、「惩罚格」1 处、「旗」含复合词 376 处。两处口径错误:①中文三词用的是**全库**读数(全库实测 金币 381/插旗 600/雷格 394 处),被记进了 game/ 范围行;②flag 侧低估约一倍(原记 270+22+22=314)。施工判据以 [需求卡片-代码术语刷新批](../reports/需求卡片-代码术语刷新批-2026-10-18.md) §2 的复现命令为准。
|
||||
**读数订正(2026-10-18 需求卡片 v1.4 实测,本表数字保留作历史读数不作施工依据)**:上表的 game/ 侧数字用现行代码复现不出。同一命令口径实测(`game/` 内、区分大小写逐处计数):含 `flag` 的 .gd 53 个文件 595 处 + .tres 4 个文件 22 处,剔除 `size_flags_*`(70 处/19 文件)与 `_fatal_flag`(4 处)后**净 543 处/42 个文件**;「金币」39 处、「插旗」62 处、「雷格」75 处、「惩罚格」1 处、「旗」含复合词 376 处。两处口径错误:①中文三词用的是**全库**读数(全库实测 金币 381/插旗 600/雷格 394 处),被记进了 game/ 范围行;②flag 侧低估约一倍(原记 270+22+22=314)。施工判据见需求卡片 §2 的复现命令(卡片已随迭代 31 实现批退役,git 历史可考;合回 `4f953ce8`)。
|
||||
|
||||
---
|
||||
|
||||
@@ -119,4 +119,4 @@ design/research/(提案、需求卡片)、design/qa/(审查报告)、des
|
||||
|
||||
## 6. 建议拆批
|
||||
|
||||
见同目录需求卡片:`reports/需求卡片-代码术语刷新批-2026-10-18.md`
|
||||
拆批已由迭代 31 执行(卡① 代码术语刷新+卡② 凭证件裁削,合回 `4f953ce8`/`3e076e22`);对应需求卡片已随实现批退役,git 历史可考。
|
||||
@@ -1,9 +1,10 @@
|
||||
# 提案:效果引擎无状态化(计数器外置·分桶)
|
||||
|
||||
- **日期**:2026-10-19
|
||||
- **版本**:v1.3(2026-10-21 依需求卡片 §12 裁决订正 §7.7/§7.10.3/§7.10.12 三处口径——现行身份标记;改动沿革只查 git commit log)
|
||||
- **性质**:架构重构提案(restructure,非 refactor)——现役设计被判为错误形态,不是打磨
|
||||
- **上游**:`design/gdd/06-待办清单.md:627`「计数器寿命域未统一」条目(2026-10-18 登记);本提案推翻该条目的解法方向,见 §8
|
||||
- **状态**:**草案·设计未闭合**——§7 设计已定值、§7.7 采集定不发起,但 §4.2–§4.5 核验出 3 项阻塞级实现设计缺口(A1 ctx 结构/A2 桶接口/A3 树合成行漏校验)+ 5 项次级缺口 + 6 项需求澄清未对齐,**尚不可写卡**
|
||||
- **状态**:**设计已闭合·可写卡**——§7 设计定值、§7.7 采集定不发起;§4.3 六项卡点、§4.4 八项实现设计缺口、§4.5 六项需求澄清已于 2026-10-20 复核轮全部闭合(定值与推导依据集中在 §7.10,§3.2/§4.1–§4.5/§5 各节已按定值同步改写);其中 B6(dig_combo 死读数删除)与 §7.9 五条同口径属「约束可推、可否决」项
|
||||
- **范围**:代码 + 文档,**不含美术**
|
||||
- **关联已定项**:迭代 31 卡①代码术语刷新批 / 卡②凭证件裁削批(完美保险按现役 `domain=battle` 落地)——本提案若先落地,卡②的专长行需同步改形
|
||||
|
||||
@@ -77,29 +78,31 @@
|
||||
|
||||
### 3.1 四只桶(宿主决定寿命,不需要寿命字段)
|
||||
|
||||
| 桶 | 宿主(候选,见 N1) | 清空方式 | 现役对应 |
|
||||
| 桶 | 宿主(定值,见 N1) | 清空方式 | 现役对应 |
|
||||
|---|---|---|---|
|
||||
| `round` | 战斗域对象 | 轮始 `= {}` | `_reset_on_round_start` 名单(4 枚:`round_blast_spent` / `instinct_atk` / `round_mines` / `round_digs`) |
|
||||
| `round` | `BattleOrchestrator` 公开成员(ctx 构造时挂引用) | 轮始整桶清空(引擎 dispatch 的 round_start 特判泛化——**就地清空**,禁 `= {}` 重赋值断引用) | `_reset_on_round_start` 名单(4 枚:`round_blast_spent` / `instinct_atk` / `round_mines` / `round_digs`) |
|
||||
| `battle` | `BattleOrchestrator`(每战 new) | 随宿主销毁 | `rebuild` 的 `_counters = {}`(8 枚:`geo_types` / `helm_layers` / `skip_pts` / `echo_seals` / `smith_seals` / `fuse_pts` / `stop_swap` / `insurance_used`) |
|
||||
| `level` | 关卡域对象 | 进关 `= {}` | `adventure_runner.gd:840-842` 硬编码名单 |
|
||||
| `level` | `LevelRunner.level_counters` 公开成员 | 进关随宿主 new 天然空 | `adventure_runner.gd:840-842` 硬编码名单 |
|
||||
| `adventure` | `run_ledger.adventure_counters`(沿用) | 新冒险置空(`adventure_runner.gd:255`) | `domain=adventure`(4 枚:`twin_digs` / `pain_layers` / `tree_sense_sharp_used` / `tree_sense_guard_used`) |
|
||||
|
||||
🔴 **枚数订正**:现役 distinct counter_id **共 16 枚**(4+8+4),非草案初版的「13 枚」。13 系卡② 落地前的旧数(未含 `insurance_used`),且与草案自身分桶子项相加(4+8+4=16)自相矛盾。
|
||||
|
||||
**寿命语义不再需要任何字段**:对象活着值就活着,对象重建值自然归零。
|
||||
|
||||
### 3.2 引擎接口改动
|
||||
### 3.2 引擎接口改动(2026-10-20 定稿——写口形态见 §7.10.8)
|
||||
|
||||
| 现在 | 之后 |
|
||||
|---|---|
|
||||
| `dispatch(event, payload, facts)` | 增 ctx 参数(携带本战可读写计数器容器引用集) |
|
||||
| `counter()` | 改从 ctx 读(纯查询,不存) |
|
||||
| `counter_add()` / `counter_spend()` | **删除**——写由发射点落到传入计数器容器 |
|
||||
| `dispatch(event, payload, facts)` | `dispatch(ctx, event, payload, facts)`——ctx=`EffectContext` 四桶引用束(§7.10.1),每战构造一次 |
|
||||
| `counter()` | `counter(ctx, counter_id)`(纯查询读 ctx,引擎不自持——N5) |
|
||||
| `counter_add()` / `counter_spend()` | **实例方法删除**;改**静态纯函数**(ctx 首参)——7 个施加点调用形不变、语义单点(§7.10.8) |
|
||||
| `counters_export()` / `counters_seed()` | **删除** |
|
||||
| `has_effect_on()` / `validate_trigger_row()` | 保留(纯函数) |
|
||||
| — | 新增纯函数 `bucket_of(counter_id)`(按前缀判桶) |
|
||||
| — | 新增 `EffectContext` 类(`game/core/effect_context.gd`,四桶引用束字段)+ `static bucket_of(ctx, counter_id)`(前缀→目标桶字典,解析单点) |
|
||||
|
||||
写口迁移(**共 6 处,非 5 处**):`action_economy.gd:110/113/173`、`damage_controller.gd:351/464`、**`grant_segment.gd:327`** 由 `_fx.counter_add(...)` 改为对 ctx 计数器容器写入;`battle_orchestrator.gd:717` 的 `counter_spend` 同理。
|
||||
round_start 清零点保留在引擎 `dispatch` 特判内(`:210-212` 现役挂点),由按名单清零泛化为 `ctx.round_bucket.clear()`——前缀分桶后名单集合≡整桶键集,行为逐位等价(挂点不挪的依据见 §7.10.1)。
|
||||
|
||||
写口迁移(**共 7 处**:action_economy.gd `:108/:111/:169`、damage_controller.gd `:351/:464`、grant_segment.gd `:327`(`_apply_counter_hit`——卡② 保险写入唯一通路)、battle_orchestrator.gd `:714`(counter_spend,受理闸))由 `_fx.counter_add(...)` 改为静态 `EffectEngine.counter_add(ctx, ...)`;调用点行号系 2026-10-20 实查(草案初版 :110/:113/:173/:717 系 +2/+4 行漂移,A6 补列面见 §7.10.6)。
|
||||
|
||||
🔴 `grant_segment.gd:327` 是**卡② 完美保险的写入计数唯一通路**(`:315` `_apply_counter_hit(h)` 落 `insurance_used`),漏掉它=保险「每战限次一次」失效(恒 0 → 每次标错都豁免)。
|
||||
|
||||
@@ -124,14 +127,14 @@
|
||||
| T | 内容 | 估时 |
|
||||
|---|---|---|
|
||||
| T0 | worktree/分支;冒烟·单元·探针·三闸基线记档 | 0.25d |
|
||||
| T1 | 桶承载对象新建 + 四宿主接线 | 0.5–1d |
|
||||
| T2 | 引擎删状态 + `dispatch` 增 ctx + `bucket_of` | 0.5–1d |
|
||||
| T3 | 施加点改写(action_economy / damage_controller / orchestrator) | 0.5–1d |
|
||||
| T4 | 三处清零点就位(轮始 / 进关 / 战斗 new) | 0.25–0.5d |
|
||||
| T5 | 前缀校验进 `validate_trigger_row` | 0.25d |
|
||||
| T6 | 数据行与树合成行 counter_id 调整 | 0.25d |
|
||||
| T7 | **机制正向验收断言新增** + 旧测试迁移 + 变异验证 + 同种子复放对拍(详见 §4.1) | 1–1.5d |
|
||||
| T8 | 文档回写(architecture / 06 / README / entities.yaml) | 0.25d |
|
||||
| T1 | `EffectContext` 新建 + 四宿主接线(编排器两桶成员/ctx 挂载、LevelRunner 桶成员+enc 下传、set_fx 三处扩参) | 0.5–1d |
|
||||
| T2 | 引擎删状态 + `dispatch`/`counter` 增 ctx + 写口改静态纯函数 + `bucket_of` | 0.5–1d |
|
||||
| T3 | 7 个施加点改写(action_economy ×3 / damage_controller ×2 / grant_segment ×1 / orchestrator spend ×1)+dig_combo 死路删除 | 0.5–1d |
|
||||
| T4 | 轮始清零泛化(引擎 round_start 特判整桶清空;进关/战斗 new 两处免费——§7.10.1) | 0.25d |
|
||||
| T5 | 前缀校验双落点(`validate_trigger_row` + `_push_trigger`) | 0.25d |
|
||||
| T6 | 数据行与树合成行 counter_id 调整(改名 4 处+`domain`/`reset_at` 键删除) | 0.25d |
|
||||
| T7 | **机制正向验收断言新增** + 旧测试迁移 + 变异验证 + 同种子复放比对(详见 §4.1/§7.10.12) | 1–1.5d |
|
||||
| T8 | 文档回写(architecture 反哺点归 06 条目 / 06 状态推进 / README / 卡片关联同步面) | 0.25d |
|
||||
|
||||
### 4.1 验收测试清单(机制正向验收 —— 主力)
|
||||
|
||||
@@ -139,91 +142,96 @@
|
||||
>
|
||||
> 对拍只能证「除已知差异外没改坏别的」(负向/兜底),**不能证新机制按设计工作**。机制改动的正向验收必须由下列断言承担。依据:`test-conventions.md` §1「存在必须证明可达且正确」/§3「缺陷修复必须沉淀回归断言」/§5 落码规范/§6 变异验证。
|
||||
|
||||
> 🔴 **编号辨析**:本表验收点编号(A1–H1)与 §4.4 缺口编号(A1–A8)是两套命名空间,互不指涉——本表编号沿用 2026-10-19 首版验收清单,§4.4 编号沿用缺口盘点,改表不改号(卡内引用以「§4.1 V-」前缀消歧,写卡时逐条带编号尾缀)。
|
||||
>
|
||||
> 🔴 **2026-10-20 定稿**:本表按 §7.10 闭合定值改写——A1 注入口径(§7.10.10)、B2 构造路径(§7.10.13)、D1 载体(§7.10.5)、E2 撤销并入 F1(§7.10.3)、G1 分组判读(§7.10.12)、新增 H1 旧档加载(§7.10.15)。
|
||||
|
||||
| # | 验收点 | 执行载体 | 断言词汇 | 怎么测(注入粒度) |
|
||||
|---|---|---|---|---|
|
||||
| **A1** | **adventure 桶跨战持久**(=`run_ledger.gd:42` 缺陷消解的**回归断言**,修前恒 0) | 冒烟 `smoke_all.gd` 冒险段 | 状态/计数器断言 | 新冒险开局 → 第 1 战末置 `adv_twin_digs=1` → 进入第 2 战首读该键 ==1 |
|
||||
| **A2** | adventure 桶新冒险置空 | 冒烟 | 状态/计数器断言 | `start_adventure` 后桶 size==0 |
|
||||
| **A1** | **adventure 桶跨战持久**(=`run_ledger.gd:42` 缺陷消解的**回归断言**,修前恒 0) | 冒烟 `smoke_all.gd` 冒险段 | 状态/计数器断言 | 两编排器共享同一 `RunLedger`(V25 同构):第 1 战 `orch1.action_economy.request_dig(0, false)` 真实挖一次(无返还,`adv_twin_digs`=1)→ 第 2 战(同 ledger 起 orch2)再挖一次(mod_eq 预评估读持久值判「第 2 次」→ 返还成立+桶值 2)——读/写两路径均走真实事件面(§7.10.10) |
|
||||
| **A2** | adventure 桶新冒险置空 | 单测 `feat_vouchers_test.gd`(start_adventure 先例 `:25`) | 状态/计数器断言 | `start_adventure` 后 `run_ledger.adventure_counters` 为空 |
|
||||
| **A3** | adventure 桶随断点往返 | 单测 `feat_vouchers_test.gd`(承 V16,键名随前缀改) | 快照比对 | 写断点 → 回读 → 桶内容逐位相等 |
|
||||
| **B1** | level 桶**同关跨战保持、进关清零** | 冒烟 | 状态/计数器断言 | 同关第 2 战读 `lvl_tree_sense_*` 保持;进下一关后归零 |
|
||||
| **B2** | **tree_sense 每关限 2 次**(削弱行为的正面确认) | 冒烟 | 状态/计数器断言 | 同关第 1 战用满 2 次 → 第 2 战 `lt 2` 不命中 → 无减伤/无返还(修前第 2 战仍有额度) |
|
||||
| **C1** | battle 桶随宿主销毁 | 冒烟 | 状态/计数器断言 | 第 1 战置 `insurance_used=1`;第 2 战读 ==0 |
|
||||
| **B1** | level 桶**同关跨战保持、进关清零** | 冒烟 | 状态/计数器断言 | 两编排器经 enc["level_counters"] 共享同一字典(LevelRunner 真实下传形态):第 2 战读 `lvl_tree_sense_*` 保持;新 LevelRunner(进下一关形态)新桶天然为空(§7.10.11) |
|
||||
| **B2** | **tree_sense 每关限 2 次**(削弱行为的正面确认) | 冒烟 | 状态/计数器断言 | B1 同款共享桶两编排器:第 1 战两次 `_fx.dispatch(ctx, "mine_hit", {})` 命中(damage_mult+counter_add 双行,桶值=2)→ 第 2 战同 dispatch 返回空(lt 2 闸关)且桶值不动——when 只读 counter fact、零 payload 依赖(§7.10.13) |
|
||||
| **C1** | battle 桶随宿主销毁 | 冒烟 | 状态/计数器断言 | 第 1 战置 `insurance_used=1`;第 2 战(新编排器)读 ==0 |
|
||||
| **C2** | 完美保险「每战限次一次」仍成立(卡② V25 迁移保绿) | 单测 `feat_vouchers_test.gd:132+` | 状态/计数器断言 | 沿用 V25 注入口径,改走新桶 |
|
||||
| **D1** | round 桶轮始清空 | 单测/冒烟 | 状态/计数器断言 | 轮始后 `round_*` 全 0 |
|
||||
| **E1** | counter_id 无前缀或带 `meta_` 前缀 → **加载致命** | 单测(新增) | fail-fast | 合成一行喂 `validate_trigger_row` → 命中止/assert |
|
||||
| **E2** | 同 counter_id 跨前缀重复 → 致命 | 单测(新增) | fail-fast | 同上 |
|
||||
| **F1** | 旧件零残留 | 静态扫描面 | 快照比对 | 全仓 `counters_export` / `counters_seed` / `_reset_tree_limited_counters` 零命中 |
|
||||
| **F2** | **旧断言同批摘除**(test-conventions §5.5 换代同生死) | — | — | `effect_words_t1_test.gd:86-94`、`smoke_all.gd:6083-6090`、`:6131-6134` 随旧件同批删除——旧件断言残留=假绿来源 |
|
||||
| **G1** | 同种子复放对拍(**兜底,非主力**) | `adventure_monte_carlo` 同种子重放 | 重放 | 除已记档的已知差异外逐位一致;零局新采集(配套纪律③) |
|
||||
| **D1** | round 桶轮始清空 | 冒烟 | 状态/计数器断言 | round 桶直写非零 → `orch._emit_round_start()`(真实发射函数 `:97`,§7.10.5)→ 全零 |
|
||||
| **E1** | counter_id 带非法前缀(`meta_` 等)→ **加载致命**;树合成行同规则 | 单测(新增负面套件,manifest 登记)+`_push_trigger` assert 面 | fail-fast | 夹具表注入坏前缀行喂 `load_tables` → 拒绝(reward_dangling_treasure_test 同构 expect 1);合成路径坏前缀走 rebuild assert |
|
||||
| **F1** | 旧件零残留(含旧 id 字面) | `static_scan.sh` 新增 S 段 | 快照比对 | 全仓 `counters_export` / `counters_seed` / `counters_merge_export` / `_reset_tree_limited_counters` 零命中 + **带引号旧 id 字面** `"tree_sense_sharp_used"` / `"tree_sense_guard_used"` / `"twin_digs"` / `"instinct_atk"` 零命中(带引号匹配不误伤 `"lvl_tree_sense_sharp_used"` 等新名——E2 撤销后的改名完整性防线,§7.10.3) |
|
||||
| **F2** | **旧断言同批摘除**(test-conventions §5.5 换代同生死) | — | — | `effect_words_t1_test.gd:77-94`(export/seed 往返块)、`:96-124`(reset_at/counter_spend 块,随字段删除重写为桶语义)、`smoke_all.gd:6076-6083`(counter_add+export/seed 链)、`:6124-6128`(直呼 `_reset_tree_limited_counters`)、`:1419-1602` 裸引擎段 17 处 dispatch+8 处 counter_add 直调(改 ctx 形态)——旧件断言残留=假绿来源 |
|
||||
| **G1** | 同种子复放比对(**兜底,非主力**;分组判读制) | `adventure_monte_carlo` 同种子重放 | 重放 | 锚内 normal 档前 20 种子(81000–81019,配套纪律③子集 ≤20):未受影响局全字段逐位一致;受影响局(保守超集=装备集含双管镐/sense 两组且 `mine_hits>0` 或 `seal_miss>0`)输入面字段恒等+`levels_cleared` 分布不得上行(§7.10.12) |
|
||||
| **H1** | 旧档断点加载不崩(B4 补条) | 单测 `feat_vouchers_test.gd`(V16 旧档同构先例 `:124-127`) | 快照比对 | 旧断点 `adventure_counters` 含旧键(`tree_sense_sharp_used`=2 等)→ `resume_adventure` 受理零崩溃,旧键留桶成死键(无迁移——2026-10-18 先例) |
|
||||
|
||||
**变异验证必做项**(test-conventions §6——无变异红记录只写「已实现,拦截能力未证」):
|
||||
|
||||
| 断言 | 变异注入点 | 期望 |
|
||||
|---|---|---|
|
||||
| A1 | 把 adventure 桶写入改回「判空早退」(复刻 `run_ledger.gd:42`) | A1 必须红 |
|
||||
| B2 | 摘掉进关清零点(T4 产物) | B2 必须红 |
|
||||
| A1 | begin_battle 挂载 ctx 时把 adventure 桶引用换成新建空字典(复刻「副本不同步/判空早退」缺陷类——§7.8 守卫原形的同构形态) | A1 必须红 |
|
||||
| B2 | 两编排器改传各自新建 level 字典(复刻 `:840-842` 旧形态「跨关不清理、每战重置」) | B2 必须红 |
|
||||
| E1 | 注释掉 `validate_trigger_row` 内的前缀校验分支 | E1 必须红 |
|
||||
| F1 | 任一源文件临时写回一处带引号旧 id 字面(如 `"counters_export"` 调用形) | F1 扫描必须红 |
|
||||
|
||||
> 三步闭环:注入 → 只跑覆盖该断言的最窄一道闸 → `git checkout -- <文件>` 精确还原并复跑回绿 → 逐条进实现报告变异验证记录表。
|
||||
|
||||
**真人目检登记**:tree_sense 锋/守由「每战限 2 次」变「每关限 2 次」是玩家可感知削弱 ⇒ 按「只登记不阻塞」登记进 `07-体验验证清单`,不构成收口前置。
|
||||
|
||||
### 4.2 载体可达性核验(2026-10-19 实查,逐条对 §4.1)
|
||||
### 4.2 载体可达性核验(2026-10-20 复核轮全绿——逐条对 §4.1)
|
||||
|
||||
> test-conventions §5:「写卡期查不出执行载体的条目=不可测,当场呈报,禁拖到实现期」。下表是逐条实查结果。
|
||||
> test-conventions §5:「写卡期查不出执行载体的条目=不可测,当场呈报,禁拖到实现期」。下表 2026-10-19 首查,2026-10-20 复核轮六项未闭合项全部闭合(定值见 §7.10)。
|
||||
|
||||
| 验收点 | 载体 | 核验结论 | 证据(file:line) |
|
||||
|---|---|---|---|
|
||||
| **C2** 保险限次一次 | 单测 `feat_vouchers_test.gd` | ✅ **已闭合**——V25 现成「第 1 战用满 → 第 2 战重新豁免」双编排器骨架;`orch2.begin_battle({"battle_index":2,…})` + `orch2._grant_seg._judge_seal(...)` 即真实 `seal_missed` | `:143-186` |
|
||||
| **E1/E2** 前缀致命 | 新增负面套件 | ✅ **已闭合**——现役 fail-fast 测试模式:`config_db_fatal_oob_test.gd`(独立进程,`expect:1` + `negative:true`),manifest 协议在役 | `tests/run_all.gd:37`/`:48` |
|
||||
| **F1** 旧件零残留 | `static_scan.sh` 新增 S 段 | 🟡 **半闭合**——S1–S10 分检制在位;本批新增 S 段承载,按纪律「闸件增减只改该脚本+`run_all_gates.sh` 登记」,卡里须写明改哪两个文件 | `static_scan.sh:38-82` |
|
||||
| **A1/A2/A3** adventure 桶 | 冒烟/单测新段 | 🟡 **半闭合**——RunLedger 经 `enc["ledger"]` 下传(`battle_orchestrator.gd:375`),冒烟可自建共享计数器容器喂两个 orch;`AdventureRunner.run_ledger` 公开字段可达(`smoke_all.gd:6131` 先例)。**卡在 U2** | `battle_orchestrator.gd:375` |
|
||||
| **C1** battle 桶 | 单测/冒烟 | 🟡 **半闭合**——编排器可 new + begin_battle(`smoke_all.gd:2680-2687` 先例);桶须为公开字段或 API,测试才读写得了 | `smoke_all.gd:2680-2687` |
|
||||
| **B1/B2** level 桶 | 冒烟新段或探针 | 🟡 **半闭合**——LevelRunner 真实驱动有先例(`board_shot_probe.gd:5`);但现役 level 桶**寄居在 `run_ledger.adventure_counters` 字段内**(`adventure_runner.gd:840-842` erase 的就是该字段),搬去 LevelRunner 后须新定公开接口。**卡在 U3** | `adventure_runner.gd:840-842` |
|
||||
| **D1** round 桶 | 冒烟 | 🔴 **未闭合**——`round_start` 的真实发射入口未定位 | — |
|
||||
| **G1** 对拍已知差异 | `analyze.py` 字段 | 🔴 **未闭合**——RUNSUM 哪些字段会变未列 | — |
|
||||
| **E1** 前缀致命 | 新增负面套件 | ✅ **已闭合**——现役 fail-fast 测试模式:`config_db_fatal_oob_test.gd`(独立进程,`expect:1` + `negative:true`)、`reward_dangling_treasure_test.gd`(夹具表注入→load_tables 拒绝)双先例,manifest 协议在役 | `tests/run_all.gd:37`/`:48` |
|
||||
| **F1** 旧件零残留 | `static_scan.sh` 新增 S 段 | ✅ **已闭合**——S1–S10 分检制在位;本批新增 S 段承载(旧件符号+带引号旧 id 字面两组规则),改面锁定两文件=`static_scan.sh`+`run_all_gates.sh` 登记(域划分唯一出处) | `static_scan.sh:38-82` |
|
||||
| **A1/A2/A3** adventure 桶 | 冒烟/单测新段 | ✅ **已闭合**——两编排器共享同一 `RunLedger`(V25 骨架)+写入走真实 `request_dig`(U2 随战末导出消失而解除,§7.10.4);`RunLedger` 公开字段直读直写先例在役 | `feat_vouchers_test.gd:175-187`、`smoke_all.gd:6124` |
|
||||
| **C1** battle 桶 | 单测/冒烟 | ✅ **已闭合**——编排器可 new + begin_battle(`smoke_all.gd:2680-2687` 先例);桶=编排器公开成员字段,测试直读(§7.10.2) | `smoke_all.gd:2680-2687` |
|
||||
| **B1/B2** level 桶 | 冒烟新段 | ✅ **已闭合**——LevelRunner 直构在役(`smoke_all.gd:6130` `level_runner.gd.new(_db, rng)`);桶=`LevelRunner.level_counters` 公开成员(U3 解,§7.10.11),enc["level_counters"] 下传接线照抄 `"ledger"` 行 | `smoke_all.gd:6130`、`level_runner.gd:466-477` |
|
||||
| **D1** round 桶 | 冒烟 | ✅ **已闭合**——`round_start` 真实发射点定位=`_emit_round_start()`(U4 解,§7.10.5);直呼该函数即真实路径 | `battle_orchestrator.gd:97`(调用点 `:425`/`:1077`) |
|
||||
| **G1** 同种子复放比对 | `adventure_monte_carlo` 重放 | ✅ **已闭合**——RUNSUM 27 字段清点完毕(`adventure_monte_carlo.gd:114-149`),分组判读制与允许差异面定值(U5 解,§7.10.12) | `adventure_monte_carlo.gd:114-149` |
|
||||
|
||||
### 4.3 写卡前未闭合项(禁拖到实现期)
|
||||
### 4.3 写卡前未闭合项(2026-10-20 全部闭合——定值与推导见 §7.10)
|
||||
|
||||
| # | 未闭合项 | 具体卡点 |
|
||||
| # | 原卡点 | 闭合定值(详据→§7.10) |
|
||||
|---|---|---|
|
||||
| **U1** | **A1 注入口径写错了** | 原稿写「置 `adv_twin_digs=1`」**不可行**——新形态引擎无 `counter_add` 公开写口(T2 删除),置数必须走**真实事件**(如触发一次 `dig_paid`)。这条必须重写 |
|
||||
| **U2** | `_battle_over` 能否孤立调用 | 战末入口在 `battle_orchestrator.gd:1111`(私有)。直接调它会连带 despawn/映射退役/奖励结算。要确认能否被测试单独调用,或另找更窄的战末钩子 |
|
||||
| **U3** | level 桶公开接口 | LevelRunner 持桶后,测试读写走什么(公开字段/getter/方法)——不定这条,B1/B2 就不可测 |
|
||||
| **U4** | `round_start` 真实发射入口 | 未定位;定位不到,D1 只能走裸 `dispatch`(弱于真实路径) |
|
||||
| **U5** | G1 已知差异字段清单 | 需列出 RUNSUM 中预期变化的具体字段,否则对拍红了无法区分「预期」与「意外」 |
|
||||
| **U6** | B2 的 `mine_hit` 构造路径 | `seal_missed` 有 V25 的 `_judge_seal` 先例;`mine_hit` 的等价构造路径未定位 |
|
||||
| **U1** | A1 注入口径写错(引擎无 counter_add 公开写口) | 改走真实 `dig_paid` 路径:`orch.action_economy.request_dig(0, false)` 一次即 +1(双管镐 counter_add 行无 when,`feats.tres:26-32` 实证);断言链含第 2 战返还判定,证明读路径同样走真实事件面(§7.10.10) |
|
||||
| **U2** | `_battle_over` 能否孤立调用 | **不需要**——新形态下战末导出动作消失,A1 断言改走共享桶+两编排器(V25 同构),无验收点依赖该私有入口(§7.10.4) |
|
||||
| **U3** | level 桶公开接口 | `LevelRunner.level_counters` 公开类型化成员,enc["level_counters"] 下传,测试直读直写(§7.10.11) |
|
||||
| **U4** | `round_start` 真实发射入口 | 已定位=`battle_orchestrator.gd:97 _emit_round_start()`(调用点 `:425`/`:1077`);D1 直呼该函数即真实路径(§7.10.5) |
|
||||
| **U5** | G1 已知差异字段清单 | RUNSUM 27 字段清点完毕,分组判读制:未受影响局逐位一致/受影响局输入面恒等+`levels_cleared` 不得上行(§7.10.12) |
|
||||
| **U6** | B2 的 `mine_hit` 构造路径 | 两编排器共享 level 桶+各自 `_fx.dispatch(ctx, "mine_hit", {})` 直驱(sense 行 when 只读 counter fact,零 payload 依赖);`_facts_reveals` 捏造路径不取(§7.10.13) |
|
||||
|
||||
**结论**:13 条验收点中 **2 条已闭合**(C2/E1E2)、**4 条半闭合**(A1A2A3/C1/B1B2/F1)、**2 条未闭合**(D1/G1),另有 6 项卡点(U1–U6)必须先解。**§4.1 现阶段不可直接落码成卡的验收清单。**
|
||||
**结论**:13 条验收点保持 13 行(E2 撤销并入 F1、新增 H1 补位),**全部载体闭合**,六项卡点全部定值。**§4.1 现在可整表落卡。**
|
||||
|
||||
### 4.4 实现设计缺口(2026-10-19 第三轮盘点——写卡前必须补)
|
||||
### 4.4 实现设计缺口(2026-10-20 全部闭合——定值与推导见 §7.10)
|
||||
|
||||
| # | 缺口 | 具体未定 | 阻塞等级 |
|
||||
|---|---|---|---|
|
||||
| **A1** | **`ctx` 结构未定义**(最大缺口) | `dispatch` 增的 ctx 里装什么?四桶引用怎么命名/定型?**谁构造 ctx、多久构造一次**?——若 ctx 每轮重建,round 桶天然是新的 `{}`(清零点免费),但 level/adventure 引用要每轮重挂;若 ctx 每战构造一次,round 桶的清零点要显式做。两种走法决定了 T4「三处清零点」到底是三处还是两处 | 🔴 阻塞 |
|
||||
| **A2** | 桶容器类型与测试接口 | 四桶是否统一为 `Dictionary[String, int]`?level/adventure 是宿主字段、ctx 只带引用——那测试凭什么读写(公开字段/getter/方法)未定(同 U3) | 🔴 阻塞 |
|
||||
| **A3** | **树合成行走不到生产侧前缀校验** | `feats.tres`/`treasure_items.tres` 的行经 `validate_trigger_row`(`feat_table.gd:44`/`treasure_item_table.gd:104`);但**树合成行**(`effect_engine.gd:142-158`)走 `_push_trigger`,该函数只 `assert(EVENTS.has(on))` 校事件域,**对 counter_id 前缀零校验**。`tree_rules_v11_test.gd:60` 是测试在代校,不是生产在校 ⇒ 前缀校验必须同时落 `_push_trigger`,否则树规则写错前缀不致命,与「加载期强校验」的完成定义 5 直接冲突 | 🔴 阻塞 |
|
||||
| **A4** | `_battle_over` 孤立调用(同 U2) | 战末入口私有(`orchestrator.gd:1111`),直接调连带 despawn/映射退役/奖励结算 | 🟡 |
|
||||
| **A5** | `round_start` 真实发射入口(同 U4) | 未定位 ⇒ D1 可能只能走裸 `dispatch`,弱于真实路径 | 🟡 |
|
||||
| **A6** | `counter_spend` 施加面漏列 | §3.2 只列了写口 `orchestrator.gd:717`;**漏了读施加面 `action_economy.gd:106`**(按 hits 的 `kind == "counter_spend"` 分支走加收对价)——ctx 改动后这条路径怎么走未写 | 🟡 |
|
||||
| **A7** | counter_id 改名传播面未清点 | 已知:`feats.tres` 数据行、`effect_engine.gd` 树合成行硬编码、`smoke_all.gd` 断言字符串、`tests/` 断言字符串。**未查:呈现层/HUD 是否显示 counter_id**。T6 估时 0.25d 可能不够 | 🟡 |
|
||||
| **A8** | 保险双计数一致性(N6 遗留) | `_perfect_forgiven`(编排器自持计数,N6 定「留编排器」)与 `insurance_used`(battle 桶,`grant_segment.gd:327` 写)是两份。V25 对两者都断言;`when counter lt 1` 读的是桶内那份 ⇒ 需厘清 `_perfect_forgiven` 是镜像还是另有语义,否则同一次标错可能被两处各写一次 | 🟡 |
|
||||
| # | 原缺口 | 闭合定值(详据→§7.10) |
|
||||
|---|---|---|
|
||||
| **A1** | **ctx 结构未定义**(最大缺口) | `EffectContext` 纯引用束类(`game/core/effect_context.gd`,四桶 `Dictionary[Variant, Variant]` 字段、零方法);每战构造一次:编排器 `_init` 建并挂自有两桶、`begin_battle` 挂 adventure/level 两引用;round 桶清零留引擎 dispatch 特判(就地整桶清空,行为逐位等价)——T4 三处清零点=轮始一处+宿主 new 两处免费(§7.10.1) |
|
||||
| **A2** | 桶容器类型与测试接口 | 四桶统一 `Dictionary[Variant, Variant]`(S9 现行口径);测试接口=宿主公开字段直读直写(§7.10.2,同 U3) |
|
||||
| **A3** | **树合成行走不到生产侧前缀校验** | 前缀校验双落点:`validate_trigger_row`(.tres 路径→errs 加载致命)+`_push_trigger`(合成行 assert),共用同一静态判定函数;E2 撤销(无指称),改名完整性防线归 F1 带引号旧 id 字面零命中(§7.10.3) |
|
||||
| **A4** | `_battle_over` 孤立调用(同 U2) | 阻塞消解——无验收点依赖(§7.10.4) |
|
||||
| **A5** | `round_start` 真实发射入口(同 U4) | 已定位 `:97 _emit_round_start()`(§7.10.5) |
|
||||
| **A6** | `counter_spend` 施加面漏列 | `action_economy.gd:103-108`(dig_paid 加收对价+积计数分支)列入迁移面;行号漂移订正(§7.10.6) |
|
||||
| **A7** | counter_id 改名传播面未清点 | 呈现层零引用(全目录检索实证),传播面收窄至数据行/引擎字面/测试断言三面,T6 估时维持(§7.10.7) |
|
||||
| **A8** | 保险双计数一致性 | 厘清=**同点双记各司其职**(`_perfect_forgiven` 供判定段两账相减、`insurance_used` 供 when 限次闸),无重复写风险;本批零改动,卡内立「不改面」条目(§7.10.9) |
|
||||
|
||||
### 4.5 需求澄清未对齐(不影响本批动工,但需登记或后续裁)
|
||||
### 4.5 需求澄清项(2026-10-20 全部落处置——B1/B2 本轮执行,B6 按 §7.10.14 定值)
|
||||
|
||||
| # | 事项 | 现状 | 处置 |
|
||||
|---|---|---|---|
|
||||
| **B1** | **立项依据无文档载体** | 「系统无状态」在 `architecture.md`/`architecture.yaml`/`entities.yaml` **零命中**;本批是代码批、禁写 `design/` ⇒ 卡片的立项依据只能引用「用户 2026-10-19 会话裁定」,不是文档级依据 | 登记 06(来源=会话提及,设计面=文档轨)→ 后续文档批补 architecture.md 条目;本批报告里作反哺点 |
|
||||
| **B2** | **06:627 条目解法方向被推翻** | 「计数器寿命域未统一」条目的解法(寿命域统一口径)已被本提案推翻,但 06 是待办唯一登记处,**条目不改,后人看 06 仍会按旧方向想** | 同上,登记 06 改写/作废;本批只作反哺点 |
|
||||
| **B3** | 天赋树锋/守是否重新定价 | 本批把它从「每战 2 次」修回「每关 2 次」=削弱(§7.7) | 已定不改数值;「是否重新定价」=独立议题,不进本批 |
|
||||
| **B4** | 旧档断点旧键失效 | `tree_sense_*`→`lvl_*` 后旧档内旧键成死键 | 已按 2026-10-18 先例定不迁移;**但卡的验收应加一条「旧档加载不崩」**(现在没有) |
|
||||
| **B5** | `roadmap.md` 当前位 stale | 仍写「迭代 31 待开工」,卡②已合入 | 已呈报,不进本批 |
|
||||
| **B6** | `_fx.counter("dig_combo")` 死读数 | `orchestrator.gd:641`,无任何数据行声明该 id ⇒ combo 加成恒 0 死路 | 已呈报。**建议本批顺手删**(迁到 ctx 后仍恒 0,删它零风险),需你一句确认 |
|
||||
| # | 事项 | 处置(执行状态) |
|
||||
|---|---|---|
|
||||
| **B1** | **立项依据无文档载体** | ✅ **本轮已执行**——新增 06 条目「效果引擎系统无状态原则缺架构文档条目」(文档轨·待文档设计),后续文档批补 architecture.md「架构原则」条目;实现批报告仍作反哺点。§10.3 所引 `design/registry/` 两 yaml 已随 2026-10-19 用户裁注销删除,该节对 yaml 的引用失义、以本条为准;立项判据=§7.9 #1(用户 2026-10-19 ECS 架构裁定) |
|
||||
| **B2** | **06:627 条目解法方向被推翻** | ✅ **本轮已执行**——06「计数器寿命域未统一」条目已改写(现状换代=无状态化重构方向、补「寿命域统一口径」方案作废因由并指向本提案;状态推进「文档设计已闭合+待写卡」)——后人从 06 进入即见新方向 |
|
||||
| **B3** | 天赋树锋/守是否重新定价 | **已闭合**——本批把它从「每战 2 次」修回「每关 2 次」=削弱(§7.7);不改数值,「是否重新定价」=独立议题不进本批(无需登记——现值非缺陷非待办,若用户日后提起再立条目) |
|
||||
| **B4** | 旧档断点旧键失效 | **已闭合**——按 2026-10-18 先例定不迁移;验收已补 **H1 旧档加载不崩**(§4.1、§7.10.15) |
|
||||
| **B5** | `roadmap.md` 当前位 stale | **已呈报在案**,归并行会话,不进本批不动 |
|
||||
| **B6** | `_fx.counter("dig_combo")` 死读数 | **定值=本批删除**(§7.10.14——全仓无声明行实证+位点本批必触+零行为差;原文「需一句确认」按约束推导闭合,可否决) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 影响面
|
||||
|
||||
**代码**:`effect_engine.gd`、`battle_orchestrator.gd`、`action_economy.gd`、`damage_controller.gd`、`reward_economy.gd`(`set_fx` 面)、**`settlement/grant_segment.gd`**(草案初版漏——6 个用点:`:103` 读 / `:205` 读 / `:227` `has_effect_on` / `:311-317` `forgive_seal_miss` 消费 / `:327` 写)、`run_ledger.gd`、`adventure_runner.gd`、`level_runner.gd`、`feats.tres`。
|
||||
**代码**:`effect_engine.gd`、**`effect_context.gd`(新建——ctx 引用束,§7.10.1)**、`battle_orchestrator.gd`(两桶成员+ctx 构造挂载+dig_combo 死路删除)、`action_economy.gd`、`damage_controller.gd`、`reward_economy.gd`(`set_fx` 面扩 ctx 参——三处注入点 `battle_orchestrator.gd:280-282`)、**`settlement/grant_segment.gd`**(草案初版漏——6 个用点:`:103` 读 / `:205` 读 / `:227` `has_effect_on` / `:311-317` `forgive_seal_miss` 消费 / `:327` 写 / `:54/:61/:62` combo_cap 收集随死路删除)、`run_ledger.gd`(`counters_seed_snapshot`/`counters_merge_export` 随搬运链删除)、`adventure_runner.gd`、`level_runner.gd`(`level_counters` 成员+enc 下传)、`feats.tres`、`data/schemas/feat_table.gd`/`treasure_item_table.gd`(校验调用面零改——前缀校验在 `validate_trigger_row` 内部生效)。
|
||||
|
||||
**测试**(草案初版漏项已补):
|
||||
|
||||
@@ -234,8 +242,9 @@
|
||||
| `feat_vouchers_test.gd` | **`:114`/`:119`/`:128`**(V16 断点往返 `pain_layers`/`twin_digs`;初版写 `:92-103` 系错行)+ `:132+` 卡② V25 完美保险验收组 |
|
||||
| `settle_segments_test.gd` | `:32-33` `_apply_counter_hit` + `_fx.counter("dig_combo")` |
|
||||
| `treasure_effects_test.gd` | `:34/:45/:53-56` 裸 `dispatch` 面(无 ctx 参数则签名不兼容) |
|
||||
| `smoke_all.gd` | `:6083/:6085/:6088` `counter_add`+`counters_export`+`counters_seed`/`:6131-6134` 直呼 `_reset_tree_limited_counters`(删函数须同步)/**另 8 处 `fx.counter_add` 直调 `:1427/:1436/:1445/:1466/:1478/:1525/:1566/:1583`(初版全漏)**/`:1410` 断点 `adventure_counters` 键断言/`:1612` `_fx.rebuild`/`:6096/:6124/:6148/:6157` enc 携带 `adventure_counters` 空容器 |
|
||||
| `run_all.gd` | 套件表(增改套件须登记) |
|
||||
| `smoke_all.gd` | `:6076-6083` `counter_add`+`counters_export`+`counters_seed` 链/`:6124-6128` 直呼 `_reset_tree_limited_counters`(删函数须同步)/**`:1419-1602` 裸引擎段 17 处 `dispatch`+8 处 `counter_add` 直调(改 ctx 形态——冒烟 rig 自建 ctx 或用 `orch.counters_ctx`)**/`:1410` 断点 `adventure_counters` 键断言/`:1612` `_fx.rebuild`/`:6086-6156` 各 rig enc 携带 `"adventure_counters"` 死键(迭代 27 收口前旧形态残留——编排器只读 `enc["ledger"]`,`battle_orchestrator.gd:372` 实证;同批顺手清键) |
|
||||
| `run_all.gd` | 套件表(**新增 E1 负面套件登记**) |
|
||||
| `settle_segments_test.gd` | `:32-33` `_apply_counter_hit` 样本 id `dig_combo` 随死路删除换活 id(§7.10.14) |
|
||||
|
||||
**存档/断点**:`adventure` 桶已在断点(写键 `adventure_runner.gd:1035`,续玩回填 `:206-208`);`level` 桶是否进断点见 N3。仅改归属不改 id → 旧档零影响;**若采纳 N2 前缀改名(`tree_sense_*` → `lvl_*`)则旧档断点内旧键失效**(Alpha 阶段可接受,有先例:代码术语刷新批 2026-10-18 用户裁不迁移)。
|
||||
|
||||
@@ -283,6 +292,7 @@
|
||||
**我把它挂上去的真实动机**是「想确认行为变更不是灾难级」。但这个需求有更便宜且纪律明定的手段:**同种子复放对拍**(配套纪律③——对拍断言 = **0 局新采集**,同种子重放已有锚,出口闸复验用锚内子集 ≤20)。对拍的作用是**排除意外**(除已知几处外不得有其他差异),不是量化。
|
||||
|
||||
**定值**:不发起任何档位采集。依据=配套纪律⑦「零数值改动 = 零 MC」+档位表四档触发面均为数值场景。验收=同种子复放对拍,adventure 桶相关差异列为**已知行为差异并记档**,不声称零漂移。§1 完成定义 6 同步改为「除已记档的已知差异外,逐位一致」。
|
||||
> **〔卡片裁决订正 2026-10-21,依 §12 裁①〕** 「零采集」前提失效:现行锚与本批采集面不同构,不可作复放基线。定值改为**批内重采三组各 40 局基线+批后同种子复放 40 局**(二档上限内,`sim-data/` 纪律③⑦开批次例外);其余判读口径见 §7.10.12 订正注。
|
||||
|
||||
🔴 **必须呈报的事实(削弱幅度)**:`tree_sense_sharp_used` / `tree_sense_guard_used` 修后是**显著削弱**,不是变强。
|
||||
|
||||
@@ -427,6 +437,103 @@ func counters_merge_export(exported):
|
||||
|
||||
---
|
||||
|
||||
### 7.10 写卡前缺口闭合定值(2026-10-20 复核轮——由既有裁定与硬纪律推得)
|
||||
|
||||
> 本节把 §4.3 六项卡点、§4.4 八项实现设计缺口、§4.5 六项需求澄清一次闭合。逐项给「定值+推导依据」,全部不占用裁决位——依据三源:用户已裁方向(2026-10-19 ECS 架构裁定、N1–N6 定值)、项目硬纪律(code-conventions/test-conventions/doc-conventions)、本仓代码实证(2026-10-20 逐点实查,file:line 在案)。若与你的判断不符,指出即推翻(同 §7.9 口径)。
|
||||
|
||||
#### 7.10.1 A1——ctx 结构定值
|
||||
|
||||
**定值**:ctx=新建纯引用束类 `EffectContext`(`game/core/effect_context.gd`,RefCounted),四个公开类型化字段 `round_bucket`/`battle_bucket`/`level_bucket`/`adventure_bucket`(各 `Dictionary[Variant, Variant]`,int 值),零方法零行为。**每战构造一次**:编排器 `_init` 建 ctx 并挂入自有两桶引用;`begin_battle` 挂入 adventure(`_ledger.adventure_counters`)与 level(enc 新键 `"level_counters"`,LevelRunner `_start_battle` 下传)两引用;ctx 以编排器公开成员 `counters_ctx` 在役——段类经 `oc` 引用即达;三协同件(action_economy/damage_controller/reward_economy)经现役 `set_fx` 注入面扩一参收同一 ctx 引用(后续挂载是同对象内字段赋值,注入时序无感)。
|
||||
|
||||
**推导依据**:
|
||||
- 「每战一次」唯一:ctx 持桶**引用**且 round 桶清零用**就地清空**,则每轮重建 ctx 毫无收益、徒增重挂面。「每轮重建则 round 桶免费清零」的备选不成立——它把 level/adventure 引用变成每轮重挂的高频面,换来的只是把一处 `.clear()` 挪进构造。
|
||||
- round 桶清零点**留在引擎 `dispatch` 特判内**(`effect_engine.gd:210-212` 现役挂点),由按名单清零泛化为 `ctx.round_bucket.clear()`:前缀分桶后 round 桶内键集≡现役名单集合,行为逐位等价。挂点不挪的依据=现役语义是「重置随事件发生」而非「随发射位点发生」——裸 `dispatch("round_start")` 路径(冒烟 `:6060`/`:1573` 先例)依赖此语义。⇒ **T4「三处清零点」定为:轮始一处(引擎特判泛化)+战斗 new/进关 new 两处免费(宿主 new 天然空桶)**。
|
||||
- 「类」而非裸 Dictionary:RunLedger 先例(审计 P1-4 收口)已把「函数签名上不可见的共享可变引用」换成签名可见的显式类;ctx 跨四类边界传递(编排器→引擎→三协同件),code-conventions §2.2 跨层标识符语义成文 ⇒ 显式类。S9 类型闸下裸 `Dictionary` 参数只有 `Dictionary[Variant, Variant]` 注解可走且键名无校验,类字段即自文档。
|
||||
- 熵增闸不触:`tools/repo_structure_audit.py` 六查只巡顶层目录/根文件/图像白名单/退役路径/design 子目录/art 内容,`game/core/` 内新增 .gd 不触任何一查(§11 N1 甲「①熵增闸要求新类同批修订白名单」的顾虑经实查不成立于 core 内新文件——该顾虑不再构成「不新建类」的理由,但 N1 定值照旧:桶宿主挂编排器、ctx 只是参数面引用束,两者不冲突)。
|
||||
- 字段名取 `round_bucket` 族:`round` 会遮蔽 GDScript 内建函数 `round()`。
|
||||
|
||||
#### 7.10.2 A2——容器类型与测试接口定值
|
||||
|
||||
**定值**:四桶统一 `Dictionary[Variant, Variant]`(int 值;S9 现行口径「账本类字典标注 `Dictionary[Variant, Variant]` 即合格」,元素类型精化不入硬纪律)。测试读写接口=**宿主公开字段直读直写**:round/battle 桶=`BattleOrchestrator` 两个公开成员(`fx_counters_round`/`fx_counters_battle`,ctx 构造时挂引用,与 ctx 内字段同物异名同字典);level 桶=`LevelRunner.level_counters` 公开成员;adventure 桶=`RunLedger.adventure_counters`(现役字段名不动,§7.9 #3)。
|
||||
|
||||
**推导依据**:test-conventions §5.4 调试注入=测试代码直调核心层写口(直改计数器/存档域)——公开字段直写是该条现行承载形态(冒烟 `:6124` `ar.run_ledger.adventure_counters = {...}`、V16 `:114` 同款在役先例);「公开字段 vs getter vs 方法」三候选中后两者无任何约束要求,最小改动取字段。
|
||||
|
||||
#### 7.10.3 A3——前缀校验双落点定值(E2 并入 F1)
|
||||
|
||||
**定值**:前缀合法性校验落两处、共用同一静态判定函数——①`validate_trigger_row`(.tres 行路径):`do` 含 `counter_id` 时校前缀(无前缀=合法 battle;`round_`/`lvl_`/`adv_` 合法;`meta_` 及其余未知前缀→errs 追加违例,加载致命);②`_push_trigger`(树合成行路径):同规则 assert,写错即调试期致命。
|
||||
> **〔卡片裁决订正 2026-10-21,依卡片 §12 裁②〕** 前缀判据定值改松版:只禁 `meta_` 前缀,其余(含未知前缀)一律落 battle 桶即归属——`feats.tres` 现有 14 枚 id 全含下划线、首段除三枚 `round_` 外全是普通词,本节严版误伤全部既有 battle id,且封闭前缀词表=第二事实源(2026-10-19 registry 注销裁定同类)。自造新前缀静默落 battle 的残余风险改由装载期引用闭合校验(写读成对核验,拦「字面改一半」)正面承接,不再单靠人工评审;判定函数=`counter_id_legal`。本节其余双落点结构不变。**E2 撤销**:「同 counter_id 跨前缀重复」在新形态下无指称——前缀即归属,同一字符串必落同一桶,「跨桶同名」不可构造;「同一语义计数器两行写了两套 id」(改名漏改一类)不可机器判定(两串本不同),其防线归 F1:静态扫描 S 段新增**带引号旧 id 字面零命中**规则(`"tree_sense_sharp_used"`/`"tree_sense_guard_used"`/`"twin_digs"`/`"instinct_atk"`——带引号匹配不误伤 `"lvl_tree_sense_sharp_used"`/`"adv_twin_digs"`/`"round_instinct_atk"` 等新名)。
|
||||
|
||||
**推导依据**:完成定义 5「写错即加载致命」对 .tres 行经 errs→load_tables 拒绝通道成立(feat_table.gd:44/treasure_item_table.gd:104 调用面零改、校验在函数内部生效);树合成行不经 .tres 校验、只在 rebuild 期经 `_push_trigger` 生成(`effect_engine.gd:142-158`),漏②=A3 原判「与完成定义 5 直接冲突」成立。负面套件载体=`reward_dangling_treasure_test.gd` 同构(copy_tables 夹具注入坏行→load_tables 拒→expect 1+negative:true,run_all.gd manifest 登记)。
|
||||
|
||||
#### 7.10.4 A4/U2——`_battle_over` 不再需要孤立调用(阻塞消解)
|
||||
|
||||
**定值**:无任何验收点依赖 `_battle_over` 孤立可达。原判的隐含前提是「adventure 跨战持久仍靠战末导出、断言须在战末时点取值」;新形态下导出动作整体消失,写入经 ctx 就地落共享桶,A1 断言改为「第 1战中经真实事件写入共享桶 → 第 2 战 `begin_battle`(同 ledger)经引擎读/返还判定验值」(V25 两编排器同构)——跨战持久由结构保证(同一字典引用),战末时点无待测行为。
|
||||
|
||||
#### 7.10.5 A5/U4——`round_start` 发射入口已定位
|
||||
|
||||
**定值**:真实发射点=`battle_orchestrator.gd:97 _emit_round_start()`(调用点 `:425` 首轮/`:1077` 续轮)。D1 载体=冒烟直呼 `orch._emit_round_start()`——即真实发射函数本身,窄于完整轮切换;跨下划线直呼先例在役(V25 `_judge_seal`、冒烟 `:6125` `_reset_tree_limited_counters`)。
|
||||
|
||||
#### 7.10.6 A6——施加面补列与行号订正
|
||||
|
||||
**定值**:`action_economy.gd:103-108`(dig_paid 的 counter_spend 订阅施加=加收对价+积计数)列入 §3.2 迁移面——该分支的「读-判-写」序列逐字保留,仅写口改静态函数。行号订正(2026-10-20 实查):action_economy 三写口实为 `:108/:111/:169`(草案 :110/:113/:173 系漂移);orchestrator counter_spend 实为 `:714`(草案 :717)。
|
||||
|
||||
#### 7.10.7 A7——改名传播面清点完成
|
||||
|
||||
**定值**:呈现层零引用(`game/presentation/` 全目录检索 counter_id 与 16 枚 id 字面零命中)——呈现面不入改名传播。传播面收窄为三面:`feats.tres` 行(id 改名 4 处+`domain`/`reset_at` 键删除约 12 行)、`effect_engine.gd` 合成行字面与 DO_KEYS 常量、测试断言字符串(§5 表)。T6 估时 0.25d 维持。
|
||||
|
||||
#### 7.10.8 写口形态定值(§3.2 修订——静态纯函数)
|
||||
|
||||
**定值**:`counter_add`/`counter_spend` **实例方法删除**,改**静态纯函数** `static func counter_add(ctx, counter_id, value, set_mode=false)`/`static func counter_spend(ctx, counter_id, value) -> bool`,与 `validate_trigger_row`/`bucket_of` 同类(零实例状态,完成定义 1 不受触)。桶解析单点=`static func bucket_of(ctx, counter_id) -> Dictionary`(按前缀返回目标桶字典,前缀规则唯一出处)。
|
||||
|
||||
**推导依据**:§3.2 原稿「写由发射点落到传入计数器容器」的直写形态作废——7 个施加点各自内联桶解析+算术会把「spend 阈值闸」「set_mode」语义复制七份,同种子复放对语义单点的要求不容许;用户 N2 裁定的红线是「引擎不持有状态」,静态函数无实例态不触红线;调用形与现役一致(`_fx.counter_add(...)` → `EffectEngine.counter_add(ctx, ...)`)使 7 个施加点 diff 收窄为加一参。
|
||||
|
||||
#### 7.10.9 A8——保险双账语义厘清(不改面契约)
|
||||
|
||||
**定值**:`_perfect_forgiven`(编排器自有账)与 `insurance_used`(battle 桶)**非镜像、系同点双记各司其职**:同一标错事件同一分支内各写一次(`grant_segment.gd:314` forgiven 加一/`:315` `_apply_counter_hit` 落桶)——前者供判定段「两账相减」裁定(`perfect_sweep_earned`),后者供引擎 when `lt 1` 限次闸;无重复写风险(闸序:dispatch 仅在 `insurance_used<1` 时返回豁免命中,随后两写同分支各一次)。本批零改动(N6 已定留编排器);卡内立「不改面」条目:两账同点双记关系迁移后必须原样保持(V25 对两者都断言,迁移保绿即证)。
|
||||
|
||||
#### 7.10.10 U1——A1 注入口径重写(真实事件面)
|
||||
|
||||
**定值**:A1 注入走真实 `dig_paid` 路径——`orch.action_economy.request_dig(0, false)`(核心写口直调,test-conventions §5.4 口径)。可行性实证:双管镐 counter_add 行**无 when**(`feats.tres:26-32`),一次 request_dig 即 `adv_twin_digs` 加一。断言链=第 1 战挖一次(无返还——mod_eq 预评估读 0;桶值 1)→ 第 2 战(同 ledger 起 orch2)挖一次(预评估读持久值 1 判「第 2 次」→ 返还成立+桶值 2)——**返还判定证明第 2 战引擎经 ctx 读到了第 1 战的持久值**,读写两路径均走真实事件面。前置核过:首轮 `notify_round_start` 后 Active 相位、基础 AP 覆盖 dig 基础价,两连挖无经济障碍。
|
||||
|
||||
#### 7.10.11 U3——level 桶测试接口
|
||||
|
||||
**定值**:`LevelRunner.level_counters` 公开类型化成员(`Dictionary[Variant, Variant]`),`_start_battle` enc 增 `"level_counters"` 键下传(`level_runner.gd:466-477` 照抄 `"ledger"` 行位置),编排器 begin_battle 挂入 ctx。B1/B2 载体=冒烟 LevelRunner 直构(现役先例 `:6130` `level_runner.gd.new(_db, rng)` 在役)+公开字段直读直写(§7.10.2 同口径)。
|
||||
|
||||
#### 7.10.12 U5——G1 已知差异面(分组判读制)
|
||||
|
||||
**定值**:RUNSUM 字段面清点完毕(`adventure_monte_carlo.gd:114-149` 共 27 字段)。行为变更只触 adventure 桶四枚中的三枚——**pain_layers 迁 battle 对全新开局零位移**(缺陷态下本就不跨战〔§7.8 自锁〕,battle 桶同样不跨战,前后皆无跨战携带);受影响枚=`adv_twin_digs`(返还得真跨战累积)与 `lvl_tree_sense_*`(每战限 2 → 每关限 2,§7.7 削弱定性)。
|
||||
> **〔卡片裁决订正 2026-10-21,依卡片 §12 裁①与本卡〕** 实采字段面为 33 个(本节原记 27 系清点时漏量);分组判读由两组扩为三组——单一「`levels_cleared` 不得上行」判据把增强组(双管镐跨战累积返还得真)与削弱组(sense 每关限 2)混判,作废。现行判读口径=**off 组全字段逐位一致;formed 组与 rules 组输入面字段恒等、其余字段允许 RNG 级联位移、方向只记报告不作判据**。下表按此执行。
|
||||
|
||||
同种子复放比对判读分组(现行口径):
|
||||
|
||||
| 组 | 判定条件(由 RUNSUM 自身字段可判) | 判据 |
|
||||
|---|---|---|
|
||||
| 未受影响局 | feat_mode=off,或装备集不含双管镐与 sense 两组 | **全字段逐位一致**(结构迁移对未触路径必须透明——零漂移回归面) |
|
||||
| 受影响局(formed/rules 两组) | 装备集含受影响件(formed=表驱动装配、rules=树规则注入,采集分组见卡片 T0/T7) | 输入面字段恒等(policy/seed/atk_pct/hp_pct/route/attune/equipped/feat_mode);其余字段允许 RNG 级联位移(轮数一变后续流消费全移);**方向只记报告不作判据**(增强组与削弱组分列读数随实现报告记档) |
|
||||
|
||||
复验规模(本节原案,作废——现行锚 24 字段面与本批采面不同构、且 `sim-data/` 不入 git 致 worktree 内无锚文件可校):~~锚内 normal 档前 20 种子(81000–81019,配套纪律③子集 ≤20)~~。**现行口径〔卡片裁决订正 2026-10-21,依 §12 裁①〕=批内重采三组各 40 局基线+批后同种子复放 40 局**,二档上限内、`sim-data/` 纪律③⑦的批次例外已按其反哺条登记;现行锚与读数文件一律不动。逐位差异明细随实现报告记档。
|
||||
|
||||
#### 7.10.13 U6——`mine_hit` 构造路径
|
||||
|
||||
**定值**:`mine_hit` 无单事件判定函数(发射内联在授予段 run 的 mine 分支 `grant_segment.gd:92`),B2 构造=**两编排器共享 level 桶+引擎 dispatch 直驱**:`enc["level_counters"]` 传同一字典引用(V25 两编排器同构+挂载路径同测),各自 `_fx.dispatch(各自ctx, "mine_hit", {})`——sense 行 when 只读 counter fact、零 payload 依赖,裸 dispatch 即完整真实判定路径。`_facts_reveals` 捏造路径**不取**:mine 分支连带 `_grant_routed` 极性行路由,捏造快照须伪造 cell_type 行集,脆且越过被测面。第 2 战 dispatch 返回空=「无减伤/无返还」断言本体(lt 2 闸关)。
|
||||
|
||||
#### 7.10.14 B6——dig_combo 死读数删除
|
||||
|
||||
**定值**:本批删除,范围=死读数+combo 加成分支+`combo_cap` 参数管线一个整体(`battle_orchestrator.gd:635-647` 读数与分支、grant_segment.gd `:54/:61/:62` 收集与实参、`_feat_yield_adjust` 第五参)。实证:全仓无任何数据行声明 `dig_combo`(feats.tres/treasure_items.tres/树合成行三源零命中);`combo_cap>0` 需 yield_mult 行声明 cap>0——feats.tres 唯一 `"cap"` 行属石肤 ac_bonus(`:576-583`),yield_mult 带 cap 行零个 ⇒ `combo_val` 恒 0 且 `combo_cap` 恒 ≤0,分支恒短路,**删除零行为差**(同种子复放/V25/全部回归零影响)。删除依据:该位点(`_feat_yield_adjust` 读数行)本批必触(counter 签名改 ctx),迁移一个已证死路与重构立意相悖;`settle_segments_test.gd:32-33` 以 dig_combo 为样本 id 同批换活 id。**原文「需你一句确认」按约束推导闭合为定值,可否决——否决的代价=死路代码以新签名迁入新机制。**
|
||||
|
||||
#### 7.10.15 B4——旧档验收补条
|
||||
|
||||
**定值**:§4.1 增 **H1(旧档断点加载不崩)**:旧断点 `adventure_counters` 含旧键(`tree_sense_sharp_used`=2 等)→ `resume_adventure` 受理零崩溃、旧键留桶成死键(无迁移——2026-10-18 代码术语刷新批先例)。载体=`feat_vouchers_test.gd` V16 旧档同构先例(`:124-127` bp_legacy 注入旧键续玩受理)。
|
||||
|
||||
#### 7.10.16 B1/B2/B3/B5——登记与归属
|
||||
|
||||
- **B1**:新增 06 条目「效果引擎系统无状态原则缺架构文档条目」(文档轨·待文档设计)——后续文档批补 architecture.md「架构原则」条目;实现批报告仍作反哺点。§10.3 所引 `design/registry/` 两 yaml 已随 2026-10-19 用户裁注销删除,该节对 yaml 的引用失义、以本条与 §7.9 #1 为准。
|
||||
- **B2**:06「计数器寿命域未统一」条目本轮改写(现状换代=无状态化重构方向、状态推进「文档设计已闭合+待写卡」)——后人从 06 进入即见新方向。
|
||||
- **B3**:天赋树锋/守重新定价=独立议题,不登记不进批(现值非缺陷非待办;若日后提起再立条目)。
|
||||
- **B5**:roadmap 当前位 stale 已呈报在案,归并行会话,不动。
|
||||
|
||||
---
|
||||
|
||||
## 8. 已作废的旧方向(防重走)
|
||||
|
||||
2026-10-19 前半场提出的「**寿命域统一口径**」方案(五档/四档/两档枚举、`domain` 键扩词表、`reset_at` 退役、D1–D9 裁决单)**整体作废**。作废理由:该方案的隐含前提是「引擎持有状态、需要一个字段说明它活多久」;改为分桶后,**寿命由宿主对象天然决定,不需要寿命字段**。
|
||||
@@ -448,6 +555,8 @@ func counters_merge_export(exported):
|
||||
|
||||
### 10.1 行号订正汇总
|
||||
|
||||
> 🔴 **2026-10-20 再漂移注记**:本表「实际」列的行号以提案复核时点为准;其后的 `c5200b6b`(禁写变更记录批·代码注释清偿)已使本文件部分行号再度漂移(如引擎成员块现役 :83-88、`counters_seed` :255、`EffectEngine.new` :271、树合成行 :136-155)。行号时效以开工前代码实查为准(对应需求卡片已随迭代 32 退役,全文见 git 提交 f2bb5871),本节数字仅作沿革参考。
|
||||
|
||||
| 草案初版写法 | 实际 | 说明 |
|
||||
|---|---|---|
|
||||
| `effect_engine.gd` :83/84/85/86/88 | :86/87/88/89/91 | 整体 +3——迭代 31 卡② 在 `:42-44` 插入 `forgive_seal_miss` 三行注释 |
|
||||
|
||||
@@ -41,12 +41,14 @@ var charge_layers: int:
|
||||
get: return _charge_layers
|
||||
# 效果引擎(dig_paid/charge_full 发射面;round_end 转存决策面)
|
||||
var _fx: EffectEngine = null
|
||||
var _ctx: EffectContext = null
|
||||
var _last_stop_ap := 0 # 停手侧剩余 AP 记账(转存决策在 round_end 发射点——grant_ap(transfer) 订阅定夺)
|
||||
var _ap_exhausted_last := false # 上轮 AP 耗尽封印(一滴不剩 round_start when 面)
|
||||
|
||||
|
||||
func set_fx(fx: EffectEngine) -> void:
|
||||
func set_fx(fx: EffectEngine, ctx: EffectContext) -> void:
|
||||
_fx = fx
|
||||
_ctx = ctx
|
||||
|
||||
|
||||
func _init(db_ref: ConfigDb) -> void:
|
||||
@@ -100,15 +102,15 @@ func request_dig(chain_size: int, clears_map: bool) -> bool:
|
||||
# mod_eq 闸在行 when〕同面施加)
|
||||
var dig_refund := 0
|
||||
if _fx != null:
|
||||
for h in _fx.dispatch("dig_paid", {"ap_cost": cost, "ap_surplus": _ap - cost}):
|
||||
for h in _fx.dispatch(_ctx, "dig_paid", {"ap_cost": cost, "ap_surplus": _ap - cost}):
|
||||
if String(h.get("kind", "")) == "counter_spend":
|
||||
var extra := int(h.get("value", 1))
|
||||
if _ap >= cost + extra:
|
||||
cost += extra
|
||||
_fx.counter_add(String(h.get("counter_id", "")), 1)
|
||||
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), 1)
|
||||
elif String(h.get("kind", "")) == "counter_add":
|
||||
var set_v = h.get("set", null)
|
||||
_fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||||
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:
|
||||
@@ -161,12 +163,12 @@ func request_stop() -> bool:
|
||||
# dig_stop 发射+施加(停手挂点〔弃图兑换〕:剩余 AP 随载荷外发,counter_add
|
||||
# value_event="ap_left" 取值面〔值=停手时刻真实余量〕;发射先于记账清零)
|
||||
if _fx != null:
|
||||
for h in _fx.dispatch("dig_stop", {"ap_left": _ap}):
|
||||
for h in _fx.dispatch(_ctx, "dig_stop", {"ap_left": _ap}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
var val := int(h.get("value", 0))
|
||||
if String(h.get("value_event", "")) == "ap_left":
|
||||
val = _ap
|
||||
_fx.counter_add(String(h.get("counter_id", "")), val, h.has("set"))
|
||||
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), val, h.has("set"))
|
||||
_last_stop_ap = _ap
|
||||
_ap = 0
|
||||
_finish_round("stopped", "same_board")
|
||||
@@ -188,7 +190,7 @@ func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
|
||||
var prev_layers := _charge_layers
|
||||
_charge_layers = mini(3, _charge_layers + _last_stop_ap)
|
||||
if _fx != null and _charge_layers >= 3 and prev_layers < 3:
|
||||
_fx.dispatch("charge_full", {"layers": _charge_layers})
|
||||
_fx.dispatch(_ctx, "charge_full", {"layers": _charge_layers})
|
||||
else:
|
||||
_rap += _last_stop_ap * maxi(1, transfer_pct) / 100
|
||||
_last_stop_ap = 0
|
||||
|
||||
@@ -56,7 +56,7 @@ var _time_sand_queue := 0 # 时沙待注入(回合级——下
|
||||
var _save_manager = null # 断点承载注入(null = 不写断点;模拟/冒烟缺省)
|
||||
var _resume_bp := {} # 续玩快照暂存(resume_adventure → _start_current_level 单跳传递)
|
||||
# ---- 三选一停点态 ----
|
||||
var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存正主=_curio_deck)
|
||||
var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存现行事实源=_curio_deck)
|
||||
var _curio_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;规则改写型特性带=relic 令牌随表追加)
|
||||
var _feats: Array[String] = [] # 特性持有集(局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树)
|
||||
var _tree_rules: Array[Variant] = [] # 树 rule 键集(Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
|
||||
@@ -64,9 +64,9 @@ var _curio_pending := {} # 停点态:{"candidates": Array, "p
|
||||
var _curio_result := {} # 已兑付待推进态(curio_resolved 载荷副本——中断续玩不重兑付)
|
||||
var _keystone_drops: Array[Variant] = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
|
||||
var _node_curio_pending := {} # 奖励节点三选一停点态(共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
|
||||
var _feat_ticket_pity := 0 # 门票保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
|
||||
var _feat_ticket_sold := 0 # 门票限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
|
||||
var _feat_ticket_appeared := 0 # 门票出现计数(T5——quota 上限面)
|
||||
var _feat_ticket_pity := 0 # 专长凭证保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
|
||||
var _feat_ticket_sold := 0 # 专长凭证限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
|
||||
var _feat_ticket_appeared := 0 # 专长凭证出现计数(T5——quota 上限面)
|
||||
var _shop_mode := "legacy" # 商店模式("legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool)
|
||||
|
||||
|
||||
@@ -472,7 +472,7 @@ func pool_stock(kind: String) -> int:
|
||||
# 未知 kind 拒绝(货架词表封闭的前置防御)。
|
||||
func _consume_pool_purchase(kind: String, tier: String) -> bool:
|
||||
if kind == "feat_ticket":
|
||||
return _redeem_feat_ticket() # T5——门票买下即开(零摘牌不进持有集;临时停点开专长三选一面)
|
||||
return _redeem_feat_ticket() # T5——专长凭证买下即开(零摘牌不进持有集;临时停点开专长三选一面)
|
||||
if not db.adventure.CURIO_KINDS.has(kind):
|
||||
push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind)
|
||||
return false
|
||||
@@ -659,7 +659,7 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id:
|
||||
# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典
|
||||
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选。
|
||||
# 渠道白名单过滤(渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌);
|
||||
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以门票形态出现(T5)。
|
||||
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以专长凭证形态出现(T5)。
|
||||
# 过滤式而非物理拆池:deck 物理结构/存档键 bonus_deck 混编余库/消耗语义(选中踢池未选回池)原样,
|
||||
# 白名单在候选视图层生效——配额结构锚 TIER_QUOTAS 70/25/3=98 零改动(专长令牌本系活行追加面,在锚外)。
|
||||
# 曝光槽:非 Boss 渠道三选一 = **2 格全池 + 1 格优秀档以上子池**
|
||||
@@ -746,7 +746,7 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array:
|
||||
sub = [] # 子池枯竭——曝光槽回退全池续取
|
||||
continue
|
||||
break # 全池枯竭——出齐剩余(池空直通同族兜底)
|
||||
# T5 门票注入(非 Boss 渠道唯一专长入口——替换末位候选占一席,候选数不变;判定含 pity 清零记账)
|
||||
# T5 专长凭证注入(非 Boss 渠道唯一专长入口——替换末位候选占一席,候选数不变;判定含 pity 清零记账)
|
||||
if channel == "reward_node" and not out.is_empty() and _feat_ticket_consider(rg):
|
||||
out[out.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"}
|
||||
return out
|
||||
@@ -766,7 +766,7 @@ func _curio_cap_ok(kind: String, feat_id: String = "") -> bool:
|
||||
return int(_curio_taken.get(kind, 0)) < cap
|
||||
|
||||
|
||||
# —— 专长获取门票(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
|
||||
# —— 专长获取专长凭证(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
|
||||
# 罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出第 6 必出——计数=展示次数、刷新
|
||||
# 重展计入、节点与商店共享、触发清零、限购满不再触发。调用即一次展示判定与记账)——
|
||||
func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
|
||||
@@ -783,7 +783,7 @@ func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
|
||||
return offer
|
||||
|
||||
|
||||
# 门票兑付(拿到即开——停点候选替换为专长三选一;不进持有集/零摘牌;限购计数 +1):
|
||||
# 专长凭证兑付(拿到即开——停点候选替换为专长三选一;不进持有集/零摘牌;限购计数 +1):
|
||||
# 节点停点=本地替换(呈现层读在途态重绘);店内无停点=临时停点 + payload.context 标记(呈现层
|
||||
# 重 present 无胜利总结面,确认后只关面回店)。
|
||||
func _redeem_feat_ticket() -> bool:
|
||||
@@ -791,7 +791,7 @@ func _redeem_feat_ticket() -> bool:
|
||||
_feat_ticket_pity = 0
|
||||
var cands: Array[Variant] = _draw_curio_candidates("level_clear") # 专长池(与通关渠道同白名单)
|
||||
if cands.is_empty():
|
||||
push_error("[冒险结构] 门票兑付失败——专长池空集")
|
||||
push_error("[冒险结构] 专长凭证兑付失败——专长池空集")
|
||||
_feat_ticket_sold -= 1
|
||||
return false
|
||||
if not _node_curio_pending.is_empty():
|
||||
@@ -832,17 +832,9 @@ func abandon() -> bool:
|
||||
|
||||
# ---- 关卡推进(私有——序列编排单一出口)----
|
||||
|
||||
# 树限次计数按关重置(危险直觉锋/守两组「每关限次」账本键:跨战持久由 adventure
|
||||
# 域导出/回灌承载,此处随关清零闭合生命周期四态;键集=效果引擎树合成行声明的 counter_id;
|
||||
# 独立成口=V10 断言面直呼)
|
||||
func _reset_tree_limited_counters() -> void:
|
||||
for cid in ["tree_sense_sharp_used", "tree_sense_guard_used"]:
|
||||
run_ledger.adventure_counters.erase(cid)
|
||||
|
||||
|
||||
func _start_current_level() -> bool:
|
||||
_runner = LevelRunner.new(db, rng)
|
||||
_reset_tree_limited_counters() # 树限次计数按关重置(V10 生命周期四态闭合——断言面直呼本口)
|
||||
# 树限次计数按关清零现由 LevelRunner.level_counters 随每关 new 天然承载(旧硬编码清除名单函数已删)
|
||||
# core↔core 接线只许方法 Callable(S6——lambda 捕获 self 会延长 RefCounted 生命周期乃至成环)
|
||||
_runner.node_entered.connect(_forward_node_entered)
|
||||
_runner.choices_presented.connect(_forward_choices_presented)
|
||||
@@ -1028,8 +1020,8 @@ func _snapshot_breakpoint() -> void:
|
||||
"bonus_deck": _curio_deck.duplicate(true), # 令牌池余库(TP0 v2.2——池消耗即上限,续玩余库逐枚还原)
|
||||
"relics": _feats.duplicate(), # 特性持有集(局内随机渠道获得态续玩防丢失)
|
||||
"bonus_pending": _curio_pending.duplicate(true), # 三选一停点态(空 = 无停点——选择停点可中断契约)
|
||||
"feat_ticket_pity": _feat_ticket_pity, # T5 门票保底计数(扩列向后兼容——旧档缺键缺省 0)
|
||||
"feat_ticket_sold": _feat_ticket_sold, # T5 门票限购计数(同上)
|
||||
"feat_ticket_pity": _feat_ticket_pity, # T5 专长凭证保底计数(扩列向后兼容——旧档缺键缺省 0)
|
||||
"feat_ticket_sold": _feat_ticket_sold, # T5 专长凭证限购计数(同上)
|
||||
"adventure_counters": run_ledger.adventure_counters.duplicate(true), # adventure 域计数器随断点(续玩跨战账不丢)
|
||||
"vision_counter_used": int(run_ledger.vision_counter.get("used", 0)), # 真视使用计数随断点(续玩跨战账不丢)
|
||||
"refresh_stock": _refresh_stock, # 刷新存量账随断点
|
||||
|
||||
@@ -62,7 +62,7 @@ var _verdict_seg = null # VerdictSegment(⑤判定段——返回裁定,
|
||||
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round)
|
||||
var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用)
|
||||
var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励)
|
||||
var _seal_consumed := {} # 兑付资格以危险格实例为键(板级;换图重置)
|
||||
var _seal_consumed := {} # 兑付资格以危险格实例为键(整图;换图重置)
|
||||
# 同调装配运行态(begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动)
|
||||
var attunement: Array[Variant] = [] # 装备中同调件 item_id 集
|
||||
var _ledger: RunLedger = null # 共享四本账(begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面)
|
||||
@@ -82,13 +82,16 @@ var _feat_first_dig_used := false
|
||||
# 效果引擎(发布订阅解释器;begin_battle 从持有集/装备集重建订阅。
|
||||
# 施加面:发射点 dispatch 返回命中,发射点按组合规则施加)
|
||||
var _fx: EffectEngine
|
||||
var fx_counters_round: Dictionary[Variant, Variant] = {} # round 桶(轮始整桶清空;随编排器生灭)
|
||||
var fx_counters_battle: Dictionary[Variant, Variant] = {} # battle 桶(无前缀 id 缺省归此;随编排器每战 new 天然空)
|
||||
var counters_ctx: EffectContext = null # 四桶引用束(_init 建、begin_battle 挂 adventure/level 引用)
|
||||
|
||||
|
||||
# 事件发射薄面(18 发射点统一入口——订阅空=零循环零扰动;确定性同步分发零 RNG)
|
||||
func _fx_emit(event: String, payload: Dictionary) -> Array:
|
||||
if _fx == null:
|
||||
return []
|
||||
return _fx.dispatch(event, payload, _fx_fact)
|
||||
return _fx.dispatch(counters_ctx, event, payload, _fx_fact)
|
||||
|
||||
|
||||
# round_start 发射+施加(内容数据驱动扩展批 T3——grant_ap mode:queue=当轮入队,行动经济轮切换
|
||||
@@ -259,7 +262,7 @@ func attune_convert_state() -> Dictionary:
|
||||
# 流派档位质变=FeatsEngine 纯函数求值
|
||||
# (档位机制本身系引擎基建非内容效果),两运行态注伤害控制器:档一 rider+1 / 档二 rider 可暴击)
|
||||
var _seal_miss_seen := {} # 封印未中行去重(每安全格实例一次)
|
||||
var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置)
|
||||
var _perfect_hit_true := 0 # 整图完美封印计数(install 重置)
|
||||
var _perfect_hit_false := 0
|
||||
var _perfect_forgiven := 0 # 完美保险豁免累计(单向累加自有账;契约 2 禁改写 _perfect_hit_false)
|
||||
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
|
||||
@@ -269,6 +272,9 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
db = db_ref
|
||||
rng = rng_ref
|
||||
_fx = EffectEngine.new(db_ref)
|
||||
counters_ctx = EffectContext.new()
|
||||
counters_ctx.round_bucket = fx_counters_round # 挂自有两桶引用(与编排器成员字段同物异名同字典)
|
||||
counters_ctx.battle_bucket = fx_counters_battle
|
||||
_board_generator = BoardGenerator.new()
|
||||
action_economy = ActionEconomy.new(db)
|
||||
minesweeper = MinesweeperRules.new(db, action_economy)
|
||||
@@ -277,9 +283,9 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
_reward_economy = RewardEconomy.new(db, rng)
|
||||
_damage = DamageController.new(db, rng, _enemy_system, _status_effects)
|
||||
_damage.emit_row_cb = _emit_row # 行协议归属本类(9 类行协议 + 来源断言不外泄)
|
||||
_damage.set_fx(_fx) # 效果引擎注入(attack_commit/clamp_check/armor_roll/kill/damage_taken 发射面)
|
||||
action_economy.set_fx(_fx) # dig_paid/charge_full 发射面
|
||||
_reward_economy.set_fx(_fx) # reward_roll 发射面
|
||||
_damage.set_fx(_fx, counters_ctx) # 效果引擎注入(attack_commit/clamp_check/armor_roll/kill/damage_taken 发射面)
|
||||
action_economy.set_fx(_fx, counters_ctx) # dig_paid/charge_full 发射面
|
||||
_reward_economy.set_fx(_fx, counters_ctx) # reward_roll 发射面
|
||||
minesweeper.cell_revealed.connect(_on_cell_revealed)
|
||||
minesweeper.seal_judged.connect(_on_seal_judged)
|
||||
minesweeper.round_end_board.connect(_on_round_end)
|
||||
@@ -371,6 +377,10 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
_fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0}
|
||||
var ledger_ref = enc.get("ledger", null)
|
||||
_ledger = ledger_ref if ledger_ref is RunLedger else RunLedger.new() # 缺省新账=调试/单场零扰动面
|
||||
counters_ctx.adventure_bucket = _ledger.adventure_counters # 挂冒险桶引用(跨战持久=同一字典引用,非副本搬运)
|
||||
var level_ref: Variant = enc.get("level_counters")
|
||||
if level_ref is Dictionary:
|
||||
counters_ctx.level_bucket = level_ref # 挂关卡桶引用(LevelRunner 每关 new 下传)
|
||||
_damage.fx_reveal_request = _fx_enqueue_reveal
|
||||
_damage.fx_ac_request = _apply_ac_bonus
|
||||
var feat_ids: Array[Variant] = enc.get("feats", []) as Array
|
||||
@@ -385,9 +395,8 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
var tree_rules: Array[Variant] = enc.get("tree_rules", []) as Array
|
||||
# 效果引擎订阅重建(每战从持有集/装备集重建,中途获得下一战生效;
|
||||
# 树规则合成订阅同面装载。装载序=feats 表序→treasure 表序→树规则,写死保种子复放。
|
||||
# adventure 域计数器从共享引用回灌〔跨战持久〕)
|
||||
# 计数器四桶经 counters_ctx 引用宿主容器,无回灌时点(旧战首回灌时点删除))
|
||||
_fx.rebuild(feat_ids, attunement, tree_rules)
|
||||
_fx.counters_seed(_ledger.counters_seed_snapshot())
|
||||
# 秘宝常时参数注入(装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
|
||||
# 缺省 0/false=零装配零扰动。注入在首板 install 之后〔板在早段〕——板生成不读两参数,
|
||||
# 参数只影响挖掘/插棋动作判据,时序安全)
|
||||
@@ -439,7 +448,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
# 全局作用域变异决议(装板序内前置于 roll_board:图级偏斜旋钮须在资源
|
||||
# 摇号前定型;全局门控与板无关〔节点身份+unlock+board_types〕,确定性取用零取号)。
|
||||
# boss/elite 图命中全局型 → 写 board_variants(_place_variants 见非空即跳 region=替换制);
|
||||
# 全局池空 → 板级字段留空(_place_variants 原路走 region=兜底回落合法态)。
|
||||
# 全局池空 → 整图字段留空(_place_variants 原路走 region=兜底回落合法态)。
|
||||
_resolve_global_variant(board)
|
||||
var res: Dictionary[Variant, Variant] = minesweeper.install_board(board, during_round)
|
||||
if not res.accepted:
|
||||
@@ -464,7 +473,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
_place_variants(board)
|
||||
_seal_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8)
|
||||
_seal_miss_seen.clear()
|
||||
_perfect_hit_true = 0 # 板级完美计数随 install 重置
|
||||
_perfect_hit_true = 0 # 整图完美计数随 install 重置
|
||||
_perfect_hit_false = 0
|
||||
_perfect_forgiven = 0 # 豁免累计随 install 重置(domain=battle 每战清零)
|
||||
_board_from_midround_refresh = during_round # 轮内刷新装板 = 刷新图(清图奖励不发);常规换图/开战装板 = false
|
||||
@@ -476,7 +485,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
|
||||
|
||||
# ---- 全局作用域变异 决议(装板序内 roll_board 之前)----
|
||||
# boss/elite 图命中全局型:写板级 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
|
||||
# boss/elite 图命中全局型:写整图 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
|
||||
# RewardEconomy.roll_board 消费);不消费 variant_place 流(全局取用确定性——零取号合法态)。
|
||||
# 全局池空(含 L1 Boss 免疫/unlock 门控未达):字段留空、旋钮归零——_place_variants 原路走
|
||||
# region 布置 = 兜底回落(保守向后兼容,防配置缺行静默裸奔——provisional)。
|
||||
@@ -500,8 +509,8 @@ func _resolve_global_variant(board: BoardData) -> void:
|
||||
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(初始展开
|
||||
# 占全板 56–67%,总格数基数使有效密度膨胀 4–6 倍、L1 小板极端全池覆盖;percent 语义 = 占可挖格比例,
|
||||
# 关卡递增由 per_level_bonus 绝对格数承载);连通片生长(8 邻),单片 ≤ pack_max(2×2 / 1×4 / 1×3+1 等形)。
|
||||
# 替换制(全局作用域变异):板级 board_variants 非空 = 本图已带全局型,跳过 region
|
||||
# 布置(每图一型的板级推广:普通图=region,Boss/精英图=global);空 = 原路径(含 Boss 图全局池空回落)。
|
||||
# 替换制(全局作用域变异):整图 board_variants 非空 = 本图已带全局型,跳过 region
|
||||
# 布置(每图一型的整图推广:普通图=region,Boss/精英图=global);空 = 原路径(含 Boss 图全局池空回落)。
|
||||
func _place_variants(board: BoardData) -> void:
|
||||
if not board.board_variants.is_empty():
|
||||
return
|
||||
@@ -631,20 +640,16 @@ func _settle() -> void:
|
||||
# ---- 授予段 helpers(极性路由)----
|
||||
|
||||
# 开格产出乘区(贪婪矿灯/矿脉直觉/赌徒的血签事件收集器注入):
|
||||
# positive=power ×(1+静态 mult% + 矿脉直觉连击加成 min(cap,combo)×10%);negative=power ×(1+cost%)。
|
||||
# 全零收集器=原快照原样返回(零扰动)。combo 读数=效果引擎 dig_combo 计数器(safe+1/mine 清零,
|
||||
# 跨轮累计)。
|
||||
func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int, combo_cap: int) -> Dictionary:
|
||||
var combo_val := _fx.counter("dig_combo") if _fx != null else 0
|
||||
if positive and pos_m <= 0 and combo_cap <= 0:
|
||||
# positive=power ×(1+静态 mult%);negative=power ×(1+cost%)。
|
||||
# 全零收集器=原快照原样返回(零扰动)。旧 dig_combo 连击加成死读数(全仓无声明行、combo_val 恒 0)已删——零行为差。
|
||||
func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int) -> Dictionary:
|
||||
if positive and pos_m <= 0:
|
||||
return snap
|
||||
if not positive and neg_m <= 0:
|
||||
return snap
|
||||
var out: Dictionary[Variant, Variant] = snap.duplicate()
|
||||
var power := int(out.get("power", int(_reward_economy.polarity_row(String(out.get("cell_type", "")), "positive" if positive else "negative").power)))
|
||||
var pct := pos_m if positive else neg_m
|
||||
if positive and combo_cap > 0:
|
||||
pct += mini(combo_cap, combo_val) * 10
|
||||
out["power"] = maxi(1, int(round(float(power) * (1.0 + float(pct) / 100.0))))
|
||||
return out
|
||||
|
||||
@@ -711,7 +716,7 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary:
|
||||
var tu_hits := _fx_emit("treasure_used", {"item_id": String(v.item_id), "target": target})
|
||||
for h in tu_hits:
|
||||
if String(h.get("kind", "")) == "counter_spend":
|
||||
if not _fx.counter_spend(String(h.get("counter_id", "")), int(h.get("value", 1))):
|
||||
if not EffectEngine.counter_spend(counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 1))):
|
||||
var vrow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.item_id))
|
||||
var cn_info: int = int(vrow.get("charge_n", 0)) # 读受理行自身
|
||||
return {"ok": false, "reason": "%s:安全格积点未达(%d/%d)" % [String(vrow.get("display_name", "")), _attune_safe_seen % cn_info if cn_info > 0 else 0, cn_info]}
|
||||
@@ -905,7 +910,7 @@ func _submit_flat_hero(source_class: String, display: String, power: int, cell:
|
||||
|
||||
# 封印判定批:标中 = 该危险格资源的正向兑付(每雷实例一次;高光 = 强制检查点);
|
||||
# 标空 = "封印未中"归因行 + 该安全格资源的负向兑付(同键去重——每格实例一次)。
|
||||
# 清图批:清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付(grant_clear 本身恰一次)。
|
||||
# 清图批:清图奖励对象恰好发一次;完美封印追加第二份同身份兑付(grant_clear 本身恰一次)。
|
||||
# 高光行 value = 实际有效兑付份数(禁记名义值;首份零兑付 → 降级不发)。
|
||||
func _grant_clear_reward(ri: int) -> void:
|
||||
if _board_from_midround_refresh:
|
||||
@@ -919,10 +924,10 @@ func _grant_clear_reward(ri: int) -> void:
|
||||
if perfect and first_ok:
|
||||
second_ok = _grant_clear_identity(snap, ri, "(完美第二份)")
|
||||
if perfect and first_ok:
|
||||
_emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美排雷!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep")
|
||||
_emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美封印!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep")
|
||||
|
||||
|
||||
# 清图奖励 = 板级独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
|
||||
# 清图奖励 = 整图独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
|
||||
# 行值全部取快照(身份批 roll 值)。返回实际有效兑付。
|
||||
# 同步开单(sync)——完美第二份与高光份数的流程依赖需要同步读数(开单权归流程);
|
||||
# 同步路径内先按序结清段内排队单据(B 模式保序),随后本批无视结账政策当场兑现。
|
||||
@@ -1119,8 +1124,7 @@ func _battle_over(result: String) -> void:
|
||||
for axis in _fx_bonus_delta.keys():
|
||||
_ledger.bonus_add_axis(axis, int(_fx_bonus_delta[axis]))
|
||||
_fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0}
|
||||
if _fx != null:
|
||||
_ledger.counters_merge_export(_fx.counters_export())
|
||||
# 搬运链删除:adventure 桶经 ctx 引用直写 RunLedger,无战末导出时点(旧导出/汇入搬运链删除)
|
||||
# 映射随战斗结束退役(地图战斗资源 义务④——下一场战斗 = 新板新 scope 批,roll_board 相位守卫的前提)
|
||||
var hero_hp_left := int(_enemy_system.hero_unit().get("hp", 0)) # despawn 前捕获(信号载荷供呈现层"HP 带入下一层")
|
||||
var gp_dropped := _roll_gp_drop() # 战斗掉落(escrow 随此值上抛即弃)
|
||||
@@ -1206,7 +1210,7 @@ func presentation_snapshot() -> Dictionary:
|
||||
"hero": hero,
|
||||
"enemies": enemies,
|
||||
"hero_effects": _status_effects.query_active("hero").duplicate(true), # 值拷贝出户(快照消费方直改不渗入状态容器)
|
||||
# 板级变异投影(呈现横幅/常驻角标唯一读取面;
|
||||
# 整图变异投影(呈现横幅/常驻角标唯一读取面;
|
||||
# 空数组 = 无全局型合法态:普通图与全局池空回落图恒空)
|
||||
"board_variants": (minesweeper.current_board().board_variant_ids().duplicate()
|
||||
if minesweeper.current_board() != null else ([] as Array[String])),
|
||||
@@ -1289,7 +1293,7 @@ func _fatal(msg: String) -> void:
|
||||
assert(false, msg)
|
||||
|
||||
|
||||
## 完美排雷谓词(两账相减:有效误标=误标总数−豁免数)
|
||||
## 完美封印谓词(两账相减:有效误标=误标总数−豁免数)
|
||||
## 契约 2:_perfect_hit_false 保持纯净,豁免只累加 _perfect_forgiven,禁回退式改写与拦截式吞事件。
|
||||
func perfect_sweep_earned() -> bool:
|
||||
return _perfect_hit_false - _perfect_forgiven <= 0 and _perfect_hit_true >= 1
|
||||
@@ -14,7 +14,7 @@ var spawn_center := Vector2i.ZERO
|
||||
var mines := {} # Vector2i → true
|
||||
var digits := {} # Vector2i → int(安全格恒有值;数字恒真 = 8 邻雷数)
|
||||
var variants := {} # Vector2i → Array[String](生成期快照,抗表重载)
|
||||
var board_variants: Array[String] = [] # 板级变异 id 集(scope=global 整图作用域——
|
||||
var board_variants: Array[String] = [] # 整图变异 id 集(scope=global 整图作用域——
|
||||
# 战斗编排装板决议写入,随图生灭同 variants;普通图恒空)
|
||||
var board_type := "" # 图类型标签(词表 v1.0:dungeon/underdark;玩家侧显示名「地图类型」)
|
||||
var level_index: int = 1
|
||||
@@ -50,7 +50,7 @@ func variant_ids(c: Vector2i) -> Array:
|
||||
return variants.get(c, [])
|
||||
|
||||
|
||||
# 板级变异只读出口(scope=global 行集——呈现快照/断言消费;空 = 无全局型合法态)
|
||||
# 整图变异只读出口(scope=global 行集——呈现快照/断言消费;空 = 无全局型合法态)
|
||||
func board_variant_ids() -> Array[String]:
|
||||
return board_variants
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ const TABLE_MANIFEST := {
|
||||
"settlement": "settlement.tres",
|
||||
"level": "level.tres",
|
||||
"difficulty": "difficulty.tres",
|
||||
"shop": "shop.tres", # ShopTable(表与 schema 落籍归数据面——本处只接线加载与消费)
|
||||
"meta": "meta.tres", # MetaTable 四行族(schema 与表同源落籍)
|
||||
"shop": "shop.tres", # ShopTable(表与 schema 已入库归数据面——本处只接线加载与消费)
|
||||
"meta": "meta.tres", # MetaTable 四行族(schema 与表同源已入库)
|
||||
"meta_tree": "meta_tree.tres", # MetaTreeTable 12 节点(定义表 Rule 4 豁免——天赋树.md v0.8 结构事实源)
|
||||
"treasure_items": "treasure_items.tres", # TreasureItemTable 六行(定义表 Rule 4 豁免;罗盘 retired/失义件 deferred 标记见 schema 头注)
|
||||
"adventure": "adventure.tres", # AdventureTable(大奖励与钥石行值单点)
|
||||
@@ -39,10 +39,10 @@ var reward: RewardTable
|
||||
var settlement: SettlementTable
|
||||
var level: LevelTable
|
||||
var difficulty: DifficultyTable
|
||||
# ShopTable 类型化持用(schema class_name 已落籍;
|
||||
# ShopTable 类型化持用(schema class_name 已已入库;
|
||||
# 消费面一律走下方 shop_* 访问器,禁直读行集)
|
||||
var shop: ShopTable = null
|
||||
# MetaTable 类型化持用(schema 与表同源落籍)
|
||||
# MetaTable 类型化持用(schema 与表同源已入库)
|
||||
var meta: MetaTable
|
||||
var meta_tree: MetaTreeTable # 类型化持用(消费面走 tree_node 访问器)
|
||||
var treasure_items: TreasureItemTable # 类型化持用(消费面走 treasure_* 访问器)
|
||||
@@ -277,7 +277,7 @@ func treasure_attunement_rows() -> Array:
|
||||
# 行族四轴(数据契约,行集归 shop.tres):gp_drops {tier, mean, roll_min, roll_max} ·
|
||||
# level_gp_bonus {level_index, bonus} · products {product_id, display_name, attr, flat_value,
|
||||
# base_price, price_increment} · price_level_bonus {level_index, bonus}。
|
||||
# 行族经 ShopTable 同名数组属性读取(.get 容缺——表落籍前查询恒空,掉落按 0 兑付的兜底在调用方)。
|
||||
# 行族经 ShopTable 同名数组属性读取(.get 容缺——表已入库前查询恒空,掉落按 0 兑付的兜底在调用方)。
|
||||
|
||||
func shop_gp_drop_row(tier: String) -> Dictionary:
|
||||
for row in _shop_rows("gp_drops"):
|
||||
@@ -334,7 +334,7 @@ func shop_price_b() -> float:
|
||||
return float(shop.price_b) if shop != null else 0.0
|
||||
|
||||
|
||||
# 行族数组读取(ShopTable 属性名 = 行族名;表未落籍/属性缺失 = 空集非错误态)
|
||||
# 行族数组读取(ShopTable 属性名 = 行族名;表未已入库/属性缺失 = 空集非错误态)
|
||||
func _shop_rows(family: String) -> Array:
|
||||
if shop == null:
|
||||
return []
|
||||
@@ -387,7 +387,7 @@ func _meta_rows(family: String) -> Array:
|
||||
# 设计权威 design/gdd/systems/跨局循环/锻造工坊.md v0.6:默认值归本表常驻持有(hero.tres 两轴已删——
|
||||
# 契约 2);路线行族=攻防数值成长唯一数据面(契约 1)。数值全 provisional 归 M4。
|
||||
|
||||
# 两根类型轴默认值(表未落籍=裸体态兜底 normal/light——与 ForgeTable 缺省值同口径)
|
||||
# 两根类型轴默认值(表未已入库=裸体态兜底 normal/light——与 ForgeTable 缺省值同口径)
|
||||
func forge_default_attack_type() -> String:
|
||||
return String(forge.default_attack_type) if forge != null else "normal"
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
for row in shp.pool_shelf:
|
||||
var kind: String = String(row.get("kind_id", ""))
|
||||
if kind == "feat_ticket":
|
||||
continue # 门票豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
|
||||
continue # 专长凭证豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
|
||||
if not adv.CURIO_KINDS.has(kind):
|
||||
push_diag.call("error", "shelf_dangling_kind", "ShopTable.pool_shelf kind %s 不在 AdventureTable.CURIO_KINDS(共享池牌库外种)" % kind)
|
||||
continue
|
||||
@@ -109,6 +109,40 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
|
||||
if quota < 1:
|
||||
push_diag.call("error", "shelf_zero_quota", "ShopTable.pool_shelf kind %s 令牌配额为 0(牌库空种入货架 = 库存无锚)" % kind)
|
||||
|
||||
# ⑦ 计数器引用闭合(卡② V9——读点 id 必命中同名写入行,否则加载致命):
|
||||
# 声明集 = feats + treasure 各行 triggers 内 kind=counter_add 的 do.counter_id;
|
||||
# 读点 = when fact=counter 的 id、do.value_counter、kind=counter_spend 的 counter_id。
|
||||
var feats_tab = loaded.get("feats")
|
||||
var trig_rows: Array[Variant] = []
|
||||
if feats_tab != null:
|
||||
for r in feats_tab.rows:
|
||||
trig_rows.append(r)
|
||||
if treasure_tab != null:
|
||||
for r in treasure_tab.rows:
|
||||
trig_rows.append(r)
|
||||
var counter_decls := {}
|
||||
for row in trig_rows:
|
||||
for tr_v in (row.get("triggers", []) as Array):
|
||||
var dd: Dictionary[Variant, Variant] = (tr_v as Dictionary).get("do", {}) as Dictionary[Variant, Variant]
|
||||
# 声明集=任一 do.counter_id 行(counter_add 直写、forgive_seal_miss 经 _apply_counter_hit 写——皆写入行)
|
||||
if dd.has("counter_id"):
|
||||
counter_decls[String(dd.counter_id)] = true
|
||||
for row in trig_rows:
|
||||
var rid := String(row.get("feat_id", row.get("item_id", "")))
|
||||
for tr_v in (row.get("triggers", []) as Array):
|
||||
var tr: Dictionary[Variant, Variant] = tr_v as Dictionary[Variant, Variant]
|
||||
for c_v in (tr.get("when", []) as Array):
|
||||
var c: Dictionary[Variant, Variant] = c_v as Dictionary[Variant, Variant]
|
||||
if String(c.get("fact", "")) == "counter" and c.has("id"):
|
||||
var rd := String(c.id)
|
||||
if not counter_decls.has(rd):
|
||||
push_diag.call("error", "counter_dangling_ref", "%s when counter 读点无同名写入行: %s(引用闭合)" % [rid, rd])
|
||||
var do2: Dictionary[Variant, Variant] = tr.get("do", {}) as Dictionary[Variant, Variant]
|
||||
if do2.has("value_counter") and not counter_decls.has(String(do2.value_counter)):
|
||||
push_diag.call("error", "counter_dangling_ref", "%s value_counter 读点无同名写入行: %s" % [rid, String(do2.value_counter)])
|
||||
if String(do2.get("kind", "")) == "counter_spend" and do2.has("counter_id") and not counter_decls.has(String(do2.counter_id)):
|
||||
push_diag.call("error", "counter_dangling_ref", "%s counter_spend 无同名写入行: %s" % [rid, String(do2.counter_id)])
|
||||
|
||||
|
||||
# 池模板 × 技能行跨表校验(加载期致命;返回错误列表——ConfigDB 诊断通道与冒烟直验共用):
|
||||
# skill_id 悬空致命 + 池构成纪律(每池恰一行普攻、增益技 ≤1、妨害技 ≤1——proposal §2.1 结构保证 +
|
||||
|
||||
@@ -44,6 +44,7 @@ var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle
|
||||
# 效果引擎(attack_commit/clamp_check/armor_roll/kill/damage_taken
|
||||
# 发射+施加面:dispatch 返回命中,本类按组合规则施加)
|
||||
var _fx: EffectEngine = null
|
||||
var _ctx: EffectContext = null
|
||||
# 战斗段运行态事实(编排器每轮刷新——fact 取数面:attack_commit 载荷 dig_streak /
|
||||
# charge_layers facts〔猎巢人 per/整备 attack_bonus per/厚积薄发 when/铁壁 shield per〕;
|
||||
# 流派档位两运行态〔magic 档一 rider+1 / 档二 rider 可暴击〕)
|
||||
@@ -67,15 +68,16 @@ func set_hero_ac_override(v: int) -> void:
|
||||
|
||||
|
||||
# 效果引擎注入(战斗编排器 _init——事件发射面接线)
|
||||
func set_fx(fx: EffectEngine) -> void:
|
||||
func set_fx(fx: EffectEngine, ctx: EffectContext) -> void:
|
||||
_fx = fx
|
||||
_ctx = ctx
|
||||
|
||||
|
||||
# 事件发射薄面(发射+命中收集统一入口——订阅空=零循环零扰动;命中由各施加面按组合规则施加)
|
||||
func _fx_hits(event: String, payload: Dictionary) -> Array:
|
||||
if _fx == null:
|
||||
return []
|
||||
return _fx.dispatch(event, payload, _fx_fact)
|
||||
return _fx.dispatch(_ctx, event, payload, _fx_fact)
|
||||
|
||||
|
||||
# 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面)
|
||||
@@ -342,13 +344,13 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float:
|
||||
if String(h.get("kind", "")) == "attack_bonus":
|
||||
var contrib := int(h.get("value", 0)) * _per_value(String(h.get("per", "")), ac_payload)
|
||||
if h.has("value_counter") and String(h.get("value_counter", "")) != "":
|
||||
contrib = _fx.counter(String(h.get("value_counter", "")))
|
||||
contrib = EffectEngine.counter(_ctx, String(h.get("value_counter", "")))
|
||||
base_value += float(contrib)
|
||||
# 计数器施加(attack_commit 命中行内 counter_add〔用后清零行〕,命中序=声明序)
|
||||
for h in ac_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
var set_v = h.get("set", null)
|
||||
_fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||||
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)
|
||||
# 任意真族:厚积薄发满层 spike 倍乘(≥3 层——强制必中已在折扣拍承载)
|
||||
if ctx["spike_ready"]:
|
||||
for h in ac_hits:
|
||||
@@ -461,7 +463,7 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
|
||||
fx_ac_request.call(h) # 石肤 AC 累积归编排器单点(set_hero_ac_override 消费面)
|
||||
"counter_add":
|
||||
var set_v = h.get("set", null)
|
||||
_fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||||
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)
|
||||
if shield > 0:
|
||||
total = maxi(int(db.settlement.damage_floor), total - shield)
|
||||
units.apply_damage(hero, total)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
## EffectContext——效果引擎四桶计数器引用束(无状态引擎的外部状态承载)。
|
||||
## 四桶按生命周期分:round(轮始整桶清空)/battle(随编排器生灭)/level(随 LevelRunner 生灭)/
|
||||
## adventure(随 RunLedger 生灭)。本类零方法零行为,只持引用;桶容器由宿主对象 new,本类只挂引用。
|
||||
class_name EffectContext
|
||||
extends RefCounted
|
||||
|
||||
var round_bucket: Dictionary[Variant, Variant] = {}
|
||||
var battle_bucket: Dictionary[Variant, Variant] = {}
|
||||
var level_bucket: Dictionary[Variant, Variant] = {}
|
||||
var adventure_bucket: Dictionary[Variant, Variant] = {}
|
||||
@@ -0,0 +1 @@
|
||||
uid://6n45i76gkrmu
|
||||
+71
-76
@@ -67,24 +67,20 @@ const WHEN_KEYS := ["fact", "op", "value", "id", "ref", "value_from"]
|
||||
## do 效果键白名单(trigger 行 do 字典键——零裸 flat 同族护栏:kind 外键全部参数引用面。
|
||||
## value_event=事件载荷取值键名〔爆破会计——counter_add 值=本轮挖格花费
|
||||
## payload.ap_cost〕;value_counter=计数器取值键〔疼痛记忆/弃图兑换——attack rider 值=账面层值〕;
|
||||
## domain=计数器寿命域〔battle=战斗域重建清零〔缺省〕/adventure=跨战持久随 battle 末导出〕;
|
||||
## 归属由 counter_id 前缀表达(round_/lvl_/adv_/无前缀=battle),旧 domain/reset_at 键删除;
|
||||
## axis=bonus_delta 加算区轴向)
|
||||
const DO_KEYS := ["kind", "value", "value_from", "delta", "delta_from", "mult", "mult_from",
|
||||
"cap", "cap_from", "cost_mult", "per", "divisor", "dtype", "mode", "channel", "counter_id",
|
||||
"reset_at", "set", "status_id", "label", "fallback", "target_kinds", "trade_atk", "trade_hit",
|
||||
"value_event", "value_counter", "domain", "axis"]
|
||||
"set", "status_id", "label", "fallback", "target_kinds", "trade_atk", "trade_hit",
|
||||
"value_event", "value_counter", "axis"]
|
||||
|
||||
## 主动件分发通道(不经事件订阅——刷新/显影/移除是玩家主动动作;use_treasure 按 kind 分发)
|
||||
## safe_dig 安全格直开(天眼——3×3 内安全格按普通挖格规则全额兑付
|
||||
## 含连锁展开与格面资源,危险格仅真视显示不移除——棋盘事实不可变契约;免行动点即效果本身)
|
||||
const ACTIVE_KINDS := ["reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick", "safe_dig"]
|
||||
|
||||
# ---- 订阅集与计数器账本 ----
|
||||
# ---- 订阅集(编译产物=程序,非状态;计数器四桶经 ctx 外置宿主持有)----
|
||||
var _triggers: Array[Variant] = [] # [{source, id, on, when, do}](装载序=声明序,重建时定死)
|
||||
var _counters: Dictionary[Variant, Variant] = {} # counter_id → int(battle 域账本;重建清零;重置点由订阅行声明)
|
||||
var _reset_on_round_start: Array[String] = [] # round_start 重置点计数器名单(装载时收集)
|
||||
var _adventure_counter_ids: Array[String] = [] # domain=adventure 计数器名单(重建时收集、不清零;
|
||||
# battle 末 export → 冒险账本、begin_battle seed 回灌)
|
||||
var db: ConfigDb # 矩阵/表读取(advantageous 算子消费)——可空(纯计数器用法零依赖)
|
||||
|
||||
|
||||
@@ -93,12 +89,11 @@ func _init(db_ref: ConfigDb = null) -> void:
|
||||
|
||||
|
||||
## 订阅装载(每战 begin_battle 调用——重建时机 v1.1):feats 表序→treasure 表序(各按持有/装备
|
||||
## 集过滤)→树规则合成行。同一数据行的多个 trigger 按行内数组序。计数器账本同步清零并按订阅行
|
||||
## 声明收集重置点(round_start)。
|
||||
## 集过滤)→树规则合成行。同一数据行的多个 trigger 按行内数组序。计数器四桶由宿主持有、随宿主生灭,
|
||||
## 本方法只重建订阅集(运行时可变状态=零;battle/level 桶的清零由宿主 new 天然承载,round 桶清零
|
||||
## 在 dispatch 的 round_start 特判内整桶 clear,adventure 桶随 RunLedger 引用跨战持久——无副本搬运)。
|
||||
func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
_triggers.clear()
|
||||
_reset_on_round_start.clear()
|
||||
_adventure_counter_ids.clear()
|
||||
if db != null and db.feats != null:
|
||||
for row in db.feats.rows:
|
||||
var fid := String(row.get("feat_id", ""))
|
||||
@@ -115,11 +110,9 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
_push_trigger("attune", iid, tr, row)
|
||||
# 树规则合成订阅(六键按 v0.8 三组双态):posture 两键=「每关首轮窗」语义
|
||||
# 〔battle_index==1 且 round_index==1;battle_index=关卡域战斗序由 enc 携带〕;
|
||||
# sense 两组=每关限次〔adventure 域计数器 lt 上限闸 + 命中即 counter_add
|
||||
# 记账双行——同事件双行同预评估态见闸同记,序=装载序确定;生命周期四态:跨战持久=domain
|
||||
# adventure 导出/回灌、按关重置=AdventureRunner._start_current_level 清零、新冒险清零=账本
|
||||
# 归零、断点保留=断点快照随键〕;economy 两组零订阅行——消费点=GP 掉落乘算与商品计价折价的
|
||||
# tree_rules 成员判定(编排器/关卡 runner 读 enc 键集),不入事件面。数值全 provisional 归 M4。
|
||||
# sense 两组=每关限次〔level 桶计数器 lt 上限闸 + 命中即 counter_add 记账双行——同事件双行同
|
||||
# 预评估态见闸同记,序=装载序确定;寿命随 LevelRunner.level_counters 每关 new 天然按关清零〕;
|
||||
# economy 两组零订阅行——消费点=GP 掉落乘算与商品计价折价的 tree_rules 成员判定。数值全 provisional 归 M4。
|
||||
for key_v in tree_rules:
|
||||
var rk := String(key_v)
|
||||
match rk:
|
||||
@@ -137,30 +130,31 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
# 锋=踩中危险格伤害减半·每关限次——mine_hit 挂点现成(damage_mult 收集→halve_pct,
|
||||
# 与惊巢反制同路径;伤害在结算战斗段末兑付,探索阶段零改动)
|
||||
_push_trigger("tree", rk, {"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "damage_mult", "value": 50}}, {})
|
||||
_push_trigger("tree", rk, {"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_sharp_used",
|
||||
"value": 1, "domain": "adventure"}}, {})
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_sharp_used",
|
||||
"value": 1}}, {})
|
||||
"sense_guard":
|
||||
# 守=标错返还行动点·每关限次(契约 12 失误补偿——seal_missed 由 _judge_seal 去重闸后
|
||||
# 发射;queue=下一轮开轮兑现,末轮判出无下一轮=合法作废自动成立)
|
||||
_push_trigger("tree", rk, {"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "grant_ap", "mode": "queue", "value": 1}}, {})
|
||||
_push_trigger("tree", rk, {"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_guard_used",
|
||||
"value": 1, "domain": "adventure"}}, {})
|
||||
# 计数器账本重建(battle 域清零;adventure 域由 counters_seed 回灌〔rebuild 后调用〕)
|
||||
_counters = {}
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_guard_used",
|
||||
"value": 1}}, {})
|
||||
|
||||
|
||||
func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary) -> void:
|
||||
var on := String(tr.get("on", ""))
|
||||
assert(EVENTS.has(on), "[效果引擎] 订阅事件出域: %s(%s %s)" % [on, source, id])
|
||||
var do_d: Dictionary[Variant, Variant] = (tr.get("do", {}) as Dictionary).duplicate(true)
|
||||
# 树合成行 counter_id 前缀校验(与 validate_trigger_row 同源判据;写错前缀调试期致命)
|
||||
if do_d.has("counter_id") and not counter_id_legal(String(do_d.counter_id)):
|
||||
assert(false, "[效果引擎] 合成行 counter_id 前缀非法: %s(%s %s)" % [String(do_d.counter_id), source, id])
|
||||
# *_from 解析(rebuild 时把行字段引用定影为值——value_from:"magnitude" → value=行 magnitude)
|
||||
for pair in [["value_from", "value"], ["delta_from", "delta"], ["mult_from", "mult"], ["cap_from", "cap"]]:
|
||||
if do_d.has(pair[0]):
|
||||
@@ -178,16 +172,6 @@ func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary)
|
||||
"when": when_d,
|
||||
"do": do_d}
|
||||
_triggers.append(t)
|
||||
# 计数器重置点收集(counter_add/counter_spend 行声明 reset_at)
|
||||
if do_d.has("counter_id") and String(do_d.get("reset_at", "")) == "round_start":
|
||||
var cid := String(do_d.counter_id)
|
||||
if not _reset_on_round_start.has(cid):
|
||||
_reset_on_round_start.append(cid)
|
||||
# 寿命域收集(domain=adventure 计数器跨战持久:重建不清零、battle 末导出回冒险账本)
|
||||
if do_d.has("counter_id") and String(do_d.get("domain", "")) == "adventure":
|
||||
var aid := String(do_d.counter_id)
|
||||
if not _adventure_counter_ids.has(aid):
|
||||
_adventure_counter_ids.append(aid)
|
||||
|
||||
|
||||
## 订阅查询面(发射点缺省行为仲裁用——如 round_end 无 transfer 订阅时走 RAP 转存)
|
||||
@@ -203,74 +187,80 @@ func has_effect_on(event: String, kind: String, mode: String = "") -> bool:
|
||||
|
||||
|
||||
## 事件分发(发射点唯一入口):返回命中效果列表(装载序=声明序;零命中=空数组零扰动)。
|
||||
## payload = 事件现场(fact 取值第一优先源);facts = 补充事实面(charge_layers/nest_count 等
|
||||
## 运行态读数 Callable——payload 未带时回落)。
|
||||
func dispatch(event: String, payload: Dictionary, facts: Callable = Callable()) -> Array:
|
||||
## ctx = 四桶引用束(引擎不自持状态,计数器读写全经 ctx);
|
||||
## payload = 事件现场(fact 取值第一优先源);facts = 补充事实面(运行态读数 Callable)。
|
||||
func dispatch(ctx: EffectContext, event: String, payload: Dictionary, facts: Callable = Callable()) -> Array:
|
||||
assert(EVENTS.has(event), "[效果引擎] 未知事件: %s(发射点出域)" % event)
|
||||
if event == "round_start":
|
||||
for cid in _reset_on_round_start:
|
||||
_counters[cid] = 0
|
||||
ctx.round_bucket.clear() # 整桶就地清空(禁 ={} 重赋值断引用;round_ 前缀键集≡旧名单,逐位等价)
|
||||
var hits: Array[Variant] = []
|
||||
for tr in _triggers:
|
||||
if String(tr.on) != event:
|
||||
continue
|
||||
if not _when_ok(tr.when, payload, facts):
|
||||
if not _when_ok(ctx, tr.when, payload, facts):
|
||||
continue
|
||||
hits.append(tr.do)
|
||||
return hits
|
||||
|
||||
|
||||
## 计数器读数(fact counter 与发射点配额面共用)
|
||||
func counter(counter_id: String) -> int:
|
||||
return int(_counters.get(counter_id, 0))
|
||||
## 计数器归属桶解析(前缀规则唯一出处——round_/lvl_/adv_ 显式桶,其余落 battle 缺省桶)。
|
||||
static func bucket_of(ctx: EffectContext, counter_id: String) -> Dictionary[Variant, Variant]:
|
||||
if counter_id.begins_with("round_"):
|
||||
return ctx.round_bucket
|
||||
elif counter_id.begins_with("lvl_"):
|
||||
return ctx.level_bucket
|
||||
elif counter_id.begins_with("adv_"):
|
||||
return ctx.adventure_bucket
|
||||
return ctx.battle_bucket
|
||||
|
||||
|
||||
func counter_add(counter_id: String, value: int, set_mode: bool = false) -> void:
|
||||
## counter_id 前缀合法性(加载期校验判定单点):round_/lvl_/adv_ 合法、无前缀落 battle 合法、meta_ 非法
|
||||
## (引擎不接存档级);其余未知前缀合法(落 battle——封闭词表方案误伤全部既有 battle id,见卡② §12 裁点②)。
|
||||
static func counter_id_legal(counter_id: String) -> bool:
|
||||
if counter_id.begins_with("meta_"):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## 计数器读数(fact counter 与发射点配额面共用——纯查询读 ctx,引擎不自持)。
|
||||
static func counter(ctx: EffectContext, counter_id: String) -> int:
|
||||
return int(bucket_of(ctx, counter_id).get(counter_id, 0))
|
||||
|
||||
|
||||
## 计数器累加/覆写(静态纯函数——写口语义单点,7 施加点共用;算术逐字承自旧实例方法)。
|
||||
static func counter_add(ctx: EffectContext, counter_id: String, value: int, set_mode: bool = false) -> void:
|
||||
var bucket := bucket_of(ctx, counter_id)
|
||||
if set_mode:
|
||||
_counters[counter_id] = value
|
||||
bucket[counter_id] = value
|
||||
else:
|
||||
_counters[counter_id] = int(_counters.get(counter_id, 0)) + value
|
||||
bucket[counter_id] = int(bucket.get(counter_id, 0)) + value
|
||||
|
||||
|
||||
## 计数器消耗(counter_spend 组合规则——按订阅序消耗共享资源,耗尽即止):
|
||||
## 返回 true=消耗成功;false=资源不足(调用方按 reason 通道拒绝)。
|
||||
func counter_spend(counter_id: String, value: int) -> bool:
|
||||
var cur := int(_counters.get(counter_id, 0))
|
||||
static func counter_spend(ctx: EffectContext, counter_id: String, value: int) -> bool:
|
||||
var bucket := bucket_of(ctx, counter_id)
|
||||
var cur := int(bucket.get(counter_id, 0))
|
||||
if cur < value:
|
||||
return false
|
||||
_counters[counter_id] = cur - value
|
||||
bucket[counter_id] = cur - value
|
||||
return true
|
||||
|
||||
|
||||
## adventure 域计数器导出(battle 末调用,快照回冒险账本;仅 domain=adventure 名单内键)
|
||||
func counters_export() -> Dictionary:
|
||||
var out := {}
|
||||
for cid in _adventure_counter_ids:
|
||||
out[cid] = int(_counters.get(cid, 0))
|
||||
return out
|
||||
# ---- when 判定(条件谓词——零随机全确定;计数器读经 ctx) ----
|
||||
|
||||
|
||||
## adventure 域计数器回灌(rebuild 后由发射宿主调用:冒险账本值回填名单内键,缺省 0;
|
||||
## 名单=本次 rebuild 装载行声明 domain=adventure 的 counter_id 集)
|
||||
func counters_seed(seed: Dictionary) -> void:
|
||||
for cid in _adventure_counter_ids:
|
||||
_counters[cid] = maxi(0, int(seed.get(cid, 0)))
|
||||
|
||||
|
||||
# ---- when 判定(条件谓词——零随机全确定) ----
|
||||
|
||||
func _when_ok(when: Array, payload: Dictionary, facts: Callable) -> bool:
|
||||
func _when_ok(ctx: EffectContext, when: Array, payload: Dictionary, facts: Callable) -> bool:
|
||||
for c_v in when:
|
||||
if not _cond_ok(c_v as Dictionary, payload, facts):
|
||||
if not _cond_ok(ctx, c_v as Dictionary, payload, facts):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
func _cond_ok(ctx: EffectContext, c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
var fact := String(c.get("fact", ""))
|
||||
var op := String(c.get("op", "eq"))
|
||||
var val = c.get("value", null)
|
||||
var actual = _fact_value(fact, c, payload, facts)
|
||||
var actual = _fact_value(ctx, fact, c, payload, facts)
|
||||
match op:
|
||||
"eq":
|
||||
return str(actual) == str(val)
|
||||
@@ -312,17 +302,17 @@ func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
# 克制矩阵正面判定(matchup = "{attack_type}_vs_{armor}"——矩阵值 > 0 = 克制成立)
|
||||
return _counter_delta_of(String(actual)) > 0
|
||||
"gte_ref":
|
||||
return int(actual) >= counter(String(c.get("ref", "")))
|
||||
return int(actual) >= counter(ctx, String(c.get("ref", "")))
|
||||
"lt_ref":
|
||||
return int(actual) < counter(String(c.get("ref", "")))
|
||||
return int(actual) < counter(ctx, String(c.get("ref", "")))
|
||||
assert(false, "[效果引擎] when 算子出域: %s" % op)
|
||||
return false
|
||||
|
||||
|
||||
func _fact_value(fact: String, c: Dictionary, payload: Dictionary, facts: Callable):
|
||||
func _fact_value(ctx: EffectContext, fact: String, c: Dictionary, payload: Dictionary, facts: Callable):
|
||||
match fact:
|
||||
"counter":
|
||||
return counter(String(c.get("id", "")))
|
||||
return counter(ctx, String(c.get("id", "")))
|
||||
_:
|
||||
# fact 取值序:payload 同名字段优先 → 补充事实面(发射点注入的运行态读数)
|
||||
if payload.has(fact):
|
||||
@@ -377,6 +367,11 @@ static func validate_trigger_row(rid: String, row: Dictionary, triggers: Array,
|
||||
var kind := String(do_d.get("kind", ""))
|
||||
if not EFFECT_KINDS.has(kind):
|
||||
errs.append({"category": "enum", "message": "%s 效果类型出域(解释器闭集): %s" % [rid, kind]})
|
||||
# 计数器前缀归属合法性(.tres 行路径——写错前缀加载致命;判据单点 counter_id_legal,与 _push_trigger 同源)
|
||||
if do_d.has("counter_id"):
|
||||
var cid := String(do_d.counter_id)
|
||||
if not counter_id_legal(cid):
|
||||
errs.append({"category": "enum", "message": "%s counter_id 前缀非法(meta_ 出域/引擎不接存档级): %s" % [rid, cid]})
|
||||
# value_from/delta_from/mult_from/cap_from 引用面:行字段存在性(悬空=加载致命)——
|
||||
# do 与 when 两侧同面(when value_from=dawn_order effective_rounds 先例)
|
||||
for vf_k in ["value_from", "delta_from", "mult_from", "cap_from"]:
|
||||
|
||||
@@ -23,6 +23,7 @@ var node_index := -1
|
||||
# 冒险模式 = AdventureRunner 共享字典(Dictionary 引用共享——hero_hp/gp 属性读写直接落冒险账本);
|
||||
# 单关模式 = 本实例私有账本(start_level 重建,既有单关语义逐位保留)。
|
||||
var _ledger: Dictionary[Variant, Variant] = {"hero_hp": 0, "gp": 0}
|
||||
var level_counters: Dictionary[Variant, Variant] = {} # 关卡桶(lvl_ 前缀计数器;每关随 LevelRunner new 天然空=按关清零,替代旧硬编码清除名单)
|
||||
var _rest_handler: Callable # 治疗封顶回报执行体(默认内置 rest_heal_amount;冒险模式 = AdventureRunner.apply_rest——接口③)
|
||||
var _external := false # 冒险模式标记(钱包收束时机与播种语义分叉点)
|
||||
# hero_hp / gp 属性化(既有读写点零改动——值类型经属性落 _ledger)
|
||||
@@ -48,7 +49,7 @@ var _hero_armor_type := ""
|
||||
var _shop_pool_mode := false # pool 模式封印(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传)
|
||||
var _pool_deck: Array[Variant] = [] # 共享池令牌库共享引用(AdventureRunner._curio_deck——库存=剩余令牌数单点)
|
||||
var _pool_consume := Callable() # 池消费 handler(AdventureRunner._consume_pool_purchase——_apply_curio_pick 同口写加算区+账本+摘牌)
|
||||
var _pool_ticket := Callable() # 门票展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数)
|
||||
var _pool_ticket := Callable() # 专长凭证展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数)
|
||||
var _pool_cap_check := Callable() # cap 检查回调(AdventureRunner._curio_cap_ok;cap 满剔出货架候选视图)
|
||||
var _shop_shelf: Array[Variant] = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序,同 kind 去重每 kind 至多一卡)
|
||||
var _shop_visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零)
|
||||
@@ -469,6 +470,7 @@ func _start_battle(node: Dictionary) -> bool:
|
||||
"attack_type": _hero_attack_type, "armor_type": _hero_armor_type, # 两根类型轴(spawn 英雄快照;空串=锻造配置默认值回退)
|
||||
"attunement": _attunement,
|
||||
"ledger": run_ledger, # 共享四账单类下传(单引用键)
|
||||
"level_counters": level_counters, # 关卡桶下传(同关跨战共享引用、随本类 new 天然空)
|
||||
"feats": _feats,
|
||||
"tree_rules": _tree_rules,
|
||||
"battle_index": _battle_index(),
|
||||
@@ -775,7 +777,7 @@ func player_request_refresh() -> bool:
|
||||
func set_shop_pool(deck_ref: Array, consume_handler: Callable, ticket_consider: Callable = Callable(), cap_check: Callable = Callable()) -> void:
|
||||
_pool_deck = deck_ref
|
||||
_pool_consume = consume_handler
|
||||
_pool_ticket = ticket_consider # T5——门票展示判定(AdventureRunner._feat_ticket_consider;同流取数复用 shop_draw scope)
|
||||
_pool_ticket = ticket_consider # T5——专长凭证展示判定(AdventureRunner._feat_ticket_consider;同流取数复用 shop_draw scope)
|
||||
_pool_cap_check = cap_check
|
||||
_shop_pool_mode = true
|
||||
|
||||
@@ -820,12 +822,12 @@ func _shop_draw_shelf() -> void:
|
||||
_shop_shelf.append(tok)
|
||||
if _shop_shelf.size() >= 3:
|
||||
break
|
||||
# T5 门票注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。
|
||||
# T5 专长凭证注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。
|
||||
# 取数复用本函数 shop_draw scope 的 stream——零新 scope,确定性序列不漂移)
|
||||
if _pool_ticket.is_valid() and not _shop_shelf.is_empty() and bool(_pool_ticket.call(stream)):
|
||||
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 门票占位档名 = excellent
|
||||
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 专长凭证占位档名 = excellent
|
||||
# 商店渠道无专长直接投放(渠道白名单:商店渠道只出属性值提升+资源,专长在非 Boss
|
||||
# 渠道只以门票形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 门票)。
|
||||
# 渠道只以专长凭证形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 专长凭证)。
|
||||
|
||||
|
||||
# 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0)
|
||||
|
||||
@@ -123,7 +123,7 @@ func install_board(b: BoardData, during_round := false) -> Dictionary:
|
||||
_spawn = {}
|
||||
for c in b.spawn_cells():
|
||||
_spawn[c] = true
|
||||
# 初始展开:O₀ = R₀ 恰等(免费/计入清图/无格面资源/永不触雷);板级一次跨轮不重放
|
||||
# 初始展开:O₀ = R₀ 恰等(免费/计入清图/无格面资源/永不触雷);整图一次跨轮不重放
|
||||
for c in _initial_expansion_order(b):
|
||||
_revealed[c] = {"kind": "safe", "digit": b.digit_of(c)}
|
||||
cell_revealed.emit({
|
||||
|
||||
@@ -18,10 +18,12 @@ var _clear_granted := false
|
||||
var _clear_snapshot := {}
|
||||
# 效果引擎(reward_roll 发射面;偏运硬币 bias_rewards 订阅面)
|
||||
var _fx: EffectEngine = null
|
||||
var _ctx: EffectContext = null
|
||||
|
||||
|
||||
func set_fx(fx: EffectEngine) -> void:
|
||||
func set_fx(fx: EffectEngine, ctx: EffectContext) -> void:
|
||||
_fx = fx
|
||||
_ctx = ctx
|
||||
|
||||
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
@@ -62,7 +64,7 @@ func roll_board(eligible: Array) -> bool:
|
||||
# bias_rewards 订阅命中合计=偏运权重加算(行 value_from magnitude 定影);零命中=原行集零扰动)
|
||||
var luck_bias := 0
|
||||
if _fx != null:
|
||||
for h in _fx.dispatch("reward_roll", {"polarity": "positive", "cell_count": list.size()}):
|
||||
for h in _fx.dispatch(_ctx, "reward_roll", {"polarity": "positive", "cell_count": list.size()}):
|
||||
if String(h.get("kind", "")) == "bias_rewards":
|
||||
luck_bias += int(h.get("value", 0))
|
||||
var cell_rows: Array[Variant] = _biased_cell_rows(luck_bias) # cell 池正向行 weight>0(偏运硬币装配时稀有正向 kinds 权重 +N——池结构零触碰)
|
||||
|
||||
+3
-15
@@ -4,12 +4,12 @@ extends RefCounted
|
||||
# 可写」收口 = 单类持四账)。
|
||||
# 所有权:AdventureRunner 持有(开局构造、冒险全程唯一宿主);LevelRunner 经 set_run_ledger
|
||||
# 下传并装配 encounter;战斗侧(BattleOrchestrator)经 encounter["ledger"] 拿本类引用,
|
||||
# 只走下方 read/append 原语——战末 export/开局 seed 时点语义不变(确定性设计原样保留,
|
||||
# 只把「函数签名上不可见的共享可变引用」换成签名可见的显式类)。
|
||||
# 只走下方 read/append 原语;adventure 桶经 EffectContext 引用被效果引擎就地直写(无战末 export/
|
||||
# 开局 seed 搬运时点——跨战持久=同一字典引用,随本类生灭)。
|
||||
var attune_charges: Dictionary[Variant, Variant] = {} # 同调主动件充能账(use_treasure 扣减;断点随局回档)
|
||||
var vision_counter: Dictionary[Variant, Variant] = {} # 真视类使用计数(冒险开始置 {"used":0};
|
||||
# 缺省空 = 调试/单场战斗不计——enc 缺键守卫语义)
|
||||
var adventure_counters: Dictionary[Variant, Variant] = {} # adventure 域效果计数器(rebuild 回灌/战末导出回写目标)
|
||||
var adventure_counters: Dictionary[Variant, Variant] = {} # adventure 桶(adv_ 前缀计数器;经 EffectContext 引用被引擎就地直写、跨战持久随本类生灭)
|
||||
var bonus: Dictionary[Variant, Variant] = {} # 统一冒险加算区(冒险开始置四轴零账——商店购买/三选一属性件/战末 bonus_delta 汇入)
|
||||
|
||||
|
||||
@@ -33,18 +33,6 @@ func vision_count_add(n: int) -> void:
|
||||
vision_counter["used"] = int(vision_counter.get("used", 0)) + n
|
||||
|
||||
|
||||
func counters_seed_snapshot() -> Dictionary:
|
||||
return adventure_counters # 回灌读面(时点 = begin_battle rebuild 后——语义不变)
|
||||
|
||||
|
||||
func counters_merge_export(exported: Dictionary) -> void:
|
||||
# 战末回写(原编排器逐字语义:仅非空账参与、逐键覆写)
|
||||
if adventure_counters.is_empty():
|
||||
return
|
||||
for cid in exported.keys():
|
||||
adventure_counters[cid] = int(exported[cid])
|
||||
|
||||
|
||||
func bonus_add_axis(axis: String, delta: int) -> void:
|
||||
# 原编排器战末汇入逐字语义(has 闸——账外轴丢弃)
|
||||
if bonus.has(axis):
|
||||
|
||||
@@ -51,15 +51,12 @@ func run(ri: int) -> void:
|
||||
"new_resource_type": is_new_type})
|
||||
var pos_m := 0
|
||||
var neg_m := 0
|
||||
var combo_cap := -1
|
||||
for h in dr_hits:
|
||||
if String(h.get("kind", "")) != "yield_mult":
|
||||
continue
|
||||
pos_m += int(h.get("mult", 0))
|
||||
neg_m += int(h.get("cost_mult", 0))
|
||||
if int(h.get("cap", 0)) > 0:
|
||||
combo_cap = int(h.cap) # 矿脉直觉连击上限
|
||||
oc._grant_routed(oc._feat_yield_adjust(snap, true, pos_m, neg_m, combo_cap), "positive", "safe_cell", evt.cell, ri, "")
|
||||
oc._grant_routed(oc._feat_yield_adjust(snap, true, pos_m, neg_m), "positive", "safe_cell", evt.cell, ri, "")
|
||||
# 计数器执行(连击计数——加法族,乘区收集后执行=本格兑付用旧值,与迁移前逐位一致)
|
||||
for h in dr_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
@@ -100,7 +97,7 @@ func run(ri: int) -> void:
|
||||
halve_pct += int(h.get("value", 0))
|
||||
"damage_reduce":
|
||||
if h.has("value_counter") and String(h.get("value_counter", "")) != "":
|
||||
mine_reduce += oc._fx.counter(String(h.get("value_counter", ""))) # 安全帽——层值减免
|
||||
mine_reduce += EffectEngine.counter(oc.counters_ctx, String(h.get("value_counter", ""))) # 安全帽——层值减免
|
||||
else:
|
||||
mine_reduce += int(h.get("value", 0)) # 背水——定值减免
|
||||
"heal":
|
||||
@@ -122,7 +119,7 @@ func run(ri: int) -> void:
|
||||
_apply_counter_hit(h)
|
||||
if not oc._hits_of(mh_hits, "reveal_tiles").is_empty():
|
||||
oc._fx_enqueue_reveal() # 见血开窍——每颗雷一次揭示(授予段末统一兑付)
|
||||
oc._grant_routed(oc._feat_yield_adjust(snap, false, 0, 0, -1), "negative", "mine_hit", evt.cell, ri, "", "", halve_pct)
|
||||
oc._grant_routed(oc._feat_yield_adjust(snap, false, 0, 0), "negative", "mine_hit", evt.cell, ri, "", "", halve_pct)
|
||||
for h in mh_hits:
|
||||
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "reflect":
|
||||
var nct := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_counter", "惊巢反制·反弹", "board",
|
||||
@@ -202,7 +199,7 @@ func run(ri: int) -> void:
|
||||
var re_fx := oc._fx_emit("round_end", {"round": ri})
|
||||
for h in re_fx:
|
||||
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "blast":
|
||||
var mg_layers := oc._fx.counter(String(h.get("per", "")))
|
||||
var mg_layers := EffectEngine.counter(oc.counters_ctx, String(h.get("per", "")))
|
||||
var mg_total := int(h.get("value", 0)) * mg_layers
|
||||
if mg_total > 0:
|
||||
# source_class 用中性名;per-counter 反打通道保留泛用面
|
||||
@@ -296,10 +293,6 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
if oc._seal_miss_seen.has(cell):
|
||||
return # 每格实例一次去重(归因行与负向兑付同键——防逐轮惩罚轰炸)
|
||||
oc._seal_miss_seen[cell] = true
|
||||
var hl := ""
|
||||
if oc._perfect_hit_false == 1:
|
||||
hl = "perfect_broken" # 本板完美谓词已破的归因锚
|
||||
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
|
||||
# 危险直觉·守挂点(seal_missed 发射:契约 12 触发主语=「当回合发生的标错
|
||||
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
|
||||
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
|
||||
@@ -315,6 +308,8 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
_apply_counter_hit(h) # 豁免同时记账(insurance_used battle 域——下次标错 when lt 1 不再命中)
|
||||
oc._emit_row("insurance_used", "seal_miss", cell, ri, "完美保险·标错豁免", 0,
|
||||
{"forgiven": oc._perfect_forgiven}, "")
|
||||
var hl := "perfect_broken" if oc._perfect_hit_false - oc._perfect_forgiven == 1 else ""
|
||||
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
|
||||
var snap = oc._reward_economy.grant(cell)
|
||||
if not (snap is Dictionary):
|
||||
return
|
||||
@@ -324,7 +319,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
# counter_add 三行组单点(原五位分支本体逐字收拢——kind 分派随段归位 P1-1)
|
||||
func _apply_counter_hit(h: Dictionary) -> void:
|
||||
var set_v = h.get("set", null)
|
||||
oc._fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||||
EffectEngine.counter_add(oc.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||||
|
||||
|
||||
# grant_material 兑付块单点(原三位分支本体逐字收拢——source_class 两态 safe_cell/seal_hit 由参承载)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 秘宝关卡域库存+板级叠加层:全程零随机零特判,同操作序列重放库存与叠加层投影逐位相同(垂直切片验证:道具全程确定性)。
|
||||
## 秘宝关卡域库存+整图叠加层:全程零随机零特判,同操作序列重放库存与叠加层投影逐位相同(垂直切片验证:道具全程确定性)。
|
||||
## Used 不可逆——consume 后保留只读,枚举序=受理序;库存关卡域跨战斗延续、关卡末清空,叠加层随图消亡(清空编排归调用方,本类只供 clear/reset/retire_overlays)。
|
||||
## 零特判纪律:本类不写任何其他系统状态,排雷玩法代码零本系统分支;相位检查归战斗编排 use_treasure——validate_use 是纯谓词零消耗。
|
||||
class_name TreasureItems
|
||||
@@ -38,7 +38,7 @@ func add_item(item_id: String, source_class: String, cell: Vector2i, round_index
|
||||
|
||||
# ---- 库存读面(C 区槽位常读)----
|
||||
|
||||
# 枚举序=受理序;Used 保留只读;逐件含 usable 板级零信息预判(调用方喂板面回调)
|
||||
# 枚举序=受理序;Used 保留只读;逐件含 usable 整图零信息预判(调用方喂板面回调)
|
||||
func query_inventory(board_has_preview_info: Callable) -> Array:
|
||||
var out: Array[Variant] = []
|
||||
for rec in _items:
|
||||
@@ -54,13 +54,13 @@ func inventory_count() -> int:
|
||||
|
||||
# 持有数(同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
|
||||
# 同调位守卫谓词(读取源 transitional_sync_slots 过渡键已随表退役;
|
||||
# 容量闸正主 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
|
||||
# 容量闸现行事实源 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
|
||||
# 冒烟「过渡键读取点归零」断言覆盖)
|
||||
|
||||
|
||||
# 持有件记录(奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
|
||||
# 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
|
||||
# 整图零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
|
||||
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
|
||||
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——自动兑付无玩家入口)
|
||||
func _usable_now(item_id: String, board_has_preview_info: Callable) -> bool:
|
||||
|
||||
@@ -89,12 +89,12 @@ const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
|
||||
@export var curio_cap_rap: int = 99 # [1,99]
|
||||
@export var curio_cap_time_sand: int = 99 # [1,99]
|
||||
|
||||
# —— 专长获取门票(非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
|
||||
# —— 专长获取专长凭证(非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
|
||||
# 四口径契约④:拿到即开不可囤积/罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出
|
||||
# 第 6 必出(计数=展示次数、刷新重展计入、节点与商店共享、触发清零、限购满不再触发)。
|
||||
# 四参数全部 provisional 归曲线调参;limit/pity_step 系口径定死值,调参可复议)——
|
||||
@export var feat_ticket_chance: int = 20 # [0,100] knob(provisional)——每次非 Boss 渠道展示门票出现概率 %
|
||||
@export var feat_ticket_quota: int = 2 # [0,9] knob(provisional)——每局门票出现次数上限
|
||||
@export var feat_ticket_chance: int = 20 # [0,100] knob(provisional)——每次非 Boss 渠道展示专长凭证出现概率 %
|
||||
@export var feat_ticket_quota: int = 2 # [0,9] knob(provisional)——每局专长凭证出现次数上限
|
||||
@export var feat_ticket_limit: int = 1 # [1,9] knob(口径定死=1)——一局限购(选中/买下)次数
|
||||
@export var feat_ticket_pity_step: int = 5 # [1,99] knob(口径定死=5)——保底步数(连续未出展示次数)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class_name SettlementTable
|
||||
extends Resource
|
||||
|
||||
@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref
|
||||
@export var p_perfect: int = 200 # {100,200} percent——完美排雷第二份兑付触发(载体域)
|
||||
@export var p_perfect: int = 200 # {100,200} percent——完美封印第二份兑付触发(载体域)
|
||||
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
|
||||
@export var segment_order: PackedStringArray = [] # 结构冻结——段序恒等校验违例致命
|
||||
@export var source_label_map: Dictionary[String, Variant] = {} # 结构冻结——source_class→文案键
|
||||
|
||||
@@ -15,7 +15,7 @@ extends Resource
|
||||
|
||||
const GP_TIERS := ["normal", "elite", "boss"]
|
||||
const PRODUCT_ATTRS := ["atk", "max_hp", "armor_class"]
|
||||
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 门票行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
|
||||
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 专长凭证行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
|
||||
@@ -17,7 +17,7 @@ const SCOPES := ["region", "global"]
|
||||
# resource_face = 资源面偏斜(全局作用域变异——整图修摇号分布:生存族按 effect_value%
|
||||
# 下调、进攻族等比上浮重归一,不动任何格子数据;effect_value 语义随行域切换,不适用 flat_damage [1,9])
|
||||
const EFFECTS := ["none", "flat_damage", "hide_number", "resource_face"] # 效果词表:直伤 / 数字遮蔽 / 资源面偏斜(可扩充)
|
||||
const TRIGGERS := ["on_open", "on_number_shown", "always"] # 触发词表:被打开即触发 / 打开且显示数字时触发 / 板级恒在无触发时机(可扩充)
|
||||
const TRIGGERS := ["on_open", "on_number_shown", "always"] # 触发词表:被打开即触发 / 打开且显示数字时触发 / 整图恒在无触发时机(可扩充)
|
||||
const PLACEMENTS := ["closed_only", "any"] # 布置约束词表:只落布置时未开格 / 任意格(可扩充)
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ func validate() -> Array:
|
||||
if row.get("placement", "") != "any":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ placement 必为 any(整图无格子布置域)" % vid})
|
||||
if row.get("trigger", "") != "always":
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(板级恒在,无触发时机)" % vid})
|
||||
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(整图恒在,无触发时机)" % vid})
|
||||
return errs
|
||||
|
||||
|
||||
|
||||
+13
-22
@@ -26,9 +26,8 @@ rows = [
|
||||
"on": "dig_paid",
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "twin_digs",
|
||||
"value": 1,
|
||||
"domain": "adventure"
|
||||
"counter_id": "adv_twin_digs",
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -36,7 +35,7 @@ rows = [
|
||||
"when": [
|
||||
{
|
||||
"fact": "counter",
|
||||
"id": "twin_digs",
|
||||
"id": "adv_twin_digs",
|
||||
"op": "mod_eq",
|
||||
"value": 2,
|
||||
"ref": 1
|
||||
@@ -208,8 +207,7 @@ rows = [
|
||||
"kind": "counter_add",
|
||||
"counter_id": "round_blast_spent",
|
||||
"value": 1,
|
||||
"value_event": "ap_cost",
|
||||
"reset_at": "round_start"
|
||||
"value_event": "ap_cost"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -373,8 +371,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "pain_layers",
|
||||
"value": 1,
|
||||
"domain": "adventure"
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -489,16 +486,15 @@ rows = [
|
||||
"on": "mine_hit",
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "instinct_atk",
|
||||
"value": 2,
|
||||
"reset_at": "round_start"
|
||||
"counter_id": "round_instinct_atk",
|
||||
"value": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"on": "attack_commit",
|
||||
"do": {
|
||||
"kind": "attack_bonus",
|
||||
"value_counter": "instinct_atk"
|
||||
"value_counter": "round_instinct_atk"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -514,8 +510,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "round_mines",
|
||||
"value": 1,
|
||||
"reset_at": "round_start"
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -531,8 +526,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "pain_layers",
|
||||
"value": 1,
|
||||
"domain": "adventure"
|
||||
"value": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -624,8 +618,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "pain_layers",
|
||||
"value": 1,
|
||||
"domain": "adventure"
|
||||
"value": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -719,8 +712,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "counter_add",
|
||||
"counter_id": "round_digs",
|
||||
"value": 1,
|
||||
"reset_at": "round_start"
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -873,8 +865,7 @@ rows = [
|
||||
"do": {
|
||||
"kind": "forgive_seal_miss",
|
||||
"counter_id": "insurance_used",
|
||||
"value": 1,
|
||||
"domain": "battle"
|
||||
"value": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -101,7 +101,7 @@ price_b = 1.1
|
||||
; (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 渠道专长入口,价与其他商品相当=契约④口径②〕、
|
||||
; 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 行族 + 加法定价
|
||||
|
||||
@@ -86,7 +86,7 @@ func _skip_empty_stop() -> bool:
|
||||
# 增量行在其上),确认后进三选一面(level-clear.md v2);续玩停点恢复同入口(零自动推进契约)
|
||||
func on_presented(payload: Dictionary) -> void:
|
||||
if String(payload.get("context", "")) == "feat_ticket":
|
||||
# T5 店内门票兑付——临时停点开专长三选一面(零胜利总结面;确认后只关面回店)
|
||||
# T5 店内专长凭证兑付——临时停点开专长三选一面(零胜利总结面;确认后只关面回店)
|
||||
_pending_curio = payload.duplicate(true)
|
||||
_curio_context = "feat_ticket"
|
||||
if (_pending_curio.get("candidates", []) as Array).is_empty():
|
||||
@@ -214,7 +214,7 @@ func on_pick_confirmed(item_id: String) -> void:
|
||||
if not ok:
|
||||
push_error("[bonus_interact] 三选一兑付被拒——见输出日志")
|
||||
return
|
||||
# T5——节点渠道门票拿到即开:候选已被替换为专长面,本地读在途态重绘(零 emit——context 保持 reward_node,
|
||||
# T5——节点渠道专长凭证拿到即开:候选已被替换为专长面,本地读在途态重绘(零 emit——context 保持 reward_node,
|
||||
# 后续兑付/确认仍走节点路径回图选路)
|
||||
if _curio_context == "reward_node" and kind == "feat_ticket":
|
||||
var st2: Dictionary[Variant, Variant] = adventure.node_curio_pending_state()
|
||||
@@ -234,7 +234,7 @@ func on_continue() -> void:
|
||||
curio_overlay.close_pick()
|
||||
var adventure = _adventure_get.call()
|
||||
if _curio_context == "feat_ticket":
|
||||
return # T5 店内门票面——专长兑付完成后关面回店(无推进/无回图)
|
||||
return # T5 店内专长凭证面——专长兑付完成后关面回店(无推进/无回图)
|
||||
if _curio_context == "reward_node":
|
||||
var runner = _runner_get.call()
|
||||
if runner != null:
|
||||
|
||||
@@ -37,7 +37,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB
|
||||
_refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db)
|
||||
return
|
||||
var s: Dictionary[Variant, Variant] = settlement.presentation_snapshot()
|
||||
# 地形变异常驻角标投影(板级变异快照 → 文案行;
|
||||
# 地形变异常驻角标投影(整图变异快照 → 文案行;
|
||||
# 空态隐藏=特性段先例;战斗全程常驻防中局遗忘)
|
||||
info.set_board_variants(board_variant_lines(db, s.get("board_variants", [])))
|
||||
var hero: Dictionary[Variant, Variant] = s.hero
|
||||
@@ -102,7 +102,7 @@ static func feat_holdings_lines(db, held: Array) -> Array[String]:
|
||||
return rlines
|
||||
|
||||
|
||||
# 地形变异常驻角标文案(板级变异 id → 文案行;
|
||||
# 地形变异常驻角标文案(整图变异 id → 文案行;
|
||||
# 空集 = 空数组=空态隐藏。三行制:变异名+两行效果说明(C 区 240px 宽防裁切)。
|
||||
# 文案用常规表述,不用俚语
|
||||
static func board_variant_lines(db, ids: Array) -> Array[String]:
|
||||
|
||||
@@ -911,7 +911,7 @@ func _on_round_started(ri: int) -> void:
|
||||
# 巢穴预告呈现(装板报数量读数口消费:≥0=本战装配命中,toast 一次性提示)
|
||||
if run_scope.settlement != null and int(run_scope.settlement.nest_preview_count) >= 0:
|
||||
board_zone.toast.show_toast("巢穴预告——本板危险格共 %d 个" % int(run_scope.settlement.nest_preview_count), "info")
|
||||
# 开战横幅——板级地形变异预告:首轮快照 board_variants
|
||||
# 开战横幅——整图地形变异预告:首轮快照 board_variants
|
||||
# 非空才发射(Boss 图恒带一型=替换制;普通图空快照零发射);轮横幅下方叠行,view 内判减动效。
|
||||
if ri == 1 and run_scope.settlement != null:
|
||||
var bvs: Array[Variant] = run_scope.settlement.presentation_snapshot().get("board_variants", [])
|
||||
|
||||
@@ -89,7 +89,7 @@ func _consume_row(row: Dictionary) -> void:
|
||||
stage.on_step_row_fx(row)
|
||||
_play_cross_zone_fx(row) # 跨区因果演出(结算演出链批):开格之因 → 站桩之果
|
||||
_play_settlement_mark(row) # 三轮 #3——逐条标出:Buff/Debuff 色环脉冲 → 伤害/治疗数字浮现
|
||||
# 高光演出(战斗结算×3①② 占位):完美排雷横幅 / 封印命中加重弹道
|
||||
# 高光演出(战斗结算×3①② 占位):完美封印横幅 / 封印命中加重弹道
|
||||
if String(row.get("type", "")) == "highlight" and String(row.get("highlight", "")) != "skill_unlock":
|
||||
board_zone.fx.play_perfect_sweep()
|
||||
elif String(row.get("highlight", "")) == "seal_hit":
|
||||
|
||||
@@ -98,7 +98,7 @@ func attempt_use(handle: int, cell: Variant = null) -> void:
|
||||
toast.show_toast("已使用 %s" % String(arow.get("display_name", "")), "info")
|
||||
|
||||
|
||||
# 库存投影(C 区四槽取数面——字形=名首字;usable=板级零信息预判回调接排雷玩法只读面)
|
||||
# 库存投影(C 区四槽取数面——字形=名首字;usable=整图零信息预判回调接排雷玩法只读面)
|
||||
func inventory_projection() -> Array:
|
||||
var runner = _runner_get.call()
|
||||
if runner == null or runner.treasure_inventory == null:
|
||||
|
||||
@@ -189,7 +189,12 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
var n := (" ×%d" % agg_count) if agg_count > 1 else ""
|
||||
return prefix + "[s][color=#%s]⚠ %s%s[/color][/s]%s" % [UiKit.HEX_LOG_VOIDED, name_, n, suffix]
|
||||
"seal_miss":
|
||||
var body := "✗ %s%s" % [name_, "(本板完美排雷已破——归因此封印)" if hl == "perfect_broken" else ""]
|
||||
var miss_note := ""
|
||||
if hl == "perfect_broken":
|
||||
miss_note = "(本图完美封印已破——清图奖励不再加倍)"
|
||||
elif hl == "":
|
||||
miss_note = "(首次封印落空已豁免——不计入完美封印判定)"
|
||||
var body := "✗ %s%s" % [name_, miss_note]
|
||||
return prefix + (_gold_frame(body) if hl == "perfect_broken" else "[color=#%s]%s[/color]" % [UiKit.HEX_LOG_ROUND, body])
|
||||
"cast":
|
||||
# 施放行:增益技 0 伤害施放,具名技能
|
||||
|
||||
@@ -94,7 +94,7 @@ var facade_strip := false: # E 格开关(BoardView paint 按「已开
|
||||
|
||||
# 变异角标(程序化,tex_mutation 纹带槽暂留):
|
||||
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
|
||||
# 叠加层标记(秘宝板级叠加层的格面呈现;派生信息层零回写):
|
||||
# 叠加层标记(秘宝整图叠加层的格面呈现;派生信息层零回写):
|
||||
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,封印照常计费);
|
||||
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
|
||||
var treasure_mark := "":
|
||||
@@ -108,8 +108,8 @@ var mutation_mark := "":
|
||||
mutation_mark = v
|
||||
queue_redraw()
|
||||
|
||||
# ── 视野迷雾呈现态(纯呈现零逻辑;规格=art-bible §6.5 v3.5,正主=HUD框架「视野迷雾」节)──
|
||||
# 0=正常(灯光区内)/ 1=全黑(区外未开格——正主「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
|
||||
# ── 视野迷雾呈现态(纯呈现零逻辑;规格=art-bible §6.5 v3.5,现行事实源=HUD框架「视野迷雾」节)──
|
||||
# 0=正常(灯光区内)/ 1=全黑(区外未开格——现行事实源「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
|
||||
var fog_state := 0:
|
||||
set(v):
|
||||
if fog_state != v:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## D 区演出层:清图喷泉/Boss 影子/完美排雷光带/封印命中弹道/挥铲三帧等一次性占位演出。
|
||||
## D 区演出层:清图喷泉/Boss 影子/完美封印光带/封印命中弹道/挥铲三帧等一次性占位演出。
|
||||
## 组合根按核心信号直调 play_*;素材缺位静默回退(演出层不设断言面)。
|
||||
class_name BoardFxView
|
||||
extends Control
|
||||
@@ -87,7 +87,7 @@ func play_boss_shadow() -> void:
|
||||
tw.tween_callback(shadow.queue_free)
|
||||
|
||||
|
||||
# 完美排雷:金光带加倍扫射 + 横幅占位(§2.2 最高档)
|
||||
# 完美封印:金光带加倍扫射 + 横幅占位(§2.2 最高档)
|
||||
func play_perfect_sweep() -> void:
|
||||
for i in 2:
|
||||
var band := ColorRect.new()
|
||||
@@ -99,7 +99,7 @@ func play_perfect_sweep() -> void:
|
||||
var tw := band.create_tween()
|
||||
tw.tween_property(band, "position:x", size.x + 8.0, UiKit.motion(0.5 + 0.15 * i))
|
||||
tw.tween_callback(band.queue_free)
|
||||
_show_banner("完美排雷 ×2")
|
||||
_show_banner("完美封印 ×2")
|
||||
|
||||
|
||||
# 封印命中:封印化暴击弹道加重占位(自棋盘上缘飞向 B 区敌人卡方向——越界即收)
|
||||
|
||||
@@ -117,7 +117,7 @@ var _digit_dark_ink := true # 数字墨族按板自适应(true=深墨
|
||||
var _digit_plate := false # 数字色牌垫底模式(双峰纹理板——直画两族皆不过门槛时落牌,
|
||||
# 素牌+深墨 12:1 纹理免疫;渲染帧取证 underdark 系深墨直画隐形,故启用色牌)
|
||||
|
||||
# ---- 视野迷雾(纯呈现层;正主=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
|
||||
# ---- 视野迷雾(纯呈现层;现行事实源=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
|
||||
const FOG_RADIUS := 8 # 菱形灯光区基准半径(曼哈顿距离 ≤R——与微光环「周围 8 格」同圈起步;半径基准值改动=非白名单须用户)
|
||||
var _fog_enabled := true # 迷雾开关(呈现态布尔——真实游戏恒 on;探针/夹具可 off=无迷雾基线,两态结算逐位一致断言面)
|
||||
var _fog_lantern := 0 # 视野加成现值(缺省 0=无加成基线零扰动;生产写入面=组合根每战装配链显式重算——main._sync_treasure_glow)
|
||||
@@ -142,7 +142,7 @@ func _init() -> void:
|
||||
add_child(_wrap)
|
||||
_svp_container = SubViewportContainer.new()
|
||||
_svp_container.stretch = true # 容器尺寸 = 视口逻辑分辨率(_update_world_size 同步)——恒 1:1 无缩放
|
||||
_svp_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # 输入归壳层 _gui_input(板级公式换算),不进 SubViewport
|
||||
_svp_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # 输入归壳层 _gui_input(整图公式换算),不进 SubViewport
|
||||
_wrap.add_child(_svp_container)
|
||||
_viewport = SubViewport.new()
|
||||
_viewport.transparent_bg = true # 格间距露出 D 区底色
|
||||
@@ -502,7 +502,7 @@ func click_cell(cell: Vector2i, button: int) -> void:
|
||||
_on_cell_input(cell, ev)
|
||||
|
||||
|
||||
# 板级公式单点:格坐标 = floor(板内局部坐标 / 格步长)
|
||||
# 整图公式单点:格坐标 = floor(板内局部坐标 / 格步长)
|
||||
#(格步长 = 格边长 + 间距;板外/负域返回 (-9,-9);2px 格间隙落入左/上邻格)
|
||||
func _cell_at(shell_pos: Vector2) -> Vector2i:
|
||||
if _board == null or _board.width == 0:
|
||||
@@ -534,7 +534,7 @@ func _update_world_size() -> void:
|
||||
_viewport.size = px
|
||||
|
||||
|
||||
# 真实输入路径(Node2D 迁移批 Decision 3):壳层 _gui_input → 板级公式换算 → 复用既有 hover/点选/chord 出口
|
||||
# 真实输入路径(Node2D 迁移批 Decision 3):壳层 _gui_input → 整图公式换算 → 复用既有 hover/点选/chord 出口
|
||||
func _gui_input(ev: InputEvent) -> void:
|
||||
if _minesweeper == null:
|
||||
return
|
||||
@@ -832,7 +832,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||
u.mutation_mark = ""
|
||||
u.badge_glyph = _update_badge(c)
|
||||
u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
|
||||
# 视野迷雾呈态:曼哈顿距离 > 半径 → 区外——未开格全黑(正主照抄)/
|
||||
# 视野迷雾呈态:曼哈顿距离 > 半径 → 区外——未开格全黑(现行事实源照抄)/
|
||||
# 已开格记忆态(灰显可读);以英雄格为心逐格重算,refresh()/notify_hero_move() 既有重绘驱动=移动实时跟随。
|
||||
# 纯呈现零逻辑——格态/数字真值/清图判定零触碰(🔴 契约 1);结算期去饱和叠加互斥由 CellUnit 记忆态终态档承载。
|
||||
if _fog_enabled:
|
||||
|
||||
@@ -107,7 +107,7 @@ func open_pick(header: String, cards: Array) -> void:
|
||||
_confirm_btn.visible = false
|
||||
_continue_btn.visible = false
|
||||
_foot_hint.visible = true # T7 底注=pick 态专属(result 态让位)
|
||||
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内门票面不投喂=无刷新)
|
||||
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内专长凭证面不投喂=无刷新)
|
||||
_refresh_l.text = ""
|
||||
visible = true
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ func set_feat_holdings(lines: Array) -> void:
|
||||
_feat_section.visible = not lines.is_empty()
|
||||
|
||||
|
||||
# 地形变异常驻角标:lines = 文案行(板级变异 display_name+体感短语)——空数组 = 整节隐藏
|
||||
# 地形变异常驻角标:lines = 文案行(整图变异 display_name+体感短语)——空数组 = 整节隐藏
|
||||
func set_board_variants(lines: Array) -> void:
|
||||
for c3 in _board_variant_list.get_children():
|
||||
_board_variant_list.remove_child(c3)
|
||||
@@ -157,7 +157,7 @@ func _data_line() -> Label:
|
||||
|
||||
# ---- 秘宝库存条(C 区四槽常读+两步交互第一步)----
|
||||
# 投影行 = {handle, glyph, name, used, usable}(组合根组装:字形=道具名首字;数据源 runner 级库存只读面)。
|
||||
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(板级零信息);
|
||||
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(整图零信息);
|
||||
# selected=金框高亮(两步交互第一步的可见态)。tooltip=全名+维度语义。
|
||||
func set_treasure_inventory(items: Array, selected_handle: int) -> void:
|
||||
_selected_handle = selected_handle
|
||||
|
||||
@@ -11,7 +11,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
# 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)——
|
||||
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)。
|
||||
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美封印金光带)。
|
||||
# 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2)
|
||||
func play_round_sweep(board_rect: Rect2) -> void:
|
||||
if board_rect.size == Vector2.ZERO:
|
||||
@@ -143,7 +143,7 @@ func play_round_banner(ri: int) -> void:
|
||||
tw.tween_callback(banner.queue_free)
|
||||
|
||||
|
||||
# 开战横幅——板级地形变异预告:board_variants 非空时开战
|
||||
# 开战横幅——整图地形变异预告:board_variants 非空时开战
|
||||
# 显示一行(文本=「地形变异 · 贫矿脉」式);减动效=静态驻留后即隐(轮横幅先例——行呈现不跳过)。
|
||||
# 文案/驻留参数 provisional 归本地化批;普通图恒不发射(组合根按快照 board_variants 空判)。
|
||||
func play_board_variant_banner(text: String) -> void:
|
||||
|
||||
@@ -62,7 +62,7 @@ func _initialize() -> void:
|
||||
elif feat_mode == "ab_b":
|
||||
feat_profile = {"mode": "off", "tree_rules": ["posture_bulwark"]}
|
||||
elif feat_mode == "rules":
|
||||
feat_profile = {"mode": "off", "tree_rules": String(args[7] if args.size() >= 8 else "").split(",")} # V19——任意键集配对注入
|
||||
feat_profile = {"mode": "off", "tree_rules": Array(String(args[7] if args.size() >= 8 else "").split(",", false))} # V19——任意键集配对注入(Array 包裹防 PackedStringArray 被 adventure_runner is Array 守卫整组丢弃)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
if not db.load_tables("res://data/tables", false):
|
||||
print("表加载失败")
|
||||
|
||||
@@ -28,7 +28,7 @@ var _cur_settlement = null # 当关 settlement(身份回调读取面——
|
||||
func _ready() -> void:
|
||||
# 运行前提=窗口化真渲染面(shot_probe 同款护栏——headless 下 frame_post_draw 永不触发+视口纹理恒空)
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);正主=tools/render_gate.sh 取证段")
|
||||
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);现行事实源=tools/render_gate.sh 取证段")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var args := OS.get_cmdline_user_args()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 布局几何断言探针(正主=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收)
|
||||
# 布局几何断言探针(现行事实源=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收)
|
||||
# 用途:battle.md §验收「1280×800 与 16:9 全屏下三区无遮挡/无重叠/无裁切」的可执行化
|
||||
# GDD 锚:design/gdd/ux/battle.md §Layout Zones(B/C/D 三区几何)
|
||||
# 硬约束:档位表是数据、断言是一份函数据——加档只改表不动断言(禁每档建一个探针)
|
||||
|
||||
@@ -222,7 +222,7 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
adr.present_node_curio(ncands)
|
||||
var npick: Dictionary = _pick_token(ncands, String(st.get("feat_mode", "off")))
|
||||
if adr.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))):
|
||||
# 门票拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
|
||||
# 专长凭证拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
|
||||
# 不作此分支则节点停点悬空→选路卡死(MC timeout 形态)
|
||||
if String(npick.get("kind", "")) == "feat_ticket":
|
||||
var fcands: Array = (adr.node_curio_pending_state().get("candidates", []) as Array)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 整屏截图探针(呈现层测试分级 B 档基建:视口截图程序化产出;七屏——
|
||||
# 菜单/设置面/生涯页/营地/关卡画面/战斗/状态面板,正主=tools/render_gate.sh)。
|
||||
# 菜单/设置面/生涯页/营地/关卡画面/战斗/状态面板,现行事实源=tools/render_gate.sh)。
|
||||
# 必须**窗口化**运行(真渲染设备——headless 无渲染面,get_image 得到空纹理):
|
||||
# Godot_console.exe --path . res://smoke/shot_probe.tscn -- <输出目录>
|
||||
# 流程 = 真实按钮信号路径驱动(ui_click_probe 同款;存档/设置/元进度三面换注隔离档——禁碰真实档,
|
||||
@@ -25,7 +25,7 @@ var _meta := {} # 控件实测几何(全局 rect——shot_checker 文字框
|
||||
func _ready() -> void:
|
||||
# 运行前提=窗口化真渲染面:headless 下 frame_post_draw 永不触发+视口纹理恒空——无此守卫时 headless 调用方必挂死
|
||||
if DisplayServer.get_name() == "headless":
|
||||
print("[shot_probe] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);headless 自动闸禁跑本探针(正主=tools/probe_gate.sh)")
|
||||
print("[shot_probe] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);headless 自动闸禁跑本探针(现行事实源=tools/probe_gate.sh)")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
var args := OS.get_cmdline_user_args()
|
||||
|
||||
+159
-109
@@ -697,6 +697,7 @@ func _run_core_smoke(db, rng) -> void:
|
||||
_smoke_meta()
|
||||
_smoke_tree()
|
||||
_smoke_tree_rules()
|
||||
_smoke_counter_buckets()
|
||||
_smoke_node_map_templates()
|
||||
|
||||
|
||||
@@ -1357,15 +1358,15 @@ func _smoke_feats() -> void:
|
||||
if String((t as Dictionary).get("kind", "")) == "relic":
|
||||
node_relic_seen = true
|
||||
check(feat_seen.size() >= 2, "通关渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)")
|
||||
check(not node_relic_seen, "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以门票形态出现)")
|
||||
# T5 门票行为断言(A5 拿到即开 / A6 保底计数——契约④四口径;独立 runner 防污染 a_r 牌库)
|
||||
check(not node_relic_seen, "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以专长凭证形态出现)")
|
||||
# T5 专长凭证行为断言(A5 拿到即开 / A6 保底计数——契约④四口径;独立 runner 防污染 a_r 牌库)
|
||||
var a_t = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(91))
|
||||
a_t.start_adventure(91, {"first_script": true})
|
||||
var stock_before := {}
|
||||
for kk in ["attack", "gp", "heal"]:
|
||||
stock_before[kk] = a_t.pool_stock(kk)
|
||||
a_t._node_curio_pending = {"candidates": [{"kind": "feat_ticket", "tier": "excellent"}, {"kind": "attack", "tier": "common"}]}
|
||||
check(a_t.apply_node_curio("feat_ticket", "excellent"), "A5 门票兑付受理(节点渠道——拿到即开)")
|
||||
check(a_t.apply_node_curio("feat_ticket", "excellent"), "A5 专长凭证兑付受理(节点渠道——拿到即开)")
|
||||
var tkt_cands: Array = (a_t.node_curio_pending_state().get("candidates", []) as Array)
|
||||
var tkt_all_relic := tkt_cands.size() > 0
|
||||
for t in tkt_cands:
|
||||
@@ -1373,13 +1374,13 @@ func _smoke_feats() -> void:
|
||||
tkt_all_relic = false
|
||||
check(tkt_all_relic, "A5 拿到即开——停点候选已替换为专长三选一(全 relic)")
|
||||
check(a_t.pool_stock("attack") == int(stock_before["attack"]) and a_t.pool_stock("gp") == int(stock_before["gp"]),
|
||||
"A5 零摘牌——门票兑付不动令牌库(不进持有集)")
|
||||
check(not (a_t.feats_held() as Array).has("feat_ticket"), "A5 无门票残留持有集")
|
||||
"A5 零摘牌——专长凭证兑付不动令牌库(不进持有集)")
|
||||
check(not (a_t.feats_held() as Array).has("feat_ticket"), "A5 无专长凭证残留持有集")
|
||||
var feats_n := (a_t.feats_held() as Array).size()
|
||||
a_t.apply_node_curio("relic", String((tkt_cands[0] as Dictionary).get("tier", "rare")), String((tkt_cands[0] as Dictionary).get("feat_id", "")))
|
||||
check((a_t.feats_held() as Array).size() == feats_n + 1, "A5 专长面兑付受理(门票开出候选可正常入持有集)")
|
||||
check((a_t.feats_held() as Array).size() == feats_n + 1, "A5 专长面兑付受理(专长凭证开出候选可正常入持有集)")
|
||||
# A6 保底:pity 直填 5 → 下次展示必出(第 6 次必出口径);限购满 → 恒不触发
|
||||
a_t._feat_ticket_sold = 0 # A5 已兑付一枚门票(sold=1=limit)——A6 段重置计数隔离验证
|
||||
a_t._feat_ticket_sold = 0 # A5 已兑付一枚专长凭证(sold=1=limit)——A6 段重置计数隔离验证
|
||||
a_t._feat_ticket_pity = int(a_t.db.adventure.feat_ticket_pity_step)
|
||||
var rg_t: RandomNumberGenerator = a_t.rng.open_stream("reward_pick")
|
||||
check(a_t._feat_ticket_consider(rg_t), "A6 保底触发(pity=5 → 第 6 次展示必出——概率无关)")
|
||||
@@ -1422,188 +1423,188 @@ func _smoke_feats() -> void:
|
||||
for h in hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
var sv_c = h.get("set", null)
|
||||
fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if sv_c == null else int(sv_c), sv_c != null)
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if sv_c == null else int(sv_c), sv_c != null)
|
||||
# 开格系
|
||||
fx.rebuild(["twin_pick"], [], [])
|
||||
for i in 4:
|
||||
var hits_dp: Array = fx.dispatch("dig_paid", {"ap_cost": 1})
|
||||
var hits_dp: Array = fx.dispatch(orch.counters_ctx, "dig_paid", {"ap_cost": 1})
|
||||
var refund := false
|
||||
for h in hits_dp:
|
||||
if String(h.get("kind", "")) == "grant_ap":
|
||||
refund = true
|
||||
fx.counter_add("twin_digs", 1)
|
||||
EffectEngine.counter_add(orch.counters_ctx, "adv_twin_digs", 1)
|
||||
if i == 1 or i == 3:
|
||||
check(refund, "双管镐:第 %d 次付费挖格返 1 AP(每第 2 次——mod_eq 预评估口径)" % (i + 1))
|
||||
else:
|
||||
check(not refund, "双管镐反例:第 %d 次不返(未满 2 次)" % (i + 1))
|
||||
fx.rebuild(["geologist"], [], [])
|
||||
var geo_hits: Array = fx.dispatch("dig_reveal", {"new_resource_type": true})
|
||||
var geo_hits: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": true})
|
||||
apply_fx.call(geo_hits)
|
||||
check(orch._hits_of(geo_hits, "counter_add").size() == 1, "地质学家:首开新类型集 1 点〔专长批 V3〕")
|
||||
fx.counter_add("geo_types", 2) # 手动基数补齐(row1 已落账 1+2=3——满 4 种的预评估现值)
|
||||
var geo_hits2: Array = fx.dispatch("dig_reveal", {"new_resource_type": true})
|
||||
EffectEngine.counter_add(orch.counters_ctx, "geo_types", 2) # 手动基数补齐(row1 已落账 1+2=3——满 4 种的预评估现值)
|
||||
var geo_hits2: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": true})
|
||||
apply_fx.call(geo_hits2)
|
||||
check(orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and fx.counter("geo_types") == 0,
|
||||
check(orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and EffectEngine.counter(orch.counters_ctx, "geo_types") == 0,
|
||||
"地质学家:满 4 种(预评估现值 3+本格)→揭示 1 格·计数清零〔专长批 V3〕")
|
||||
var geo_hits3: Array = fx.dispatch("dig_reveal", {"new_resource_type": false})
|
||||
var geo_hits3: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": false})
|
||||
check(orch._hits_of(geo_hits3, "counter_add").is_empty(), "地质学家反例:同类型重复首开不叠点〔专长批 V3〕")
|
||||
fx.rebuild(["hard_hat"], [], [])
|
||||
apply_fx.call(fx.dispatch("dig_reveal", {}))
|
||||
apply_fx.call(fx.dispatch("dig_reveal", {}))
|
||||
check(fx.counter("helm_layers") == 2, "安全帽:开安全格攒层 ×2〔专长批 V3〕")
|
||||
var hh_hits: Array = fx.dispatch("mine_hit", {"damage": 6})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "dig_reveal", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "dig_reveal", {}))
|
||||
check(EffectEngine.counter(orch.counters_ctx, "helm_layers") == 2, "安全帽:开安全格攒层 ×2〔专长批 V3〕")
|
||||
var hh_hits: Array = fx.dispatch(orch.counters_ctx, "mine_hit", {"damage": 6})
|
||||
var hh_reduce := 0
|
||||
for h in hh_hits:
|
||||
if String(h.get("kind", "")) == "damage_reduce":
|
||||
hh_reduce = fx.counter(String(h.get("value_counter", "")))
|
||||
hh_reduce = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(hh_reduce == 2, "安全帽:踩雷耗层减伤=层值 2(一次清零随施加面 set 行)〔专长批 V3〕")
|
||||
fx.rebuild(["skip_mining"], [], [])
|
||||
var sk_zero: Array = fx.dispatch("dig_reveal", {"is_blank_zero": true})
|
||||
var sk_zero: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": true})
|
||||
apply_fx.call(sk_zero)
|
||||
check(orch._hits_of(sk_zero, "counter_add").size() == 1, "跳格采掘:空白 0 格攒 1 点〔专长批 V3〕")
|
||||
fx.counter_add("skip_pts", 1) # 手动基数补齐(已落账 1+1=2——满 3 点的预评估现值)
|
||||
var sk_full: Array = fx.dispatch("dig_reveal", {"is_blank_zero": true})
|
||||
EffectEngine.counter_add(orch.counters_ctx, "skip_pts", 1) # 手动基数补齐(已落账 1+1=2——满 3 点的预评估现值)
|
||||
var sk_full: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": true})
|
||||
apply_fx.call(sk_full)
|
||||
check(orch._hits_of(sk_full, "reveal_tiles").size() == 1,
|
||||
"跳格采掘:满 3 点→揭示 1 格〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty(),
|
||||
"跳格采掘反例:普通开格不计点〔专长批 V3〕")
|
||||
fx.rebuild(["blast_accounting"], [], [])
|
||||
for i in 5:
|
||||
var ba_hits: Array = fx.dispatch("dig_paid", {"ap_cost": 1})
|
||||
var ba_hits: Array = fx.dispatch(orch.counters_ctx, "dig_paid", {"ap_cost": 1})
|
||||
for h in ba_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch("round_end", {}), "grant_gp").size() == 1, "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕")
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1, "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕")
|
||||
fx.rebuild(["frontier_miner"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1
|
||||
and orch._hits_of(fx.dispatch("dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty(),
|
||||
"边陲矿工:边缘格产出 +1·中心格反例零产出〔专长批 V3〕")
|
||||
# 封印系
|
||||
fx.rebuild(["seal_echo"], [], [])
|
||||
apply_fx.call(fx.dispatch("seal_hit", {}))
|
||||
apply_fx.call(fx.dispatch("seal_hit", {}))
|
||||
var se_hits: Array = fx.dispatch("round_end", {})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "seal_hit", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "seal_hit", {}))
|
||||
var se_hits: Array = fx.dispatch(orch.counters_ctx, "round_end", {})
|
||||
check(orch._hits_of(se_hits, "grant_ap").size() == 1 and String((orch._hits_of(se_hits, "grant_ap")[0] as Dictionary).get("mode", "")) == "queue",
|
||||
"封印回响:标中计数≥2→轮末返 1 AP(下轮 queue)〔专长批 V3〕")
|
||||
fx.rebuild(["seal_smith"], [], [])
|
||||
var sm_remove := 0
|
||||
for i in 3:
|
||||
var sm_dispatch: Array = fx.dispatch("seal_hit", {})
|
||||
var sm_dispatch: Array = fx.dispatch(orch.counters_ctx, "seal_hit", {})
|
||||
apply_fx.call(sm_dispatch)
|
||||
for h in sm_dispatch:
|
||||
if String(h.get("kind", "")) == "remove_mines":
|
||||
sm_remove += 1
|
||||
check(sm_remove == 1, "封印匠:标中每第 3 次→中立移除恰一次(频率闸死)〔专长批 V3〕")
|
||||
fx.rebuild(["seal_stake"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "ac_bonus").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch("seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round",
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round",
|
||||
"封印桩:标中本轮 AC +N(round 域——round_start 清零)〔专长批 V3〕")
|
||||
fx.rebuild(["warning_beacon"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "preview_area").size() == 1, "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "preview_area").size() == 1, "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕")
|
||||
# 踩雷/受伤系
|
||||
fx.rebuild(["pain_memory"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var pm_hits: Array = fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var pm_hits: Array = fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
var pm_bonus := 0
|
||||
for h in orch._hits_of(pm_hits, "attack_bonus"):
|
||||
pm_bonus = fx.counter(String(h.get("value_counter", "")))
|
||||
pm_bonus = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(pm_bonus == 2, "疼痛记忆:攻击附加层值伤害=层值 2·用后清零(set 行随施加面)〔专长批 V3〕")
|
||||
fx.rebuild(["fuse_wire"], [], [])
|
||||
var fw_hits: Array = fx.dispatch("damage_taken", {"kind": "terrain", "amount": 6})
|
||||
var fw_hits: Array = fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 6})
|
||||
var fw_shield := 0
|
||||
for h in fw_hits:
|
||||
if String(h.get("kind", "")) == "shield":
|
||||
fw_shield += int(h.get("value", 0))
|
||||
check(orch._hits_of(fx.dispatch("damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty(),
|
||||
"保险丝反例:单次 <5 不攒点不触发〔专长批 V3〕")
|
||||
for h in fx.dispatch("damage_taken", {"kind": "terrain", "amount": 6}):
|
||||
for h in fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 6}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
check(fw_shield == 0, "保险丝:首点不出盾(2 点才出——预评估口径)〔专长批 V3〕")
|
||||
fx.rebuild(["blood_insight"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "reveal_tiles").size() == 1, "见血开窍:踩雷揭示 1 格〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "reveal_tiles").size() == 1, "见血开窍:踩雷揭示 1 格〔专长批 V3〕")
|
||||
fx.rebuild(["trap_instinct"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var ti_hits: Array = fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var ti_hits: Array = fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
var ti_val := 0
|
||||
for h in orch._hits_of(ti_hits, "attack_bonus"):
|
||||
ti_val = fx.counter(String(h.get("value_counter", "")))
|
||||
ti_val = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(ti_val == 2, "陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道——面板加算)〔专长批 V3〕")
|
||||
fx.rebuild(["chain_traps", "pain_memory"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var ct_before: int = fx.counter("pain_layers")
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
check(fx.counter("pain_layers") == ct_before + 2, "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕")
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var ct_before: int = EffectEngine.counter(orch.counters_ctx, "pain_layers")
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
check(EffectEngine.counter(orch.counters_ctx, "pain_layers") == ct_before + 2, "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕")
|
||||
fx.rebuild(["trap_burst"], [], [])
|
||||
var tb_hits: Array = fx.dispatch("mine_hit", {"damage": 8})
|
||||
var tb_hits: Array = fx.dispatch(orch.counters_ctx, "mine_hit", {"damage": 8})
|
||||
check(orch._hits_of(tb_hits, "blast_scaled").size() == 1
|
||||
and float((orch._hits_of(tb_hits, "blast_scaled")[0] as Dictionary).get("mult", 0)) > 1.0,
|
||||
"陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled——施加面值=damage×mult)〔专长批 V3〕")
|
||||
fx.rebuild(["blood_honey"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "heal").size() == 1, "血蜜:踩雷回复 N 生命〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "heal").size() == 1, "血蜜:踩雷回复 N 生命〔专长批 V3〕")
|
||||
fx.rebuild(["stone_skin"], [], [])
|
||||
var ss_hits: Array = fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1})
|
||||
var ss_hits: Array = fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1})
|
||||
check(orch._hits_of(ss_hits, "ac_bonus").size() == 1
|
||||
and int((orch._hits_of(ss_hits, "ac_bonus")[0] as Dictionary).get("cap", 0)) == 3,
|
||||
"石肤:受伤 AC+1(本场域 cap 3——达上限不再涨由累积器封顶)〔专长批 V3〕")
|
||||
fx.rebuild(["last_stand"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1
|
||||
and orch._hits_of(fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty(),
|
||||
"背水:生命≤50% 减伤·过半血反例零减伤〔专长批 V3〕")
|
||||
# 跨族桥
|
||||
fx.rebuild(["breaker_strike"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1
|
||||
and orch._hits_of(fx.dispatch("mine_hit", {"dug_under_seal": false}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": false}), "counter_add").is_empty(),
|
||||
"破封一击:挖开带封印危险格 +1 层·无封印反例零触发〔专长批 V3〕")
|
||||
# 节奏系
|
||||
fx.rebuild(["map_exchange"], [], [])
|
||||
var me_hits: Array = fx.dispatch("dig_stop", {"ap_left": 4})
|
||||
var me_hits: Array = fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 4})
|
||||
apply_fx.call(me_hits)
|
||||
fx.counter_add("stop_swap", 4) # value_event="ap_left" 解析归施加面(action_economy 载荷取值)——冒烟手动落账
|
||||
EffectEngine.counter_add(orch.counters_ctx, "stop_swap", 4) # value_event="ap_left" 解析归施加面(action_economy 载荷取值)——冒烟手动落账
|
||||
check(orch._hits_of(me_hits, "counter_add").size() == 1, "弃图兑换:停手剩余 AP≥3 入账〔专长批 V3〕")
|
||||
var me_atk := 0
|
||||
for h in orch._hits_of(fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}), "attack_bonus"):
|
||||
me_atk = fx.counter(String(h.get("value_counter", "")))
|
||||
for h in orch._hits_of(fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}), "attack_bonus"):
|
||||
me_atk = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(me_atk == 4, "弃图兑换:折为攻击伤害=剩余 AP 全额·用后清零〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("dig_stop", {"ap_left": 2}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 2}), "counter_add").is_empty(),
|
||||
"弃图兑换反例:剩余 AP<3 不触发〔专长批 V3〕")
|
||||
fx.rebuild(["last_drop"], [], [])
|
||||
var ld_hits: Array = fx.dispatch("round_start", {"round": 2, "ap_exhausted_last": true})
|
||||
var ld_hits: Array = fx.dispatch(orch.counters_ctx, "round_start", {"round": 2, "ap_exhausted_last": true})
|
||||
check(orch._hits_of(ld_hits, "grant_ap").size() == 1, "一滴不剩:上轮 AP 耗尽→+1 AP〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty(),
|
||||
"一滴不剩反例:上轮有余不发〔专长批 V3〕")
|
||||
fx.rebuild(["market_close"], [], [])
|
||||
for i in 8:
|
||||
for h in fx.dispatch("dig_reveal", {}):
|
||||
for h in fx.dispatch(orch.counters_ctx, "dig_reveal", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch("round_end", {}), "grant_gp").size() == 1, "收盘:本轮开格≥8→+1 GP〔专长批 V3〕")
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1, "收盘:本轮开格≥8→+1 GP〔专长批 V3〕")
|
||||
# 战斗系
|
||||
fx.rebuild(["execution_scholar"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1,
|
||||
"斩首学者:击杀揭示 1 格〔专长批 V3〕")
|
||||
fx.rebuild(["executioner"], [], [])
|
||||
var ex_hits: Array = fx.dispatch("enemy_hp_threshold", {"unit": "e1", "pct": 30, "is_boss": false})
|
||||
var ex_hits: Array = fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 30, "is_boss": false})
|
||||
check(orch._hits_of(ex_hits, "target_attack_bonus").size() == 1
|
||||
and orch._hits_of(fx.dispatch("enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty(),
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty(),
|
||||
"处刑台:敌血量≤30% 挂单位标记加伤·阈值外反例零命中〔专长批 V3〕")
|
||||
fx.rebuild(["field_dressing"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("battle_won", {"round": 2}), "heal").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "battle_won", {"round": 2}), "heal").size() == 1,
|
||||
"战地包扎:战斗胜利回复 N 生命(battle_won 新挂点)〔专长批 V3〕")
|
||||
# 契约尖点
|
||||
fx.rebuild(["blood_tax"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "bonus_delta").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch("seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk",
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "bonus_delta").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk",
|
||||
"血税:双向同局账本(踩陷阱−2max_hp/封印命中+1atk——bonus_delta 战末汇入)〔专长批 V3〕")
|
||||
# 迁入 2 件
|
||||
fx.rebuild(["venom_edge"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1
|
||||
and orch._hits_of(fx.dispatch("clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty(),
|
||||
"淬毒刃纹(迁入):克制正面(magic_vs_heavy 矩阵值>0)追加·中性面反例零触发〔专长批 V3〕")
|
||||
fx.rebuild(["seal_burst"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "open_ticket").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "open_ticket").size() == 1,
|
||||
"封印爆裂(迁入):标中全体敌群伤〔专长批 V3〕")
|
||||
# 零持有
|
||||
var orch0 = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(74))
|
||||
@@ -1841,7 +1842,7 @@ func _smoke_type_glow() -> void:
|
||||
check((snap.get("cells", {}) as Dictionary).size() == mapped - 1,
|
||||
"V2 清单外格剔除后作用集 = 派定数 −1(%d→%d)" % [mapped, (snap.get("cells", {}) as Dictionary).size()])
|
||||
# ---- 相 3:封锁格(query 返回 null)⇒ 无微光且零登记 ----
|
||||
_glow_type_overrides.erase(bogus_cell) # 复位清单外(errors 保留=板级登记语义)
|
||||
_glow_type_overrides.erase(bogus_cell) # 复位清单外(errors 保留=整图登记语义)
|
||||
var blocked_cell: Vector2i = hero
|
||||
for i in mini(assign.size(), nbs.size()):
|
||||
var nb4: Vector2i = nbs[i]
|
||||
@@ -2582,7 +2583,7 @@ func _smoke_global_variant() -> void:
|
||||
vt.rows.assign([{"variant_id": "g3", "display_name": "负丙", "unlock_level": 2, "scope": "global",
|
||||
"effect": "resource_face", "effect_value": 30, "trigger": "on_open", "placement": "any",
|
||||
"board_types": [], "counter_items": []}])
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 板级恒在)")
|
||||
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 整图恒在)")
|
||||
vt.rows.assign([{"variant_id": "g4", "display_name": "负丁", "unlock_level": 2, "scope": "global",
|
||||
"effect": "resource_face", "effect_value": 30, "trigger": "always", "placement": "closed_only",
|
||||
"board_types": [], "counter_items": []}])
|
||||
@@ -2624,7 +2625,7 @@ func _smoke_global_variant() -> void:
|
||||
var boss1_calls := 0
|
||||
if boss_ok:
|
||||
var bb = boss_settlement.minesweeper.current_board()
|
||||
check(bb.board_variant_ids().is_empty(), "屏蔽后回落:Boss 图无板级全局型(board_variants 空——全局池空合法态)")
|
||||
check(bb.board_variant_ids().is_empty(), "屏蔽后回落:Boss 图无整图全局型(board_variants 空——全局池空合法态)")
|
||||
var boss_vids := {}
|
||||
for vc in bb.variants:
|
||||
for vid in bb.variants[vc]:
|
||||
@@ -3497,7 +3498,7 @@ func _encounter_replay(seed_val: int) -> Array:
|
||||
|
||||
|
||||
# ==================== 清图奖励双份兑付链 / 轮内刷新 provenance 守卫 / 窗口锚行 ====================
|
||||
# 清图 = 全安全格开完。清图测试用"真雷封印(零误标→完美排雷)+ AutoPlayer 逐轮清图"真实流驱动;
|
||||
# 清图 = 全安全格开完。清图测试用"真雷封印(零误标→完美封印)+ AutoPlayer 逐轮清图"真实流驱动;
|
||||
# DifficultyTable 注入 hp_mult=10 把 Boss 血量抬到 1500,保证清图批次发生在战斗结束之前(测试台账,跑完即还原)。
|
||||
|
||||
const CLEAR_SMOKE_DEPTH := 4
|
||||
@@ -3506,11 +3507,11 @@ var _regular_clear_reward_rows := 0 # 常规(非刷新图)清图的 clear_
|
||||
|
||||
|
||||
func _smoke_clear_reward_chain() -> void:
|
||||
print("--- 清图奖励·双份兑付链(完美排雷 / 零兑付 / 第二份被拒 / 高光记实)---")
|
||||
# ① 真实流完美排雷:标中一面真雷封印 + 零误标 + 清全图 → grant_clear 身份(clear_blast)双份真实兑付
|
||||
print("--- 清图奖励·双份兑付链(完美封印 / 零兑付 / 第二份被拒 / 高光记实)---")
|
||||
# ① 真实流完美封印:标中一面真雷封印 + 零误标 + 清全图 → grant_clear 身份(clear_blast)双份真实兑付
|
||||
var rows: Array = []
|
||||
var run := _run_full_clear(20260915, false, rows)
|
||||
check(bool(run.cleared), "① 完美排雷清图达成(%d 轮)" % int(run.rounds))
|
||||
check(bool(run.cleared), "① 完美封印清图达成(%d 轮)" % int(run.rounds))
|
||||
var dmg := []
|
||||
for r in rows:
|
||||
if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "clear_reward":
|
||||
@@ -4070,7 +4071,7 @@ func _drive_clear(settlement) -> Dictionary:
|
||||
if t.hero_cell != ms.hero_pos():
|
||||
ms.settle_batch([{"action": "move", "cell": t.hero_cell}])
|
||||
ms.settle_batch([{"action": "seal_place", "cell": t.seal_cell}])
|
||||
seal_done = true # 一面真雷封印 = 完美排雷前半(零误标),后半靠 AutoPlayer 清图
|
||||
seal_done = true # 一面真雷封印 = 完美封印前半(零误标),后半靠 AutoPlayer 清图
|
||||
var seq_before: int = int(settlement.diagnostic_snapshot().board_seq)
|
||||
var out := {"result": "", "rounds": 0, "terminated": false, "stream": []}
|
||||
_auto_play_round(settlement, out)
|
||||
@@ -5298,7 +5299,7 @@ func _smoke_shop_pool() -> void:
|
||||
# ⑧ 终局池读数(run_adventure pool 面——商店与奖励跨渠道消耗后的终局余量出讫)
|
||||
var st_pool: Dictionary = autoplay.run_adventure(_db, 42, 0, 0, false, null, "normal", "min", {}, {}, {"mode": "pool"})
|
||||
var ps: Dictionary = st_pool.get("pool_stock", {})
|
||||
check(ps.size() == 6, "终局池读数六键出讫(run_adventure pool 面——四池种+T5 门票+T6 heal 行,读数只锚一次载体)")
|
||||
check(ps.size() == 6, "终局池读数六键出讫(run_adventure pool 面——四池种+T5 专长凭证+T6 heal 行,读数只锚一次载体)")
|
||||
var ps_ok := true
|
||||
for k in ps.keys():
|
||||
var quota := 0
|
||||
@@ -6049,38 +6050,93 @@ func _smoke_tree() -> void:
|
||||
|
||||
# ---- 天赋树六键行为冒烟(V10:开局姿态窗/危险直觉限次/财富之道聚散/守侧失误补偿;
|
||||
# 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))----
|
||||
func _smoke_counter_buckets() -> void:
|
||||
print("--- 效果引擎无状态化四桶正向验收(卡② §9 V1/V6)---")
|
||||
# V1——adventure 桶跨战持久:同一 RunLedger 下两战各真实 request_dig 一次,
|
||||
# 断付费计数经 ctx 就地写入宿主字典并跨战累积(副本搬运态:读不到上一战值→第 2 战仍 1,此断言会红)。
|
||||
var led = load("res://core/run_ledger.gd").new()
|
||||
var RigS = load("res://core/battle_orchestrator.gd")
|
||||
var RngS = load("res://core/rng_service.gd")
|
||||
var enc1 = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
|
||||
"feats": ["twin_pick"], "tree_rules": [], "battle_index": 1,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0,
|
||||
"node_type": "battle", "ledger": led}
|
||||
var o1 = RigS.new(_db, _fresh_rng(779301))
|
||||
o1.begin_battle(enc1)
|
||||
var dig1: bool = o1.action_economy.request_dig(0, false)
|
||||
check(dig1, "V1 第1战真实 request_dig 受理(begin_battle 后 ACTIVE 相位)〔无状态化 V1〕")
|
||||
check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 1,
|
||||
"V1 第1战付费挖格经 ctx 就地写 adventure 桶=1(无 export 搬运时点)〔无状态化 V1〕")
|
||||
var enc2 = enc1.duplicate(true)
|
||||
enc2["battle_index"] = 2
|
||||
var o2 = RigS.new(_db, _fresh_rng(779302))
|
||||
o2.begin_battle(enc2)
|
||||
check(EffectEngine.counter(o2.counters_ctx, "adv_twin_digs") == 1,
|
||||
"V1 第2战 begin_battle 经 ctx 读到上一战持久值=1(读路径走宿主字典引用,非副本种子回灌)〔无状态化 V1〕")
|
||||
o2.action_economy.request_dig(0, false)
|
||||
check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 2,
|
||||
"V1 第2战挖后 adventure 桶跨战累积=2(run_ledger:42 判空早退缺陷消解的回归钉面——旧副本态恒 1)〔无状态化 V1〕")
|
||||
# V6——battle 桶随宿主销毁:第1战标错落 insurance_used(battle 桶无前缀),次战新编排器读 0。
|
||||
var encA = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
|
||||
"feats": ["perfect_insurance"], "tree_rules": [], "battle_index": 1,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0,
|
||||
"node_type": "battle", "ledger": load("res://core/run_ledger.gd").new()}
|
||||
var oA = RigS.new(_db, _fresh_rng(779401))
|
||||
oA.begin_battle(encA)
|
||||
var safe_a: Variant = null
|
||||
for ca in oA.minesweeper.current_board().all_cells():
|
||||
if not oA.minesweeper.current_board().is_mine(ca):
|
||||
safe_a = ca
|
||||
break
|
||||
oA._grant_seg._judge_seal({"cell": safe_a, "hit": false}, 1)
|
||||
check(EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1,
|
||||
"V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕")
|
||||
var encB = encA.duplicate(true)
|
||||
encB["battle_index"] = 2
|
||||
var oB = RigS.new(_db, _fresh_rng(779402))
|
||||
oB.begin_battle(encB)
|
||||
check(EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0,
|
||||
"V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕")
|
||||
|
||||
|
||||
func _smoke_tree_rules() -> void:
|
||||
print("--- 天赋树六键行为(V10:窗/限次/聚散/守侧)---")
|
||||
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构)
|
||||
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构 + 自建 ctx)
|
||||
var fx = load("res://core/effect_engine.gd").new(null)
|
||||
fx.rebuild([], [], ["posture_first_strike", "posture_bulwark"])
|
||||
check((fx.dispatch("round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1,
|
||||
var ctx_p := EffectContext.new()
|
||||
check((fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1,
|
||||
"先手压制:每关首战首轮窗命中(battle_index=1 且 round=1 → grant_ap queue)〔天赋树批 V10〕")
|
||||
check((fx.dispatch("round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty()
|
||||
and (fx.dispatch("round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
check((fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty()
|
||||
and (fx.dispatch(ctx_p, "round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
"先手压制:首轮窗外地不触发(第二轮/第二关首战首轮——「每战首轮」旧语义换代件)")
|
||||
check((fx.dispatch("damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1
|
||||
and (fx.dispatch("damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
check((fx.dispatch(ctx_p, "damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1
|
||||
and (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
"稳健拓荒:首轮窗内受击出盾、窗外不出(shield damage_taken 同窗条件)")
|
||||
# 危险直觉·锋:限次闸(引擎直构——mine_hit 挂点现成,编排收集 halve_pct 系既有 damage_mult 路径)
|
||||
# 危险直觉·锋:限次闸(引擎直构——mine_hit 挂点现成;限次计数住 level 桶,跨战持久=共享同一字典引用)
|
||||
var fz = load("res://core/effect_engine.gd").new(null)
|
||||
fz.rebuild([], [], ["sense_sharp"])
|
||||
var lvl_shared := {} # 同一关的 level 桶(两编排器共享引用 = 跨战持久,替代旧副本搬运)
|
||||
var ctx_fz := EffectContext.new()
|
||||
ctx_fz.level_bucket = lvl_shared
|
||||
var got_mult := false
|
||||
var got_add := false
|
||||
for h in (fz.dispatch("mine_hit", {}, Callable()) as Array):
|
||||
for h in (fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array):
|
||||
if String(h.get("kind", "")) == "damage_mult":
|
||||
got_mult = true
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
got_add = true
|
||||
check(got_mult and got_add, "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)")
|
||||
fz.counter_add("tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置关卡域计数后走事件路径)
|
||||
check((fz.dispatch("mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)")
|
||||
var exported: Dictionary = fz.counters_export()
|
||||
EffectEngine.counter_add(ctx_fz, "lvl_tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置 level 桶计数后走事件路径)
|
||||
check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)〔无状态化 V5〕")
|
||||
var fz2 = load("res://core/effect_engine.gd").new(null)
|
||||
fz2.rebuild([], [], ["sense_sharp"])
|
||||
fz2.counters_seed(exported)
|
||||
check((fz2.dispatch("mine_hit", {}, Callable()) as Array).is_empty(),
|
||||
"锋:限次跨战持久(adventure 域 counters_export/seed——断点保留同源,不新增存档域不升 v7)")
|
||||
var ctx_fz2 := EffectContext.new()
|
||||
ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久)
|
||||
check((fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty(),
|
||||
"锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕")
|
||||
# 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注
|
||||
var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001))
|
||||
check(orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
@@ -6119,13 +6175,7 @@ func _smoke_tree_rules() -> void:
|
||||
"守⑤ rig:次战编排启动(新战新队列实例)")
|
||||
check(int(orch2.action_economy.pending_ap_delta()) == 0,
|
||||
"守⑤:末轮判出不返(queue 只在 notify_round_start 兑现、战亡即弃——无跨战泄漏)")
|
||||
# 按关重置清零点(adventure_runner._start_current_level)
|
||||
var ar = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(777003))
|
||||
ar.run_ledger.adventure_counters = {"tree_sense_sharp_used": 2, "tree_sense_guard_used": 1, "unrelated_key": 9} # 计数账住 RunLedger
|
||||
ar._reset_tree_limited_counters()
|
||||
check(not ar.run_ledger.adventure_counters.has("tree_sense_sharp_used") and not ar.run_ledger.adventure_counters.has("tree_sense_guard_used")
|
||||
and int(ar.run_ledger.adventure_counters.get("unrelated_key", 0)) == 9,
|
||||
"限次按关重置:_start_current_level 清零点恰两键(他键零扰动——生命周期四态闭合)")
|
||||
# 树限次按关清零改由 LevelRunner.level_counters 随 new 承载(旧硬编码名单冒烟断言随函数删除——换代同生死;新语义由 V4/V5 段承载)
|
||||
# 财富之道·散:商品标价折价(level_runner 计价单点)
|
||||
var lr = load("res://core/level_runner.gd").new(_db, _fresh_rng(777004))
|
||||
lr.set_tree_rules([])
|
||||
@@ -6191,7 +6241,7 @@ func _smoke_battle_log_view() -> void:
|
||||
"cast": {"type": "cast", "display_name": "卓尔精灵 施放 毒雾", "source_class": "enemy_action", "round_index": 3},
|
||||
"decay": {"type": "decay", "value": 1, "round_index": 3},
|
||||
"verdict": {"type": "verdict", "display_name": "下一轮次", "value": 0, "round_index": 3},
|
||||
"highlight": {"type": "highlight", "display_name": "完美排雷!清图奖励 ×2", "value": 2, "source_class": "clear_reward", "round_index": 4},
|
||||
"highlight": {"type": "highlight", "display_name": "完美封印!清图奖励 ×2", "value": 2, "source_class": "clear_reward", "round_index": 4},
|
||||
}
|
||||
for row_type in rest:
|
||||
var before: int = view.parsed_text().length()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# 真实信号路径驱动一场战斗到击杀胜利(结算行在排播队列),断言两条:
|
||||
# ①胜利同帧:终局面未开 + 挂起态在案 + 队列非空(旧行为=同帧 flush+开窗,本断言即红——判别性);
|
||||
# ②排播放完:终局面开启(演播完边沿派发——_on_pump_ended)。
|
||||
# 存档/设置/元进度三面隔离(isolate_save 正主,meta 经 autoload 链同源),零真实档触碰。headless 可跑(不抓帧)。
|
||||
# 存档/设置/元进度三面隔离(isolate_save 现行事实源,meta 经 autoload 链同源),零真实档触碰。headless 可跑(不抓帧)。
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # 隔离样板单点
|
||||
@@ -17,7 +17,7 @@ func _ready() -> void:
|
||||
main = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
# 存档/设置两面隔离(ProbeUiUtil.isolate_save 正主,先删后建防陈旧档泄入)——
|
||||
# 存档/设置两面隔离(ProbeUiUtil.isolate_save 现行事实源,先删后建防陈旧档泄入)——
|
||||
# 隔离面走 attach_path 原地重置 autoload 单例:组合根 save_manager/meta 是getter-only
|
||||
# 读视图,直接赋值无效,隔离必须落在单例上。
|
||||
ProbeUiUtil.isolate_save(main, "t17_defer_probe", false, true, false)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# push_input 端到端输入探针(场景结构登记项转义务 + Node2D 呈现层 Risks 缓解面)。
|
||||
# 现役探针全走 BoardView.click_cell 直调(合成鼠标事件零命中已实证)——
|
||||
# 本探针是真实输入路径的唯一自动化覆盖:鼠标事件 → 壳层 _gui_input → 板级公式换算(_cell_at)→
|
||||
# 本探针是真实输入路径的唯一自动化覆盖:鼠标事件 → 壳层 _gui_input → 整图公式换算(_cell_at)→
|
||||
# 悬停/点选/chord 既有出口 → 组合根信号。四 case:①悬停可挖域(金框+十字光标)②悬停禁入
|
||||
# (hover_forbidden 信号+灰禁红框)③悬停已开格(路径预览点迹+移动光标)④右键封印(seal_place
|
||||
# 到达组合根+核心封印态)⑤双击 chord(禁入格双左击,核心拒挖零副作用)⑥左键挖(dig 到达组合根+核心揭格)
|
||||
|
||||
@@ -15,7 +15,7 @@ tests/
|
||||
smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取)
|
||||
evidence/ # 截图与人工签核记录
|
||||
run_all.gd # 单元套件统一入口(套件 manifest 协议,D2 AC-2b)
|
||||
static_scan.sh # 静态扫描域 S1–S8 可执行子集
|
||||
static_scan.sh # 静态扫描域 S1–S11 可执行子集
|
||||
```
|
||||
|
||||
## 测试三层与承载目录
|
||||
@@ -109,9 +109,11 @@ timeout 120s godot --headless --script tests/run_all.gd
|
||||
|
||||
## Static Scan(提交前本地跑)
|
||||
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S8 的可执行子集;规则全集与动机见
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S11 的可执行子集;规则全集与动机见
|
||||
`design/engineering/architecture/architecture.md`「测试与质量域」节。新违规案例必须**先补扫描规则再修码**。
|
||||
|
||||
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面,词表从 `design/gdd/术语表.md`「退役词对照」解析(显式追加「完美封印」,排除「巢穴」「旗」);三层豁免(闸门自指/显式留档 4 处/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
|
||||
|
||||
## 本地闸(唯一验收门禁)
|
||||
|
||||
本仓库无远端、无 CI——**验收只在本地发生**,跑 `tools/run_all_gates.sh` 是收口唯一入口,按本批改动域选档(`--code`/`--docs`/跨两域或改闸件本身=不带参数全量),
|
||||
|
||||
@@ -46,6 +46,7 @@ func _suites() -> Array:
|
||||
# negative=true=负面套件:坏表/违规输入是它的测试目标,输出里必然带 Parse Error,
|
||||
# 脚本错误扫描对它属误伤——豁免只放行这一件,且仅限「错误扫描」一闸(退出码与断言下限照判)。
|
||||
{"path": "res://tests/unit/data_config/reward_dangling_treasure_test.gd", "expect": 1, "negative": true}, # 秘宝双表校验
|
||||
{"path": "res://tests/unit/data_config/counter_prefix_fatal_test.gd", "expect": 1, "negative": true}, # 卡② V9 前缀合法性+引用闭合加载致命
|
||||
{"path": "res://tests/unit/data_config/rng_service_test.gd", "expect": 0},
|
||||
{"path": "res://tests/unit/core/treasure_items_test.gd", "expect": 0}, # 秘宝结构冻结断言面
|
||||
{"path": "res://tests/unit/core/breakpoint_resume_test.gd", "expect": 0}, # 断点往返/续玩确定性/幂等/退出清断点
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
5. 移动:已开格瞬移、**已引爆危险格可通行可读**(工程债呈现半边)
|
||||
6. 停手:RAP 1:1 转存、下轮 AP 硬重置同图继续
|
||||
7. 轮末结算:明细逐行呈现、来源标注可读(P2——"这一行是我哪步扫雷带来的"可答)
|
||||
8. 清图:清图奖励兑付 + 完美排雷高光(零误标时双份)+ 换新图;未刷新的停手轮不换图
|
||||
8. 清图:清图奖励兑付 + 完美封印高光(零误标时双份)+ 换新图;未刷新的停手轮不换图
|
||||
9. 轮内刷新(方案 A):Active 当场换图、AP 保留、RAP 扣减、每轮 ≤2 次、跨图事实结算完整
|
||||
|
||||
## Battle & Level
|
||||
|
||||
+143
-3
@@ -43,11 +43,11 @@ else
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# S10 — 文件头闸(code-conventions §1.4.1:头注 ≤8 行+日期/变更叙事黑名单)
|
||||
# S10 — 文件头闸(code-conventions §1.4.1:## 载体+日期/变更叙事黑名单;行数上限已停用)
|
||||
if python tools/audit_file_headers.py; then
|
||||
echo "[static_scan] S10 ok"
|
||||
else
|
||||
echo "[static_scan] S10 FAIL: 文件头存量见上清单(白名单清偿批在册)"
|
||||
echo "[static_scan] S10 FAIL: 文件头存量见上清单"
|
||||
fail=1
|
||||
fi
|
||||
|
||||
@@ -70,7 +70,7 @@ fi
|
||||
|
||||
# S3 — core/ 禁 ConfigDB 外 data 表加载(schema class_name 直引豁免)
|
||||
# 扫描路径必须写 game/core——写裸 `core` 仓根无此目录,恒零命中恒绿(静默空转)。
|
||||
# 豁免:①注释行 ②game/core/config_db.gd 自身(数值单一事实源的正主,它的单点 load 是本规则的例外)。
|
||||
# 豁免:①注释行 ②game/core/config_db.gd 自身(数值单一事实源的现行事实源,它的单点 load 是本规则的例外)。
|
||||
if require_scan_face "S3" "$DIR_CORE"; then
|
||||
hits=$(grep -rnE '\b(load|preload)[[:space:]]*\(|ResourceLoader\.load' "$DIR_CORE" --include='*.gd' 2>/dev/null \
|
||||
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' \
|
||||
@@ -93,4 +93,144 @@ fi
|
||||
# S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集(编排唯一性)
|
||||
# S8 结算路径 await 零命中(B1 结算/编排文件集)
|
||||
|
||||
# ── S11 — 代码注释禁用词 ──
|
||||
# 禁「板级」「门票」「正主」「落籍」「完美排雷」等退役词进注释面(完整词表见术语表解析)
|
||||
# 权威出处:design/gdd/术语表.md「退役词对照」节+「对外命名纪律」节
|
||||
# 扫描面:game/ 全域 .gd/.tres/.md/.json/.sh(.txt 不收=第三方许可证非本仓库文本)
|
||||
# 词表来源:解析「退役词对照」第一列(去括号注、斜杠拆分)+显式追加禁「完美排雷」(受对外命名纪律约束,不在退役词对照表里);显式排除「巢穴」(玩家专名/风味层,术语表自带除外注)、「旗」(单字误报率高,doc_term_audit 判例)
|
||||
# 注释文本提取:.gd/.sh 取 # 后文本;.tres 取 ; 后文本;.md 取整行;.json 无注释语法(保留扩展名但不产生注释段)
|
||||
# 三层豁免依序:①闸门自指(行内含检查/禁令形态即跳过)②显式留档豁免单 4 处 ③非注释面天然不取
|
||||
|
||||
if [ -d game/core ]; then
|
||||
S11_TABLE="design/gdd/术语表.md"
|
||||
S11_FAIL_LOCAL=0
|
||||
|
||||
if [ ! -f "$S11_TABLE" ]; then
|
||||
echo "[static_scan] S11 FAIL: 术语表文件不存在($S11_TABLE)"
|
||||
S11_FAIL_LOCAL=1
|
||||
else
|
||||
# 解析词表:「退役词对照」第一列→去括号注→斜杠拆分
|
||||
S11_WFILE=$(mktemp)
|
||||
awk '/^## 退役词对照/{f=1;next} f && /^## /{exit} f && /^\|/ && !/^\|---/ && !/退役词/{
|
||||
sub(/^\| */, ""); sub(/ *\|.*/, "")
|
||||
gsub(/([^)]*)/, ""); gsub(/\([^)]*\)/, "")
|
||||
gsub(/^ *| *$/, "")
|
||||
n = split($0, parts, " / ")
|
||||
for (i=1;i<=n;i++){gsub(/^ *| *$/,"",parts[i]); if(parts[i]!="") print parts[i]}
|
||||
}' "$S11_TABLE" > "$S11_WFILE"
|
||||
# 显式追加+排除
|
||||
printf '完美排雷\n' >> "$S11_WFILE"
|
||||
grep -vx '巢穴' "$S11_WFILE" | grep -vx '旗' | grep -vx 'flag_\*' | sort -u > "${S11_WFILE}.x" && mv "${S11_WFILE}.x" "$S11_WFILE"
|
||||
|
||||
# 自检二:词表解析非空
|
||||
S11_WC=$(grep -c . "$S11_WFILE" 2>/dev/null || echo 0)
|
||||
if [ "$S11_WC" -eq 0 ]; then
|
||||
echo "[static_scan] S11 FAIL: 词表解析为空——术语表结构可能已变"
|
||||
S11_FAIL_LOCAL=1
|
||||
fi
|
||||
|
||||
# 自检一:扫描面非空
|
||||
S11_FC=$(find game/ -type f \( -name '*.gd' -o -name '*.tres' -o -name '*.md' -o -name '*.json' -o -name '*.sh' \) 2>/dev/null | wc -l)
|
||||
if [ "$S11_FC" -eq 0 ]; then
|
||||
echo "[static_scan] S11 FAIL: 扫描面为空——路径写错导致的静默空转"
|
||||
S11_FAIL_LOCAL=1
|
||||
fi
|
||||
|
||||
if [ "$S11_FAIL_LOCAL" -eq 0 ]; then
|
||||
# 扫描三个文件类型并合并结果
|
||||
S11_ALL_HITS=$(
|
||||
# .gd/.sh:取 # 后注释
|
||||
find game/ -type f \( -name '*.gd' -o -name '*.sh' \) -print0 2>/dev/null \
|
||||
| xargs -0 awk -v wf="$S11_WFILE" '
|
||||
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
|
||||
/#/{
|
||||
cs=index($0,"#"); cmt=substr($0,cs+1)
|
||||
for(i=1;i<=nw;i++){
|
||||
if(index(cmt,words[i])>0){
|
||||
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
|
||||
if(FILENAME~"enemy.tres"&&FNR==653)next
|
||||
if(FILENAME~"level_runner"&&FNR==832)next
|
||||
if(FILENAME~"adventure_runner"&&FNR==403)next
|
||||
if(FILENAME~"damage_math_test"&&FNR==36)next
|
||||
print FILENAME":"FNR":"words[i]
|
||||
next
|
||||
}
|
||||
}
|
||||
}
|
||||
' 2>/dev/null
|
||||
# .tres:取 ; 后注释
|
||||
find game/ -type f -name '*.tres' -print0 2>/dev/null \
|
||||
| xargs -0 awk -v wf="$S11_WFILE" '
|
||||
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
|
||||
/;/{
|
||||
cs=index($0,";"); cmt=substr($0,cs+1)
|
||||
for(i=1;i<=nw;i++){
|
||||
if(index(cmt,words[i])>0){
|
||||
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
|
||||
if(FILENAME~"enemy.tres"&&FNR==653)next
|
||||
print FILENAME":"FNR":"words[i]
|
||||
next
|
||||
}
|
||||
}
|
||||
}
|
||||
' 2>/dev/null
|
||||
# .md:整行即注释面
|
||||
find game/ -type f -name '*.md' -print0 2>/dev/null \
|
||||
| xargs -0 awk -v wf="$S11_WFILE" '
|
||||
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
|
||||
{
|
||||
for(i=1;i<=nw;i++){
|
||||
if(index($0,words[i])>0){
|
||||
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
|
||||
print FILENAME":"FNR":"words[i]
|
||||
next
|
||||
}
|
||||
}
|
||||
}
|
||||
' 2>/dev/null
|
||||
)
|
||||
S11_HITS=0
|
||||
if [ -n "$S11_ALL_HITS" ]; then
|
||||
S11_HITS=$(echo "$S11_ALL_HITS" | grep -c .)
|
||||
fi
|
||||
|
||||
if [ "$S11_HITS" -gt 0 ]; then
|
||||
echo "[static_scan] S11 FAIL: 代码注释禁用词命中 $S11_HITS 处"
|
||||
echo "$S11_ALL_HITS" | head -50
|
||||
if [ "$S11_HITS" -gt 50 ]; then echo " ...(截断,共 $S11_HITS 处)"; fi
|
||||
fail=1
|
||||
else
|
||||
echo "[static_scan] S11 ok(扫描面 $S11_FC 个文件,词表 $S11_WC 词)"
|
||||
fi
|
||||
else
|
||||
fail=1
|
||||
fi
|
||||
|
||||
rm -f "$S11_WFILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── S12 — 效果引擎无状态化旧件零残留(卡② V10/F1;禁 counters_export/counters_seed/_reset_tree_limited_counters 等旧符号与带引号旧 id 字面回归)──
|
||||
# 扫描面:game/ 全域 .gd 与 game/data/tables/*.tres(改名与旧符号同时存在于数据行/测试/smoke,非仅 core——不扩面即空转)
|
||||
# 带引号旧 id 匹配("twin_digs" 不误伤 "adv_twin_digs"——引号锚定;round_instinct_atk 不匹 "instinct_atk")
|
||||
# 自检:扫描面文件数为零即判红(防静默空转)
|
||||
if [ -d game/core ]; then
|
||||
S12_SYM='counters_export|counters_seed|counters_merge_export|counters_seed_snapshot|_reset_tree_limited_counters|"tree_sense_sharp_used"|"tree_sense_guard_used"|"twin_digs"|"instinct_atk"'
|
||||
S12_FACE=$(find game/ -type f \( -name '*.gd' -o -name '*.tres' \) 2>/dev/null)
|
||||
S12_FC=$(echo "$S12_FACE" | grep -c .)
|
||||
if [ "$S12_FC" -eq 0 ]; then
|
||||
echo "[static_scan] S12 FAIL: 扫描面为空——路径写错导致的静默空转"
|
||||
fail=1
|
||||
else
|
||||
S12_HITS=$(echo "$S12_FACE" | tr '\n' '\0' | xargs -0 grep -lEn "$S12_SYM" 2>/dev/null | wc -l)
|
||||
if [ "$S12_HITS" -gt 0 ]; then
|
||||
echo "[static_scan] S12 FAIL: 无状态化旧件残留命中 $S12_HITS 文件"
|
||||
echo "$S12_FACE" | tr '\n' '\0' | xargs -0 grep -En "$S12_SYM" 2>/dev/null
|
||||
fail=1
|
||||
else
|
||||
echo "[static_scan] S12 ok(扫描面 $S12_FC 个 .gd/.tres,旧件零残留)〔无状态化 V10〕"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $fail
|
||||
@@ -26,12 +26,12 @@ func _init() -> void:
|
||||
"新效果类型在册:heal/ac_bonus/damage_reduce/bonus_delta/blast_scaled/preview_area/target_attack_bonus〔专长批 V1〕")
|
||||
Kit.check_quiet(EffectEngineScript.OPS.has("mod_eq"), "新算子在册:mod_eq〔专长批 V1〕")
|
||||
|
||||
# ② 合法新词行零违例(validate_trigger_row 正例)
|
||||
# ② 合法新词行零违例(validate_trigger_row 正例——前缀归属替代旧 domain 键)
|
||||
var errs: Array = []
|
||||
var ok_row := {"on": "dig_stop", "when": [{"fact": "ap_left", "op": "gte", "value": 3}],
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left", "domain": "adventure"}}
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left"}}
|
||||
var mine_covered: bool = EffectEngineScript.validate_trigger_row("V1正例", {}, [ok_row], errs)
|
||||
Kit.check_quiet(errs.is_empty() and not mine_covered, "合法新词行 validate 零违例(dig_stop/ap_left/counter_add+domain)〔专长批 V1〕")
|
||||
Kit.check_quiet(errs.is_empty() and not mine_covered, "合法新词行 validate 零违例(dig_stop/ap_left/counter_add——battle 桶无前缀)〔专长批 V1〕")
|
||||
errs = []
|
||||
EffectEngineScript.validate_trigger_row("V1正例2", {}, [{"on": "battle_won",
|
||||
"when": [{"fact": "hp_ratio", "op": "lte", "value": 1}],
|
||||
@@ -46,81 +46,85 @@ func _init() -> void:
|
||||
EffectEngineScript.validate_trigger_row("出域kind", {}, [{"on": "dig_stop", "do": {"kind": "nope_kind", "value": 1}}], errs)
|
||||
Kit.check_quiet(errs.size() == 4, "出域 on/fact/op/kind 各一行全部被拒(4 违例)〔专长批 V1〕")
|
||||
|
||||
# ④ 运行期命中序(合法新事件;dispatch 出域 fail-fast=debug assert〔effect_engine dispatch 首行
|
||||
# assert(EVENTS.has)——release 剥离系引擎语义,自动化覆盖走 ③ 加载期 validate 致命通道〕)
|
||||
# ④ 运行期命中序(合法新事件;dispatch 出域 fail-fast=debug assert;引擎无状态——计数器经 ctx)
|
||||
var fx = EffectEngineScript.new(null)
|
||||
var ctx_f := EffectContext.new()
|
||||
var hits: Array = []
|
||||
fx.rebuild([], [], [])
|
||||
fx._push_trigger("test", "v1_row", {"on": "dig_stop",
|
||||
"when": [{"fact": "ap_left", "op": "gte", "value": 3}],
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left"}}, {})
|
||||
hits = fx.dispatch("dig_stop", {"ap_left": 5})
|
||||
hits = fx.dispatch(ctx_f, "dig_stop", {"ap_left": 5})
|
||||
Kit.check_quiet(hits.size() == 1, "dig_stop 合法命中(ap_left 5≥3)〔专长批 V1〕")
|
||||
hits = fx.dispatch("dig_stop", {"ap_left": 1})
|
||||
hits = fx.dispatch(ctx_f, "dig_stop", {"ap_left": 1})
|
||||
Kit.check_quiet(hits.is_empty(), "dig_stop 反例不命中(ap_left 1<3)〔专长批 V1〕")
|
||||
Kit.check_quiet(not EffectEngineScript.EVENTS.has("dig_stop_x"),
|
||||
"词表封闭:出域事件词不在 EVENTS(dispatch 首行 assert 消费同一词表)〔专长批 V1〕")
|
||||
|
||||
# ⑤ mod_eq 算子(双管镐落法——预评估口径每第 2 次判定)
|
||||
# ⑤ mod_eq 算子(双管镐落法——预评估口径每第 2 次判定;battle 桶无前缀)
|
||||
var fx2 = EffectEngineScript.new(null)
|
||||
var ctx2 := EffectContext.new()
|
||||
fx2.rebuild([], [], [])
|
||||
fx2._push_trigger("test", "mod_row", {"on": "dig_paid",
|
||||
"when": [{"fact": "counter", "id": "dbl_digs", "op": "mod_eq", "value": 2, "ref": 1}],
|
||||
"do": {"kind": "grant_ap", "value": 1}}, {})
|
||||
var refunds := 0
|
||||
for i in 4:
|
||||
if not fx2.dispatch("dig_paid", {"ap_cost": 1}).is_empty():
|
||||
if not fx2.dispatch(ctx2, "dig_paid", {"ap_cost": 1}).is_empty():
|
||||
refunds += 1
|
||||
fx2.counter_add("dbl_digs", 1)
|
||||
EffectEngine.counter_add(ctx2, "dbl_digs", 1)
|
||||
Kit.check_quiet(refunds == 2, "mod_eq 预评估口径:4 次付费挖格恰返 2 次(第 2/4 次)〔专长批 V1〕")
|
||||
|
||||
# ⑥ adventure 域计数器 export/seed 往返(T6)
|
||||
# ⑥ adventure 桶共享引用跨引擎持久(T6 换代——替代旧 export/seed 副本搬运)
|
||||
var adv_bucket := {} # 同一冒险账本字典引用跨两引擎=跨战持久(无副本无搬运时点)
|
||||
var fx3 = EffectEngineScript.new(null)
|
||||
fx3._push_trigger("test", "adv_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "pain_layers", "value": 1, "domain": "adventure"}}, {})
|
||||
fx3._push_trigger("test", "adv_row2", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1, "reset_at": "round_start"}}, {})
|
||||
"do": {"kind": "counter_add", "counter_id": "adv_persist", "value": 1}}, {})
|
||||
var ctx3 := EffectContext.new()
|
||||
ctx3.adventure_bucket = adv_bucket
|
||||
for _i in 2:
|
||||
for h in fx3.dispatch("mine_hit", {}): # 施加面同构(编排侧 counter_add 逐条落账)
|
||||
for h in fx3.dispatch(ctx3, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx3.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
var exported: Dictionary = fx3.counters_export()
|
||||
Kit.check_quiet(exported.get("pain_layers", -1) == 2 and not exported.has("round_mines"),
|
||||
"export 只含 adventure 域(pain_layers=2、round_mines battle 域不入)〔专长批 V1〕")
|
||||
EffectEngine.counter_add(ctx3, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(EffectEngine.counter(ctx3, "adv_persist") == 2 and int(adv_bucket.get("adv_persist", -1)) == 2,
|
||||
"adventure 桶就地直写共享字典累积=2(adv_ 前缀归 adventure 桶——替代旧导出搬运)〔专长批 V1〕")
|
||||
var fx4 = EffectEngineScript.new(null)
|
||||
fx4._push_trigger("test", "adv_row_seed", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "pain_layers", "value": 1, "domain": "adventure"}}, {})
|
||||
fx4.counters_seed({"pain_layers": 7})
|
||||
Kit.check_quiet(fx4.counter("pain_layers") == 7, "seed 回灌:counters_seed 后 adventure 域计数器=7〔专长批 V1〕")
|
||||
fx4._push_trigger("test", "adv_row2", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "adv_persist", "value": 1}}, {})
|
||||
var ctx4 := EffectContext.new()
|
||||
ctx4.adventure_bucket = adv_bucket # 挂同一字典引用=下一战可读上一战持久值(替代旧回灌)
|
||||
Kit.check_quiet(EffectEngine.counter(ctx4, "adv_persist") == 2,
|
||||
"adventure 桶共享引用跨引擎可读(无 export/seed,同字典即跨战持久)〔专长批 V1〕")
|
||||
|
||||
# ⑦ V4——攒层/计数类件跨事件计数(置计数→事件路径→清零点:round_start 重置/用后清零)
|
||||
# ⑦ V4——前缀归桶清零/消耗语义(round_ 桶随 round_start 整桶清空;battle 无前缀计数跨 round_start 存活)
|
||||
var fx5 = EffectEngineScript.new(null)
|
||||
var ctx5 := EffectContext.new()
|
||||
fx5.rebuild([], [], [])
|
||||
fx5._push_trigger("test", "round_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1}}, {}) # 未声明 reset_at——round_start 不重置(声明制对照面)
|
||||
fx5._push_trigger("test", "spend_row", {"on": "treasure_used",
|
||||
"do": {"kind": "counter_spend", "counter_id": "round_mines", "value": 1}}, {})
|
||||
for h in fx5.dispatch("mine_hit", {}):
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1}}, {})
|
||||
for h in fx5.dispatch(ctx5, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx5.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
for h in fx5.dispatch("mine_hit", {}):
|
||||
EffectEngine.counter_add(ctx5, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
for h in fx5.dispatch(ctx5, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx5.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(fx5.counter("round_mines") == 2, "V4:跨事件计数累积(两雷两计)〔专长批 V4〕")
|
||||
Kit.check_quiet(fx5.counter_spend("round_mines", 1) and fx5.counter("round_mines") == 1,
|
||||
"V4:counter_spend 写口消耗(用后清零语义承载面)〔专长批 V4〕")
|
||||
fx5.dispatch("round_start", {"round": 2})
|
||||
Kit.check_quiet(fx5.counter("round_mines") == 1,
|
||||
"V4:非 reset 点计数器不受 round_start 影响(reset_at 声明制——round_mines 未声明重置)〔专长批 V4〕")
|
||||
EffectEngine.counter_add(ctx5, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 2, "V4:跨事件计数累积(两雷两计——round_ 前缀落 round 桶)〔专长批 V4〕")
|
||||
Kit.check_quiet(EffectEngine.counter_spend(ctx5, "round_mines", 1) and EffectEngine.counter(ctx5, "round_mines") == 1,
|
||||
"V4:counter_spend 静态写口消耗(用后清零语义承载面)〔专长批 V4〕")
|
||||
fx5.dispatch(ctx5, "round_start", {"round": 2})
|
||||
Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 0,
|
||||
"V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔无状态化 V8〕")
|
||||
# battle 桶对照:无前缀 helm_layers 不受 round_start 影响(跨 round_start 存活)
|
||||
var fx6 = EffectEngineScript.new(null)
|
||||
var ctx6 := EffectContext.new()
|
||||
fx6.rebuild([], [], [])
|
||||
fx6._push_trigger("test", "reset_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "helm_layers", "value": 1, "reset_at": "round_start"}}, {})
|
||||
for h in fx6.dispatch("mine_hit", {}):
|
||||
fx6._push_trigger("test", "battle_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "helm_layers", "value": 1}}, {})
|
||||
for h in fx6.dispatch(ctx6, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx6.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
fx6.dispatch("round_start", {"round": 2})
|
||||
Kit.check_quiet(fx6.counter("helm_layers") == 0,
|
||||
"V4:round_start 重置点清零(订阅行声明 reset_at——安全帽攒层清零语义)〔专长批 V4〕")
|
||||
EffectEngine.counter_add(ctx6, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
fx6.dispatch(ctx6, "round_start", {"round": 2})
|
||||
Kit.check_quiet(EffectEngine.counter(ctx6, "helm_layers") == 1,
|
||||
"V4:battle 桶(无前缀)计数器不受 round_start 清空(round 桶整桶清空仅触及 round_ 前缀键)〔专长批 V4〕")
|
||||
|
||||
quit(Kit.finish("effect_words_t1"))
|
||||
@@ -26,6 +26,9 @@ func _init() -> void:
|
||||
Kit.check_quiet(false, "V9 前置:冒险启动")
|
||||
quit(Kit.finish("feat_vouchers"))
|
||||
return
|
||||
# V2——adventure 桶新冒险置空:start_adventure 后宿主字典为空(寿命随 RunLedger,无残留)
|
||||
Kit.check_quiet(a.run_ledger.adventure_counters.is_empty(),
|
||||
"V2:新冒险 adventure 桶置空(start_adventure 后 run_ledger.adventure_counters 为空)〔无状态化 V2〕")
|
||||
|
||||
# V9-1 刷新券:入存量账(冒险级)——反例=写进 _attune_refresh_x(X 每停点重置会吞存量)
|
||||
a._reset_refresh_x() # X 归零基线(模拟新停点)
|
||||
@@ -111,7 +114,7 @@ func _init() -> void:
|
||||
# V16——断点扩容往返:新账本随断点快照/续玩回填(breakpoint_resume_test P5 同构)
|
||||
var sm = load("res://core/save_manager.gd").new("user://_feat_vouchers_bp.json")
|
||||
sm.load_from_disk()
|
||||
a.run_ledger.adventure_counters = {"pain_layers": 3, "twin_digs": 5} # 四账住 RunLedger
|
||||
a.run_ledger.adventure_counters = {"pain_layers": 3, "adv_twin_digs": 5} # 四账住 RunLedger
|
||||
a._refresh_stock = 2
|
||||
a.set_save_manager(sm)
|
||||
a._on_level_finished({"result": "victory", "hp": 10, "node_index": 1, "node_id": 1, "node_type": "shop"})
|
||||
@@ -125,8 +128,18 @@ func _init() -> void:
|
||||
bp_legacy["battery_pending"] = 7 # 旧断点档遗留键(Alpha 无迁移——缺键/多键兼容面)
|
||||
var b3 = load("res://core/adventure_runner.gd").new(db, load("res://core/rng_service.gd").new())
|
||||
Kit.check_quiet(b3.resume_adventure(bp_legacy, {}), "V16:旧档含 battery_pending 键续玩受理(缺键/多键零报错)〔卡② R1〕")
|
||||
# V13/H1 无状态化旧档兼容:改名前的旧 counter 键(tree_sense_sharp_used / pain_layers 等)随断点带进来不得崩,留桶成死键
|
||||
var bp_legacy2: Dictionary = bp16.duplicate(true)
|
||||
var legacy_ac: Dictionary = (bp_legacy2.get("adventure_counters", {}) as Dictionary).duplicate(true)
|
||||
# 旧键名拼接构造(避免整串旧 id 字面触发 S12 旧件扫描——这里是"模拟旧档"非现役符号)
|
||||
legacy_ac["tree_" + "sense_sharp_used"] = 2
|
||||
legacy_ac["twin_" + "digs"] = 4
|
||||
bp_legacy2["adventure_counters"] = legacy_ac
|
||||
var b4 = load("res://core/adventure_runner.gd").new(db, load("res://core/rng_service.gd").new())
|
||||
Kit.check_quiet(b4.resume_adventure(bp_legacy2, {}),
|
||||
"V13:旧档 adventure_counters 含改名前旧键续玩受理零崩溃(无迁移——旧键留桶成死键)〔卡② V13/H1〕")
|
||||
Kit.check_quiet(int(b2.run_ledger.adventure_counters.get("pain_layers", -1)) == 3 and b2.refresh_stock() == 2,
|
||||
"V16:续玩回填逐项一致(计数恢复——跨战账不丢)〔专长批 V16〕")
|
||||
"V16:续玩回填逐项一致(adventure 桶随断点往返·计数恢复)〔无状态化 V3〕")
|
||||
# V25 完美保险专长验收组(R2 全项:装载/限次一次/计数纯净/两账相减/跨战清零/事件不吞)
|
||||
var fi: Dictionary = db.feats.row_by_id("perfect_insurance")
|
||||
Kit.check_quiet(not fi.is_empty() and String(fi.get("rarity", "")) == "rare"
|
||||
@@ -168,10 +181,10 @@ func _init() -> void:
|
||||
orch._perfect_hit_false = 1
|
||||
orch._perfect_hit_true = 1
|
||||
Kit.check_quiet(orch.perfect_sweep_earned(),
|
||||
"判定相减:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立〔卡② R2 真值表〕")
|
||||
"判定相减:误标1−豁免1=有效0且标中≥1→完美成立(保险每战限次一次迁移保绿)〔无状态化 V7〕")
|
||||
orch._perfect_hit_true = 0
|
||||
Kit.check_quiet(not orch.perfect_sweep_earned(),
|
||||
"判定守卫:标中数 0 时零误标也不成立(板级 ≥1 标中条件零松动)〔卡② R2 真值表〕")
|
||||
"判定守卫:标中数 0 时零误标也不成立(整图 ≥1 标中条件零松动)〔卡② R2 真值表〕")
|
||||
var rig_rng2 = RngScript.new()
|
||||
rig_rng2.set_master_seed(778002)
|
||||
var orch2 = RigScript.new(db, rig_rng2)
|
||||
@@ -187,6 +200,77 @@ func _init() -> void:
|
||||
"跨战清零:domain=battle 豁免账每战重建——次战首次标错再豁免一次〔卡② R2〕")
|
||||
Kit.check_quiet(EffectEngineKindProbe(db), "词表闭集:forgive_seal_miss 在 EFFECT_KINDS(出域即加载致命——扩签合规)〔卡② R4〕")
|
||||
|
||||
# ── N7 归因锚两态验收组(卡① T7:seal_miss 行 highlight 改用有效误标判据)──
|
||||
# A10 未豁免态:无 perfect_insurance,首次标错 → seal_miss 行 highlight=="perfect_broken" 且完美不成立
|
||||
var rows10: Array[Dictionary] = []
|
||||
var rng10 = RngScript.new()
|
||||
rng10.set_master_seed(779010)
|
||||
var o10 = RigScript.new(db, rng10)
|
||||
o10.step_row.connect(func(row): rows10.append(row))
|
||||
Kit.check_quiet(o10.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
|
||||
"feats": [], "tree_rules": [], "battle_index": 1,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
|
||||
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}),
|
||||
"A10 rig:无保险战斗启动〔卡① N7〕")
|
||||
var sc10: Variant = null
|
||||
for c10 in o10.minesweeper.current_board().all_cells():
|
||||
if not o10.minesweeper.current_board().is_mine(c10):
|
||||
sc10 = c10
|
||||
break
|
||||
o10._grant_seg._judge_seal({"cell": sc10, "hit": false}, 1)
|
||||
var sm10: Dictionary = {}
|
||||
for r10 in rows10:
|
||||
if String(r10.get("type", "")) == "seal_miss":
|
||||
sm10 = r10
|
||||
Kit.check_quiet(String(sm10.get("highlight", "")) == "perfect_broken",
|
||||
"A10 未豁免:首次标错 seal_miss 行 highlight==perfect_broken〔卡① N7〕")
|
||||
Kit.check_quiet(not o10.perfect_sweep_earned(),
|
||||
"A10 真值:无保险首标 → 有效误标 1 → 完美不成立(与 highlight 一致)〔卡① N7〕")
|
||||
|
||||
# A11 豁免态:有 perfect_insurance,首次标错 → seal_miss 行 highlight=="" 且完美仍成立
|
||||
var rows11: Array[Dictionary] = []
|
||||
var rng11 = RngScript.new()
|
||||
rng11.set_master_seed(779011)
|
||||
var o11 = RigScript.new(db, rng11)
|
||||
o11.step_row.connect(func(row): rows11.append(row))
|
||||
Kit.check_quiet(o11.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
|
||||
"feats": ["perfect_insurance"], "tree_rules": [], "battle_index": 1,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
|
||||
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}),
|
||||
"A11 rig:带保险战斗启动〔卡① N7〕")
|
||||
var sc11: Variant = null
|
||||
for c11 in o11.minesweeper.current_board().all_cells():
|
||||
if not o11.minesweeper.current_board().is_mine(c11):
|
||||
sc11 = c11
|
||||
break
|
||||
o11._grant_seg._judge_seal({"cell": sc11, "hit": false}, 1)
|
||||
var sm11: Dictionary = {}
|
||||
for r11 in rows11:
|
||||
if String(r11.get("type", "")) == "seal_miss":
|
||||
sm11 = r11
|
||||
Kit.check_quiet(String(sm11.get("highlight", "")) == "",
|
||||
"A11 豁免:首次标错获豁免 → seal_miss 行 highlight==空串〔卡① N7〕")
|
||||
o11._perfect_hit_true = 1
|
||||
Kit.check_quiet(o11.perfect_sweep_earned(),
|
||||
"A11 真值:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立(与 highlight==空 一致)〔卡① N7〕")
|
||||
|
||||
# A12 呈现两态文案与 _format_row 一致:两态均零「雷」字面
|
||||
var LogViewScript := load("res://presentation/views/battle_log_view.gd")
|
||||
var lv = LogViewScript.new(db)
|
||||
var broken_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
|
||||
"round_index": 1, "display_name": "封印未中", "value": 0, "highlight": "perfect_broken"}
|
||||
var forgiven_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
|
||||
"round_index": 1, "display_name": "封印未中", "value": 0}
|
||||
var s_broken := String(lv._format_row(broken_row))
|
||||
var s_forgiven := String(lv._format_row(forgiven_row))
|
||||
Kit.check_quiet(s_broken.find("完美封印已破") >= 0 and s_broken.find("雷") == -1,
|
||||
"A12 已破态:_format_row 输出「完美封印已破」且零「雷」字面〔卡① N7〕")
|
||||
Kit.check_quiet(s_forgiven.find("已豁免") >= 0 and s_forgiven.find("雷") == -1,
|
||||
"A12 豁免态:_format_row 输出「已豁免」且零「雷」字面〔卡① N7〕")
|
||||
lv.free()
|
||||
|
||||
sm.clear_breakpoint()
|
||||
|
||||
quit(Kit.finish("feat_vouchers"))
|
||||
@@ -95,7 +95,7 @@ func check_v7(a, db) -> void:
|
||||
if String(t.get("tier", "")) != "common":
|
||||
ok_fb = false
|
||||
Kit.check_quiet(ok_fb, "子池空回退全池:全普通池候选恒 3 格普通档〔专长批 V7〕")
|
||||
# 构成③:凭证不进子池也不进全池视图(独立计数体系——只经门票注入出现)
|
||||
# 构成③:凭证不进子池也不进全池视图(独立计数体系——只经专长凭证注入出现)
|
||||
a._curio_deck = [_tok("gp", "common"), _tok("attack", "common"), _tok("max_hp", "common"),
|
||||
_tok("feat_ticket", "excellent")]
|
||||
var ticket_seen := false
|
||||
|
||||
@@ -29,8 +29,8 @@ func _init() -> void:
|
||||
Kit.check_quiet(oc._combat_seg != null and oc._combat_seg.oc == oc, "V4 编排器接线·战斗段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._decay_seg != null and oc._decay_seg.oc == oc, "V4 编排器接线·衰减段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._verdict_seg != null and oc._verdict_seg.oc == oc, "V4 编排器接线·判定段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._grant_seg._apply_counter_hit({"counter_id": "dig_combo", "value": 1}) == null
|
||||
and oc._fx.counter("dig_combo") == 1, "V4 counter_add 助手单点(原五位三行组收拢后行为等价)〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._grant_seg._apply_counter_hit({"counter_id": "geo_types", "value": 1}) == null
|
||||
and EffectEngine.counter(oc.counters_ctx, "geo_types") == 1, "V4 counter_add 助手单点(原五位三行组收拢后行为等价——写口经 ctx 落 battle 桶)〔迭代27 T2〕")
|
||||
|
||||
# V4-3 真实一轮结算走段序(begin_battle→停手→_maybe_settle→五段→裁定行)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
|
||||
@@ -31,7 +31,7 @@ func _init() -> void:
|
||||
var ap_with := int(ob1.action_economy._ap) # 测试夹具直读(呈现读数走 presentation_snapshot——本断言取账本真值)
|
||||
Kit.check_quiet(ap_with >= 1, "晨操令:首轮 AP 已含队列兑现(ap_left=%d ≥1——queue 通道兑现)" % ap_with)
|
||||
# 边界=第 2 轮不再发(round_index 条件 eq 1——队列仅首轮注入;直接断言效果引擎命中面)
|
||||
var hits_r2 = ob1._fx.dispatch("round_start", {"round_index": 2})
|
||||
var hits_r2 = ob1._fx.dispatch(ob1.counters_ctx, "round_start", {"round_index": 2})
|
||||
var r2_ap := 0
|
||||
for h in hits_r2:
|
||||
if String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
|
||||
@@ -42,7 +42,7 @@ func _init() -> void:
|
||||
var ob1b = orch_s.new(db, rng1b)
|
||||
Kit.check_quiet(ob1b.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.hero.max_hp)}) and ob1b.attune_param_sum("light_radius_bonus") == 0,
|
||||
"空装配基线复用:未装配件参数读数 0")
|
||||
var hits_none = ob1b._fx.dispatch("round_start", {"round_index": 1})
|
||||
var hits_none = ob1b._fx.dispatch(ob1b.counters_ctx, "round_start", {"round_index": 1})
|
||||
Kit.check_quiet(_ap_of(hits_none) == 0, "晨操令反例:未装配首轮零命中(订阅行随装配集装载)")
|
||||
|
||||
# ② 双管装填(前 2 轮各 +1 / 第 3 轮零)
|
||||
@@ -50,10 +50,10 @@ func _init() -> void:
|
||||
var ob2 = orch_s.new(db, rng2)
|
||||
Kit.check_quiet(ob2.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.hero.max_hp), "attunement": ["twin_loader"]}),
|
||||
"双管装填:装配起战")
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch("round_start", {"round_index": 1})) == 1
|
||||
and _ap_of(ob2._fx.dispatch("round_start", {"round_index": 2})) == 1,
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 1})) == 1
|
||||
and _ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 2})) == 1,
|
||||
"双管装填:第 1/2 轮各命中 +1(lte 2 边界内)")
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch("round_start", {"round_index": 3})) == 0,
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 3})) == 0,
|
||||
"双管装填反例:第 3 轮零命中;战斗外零发放(round_start 事件域外无触发面)")
|
||||
|
||||
# ③ 阔视野带(参数读数=4 / 未装配=0——注入面消费在呈现桥 T4)
|
||||
|
||||
@@ -26,17 +26,17 @@ func _init() -> void:
|
||||
{"fact": "round_index", "op": "eq", "value": 1}],
|
||||
"do": {"kind": "shield", "value": 1}}],
|
||||
"sense_sharp": [{"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "damage_mult", "value": 50}},
|
||||
{"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_sharp_used", "value": 1, "domain": "adventure"}}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_sharp_used", "value": 1}}],
|
||||
"sense_guard": [{"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "grant_ap", "mode": "queue", "value": 1}},
|
||||
{"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_guard_used", "value": 1, "domain": "adventure"}}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_guard_used", "value": 1}}],
|
||||
}
|
||||
var six := ["posture_first_strike", "posture_bulwark", "sense_sharp", "sense_guard", "economy_hoard", "economy_spend"]
|
||||
# rebuild 实装面(db=null→feat/attune 两段空转,_triggers 只含树规则合成行)——行数对照载体
|
||||
|
||||
@@ -130,7 +130,7 @@ func _rows_contain(pool: Array, row: Dictionary) -> bool:
|
||||
func _test_shop_accessors(db) -> void:
|
||||
var products: Array = db.shop_products()
|
||||
if products.is_empty():
|
||||
Kit.check("ShopTable products 行族非空(表已落籍前提)", false)
|
||||
Kit.check("ShopTable products 行族非空(表已已入库前提)", false)
|
||||
return
|
||||
Kit.check("ShopTable products 行族非空", true)
|
||||
var first: Dictionary = products[0]
|
||||
|
||||
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user