Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c19e2301b | ||
|
|
88d1344def | ||
|
|
eff87256b6 | ||
|
|
261ff5b615 | ||
|
|
0657370be9 | ||
|
|
abf3d51636 | ||
|
|
240448be7f | ||
|
|
f0f5d8b437 | ||
|
|
c94c2108e3 | ||
|
|
f4902bc746 | ||
|
|
bfea147581 | ||
|
|
8e294de976 | ||
|
|
91f91744cd | ||
|
|
c3206f86b2 | ||
|
|
f33f9c1cfe | ||
|
|
fc30e984c6 | ||
|
|
25b2a0bd33 | ||
|
|
2b7db78e8f | ||
|
|
82b2cbeaff | ||
|
|
4f52e288b1 | ||
|
|
a93e6c0005 | ||
|
|
c538536265 | ||
|
|
dda2238f32 | ||
|
|
65b2a57449 | ||
|
|
399ed3ad53 | ||
|
|
2ce70ac708 | ||
|
|
469d856f1c | ||
|
|
a9c006e7d1 | ||
|
|
5ad72f44a0 | ||
|
|
c585a6bd73 | ||
|
|
c009cf7b1e | ||
|
|
b991f52ac2 | ||
|
|
cd121b18c2 | ||
|
|
83a4fbf5c5 | ||
|
|
70d071f850 | ||
|
|
8be5d425ca | ||
|
|
4554e0e702 | ||
|
|
b75688c6ca | ||
|
|
ab5a45af00 | ||
|
|
afee92476d | ||
|
|
b5433285b4 | ||
|
|
546ed1d611 | ||
|
|
37ed6ab426 | ||
|
|
5269ed4080 | ||
|
|
5ecfabc251 | ||
|
|
57084e0bd3 | ||
|
|
12af4fe00c | ||
|
|
22809f9b79 | ||
|
|
94dfa7e79b | ||
|
|
7fd50fc485 | ||
|
|
e7090bc9c1 | ||
|
|
3a8634ab92 | ||
|
|
92e9c70a8d | ||
|
|
4a2ed96182 | ||
|
|
33525d19de | ||
|
|
3499638dee | ||
|
|
e075f92e0f | ||
|
|
34bb62a55b | ||
|
|
ceb816cd39 | ||
|
|
94628fca83 | ||
|
|
870b2f2bf6 | ||
|
|
3a1878ff80 | ||
|
|
d216ae0498 | ||
|
|
e090ea8af2 | ||
|
|
263050adf1 | ||
|
|
eb5dc1e7cb | ||
|
|
aba54ab228 | ||
|
|
3fa99583fb |
No files matched your search
@@ -16,7 +16,7 @@
|
||||
|
||||
## 运行与验证(工作目录 = 含 project.godot 的切片目录)
|
||||
|
||||
- **合回验证 = 本地 CI(2026-10-30 用户裁)**:git merge 合回 master 落地后,post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh` 跑一次全量域闸(调 `tools/run_all_gates.sh` 不带参数档,02 全套件),结果落 `game/tests/.gates/ci-status.json`(commit/绿红/红闸名单+关键日志行/耗时)+`ci-history.jsonl` 流水账;CI 红态下每个修复提交经 post-commit 钩子(`tools/hooks/post-commit`)自动追绿复验,绿态与无状态零触发——主区小批直提不重复跑全量,同一份字节禁止第二次人手跑。CI 红不回滚合并,红项在案、修复提交追绿;开工先读 CI 状态,红未修不开新批。跑批起手自动 `--editor --quit` 刷类名缓存(.godot 缓存不入 git,防合并后失配假红)。`tools/run_all_gates.sh` 仍是全部验证闸的**唯一入口与唯一清单,域划分与选闸算法只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单;`--code`/`--docs`/`--quick` 手动档只服务开发期按域跑批(开发期窄测纪律见 process-conventions §6),不构成验收;全量回归由用户自发发起
|
||||
- **合回验证 = 本地 CI(2026-10-30 用户裁)**:git merge 合回 master 落地后,post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh` 跑一次全量域闸(调 `tools/run_all_gates.sh` 不带参数档,02 全套件),结果落 `game/tests/.gates/ci-status.json`(commit/绿红/红闸名单+关键日志行/耗时)+`ci-history.jsonl` 流水账;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(红态修复走新开修复 worktree,`fix-<任务名>` 分支,免重塑免数卡;post-commit 钩子保留为红态主区直提兜底触发,绿态与无状态零触发)——批内提交零钩子检查、合回只验一次,同一份字节禁止第二次人手跑。CI 红不回滚合并,红项在案、修复合回追绿;开工先读 CI 状态,红未修不开新批。跑批起手自动 `--editor --quit` 刷类名缓存(.godot 缓存不入 git,防合并后失配假红)。`tools/run_all_gates.sh` 仍是全部验证闸的**唯一入口与唯一清单,域划分与选闸算法只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单;`--code`/`--docs`/`--quick` 手动档只服务开发期按域跑批(开发期窄测纪律见 process-conventions §6),不构成验收;全量回归由用户自发发起
|
||||
- 单件直跑(调试期最窄确认用,**不构成验收**):冒烟测试 `python tools/run_smoke.py`(`game/tests/smoke_boot.tscn` 极小启动场景)· 系统测试 `python tools/run_system_test.py`(宿主 `game/tests/system_test.gd`)· 场景探针 `…console.exe --headless --path . res://probe/ui_probe.tscn` · 单元与集成 `… --script res://tests/run_all.gd`
|
||||
- 调参批量:`--script res://sim/adventure_monte_carlo.gd`(全冒险期望)· `res://sim/reward_expectation.gd`(奖励期望);关卡完成率/难度类测量一律走全冒险
|
||||
- 数值验证:`python tools/balance_verifier.py`(采集端 `res://sim/verifier_batch.gd`,报告与原始数据落 `sim-data/`)
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
| 时机 | 权威文档 |
|
||||
|---|---|
|
||||
| 批次开工 / 流程纪律 | `design/engineering/process-conventions.md`——车道铁律(主区直改+直提;大批约定走 worktree)、合回后本地 CI、五段流水线、批次清单、测试两入口与 MC/闸机细则、像素纪律 |
|
||||
| 批次开工 / 流程纪律 | `design/engineering/process-conventions.md`——车道铁律(一切改动走独立 worktree,主区只作合并落点)、合回后本地 CI、五段流水线、批次清单、测试两入口与 MC/闸机细则、像素纪律 |
|
||||
| 流水线段入口 | 技能对照——doc-design=文档设计(兼任反哺点落文)/asset-ingest=美术录入/req-card=写卡/iter-plan=迭代编排/iter=实现(五者均装于项目 `.zcode/skills/`);登记不立技能;批后收尾与中间文档退役=batch-closure(同一目录);格式与状态机权威=`design/engineering/doc-conventions.md` §12 |
|
||||
| 定代码方案 / 写代码 / 改代码前 | `design/engineering/code-conventions.md`——结构/契约/语言/数值/流程五类硬纪律(反例库 = 同目录 code-conventions-cases.md) |
|
||||
| 定测试方案 / 写测试 / 改测试前 | `design/engineering/test-conventions.md`——探针标配 / 回归断言 / 提交前验证 / 验收清单落码 |
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
**判据**:描述含多个实体、实体间有明确关系(先后顺序、包含、依赖、状态转移、布局对比)的内容时,直接画图说明,不写长段文字;单点事实(一句装得下)不画,纯数据对照用表格。
|
||||
|
||||
**载体**:默认用 visualize 技能出 HTML/SVG(会话内联渲染,表现力优先),流程/状态/结构/实体关系与空间对比、像素级示意、界面草图同一通道;查看端不支持内联渲染时退回 Mermaid 代码块,仍不行用字符画。
|
||||
**载体**:用 diagram-design 技能出 HTML/SVG 内联交付(流程/状态/结构/实体关系与空间对比、像素级示意、界面草图同一通道),大图精修与导出用 archify 技能;查看端不支持内联渲染时用字符画。
|
||||
|
||||
**边界**:硬数值与前提条件留在文字里,图不承载唯一数值依据;图与文禁重复说同一件事,文字只留图说不清的部分。
|
||||
|
||||
@@ -93,12 +93,12 @@
|
||||
|
||||
## 硬性红线(勿违反;操作细则在 process-conventions.md 与各规约)
|
||||
|
||||
- 车道铁律(判据与改动域划分同一,按文件性质切;2026-10-30 用户裁本地 CI 化)——一切面(含代码面 `game/` 下非 `.md` 的行为载体与 `.zcode/`、`.qoder/` 等不入 git 面)主工作区直改+直提;commit 点由 git pre-commit 钩子(`tools/hooks/pre-commit`,`core.hooksPath` 挂载)现场跑文档域闸,绿落地红拦,合并收尾提交跳过(归合回后 CI);代码面全量验证归合回后本地 CI(`tools/hooks/post-merge` 触发,见「运行与验证」首条);大批次(迭代实现批等)按约定仍走独立 worktree(冻结→件核→并 master→重塑→主区合回→CI 验证),约定非强制;`--no-verify` 仅属用户,agent 禁用(细则见 process-conventions §1)
|
||||
- 车道铁律(2026-10-31 用户裁恢复)——主工作区只作合并落点,一切开发/设计改动走独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`(建区即跑 `tools/setup_worktree_userdir.sh`),收口=冻结→件核→并 master→重塑→主区 `git merge --no-ff`→删区(合回即删,2026-09-19 原裁),CI 红不回滚合并——新开修复 worktree(`fix-<任务名>` 分支,免重塑免数卡)修复合回追绿;主区唯一合法写动作=合并,agent 侧由 worktree-guard 拦主区 Edit/Write 与非 merge 提交;豁免面仅 `.zcode/`、`.qoder/`(不入 git,主区直改);commit 点零钩子检查(pre-commit 直改闸已退役),全量验证归合回后本地 CI(`tools/hooks/post-merge` 触发,见「运行与验证」首条);`--no-verify` 仅属用户,agent 禁用(细则见 process-conventions §1)
|
||||
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
|
||||
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
|
||||
- 合回后验证 = 本地 CI 全量域闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。文档面直改批验收=commit 点 git 钩子自动跑 `--docs`(process-conventions §1)
|
||||
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复提交追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令在主工作区直改+直提(commit 点 git 钩子自动跑文档域闸);大体量改动(含触及代码面的工程改动如纯测试维护)按约定走 worktree 批(非强制);文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
|
||||
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸(车道细则=process-conventions §1)
|
||||
- 合回后验证 = 本地 CI 全量域闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。文档面批与代码面批验收同走合回 CI 全量(`--docs` 仅属开发期手动档,process-conventions §1)
|
||||
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复合回追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令走独立 worktree 直改+合回(车道铁律,commit 点零钩子检查);大体量改动(含触及代码面的工程改动如纯测试维护)同车道无特殊通道;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
|
||||
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI(车道细则=process-conventions §1)
|
||||
- MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起
|
||||
- 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6)
|
||||
- **验收闭合**——每个需求点与每个行为改动都要有从真实入口进入的验收断言,可 grep 定位、在当轮真实跑批中执行并通过,判据绑被测改动特有的可观测提交面;**禁变异类测试与撤销类取证**(2026-10-24/2026-10-28 用户裁——人为制造红属伪造证据),证明做不出=停卡呈报;禁删断言/放宽阈值换绿;合回前独立验收审计未过则禁止合并(细则=test-conventions §8+iter 技能第 5 步)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
**通用规格纪律**:
|
||||
|
||||
- 交付格式 PNG 透明底;动画用序列帧或精灵表(SpriteFrames),帧数规格随样张定(候选档已成文 art-bible §8.4)。命名规范候选草案见 art-bible §8.3。
|
||||
- **分辨率档位已定案:1280×800 + 1920×1080 两档**,窗口不可调、格边长按档位计算,后续调整走修订;像素画布基准见 **art-bible §8.2**(主角人设原图 48×72〔人设侧画布〕;站桩立绘/战斗状态图源高 48;棋盘小人 32×32 二头身;敌种逐键源档 36–94;整数倍缩放纪律;终值归样张冻结,待办清单已登记)。
|
||||
- **分辨率档位已定案:1280×800 + 1920×1080 两档**,窗口不可调、格边长按档位计算,后续调整走修订;像素画布基准见 **art-bible §8.2**(棋盘小人 32×32 二头身独立源档;站桩立绘=原稿分辨率入包、显示按三档绝对高 96/144/188 缩放,定值单点与逐件归档见 §8.2 站桩立绘显示档条)。
|
||||
- 程序化占位是现行结构冻结条款:资产未就位的槽位保持占位渲染,不空窗。
|
||||
- **复用闸(♻ 渠道四道验收,一道不过不入包)**:① 色板量化——改色对齐 art-bible §4.1 token,不保留原包杂色;② 吸色检查——无 HSV 色相 300°–345°(紫赤禁入带);③ 灰度/色弱可辨——去色相后仍可辨且不与语义件混淆;④ 授权干净——CC0 优先,CC-BY 署名记入设置/关于页,license 不明的一律不用。
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
|
||||
## 2. P1 —— 角色级(样张通过后开工,ComfyUI)
|
||||
|
||||
> 提示词规格卡 art-prompts.md 现行为 **v2.0**(主角三态+八敌种,逐资产自包含完整提示词,整段可直贴)。
|
||||
> 提示词规格卡 art-prompts.md 现行为 **v2.13**(主角三态+八敌种逐卡提示词仍在册;站桩立绘走 §5.4 按档派生、不重画)。
|
||||
|
||||
### 2.1 主角战斗小人
|
||||
|
||||
- **要什么**:待机 / 攻击 / 受击 三态起步(移动态样张后定);透明底 PNG。
|
||||
- **风格/规格**:小人原型——**窄、直立、对称,轮廓凹凸 ≤2**;2~3 头身两档下剪影均成立;主色小人蓝 `#2E86DE`(候选值,样张冻结终稿);格内占位最窄、不吞数字位。
|
||||
- **来源**:✦ ComfyUI(prompt 卡 v2.0 §2);剪影原型是设计载荷(宽≥高/凹凸计数/甲档/紫赤独占),素材站无对的,不复用。
|
||||
- **现状**:站姿样张已有先例(源文件已清理);战斗三态未出。
|
||||
- **现状**:战斗三态在役(迭代 6 T3 落位);2026-11-01 显示档改中档 144,按档派生落位归立绘显示档实装批。
|
||||
|
||||
### 2.2 八敌种形象(键集事实源 = `data/tables/enemy.tres` 行键)
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
| 钩爪恐魔 hook_horror | 普通怪(钩爪/多足轮廓注意凹凸计数控制) | light | 第三关群行捕食者,小体量可辨 |
|
||||
| 灰矮人石君王 duergar_king | **Boss:锐角与不对称**,体量最大 | heavy | 第三关 Boss;紫赤色相独占+形状独占双通道 |
|
||||
|
||||
- **通用规格**:亮色描边+落影底盘轮廓通道硬规则;透明底 PNG;每敌种 2–3 版候选;护甲类型视觉区分方案已落 art-bible §5.3(已定稿)。
|
||||
- **通用规格**:亮色描边+落影底盘轮廓通道硬规则;透明底 PNG;显示档三档绝对高 96/144/188、源=原稿不降采样(art-bible §8.2 v3.32);护甲类型视觉区分方案已落 art-bible §5.3(已定稿)。
|
||||
- **来源**:✦ ComfyUI(prompt 卡 v2.0 §3,八敌种+L2 熊地精变体共 9 卡);理由同 §2.1——不复用。
|
||||
- **联动**:显示名以 `data/tables/enemy.tres` 与敌人系统文档为准(L2 熊地精=bugbear 复用,显示名熊地精系)。
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
| 站桩石板地台件 `scene_stage_plaza.png` | 近景地台独立件(art-bible §6.1/§8.2) | 源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定,不吃 `wall_ambient` tint;连续地面层在其下方,先保留 | ⚙/✦ 自产 |
|
||||
| 特殊技动画终稿 | 并入 §2.3 三档意图动画,不另起体系 | 攻/防两型旧口径由三档替代 | ✦/⚙ 自产(同 §2.3) |
|
||||
| 连锁坍塌显形演出 | 连锁展开触发时的连锁演出 | 与音频方向同批配对登记;「坍塌显形」=连锁展开的玩家可见名 | ♻ 粒子/碎块改色 + ⚙ 坍塌结构自产 |
|
||||
| 封印特效 | 该动作的呈现名 = **封印**(art-bible §3.2):**施印瞬间特效 + 印记常显形态**两件 | 印记旗面走封印黑 `#000000`(现行封印色,色史归 art-bible §4.1);特效走运动+色相(§2:状态/情绪不走明度,空间光照可走);现行占位 = 封印黑「旗」字形;印记常显形态可与 §3.3 旗面像素化合并出 | ✦/⚙ 自产(精灵+粒子,烟尘光点可 ♻ 改色) |
|
||||
| 封印特效 | 该动作的呈现名 = **封印**(art-bible §3.2):**施印瞬间特效 + 印记常显形态**两件 | 印记旗面走封印黑 `#000000`(现行封印色,色史归 art-bible §4.1);特效走运动+色相(现行取法——明度分层不动);现行占位 = 封印黑「旗」字形;印记常显形态可与 §3.3 旗面像素化合并出 | ✦/⚙ 自产(精灵+粒子,烟尘光点可 ♻ 改色) |
|
||||
| 弹道/退场等动画密度 | 战斗表现输入清单,方案未定 | 样张锁动画方案后启动 | ♻ 通用弹道/光点/消散粒子改色为主 |
|
||||
|
||||
**现状**:三关舞台背景层与连续地面层已交付(2026-10-31 入包 `game/assets/art/scene/`:dungeon v10c / underdark v10c / deep_underdark v10c,均 1280×252;共用地面 v12b,1280×100,按作者裁保留原亮度 L≈0.132);小石台件已在役。
|
||||
|
||||
+20
-17
@@ -1,6 +1,10 @@
|
||||
# 美术圣经(Art Bible)— Dungeon Sweeper
|
||||
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.30,历次修订明细见 git 提交史
|
||||
<<<<<<< HEAD
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.33,历次修订明细见 git 提交史
|
||||
=======
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.33,历次修订明细见 git 提交史
|
||||
>>>>>>> docs-mood-ban
|
||||
>
|
||||
> **Art Director Sign-Off(AD-ART-BIBLE)**:历次批均未签核——此为 Lean/Solo 模式(非 PHASE-GATE)下的现行治理状态;full 门禁签核时机待用户表态。
|
||||
> 风格基调:地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身。
|
||||
@@ -30,7 +34,7 @@
|
||||
|
||||
**原则二|明亮为皮,明度分档为骨**(取自 Moonlighter、Peglin;锚定 P1 可读性前提、P4 探险氛围)
|
||||
|
||||
取 Moonlighter 的暖质地砖与亲和冒险者气质、Peglin 的明快全年龄洞穴色彩:全局暖倾向、无压抑。可读性由**格底明度分档**承载——已开格(数字所在格)明度走世界底层档(§2 明度分层;格态语义见《排雷玩法》;美术侧据此未开格走受光墙顶档、已开格走挖出空间沉暗档,与 §8.5 图底分离同轴),亮族数字墨直接画在格底上,直画对比 ≥4.5:1 恒成立。**数字色牌为后备手段**——仅复杂格底(物件纹样/变异纹带/微光叠加致格底对比不足)时垫牌后备(画法 §3.3、色值 §4.1)。格子本体美术放开:纹理、材质、装饰变体、类型纹样皆可入画;格底参数域见 §4.1 硬指标(分档明度域、饱和上限),仍避 Boss 禁入色相带。与参考之别:ITB 以半透明压暗板承载信息;本作格底功能分档(信息可读性服务,属 §2 明度分层允许域),情绪压暗仍禁。验收:机检〈1–8 全档数字对所在已开格底对比 ≥4.5:1〉
|
||||
取 Moonlighter 的暖质地砖与亲和冒险者气质、Peglin 的明快全年龄洞穴色彩:全局暖倾向、无压抑。可读性由**格底明度分档**承载——已开格(数字所在格)明度走世界底层档(§2 明度分层;格态语义见《排雷玩法》;美术侧据此未开格走受光墙顶档、已开格走挖出空间沉暗档,与 §8.5 图底分离同轴),亮族数字墨直接画在格底上,直画对比 ≥4.5:1 恒成立。**数字色牌为后备手段**——仅复杂格底(物件纹样/变异纹带/微光叠加致格底对比不足)时垫牌后备(画法 §3.3、色值 §4.1)。格子本体美术放开:纹理、材质、装饰变体、类型纹样皆可入画;格底参数域见 §4.1 硬指标(分档明度域、饱和上限),仍避 Boss 禁入色相带。与参考之别:ITB 以半透明压暗板承载信息;本作格底功能分档(信息可读性服务)。验收:机检〈1–8 全档数字对所在已开格底对比 ≥4.5:1〉
|
||||
|
||||
*设计测试:当格子美术与数字可读性冲突时,先调格底明度档,复杂格底垫色牌后备,不动格子画。*
|
||||
|
||||
@@ -69,7 +73,7 @@
|
||||
|
||||
> 本节服从第 1 节宣言「明快的地牢,轻快的扫荡,仔细的推理」。能量值为全作统一 0–10 刻度:0 静谧,10 沸腾。
|
||||
>
|
||||
> **明度分层(硬规则)**:明度按视觉层分带——**信息面层**(数字/UI 数据/预告/语义标识)最高、**世界中层**(格底顶面/角色/物件)居中、**世界底层**(背景/纵深/远景/已开空间)可更低;层间明度差服务空间纵深与光照(Moonlighter 式洞穴层次)。**情绪明暗禁令**:明度变化禁止用于**情绪表达**——画面禁止靠压暗/渐晕阴沉下来;roguelike 失败重开,世界无光暗记忆;Darkest Dungeon 式压暗仍在 §9.2 反面清单。**状态表达不受本禁令约束**:状态可用明度、色相、饱和、运动、元素任一手段承载。**允许域**:空间纵深、光照氛围、形体塑造。类型微光的明度梯度(§4.2)属信息面层内梯度,不扰世界层明度档。验收:目检〈情绪表达不走压暗/渐晕;三档明度带层间灰度可分〉
|
||||
> **明度分层(硬规则)**:明度按视觉层分带——**信息面层**(数字/UI 数据/预告/语义标识)最高、**世界中层**(格底顶面/角色/物件)居中、**世界底层**(背景/纵深/远景/已开空间)可更低;层间明度差服务空间纵深与光照(Moonlighter 式洞穴层次)。类型微光的明度梯度(§4.2)属信息面层内梯度,不扰世界层明度档。验收:目检〈三档明度带层间灰度可分〉
|
||||
>
|
||||
> **世界底层两子带**:世界底层拆两条子带——**底层下沿带 L 0.014–0.06**(远景、洞窟纵深、B 区舞台底、屏幕根底)与**底层上沿带 L 0.08–0.14**(已开格底、迷雾记忆态合成底),子带间隔 ≥0.02。拆两子带用于解决**世界底层内部** B 区舞台底(L=0.036)与 D 区已开格底(L≈0.10)同层难辨的问题——两者同属世界底层,单带下跨度 0.126 吃掉层间间隔 0.08,灰度图上会表现为两个不同的底。§1.4 整屏②项约束的是层间可分(底层上沿 0.14 对世界中层下沿 0.22,差 0.08 本就满足),②项不约束同层内可分,故本条不属②项的成立要件,而是世界底层内部的可读性纪律。验收:机检〈同一屏的世界底层成员全部落进两条子带之一,两子带 L 区间不重叠且间隔 ≥0.02〉
|
||||
|
||||
@@ -84,15 +88,15 @@
|
||||
### 2.2 轮末自动战斗结算
|
||||
|
||||
- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;完美封印(概念定义与兑付结构见《地图战斗资源》)的高光兑现——结算带高光行,以其为全场喝彩级的最高反馈档
|
||||
- **色彩性格**:情绪不走明度(§2 禁令),色彩转向金橙主导、饱和拉高;数字色转译为弹道色沿行推进
|
||||
- **色彩性格**:情绪由色相与饱和承载——金橙主导、饱和拉高,明度分层不动;数字色转译为弹道色沿行推进
|
||||
- **氛围形容词**:酣畅、递进、一气呵成
|
||||
- **能量**:7/10,随连击爬升
|
||||
- **承载元素**:兑现光带逐行横扫棋盘,开格化箭雨、印记化暴击砸向敌人,因果全程可见可归因。验收:目检〈兑现光带逐行横扫、因果可见可归因;情绪表达不走压暗〉
|
||||
- **承载元素**:兑现光带逐行横扫棋盘,开格化箭雨、印记化暴击砸向敌人,因果全程可见可归因。验收:目检〈兑现光带逐行横扫、因果可见可归因〉
|
||||
|
||||
### 2.3 清图奖励与换图
|
||||
|
||||
- **主情绪**:短促丰收——宝箱揭盖式的喜悦,三五秒即收,不拖节奏
|
||||
- **色彩性格**:全屏饱和峰值:金粒喷泉与彩纸迸发,饱和瞬时拉满后回落;明度不动(§2 情绪禁令)
|
||||
- **色彩性格**:全屏饱和峰值:金粒喷泉与彩纸迸发,饱和瞬时拉满后回落;明度分层不动
|
||||
- **氛围形容词**:丰收、轻快、干脆
|
||||
- **能量**:8/10,尖峰后速降
|
||||
- **承载元素**:最后一格碎砖中喷出的 GP 喷泉,叮当落入 UI 钱袋。验收:目检〈金粒喷泉短促收敛,饱和峰值后回落〉
|
||||
@@ -108,7 +112,7 @@
|
||||
### 2.5 战斗失败(冒险终结)
|
||||
|
||||
- **主情绪**:温柔的惋惜与「再来一次」的邀请——有分量、不惩罚,像合上一册冒险日志
|
||||
- **色彩性格**:情绪不走明度(§2 禁令),色相转暖灰掺暮蓝;全作禁血红与黑场,唯此一拍最静
|
||||
- **色彩性格**:情绪由色相承载——色相转暖灰掺暮蓝,明度分层不动;全作禁血红与黑场,唯此一拍最静
|
||||
- **氛围形容词**:惋惜、安静、余温、可再来
|
||||
- **能量**:1/10
|
||||
- **承载元素**:小人坐在碎砖上摇头掸灰,落下的封印印记逐枚化作光点收回行囊。验收:目检〈无血红与黑场;色相转暖灰掺暮蓝〉
|
||||
@@ -159,7 +163,7 @@
|
||||
|
||||
### 3.2 环境几何
|
||||
|
||||
- **雷图棋盘呈现基准**:战棋地块质感 × 正俯视角——取 ITB 参考图(`references/itb_1.jpg`)的地表质感骨架:地块纹理多样、起伏体块感、岛缘/砖缘厚度、格上装饰小立牌;但相机一律正上方垂直向下,格线屏轴对齐,**禁等距/斜视角与菱形格**;形状语言仍守总纲圆角方形。砖缘厚度与岛缘立体面属形体塑造,不属状态/情绪表达,不受 §2 情绪明暗禁令约束(属 §2 明度分层允许域;顶面格底仍守 §4.1 参数域)。装饰小立牌属 ITB 参考的观感元素,本作不采用;装饰变体走氛围层纪律(纹理级/物件级形状家族,见下方装饰变体条),零信息纪律以原则三为准。验收:目检〈棋盘正俯视、格线屏轴对齐、无菱形格〉 ←原二;原三;§2
|
||||
- **雷图棋盘呈现基准**:战棋地块质感 × 正俯视角——取 ITB 参考图(`references/itb_1.jpg`)的地表质感骨架:地块纹理多样、起伏体块感、岛缘/砖缘厚度、格上装饰小立牌;但相机一律正上方垂直向下,格线屏轴对齐,**禁等距/斜视角与菱形格**;形状语言仍守总纲圆角方形。砖缘厚度与岛缘立体面属形体塑造(顶面格底仍守 §4.1 参数域)。装饰小立牌属 ITB 参考的观感元素,本作不采用;装饰变体走氛围层纪律(纹理级/物件级形状家族,见下方装饰变体条),零信息纪律以原则三为准。验收:目检〈棋盘正俯视、格线屏轴对齐、无菱形格〉 ←原二;原三;§2
|
||||
- **地砖**:一切环境体由同一圆角方形衍生:未开 = 完整凸砖,已开 = 同形缺角碎裂,雷 = 碎裂加巢穴纹(危险格玩家说法见《排雷玩法》);只以"完整度"叙事,不为状态新造形。验收:目检〈三格态仅以完整度区分,无新造状态形〉 ←原三;P4
|
||||
- **装饰变体(未开格氛围层)**:覆尘完整砖之上叠加裂纹/碎石斑/物件级点缀等装饰。物件级装饰(小石块/陶片/骨殖等)须走**专属装饰形状家族**(与信息元素形状语言隔离、灰度下不与任何逻辑元素同形),数量配额按图类型定(候选 3–5 种,样张已冻结,§1.4),一律残缺、内凹、低对比而自然后退(§3.4);零信息纪律(分布随机、与雷/安/类型零相关、灰度下禁止制造新分类特征、分布每板一次跨轮不复掷)以原则三为准,此处不赘。验收:目检〈物件级装饰不与任何逻辑元素同形;装饰残缺低对比后退〉 ←原三
|
||||
- **结果整格表达(已开格)**:已开格按当格结果整格表达(结果清单见原则三);画法——产资源格的家族物件纹样用 §3.1 道具点剪影语言(直线几何体)角嵌于砖缘,砖心与数字字形占位区留白(已开格仍承载数字);危险格巢穴纹较已开格更凶一档,形状+颜色双通道。验收:目检〈产资源格嵌家族纹样且砖心留白;巢穴纹更凶一档〉 ←原三;P3
|
||||
@@ -377,7 +381,7 @@
|
||||
|
||||
- **双舞台常驻下的 B 区环境**:战斗画面区(站桩立绘所在)的背景 = 明亮地窟,与 §1 风格基调同源——暖倾向、无压抑;背景整体**低对比自然后退**(§3.4 焦点预算延伸):背景禁止构成完整凸形、禁止抢 B 区立绘与 D 区数字的焦点,识别层级恒为 立绘 > 背景。验收:目检〈背景无完整凸形、立绘最抢眼〉
|
||||
- **舞台正面目标**:B 区 = **地窟看台**——立绘脚下地台/落影承载(世界中层),背景环境层低饱和后退、明度走世界底层档;角色禁止直接贴平色底。规格与终值归整屏样张闸(§1.4)冻结。**地台面上零文字**——暖白 `#F0E2C8` 对地台板身 `#968264` 实测仅 2.89:1,已越 §7.2 的 4.5:1 硬指标,落法按 §7.2「文字禁直接压纹理底」(垫容器、素牌或罩层),非新条款;站桩文字位改垫素底或落进结算明细带(实装=站桩名字行垫 `HP_BAR_BACK` 深档素底牌、文字基线上移至地台顶上方——2026-10-30 落地)。验收:目检〈立绘脚下有地台/落影,非贴平色底〉+机检〈B 区站桩文字位正下方像素非地台板身色 #968264〉
|
||||
- **明度分层适用**:背景氛围的情绪表达仍禁止以压暗/渐晕实现(§2 禁令);空间纵深与洞穴感走明度分层允许域——世界底层档、光照氛围、色相饱和与形体承载(§3.2 同款逻辑)。验收:方向性
|
||||
- **明度分层适用**:空间纵深与洞穴感走明度分层——世界底层档、光照氛围、色相饱和与形体承载(§3.2 同款逻辑)。验收:方向性
|
||||
- **图类型联动**:背景层按 `board_type` 选三张变体(`scene_stage_bg_dungeon.png` / `scene_stage_bg_underdark.png` / `scene_stage_bg_deep_underdark.png`,方向取 §6.3 三关色板候选);**背景不再走 §4.3 `wall_ambient` tint**(该槽保留给棋盘/世界的墙雾氛围,避免对三张背景二次染色)。紫赤仅 Boss 战允许短暂进入背景氛围纹样(§2.6 色相独占的舞台侧延伸,高饱和独占不变),非 Boss 战高饱和紫赤禁入。验收:机检〈背景吸色无非 Boss 高饱和紫赤;背景素材不引用 `wall_ambient` 槽〉
|
||||
- **场景构成(2026-10-31 用户裁)**:B 区舞台从远到近分三段——**顶部空区**:洞顶以上的昏暗空间,只留深色空区,**不画钟乳石、吊锥、悬挂物或任何可辨形状**;**远景(纵深洞厅)**:不是贴墙的后壁,按远/中/近三层组织——远层暗剪影(远处洞口/通道、层叠岩体、洞底轮廓)、中层主要细节层(岩台、岩柱、凹壁、裂缝)、近层左右暗色岩框;剪影被中层部分遮挡、不贴顶、不孤立,**禁贴墙感、禁单件圆柱贴顶、禁砖石墙/规则砌缝/方整石块**;三关母题(地精=支洞/木栈道/木桩围栏;卓尔=巨柱/石笋/蛛网/螺旋塔剪影/巨菇/石桥;灰矮人=石笋城郭/棱角石厅/石桥/矿道口/烟囱塔)见 `battle-stage-prompts.md` §3.2;**分隔带**:远壁根部与近景地面之间一条不规则碎石带/阴影缝,边界清楚但不笔直,**禁瓷砖缝、踢脚线、规则台阶**;**近景地面**:干净压实泥土为主,全幅只零星几颗小石、散点分布,中央平缓留空给角色站位,**禁成排/成堆碎石、满铺砂砾、泥点噪声、瓷砖、规则铺装、草、木地板**。纵深与三段构图归 §1.4 样张;逐条要素、分层提示词与否定后缀 = `battle-stage-prompts.md`(本文件不重复维护)。验收:目检〈顶部无钟乳石/吊锥;远景有纵深、非贴墙;无孤立圆柱贴顶;近景为泥土碎石;分隔带不规则;中央无大障碍〉
|
||||
- **图层归属(2026-10-31 用户裁)**:背景层 = 三关变体 `scene_stage_bg_dungeon/_underdark/_deep_underdark.png`(顶部空区+纵深洞厅远景,不画近景地面;按 `board_type` 选一张);近景地面 = **独立连续地面层** `scene_stage_ground.png`(泥土/砂砾/碎石地面,全宽、底边贴 B 区底、顶缘即分隔带;高度读布局表 `ground_h`,字段由地面层实装批新增并回填,本批终稿高 100、代码批落表);现有每角色小石台 `scene_stage_plaza.png` 保留,叠在连续地面之上。连续地面层与小石台**三关共用**、跨关恒定、**不吃 `wall_ambient` tint**(世界中层承载面随关卡换明度会破 §2 层间关系);背景三张变体各自带关卡色族,不再叠 tint。验收:机检〈三张背景文件名带 board_type 后缀;地面层/小石台文件名无 board_type 后缀;背景与地面层均不引用 `wall_ambient` 槽〉
|
||||
@@ -395,7 +399,7 @@
|
||||
- **交互四态(零新素材,全部程序化)**:悬停=**轮廓亮边 + 浮动**(浮动须整像素吸附);按下=位移 1px;**禁用/未解锁=降明度 + 去饱和**(§2 已缩窄至情绪域,状态表达不受限),**仅锻炉(锻造工坊)与宝箱(秘宝匣)两件会有此态**;已解锁=常态。
|
||||
- **钥石余额**:常驻画面顶部的一行小 HUD 栏(高 48px,**浮层覆盖,不挤压画面**)。
|
||||
- **事实源边界**:解锁条件/价格/存档写入归元进度 GDD 与 `MetaProgression`;素材命名与路径归 `asset-path-map`;清单登记归 `art-assets-checklist` §3.5。
|
||||
- **待裁(本节不填数,由作者定)**:① **营地环境子板色值**——色彩脚本只给定色方向,落地色值属 §4.1 单点,须过 §4.2 语义检查与 RGB 三通道差 ≥8/255 两道门;② **门扇与门洞的高宽比例**——中景带自底部往上约 2/3 处起之后高度有限,门扇高度远超围墙带,两者关系待作者重定;③ **帐篷的落地尺寸**——帐篷体量大于被替换的旧承载件,尺寸待作者重定;坐标草案的现行出口见 `camp.md` 的指路句。
|
||||
- **已裁(2026-11-01 用户裁,随迭代 47 卡④ 开工门销号)**:① **营地环境子板色值**——不前置拍板:T1 量化场景实际用色后按 §4.1 场景环境子板条款登记(每屏一套、≤32 色、过 §4.2 语义检查与 RGB 三通道差 ≥8/255 两道门);② **门扇与门洞的高宽比例**——门扇 240×220 追认现值,坐标按门洞 x520–760 对齐(`camp-scene-prompts.md` §2.3);③ **帐篷的落地尺寸**——300×170 追认现值(各素材第一张样本稿已过审为终稿,重定即触发重出,不裁)。
|
||||
|
||||
### 6.3 三关图类型色板候选(board_type 换挡方向)
|
||||
|
||||
@@ -422,7 +426,6 @@
|
||||
- **记忆态可读性判据**:数字对合成底**归一后(真实渲染面)三关 ≥4.5:1**(WCAG 硬指标;合成底 L1 #4C4038/L2 #3F4147/L3 #3E4048;行值与素材随动,测点=ui_contrast_probe 迷雾段)。**归一注**:装载亮度归一是现行生产路径(`board_view._normalize_floor_luma`),原图均值≠渲染面,复算须落归一后底。可辨性自查(三通道,随样张/实机自检):①对全黑未开格——合成底 #3E3731(L≈0.036)对 L0,实机目检常光档可分(暗房档一并抽检);②对灯光区内活态已开格——主判据=数字墨灰度差(记忆墨 L0.55 对亮墨三档 L≥0.75 / 色牌 #F5EAD3 L≈0.83,灰度 ΔL≥0.20)+ 色相通道(记忆态近中性灰,对活态暖底暖墨);③格态三形在记忆态保持灰度可辨——形通道经纹理+灰罩形态不变,沿 §1.4 验收 2 同源判据。验收:机检〈ui_contrast_probe 迷雾段记忆墨对比 ≥4.5:1〉←§1.4
|
||||
- **迷雾过渡边:硬边(逐格二值)**:灯光区边界 = **整格二值切换**(区内全渲染 / 区外全黑或记忆态),**不做渐变过渡带、不做半透明晕边**。理由:①边界是信息边界不是氛围晕——渐变中间态 =「看得见但看不清」,破数字可读硬指标与记忆态明度档;②现行事实源验收断言(边界格呈态、两态逐位一致)须格级二值可判;③与砖面整格表达(§3.2 高墙瓦片体系)、ITB 格内信息分层气质同轴。无动画,减动效无涉。←现行事实源;§1.2 原则一;§3.2
|
||||
- **与结算期去饱和的叠加共存**:结算期去饱和偏移(settlement_fx_view,同族先例)与迷雾调制叠加规则 = **记忆态为终态档**——区外已开格在结算期保持记忆态档、**不再叠加**结算去饱和(灰显已含去饱和语义,二次调制无信息增量且明度不可控);灯光区内格照常吃结算去饱和;全黑格两态无差。叠加态随实施批共存校验 + 探针断言(provisional,实机目检可代拍)。验收:机检〈叠加态下记忆态灰显不被二次调制〉
|
||||
- **§2 明度禁令自查**:迷雾遮蔽与记忆态灰显属 §2 明度分层**光照氛围允许域**(洞穴举灯的空间光照呈现,与 Moonlighter 取列「亮地板/暗深渊/发光体」光照层次同族),非情绪压暗(§9.2 Darkest 反面条款不触——那禁的是情绪表达)、非状态明暗编码(格态语义仍由形+色相承载,灰度下三态可辨不受扰)。←§2;§9.1;§9.2
|
||||
|
||||
---
|
||||
|
||||
@@ -516,19 +519,19 @@
|
||||
- **全局纹理过滤与平滑渐变件例外**:`project.godot` `default_texture_filter=0`(Nearest)全局——像素风 UI 皮(九宫格/主题石纹样式盒/滑轨抓子/勾选/箭头图标)翻 Nearest=意图面;**显式 LINEAR 豁免清单(有意例外)= vignette / gradient_base / beams 三件程序化平滑渐变件**——保平滑连续 alpha 的程序化氛围件(§7.6 三已裁);新增平滑件须同批入册本清单,禁静默挂 LINEAR。翻转面枚举与冒烟断言(6 条)在闸。验收:机检〈过滤面枚举与 LINEAR 清单逐项一致〉
|
||||
- **显示档登记例外(64 源 0.5×,待素材批消除)**:柜台四枚 `icon_forge/workshop/atlas/keystone`(64×64 源)显示档统一 **32px = 0.5×**;状态九枚(32×32 源)mini(站桩)档 **16px = 0.5×**。两处均系「显示尺寸侧唯一可调、素材一字节禁改」红线下的人工检整数半倍档,登记为**待消除例外**——素材设计出 32px 源后回归 1×。常规档显示一律 1×(状态九枚 32px、手绘十枚 20px/40px 双档、BCard 图标槽 40px=2×、**护甲类型图标三枚 24px=1×**——源 24×24,归甲类整数倍纪律)。验收:机检〈显示档与登记表一致〉
|
||||
- **乙类·角色与单位精灵**(有机形体、非文字):允许运行时非整数倍缩放(NEAREST 过滤不变),容忍轻度像素宽窄不均;判据 = 实机目检,可感不适即回整数档。乙类免逐案豁免。验收:目检〈立绘无可感拉花〉
|
||||
- **站桩立绘槽位=高定宽随**:立绘/精灵槽位只定「高度的整数倍档」,宽度随源图宽高比自适应——**禁止把全部立绘塞进统一方框强拉**(纵横倍率必须恒等)。反面实例:统一方框把窄条生物源(细长本体裁掉透明边)按主角比例横拉压扁发花。乙类的非整数豁免只覆盖**等比**缩放,不含纵横异倍变形。验收:机检〈立绘纵横倍率恒等〉
|
||||
- **站桩立绘槽位=高定宽随**:立绘/精灵槽位只定「高度的绝对档(档高取整数像素值)」,宽度随源图宽高比自适应——**禁止把全部立绘塞进统一方框强拉**(纵横倍率必须恒等)。反面实例:统一方框把窄条生物源(细长本体裁掉透明边)按主角比例横拉压扁发花。乙类的非整数豁免只覆盖**等比**缩放,不含纵横异倍变形。验收:机检〈立绘纵横倍率恒等〉
|
||||
- **1920×1080 档策略**:全局等比(stretch=canvas_items+aspect **keep**)——16:10 设计在 16:9 窗口=等比 1.35×+左右黑边各 ~96px,构图与 1280×800 基准档完全一致;甲类 1.35 非整数缩放例外注记:轻度宽窄不均系本档位接受代价(像素游戏 shipped 常态),实机目检可感不适→回乙案(1920 独立 2× 基准)重议;乙类同前款。验收:目检〈1920 档构图与 1280 档一致〉
|
||||
- **画布基准(暂定值)**:
|
||||
- **主角人设原图 = 48×72**(1× 基准,暂定;与站姿降采样可读性验证一致);
|
||||
- **棋盘小人 = 32×32 二头身派生版(暂定,允许更小)**——独立资产,自人设定稿压缩派生(character-design §5.4),不吞数字位、占位 ≤ 0.75× 格边长;板越小占比摩擦越大(13×13 / 14×14 更小,算例见 [battle.md](../gdd/ux/battle.md)「棋盘格尺寸公式」),32px 源档的去留已定终值 = **保留 32×32 源档**(沿「独立小源档」路线;N=12 板下显示占位上限 27px = 0.84 倍,正落本节乙类非整数缩放许可范围,**连豁免都不用再办**),待决标注注销;
|
||||
- **站桩立绘源档 = 八敌逐键定档**:主角 48 不变;八敌源档高 = goblin 36 / giant_spider 38 / duergar 46 / hook_horror 58 / bugbear 62 / ogre_chief 74 / drider 90 / duergar_king 94,显示一律 **×2 整数档(NEAREST)** = 72–188。定档依据 = 生物体型三约束(钩爪恐魔>灰矮人、灰矮人<熊地精、食人魔>熊地精——体量=物种体型,非强度线性映射,见 character-design §5.2);可读性实测门槛 = 源高 ≥32(24 糊/32 可读/48 清楚/64 细节完整),全阶梯 36–94 达标;派生管线 = pixel-1.png 紧裁 → 等比缩至档高 → dom_block_resize 块内众数降采样(tools/pixelize_candidates.py `STANDEE_TIER_H`,`SIZES` 降格为管线上游中间分辨率表);体量存在感经整屏对比校验(B 区高读 `stage_h`/立绘显示上限 188)。验收:机检〈standee_asset_check 逐键档高校验〉
|
||||
- **站桩立绘排版**:① 区高 B 区 ≈1/3 屏 / C 区 ≈2/3 屏,**扫雷盘原尺寸不缩、格子不变**(区高事实源 = 布局表 `battle_layout.tres`;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:drider/duergar_king)**允许向上溢出盒顶 N px**(N 由实装批按实测尺寸算定,**与背景/光柱无关**——现行溢出区恰为临时光柱氛围层〔§6.1 方向稿四柱,x 基准 110/900/1055/1210@1280,`battle_stage_view.gd:47-56`〕,正式舞台背景插画(三关变体)到位后该层直接退役(背景不画光柱),**溢出判定不随之变更**;横向禁止压到顶部中央轮次·阶段指示,溢出上限由实装批用真图复算 x 区间定),血条仍落底(落底排的第二个收益=头部探顶不带出血条);意图徽标悬浮头顶上方(探顶档压胸);④ 「血条悬浮头顶+立绘盒内居中」排式已弃——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
|
||||
- **站桩立绘显示档 = 三档绝对高**:小型 96 / 中型 144 / 大型 188。逐件归档——小:地精、巨蜘蛛、灰矮人;中:主角、钩爪恐魔、熊地精;大:食人魔酋长、蛛化卓尔、灰矮人石君王。**源 = 原稿像素网格**(八敌 `pixel-1`、主角手修 2× 三态稿),紧裁后原样入包,**禁再降采样**;显示倍率 = 档高 ÷ 源高,NEAREST,宽度随源图比例(高定宽随不变,见上条)。定档依据 = 生物体型(体量=物种体型,非强度线性映射,见 character-design §5.2),**顺序由显示档承载、不由源高承载**。已接受项:中型三件与地精/巨蜘蛛的源高(92/94/123/90/81)低于档高,属放大显示;食人魔酋长显示宽 157 超桩盒 150。验收:机检〈standee_asset_check 逐件显示档高与归档一致、纵横倍率恒等〉+目检〈同屏体量顺序成立〉
|
||||
- **站桩立绘排版**:① 区高 B 区 ≈1/3 屏 / C 区 ≈2/3 屏,**扫雷盘原尺寸不缩、格子不变**(区高事实源 = 布局表 `battle_layout.tres`;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:食人魔酋长/蛛化卓尔/灰矮人石君王)**允许向上溢出盒顶 N px**(N 由实装批按实测尺寸算定,**与背景/光柱无关**——现行溢出区恰为临时光柱氛围层〔§6.1 方向稿四柱,x 基准 110/900/1055/1210@1280,`battle_stage_view.gd:47-56`〕,正式舞台背景插画(三关变体)到位后该层直接退役(背景不画光柱),**溢出判定不随之变更**;横向禁止压到顶部中央轮次·阶段指示,溢出上限由实装批用真图复算 x 区间定),血条仍落底(落底排的第二个收益=头部探顶不带出血条);意图徽标悬浮头顶上方(探顶档压胸);④ 「血条悬浮头顶+立绘盒内居中」排式已弃——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
|
||||
- **站桩石板地台件**:`scene_stage_plaza.png`,源 48×12、九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定、不吃 §4.3 `wall_ambient` tint(独立理由见 §6.1 近景地台独立件条)。验收:机检〈地台件按九宫格拉伸后顶缘受光线宽度恒为 2px〉
|
||||
|
||||
### 8.3 导出格式与命名规范
|
||||
|
||||
- **归档纪律**:图片素材**不入 git 版本管理**——仓库只保留运行时引用资产(game/assets);生成链路原始产物(AI 原图/中间产物/候选版本)归档至 `archive/` 并择机上对象存储备份;主文档(圣经/GDD/清单)**禁引用 artwork/ 散文件路径**——定稿成图以 game/assets 为唯一事实源,过程稿引用一律用归档包路径或描述性出处。验收:机检〈主文档 grep 无 artwork/ 散文件路径〉
|
||||
- **入包与对账检查器(挂点已立,实现随 PRD 工具批)**:①**色板检查器**——扫描新素材色板,报新增色/近色告警/超 §4.1 上限(48)拦截,挂素材流入包步(asset-path-map §2 第 3 步);②**路径核对器**——扫描 asset-path-map 声明的全部路径,悬空/未清即报,随大批与审计例行跑;③**standee_asset_check 硬闸升级规格**——按 §8.2 逐键定档校验体量与档高、像素域色数 ≤32、无非整数倍缩放痕迹、描边亮度达标(亮描边明度档);把「高定宽随」教训变成代码闸而非文字。验收:机检〈三检查器在闸可跑〉
|
||||
- **入包与对账检查器(挂点已立,实现随 PRD 工具批)**:①**色板检查器**——扫描新素材色板,报新增色/近色告警/超 §4.1 上限(48)拦截,挂素材流入包步(asset-path-map §2 第 3 步);②**路径核对器**——扫描 asset-path-map 声明的全部路径,悬空/未清即报,随大批与审计例行跑;③**standee_asset_check 硬闸升级规格**——按 §8.2 显示档逐件校验显示档高与归档、纵横倍率恒等、像素域色数 ≤32、描边亮度达标(亮描边明度档);把「高定宽随」教训变成代码闸而非文字。验收:机检〈三检查器在闸可跑〉
|
||||
- **交付格式**:PNG 透明底;动画 = 序列帧或引擎精灵表(SpriteFrames),帧尺寸恒定、横向等宽排列(候选)。验收:目检〈交付件透明底无杂边〉
|
||||
- **命名规范(候选草案)**:`<域>_<对象键>_<状态|变体>[_<序号>].png`,全小写 snake_case;**对象键 = 数据表主键**(effect_id/reward_id/cell_type/enemy_id——图标键契约 §4.6 的文件侧延伸:资产文件名键 = 图标键,全局恒定禁运行时换图);图类型变体后缀 = board_type 键(`_dungeon`/`_underdark`/`_deep_underdark`);候选评审件与终稿分目录(`artwork/candidates/` 承载评审件)。验收:机检〈命名与目录检查器零红〉
|
||||
|
||||
@@ -557,7 +560,7 @@
|
||||
| 参考 | 取 | 弃 |
|
||||
|---|---|---|
|
||||
| **Into the Breach** | 格内信息分层、攻击预告上格范式、战棋地块质感骨架(§1.2 原则一;§3.2) | 冷暗军事色调、压暗信息板、斜切 HUD(原则二;§3.3) |
|
||||
| **Moonlighter** | 暖质地砖与砖缝质感、亲和冒险者气质、木石暖色道具(原则二/原则三);**光照层次与明度纵深**(亮地板/暗深渊/发光体三档,§2 明度分层) | 全拟物倾向(§3.3 半拟物边界);Darkest 式情绪压暗(§9.2) |
|
||||
| **Moonlighter** | 暖质地砖与砖缝质感、亲和冒险者气质、木石暖色道具(原则二/原则三);**光照层次与明度纵深**(亮地板/暗深渊/发光体三档,§2 明度分层) | 全拟物倾向(§3.3 半拟物边界);Darkest 式压暗(§9.2 反面基准) |
|
||||
| **Peglin** | 明快全年龄洞穴色彩、胖体量亲和怪(§5.1 普通怪原型) | 色块依赖——先读轮廓后读颜色(§5.1) |
|
||||
|
||||
- **反面条款已成文**(§9.2);反面参考**图证**待本作素材生成后增补。验收:方向性
|
||||
@@ -566,7 +569,7 @@
|
||||
|
||||
| 反面参考 | 违反条款 | 立法理由 |
|
||||
|---|---|---|
|
||||
| **Darkest Dungeon** 式压暗 | §2 情绪明暗禁令(明度分层下仍禁) | 状态与情绪只走色相/饱和/运动/元素四手段,不以变暗表达情绪(失败重开,世界无光暗记忆) |
|
||||
| **Darkest Dungeon** 式压暗 | §9.2 反面基准(风格取向记录,非禁令——压暗属普通设计决策,逐案走样张与目检) | 失败重开,世界无光暗记忆——明亮基调下不以整屏变暗作常态表达 |
|
||||
| **传统扫雷白牌/贴纸式数字承载** | §1.2 原则二(明度分档为骨) | 数字由格底明度分档承载、直画入世界;色牌仅复杂格底后备——满屏白牌=电子表格观感,非游戏画面 |
|
||||
| **Enter the Gungeon** 式粒子与色相爆炸 | §3.4 焦点预算;§4.2 色相纪律 | 信息层禁止被特效与杂色吞没,特效入画先过焦点预算 |
|
||||
| **Hades** 式特效密度 | §2.2 兑现爽感的上限 | 爽感靠逐行因果归因,不靠满屏特效;承载元素以 §2.2 清单为界 |
|
||||
|
||||
@@ -176,7 +176,7 @@ python tools/shot_checker.py <截图目录>
|
||||
python tools/shot_checker.py <截图目录> shot_menu.png shot_battle.png
|
||||
```
|
||||
|
||||
**判据或口径**:A 屏级骨架——PNG 存在、任一边 1280、灰度 std 不低于 8、唯一色数不低于 64。B 文字对比——战斗 C 区面板与明细带提示的 WCAG 不低于 4.5:1。C 明度三档——战斗屏信息面高于世界中,世界中高于世界底。D 文字禁压纹理——文字框背景中带灰度 std 不高于 18。F 文字在——菜单标题框内与中位灰度差大于 60 的像素占比落在 2% 到 55%。B/C/D 只在清单含 `shot_battle.png` 时跑,F 只在含 `shot_menu.png` 时跑,其余屏只跑 A。全过 0,缺参数或任一失败 1。
|
||||
**判据或口径**:A 屏级骨架——PNG 存在、任一边 1280、灰度 std 不低于 8(取值在圣经 §7.2 文字 4.5:1 推论下界之下、正常实测最低 19.3 之上)、唯一色数不低于 40(正常实测地板 60 的 2/3,低于一切正常屏;色数异常回落=画面异常信号)。B 文字对比——战斗 C 区面板与明细带提示的 WCAG 不低于 4.5:1。C 明度三档——战斗屏信息面高于世界中,世界中高于世界底。D 文字禁压纹理——文字框背景中带灰度 std 不高于 18。F 文字在——菜单标题框内与中位灰度差大于 60 的像素占比落在 2% 到 55%。B/C/D 只在清单含 `shot_battle.png` 时跑,F 只在含 `shot_menu.png` 时跑,其余屏只跑 A。全过 0,缺参数或任一失败 1。
|
||||
|
||||
**已知坑与适用边界**:截图必须由窗口化 `shot_probe` 产出;headless 没有渲染面,截图是空的。`shot_meta.json` 缺失时 B/C/D 直接判 meta 几何缺失,不会跳过。尺寸断言按「任一边等于 1280」放行;1920×1080 截图两边都不等于 1280,会被判非工程档,与文件头「1920 档 1.5×」说明不一致,按源码为准。焦点预算是目检项,机检不判。
|
||||
|
||||
|
||||
+33
-40
@@ -1,4 +1,4 @@
|
||||
# 生图提示词卡(角色级资产)v2.12
|
||||
# 生图提示词卡(角色级资产)v2.13
|
||||
|
||||
> **性质**:美术清单生图通道(✦ 用户自跑,现走线上 API)的执行侧文件——每资产一段**自包含完整提示词**:正向 = 通用底座+主体+动作已拼妥、负向 = 通用底座+增补已并妥,整段复制直接贴进工作流即可,**不需要读其他文件、不需要拼装**。人物规格转写自 [character-design.md](character-design.md)(人物设计 v1.0,自 art-bible §5 迁出,圣经 §5 为存根),风格/色彩条款转写自 [art-bible](art-bible.md)(§4.1 主色板 / §4.2 语义色规则);冲突时人物规格以 character-design 为准、色彩以 art-bible 为准。
|
||||
> **边界**:本文件只管**角色级**资产的生图通道。图标/格面/样张走脚本管线(⚙ 代理出),规格在 art-bible §4.6 与 [art-assets-checklist](art-assets-checklist.md),不需要提示词。**场景级资产(营地场景图的背景层与八个物件层)另立 [camp-scene-prompts](camp-scene-prompts.md);战斗舞台背景层与连续地面层另立 [battle-stage-prompts](battle-stage-prompts.md),本文件不覆盖**;两卡的角色/场景分工以「谁的资产谁出卡」为界,同一段提示词不两处维护。战斗舞台的小石台件与护甲类型图标三枚的**出图工单**登记在本文件 §5;§5 只把各权威节的定值收进逐件工单,不立新值。意图动画三档是动画资产,等一屏样张锁方案后另立规格卡,本文件不覆盖。
|
||||
@@ -290,7 +290,7 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
|
||||
> **定值纪律(§8 头注 定值单点纪律)**:本节**指路不立值**——色值 = 圣经 §4.1(含场景环境子板)、尺寸与缩放 = §8.2、明度带 = §2、甲档 = character-design §5.3、命名 = §8.3 + asset-path-map。本节新增零定值;凡圣经未定的值,一律进本节 §5.5 缺口,不在本节填空。
|
||||
> **验收标注纪律**:逐条按三档标 `←机检: <断言>` / `←目检: <判据>` / `←方向性`,照圣经头注 2026-10-06 用户拍板的写法。
|
||||
> **流程**:出图前后一律走 §0 五步;出图登记义务照 remote-gen-prompts 头注轻量口径(用途+生成时间必注,其余参数有则记),随候选图存 `artwork/candidates/<资产名>/`;登记不作正稿遴选前置。
|
||||
> **产出状态**:组二地台件与组三护甲三枚已按各节 ⑤「程序化产出即终稿」产出入包(`scene_stage_plaza.png` 48×12、`ui_armor_light/ward/heavy.png` 24×24,路径见各组 ① 字段;登记=战斗一屏样张批,合并提交 3b66d1a6);组一背景插画已迁至 [battle-stage-prompts](battle-stage-prompts.md)(仍待出图),组四立绘重产仍待出图。
|
||||
> **产出状态**:组二地台件与组三护甲三枚已按各节 ⑤「程序化产出即终稿」产出入包(`scene_stage_plaza.png` 48×12、`ui_armor_light/ward/heavy.png` 24×24,路径见各组 ① 字段;登记=战斗一屏样张批,合并提交 3b66d1a6);组一背景插画见 [battle-stage-prompts](battle-stage-prompts.md)(三张背景+共用地面已入包,四柱/平色底退役归代码批),组四改按档派生(不重画,待实装批落位)。
|
||||
|
||||
### 5.1 组一 · B 区舞台背景插画(1 件)
|
||||
|
||||
@@ -375,25 +375,27 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
|
||||
- ←方向性: 冷灰与全作暖底成材质对比(§5.3),不靠明度差与地台 `#968264`(L≈0.234)抢读——立绘与地台的可辨归 §5.1 轮廓通道
|
||||
- 来源与工艺:♻ 素材站 plate armor 肩甲/胸甲类图标改色 → 闸四道;⚙ 兜底 24×24 内肩 + 胸两块甲板,与全作暖底成材质对比(§5.3 冷灰金属与暖底对比)
|
||||
|
||||
### 5.4 组四 · 主角与三 Boss 立绘重产(4 件)
|
||||
### 5.4 组四 · 站桩立绘按档派生(9 件,不重画)
|
||||
|
||||
**四件共用字段**
|
||||
> 本节是站桩立绘的**按档派生**工单:现有形象即终稿、禁改形象;清晰度靠保留原稿分辨率+显示按三档绝对高缩放解决,不重画、不生成。
|
||||
|
||||
**九件共用字段**
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| ① 路径与命名 | `game/assets/art/character/character_hero_portrait.png` + `game/assets/art/character/character_enemy_<enemy_id>.png`(域键 `character`,对象键 = `hero` / enemy_id,命名照 §8.3 与 asset-path-map §3 现役写法) |
|
||||
| ② 尺寸档与像素网格 | **源档高逐键** = 主角 48 / ogre_chief 74 / drider 90 / duergar_king 94(§8.2 立绘源档表,逐件值见本节 5.4.1–5.4.4);**宽随源图宽高比,纵横倍率恒等**(§8.2 站桩立绘槽位 = 高定宽随条,禁把立绘塞进统一方框强拉);九宫格 = 否;显示一律 **×2 整数档** = 96 / 148 / 180 / 188;**乙类**(角色与单位精灵)——乙类允许非整数,本批按 §8.2 定案走 ×2 整数档 |
|
||||
| ③ 色板约束 | 进**语义/UI token 板**(§4.1 分层①);**48 余量 0**(2026-10-28 用户裁:本组新增卓尔浅紫灰皮 1 枚,全板余量 2 → 0,额度已用尽)。逐件沿用 character-design §5.4 / §5.5 已登记色值;该两节只给形容词未给色值的部件,**2026-10-28 用户裁已落值**——四处换色合并(零额度)+ 卓尔浅紫灰皮新增 1 枚,逐件取值见本节 5.4.1–5.4.4,额度账见 §5.5 缺口 4。**石君王岩石板甲 = 重甲甲材质色、与 §5.3.3 同一枚,工单直接引 `#868C9A`、不另计额度**。单张像素域色数 ≤32。第二道门同本节 5.1 |
|
||||
| ② 尺寸档与像素网格 | **源 = 原稿像素网格**(八敌 `pixel-1`、主角手修 2× 三态稿),紧裁后原样入包,**禁再降采样**;**显示档 = 三档绝对高**——小 96(地精/巨蜘蛛/灰矮人)、中 144(主角/钩爪恐魔/熊地精)、大 188(食人魔酋长/蛛化卓尔/灰矮人石君王),定值单点 = art-bible §8.2;显示倍率 = 档高 ÷ 源高,NEAREST,宽随源图比例(高定宽随、纵横倍率恒等,禁方框强拉);九宫格 = 否 |
|
||||
| ③ 色板约束 | 色值以**现役稿**为准,本批零新增色、零换色;单张像素域色数 ≤32(`pixel-1` 与手修 2× 稿实测均 ≤32)。`#A79CB8` 保留在板、本批不消费,色板额度议题另批讨论 |
|
||||
| ④ 明度带归属 | **世界中层 L 0.22–0.45**(§2 世界中层 = 格底顶面 / 角色 / 物件)——判定对象 = 角色体块主色。描边与落影底盘属轮廓通道(character-design §5.1)、紫赤点缀属 §4.2 色相独占的语义色,两者都不按明度带判定 |
|
||||
| ⑤ 是否程序化件 | 否 → **须手绘**。口径 = **精细手绘像素**(2026-10-14 用户裁:仍是像素风,手绘像素簇 + 更高源密度;不是插画、不是 HD-2D、不回退高清图);**禁 AI 降采样当终稿**。现行 AI 降采样立绘 = 占位态,按 §7.1 占位完成条款须在终稿闸前换代 |
|
||||
| ⑤ 是否程序化件 | 否 → **零绘制**:源 = 现有原稿,本批不生成、不重画(现有形象即终稿)。§7.1 占位完成条款对本组不适用——现役稿即终稿 |
|
||||
| ⑥ 验收判据 | 见下方共用验收 |
|
||||
| ⑦ 出图操作要点 | 见下方工艺要点(手绘,不产生图提示词) |
|
||||
| ⑦ 操作要点 | 见下方派生要点(零绘制,不产生图提示词) |
|
||||
|
||||
**共用验收(四件都要过)**
|
||||
|
||||
- ←机检: 源档高逐位等于 48 / 74 / 90 / 94,显示 = 源 ×2(96 / 148 / 180 / 188),过滤 = NEAREST
|
||||
- ←机检: 逐件显示档高与归档一致(小 96 / 中 144 / 大 188),过滤 = NEAREST,纵横倍率恒等;源高低于档高者按已接受放大项登记(主角 92 / 钩爪 94 / 熊地精 123 / 地精 90 / 巨蜘蛛 81)
|
||||
- ←机检: 单张像素域色数 ≤32
|
||||
- ←机检: 脚底基线对齐(派生管线四条:等比整数缩放 + 降色 ≤32 + 脚底基线对齐 + 命名合规)
|
||||
- ←机检: 脚底基线对齐(紧裁 + 底中锚;命名合规、像素域色数 ≤32 同旧)
|
||||
- ←机检: 主角描边 = `(40,19,13)`;敌方描边取暖白族(非黑、非深褐)—— character-design §5.1 主角描边例外条
|
||||
- ←机检: Alpha 直出——不透明区外缘 1px 环内无生成底残留(无 R/G/B 三通道均 ≥230 且 alpha > 0 的像素),防抠图白边
|
||||
- ←机检: 零 HSV 300°–345° 像素(drider / duergar_king 两 Boss 除外,§4.2 独占)
|
||||
@@ -401,48 +403,39 @@ Seamless tileable pixel art texture of dry dirt ground, top-down, for a dungeon
|
||||
- ←目检: 放大 4 倍,轮廓与衣服是成组的像素,不是降采样噪点(三判据①)
|
||||
- ←目检: 显示 96–188px 下脸、铲、旗、物种能认出来,不必先读名字(三判据②)
|
||||
- ←目检: 显示仍用邻近采样,不出现插画那种平滑渐变(三判据③)
|
||||
- ←方向性: 精细手绘像素气质——不是插画、也不是半像素半高清
|
||||
- ←方向性: 现有形象即终稿,本批禁改形象
|
||||
|
||||
三判据出处 = `reports/美术审计-cursor会话存档-2026-10-14.md` §2.2。
|
||||
|
||||
**工艺要点(手绘)**
|
||||
**派生要点(零绘制)**
|
||||
|
||||
1. 出件顺序 = 先主角、再三 Boss(Alpha 范围 = 主角 + 三 Boss 重产,其余敌种当前可读则保留到 Beta)。
|
||||
2. **源密度判据 = 三判据②**:显示 96–188px 下脸、铲、旗、物种可认——照这条反推源档即可,逐键定档已完成(§8.2),本批不必再猜档位。
|
||||
3. 手绘起稿顺序 = 剪影(§5.1 archetype)→ 体块主色 → 暗部与受光 → 描边 → 落影底盘。
|
||||
4. 逐件 2–3 版候选;登记照 remote-gen-prompts 头注轻量口径(用途+生成时间必注,手绘作者有则记),随候选图存 `artwork/candidates/<资产名>/`。
|
||||
5. 候选评审 = 并排横评,**隐去文件名与来源后按三判据逐条判读**(§0 五步第 1 步「便于横评」的手绘侧落法),不靠记忆与印象拍板。
|
||||
6. 出件 = 透明 PNG(Alpha 直出,禁先出白底图再抠);入包前过 §8.3 色板检查器(报新增色 / 近色告警 / 超 48 上限拦截)。
|
||||
1. 出件顺序 = 先主角三态,再三 Boss,再其余四敌(同管线,无绘制差异)。
|
||||
2. 每件 = `pixel-1`/手修 2× 稿紧裁(去透明边、底中锚)→ 原样落 `game/assets/art/character/character_*`;**不做 `dom_block_resize`**。
|
||||
3. 入包前过 §8.3 色板检查器与 standee_asset_check(按 §8.2 显示档校验逐件档高与纵横倍率)。
|
||||
4. 桩盒宽 150 与 >150 溢出档的实装口径归代码批(art-bible §8.2 排版条);食人魔超宽 8px 为已接受项。
|
||||
|
||||
**5.4.1 主角(源 48 / 显示 96)**
|
||||
**5.4.1 主角(中档 144)**
|
||||
|
||||
- 形象 = character-design §5.4 形象锚逐字执行:蓝兜帽取下披肩、暖米白上衣、深棕工装裤、额前护目镜、半指手套、卷裤脚皮护膝、圆头挖掘铲、结构式冒险背包配朱红矩形旗、腰挂提灯
|
||||
- 已登记色:兜帽 `#2E86DE` / 上衣 `#EFE3C8` / 长裤 `#6B4A2E` / 皮革 `#8A5A34` / 眼 `#33261B` / 旗 `#C63D22` / 描边 `(40,19,13)`
|
||||
- 已落值(2026-10-28 用户裁):**铲刃浅铁灰 = `FOG_MEMORY_INK #C4C4C4`**;**提灯铜色 = `GOLD_DEEP #B07A14`**——两件均走换色合并、零额度(character-design §5.4 对这两件只有形容词,账见 §5.5 缺口 4)。代价已随裁定接受:`#C4C4C4` 一色两岗(语义岗 = 迷雾记忆墨 §6.5)、`#B07A14` 属金族(§4.2 金只说收益与兑现);两者均为角色随身件材质色,不进信息层与语义物件岗
|
||||
- 破例记账:背包 + 旗 = 主角对 §5.1「对称」的唯一破例组,旗面朝向恒定画右;轮廓凹凸预算按背包 + 旗整组记账
|
||||
- 朝向:战斗画面的侧身面向怪物形象属状态图(§5.4 生成顺序口径);重产沿用现役朝向,不重新裁定
|
||||
- 形象 = 现役稿即终稿,character-design §5.4 形象锚已兑现于现役素材;朝向沿用现役,不重新裁定
|
||||
- 色 = 以现役稿为准(§5.5 缺口 4 的额度账随色板议题另批处理)
|
||||
- 源 = `hero-ref/edit/normal_2x.png`(内容 88×92);attack/hit 用 `attack_2x.png`/`hit_2x.png`
|
||||
|
||||
**5.4.2 食人魔酋长 ogre_chief(源 74 / 显示 148)**
|
||||
**5.4.2 食人魔酋长 ogre_chief(大档 188)**
|
||||
|
||||
- 档位 = 精英档(形状仍守精英档:普通怪形 + 恰一处垂直延伸 = 头顶骨与羽冠,底盘不变;第一关关底 Boss 战,不升 Boss 形)
|
||||
- 甲档 = 轻甲 → 甲面覆盖率 ≈ 0:皮条 / 骨饰点缀、裸身为主,零甲板零符纹
|
||||
- 主色域 = 暖褐皮、圆肚;**暖褐皮 = `暖石 #B8A582`**(2026-10-28 用户裁:换色合并、零额度;体块主色带 §5.4 ④ L 0.22–0.45 校验 L≈0.387 ✓;character-design §5.5 该行原只有形容词)
|
||||
- 档位 = 大型(三场 Boss 战全归大型);形状仍守精英档剪影——普通怪形 + 恰一处垂直延伸(头顶骨与羽冠),底盘不变
|
||||
- 甲档 = 轻甲(皮条/骨饰点缀、裸身为主,零甲板零符纹)——现役稿已如此
|
||||
- 源 = `ogre_chief/pixel-1.png`(158×189);显示宽 157 超桩盒 150 为已接受项
|
||||
|
||||
**5.4.3 蛛化卓尔精灵 drider(源 90 / 显示 180)**
|
||||
**5.4.3 蛛化卓尔精灵 drider(大档 188)**
|
||||
|
||||
- 档位 = Boss(敌类中唯一允许锐角与不对称者,体量最大)
|
||||
- 甲档 = 魔法护甲 → 符纹饰带(暖白,覆盖率居中);蛛腹几丁质属天然甲壳,不计甲档
|
||||
- 独占:紫赤 `#8F2F6A`(色相 323°)+ 形状独占双通道;全项目色相 300°–345° 只许本件与 5.4.4 出现
|
||||
- **卓尔上体浅紫灰皮 = `#A79CB8`**(2026-10-28 用户裁:**新增 1 枚**,入 §4.1 分层①——在册紫族四枚全部语义岗占用且 L <0.22,无一可复用)。体块主色带 L 0.22–0.45 校验 L≈0.355 ✓;色相 264° 避 300°–345° 禁入带,紫赤独占仍归 `#8F2F6A`。**对比实测(本节新算,此前未给值)**:对深素底 `#282220` **6.04:1**、对舞台底 `#3E362C` **4.57:1**——两处均过 §4.1 图形 ≥3:1 门槛
|
||||
- 大体档:显示 180 > 150 → 允许向上溢出盒顶(§8.2 站桩排版③ 甲案;溢出量 N 由实装批用真图复算 x 区间定,血条仍落底)
|
||||
- 档位 = 大型;紫赤 `#8F2F6A` 色相独占 + 形状独占双通道维持现役稿
|
||||
- 甲档 = 魔法护甲(符纹饰带)——现役稿已如此
|
||||
- 源 = `drider/pixel-1.png`(140×189);皮色 `#A79CB8` 保留在板、本批不消费
|
||||
|
||||
**5.4.4 灰矮人石君王 duergar_king(源 94 / 显示 188)**
|
||||
**5.4.4 灰矮人石君王 duergar_king(大档 188)**
|
||||
|
||||
- 档位 = Boss(锐角与不对称,体量最大档)
|
||||
- 甲档 = 重甲 → 金属甲板(冷灰,覆盖率最大)
|
||||
- 独占:紫赤 `#8F2F6A` + 形状独占双通道;大地 / 岩石主题
|
||||
- 岩石板甲与灰皮(2026-10-28 用户裁,character-design §5.5 该行原只有形容词):**岩石板甲 = 重甲甲材质色,与 §5.3.3 同一枚 → 直接引 `#868C9A`,不另计额度**;**灰皮 = `STONE #786E62`**(按**次级皮**口径——石君王重甲覆盖率最大、板甲视同体块主色,故灰皮属次级皮、不受 L 0.22–0.45 约束;换色合并、零额度)
|
||||
- 大体档:显示 188 > 150 → 溢出盒顶同 5.4.3
|
||||
- 档位 = 大型(锐角与不对称);紫赤独占 + 岩石板甲维持现役稿色
|
||||
- 源 = `duergar_king/pixel-1.png`(133×189)
|
||||
|
||||
### 5.5 缺口(四条 · 缺口 1/2/4 于 2026-10-28 已裁转指路,缺口 3 结论已立)
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
| 素材 | 状态 | 需求文档 |
|
||||
|---|---|---|
|
||||
| 主角战斗小人(待机/攻击/受击三态) | ✅ B区站桩三态全接(2026-09-29 迭代 6 T3:normal/attack/hit 48px 档入包+挂事件切换+结算回 idle,短驻自回;棋盘 1.5×/脚锚25%/椭圆影在役不变;hit 态源=new-hit.pxo 已程序导出) | [character-design](character-design.md) + [art-prompts](art-prompts.md) §2 |
|
||||
| 八敌种形象(L2 熊地精=bugbear 复用,不单列) | ✅ B区站桩八敌真图服役(2026-09-29 迭代 6 T1/T2:pixel-1 源→48px 档统一比例/底中锚/众数投票降采样,character_enemy_<键>.png 按常量入位,字形回退退役为缺位兜底;素材不入 git 本地在盘)。八敌源档已定(36–94,art-bible §8.2),旧统一 48 档为过渡形态,新档重产落位归 PRD(tools/pixelize_candidates.py `STANDEE_TIER_H` 已改,产出与定档总图一致) | [art-bible](art-bible.md) §5 + [art-prompts](art-prompts.md) §3 |
|
||||
| 主角战斗小人(待机/攻击/受击三态) | ✅ B区站桩三态全接(2026-09-29 迭代 6 T3:normal/attack/hit 48px 档入包+挂事件切换+结算回 idle,短驻自回;棋盘 1.5×/脚锚25%/椭圆影在役不变;hit 态源=new-hit.pxo 已程序导出;2026-11-01 显示档改中档 144(源=手修 2× 三态稿,art-bible §8.2 v3.32),按档派生落位归本条实装批) | [character-design](character-design.md) + [art-prompts](art-prompts.md) §2 |
|
||||
| 八敌种形象(L2 熊地精=bugbear 复用,不单列) | ✅ B区站桩八敌真图服役(2026-09-29 迭代 6 T1/T2:pixel-1 源→48px 档统一比例/底中锚/众数投票降采样,character_enemy_<键>.png 按常量入位,字形回退退役为缺位兜底;素材不入 git 本地在盘)。2026-11-01 显示档换代=三档绝对高 96/144/188(art-bible §8.2 v3.32),源 = 原稿不降采样,按档派生落位归本条实装批 | [art-bible](art-bible.md) §5 + [art-prompts](art-prompts.md) §3 |
|
||||
| 意图准备动画三档 | ◐ | [art-bible](art-bible.md) §4.6.4 |
|
||||
|
||||
## P2 图标与格面(可与样张并行)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 营地场景 · 分层生图提示词卡 v1.2
|
||||
# 营地场景 · 分层生图提示词卡 v1.3
|
||||
|
||||
> **性质**:营地场景图(1280×800,一张可点击场景图 + 十张分层:背景层 1 + 围墙层 1 + 八个物件层)的出图执行侧文件。**场景级资产,与角色级 [art-prompts](art-prompts.md) 并列,两文件互不覆盖**。
|
||||
> **依据**:色彩与氛围 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`(本卡不复述色值);坐标与层序 = `artwork/2026-10-23-营地场景/布局草案-1280x800.html`;物件命名与消费链 = [asset-path-map](asset-path-map.md);画法与交付形态 = [art-bible](art-bible.md) §6.2。冲突时色彩以色彩脚本为准,画法以 art-bible 为准。
|
||||
@@ -171,10 +171,10 @@ A campfire made of a ring of grey stones around crossed stacked logs with a warm
|
||||
|
||||
---
|
||||
|
||||
## 5. 待裁(不阻塞出图,阻塞坐标表与定稿)
|
||||
## 5. 已裁(2026-11-01 用户裁,随迭代 47 卡④ 开工门销号;不再阻塞坐标表与定稿)
|
||||
|
||||
1. **帐篷尺寸**:现 300×170 待重定,重定后按比例重出
|
||||
2. **门扇高宽**:现 240×220,围墙带只有 43px 高;围墙独立成层后门扇叠在门洞上,两者高宽关系待裁
|
||||
3. **布告板位置**:现 y330 悬空在围墙下方,建议 `(845, 268, 130, 140)` 挂回围墙带
|
||||
4. **描边颜色追认**:本卡按奶油色(去底技术前提);若改参照物的深灰褐,需换去底方案
|
||||
5. **营地环境子板色值**:art-bible §4.1 为数值单点,色值待作者拍板,本卡不复述
|
||||
1. **帐篷尺寸**:300×170 追认现值(各素材第一张样本稿已过审为终稿;重定即按比例重出、不裁)
|
||||
2. **门扇高宽**:240×220 追认现值,坐标按门洞 x520–760 对齐(§2.3);围墙带桩身高 43px、门扇叠在门洞上的叠放关系随此锁定
|
||||
3. **布告板位置**:采纳建议值 `(845, 268, 130, 140)` 挂回围墙带(y330 悬空案弃);终值随坐标表(卡④ `camp_scene_layout.tres`)落盘
|
||||
4. **描边颜色**:奶油色追认——去底脚本「奶油描边膨胀 3px 当屏障」的技术前提随现值锁定(§1 注记同源);改深灰褐须换去底方案全批重出,不裁
|
||||
5. **营地环境子板色值**:不前置拍板——T1 量化场景实际用色后按 art-bible §4.1 两道门登记(语义检查+RGB 三通道差 ≥8/255),本卡仍不复述色值
|
||||
@@ -42,7 +42,7 @@
|
||||
- **兜帽披肩**:兜帽**不戴在头上**,取下披于肩上——露出棕色短发与整脸(表情可读),蓝色兜帽披在肩头仍是最近的"己方"识别点;三态同款披肩。
|
||||
- **圆头挖掘铲(武器)**:持铲不持剑——**铲 = 本作核心动词"挖安全格"的角色化**,挖掘与攻击共用同一动作语义(攻击表现即挥铲,后续攻击类表现优先围绕铲展开)。画法:圆头铲刃 + 木柄(木柄走木色族 `#8A5A34`,铲刃浅铁灰、不做冷灰重甲质感);**待机态 = 铲插立在地上、一只手扶在铲柄顶端**;攻击/受击态仍为手持。刀光/铲击光效/弹道不进精灵图(归战斗演出另立资产)。
|
||||
- **冒险背包与背包旗(身份件)**:背负**结构式冒险背包**——翻盖+皮扣带的帆布背包(暖卡其布面,皮革件走木色族),**宽 ≈1.5× 体宽**:双肩背负、两侧各略超出身形一点;**禁圆麻袋化**(措辞弃"鼓胀掳获袋")、**禁特里式 3× 超宽**(反面参照:旷野之息特里;正面参照:合金弹头双主角大背包、超级马里奥奇诺比奥队长)。**旗杆插在背包一侧**(不要求完整可见,**被头部遮挡一部分亦可**),旗子**尺寸大于头部**,短杆**矩形**旗旗面 = **旗朱红 `#C63D22`**(人设背包旗与棋盘封印色**脱钩**——封印色仅作用于棋盘格封印旗,色史归 art-bible §4.1;人设旗形为矩形非三角,以 normal/hit 定稿成图为基准),把"宣告落旗"的角色化焊在人设上,主角即背包旗持有者(**动作呈现名 = 封印**,见 art-bible §3.2;背包旗**不**视作封印印记的携带形态——旗面旗朱红矩形,与棋盘封印黑旗形色均不同,仅保留"主角即背包旗持有者"的身份叙事)。三条纪律:①旗面红=旗朱语义**仅旗非服饰**(负向 `no red clothing` 不拦截正向 `red flag`——红服饰禁令与红旗的边界由正向句显式锁色值;封印黑仍为棋盘格封印旗专属色,圣经 §4.1 不变);②背包+旗 = 主角对 §5.1"对称"的**唯一破例组**(语义配件非装饰),旗面朝向恒定画右、跨素材一致;③轮廓凹凸预算按"背包+旗"整组记账,样张(圣经 §1.4)验证背负后剪影仍窄、仍可辨、不超格宽。
|
||||
- **护目镜与腰灯**:**护目镜推在额前**(兜帽披肩后直接戴于额上,不戴眼上——眼睛是表情载体;皮革带+圆镜片,防尘挖掘者的职业信号);**提灯挂腰间,定案不手持**(小号铜色提灯,**不点亮**;空间光照属圣经 §2 明度分层允许域,**提灯点亮为候选**,点亮形制(光晕半径/暖色值/与舞台光照关系)归整屏样张重议;头部饰品唯一化,头灯方案已弃)。
|
||||
- **护目镜与腰灯**:**护目镜推在额前**(兜帽披肩后直接戴于额上,不戴眼上——眼睛是表情载体;皮革带+圆镜片,防尘挖掘者的职业信号);**提灯挂腰间,定案不手持**(小号铜色提灯,**不点亮**;空间光照走圣经 §2 明度分层,**提灯点亮为候选**,点亮形制(光晕半径/暖色值/与舞台光照关系)归整屏样张重议;头部饰品唯一化,头灯方案已弃)。
|
||||
- **衣着细节**:**棕色半指手套**(挖土护手,露指保灵巧读感)+ **裤脚卷起、棕色皮护膝**(挖掘跪地的工装感)——小件走皮革木色族,不占剪影凹凸预算,不新增语义色。
|
||||
- **三态**:① 待机 = 窄、直立、对称(§5.1 小人原型,含背包、旗与提灯记账);② 攻击 = 允许破对称,整体仍窄、不超格宽;③ 受击 = 无血红、无伤害数字(伤害数字归 HUD 层,不进精灵图)。
|
||||
- **随身件 = 秘宝形象候选池**:铲、旗、背包、提灯四件随身件是未来秘宝设计的**形象候选池**——道具实装可借用主角身上已有的同款(彩蛋闭环:玩家在道具栏认出主角身上的物件);具体道具语义/数值归秘宝 GDD,人设侧义务 = 四件画得出来、认得出。
|
||||
@@ -67,7 +67,7 @@
|
||||
- **主色域 = 物种本色**,不受圣经 §4.2 语义色禁则约束(该禁则管格面与 UI 的逻辑色,角色身体主色豁免);**两例外**:禁入带 300°–345° 全角色禁入(仅 drider/duergar_king 两 Boss 独占)、蓝族禁入敌方主色(§5.4 同款)。
|
||||
- **L2 熊地精 = bugbear 复用**(第二关三行,显示名熊地精系,非独立键):同剪影**换色板不换形**(色板替换走脚本管线——原则三"先换色板"的角色侧应用);变体主色避小人蓝族与禁入带。
|
||||
- 每敌种出 2–3 版候选(清单 §2.2);受击反馈/死亡表现/顺位标记三类呈现下限见圣经 §4.6.4 资产需求行。
|
||||
- **立绘源档高(定值单点 = 圣经 §8.2 立绘源档表,本档指路不复述)**:八敌 36–94 逐键定档 + 主角 48 不变、显示一律 ×2 整数档(NEAREST)——**数值权威一律圣经 §8.2**(定值单点纪律,圣经 §8 头注);定档经三体型约束校验(§5.2)确认;派生管线与排版规范同归圣经 §8.2。
|
||||
- **立绘显示档(定值单点 = 圣经 §8.2 站桩立绘显示档条,本档指路不复述)**:三档绝对高 96/144/188,逐件归档、源档与缩放规则一律见圣经 §8.2——**数值权威 = 圣经 §8.2**(定值单点纪律,圣经 §8 头注);归档经三体型约束校验(§5.2)确认;派生管线与排版规范同归圣经 §8.2。
|
||||
- **钩爪恐魔「宽≥高」豁免登记(待样张终审)**:定档源 58 高窄条束状剪影(长臂圆钩),宽高比 <1,与 §5.1 普通怪「近圆墩形宽≥高」冲突——按体量=生物天性(§5.2)登记豁免:以长臂束状轮廓为识别锚,不强行压成墩形;样张(圣经 §1.4)终审复验灰度剪影可辨性。
|
||||
|
||||
## 5.6 意图准备动画三档(规格承载节)
|
||||
|
||||
@@ -249,18 +249,17 @@
|
||||
| **文档域** | gdd_lint、doc_link_audit、doc_term_audit、流水线闸 | `game/` 内的 `.md` 与 `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 的文件性质推出应验的域(`game/` 内 `.md` 归文档域),再校验回执的 `domain` 字段与新增红。代码域跑前先过**素材齐盘预检**——`game/assets/art/**` 与一切 `*.import` 边车都不入 git(2026-09-29 裁定),worktree 冷检出必空盘,空盘态冒烟 V1 成片假红、渲染闸更是一段 golden 都不比。
|
||||
口径三条:**域划分的唯一出处是 `tools/run_all_gates.sh`**(`--code`/`--docs`/不带参数=全量),本表是它的现状登记、不是第二份清单;改闸件本身或一次改动跨两域 → 跑全量档(§6.2 共享件例外);开发期手动档按本批改动域选 `--code`/`--docs`,合回后 CI 恒跑全量档。代码域跑前先过**素材齐盘预检**——`game/assets/art/**` 与一切 `*.import` 边车都不入 git(2026-09-29 裁定),worktree 冷检出必空盘,空盘态冒烟 V1 成片假红、渲染闸更是一段 golden 都不比。
|
||||
|
||||
### 车道分层执法(2026-10-26 用户裁「按路径切车道,取代一切改动走 worktree」)
|
||||
### 车道分层执法(2026-10-31 用户裁恢复「一切改动走 worktree」)
|
||||
|
||||
改动车道与改动域同一判据(文件性质):代码面=worktree 批车道,文档面=主工作区直改车道。执法全机械——判据只有文件路径性质与 git 图,不含「改动大小」等语义判断;「预期大改」由用户开工点名自愿开 worktree,不进规则。
|
||||
一切改动走独立 worktree,主工作区只作合并落点。执法全机械——判据只有 git 图与路径(豁免面 `.zcode/`、`.qoder/` 不入 git,主区直改),不含「改动大小」等语义判断。
|
||||
|
||||
| 执法层 | 载体 | 职责 | 判据 |
|
||||
|---|---|---|---|
|
||||
| **git pre-commit 钩子** | `tools/hooks/pre-commit`(版本化本体,`core.hooksPath` 挂载;在位性由 run_all_gates.sh 跑批自检——失位即红) | 直改车道闸机检:staged 不含代码面的提交,提交点现场跑 `run_all_gates.sh --docs`(秒级文本扫描),绿落地红拦;staged 含代码面时主工作区=拦(代码面唯一合法路径=worktree 批合回)、任务 worktree=跳过(归批末闸+合回闸,不打扰迭代批中间提交) | staged 文件性质+主区/worktree 判定(git-dir 与 common-dir 同一=主区) |
|
||||
| **worktree-guard**(ZCode PreToolUse) | `.zcode/hooks/worktree-guard.sh`(本机件,不入 git) | agent 侧车道纪律:主工作区代码面的 Edit/Write 与 staged 含代码面的 commit 拦截;`git commit --no-verify` 拦截(该旗标仅属用户);合回闸回执六况校验 | 路径性质+分支名+回执 JSON+git 图 |
|
||||
| **worktree-guard**(ZCode PreToolUse) | `.zcode/hooks/worktree-guard.sh`(本机件,不入 git) | agent 侧车道纪律:主工作区 Edit/Write 与主区非 merge `git commit` 拦截(豁免 `.zcode/`、`.qoder/`;worktree 内目标快速放行;合并收尾提交凭 MERGE_HEAD 豁免);`git commit --no-verify` 拦截(该旗标仅属用户);iter 分支合回一卡一提交数卡校验;规则闸 | 路径性质+分支名+MERGE_HEAD+git 图 |
|
||||
|
||||
取舍依据:失败代价不对称——docs 红是死链/术语/格式,下次任何跑批对基线比对必暴露、秒级修复,故文档面轻执法(commit 点闸、无回执流程);代码面红是运行时行为坏,保留「worktree+批末域闸+回执硬绑定」全套。git 层钩子对一切提交者生效(含用户手工提交,紧急出口 `--no-verify`);ZCode 层只约束 agent。子代理覆盖面(2026-10-26 冷启动评估实测):子代理的工具调用不经过 ZCode 层守卫(代码面 Edit 未被拦、`--no-verify` 探针未被拦)——ZCode 层实际只覆盖主会话工具调用,git 层因此是唯一覆盖一切提交者的执法层;残余暴露面=蓄意 `--no-verify`(pre-commit 族钩子被该旗标整族跳过,git 层无法自防),属故意越权而非遗忘型失误,与防遗忘的设计目标正交。
|
||||
验证收拢为合回后本地 CI 全量(post-merge 触发),commit 点零执法;主工作区防污染由 ZCode 层守卫承担——只约束 agent,用户手工改动不受限。子代理覆盖面(2026-10-26 冷启动评估实测):子代理的工具调用不经过 ZCode 层守卫——ZCode 层实际只覆盖主会话工具调用;残余暴露面=子代理写主工作区与蓄意 `--no-verify`(该旗标整族跳过 git 层钩子),属故意越权而非遗忘型失误,与防遗忘的设计目标正交。
|
||||
|
||||
### 三条判定硬闸现行状态
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# 流程规范(Dungeon Sweeper)
|
||||
|
||||
> 约束范围:一切批次的流程纪律——车道(代码面 worktree 批/其余主区直改)、五段流水线、批次工作流、测试执行细则、像素件工作纪律。
|
||||
> 约束范围:一切批次的流程纪律——车道(一切改动走独立 worktree,主区只作合并落点)、五段流水线、批次工作流、测试执行细则、像素件工作纪律。
|
||||
> 分工:硬性红线的一句话索引在 AGENTS.md「硬性红线」;本文承载其操作细则;批次开工前通读本文。
|
||||
|
||||
**开工任何批次前必须通读本文。**
|
||||
|
||||
## 1. 分支纪律(车道铁律)
|
||||
## 1. 分支纪律(worktree 铁律)
|
||||
|
||||
一切面主工作区直改+直提(2026-10-30 用户裁本地 CI 化:验证挪到合回后,主区直提不再拦任何面;判据与 §3 第 8 条改动域划分同一):
|
||||
主工作区(master 检出)只作合并落点,一切开发/设计改动禁止直接发生在主工作区(2026-10-31 用户裁恢复;2026-10-30 的「主区直改+直提」车道与 commit 点 pre-commit 直改闸随之退役;验证模型不动——合回后本地 CI 仍是唯一强制验证点):
|
||||
|
||||
1. **主区直改车道(一切面)**:`game/`(含代码面=`game/` 下非 `.md` 的行为载体)、`design/`、`reports/`、`tools/`、根规则与清单文件、`.zcode/`、`.qoder/`——直接改、直接提交。验收分两层:commit 点 git pre-commit 钩子(`tools/hooks/pre-commit`,经 `core.hooksPath` 挂载,在位性由 run_all_gates.sh 跑批自检)现场跑 `tools/run_all_gates.sh --docs`(秒级文本扫描)绿即落地红即拦,合并收尾提交跳过(MERGE_HEAD 在,内容归合回后 CI);代码面的全量验证归合回后本地 CI(下条)。开发期纪律不变:卡内只测自己改的东西、出红只跑红件最小单元(§6)
|
||||
2. **合回后验证(本地 CI,2026-10-30 用户裁)**:git merge 合回 master 落地后 post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh merge` 对 master 尖跑一次全量域闸——唯一强制全量验证点;CI 红态下每个修复提交经 post-commit 钩子(`tools/hooks/post-commit`)自动追绿复验,绿态与无状态零触发。重跑判据:同一份字节禁止第二次人手跑;有意义的重跑只剩「验证修复」一类。CI 红不回滚合并——红项记 `game/tests/.gates/ci-status.json`,修复提交追绿;开工先读 CI 状态,红未修不开新批。跑批起手 `--editor --quit` 刷类名缓存(在册坑:.godot 缓存不入 git,合并后失配假红假绿)——原「合并后零复跑」的秒级动作由跑批件接管。并跑语义=跑完补跑(coalesce):跑批期间新触发不中断当前轮,轮尾对最新尖端补跑,绝不逐笔各跑一轮
|
||||
3. **worktree 批车道(大批约定,非强制)**:迭代实现批与大体量改动按约定仍走独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`;建区后立刻跑 `tools/setup_worktree_userdir.sh`——给该区独立 Godot user 目录(测试夹具、试玩日志、截图产物不与其他区共用;主区不生成,真实档保持原位)。收口顺序固定为**冻结 → 件核 → 并 master → 重塑 → 主区合回 → CI 验证**——代码与断言改完即冻结,全仓件核 grep 查残留;master 已被别的批次推进时先 `git merge master` 解冲突;**重塑**=迭代分支(iter<N> 形)合回前执行 `python tools/reshape_cards.py`,把分支线性史按卡末态快照压成一卡一笔(可选追加一笔纯文档收尾),master 台账由此每卡一行——工具自带「重塑前后树逐字节一致」断言与 backup 分支,不改任何文件内容;非迭代工程小批免重塑;然后主工作区 `git merge --no-ff <任务分支>`(合回守卫对迭代分支数卡校验,未归卡提交拦下),合并落地 post-merge 钩子触发 CI,红则修复提交(主区直提)追绿,全绿后 `git worktree remove`+删分支。游戏类测试(冒烟/单测/MC/探针)批内默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
|
||||
4. **执法分层(全机械,零自觉依赖)**:①git pre-commit 钩子——一切提交的文档域闸机检(合并收尾提交豁免)。②post-merge/post-commit 钩子——本地 CI 触发(仅主工作区;任务 worktree 内的合并不触发)。③`.zcode/hooks/worktree-guard.sh`(PreToolUse,本机件不入 git)——agent 侧:`git commit --no-verify` 拦截(该旗标仅属用户)、iter<N> 分支合回的一卡一提交数卡校验、规则闸(写 design//game/ 前核对会话规约读取记录)。git 层钩子对一切提交者生效(含用户手工提交,紧急出口 `--no-verify`);ZCode 层只约束 agent
|
||||
1. **worktree 车道(一切面)**:每批先建独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`;建区后立刻跑 `tools/setup_worktree_userdir.sh`——给该区独立 Godot user 目录(测试夹具、试玩日志、截图产物不与其他区共用;主区不生成,真实档保持原位)。所有改动与提交都在 worktree 内自由进行——commit 点零钩子检查(`tools/hooks/pre-commit` 已删除)。豁免面仅 `.zcode/`(钩子与配置)与 `.qoder/`(本机 harness 配置)允许主工作区直改——两份实测均不入 git(走 worktree 反而带不进去,改动只对本机生效)
|
||||
2. **收口顺序(固定)**:**冻结 → 件核 → 并 master → 重塑 → 主区合回 → 删区**——代码与断言改完即冻结,全仓件核 grep 查残留;master 已被别的批次推进时先在 worktree 内 `git merge master` 解冲突(必须在合回之前,合回后才并=CI 验的是中间态);**重塑**=迭代分支(iter<N> 形)合回前执行 `python tools/reshape_cards.py`,把分支线性史按卡末态快照压成一卡一笔(可选追加一笔纯文档收尾),master 台账由此每卡一行——工具自带「重塑前后树逐字节一致」断言与 backup 分支,不改任何文件内容;非迭代批免重塑;然后主工作区 `git merge --no-ff <任务分支>`——这是主区唯一合法写动作(合回守卫对迭代分支数卡校验,未归卡提交拦下)。**合回落地即 `git worktree remove`+删分支(合回即删,2026-09-19 原裁);post-merge 钩子已在合回落地时自动触发 CI,红态不回滚合并——新开修复 worktree(`fix-<任务名>` 分支,非 iter<N> 形免重塑免数卡)修复提交再合,落地复验追绿后删修复区**。游戏类测试(冒烟/单测/MC/探针)批内默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
|
||||
3. **合回后验证(本地 CI,2026-10-30 用户裁)**:git merge 合回 master 落地后 post-merge 钩子(`tools/hooks/post-merge`)后台触发 `tools/ci_runner.sh merge` 对 master 尖跑一次全量域闸——唯一强制全量验证点;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(红态修复走新开修复 worktree,`fix-<任务名>` 分支,非 iter<N> 形免重塑免数卡;post-commit 钩子保留为红态主区直提兜底触发,绿态与无状态零触发)。重跑判据:同一份字节禁止第二次人手跑;有意义的重跑只剩「验证修复」一类。CI 红不回滚合并——红项记 `game/tests/.gates/ci-status.json`,开工先读 CI 状态,红未修不开新批。跑批起手 `--editor --quit` 刷类名缓存(在册坑:.godot 缓存不入 git,合并后失配假红假绿)。并跑语义=跑完补跑(coalesce):跑批期间新触发不中断当前轮,轮尾对最新尖端补跑,绝不逐笔各跑一轮
|
||||
4. **执法分层(全机械,零自觉依赖)**:①`.zcode/hooks/worktree-guard.sh`(PreToolUse,本机件不入 git)——agent 侧:主工作区 Edit/Write 与主区非 merge `git commit` 拦截(豁免 `.zcode/`、`.qoder/`;worktree 内目标快速放行;合并收尾提交凭 MERGE_HEAD 豁免)、`git commit --no-verify` 拦截(该旗标仅属用户)、iter<N> 分支合回的一卡一提交数卡校验、规则闸(写 design//game/ 前核对会话规约读取记录)。②post-merge/post-commit 钩子——本地 CI 触发(仅主工作区;任务 worktree 内的合并不触发)。③git 层不设车道钩子(pre-commit 直改闸已退役)——用户手工改动不受限;ZCode 层只约束 agent(子代理不经守卫层)
|
||||
|
||||
## 2. 五段流水线
|
||||
|
||||
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸」(车道细则见 §1)。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。
|
||||
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI」(车道细则见 §1)。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。
|
||||
|
||||
| 段 | 入口技能 | 产出 | 出入口铁律 |
|
||||
|---|---|---|---|
|
||||
@@ -40,7 +40,7 @@
|
||||
3. **用户发开工令才动手**——需求卡片经用户批准(U0)后方开工
|
||||
|
||||
**环境与基线**
|
||||
4. **代码面批独立 worktree+拷导入缓存**——触及代码面的批次按 §1 建 worktree;先拷主区 `.godot/` 目录与字体 `.import` 边车再跑测试,否则全量重导入资源近似卡死。检出工作区零测试,直接开工(2026-10-22 频率规则);闸壳每次跑批先自检/自愈本区 user 目录隔离(§1 第 1 条),建不起来即拒跑;直改车道(非代码面)无此项
|
||||
4. **一切批独立 worktree+拷导入缓存**——按 §1 建 worktree;涉代码域跑闸或测试的批先拷主区 `.godot/` 目录与字体 `.import` 边车(纯文档批免拷),否则全量重导入资源近似卡死。检出工作区零测试,直接开工(2026-10-22 频率规则);闸壳每次跑批先自检/自愈本区 user 目录隔离(§1 第 1 条),建不起来即拒跑
|
||||
5. **涉数值改动先采改前读数**——与改后同随机种子配对比较并写明逐项比对口径;无数值改动禁跑模拟
|
||||
|
||||
**实施**
|
||||
@@ -48,8 +48,8 @@
|
||||
7. **验收改动先行声明**——验收变化(基线换代/探针增改/旧断言改判)预先写进需求卡片,禁事后补
|
||||
|
||||
**完成**
|
||||
8. **合回后 CI 验证(worktree 批;主区直改小批的验收在 commit 点由 git 钩子执行,见 §1 第 1 条)**——主工作区 `git merge --no-ff <任务分支>` 落地后 post-merge 钩子自动跑全量域闸(不带参数档,02 全套件),判定与红项落 `game/tests/.gates/ci-status.json`。全量是唯一强制档:跨卡触面、探针与未申报套件的消费漏网只有全量闸能抓(迭代 44 实证),关联档选测的盲区由它兜底。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分(含选闸算法与套件族映射表)**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。开发期手动跑批按需选档(按文件性质判域:代码面→`--code`,文档件→`--docs`,两类都有→全量;`--code` 关联档选测——02 只跑「需求卡片申报 ∪ diff 推导」并集套件、00 冒烟/01 系统测试/09 静态扫描三件保险恒跑、横切面回落全量——算法见闸脚本),手动跑不构成验收。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占 CI 全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;闸真崩溃时 CI 判红在案(跑不出回执=verdict 红,fail-closed 语义保留)
|
||||
9. **重跑语义(2026-10-30 用户裁,取代原「每批一次/合并后零复跑」口径)**——同一份字节禁止第二次人手跑;允许的重跑只剩两类:① CI 红后的修复提交(post-commit 钩子自动追绿复验,每笔修复至多触发一轮);② 用户自发发起的全量回归。「跑都跑了再跑一遍保险」「顺手改点再补一跑」都不构成重跑理由。类名缓存刷新与开机确认由 CI 跑批起手接管(§1 第 2 条),合并后无手工动作
|
||||
8. **合回后 CI 验证(唯一强制验证点)**——主工作区 `git merge --no-ff <任务分支>` 落地后 post-merge 钩子自动跑全量域闸(不带参数档,02 全套件),判定与红项落 `game/tests/.gates/ci-status.json`。全量是唯一强制档:跨卡触面、探针与未申报套件的消费漏网只有全量闸能抓(迭代 44 实证),关联档选测的盲区由它兜底。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分(含选闸算法与套件族映射表)**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。开发期手动跑批按需选档(按文件性质判域:代码面→`--code`,文档件→`--docs`,两类都有→全量;`--code` 关联档选测——02 只跑「需求卡片申报 ∪ diff 推导」并集套件、00 冒烟/01 系统测试/09 静态扫描三件保险恒跑、横切面回落全量——算法见闸脚本),手动跑不构成验收。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占 CI 全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;闸真崩溃时 CI 判红在案(跑不出回执=verdict 红,fail-closed 语义保留)
|
||||
9. **重跑语义(2026-10-30 用户裁,取代原「每批一次/合并后零复跑」口径)**——同一份字节禁止第二次人手跑;允许的重跑只剩两类:① CI 红后的修复合回(post-merge 钩子复验,每轮修复至多触发一轮);② 用户自发发起的全量回归。「跑都跑了再跑一遍保险」「顺手改点再补一跑」都不构成重跑理由。类名缓存刷新与开机确认由 CI 跑批起手接管(§1 第 2 条),合并后无手工动作
|
||||
10. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §12
|
||||
11. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,**另禁写 `game/tests/README.md`(能力清单,归写卡段维护,test-conventions §8.9)**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务+实现决定记录——超白名单/超卡片明列范围事项逐条记「决了什么/为什么/代价/撤销方式」,全部按卡执行时只写一行「零拍板」)、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由工程/文档设计改动批(直改类)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归工程/文档设计改动批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注
|
||||
12. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
## 4. 验证时机(合回后本地 CI 制,2026-10-30 换代)
|
||||
|
||||
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6);主区小改孤跑验证后直提。全量验证归合回后的本地 CI:git merge 合回 master 触发 post-merge 钩子跑 `tools/run_all_gates.sh` 不带参数档(两域全量,02 全套件——跨卡触面与关联档选测的盲区由全量兜底),结果落 `game/tests/.gates/ci-status.json`;CI 红态下修复提交经 post-commit 钩子自动追绿。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
|
||||
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6)。全量验证归合回后的本地 CI:git merge 合回 master 触发 post-merge 钩子跑 `tools/run_all_gates.sh` 不带参数档(两域全量,02 全套件——跨卡触面与关联档选测的盲区由全量兜底),结果落 `game/tests/.gates/ci-status.json`;CI 红态下修复合回落地即由 post-merge 钩子复验追绿(修复走新开 `fix-<任务名>` 修复 worktree,process-conventions §1)。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
|
||||
行为探针使用随机化玩家行为(封印/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。
|
||||
|
||||
## 5. 测试函数怎么写——验收清单条目的落码规范
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| `术语表.md` | 全库专用术语的唯一对照表——危险格/安全格/同调位/RAP/GP/真视等定义+退役词对照(2026-10-11 建立,v1.23) |
|
||||
| `待办清单.md`(仓库根,不属本树) | 全项目待办事项的唯一登记处;各系统文档的「待办」节只留指路存根 |
|
||||
| `体验验证清单.md`(仓库根,不属本树) | 需要真人试玩观测的验证项登记处(与待办清单分工:待办清单是"要拍板的事",本篇是"要玩过才知道的事");每条注明结论回流路径 |
|
||||
| `08-Alpha版本标准.md` | Alpha 版本包含哪些东西的清单——里程碑 M3 的完成标准;只列条目不写内容,定义一律指路各权威文档(**已定稿 v1.20**,2026-10-13) |
|
||||
| `08-Alpha版本标准.md` | Alpha 版本包含哪些东西的清单——里程碑 M3 的完成标准;只列条目不写内容,定义一律指路各权威文档(**已定稿 v1.21**,2026-10-13) |
|
||||
|
||||
## systems/ 五个循环
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **一句话定位**:本文定义这个项目的 **Alpha 版本包含哪些东西**——它是里程碑 M3 的完成标准,也是判断"还差什么"的对照表。
|
||||
> **本文只列条目,不写内容**:每样东西是什么、规则怎么走,一律归各自的权威文档,本文只给指路链接。想知道某样东西的定义,请点链接,不要在本文里找解释。
|
||||
> **状态**:**已定稿(v1.20)**——2026-10-13 用户令定稿,历次升版明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **状态**:**已定稿(v1.22)**——2026-10-13 用户令定稿,历次升版明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **成文**:2026-10-13。
|
||||
|
||||
## 本文怎么用
|
||||
@@ -104,7 +104,7 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 开始覆盖层 | —— | 在役 |
|
||||
| 选档界面(3 存档位) | —— | 在役(迭代 33 卡③——空槽新档/有档进档/删档二次确认+摘要三项;2026-10-22 用户裁增行〔反哺 #8〕) |
|
||||
| 营地(天赋树 / 锻造工坊 / 秘宝匣 / 图鉴) | [camp](ux/camp.md) | 在役(场景图与七物件点选随营地交互重做列入 Alpha,见下行) |
|
||||
| 营地交互重做(五卡:存档制数据层/主菜单三入口/选档界面/营地场景图/Esc 浮窗与营地顶栏) | [camp](ux/camp.md)+[需求澄清](../../reports/需求澄清-营地交互重做-2026-10-18.md) | 部分在役(卡①②③ 已随迭代 33 实装合回,2026-10-22;卡④营地场景图 2026-10-21 用户令退回美术设计段,美术生成方案提案 2026-10-22 成文,余三个用户侧前置未销号:像素规格执行(Q6 甲过渡口径已裁)、点选反馈设计稿、参照物;卡⑤ Esc 浮窗随卡④ 代码波同行、未编排。2026-10-21 用户裁:五卡整体列入 Alpha。出口=五卡各自验收标准通过+营地屏用户目检通过) |
|
||||
| 营地交互重做(五卡:存档制数据层/主菜单三入口/选档界面/营地场景图/Esc 浮窗与营地顶栏) | [camp](ux/camp.md)+[需求澄清](../../reports/需求澄清-营地交互重做-2026-10-18.md) | 部分在役(卡①②③ 已随迭代 33 实装合回,2026-10-22;卡④营地场景图美术设计已闭合(样张过审 2026-10-31)、v3.0 现行有效卡重立,与卡⑤ Esc 浮窗同编迭代 47(待开工)。2026-10-21 用户裁:五卡整体列入 Alpha。出口=五卡各自验收标准通过+营地屏用户目检通过) |
|
||||
| 生涯页 | [生涯](systems/跨局循环/生涯.md) | 在役(期一) |
|
||||
| 关卡地图屏 | [level-map](ux/level-map.md) | 在役 |
|
||||
| 战斗屏 | [battle](ux/battle.md) | 在役 |
|
||||
@@ -135,7 +135,7 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 机器可读的呈现断言(对比度 / 结构) | ——(清单唯一出处=process-conventions 运行与验证节) | 在役 |
|
||||
| 分辨率两档(1280×800 / 1920×1080) | [HUD框架](systems/表现与支撑/HUD框架.md) | 在役 |
|
||||
| 绘制调用数达标 | —— | 在役 |
|
||||
| 真人试玩轮次 | [体验验证清单](../../体验验证清单.md) | 在役(义务维持;当前轮次未清:㉚/㉛ 未核销,迭代 34 三条目检项待登记) |
|
||||
| 真人试玩轮次 | [体验验证清单](../../体验验证清单.md) | 在役(义务维持;当前轮次未清=㉚–㊸ 各条待用户核销——迭代 32 起各批实机目检项登记不阻塞,迭代 42 无玩家可见改动未登记) |
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +147,7 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 棋盘瓦片与格面资源图标 | [美术圣经](../art/art-bible.md) | 在役 |
|
||||
| 未开格类型微光(五组色相+明度档) | [HUD框架](systems/表现与支撑/HUD框架.md) | 在役(明度档 provisional 值登记见 art-bible 的 git 提交史,复核转正归战斗一屏样张) |
|
||||
| 棋盘观感(第二 / 三关亮度与对比度) | [待办清单](../../待办清单.md) | 待做(开工前置=先做素材轴/UI 轴对照截图定性,再按定轴修复;旧卡 v1.4 存 reports/ 作素材、非已写卡状态,重启须重新立卡;出口=用户实机目检第二、三关不再判「看着费劲」) |
|
||||
| 立绘清晰度回退评估(是否改用高清图) | [角色设计](../art/character-design.md) | 待做(评估已结——2026-10-14 用户裁维持像素风、升级精细手绘像素、不回退高清图,裁决录=[美术审计-cursor会话存档](../../reports/美术审计-cursor会话存档-2026-10-14.md) §2.2;Alpha 范围=主角+三个 Boss 按新口径重产,其余敌种当前可读则保留到 Beta;出口=用户目检八敌体量关系通过且主角与三个 Boss 重产完成) |
|
||||
| 立绘显示清晰度(原稿分辨率+三档显示) | [角色设计](../art/character-design.md) | 待做(评估已结——不回退高清、维持像素风;终态方案=保留原稿分辨率、入包禁降采样、显示按三档绝对高 96/144/188 缩放、现有形象即终稿;裁决录=待办清单本条+art-bible §8.2;出口=9 件按档落位+用户目检清晰度与同屏体量顺序通过) |
|
||||
| 敌人立绘(全敌种) | [角色设计](../art/character-design.md) | 在役 |
|
||||
| 主角立绘三态 | [角色设计](../art/character-design.md) | 在役 |
|
||||
| 状态图标 | [美术圣经](../art/art-bible.md) | 在役 |
|
||||
@@ -157,7 +157,7 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 站桩帧动画 | [待办清单](../../待办清单.md) | 待做(列入 Alpha 定版前置清单:定版前完成最小版,路线预选拆件驱动;未完成则顺延 Beta;不占功能迭代) |
|
||||
| 全局纹理过滤设为邻近采样 | [待办清单](../../待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:default_texture_filter=0+平滑渐变件三处显式 LINEAR 白名单,翻转面全枚举闭合) |
|
||||
| UI 字体(全局像素字,Fusion Pixel 单套单字重) | [美术圣经](../art/art-bible.md) §7.4 | 在役(2026-10-24 用户裁现状即终稿——拉丁沿用自带字形不另接专件、不上第二字重;信息层级手段=字号档差+墨色深浅) |
|
||||
| 战斗一屏样张(界面美术的终稿闸) | [美术圣经](../art/art-bible.md) §1.4 | 待做(Alpha 硬前置:站桩帧动画的定版前置与主角+三 Boss 重产都依赖它;出口=用户目检通过的战斗屏样张,并产出尺寸档/色板/明度档清单) |
|
||||
| 战斗一屏样张(界面美术的终稿闸) | [美术圣经](../art/art-bible.md) §1.4 | 部分在役(规格半已闭合=目标规格清单已落 art-bible 五张规格表〔2026-10-30 合回〕,五项实装定值与样张机检随迭代 43 入库;未结半=出口未达——方向稿用户目检(体验验证清单㊶)先行,样张整体过闸随其后。站桩帧动画的定版前置与主角+三 Boss 重产依赖照旧) |
|
||||
| UI 氛围层与九宫格切片观感修 | [待办清单](../../待办清单.md) | 部分在役(光柱硬边已修=连续 alpha 渐变;未结半=九宫格切片错位——Alpha 内做最小修复:核对素材尺寸,必要时重出九宫格素材,全套素材重出排 Beta;出口=结算屏与图例屏错位消失,渲染闸+用户目检通过) |
|
||||
| UI 元素细节修(滑轨填充 / 图标垫块 / 留白 / 开发件外泄) | [待办清单](../../待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:①滑轨填充随迭代 25 注销;余②图标垫块明度差降档/③商店+营地布局修/④诊断钮迁调试浮窗 全落地) |
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
>
|
||||
> **兑付频率(先看这条,它是三条渠道的共同前提)**:奇物**不是打一场战斗给一份**。关内的普通战斗节点打完了什么也不给;一关之内奇物**只兑付一次**,触发点是**关底 Boss 被击败**——而按 [冒险结构](冒险结构.md) 的定义,打掉关底 Boss 恰好就等于"通过这一关",这两件事是同一个时刻,不是一条链上的两步。
|
||||
>
|
||||
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.2。本文件是奇物的唯一定义文档。
|
||||
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.3。本文件是奇物的唯一定义文档。
|
||||
>
|
||||
> 数值纪律:本文件只写概念、取值域与指路;每件给多少、效果是什么一律不进正文,事实源是 `data/tables/feats.tres` 与 `data/tables/adventure.tres`。
|
||||
|
||||
@@ -99,25 +99,25 @@
|
||||
|
||||
### 换图系(7 件,设计定稿,待实现)
|
||||
|
||||
换图系把「弃图刷新」从坏图逃生改成主动构筑行为。两件基座分别解决 RAP 的来源与价格;后续专长让成功刷新产生输出、生存或资源收益。七件不要求一次拿齐,不设强制先拿基座、集件标签或集件奖励。
|
||||
换图系把「弃图刷新」从坏图逃生改成主动构筑行为。两件基座分别解决 RAP 的来源与价格;后续专长让成功刷新产生输出、生存或 GP 资源收益。七件不要求一次拿齐,不设强制先拿基座、集件标签或集件奖励。
|
||||
|
||||
| 层 | 代号 | 规则 | 行为职责 |
|
||||
|---|---|---|---|
|
||||
| 基座 | 掘进储能 | 每次付费挖格后额外获得 RAP;安全格与危险格同口径 | 持续挖图也能准备刷新,不再必须少挖停手 |
|
||||
| 基座 | 折价换图 | 弃图刷新的 RAP 成本减半 | 同一份储备支持更多刷新 |
|
||||
| 输出 | 弃图轰击 | 成功刷新后,在本轮战斗段对全体存活敌人造成一次伤害 | 刷新成为直接进攻行为 |
|
||||
| 输出 | 换图蓄势 | 成功刷新攒层,层数有上限;下一次攻击附加全部层数对应的伤害后清零,战斗结束清空 | 连续换图蓄成一次单点爆发 |
|
||||
| 生存 | 换图庇护 | 成功刷新后,在本轮授予段获得护盾 | 用刷新预防后续伤害 |
|
||||
| 生存 | 弃图休整 | 成功刷新后,在本轮授予段回复生命,封顶生命上限 | 放弃当前图换取恢复 |
|
||||
| 资源 | 弃图回收 | 成功刷新后,按旧图探索进度在本轮授予段回收清图奖励 | 先探索再主动弃图变现 |
|
||||
| 基座 | 矿脉储能 | 每次付费挖格被受理后,额外获得 RAP;安全格与危险格同口径 | 持续挖图也能准备刷新,不再必须少挖停手 |
|
||||
| 基座 | 省力周转 | 弃图刷新的 RAP 成本减半 | 同一份储备支持更多刷新 |
|
||||
| 输出 | 崩壁冲击 | 成功刷新后,在本轮战斗段末对全体存活敌人造成一次固定伤害 | 刷新成为直接进攻行为 |
|
||||
| 输出 | 蓄力周转 | 成功刷新攒层,最多 3 层;本轮下一次主角攻击消费全部层数后清零,战斗结束不跨战保留 | 连续换图蓄成一次单点爆发 |
|
||||
| 生存 | 石层缓冲 | 本轮至少成功刷新一次后,主角每次受到伤害固定减少 2 点;不叠加 | 刷新形成当轮全局防御姿态 |
|
||||
| 生存 | 坑道小憩 | 每次成功刷新累计 1 点恢复,最多 3 点;本轮授予段合计回复生命,封顶生命上限 | 放弃当前图换取恢复 |
|
||||
| 资源 | 矿道红利 | 每次成功刷新固定获得 1 GP | 刷新动作自身带少量GP收益 |
|
||||
|
||||
共同规则:
|
||||
|
||||
- **双基座叠加**:掘进储能与折价换图可以同时持有,产 RAP 与成本减半分别生效;每轮刷新次数上限继续限频,不设互斥规则。
|
||||
- **成功才触发**:后续五件只认成功换图;刷新被拒绝时不产生效果。同一轮多次成功刷新按发生顺序逐次登记。
|
||||
- **轮末兑付**:刷新不新增结算段。群伤、护盾、回血与回收按[战斗结算](../战斗循环/战斗结算.md)的既有段序兑付,刷新瞬间不直接改这些战斗结果。
|
||||
- **回收看旧图**:弃图回收读取被放弃旧图的探索进度,不把旧图或新图判为清图;具体口径归[地图战斗资源](../战斗循环/地图战斗资源.md)。
|
||||
- **基线不变**:没有这些专长时,RAP 来源、刷新成本、清图奖励与刷新结果保持原规则。数值与稀有度归 `feats.tres`,落表前本节只作设计声明。
|
||||
- **双基座叠加**:矿脉储能与省力周转可以同时持有,产 RAP 与成本减半分别生效;每轮刷新次数上限继续限频,不设互斥规则。
|
||||
- **成功才触发**:后续五件只认成功换图;刷新被拒绝时不产生事实,也不给输出、生存或资源收益。同一轮多次成功刷新按发生顺序逐次登记。
|
||||
- **轮末兑付**:刷新不新增结算段。崩壁冲击走本轮战斗段末结账,石层缓冲在每次主角受伤时消费,坑道小憩与矿道红利走本轮授予段兑付。
|
||||
- **全局受伤面**:石层缓冲覆盖敌方攻击、危险格、错误封印资源格与地形造成的主角伤害;临时生命值不在本流派设计范围内。
|
||||
- **基线不变**:没有这些专长时,RAP 来源、刷新成本、清图奖励与伤害结果保持原规则。数值与稀有度归 `feats.tres`,落表前本节只作设计声明。
|
||||
|
||||
### 迁入登记(秘宝域迁出件;实装批落地后并入件表)
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
11. 凭证拿到即开,不进持有集,玩家无法囤积——选中或买下的同一时刻就开专长三选一。
|
||||
12. 凭证带有计步保底:连续 5 次三选一展示未出现则第 6 次必出;计数跨奖励节点与商店共享,刷新后的重新展示计入次数,限购用满后保底不再触发。
|
||||
13. 被称为流派基座的专长必须能说明拿到前后的操作差异与新行为的持续资源来源;只有同一操作后的泛用数值增长,不得登记为基座。
|
||||
14. 换图系两件基座同时持有时都生效;后续五件只在成功刷新后触发,无专长基线不变;弃图回收按旧图探索进度结算,同一旧图至多回收一次。
|
||||
14. 换图系两件基座同时持有时都生效;后续五件只在成功刷新后触发,无专长基线不变;石层缓冲覆盖所有主角受伤路径且同轮不叠加,矿道红利按成功刷新次数固定获得 GP。
|
||||
|
||||
> 实装批落地前,现役代码按旧 27 件专长目录与旧资源成员在同框架下运行;新成员口径(27 件基础件表 / 凭证件表)自实装批起验。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 地图战斗资源
|
||||
|
||||
> 一句话:格面资源的产出、极性字典与清图奖励封装
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
| defense 护甲 | ac_up 盾牌加护(AC 掷点阈值轴——2026-09-16 换装,不入池)——幅度/上限/权重见 `reward.tres` | off_guard 失衡(AC 掷点阈值轴负向——2026-09-18 换装,不入池、退出主角防御池)——幅度/上限见 `status_effects.tres` off_guard 行 |
|
||||
| heal 回血 | heal_minor(满血零兑付照旧)——威力/权重见 `reward.tres` | heal_backlash 直伤:战斗结算直接扣血,不经增益状态——威力见 `reward.tres` |
|
||||
| blast 群伤 | spark_aoe 全体存活敌各吃一笔——威力/权重见 `reward.tres` | enemy_alert 警觉:敌方全体护甲等级 +1/层(`status_effects.tres` alert 行)——见 `reward.tres` |
|
||||
| weaken 削弱 | weaken(敌方输出池;同名重复授予只刷新剩余次数——2026-09-18 续时定案)——幅度见 `status_effects.tres` weaken 行 | sapped(主角输出池)——幅度见 `status_effects.tres` sapped 行 |
|
||||
| weaken 削弱 | weaken(敌方输出池;同名重复授予只加时长)——幅度见 `status_effects.tres` weaken 行 | sapped(主角输出池)——幅度见 `status_effects.tres` sapped 行 |
|
||||
| action 行动点 | ap_up:下轮行动点 +1——见 `reward.tres` | ap_drain 迟滞:下轮行动点 −1——见 `reward.tres` |
|
||||
| gp 财货 | gp_gain GP 袋:战斗内 GP 缓冲 +power,战斗完成随 GP 掉落统一入钱包账本(兑付编排归[战斗结算](战斗结算.md)、账本归[冒险结构](../冒险循环/冒险结构.md))——威力/权重见 `reward.tres` | gp_loss 破财:战斗内 GP 缓冲 −power,weight=0 纯字典不参与摇号——见 `reward.tres` |
|
||||
| pierce 穿刺 | pierce_strike 穿刺弹:单目标克制直伤(attack_type=pierce 查克制矩阵,必中全额不掷点;目标 = 固定顺位最前存活敌)——威力/权重见 `reward.tres` | pierce_snare 穿刺陷阱:主角直伤,不经增益状态——威力见 `reward.tres` |
|
||||
@@ -158,29 +158,8 @@
|
||||
| 可配置可替换 | 内容 = 池行值;允许即时类(当场兑付,不入库) |
|
||||
| 图类型差异化是预留插槽 | 现行只有默认槽;结构位已留,启用走棋盘生成词表落定 |
|
||||
|
||||
持有弃图回收专长时,同一清图奖励对象也可按旧图探索进度部分回收;这不改变正常清图与清图奖励的定义,例外口径见下节。
|
||||
|
||||
举证:本节为封装与流程规则,无计算。
|
||||
|
||||
### 弃图回收:按旧图探索进度兑付(设计定稿,待实现)
|
||||
|
||||
弃图回收让玩家不必挖完最后几个安全格,也能把已经推进的探索换成一部分清图奖励。它只读取被放弃的旧图,不把旧图判为清图,也不把新图判为已清图。
|
||||
|
||||
旧图退役前记录三项读数:装板初始已开安全格数、装板初始未开安全格数、退役时已开安全格数。探索进度定义为:
|
||||
|
||||
```
|
||||
探索进度 =(退役时已开安全格数 − 装板初始已开安全格数)÷ 装板初始未开安全格数
|
||||
```
|
||||
|
||||
进度限制在 0~1。玩家付费挖开与连锁展开的安全格都真实推进探索,计入分子;装板时白送的初始格、封锁格与危险格不计入。没有新探索就刷新,进度为 0,不能获得回收奖励。
|
||||
|
||||
回收兑付的是旧图在装板期已经摇定的清图奖励对象,按探索进度取对应份额;具体比例、档位、取整与数值归调参。它只发基础奖励的进度份额:
|
||||
|
||||
- 不触发完美封印加倍,不产生清图事件,不执行正常清图的整板兑付。
|
||||
- 同一旧图只在成功退役时回收一次;新图不继承旧图探索进度。
|
||||
- 刷新被拒绝时旧图不退役,也不回收;成功后的兑付时机归[战斗结算](战斗结算.md)本轮授予段。
|
||||
- 正常清图仍发完整清图奖励对象;弃图回收不得让同等探索投入的收益无条件高于正常清图。
|
||||
|
||||
## 依赖与输出
|
||||
|
||||
本系统被两侧夹住:事实与数值从上游来,调用与呈现往下游去。下表就是删除任何一方时的影响面清单。
|
||||
@@ -260,9 +239,6 @@
|
||||
- 类型封闭与字典完整:表里出现九型之外的类型、或任一型缺正向/负向行,加载拒绝。
|
||||
- 未生效可见:满层增益、满血正向治疗、无存活敌的群伤,各自在结算明细记"奖励未生效"行,零折算、零补偿。
|
||||
- 加载护栏:池空、清图池无正当量、增益引用悬空——任一命中即加载时返回错误并中止;默认内容加载通过。
|
||||
- 弃图回收进度:付费挖开与连锁展开的安全格计入探索,初始格、封锁格与危险格不计;无新探索时进度为 0,不能获得回收奖励。
|
||||
- 弃图回收恰一次:成功退役的旧图按当次探索进度回收其预摇清图奖励对象,新图不继承进度,同一旧图不能重复回收;被拒绝的刷新零兑付。
|
||||
- 回收不是清图:弃图回收不触发完美封印加倍、清图事件或正常清图整板兑付;正常清图仍发完整清图奖励对象。
|
||||
- 表实一致:数据表字段与奖励表字段定义逐字段一致。
|
||||
|
||||
## 待办
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 增益状态
|
||||
|
||||
> 一句话:增益/减益的状态池、叠层与衰减
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
|
||||
## 设计目的
|
||||
|
||||
战斗里有一类效果不在当场一次性用掉,而要管上几次结算:挖开安全格拿到的主角增益、封印标中带来的怪物削弱、踩雷喂给怪物的增强(警觉——blast 型负向)、标错封印招来的负向兑付,都是这类。本文把它们统称"状态":发给主角的有益效果叫增益(失衡与乏力是例外——它们是发给主角的减益),发给敌人的负面效果叫减益;敌方侧的增强同样是状态——几个方向共用同一个状态池。
|
||||
|
||||
增益状态就是这一个状态池:它收下全部状态,用同一套规矩管理——发入、合并(同一种效果重复拿到时按表行策略处理:叠层加一,或只刷新剩余次数——读 `status_effects.tres` 逐行 stack_policy)、衰减(每次结算后剩余次数减一,减到零效果消失)、清空(战斗结束全部清除)。没有它,几类效果得各写一套计时逻辑,结算明细也无法逐行指回"这效果是我哪步棋带来的"。状态池管机制、不管数值从哪来:某个效果该多大,由各收益系统的表去定,状态池只照章入列、按次衰减。
|
||||
增益状态就是这一个状态池:它收下全部状态,用同一套规矩管理——发入、合并(同一种效果重复拿到时按表行策略处理:叠层加一,或把剩余次数累加上本次授予的时长——读 `status_effects.tres` 逐行 stack_policy)、衰减(每次结算后剩余次数减一,减到零效果消失)、清空(战斗结束全部清除)。没有它,几类效果得各写一套计时逻辑,结算明细也无法逐行指回"这效果是我哪步棋带来的"。状态池管机制、不管数值从哪来:某个效果该多大,由各收益系统的表去定,状态池只照章入列、按次衰减。
|
||||
|
||||
## 系统范围
|
||||
|
||||
@@ -76,14 +76,14 @@
|
||||
|
||||
举证:本节为授予路径与登记规则,无计算。
|
||||
|
||||
### 同名状态再拿到:叠层、刷新、并存
|
||||
### 同名状态再拿到:叠层、累加、并存
|
||||
|
||||
同一种状态还没消失又拿到一份,怎么合并由状态表每行的合并策略字段说了算,共三种。合并在授予那一刻由状态池完成;多个状态在结算里按什么先后起作用,是战斗结算的编排——两件事互不干扰。
|
||||
|
||||
| 合并策略 | 重复授予时发生什么 | 说明 |
|
||||
|---|---|---|
|
||||
| 叠加 | 层数加一;剩余结算次数不动——只有首次授予定钟 | 现行内容行多数用叠加(逐行合并策略读 `status_effects.tres`) |
|
||||
| 刷新 | 剩余结算次数重置为该实例首次授予时的初始值,层数不变 | 刷新类内容行已就位——削弱行 weaken(2026-09-18 续时定案)、增益技行 empower / fortify、**主角增益行 attack_up / ac_up(2026-09-20 用户拍板「只续时不叠层」,全状态表增益/减益两侧合并语义就此统一为:主角可重复获取的增益与减益一律续时、不叠层)**;逐行读 `status_effects.tres` |
|
||||
| 累加 | 剩余结算次数加上本次授予的时长——技能施放行带覆盖时长时按覆盖值加;层数不变 | 现行内容行六行用累加(削弱行 weaken、增益技行 empower / fortify、主角增益行 attack_up / ac_up);重复拿一次就多攒一段时长,同一轮拿几份攒几段,停手后按剩余次数逐轮耗完;逐行读 `status_effects.tres` |
|
||||
| 并存 | 新建一份独立实例,两份各算各的 | 同上 |
|
||||
|
||||
并存分支没有满层上限,但状态池规模天然有界:实例数的上界就是受理过的授予数——多受理一份就多一份实例,多少份在授予那一刻就完全确定,不会无限膨胀。
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
### 剩余次数怎么倒数:按结算次数衰减
|
||||
|
||||
每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。MVP 时期六行内容均为一次性(削弱后改续时——见 git 提交史;2026-09-20 起主角增益两行也走续时、duration 表值仍为 1——重复拿等于重新获得一次当轮增益);跨轮持续的状态,寿命由表行初始值表达,给几就活几次结算。
|
||||
每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。现行累加行里主角增益两行持续 1、削弱与敌方增益技行持续 2——累加语义下重复拿一次就按本次授予时长多攒一段,同一轮拿几份攒几段;跨轮持续的状态,寿命由表行初始值表达,给几就活几次结算。
|
||||
|
||||
```
|
||||
一轮结算:查询生效清单 → 逐行兑付(层数 × 行值)→ 轮末衰减(全部实例剩余次数 −1)→ 归零实例移除
|
||||
@@ -140,8 +140,8 @@
|
||||
|
||||
| 例子 | 状态池输出 | 消费侧读法 |
|
||||
|---|---|---|
|
||||
| 暴走叠两层 | +(2 × enrage 行幅度)(读表;越出单行取值域,原样输出) | 压在天花板 +100% 上:敌方输出乘区封顶 ×2(削弱已改续时——"叠两层压地板 −100% 归零"的读法随 2026-09-18 结构修复批退役) |
|
||||
| 主角盾牌加护再拿一份 | 层数恒 1、剩余结算次数重置(refresh——2026-09-20 主角增益续时批) | 不经任何池:掷点判定阈值 = 主角基准(读 hero 表)+ 层数(现行 +1——上限 2 层叠读取法退役,局内可达 AC 上限 11) |
|
||||
| 暴走叠两层 | +(2 × enrage 行幅度)(读表;越出单行取值域,原样输出) | 压在天花板 +100% 上:敌方输出乘区封顶 ×2(削弱非叠层行——"叠两层压地板 −100% 归零"的读法不成立) |
|
||||
| 主角盾牌加护再拿一份 | 层数恒 1、剩余结算次数累加本次授予的时长 | 不经任何池:掷点判定阈值 = 主角基准(读 hero 表)+ 层数(现行 +1——上限 2 层叠读取法退役,局内可达 AC 上限 11) |
|
||||
|
||||
状态池还有一条快照纪律:实例的幅度、初始次数、量纲在授予那一刻从表行拷贝。战斗中配置表即使被重载,已授予实例不受任何影响,只影响后续授予。
|
||||
|
||||
@@ -162,15 +162,15 @@
|
||||
|
||||
| 状态 | 效果一句话 | 发给谁 | 幅度(现行值) | 时限 / 合并 | 来源类别 |
|
||||
|---|---|---|---|---|---|
|
||||
| attack_up 增伤 | 主角攻击提升 | 主角 | 见 `data/tables/status_effects.tres` attack_up 行 | 时限/上限读表 / 刷新(2026-09-20 续时批 stack→refresh,层数恒 1) | 安全格 |
|
||||
| ac_up 盾牌加护 | 主角护甲等级提升(掷点折扣判定阈值——AC 轴,不入池) | 主角 | 见 `status_effects.tres` ac_up 行(2026-09-16 换装,原 defense_up 行已退役) | 时限/上限读表 / 刷新(2026-09-20 续时批 stack→refresh,层数恒 1) | 安全格 |
|
||||
| attack_up 增伤 | 主角攻击提升 | 主角 | 见 `data/tables/status_effects.tres` attack_up 行 | 时限/上限读表 / 累加(层数恒 1) | 安全格 |
|
||||
| ac_up 盾牌加护 | 主角护甲等级提升(掷点折扣判定阈值——AC 轴,不入池) | 主角 | 见 `status_effects.tres` ac_up 行(2026-09-16 换装,原 defense_up 行已退役) | 时限/上限读表 / 累加(层数恒 1) | 安全格 |
|
||||
| alert 警觉 | 敌方护甲等级 +1/层 | 敌方 | 见 `status_effects.tres` alert 行 | 1 结算 / 叠加读表 | 踩雷 / 标空封印(blast 型负向) |
|
||||
| weaken 削弱 | 敌方攻击被削弱(同名重复标中只刷新剩余次数、层数恒 1) | 敌方 | 见 `status_effects.tres` weaken 行(取值域 [60,85],取负生效) | 时限读表 / 刷新(2026-09-18 续时定案) | 封印标中 |
|
||||
| weaken 削弱 | 敌方攻击被削弱(同名重复标中只加时长、层数恒 1) | 敌方 | 见 `status_effects.tres` weaken 行(取值域 [60,85],取负生效) | 时限读表 / 累加 | 封印标中 |
|
||||
| enrage 暴走 | 敌方攻击提升 | 敌方 | 见 `status_effects.tres` enrage 行 | 时限/上限读表 / 叠加 | 标空封印反噬 / 踩雷(attack 型负向) |
|
||||
| off_guard 失衡 | 主角护甲等级降低(AC 轴——掷点判定阈值下调、更易被命中;2026-09-18 换装,原百分比承伤池已退) | 主角 | 见 `status_effects.tres` off_guard 行 | 时限/上限读表 / 叠加 | 标空封印反噬 / 踩雷(defense 型负向)/ 敌方妨害技(2026-09-19 新效应词汇批——蛛网束缚载体,duration 由技能行覆盖) |
|
||||
| sapped 乏力 | 主角输出降低(weaken 型负向,主角输出池) | 主角 | 见 `status_effects.tres` sapped 行 | 时限/上限读表 / 叠加 | 危险格负向 / 标空封印(2026-09-15 格面资源批)/ 敌方妨害技(大地脉动——L3 Boss 石君王独占,2026-09-20 第三关内容批) |
|
||||
| empower 蓄力 | 敌方输出倍率增益(敌方输出池——蓄力技载体;幅度读表) | 敌方 | 见 `status_effects.tres` empower 行 | duration 读表 = 1 个有效增益拍 / 刷新 | 敌方技能施放(2026-09-16 敌人技能池批) |
|
||||
| fortify 硬化 | 敌方防御提升(**敌方防御池**——硬化技载体;负值入池 = 主角攻击折半,2026-09-18 符号纠正——旧正值实际翻倍) | 敌方 | 见 `status_effects.tres` fortify 行 | duration 读表 = 1 个有效增益拍 / 刷新 | 敌方技能施放(2026-09-16 敌人技能池批) |
|
||||
| empower 蓄力 | 敌方输出倍率增益(敌方输出池——蓄力技载体;幅度读表) | 敌方 | 见 `status_effects.tres` empower 行 | duration 读表 = 1 个有效增益拍 / 累加 | 敌方技能施放(2026-09-16 敌人技能池批) |
|
||||
| fortify 硬化 | 敌方防御提升(**敌方防御池**——硬化技载体;负值入池 = 主角攻击折半,2026-09-18 符号纠正——旧正值实际翻倍) | 敌方 | 见 `status_effects.tres` fortify 行 | duration 读表 = 1 个有效增益拍 / 累加 | 敌方技能施放(2026-09-16 敌人技能池批) |
|
||||
|
||||
empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(技能行 buff_ref 引用,施放时长由技能行覆盖——技能冷却与增益持续是两个计时器):两行 duration 表值(读表)的口径是"先兑付后衰减含施放当轮"——施放发生在第 N 结算战斗段,存活到第 N+1 结算战斗段才兑付"下一拍",即"持续 1 个有效结算"(proposal 语义的状态池落地)。敌方防御池是第四个乘区池:敌方作为防守方的百分比修正池,消费面 = [战斗结算](战斗结算.md)主角攻击的池乘区——四池对称(敌打主角 = 主角防御池 × 敌方输出池,主角打敌 = 主角输出池 × 敌方防御池),状态池语义零延伸:仍是同构实例、side-wide 加算、不封顶不保底。
|
||||
|
||||
@@ -195,7 +195,7 @@ empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(
|
||||
|
||||
1. 战斗结算通知战斗开始:状态池初始化,空池进入可用状态。
|
||||
2. 轮末结算时,战斗结算把这一轮的扫雷事实兑换成授予:挖开安全格给主角上增益、封印标中削弱敌人、踩雷给敌方上增强、标空封印触发反噬——授予入口是唯一进货口。
|
||||
3. 状态池按表行合并策略受理(新建 / 叠一层 / 刷新),实例立即生效并携带归因记录。
|
||||
3. 状态池按表行合并策略受理(新建 / 叠一层 / 累加时长),实例立即生效并携带归因记录。
|
||||
4. 战斗结算查询生效清单,逐行兑付进结算公式(有效值 = 层数 × 行值)。
|
||||
5. 兑付完做轮末衰减:全部实例剩余次数减一,归零移除——先兑付、后衰减。
|
||||
6. 战斗外授予、未知状态、满层授予一律被拒且状态池不变;满层的"奖励未生效"行归战斗结算的结算明细。
|
||||
@@ -207,7 +207,7 @@ empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(
|
||||
|
||||
| 优先级 | 内容 | 后置原因 |
|
||||
|---|---|---|
|
||||
| P1 | 刷新、并存两类合并策略的后续内容行 | 语义已定义、状态池已承载;刷新类已有内容(empower/fortify 2026-09-16 敌人技能池批、weaken 2026-09-18 续时定案),并存仍空,首个并存内容行上线时顺带补验收 |
|
||||
| P1 | 累加、并存两类合并策略的后续内容行 | 语义已定义、状态池已承载;累加类已有内容(weaken/empower/fortify/attack_up/ac_up),并存仍空,首个并存内容行上线时顺带补验收 |
|
||||
| P1 | 清图奖励含增益的授予路径 | 来源类别已预留;内容与档位归[地图战斗资源](地图战斗资源.md)定稿 |
|
||||
| P1 | 单体寻址扩展位(接口已定稿——五条语义见核心规则〈单体寻址扩展位〉节) | 实现随首个单体效果的内容批进游戏,真实入口验收随该批闭合 |
|
||||
| P2 | 驱散 / 延时 / 转移接口 | 状态一旦生效,玩家没有任何手段摘掉、延后或挪给别人——省掉一整类界面复杂度是明确的设计决定;要加属于新增接口的修订,不是调参 |
|
||||
@@ -230,12 +230,12 @@ empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(
|
||||
| sapped 幅度 | 百分比 [−100,100] 且非 0 | percent 行 magnitude | 乏力;危险格负向 weaken 型,进主角输出修正池(2026-09-15 格面资源批补登记) |
|
||||
| empower 幅度 | 百分比 [−100,100] 且非 0 | percent 行 magnitude | 蓄力;敌方技能池增益技载体,进敌方输出修正池 |
|
||||
| fortify 幅度 | 百分比 [−100,100] 且非 0 | percent 行 magnitude | 硬化;敌方技能池增益技载体,进**敌方防御池**(负值 = 减伤方向——2026-09-18 符号纠正) |
|
||||
| 初始剩余次数 | 整数 [1,99] | duration_settlements | 一次性语义行逐行读表;续时行 = weaken(2026-09-18 定案,duration 2)+ empower/fortify(1 个有效增益拍——先兑付后衰减含施放当轮)+ **attack_up / ac_up(2026-09-20 主角增益续时批,duration 表值 1 维持——续时语义在 duration=1 下表现为重复拿重置当轮倒数;跨轮延续随将来 duration 拉长自然显形,数值归状态层重设计批)**;拉长 = 恢复跨轮延续、规划变简单 |
|
||||
| 叠层上限 | 整数 [1,16] | max_stack | 仅叠加策略消费;刷新行层数恒 1——weaken / attack_up / ac_up 表值上限在续时语义下不消费(2026-09-18 / 2026-09-20),empower/fortify ×1(冷却闸下不可能自叠加) |
|
||||
| 初始剩余次数 | 整数 [1,99] | duration_settlements | 累加行 = weaken(duration 2)+ empower/fortify(表值 2 = 先兑付后衰减含施放当轮的 1 个有效增益拍)+ attack_up / ac_up(duration 1);累加语义下重复拿一次就按本次授予时长加一段;拉长表值 = 每次授予攒更多段、规划变简单 |
|
||||
| 叠层上限 | 整数 [1,16] | max_stack | 仅叠加策略消费;累加行层数恒 1、表值上限不消费,empower/fortify ×1(冷却闸下不可能自叠加) |
|
||||
|
||||
非调参的冻结项:三类合并策略语义、两种量纲语义、来源类别枚举、"先兑付后衰减"时序、拒绝原子性、零随机——改动走本文修订流程,不走调参。
|
||||
|
||||
联动纪律:增伤与削弱是当量锚的两端;盾牌加护与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)的承伤预期联动(掷点折扣轴——AC 行值变更须复算生存链区间;2026-09-20 主角增益续时批后 ac_up 层数恒 1、局内可达 AC 上限 11,复算随调参批);削弱改续时后"单轮层数上限受[行动经济](../排雷循环/行动经济.md)行动点约束"的联动随之失效(2026-09-18 续时定案——同名重复标中只刷新剩余次数、层数恒 1;2026-09-20 起主角侧 attack_up / ac_up 同口径)。这些联动值禁止独立调参,修订须重跑锚对照并同步登记册。
|
||||
联动纪律:增伤与削弱是当量锚的两端;盾牌加护与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)的承伤预期联动(掷点折扣轴——AC 行值变更须复算生存链区间;ac_up 层数恒 1、局内可达 AC 上限 11,复算随调参批);削弱与主角增益行同名重复授予只加时长、层数恒 1,"单轮层数上限受[行动经济](../排雷循环/行动经济.md)行动点约束"的联动不成立。这些联动值禁止独立调参,修订须重跑锚对照并同步登记册。
|
||||
|
||||
## 验收标准
|
||||
|
||||
@@ -243,7 +243,7 @@ empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(
|
||||
- 多向同构:主角侧增益与减益、敌方侧增强与削弱共用同一套受理、合并、倒数、清除语义,不存在第二套计时逻辑。
|
||||
- 授予即生效:状态发入当轮结算即兑付第一次,无"待生效"中间态。
|
||||
- 只登记不去重:同一事实被转化两次授予,登记两层——去重责任在战斗结算。
|
||||
- 叠层不减钟:叠加策略行叠加只加层数,剩余次数保持首次授予定下的轨迹不变(刷新策略行则相反——层数不变、只重置剩余次数;两策略逐行读表)。
|
||||
- 叠层不减钟:叠加策略行叠加只加层数,剩余次数保持首次授予定下的轨迹不变(累加策略行则相反——层数不变、剩余次数加上本次授予的时长;两策略逐行读表)。
|
||||
- 满层拒绝原子且留痕:叠满后再授予返回失败、状态池逐字不变;结算明细多一行"奖励未生效"(含来源)。
|
||||
- 先兑付后衰减:每轮结算恰做一次衰减,归零实例当轮移除;停手轮、弃图刷新轮照常衰减。
|
||||
- 合成不设上下限:状态池输出的有效值恒等于层数 × 行值,越出百分比域不封顶不保底;地板在战斗结算侧。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 战斗结算
|
||||
|
||||
> 一句话:轮末结算的编排与逐行兑现
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.4
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
| 判定行 | 胜 / 负 / 继续各一行;终局判定行同时是胜负画面的数据锚 |
|
||||
| 高光行 | 完美封印成立时一行,只记实际有效兑付 |
|
||||
|
||||
每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调封印标中、清图奖励这类值得高亮的结果)。没生效也要留痕:两类"未生效"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。
|
||||
每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调封印标中、清图奖励这类值得高亮的结果)。奖励直伤行的数值字段沿用 `effectiveness` 键名、值为整数点数——键名与量纲是数据导出与分析工具的兼容契约,改动须连动分析工具面。没生效也要留痕:两类"未生效"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。
|
||||
|
||||
任何一行找不到来源即断言失败——宁可返回错误,也不让"合并到看不出来源"的呈现混过数据层。但有些行天然没有棋盘来源,断言为此留了两层豁免,豁免集合是封闭清单,改动同样走本文修订。
|
||||
|
||||
@@ -268,21 +268,21 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
|
||||
### 换图系专长:刷新事实与本轮兑付(设计定稿,待实现)
|
||||
|
||||
持有换图系后续专长时,成功刷新除执行上述换图编排外,还产生一次 `board_refreshed` 事实。旧图退役前保存它的探索进度与清图奖励对象身份;新板就位后登记事实。刷新被拒绝时不产生事实,任何后续效果都不触发。同一轮多次成功刷新按发生顺序逐次登记,不合并成一次。
|
||||
持有换图系后续专长时,成功刷新除执行上述换图编排外,还产生一次 `board_refreshed` 事实。事实只在新板装板成功后登记;刷新被拒绝时不产生事实,任何后续效果都不触发。同一轮多次成功刷新按发生顺序逐次登记,不合并成一次。
|
||||
|
||||
刷新只登记事实,不当场插入结算。各效果在既有五段内兑付:
|
||||
刷新只登记事实,不当场插入结算。各效果在既有五段与对应消费点兑付:
|
||||
|
||||
| 专长 | 兑付规则 |
|
||||
|---|---|
|
||||
| 弃图轰击 | 每次刷新事实开一张全体敌人伤害单据,在本轮战斗段末经伤害控制器统一结账;目标按结账时的存活状态解析 |
|
||||
| 换图蓄势 | 每次刷新事实当场累计一层,受表值上限约束;下一次主角攻击附加全部层数对应的伤害,随后清零;战斗结束清空 |
|
||||
| 换图庇护 | 每次刷新事实在本轮授予段经既有护盾通道授予一次,不新开挖掘期状态入口 |
|
||||
| 弃图休整 | 每次刷新事实在本轮授予段回复生命,按既有治疗规则封顶生命上限;满血时记未生效行 |
|
||||
| 弃图回收 | 每次刷新事实在本轮授予段按旧图探索进度回收其清图奖励对象;进度与奖励口径归[地图战斗资源](地图战斗资源.md) |
|
||||
| 崩壁冲击 | 每次刷新事实开一张全体敌人伤害单据,在本轮战斗段末经伤害控制器统一结账;目标按结账时的存活状态解析 |
|
||||
| 蓄力周转 | 每次刷新事实累计一层,最多 3 层;本轮下一次主角攻击消费全部层数对应的攻击加成,随后清零;战斗结束不跨战保留 |
|
||||
| 石层缓冲 | 每次刷新事实把当轮全局减伤状态置为生效;本轮主角每次受到伤害都固定减少 2 点,同一轮多次刷新不继续叠加 |
|
||||
| 坑道小憩 | 每次刷新事实累计 1 点恢复量,最多 3 点;本轮授予段合计回复生命,封顶生命上限,随后清零;满血时记未生效行 |
|
||||
| 矿道红利 | 每次刷新事实固定获得 1 GP,走战斗内 GP 缓冲并在授予段留痕;不按旧图探索进度折算,也不与清图奖励绑定 |
|
||||
|
||||
弃图轰击若击杀最后敌人,仍由本轮判定段按既有顺序判胜,不新增挖掘期即时胜利出口。换图系效果使用扩展行留痕:来源写明专长与被弃旧图,弃图轰击走伤害单据,护盾、回血与回收各保留自己的兑付记录;它们不指向单一棋盘格,使用明细流水的专长刷新效果来源豁免,不伪造来源格。
|
||||
全局受伤事件统一:`damage_taken` 是主角受到伤害的唯一效果事件,覆盖敌方攻击、危险格、错误封印资源格与地形造成的主角伤害。石层缓冲订阅该事件并在现行伤害下限之前消费;旧有订阅同一事件的专长也按这一完整语义生效。临时生命值不在此机制内。
|
||||
|
||||
弃图回收不是清图:不触发完美封印加倍,不发清图事件,不改变「刷新图不发清图奖励」。它兑的是被放弃旧图按探索进度回收的一部分奖励。
|
||||
崩壁冲击若击杀最后敌人,仍由本轮判定段按既有顺序判胜,不新增挖掘期即时胜利出口。换图系效果使用扩展行留痕:崩壁冲击走伤害单据,石层缓冲在受伤消费行体现减免,坑道小憩与矿道红利分别走治疗与 GP 入账记录。刷新事实来源不指向单一棋盘格,使用明细流水的专长刷新效果来源豁免,不伪造来源格。
|
||||
|
||||
战斗开始按固定序列开张:关上配置热重载的闸门、初始化状态池、掷骰定敌方实际数量、生成敌人、装板并预分配资源恰一次、变异布置([地形变异](../排雷循环/地形变异.md)——棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 → 战斗资源格设置 → 变异格设置,消费 `variant_place` 封闭流)、发开轮通知并执行开轮摇号(摇定首轮各敌本拍动作)。上一关的大奖励(如血上限提升)经战斗开始入参的覆盖值通道生效,内容归[冒险结构](../冒险循环/冒险结构.md)。
|
||||
|
||||
@@ -379,8 +379,9 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
- 行型封闭:流水行型只出现本文登记的基础 9 类与扩展行;行型词表改动必须走本文修订并同步呈现层行格式化,未登记行型禁止落行。
|
||||
- 伤害单据管线(2026-09-24):全部伤害(主角先手、敌方伤害技、棋盘克制直伤、群伤、反噬、地形变异)经伤害控制器单据管线兑现,兑付配方封闭五种;目标在结账时刻照规则解析;死亡事件由单位自行宣布——怪死其未结单据作废(零行、不进冷却不进保底计数)、主角死兑付终止;段界结账为现行口径、开单即结为回切结构位,两模式各自同种子同输入逐位复现。
|
||||
- 换图系刷新事实:成功刷新保存旧图探索进度并登记一次事实,被拒绝时不登记;同一轮多次刷新按发生顺序逐次兑付,刷新本身不新增结算段或轮次结束。
|
||||
- 换图系输出:弃图轰击在本轮战斗段末经伤害控制器结算;换图蓄势受层数上限约束,下一次主角攻击消费全部层数,战斗结束清空;群伤致敌清空时仍由本轮判定段判胜。
|
||||
- 换图系生存与回收:护盾、回血与弃图回收在本轮授予段兑付并留下可归因扩展行;回复封顶生命上限;回收不触发清图事件或完美封印加倍。
|
||||
- 换图系输出:崩壁冲击在本轮战斗段末经伤害控制器结算;蓄力周转受 3 层上限约束,本轮下一次主角攻击消费全部层数,战斗结束清空;群伤致敌清空时仍由本轮判定段判胜。
|
||||
- 换图系生存与资源:石层缓冲经全局 `damage_taken` 对敌方攻击、危险格、错误封印与地形伤害各减 2,同轮不叠加;坑道小憩在授予段按累计值回血并封顶;矿道红利每次成功刷新固定入账 1 GP。
|
||||
- 全局受伤事件:`damage_taken` 只在主角实际承伤消费点发射一次,所有伤害路径均经同一效果引擎减免面,禁止同一张单重复发射或只让部分伤害来源订阅生效。
|
||||
- 条款3 对账(2026-10-05 判式换代批 T3——与行动经济 F1 条款3 重立同步):①聚合健康式 (1−ρ̂)·E_dig(u) − ρ̂·V_mine(u) ≥ 0 与 β 比值带 [1.2, 2.5] 判读逐 u 检算(清图支配性审计为常设重跑项,读数快照登记归 clause3 重立条);②封印溢价兑付正确:封印标中正向兑付 = 同型正向 ×(1+δ)(δ 读 `settlement.tres` flag_hit_premium,100=无溢价直通零扰动),三类形态全覆盖(flat power / percent 幅度 / gp 缓冲)、标空与挖格兑付不乘溢价、危险格与安全格同池结构零触碰。
|
||||
|
||||
## 待办
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
| trigger(触发条件) | 全部,可省 | {fact, op, value, latch} 四键——fact 与 op 取「触发条件」节封闭词表,value 类型与域随 fact,出域加载期拒绝;缺省无字段=无条件技,旧行为零变化(2026-10-26 情境条件触发器批立列、2026-10-27 迭代 39 实装落地;原池条目的血量百分比字段收编为本字段闩型首用例) |
|
||||
| P1 字段位 | — | 命中段数、目标数——随对应后置批加列(触发条件位已由 trigger 立列收编——2026-10-26 立列、2026-10-27 实装;原"固定值分量"位已由 rider_mult 实装收编——2026-09-19;多段命中「命中段数」保留以防复活) |
|
||||
|
||||
普通攻击也是一行技能行,两条域校验锁死它的特殊性:倍率恒 1、冷却恒 0——想配倍率或冷却的就不是普攻,是伤害技。技能伤害存倍率、不存固定值:伤害 = 施放者攻击基线 × 技能倍率——同一行"重击",不同敌种按各自攻击基线缩放 = 基线 A × 技能倍率 M(行值读 enemy.tres / skills.tres),深层强化行自动缩放,这是共享技能表成立的前提;**固定的绝对值伤害不满足该前提**(不随基线缩放、跨关稀释——Brotato 教训),"固定值伤害"的合法形态是**必中附加分量 rider_mult**(×攻击基线的必中小倍率,2026-09-19 实装)。增益技带效果引用与持续回合,百分比天然按施放者同池生效,无缩放问题;妨害技同构,落点由状态行 target_side 路由到主角。
|
||||
普通攻击也是一行技能行,两条域校验锁死它的特殊性:倍率恒 1、冷却恒 0——想配倍率或冷却的就不是普攻,是伤害技。技能伤害存倍率、不存固定值:伤害 = 施放者攻击基线 × 技能倍率——同一行"重击",不同敌种按各自攻击基线缩放 = 基线 A × 技能倍率 M(行值读 enemy.tres / skills.tres),深层强化行自动缩放,这是共享技能表成立的前提;**固定的绝对值伤害不满足该前提**(不随基线缩放、跨关稀释——Brotato 教训),"固定值伤害"的合法形态是**必中附加分量 rider_mult**(×攻击基线的必中小倍率,2026-09-19 实装)。增益技带效果引用与持续回合,百分比天然按施放者同池生效,无缩放问题;妨害技同构,落点由状态行 target_side 路由到主角。技能伤害基线的取整:攻击基线 × 倍率按四舍五入、正好半格时一律向上取——全伤害管线唯一不用向下取整的点(半点上取保住低攻击档特殊技的辨识度:攻击 1 配 ×1.5 的突刺落到 2 而不是 1)。
|
||||
|
||||
池构成纪律(加载校验,违规拒绝入表):**每池恰一行普通攻击**(普攻冷却恒 0 → 可摇集合恒非空,"空池不可能"由结构保证,最坏情况怪物平砍等待——那是玩家的反打窗口而非死拍);**每池增益类技能 ≤1**(非 RPG,怪物连出不同增益显得呆);**每池妨害类技能 ≤1**(2026-09-19 新效应词汇批同构纪律);**每池至少一行不带触发条件的特殊技**(普攻不算——保底特殊技集合恒非空的结构保证,2026-10-26 触发条件批);池条目 skill_id 悬空致命;权重 > 0、冷却 ≥ 0。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 行动经济
|
||||
|
||||
> 一句话:每轮行动点怎么花、停手与刷新的代价
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -97,12 +97,12 @@
|
||||
|
||||
| 专长 | 规则 | 不变边界 |
|
||||
|---|---|---|
|
||||
| 掘进储能 | 每次付费挖格被受理后,额外获得表值 RAP;安全格与危险格同口径 | 连锁展开只随首格的一次付费动作结算,不逐格追加;封印、移动、刷新不产 RAP |
|
||||
| 折价换图 | 弃图刷新的 RAP 成本为基线的 50% | 只改价格;AP、轮次、每轮刷新上限与换图流程不变 |
|
||||
| 矿脉储能 | 每次付费挖格被受理后,额外获得表值 RAP;安全格与危险格同口径 | 连锁展开只随首格的一次付费动作结算,不逐格追加;封印、移动、刷新不产 RAP |
|
||||
| 省力周转 | 弃图刷新的 RAP 成本为基线的 50% | 只改价格;AP、轮次、每轮刷新上限与换图流程不变 |
|
||||
|
||||
两件基座可以同时持有并叠加:付费挖格照常产 RAP,刷新同时按半价扣费。两条规则作用在不同账户入口,不互相覆盖,也不设互斥。刷新仍须储备达到折后门槛才受理;储备不足或超过每轮次数上限时拒绝,状态不变。RAP 仍在战斗结束清零,刷新本身不返 RAP。
|
||||
|
||||
无掘进储能时,挖格不新增 RAP;无折价换图时,刷新成本仍读全局基值。后续输出、生存与资源专长如何使用成功刷新这个事实,归[战斗结算](../战斗循环/战斗结算.md)。
|
||||
无矿脉储能时,挖格不新增 RAP;无省力周转时,刷新成本仍读全局基值。后续输出、生存与资源专长如何使用成功刷新这个事实,归[战斗结算](../战斗循环/战斗结算.md)。
|
||||
|
||||
### 下轮行动点修正(格面资源八型批 2026-09-20)
|
||||
|
||||
@@ -249,8 +249,8 @@
|
||||
| move_ap_cost | int | 表值(冻结 0) | 移动免费——冻结,非调参项 |
|
||||
| chain_ap_cost | int | 表值(冻结 0) | 连锁免费——冻结,非调参项 |
|
||||
| refresh_cap_per_round | int | 表值(provisional,结构位预留) | 每轮刷新上限,开轮重置计数 |
|
||||
| (跨表)掘进储能产 RAP | int [1, 64] | 设计定稿,待入 `feats.tres` | 每次受理的付费挖格兑付一次;连锁不逐格追加 |
|
||||
| (跨表)折价换图成本率 | percent | 50%(结构已裁,待入 `feats.tres`) | 只改持有者的刷新价格;双基座叠加时不覆盖掘进储能 |
|
||||
| (跨表)矿脉储能产 RAP | int [1, 64] | 设计定稿,待入 `feats.tres` | 每次受理的付费挖格兑付一次;连锁不逐格追加 |
|
||||
| (跨表)省力周转成本率 | percent | 50%(结构已裁,待入 `feats.tres`) | 只改持有者的刷新价格;双基座叠加时不覆盖矿脉储能 |
|
||||
| (跨表)跨冒险行动点加成 | int [0,10] / int [0,99],冒险表 | adventure_table bonus_ap_flat / bonus_cap_ap(provisional) | 三选一 AP 件兑现值与全冒险次数上限;加成轴语义见「跨冒险行动点加成轴」节 |
|
||||
|
||||
字段名注记:2026-09-18 措辞完成批已全链对齐正式名——表字段与本文行名统一为 `refresh_cap_per_round`,刷新入口命名与表字段口径对齐(命名统一登记兑现,切片临时名零残留)。
|
||||
@@ -265,8 +265,8 @@
|
||||
- 仲裁唯一:最后 1 点行动点挖开最后一个安全格时,判清图(剩余清零 + 换图 + 清图奖励),不判耗尽;输出恰一条。
|
||||
- 停手 1:1:剩余 3 点停手,储备池加 3,下轮同图继续。
|
||||
- 刷新规则:储备不足时刷新被拒绝且状态不变;同一轮第 3 次刷新被拒绝;刷新不重置行动点、不发清图奖励、旧图上的封印印记失效。
|
||||
- 掘进储能:持有专长时,每次受理的付费挖格额外产一次 RAP,安全格与危险格同口径,连锁展开不逐格追加;无专长时挖格不新增 RAP。
|
||||
- 折价换图与双基座:持有折价换图时按基线的 50% 扣费;与掘进储能同时持有则两条规则都生效,刷新仍受储备门槛与每轮次数上限约束。
|
||||
- 矿脉储能:持有专长时,每次受理的付费挖格额外产一次 RAP,安全格与危险格同口径,连锁展开不逐格追加;无专长时挖格不新增 RAP。
|
||||
- 省力周转与双基座:持有省力周转时按基线的 50% 扣费;与矿脉储能同时持有则两条规则都生效,刷新仍受储备门槛与每轮次数上限约束。
|
||||
- 储备清零:战斗结束信号到达后储备池归 0,行动点进入休眠。
|
||||
- 跨字段校验:任一动作成本配置超过每轮预算时,加载期致命中止。
|
||||
- 零随机:同一动作序列驱动两个独立行动点实例,轮次结束记录与储备轨迹逐项相同。
|
||||
|
||||
@@ -366,7 +366,7 @@
|
||||
- [ ] 两种格态(已开格 / 未开格)灰度截图下一眼可辨(排雷玩法×6②)
|
||||
- [ ] 悬停光标区分移动/挖/封印三语义;可挖域(8 向相邻)常驻指示;非相邻格悬停显禁止反馈(排雷玩法×6③)
|
||||
- [ ] 已引爆危险格呈"可通行可读"(排雷玩法×6④)
|
||||
- [ ] 全部数字对所在格底对比 ≥4.5:1;明度分层规则无违例(art-bible §2/§4.1,v3.0——状态区分不走明度,格底功能分档合法)
|
||||
- [ ] 全部数字对所在格底对比 ≥4.5:1;明度分层规则无违例(art-bible §2/§4.1——格底功能分档合法,格态语义由形与色相承载)
|
||||
|
||||
**交互与反馈**
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 营地场景 UX spec(camp)
|
||||
|
||||
> 状态:v2.2(现行)
|
||||
> 状态:v2.3(现行)
|
||||
> 事实源:本 spec(呈现)+ [元进度](../systems/跨局循环/元进度.md)(机制)
|
||||
> 美术画法:[art-bible](../../art/art-bible.md) §6.2/§7.5;素材命名与路径:[asset-path-map](../../art/asset-path-map.md)
|
||||
> 相邻:[level-map.md](level-map.md)(主菜单语境)
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
## 叠层与开合
|
||||
|
||||
各界面开合走模态栈单口,层序按模态栈契约登记。**承载形态(独立全屏页 vs 单一内页切换)与叠层规则归卡④ §9 代拍板域,本 spec 不预设值**;派生规则:独立全屏页=场景根隐藏/单一内页切换=场景根留底(「缩放虚化」为第三形态,禁批内自选)。ESC 浮窗(返回主菜单/设置/退出游戏)归卡⑤。
|
||||
各界面开合走模态栈单口,层序按模态栈契约登记。**承载形态已定稿(2026-11-01 用户裁)**:五个功能界面(锻造工坊/秘宝匣/天赋树/奖励格图鉴/生涯记录)各以**功能窗口**打开——场景根留底可见,窗口之下垫半透明压暗遮罩(交互聚焦用,复用既有模态遮罩件,现值锚黑色 0.6;观感随样张目检校准,属普通设计决策)。**五窗互斥单开**(模态栈深恒 1,不开窗叠窗);各窗自带关闭钮(卡④ v1.1 承诺面)。ESC 优先序:有功能窗口开着 → 先关最上层窗;无窗 → 才进卡⑤ ESC 浮窗(返回主菜单/设置/退出游戏)。
|
||||
|
||||
## 口径
|
||||
|
||||
|
||||
@@ -234,6 +234,6 @@
|
||||
| [HUD框架](../systems/表现与支撑/HUD框架.md) | 模态 overlay 层级纪律、订阅纪律(推进信号两线之一) | Navigation Position + Data Requirements 架构注记 |
|
||||
| HUD框架 | 可访问性基线全量(对比/色弱/字号/闪烁/减动效/纯鼠标/中文字体) | Accessibility |
|
||||
| [battle.md](battle.md) OQ14 二轮拍板 | GP = 关卡画面常驻总量 | 顶栏 GP 常驻徽章 |
|
||||
| [art-bible](../../art/art-bible.md) | 方块地牢观感(§3.2 圆角方形世界侧)、色板冻结(§4.1)、半拟物(§7.1)、明度分层(§2,v3.0——状态不走明度、空间光照可走) | 图区组件 + Accessibility 对比条 |
|
||||
| [art-bible](../../art/art-bible.md) | 方块地牢观感(§3.2 圆角方形世界侧)、色板冻结(§4.1)、半拟物(§7.1)、明度分层(§2) | 图区组件 + Accessibility 对比条 |
|
||||
|
||||
---
|
||||
@@ -30,12 +30,13 @@ const TABLE_MANIFEST := {
|
||||
"feats": "feats.tres", # FeatTable 规则改写型特性(定义表 Rule 4 豁免——TreasureItemTable 先例;空表合法态=主时间线零扰动)
|
||||
"forge": "forge.tres", # ForgeTable——两根类型轴默认值+六路线行族;攻防所有权切分唯一数据面
|
||||
"battle_layout": "battle_layout.tres", # BattleLayoutTable——战斗屏分区几何与明细带高度单行
|
||||
"camp_scene_layout": "camp_scene_layout.tres", # CampSceneLayoutTable——营地场景 10 层贴图/命中矩形与门洞缺口单行
|
||||
}
|
||||
|
||||
var combat: ConfigDomainCombat # 战斗编排域七表(board_gen/variation/status_effects/enemy/skill/hero/settlement)
|
||||
var run: ConfigDomainRun # 冒险 runner 域七表(level/difficulty/level_gen_params/reward/adventure/action_economy/shop)
|
||||
var crossrun: ConfigDomainCrossrun # 跨局元进度域五表(meta/meta_tree/treasure_items/feats/forge)
|
||||
var presentation: ConfigDomainPresentation # 表现层布局域一表(battle_layout)
|
||||
var presentation: ConfigDomainPresentation # 表现层布局域两表(battle_layout/camp_scene_layout)
|
||||
|
||||
var is_ready := false
|
||||
var _diagnostics: Array[Dictionary] = [] # {severity, category, message}
|
||||
@@ -149,6 +150,9 @@ func _assign_loaded(loaded: Dictionary) -> void:
|
||||
crossrun.feats = loaded.get("feats")
|
||||
crossrun.forge = loaded.get("forge")
|
||||
presentation.battle_layout = loaded.get("battle_layout")
|
||||
presentation.camp_scene_layout = loaded.get("camp_scene_layout")
|
||||
for problem: String in presentation.camp_scene_layout.validate():
|
||||
_fatal("camp_scene_layout", problem) # 布局几何红线(命中区越画面/相交/侵顶栏)=数据致命
|
||||
# resolver 协作件注入三表(resolve 委托承载——resolver 文件与键零搬移)
|
||||
combat.board_gen._resolver = _resolver
|
||||
combat.enemy._resolver = _resolver
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## 表现层布局域容器(D2 域容器——config_db 表持有按域收敛):战斗屏布局一表类型化持有。
|
||||
## 表现层布局域容器(D2 域容器——config_db 表持有按域收敛):战斗屏与营地场景布局两表类型化持有。
|
||||
## 零方法零行为——纯持有结构;「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88 定稿注记)。
|
||||
class_name ConfigDomainPresentation
|
||||
extends Resource
|
||||
|
||||
var battle_layout: BattleLayoutTable
|
||||
var camp_scene_layout: CampSceneLayoutTable
|
||||
@@ -0,0 +1,13 @@
|
||||
## 营地场景单层规格(camp_scene_layout.tres 行件):层键+贴图矩形+命中矩形+命中键。
|
||||
## z 序=表内数组序(视图子节点序同源派生);坐标一律终稿画布 1280×800 像素坐标。
|
||||
class_name CampSceneLayerSpec
|
||||
extends Resource
|
||||
|
||||
@export var key: StringName = &"" # 层键(对齐 UiKit.TEX_CAMP_* 常量后缀)
|
||||
@export var texture_rect: Rect2 = Rect2() # 贴图矩形(1:1 像素贴图落位)
|
||||
@export var hotspot: Rect2 = Rect2() # 命中矩形(零矩形=不可点层——bg/wall/tent)
|
||||
@export var hotspot_key: StringName = &"" # object_pressed 载荷键(可点层必填)
|
||||
|
||||
|
||||
func is_clickable() -> bool:
|
||||
return hotspot.size.x > 0.0 and hotspot.size.y > 0.0
|
||||
@@ -0,0 +1 @@
|
||||
uid://bll8d0s1nmarf
|
||||
@@ -0,0 +1,67 @@
|
||||
## 营地场景布局表 schema(camp_scene_layout.tres):10 层贴图矩形/命中矩形/门洞缺口/围墙带包络/顶栏禁区。
|
||||
## 坐标数值唯一事实源=数据行本表——视图、探针、单测一律读表取值,禁另写坐标字面量。
|
||||
class_name CampSceneLayoutTable
|
||||
extends Resource
|
||||
|
||||
@export var canvas := Vector2(1280, 800) # 终稿画布(工程档同幅面)
|
||||
@export var hud_band_h := 48 # 顶栏禁区高度(命中不侵)
|
||||
@export var gate_gap := Vector2i(520, 760) # 门洞缺口 x 范围(透出背景层小路,禁填死)
|
||||
@export var wall_band := Rect2(0, 240, 1280, 70) # 围墙带包络(门柱顶 240—桩底 310;围墙贴图须落在其内)
|
||||
@export var layers: Array[CampSceneLayerSpec] = []
|
||||
|
||||
|
||||
## 加载期 fail-fast 校验(ConfigDB 装载即查,问题非空=数据致命)。
|
||||
## 校验面按件拆分:层身份与贴图矩形/命中区边界/命中区互斥/围墙带包络。
|
||||
func validate() -> Array[String]:
|
||||
var problems: Array[String] = []
|
||||
var clickables: Array[CampSceneLayerSpec] = _collect_clickables(problems)
|
||||
_check_hotspot_bounds(clickables, problems)
|
||||
_check_hotspot_disjoint(clickables, problems)
|
||||
_check_wall_band(problems)
|
||||
return problems
|
||||
|
||||
|
||||
func _collect_clickables(problems: Array[String]) -> Array[CampSceneLayerSpec]:
|
||||
var seen: Dictionary[StringName, bool] = {}
|
||||
var clickables: Array[CampSceneLayerSpec] = []
|
||||
for layer in layers:
|
||||
if layer.key == &"":
|
||||
problems.append("层键为空")
|
||||
continue
|
||||
if seen.has(layer.key):
|
||||
problems.append("层键重复: %s" % layer.key)
|
||||
seen[layer.key] = true
|
||||
if _out_of_canvas(layer.texture_rect):
|
||||
problems.append("贴图矩形越画面: %s %s" % [layer.key, layer.texture_rect])
|
||||
if layer.is_clickable():
|
||||
if layer.hotspot_key == &"":
|
||||
problems.append("可点层缺命中键: %s" % layer.key)
|
||||
clickables.append(layer)
|
||||
elif layer.hotspot_key != &"":
|
||||
problems.append("不可点层带命中键: %s" % layer.key)
|
||||
return clickables
|
||||
|
||||
|
||||
func _check_hotspot_bounds(clickables: Array[CampSceneLayerSpec], problems: Array[String]) -> void:
|
||||
for h in clickables:
|
||||
if h.hotspot.position.x < 0.0 or h.hotspot.position.y < hud_band_h \
|
||||
or _out_of_canvas(h.hotspot):
|
||||
problems.append("命中区越画面或侵顶栏禁区: %s %s" % [h.key, h.hotspot])
|
||||
|
||||
|
||||
func _check_hotspot_disjoint(clickables: Array[CampSceneLayerSpec], problems: Array[String]) -> void:
|
||||
for i in clickables.size():
|
||||
for j in range(i + 1, clickables.size()):
|
||||
if clickables[i].hotspot.intersects(clickables[j].hotspot):
|
||||
problems.append("命中区相交: %s × %s" % [clickables[i].key, clickables[j].key])
|
||||
|
||||
|
||||
func _check_wall_band(problems: Array[String]) -> void:
|
||||
for layer in layers:
|
||||
if layer.key == &"wall" and not wall_band.encloses(layer.texture_rect):
|
||||
problems.append("围墙贴图越围墙带包络: %s" % layer.texture_rect)
|
||||
|
||||
|
||||
func _out_of_canvas(rect: Rect2) -> bool:
|
||||
return rect.position.x < 0.0 or rect.position.y < 0.0 \
|
||||
or rect.end.x > canvas.x or rect.end.y > canvas.y
|
||||
@@ -0,0 +1 @@
|
||||
uid://c6p8wyvcc5upp
|
||||
@@ -0,0 +1,82 @@
|
||||
[gd_resource type="Resource" script_class="CampSceneLayoutTable" load_steps=13 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://data/schemas/camp_scene_layout_table.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://data/schemas/camp_scene_layer_spec.gd" id="2"]
|
||||
|
||||
[sub_resource type="Resource" id="1"]
|
||||
script = ExtResource("2")
|
||||
key = &"bg"
|
||||
texture_rect = Rect2(0, 0, 1280, 800)
|
||||
hotspot = Rect2(0, 0, 0, 0)
|
||||
hotspot_key = &""
|
||||
|
||||
[sub_resource type="Resource" id="2"]
|
||||
script = ExtResource("2")
|
||||
key = &"wall"
|
||||
texture_rect = Rect2(0, 261, 1280, 49)
|
||||
hotspot = Rect2(0, 0, 0, 0)
|
||||
hotspot_key = &""
|
||||
|
||||
[sub_resource type="Resource" id="3"]
|
||||
script = ExtResource("2")
|
||||
key = &"board"
|
||||
texture_rect = Rect2(845, 268, 130, 121)
|
||||
hotspot = Rect2(845, 268, 130, 140)
|
||||
hotspot_key = &"board"
|
||||
|
||||
[sub_resource type="Resource" id="4"]
|
||||
script = ExtResource("2")
|
||||
key = &"gate"
|
||||
texture_rect = Rect2(520, 250, 240, 211)
|
||||
hotspot = Rect2(520, 250, 240, 220)
|
||||
hotspot_key = &"gate"
|
||||
|
||||
[sub_resource type="Resource" id="5"]
|
||||
script = ExtResource("2")
|
||||
key = &"dummy"
|
||||
texture_rect = Rect2(340, 520, 120, 180)
|
||||
hotspot = Rect2(340, 520, 120, 180)
|
||||
hotspot_key = &"dummy"
|
||||
|
||||
[sub_resource type="Resource" id="6"]
|
||||
script = ExtResource("2")
|
||||
key = &"forge"
|
||||
texture_rect = Rect2(40, 467, 240, 193)
|
||||
hotspot = Rect2(40, 420, 240, 240)
|
||||
hotspot_key = &"forge"
|
||||
|
||||
[sub_resource type="Resource" id="7"]
|
||||
script = ExtResource("2")
|
||||
key = &"tent"
|
||||
texture_rect = Rect2(982, 520, 251, 170)
|
||||
hotspot = Rect2(0, 0, 0, 0)
|
||||
hotspot_key = &""
|
||||
|
||||
[sub_resource type="Resource" id="8"]
|
||||
script = ExtResource("2")
|
||||
key = &"chest"
|
||||
texture_rect = Rect2(1097, 700, 106, 90)
|
||||
hotspot = Rect2(1085, 700, 130, 90)
|
||||
hotspot_key = &"chest"
|
||||
|
||||
[sub_resource type="Resource" id="9"]
|
||||
script = ExtResource("2")
|
||||
key = &"notebook"
|
||||
texture_rect = Rect2(818, 704, 100, 66)
|
||||
hotspot = Rect2(818, 690, 100, 80)
|
||||
hotspot_key = &"notebook"
|
||||
|
||||
[sub_resource type="Resource" id="10"]
|
||||
script = ExtResource("2")
|
||||
key = &"campfire"
|
||||
texture_rect = Rect2(560, 571, 160, 149)
|
||||
hotspot = Rect2(560, 640, 160, 80)
|
||||
hotspot_key = &"campfire"
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
canvas = Vector2(1280, 800)
|
||||
hud_band_h = 48
|
||||
gate_gap = Vector2i(520, 760)
|
||||
wall_band = Rect2(0, 240, 1280, 70)
|
||||
layers = [SubResource("1"), SubResource("2"), SubResource("3"), SubResource("4"), SubResource("5"), SubResource("6"), SubResource("7"), SubResource("8"), SubResource("9"), SubResource("10")]
|
||||
@@ -7,7 +7,7 @@ extends RefCounted
|
||||
## 三束按域收拢(u21——同 u20 形;views=视图句柄/getters=查询 Callable 活引用/flow=编排 Callable):
|
||||
## 原 12 参长签名收 4 参——字段即原构造参数逐一同名迁移,键面=消费方改址合同。
|
||||
var db
|
||||
var views: Dictionary = {} # {camp_overlay, shop_overlay, toast, start_overlay, logger} # type-hint: ignore(三束异构混载——视图句柄/查询 Callable/编排 Callable)
|
||||
var views: Dictionary = {} # {camp_scene, forge_view, treasure_view, tree_view, codex_view, shop_overlay, toast, start_overlay, save_select_overlay, logger, refresh_bus} # type-hint: ignore(三束异构混载——视图句柄/刷新单口/查询 Callable/编排 Callable;卡④ T3 营地域改挂)
|
||||
var getters: Dictionary = {} # {meta_get, save_get, runner_get} # type-hint: ignore(同上——Callable 束)
|
||||
var flow: Dictionary = {} # {open_modal, close_modal, refresh_presentations} # type-hint: ignore(同上——Callable 束)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ extends RefCounted
|
||||
var db
|
||||
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面)
|
||||
var _save_get: Callable # () → 存档承载现值(同上——营地 recent 摘要读数面)
|
||||
var camp_overlay: CampOverlay
|
||||
var views: Dictionary = {} # {camp_scene, forge_view, treasure_view, tree_view, codex_view, shop_overlay, toast, start_overlay, save_select_overlay, logger, refresh_bus} # type-hint: ignore(三束异构混载——视图句柄/单口件;营地域句柄经 ctx.views 取用——组合根 ui.modals.camp 子容器同源)
|
||||
var shop_overlay: ShopOverlay
|
||||
var toast: ToastStack
|
||||
var start_overlay: Control # 开始层互斥面(开合仿设置面先例——非模态且树序在后,规范 2.3)
|
||||
@@ -23,7 +23,7 @@ func _init(ctx: CampFlowContext) -> void:
|
||||
db = ctx.db
|
||||
_meta_get = ctx.getters.meta_get
|
||||
_save_get = ctx.getters.save_get
|
||||
camp_overlay = ctx.views.camp_overlay
|
||||
views = ctx.views # 营地域五目标+刷新单口+关层收闭面(卡④ T3——CampOverlay 退役后投影目标改线)
|
||||
shop_overlay = ctx.views.shop_overlay
|
||||
toast = ctx.views.toast
|
||||
start_overlay = ctx.views.start_overlay
|
||||
@@ -34,21 +34,54 @@ func _init(ctx: CampFlowContext) -> void:
|
||||
_refresh_presentations = ctx.flow.refresh_presentations
|
||||
|
||||
|
||||
# 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
|
||||
# 规范 2.3 遮挡纪律;打开时先刷条件解锁——达标未解锁就地入账)
|
||||
# 打开营地场景根(进档落点——卡④ T3 改挂:开层=交互式场景图,四子界面经场景点选进)
|
||||
func open_camp() -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
return
|
||||
meta.refresh_treasure_unlocks() # 条件解锁刷新
|
||||
start_overlay.visible = false
|
||||
_open_modal.call(camp_overlay) # 模态栈单口(登记层开合一律走栈)
|
||||
camp_overlay.open_camp(camp_data())
|
||||
_open_modal.call(views.camp_scene) # 模态栈单口(场景根开——选档层被栈遮挡挂起)
|
||||
views.refresh_bus.refresh(camp_data()) # 进档动线尾显式刷新一闸(卡⑤ 契约 5 时机②——HUD 读数重读)
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# ---- 营地四子界面开层(场景点选路由分派——组合根 _on_camp_object_pressed;数据投喂=快照投影)----
|
||||
|
||||
|
||||
func open_camp_forge() -> void:
|
||||
_open_modal.call(views.forge_view)
|
||||
views.forge_view.open_camp(camp_data())
|
||||
|
||||
|
||||
func open_camp_treasure() -> void:
|
||||
_open_modal.call(views.treasure_view)
|
||||
views.treasure_view.open_camp(camp_data())
|
||||
|
||||
|
||||
func open_camp_tree() -> void:
|
||||
_open_modal.call(views.tree_view)
|
||||
views.tree_view.open_camp(camp_data())
|
||||
|
||||
|
||||
func open_camp_codex() -> void:
|
||||
_open_modal.call(views.codex_view)
|
||||
views.codex_view.open_camp(camp_data())
|
||||
|
||||
|
||||
# 子界面返回钮(视图只发 closed——关层归本口;场景根留底复现,开始层不动)
|
||||
func on_camp_sub_closed(view: Control) -> void:
|
||||
_close_modal.call(view)
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# 关闭整个营地(关层收闭——卡④ T3:营地层与选档层同收+开始层恢复,禁只关营地留下选档回闪;
|
||||
# 触发面=篝火/ESC 回主菜单动线,卡④ T4 接线)
|
||||
func on_camp_closed() -> void:
|
||||
_close_modal.call(camp_overlay) # 模态栈单口
|
||||
for v: Control in [views.camp_scene, views.forge_view, views.treasure_view, views.tree_view, views.codex_view]:
|
||||
if v.visible:
|
||||
_close_modal.call(v)
|
||||
_close_modal.call(views.save_select_overlay)
|
||||
start_overlay.visible = true
|
||||
_refresh_presentations.call() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
|
||||
|
||||
@@ -79,7 +112,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
if not meta.buy_treasure_unlock(attr):
|
||||
toast.show_toast("购买被拒(余额不足或不可购买)", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
return
|
||||
# Y 刷新升级行购买(双入口之一——树上 ks_refresh 同账;
|
||||
@@ -87,7 +120,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
if not meta.buy_attune_refresh():
|
||||
toast.show_toast("购买被拒", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -99,7 +132,7 @@ func on_camp_attune_toggle(item_id: String) -> void:
|
||||
if not meta.toggle_attunement(item_id):
|
||||
toast.show_toast("同调位已满——先卸下再装配", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
|
||||
|
||||
# 天赋树购买(写口唯一归 MetaProgression.tree_buy/tree_buy_fork;Fork 节点购=激活侧)
|
||||
@@ -111,7 +144,7 @@ func on_camp_tree_buy(node_id: String) -> void:
|
||||
if not ok:
|
||||
toast.show_toast("购买被拒——前置/行门槛/余额不足", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -123,7 +156,7 @@ func on_camp_fork_toggle(group: String, side: String) -> void:
|
||||
if not meta.tree_toggle_fork(group, side):
|
||||
toast.show_toast("切换被拒——未激活组请先购买", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -136,7 +169,7 @@ func on_camp_forge_buy(route_id: String) -> void:
|
||||
if not meta.forge_buy_level(route_id):
|
||||
toast.show_toast("锻造被拒——钥石不足或路线不可锻造", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -148,7 +181,7 @@ func on_camp_forge_unlock(route_id: String) -> void:
|
||||
if not meta.forge_buy_unlock(route_id):
|
||||
toast.show_toast("解锁被拒——钥石不足或已解锁", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -162,7 +195,7 @@ func on_camp_forge_select(route_id: String) -> void:
|
||||
if not ok:
|
||||
toast.show_toast("切换被拒——路线未解锁", "warn")
|
||||
return
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
|
||||
@@ -123,19 +123,37 @@ func _start_adventure_from_menu() -> void:
|
||||
|
||||
|
||||
# 确认弹窗分派(卡③ 契约 3):按 _confirm_consumer 路由——"delete_slot"=卡③ 删档确认
|
||||
#(确认才删、取消只关窗——裁决 3)。"new_adventure" 消费方已随卡② 主菜单确认链摘除退役
|
||||
#(mn6/V5——主菜单零确认残留),分派机制保留(卡④ 到位接入第三消费方不返工)。
|
||||
#(确认才删、取消只关窗——裁决 3)。"gate"/"gate_new"=卡④ 大门两分支消费方(有/无断点),
|
||||
# "campfire_leave"=篝火二次确认(卡④ T4 接入——分派机制扩展第三/第四消费方)。
|
||||
func _on_confirm_accepted() -> void:
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 模态栈单口
|
||||
match _confirm_consumer:
|
||||
"delete_slot":
|
||||
save_manager.slots.delete_slot(_confirm_consumer_slot)
|
||||
ui.modals.save_select_overlay.open_save_select(HudPresenter.save_select_data(save_manager)) # 就地刷新(该槽回空态)
|
||||
"gate":
|
||||
_camp_pres.on_camp_closed() # 冒险发起后营地场景屏隐藏(选档层同收——卡④ T4 关层收闭)
|
||||
_resume_from_menu() # 继续上一段冒险(现役续玩编排同源——卡④ 契约 4)
|
||||
"gate_new":
|
||||
_camp_pres.on_camp_closed()
|
||||
_start_adventure() # 开始新冒险(现役开局编排同源)
|
||||
"campfire_leave":
|
||||
_camp_pres.on_camp_closed() # 篝火确认离场=营地与选档层同收+开始层恢复(回主菜单)
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
# 次确认分派(三钮形态中钮——大门「开始新冒险」出口;卡④ T4)
|
||||
func _on_confirm_secondary() -> void:
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 模态栈单口
|
||||
match _confirm_consumer:
|
||||
"gate":
|
||||
_camp_pres.on_camp_closed()
|
||||
_start_adventure()
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
func _on_confirm_declined() -> void:
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 只关窗(取消无状态变更——裁决 3:取消不删);关侧恢复归 ModalFlow
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 只关窗(取消无状态变更——裁决 3:取消不删/卡④:退回营地);关侧恢复归 ModalFlow
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
@@ -500,12 +518,35 @@ func _sync_status_entry() -> void:
|
||||
MainOverlayFlow._sync_status_entry(self)
|
||||
|
||||
|
||||
# ---- 探针缝保缝(click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd)----
|
||||
# ---- 探针缝保缝(click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd / main_overlay_flow.gd)----
|
||||
|
||||
func _on_camp_closed() -> void:
|
||||
MainOverlayFlow._on_camp_closed(self)
|
||||
|
||||
|
||||
func _on_camp_object_pressed(key: StringName) -> void:
|
||||
MainOverlayFlow._on_camp_object_pressed(self, key) # 场景点选路由(卡④ T3——薄委托,实现归覆盖层流程承接件)
|
||||
|
||||
|
||||
# Esc 输入入口(产品码首个键盘入口——卡⑤ T1 契约 9 接线定稿:事件驱动 _unhandled_input、
|
||||
# KEY_ESCAPE 键值判定配 physical_keycode、pressed 且非 echo、禁 Input 单例轮询、不挂 ui_cancel)
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo and (event.keycode == KEY_ESCAPE or event.physical_keycode == KEY_ESCAPE):
|
||||
MainEscFlow.on_esc_input(self)
|
||||
|
||||
|
||||
func _on_esc_return() -> void:
|
||||
MainEscFlow._on_return_to_menu(self)
|
||||
|
||||
|
||||
func _on_esc_settings() -> void:
|
||||
MainEscFlow._on_settings(self)
|
||||
|
||||
|
||||
func _on_esc_quit() -> void:
|
||||
MainEscFlow._on_quit(self)
|
||||
|
||||
|
||||
func _camp_data() -> Dictionary:
|
||||
return MainOverlayFlow._camp_data(self)
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
## 组合根 Esc 流程承接件(产品码首个键盘输入入口——main._unhandled_input 同名薄委托落地):
|
||||
## Esc 四态路由(契约 9 判据单点=ModalStack 登记册现开集合,禁手枚名单)+三出口动线。
|
||||
## 静态承接件零成员态(仿 main_overlay_flow 形态——main 侧留同名薄委托,探针 main.X 路径零改动)。
|
||||
class_name MainEscFlow
|
||||
extends RefCounted
|
||||
|
||||
|
||||
## 营地语境判据(Q1 甲——场景根 visible;子界面开态抑制由契约 9① 现开集合承载,不并入本谓词)
|
||||
static func is_camp_context(root: Variant) -> bool:
|
||||
return root.ui.modals.camp.scene != null and root.ui.modals.camp.scene.visible
|
||||
|
||||
|
||||
## Esc 优先序(camp.md v2.3 定稿 2026-11-01 用户裁——有功能窗开着→先关最上层窗,覆盖卡⑤ 契约 9①
|
||||
## 的 Q1 甲无反馈;设置开在浮窗之上仍无反馈=契约 3 保留):
|
||||
## ①功能窗开着→close 最上层窗→①'设置开在浮窗之上→无反馈→②浮窗开→close(Q2 同键单义)→
|
||||
## ③营地语境→open→④非营地→无反馈
|
||||
static func on_esc_input(root: Variant) -> void:
|
||||
var esc: Control = root.ui.modals.camp.esc_overlay
|
||||
var window := _topmost_camp_window(root)
|
||||
if window != null:
|
||||
root.ui.modals.flow.close(window) # ① 先关最上层功能窗(v2.3 定稿)
|
||||
return
|
||||
if root.ui.modals.settings_overlay.visible:
|
||||
return # ①' 设置开在浮窗之上 → 无反馈(浮窗不隔层关闭——契约 3)
|
||||
if esc.visible:
|
||||
root.ui.modals.flow.close(esc) # ② 再按 Esc 关窗
|
||||
return
|
||||
if is_camp_context(root):
|
||||
root.ui.modals.flow.open(esc) # ③ 营地语境唤起
|
||||
# ④ 非营地语境 → 无反馈
|
||||
|
||||
|
||||
# 五功能窗现开查询(camp.md v2.3 五窗互斥单开——模态栈深恒 1,至多一窗在开;生涯页入窗集)
|
||||
static func _topmost_camp_window(root: Variant) -> Control:
|
||||
for w: Control in [root.ui.modals.camp.forge, root.ui.modals.camp.treasure, root.ui.modals.camp.tree,
|
||||
root.ui.modals.camp.codex, root.ui.modals.career_overlay]:
|
||||
if w != null and w.visible:
|
||||
return w
|
||||
return null
|
||||
|
||||
|
||||
|
||||
## 返回主菜单动线(契约 4):首步 close 浮窗(缺此步浮窗残留主菜单上方)→营地与选档层同收+开始层恢复
|
||||
static func _on_return_to_menu(root: Variant) -> void:
|
||||
root.ui.modals.flow.close(root.ui.modals.camp.esc_overlay)
|
||||
root._camp_pres.on_camp_closed() # 关层收闭单口(卡④ T3——营地层与选档层同收+开始层恢复)
|
||||
|
||||
|
||||
## 设置出口(浮窗登记在设置层之下——open 只隐上方层,浮窗由树序遮挡非属性隐藏;关后重现=栈语义)
|
||||
static func _on_settings(root: Variant) -> void:
|
||||
root.ui.modals.flow.open(root.ui.modals.settings_overlay)
|
||||
|
||||
|
||||
## 退出出口(_quit_game 原样复用——settings_store.save_settings+quit,零语义副作用)
|
||||
static func _on_quit(root: Variant) -> void:
|
||||
root._quit_game()
|
||||
@@ -0,0 +1 @@
|
||||
uid://caa868un7dnt4
|
||||
@@ -10,7 +10,11 @@ static func _open_settings(root: Variant) -> void:
|
||||
|
||||
static func _on_settings_closed(root: Variant) -> void:
|
||||
root.ui.modals.flow.close(root.ui.modals.settings_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
# 语境分支(卡⑤ 契约 3——谓词=camp_esc_overlay.visible,谁开的设置谁收):
|
||||
# 营地浮窗路径关设置回浮窗不回主菜单(栈语义:浮窗登记在下、由树序遮挡非属性隐藏);
|
||||
# 菜单路径保留开始层恢复硬副作用。
|
||||
if not root.ui.modals.camp.esc_overlay.visible:
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)----
|
||||
@@ -40,7 +44,43 @@ static func _open_career(root: Variant) -> void:
|
||||
|
||||
static func _on_career_closed(root: Variant) -> void:
|
||||
root.ui.modals.flow.close(root.ui.modals.career_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
if not root.ui.modals.camp.scene.visible: # 营地语境关生涯不回开始层(笔记本入口——卡④ T3;栈已复现场景根)
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# ---- 营地场景点选路由(卡④ T3/T4——场景根 object_pressed 组合根分派)----
|
||||
# 大门两分支(裁决 5,Q5 甲):点大门永远先出确认不直接进冒险——有断点=三出口弹窗
|
||||
#(继续上一段冒险/开始新冒险/取消),无断点=是否开始新冒险(是/否);cancelled 恒=无变更退回营地。
|
||||
# 篝火回主菜单必须二次确认(camp.md v2.2 裁定);确认后营地层与选档层同收+开始层恢复(关层收闭)。
|
||||
|
||||
|
||||
static func _on_camp_object_pressed(root: Variant, key: StringName) -> void:
|
||||
match key:
|
||||
&"forge":
|
||||
root._camp_pres.open_camp_forge()
|
||||
&"chest":
|
||||
root._camp_pres.open_camp_treasure()
|
||||
&"dummy":
|
||||
root._camp_pres.open_camp_tree()
|
||||
&"board":
|
||||
root._camp_pres.open_camp_codex()
|
||||
&"notebook":
|
||||
_open_career(root) # 生涯页入口改挂帐篷旁笔记本(CareerOverlay 本体零改——卡④ T3)
|
||||
&"gate":
|
||||
_open_gate_confirm(root)
|
||||
&"campfire":
|
||||
root._confirm_consumer = "campfire_leave"
|
||||
root.ui.modals.confirm_dialog.open("确定离开营地回到主菜单?", "离开", "留下")
|
||||
|
||||
|
||||
static func _open_gate_confirm(root: Variant) -> void:
|
||||
if root.save_manager.has_breakpoint():
|
||||
root._confirm_consumer = "gate"
|
||||
root.ui.modals.confirm_dialog.open_three("有未完成的冒险——继续上一段冒险,还是开始新冒险?",
|
||||
"继续上一段冒险", "开始新冒险", "取消")
|
||||
return
|
||||
root._confirm_consumer = "gate_new"
|
||||
root.ui.modals.confirm_dialog.open("是否开始新冒险?", "是", "否")
|
||||
|
||||
|
||||
# 生涯页快照投影 —— 承接体见 HudPresenter.career_data(开层调用点直接改引)
|
||||
@@ -65,7 +105,7 @@ static func _on_save_select_closed(root: Variant) -> void:
|
||||
|
||||
|
||||
|
||||
# 空槽新档链(Q3 裁乙暂行落点=现役 CampOverlay;卡④ 重立卡后改挂场景图):卡① 创建=激活=白板落盘,
|
||||
# 空槽新档链(卡④ T3 换代:落点=营地场景根——CampOverlay 随四柜台拆解退役):卡① 创建=激活=白板落盘,
|
||||
# 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。
|
||||
static func _on_save_select_new(root: Variant, slot: int) -> void:
|
||||
root.save_manager.slots.create_slot(slot)
|
||||
@@ -73,7 +113,7 @@ static func _on_save_select_new(root: Variant, slot: int) -> void:
|
||||
|
||||
|
||||
|
||||
# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→暂行落点 CampOverlay。
|
||||
# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→落点=营地场景根(卡④ T3)。
|
||||
static func _on_save_select_load(root: Variant, slot: int) -> void:
|
||||
root.save_manager.slots.activate_slot(slot)
|
||||
root._camp_pres.open_camp()
|
||||
|
||||
@@ -83,7 +83,7 @@ static func _build_battle_wiring(root: Variant) -> void:
|
||||
|
||||
static func _build_overlays(root: Variant) -> void:
|
||||
# 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局
|
||||
# < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗;
|
||||
# < 商店 < 营地场景根 < 四子界面 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗;
|
||||
# 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动)
|
||||
var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(root, root.db)
|
||||
root.ui.modals.legend_overlay = overlays.legend_overlay
|
||||
@@ -92,8 +92,34 @@ static func _build_overlays(root: Variant) -> void:
|
||||
root.ui.modals.shop_overlay = overlays.shop_overlay
|
||||
root.ui.screens.camp_screen = CampScreenScene.instantiate()
|
||||
root.add_child(root.ui.screens.camp_screen)
|
||||
root.ui.modals.camp_overlay = root.ui.screens.camp_screen.camp_overlay
|
||||
# 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child
|
||||
# 营地场景根装配(布局表经 ConfigDB 单点注入——视图禁自载表路径;CampOverlay 随四柜台拆解退役)
|
||||
var camp_layout: CampSceneLayoutTable = root.db.presentation.camp_scene_layout
|
||||
root.ui.screens.camp_screen.camp_scene.setup_layers(camp_layout)
|
||||
root.ui.screens.camp_screen.camp_scene.bind_hotspots(camp_layout)
|
||||
root.ui.modals.camp.scene = root.ui.screens.camp_screen.camp_scene
|
||||
# 营地四子界面(独立全屏页——场景根留底形态;句柄落 ui.modals.camp 子容器——禁平铺回 ui_modals)
|
||||
var forge := ForgeView.new()
|
||||
root.add_child(forge)
|
||||
root.ui.modals.camp.forge = forge
|
||||
var treasure := TreasureView.new()
|
||||
root.add_child(treasure)
|
||||
root.ui.modals.camp.treasure = treasure
|
||||
var tree_view := TreeView.new()
|
||||
root.add_child(tree_view)
|
||||
root.ui.modals.camp.tree = tree_view
|
||||
var codex := CodexView.new()
|
||||
root.add_child(codex)
|
||||
root.ui.modals.camp.codex = codex
|
||||
# Esc 浮窗(卡⑤——登记于设置层之前的模态;三钮意图经 MainEscFlow 路由)
|
||||
var esc_overlay := CampEscOverlay.new()
|
||||
esc_overlay.setup_buttons()
|
||||
esc_overlay.bind_signals()
|
||||
root.add_child(esc_overlay)
|
||||
root.ui.modals.camp.esc_overlay = esc_overlay
|
||||
# 营地顶栏 HUD 栏(卡⑤——钥石余额常驻;挂卡④场景根顶栏预留位+入刷新单口目标册)
|
||||
var hud_band := CampHudBand.new()
|
||||
root.ui.modals.camp.scene.set_hud_band(hud_band)
|
||||
# 屏壳/子界面插位由 LAYER_CONTRACT 槽统一落位——禁散落 move_child
|
||||
root.ui.modals.legend_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.legend_overlay))
|
||||
root.ui.modals.node_overlay.continue_pressed.connect(root._on_node_continue)
|
||||
root.ui.modals.level_screen.route_chosen.connect(root._on_route_chosen)
|
||||
@@ -158,9 +184,18 @@ static func _build_menu_wiring(root: Variant) -> void:
|
||||
root.ui.screens.debug_badge.pressed.connect(root._on_debug_badge_pressed)
|
||||
# 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发;
|
||||
# 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入)
|
||||
# 营地刷新单口(各写口尾→现开界面+场景根 HUD 一闸完成——卡④ T3;卡⑤ HUD band 随卡⑤挂册)
|
||||
var camp_bus := CampRefreshBus.new()
|
||||
camp_bus.bind_targets([root.ui.modals.camp.forge, root.ui.modals.camp.treasure,
|
||||
root.ui.modals.camp.tree, root.ui.modals.camp.codex,
|
||||
root.ui.screens.camp_screen.camp_scene.hud_band] as Array[Control])
|
||||
root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db,
|
||||
{"camp_overlay": root.ui.modals.camp_overlay, "shop_overlay": root.ui.modals.shop_overlay,
|
||||
"toast": root.ui.battle.board_zone.toast, "start_overlay": root.ui.screens.start_overlay, "logger": root.logger},
|
||||
{"camp_scene": root.ui.modals.camp.scene, "forge_view": root.ui.modals.camp.forge,
|
||||
"treasure_view": root.ui.modals.camp.treasure, "tree_view": root.ui.modals.camp.tree,
|
||||
"codex_view": root.ui.modals.camp.codex, "shop_overlay": root.ui.modals.shop_overlay,
|
||||
"toast": root.ui.battle.board_zone.toast, "start_overlay": root.ui.screens.start_overlay,
|
||||
"save_select_overlay": root.ui.modals.save_select_overlay, "logger": root.logger,
|
||||
"refresh_bus": camp_bus},
|
||||
{"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager,
|
||||
"runner_get": func() -> Variant: return root.run_scope.runner},
|
||||
{"open_modal": root.ui.modals.flow.open, "close_modal": root.ui.modals.flow.close,
|
||||
@@ -168,14 +203,19 @@ static func _build_menu_wiring(root: Variant) -> void:
|
||||
root.ui.modals.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy)
|
||||
root.ui.modals.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1
|
||||
root.ui.modals.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave)
|
||||
root.ui.modals.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy)
|
||||
root.ui.modals.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle)
|
||||
root.ui.modals.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy)
|
||||
root.ui.modals.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle)
|
||||
root.ui.modals.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression)
|
||||
root.ui.modals.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock)
|
||||
root.ui.modals.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select)
|
||||
root.ui.modals.camp_overlay.closed.connect(root._camp_pres.on_camp_closed)
|
||||
# 营地信号接线(CampOverlay 退役——四子界面意图信号+场景点选组合根路由+closed 经 bind 携视图)
|
||||
root.ui.modals.camp.forge.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy)
|
||||
root.ui.modals.camp.forge.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock)
|
||||
root.ui.modals.camp.forge.forge_select_requested.connect(root._camp_pres.on_camp_forge_select)
|
||||
root.ui.modals.camp.treasure.buy_requested.connect(root._camp_pres.on_camp_buy)
|
||||
root.ui.modals.camp.treasure.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle)
|
||||
root.ui.modals.camp.tree.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy)
|
||||
root.ui.modals.camp.tree.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle)
|
||||
root.ui.modals.camp.forge.closed.connect(root._camp_pres.on_camp_sub_closed.bind(root.ui.modals.camp.forge))
|
||||
root.ui.modals.camp.treasure.closed.connect(root._camp_pres.on_camp_sub_closed.bind(root.ui.modals.camp.treasure))
|
||||
root.ui.modals.camp.tree.closed.connect(root._camp_pres.on_camp_sub_closed.bind(root.ui.modals.camp.tree))
|
||||
root.ui.modals.camp.codex.closed.connect(root._camp_pres.on_camp_sub_closed.bind(root.ui.modals.camp.codex))
|
||||
root.ui.modals.camp.scene.object_pressed.connect(root._on_camp_object_pressed)
|
||||
root.ui.screens.menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口
|
||||
root.ui.screens.menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单
|
||||
|
||||
@@ -194,7 +234,12 @@ static func _build_debug_and_resolvers(root: Variant) -> void:
|
||||
# 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer,
|
||||
# confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工)
|
||||
root.ui.modals.confirm_dialog.confirmed.connect(root._on_confirm_accepted)
|
||||
root.ui.modals.confirm_dialog.secondary_confirmed.connect(root._on_confirm_secondary) # 三钮次确认(卡④ 大门「开始新冒险」出口)
|
||||
root.ui.modals.confirm_dialog.cancelled.connect(root._on_confirm_declined)
|
||||
# Esc 浮窗三出口接线(卡⑤——意图信号经薄委托归 MainEscFlow 动线;输入入口=main._unhandled_input)
|
||||
root.ui.modals.camp.esc_overlay.return_requested.connect(root._on_esc_return)
|
||||
root.ui.modals.camp.esc_overlay.settings_requested.connect(root._on_esc_settings)
|
||||
root.ui.modals.camp.esc_overlay.quit_requested.connect(root._on_esc_quit)
|
||||
|
||||
# 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
|
||||
# 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
class_name CampScreen
|
||||
extends Control
|
||||
# 营地屏壳(营地覆盖层的屏壳容器)。
|
||||
# 营地覆盖层视图原样构建(CampOverlay 类视图继续代码构建、零 .tscn 化改造——契约 2);
|
||||
# 购买/开合接线归组合根经 CampShopPresenter(信号连接只在组合根)。
|
||||
# 可独立实例化(F6/headless 挂载)——V2 探针断言载体。
|
||||
# 营地屏壳(营地场景根的屏壳容器)。
|
||||
# 场景图视图代码构建(CampSceneView 继续代码构建、零 .tscn 化改造);布局表经组合根注入
|
||||
# (main_ui_build 装配段 setup_layers/bind_hotspots——视图禁自载表路径);购买/开合接线归组合根经
|
||||
# CampShopPresenter(信号连接只在组合根)。可独立实例化(F6/headless 挂载)——V2 探针断言载体。
|
||||
|
||||
var camp_overlay: CampOverlay # 营地覆盖层(原 build_overlays 营地段迁入——main 组合根句柄指向本屏成员)
|
||||
var camp_scene: CampSceneView # 营地场景图(交互式营地场景根——main 组合根句柄指向本屏成员)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE # 屏壳=纯容器(ui_input 实证:全屏 STOP 壳会吃掉下层模态鼠标输入)
|
||||
camp_overlay = CampOverlay.new()
|
||||
add_child(camp_overlay)
|
||||
camp_scene = CampSceneView.new()
|
||||
add_child(camp_scene)
|
||||
@@ -0,0 +1,12 @@
|
||||
## 营地交互域模态句柄子容器:持营地域全部模态句柄(终态六枚=营地场景根+锻造/秘宝/天赋树/图鉴四子界面+Esc 浮窗)。
|
||||
## RefCounted 不挂树;随 UiModals._init 构造(与 flow 同拍)。Esc 浮窗枚随卡⑤落——禁空占位凑结构。
|
||||
## 新营地句柄禁平铺回 ui_modals:营地域句柄寻址一律 ui.modals.camp.X 二层(模态句柄册分域纪律——全量分家另立批)。
|
||||
class_name UiCampModals
|
||||
extends RefCounted
|
||||
|
||||
var scene: CampSceneView # 营地场景根(交互式营地场景图——camp_screen 屏壳成员住宿)
|
||||
var forge: ForgeView # 锻造工坊子界面
|
||||
var treasure: TreasureView # 秘宝匣子界面
|
||||
var tree: TreeView # 天赋树子界面
|
||||
var codex: CodexView # 奖励格图鉴子界面(占位挂起态)
|
||||
var esc_overlay: CampEscOverlay # Esc 浮窗(卡⑤——终态六枚落满)
|
||||
@@ -0,0 +1 @@
|
||||
uid://goaqtequmh8s
|
||||
@@ -1,4 +1,4 @@
|
||||
## 模态层域句柄册:十三个模态覆盖层句柄+模态栈编排(ModalFlow——登记/开合/输入锁谓词)的注册面域容器。
|
||||
## 模态层域句柄册:十二个平铺模态句柄+营地交互域子容器(camp)+模态栈编排(ModalFlow——登记/开合/输入锁谓词)的注册面域容器。
|
||||
## RefCounted 不挂树;模态登记唯一口=register_layer(anchors 撑满宿主+排入 ModalStack)。
|
||||
## flow 随容器构造即建(装配早期注入点经 root.ui.modals.flow.* 取 Callable——晚于 flow 建立时点)。
|
||||
class_name UiModals
|
||||
@@ -11,7 +11,7 @@ var level_screen: LevelMapScreen
|
||||
var curio_overlay: CurioPickOverlay
|
||||
var end_overlay: BattleEndOverlay
|
||||
var shop_overlay: ShopOverlay
|
||||
var camp_overlay: CampOverlay
|
||||
var camp: UiCampModals
|
||||
var settings_overlay: SettingsOverlay
|
||||
var status_panel: StatusPanel
|
||||
var career_overlay: CareerOverlay
|
||||
@@ -22,6 +22,7 @@ var debug_overlay: DebugOverlay
|
||||
|
||||
func _init() -> void:
|
||||
flow = ModalFlow.new() # 查询/钩子 Callable 由 _build_modal_flow 注入(注入时点不变——注入值面向已在座实例)
|
||||
camp = UiCampModals.new() # 营地域句柄子容器(分域纪律——营地域句柄禁平铺回本册)
|
||||
|
||||
|
||||
func register_layer(m: Control, tags: Array = []) -> void:
|
||||
|
||||
@@ -22,7 +22,12 @@ const LAYER_CONTRACT: Array[Dictionary] = [
|
||||
{"key": "curio_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "end_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "shop_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "camp_overlay", "kind": "modal", "tags": [], "shell": "camp_screen"},
|
||||
{"key": "camp_scene", "kind": "modal", "tags": [], "shell": "camp_screen"}, # 营地场景根(交互式场景图——卡④)
|
||||
{"key": "forge_view", "kind": "modal", "tags": []}, # 营地四子界面(独立全屏页——场景根留底形态,卡④ T3)
|
||||
{"key": "treasure_view", "kind": "modal", "tags": []},
|
||||
{"key": "tree_view", "kind": "modal", "tags": []},
|
||||
{"key": "codex_view", "kind": "modal", "tags": []},
|
||||
{"key": "camp_esc_overlay", "kind": "modal", "tags": []}, # Esc 浮窗(卡⑤——登记于营地层与设置层之间,契约 3 栈语义正解)
|
||||
{"key": "settings_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "status_panel", "kind": "modal", "tags": []},
|
||||
{"key": "career_overlay", "kind": "modal", "tags": []},
|
||||
@@ -58,7 +63,12 @@ func node(key: String) -> Node:
|
||||
"end_overlay": return modals.end_overlay
|
||||
"shop_overlay": return modals.shop_overlay
|
||||
"camp_screen": return screens.camp_screen
|
||||
"camp_overlay": return modals.camp_overlay
|
||||
"camp_scene": return modals.camp.scene
|
||||
"forge_view": return modals.camp.forge
|
||||
"treasure_view": return modals.camp.treasure
|
||||
"tree_view": return modals.camp.tree
|
||||
"codex_view": return modals.camp.codex
|
||||
"camp_esc_overlay": return modals.camp.esc_overlay
|
||||
"settings_overlay": return modals.settings_overlay
|
||||
"status_panel": return modals.status_panel
|
||||
"career_overlay": return modals.career_overlay
|
||||
|
||||
@@ -203,15 +203,11 @@ func set_world_font(f: Font) -> void:
|
||||
|
||||
# D 区可用面积(AP 条之外)→ 格边长 = floor((min(宽, 高) − 预留 − (N−1)×格间距) / N)(battle.md 格尺寸公式)
|
||||
# 公式须补算 (N−1)×2px 网格间距与垂直预留 24px,否则 12×12 板实高溢出可用区 ~6px,下缘被 clip_contents 裁掉。
|
||||
# 算式唯一落点 = _cell_for(迭代 46 卡C——探针与单元测试共用该静态函数求值,禁复写算式造第二口径)。
|
||||
func set_cell_area(avail: Vector2) -> void:
|
||||
if _board == null or _board.width == 0:
|
||||
return
|
||||
var n: int = maxi(_board.width, _board.height)
|
||||
var sep := SEP # 格间距(_init 同值)
|
||||
var d := DEPTH_PX
|
||||
var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出)
|
||||
var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))
|
||||
_cell = float(clampi(cell, 26, 46))
|
||||
_cell = float(_cell_for(maxi(_board.width, _board.height), avail))
|
||||
_hero_sprite.set_cell_size(_cell, float(SEP))
|
||||
var stride := _cell + float(SEP)
|
||||
for c in _cells:
|
||||
@@ -224,6 +220,17 @@ func set_cell_area(avail: Vector2) -> void:
|
||||
_update_world_size()
|
||||
|
||||
|
||||
# 格边长算式唯一落点(迭代 46 卡C T1 自 set_cell_area 抽出——逐位同式:SEP/DEPTH_PX 读本文件
|
||||
# 既有常量、clampi [26,46] 留本函数内;名字带下划线是 S13 max-public-methods 27 顶格的约束,禁改公开名。
|
||||
# 探针(非当前板型 13/14×实板可用面积)与单元测试(四档板型)经本函数求值,复写算式即造第二口径)
|
||||
static func _cell_for(n: int, avail: Vector2) -> int:
|
||||
var sep := SEP # 格间距(_init 同值)
|
||||
var d := DEPTH_PX
|
||||
var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出)
|
||||
var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))
|
||||
return clampi(cell, 26, 46)
|
||||
|
||||
|
||||
# 悬崖参数下发(网格像素尺寸/步距/列数/材质锚色——BoardWorld._draw 消费)
|
||||
func _refresh_slab() -> void:
|
||||
if _board == null or _board.width == 0:
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## Esc 浮窗视图(营地语境 Esc 唤起):返回主菜单/设置/退出游戏三钮(裁决 9——三项且仅三项)。
|
||||
## 壳=UiKit.modal_shell(居中模态壳,ui_kit 工厂单点);退出钮=UiKit.danger_button;零新素材零新色值。
|
||||
## Q2 再按 Esc 关窗=组合根 Esc 路由直呼 flow.close(本视图无自关通路);三钮 pressed 外发意图不内含动线。
|
||||
## 零动效(契约 6——直开直关,子树零 AnimationPlayer、源码零动效令牌)。
|
||||
class_name CampEscOverlay
|
||||
extends Control
|
||||
|
||||
signal return_requested ## 返回主菜单意图(动线归 MainEscFlow._on_return_to_menu——首步 close 浮窗)
|
||||
signal settings_requested ## 设置意图(开设置层——浮窗登记在其下,栈语义关后重现)
|
||||
signal quit_requested ## 退出游戏意图(_quit_game 原样复用)
|
||||
|
||||
var _return_btn: Button
|
||||
var _settings_btn: Button
|
||||
var _quit_btn: Button
|
||||
|
||||
|
||||
func setup_buttons() -> void:
|
||||
var box: VBoxContainer = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(360, 0), 12,
|
||||
UiKit.panel_skinned(Vector4(14, 12, 14, 12))).box
|
||||
_return_btn = UiKit.button("返回主菜单")
|
||||
box.add_child(_return_btn)
|
||||
_settings_btn = UiKit.button("设置")
|
||||
box.add_child(_settings_btn)
|
||||
_quit_btn = UiKit.danger_button("退出游戏")
|
||||
box.add_child(_quit_btn)
|
||||
visible = false # 登记模态初始隐(开合走栈——模态纪律)
|
||||
|
||||
|
||||
func bind_signals() -> void:
|
||||
_return_btn.pressed.connect(func() -> void: return_requested.emit())
|
||||
_settings_btn.pressed.connect(func() -> void: settings_requested.emit())
|
||||
_quit_btn.pressed.connect(func() -> void: quit_requested.emit())
|
||||
@@ -0,0 +1 @@
|
||||
uid://col25elh1utw5
|
||||
@@ -0,0 +1,27 @@
|
||||
## 营地顶栏小 HUD 栏(裁决 10——钥石余额常驻,最小集形态):承载 UiKit.keystone_badge(工厂单点)。
|
||||
## 读数只经 camp_data() 投影单口(禁第二账本、禁视图自算);刷新挂卡④ CampRefreshBus 单口
|
||||
## (bus 目标册成员——显式触发才变,禁 _ready 自刷/自持第二入口,契约 5);载体=PRESET_TOP_WIDE 48px 浮层。
|
||||
class_name CampHudBand
|
||||
extends Control
|
||||
|
||||
var _badge: PanelContainer
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||
custom_minimum_size = Vector2(0, 48) # 高 48px 浮层覆盖、不挤压画面(camp.md §顶栏)
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE # HUD 栏不挡场景命中区(浮层覆盖语义)
|
||||
_badge = UiKit.keystone_badge("钥石 0")
|
||||
_badge.set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
||||
_badge.position = Vector2(-160, 8) # 顶栏右位次(UiKit 现行件域内——代拍板 3)
|
||||
add_child(_badge)
|
||||
|
||||
|
||||
## 余额写入(格式串=既有消费面同款「钥石 %d」——badge 工厂约定单点,禁第二格式串)
|
||||
func set_balance(v: int) -> void:
|
||||
_badge.value_label.text = "钥石 %d" % v
|
||||
|
||||
|
||||
## 刷新单口落点(CampRefreshBus 目标册成员——显式触发才变,契约 5 刷新时机①②③同一闸)
|
||||
func refresh_camp(camp_data: Dictionary) -> void:
|
||||
set_balance(int(camp_data.get("balance", 0)))
|
||||
@@ -0,0 +1 @@
|
||||
uid://by6ugjfdicgud
|
||||
@@ -1,655 +0,0 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 营地覆盖层:树淬炼/锻造工坊/秘宝匣/奖励格图鉴四柜台只读投影,购买与切换意图经信号外发。
|
||||
## 柜台锁定态(钥匙未点亮)开/关零副作用——零写档零报错、锁定行纯文案无可交互购买路径。
|
||||
## 开合与开始层互斥归组合根承接(打开前隐藏开始层)。
|
||||
## 内部状态件(u1 子域拆件——契约 9 职责轴):_TreeState=树淬炼柜台内容容器(单一变化原因=树行板结构);
|
||||
## _ForgeState=锻造工坊与秘宝匣两柜台行板与锁定门(钥匙门同构);_AttuneState=同调装配段节点与钮册;
|
||||
## _SideState=Y 刷新升级行与最近冒险账面。探针直读冻结成员(_balance_label/_tree_buy_btns/
|
||||
## _tree_fork_btns/_workshop_holder/_workshop_rows)住宿主不进状态件。
|
||||
class_name CampOverlay
|
||||
extends Control
|
||||
|
||||
signal buy_requested(attr: String)
|
||||
signal closed
|
||||
signal attune_toggled(item_id: String) ## 同调装配切换意图(写口唯一归 MetaProgression.toggle_attunement)
|
||||
signal tree_buy_requested(node_id: String) ## 天赋树购买意图(写口唯一归 MetaProgression.tree_buy)
|
||||
signal tree_fork_toggled(group: String, side: String) ## 双态切换意图(一节点双态——零币切态)
|
||||
signal forge_buy_requested(route_id: String) ## 锻造路线购买意图(写口唯一归 MetaProgression.forge_buy_level)
|
||||
signal forge_unlock_requested(route_id: String) ## 锻造路线解锁意图(写口唯一归 MetaProgression.forge_buy_unlock)
|
||||
signal forge_select_requested(route_id: String) ## 锻造路线选中意图(写口唯一归 MetaProgression.forge_select)
|
||||
|
||||
# 方向稿取色(柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit
|
||||
const HEADER_BAND_COL := UiKit.CAMP_HEADER_BAND
|
||||
const ROW_PLATE_COL := UiKit.CAMP_ROW_PLATE
|
||||
|
||||
|
||||
class _TreeState extends RefCounted:
|
||||
var _tree_holder: VBoxContainer # 树淬炼柜台内容(主界面——脊柱两行+钥匙/socket 行板+Fork 双态钮)
|
||||
|
||||
|
||||
class _ForgeState extends RefCounted:
|
||||
var _forge_lock_l: Label # 锻造工坊柜台锁定态行(钥匙未点亮)
|
||||
var _forge_workbench: VBoxContainer # 锻造工坊工作间(钥匙点亮后展开——两区六路线行板)
|
||||
var _forge_zone_weapon: VBoxContainer # 武器锻造区行板容器(三路线)
|
||||
var _forge_zone_armor: VBoxContainer # 护甲锻造区行板容器(三路线)
|
||||
var _forge_rows: Array[Dictionary] = [] # [{route_id, name_l, lvl_l, status_l, buy_btn, unlock_btn, select_btn, next_price, unlock_price}](open/refresh 重建;探针消费)
|
||||
var _ws_scroll: ScrollContainer # 秘宝匣柜台内容容器(钥匙点亮后可见)
|
||||
var _ws_lock_l: Label # 秘宝匣柜台锁定态行
|
||||
|
||||
|
||||
class _AttuneState extends RefCounted:
|
||||
var _attune_header_l: Label # 同调装配段(Slots 点亮呈现)
|
||||
var _attune_btns: Array[Dictionary] = [] # [{item_id, btn}]——装配/卸下切换钮(满位禁选随刷)
|
||||
var _attune_nodes: Array[Control] = [] # 段内节点册(open_camp 重建时定向清理,不扰工坊六行)
|
||||
|
||||
|
||||
class _SideState extends RefCounted:
|
||||
var _refresh_l: Label # Y 刷新升级行(双入口之一——树上 ks_refresh 同账)
|
||||
var _refresh_btn: Button
|
||||
var _recent_list: Label # 最近冒险(逐行拼接 label——只读账面,随 open_camp 重建)
|
||||
|
||||
|
||||
var _balance_label: Label # 探针直读冻结(S17①——住宿主)
|
||||
var _tree_buy_btns: Array[Dictionary] = [] # [{node_id, btn}]——树购买/解锁钮登记面(open/refresh 重建时刷新;探针消费;住宿主)
|
||||
var _tree_fork_btns: Array[Dictionary] = [] # [{group, side, btn}]——Fork 双态切换钮登记面(探针消费;住宿主)
|
||||
var _workshop_holder: VBoxContainer # 秘宝匣柜台行板(六行+同调装配段+Y 升级行;探针直读冻结——住宿主)
|
||||
var _workshop_rows: Array[Dictionary] = [] # [{item_id, progress_l, status_l}](探针消费;住宿主)
|
||||
var _tree := _TreeState.new()
|
||||
var _forge := _ForgeState.new()
|
||||
var _attune := _AttuneState.new()
|
||||
var _side := _SideState.new()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# 底=不透明暖亮档(半透 dim 会透出底下棋盘/战斗提示条,故须实底;demo #v-camp=bg-bright 实底,
|
||||
# 遮蔽语义由组合根输入锁兜底)
|
||||
var dim := UiKit._modal_dim(self, UiKit.BG_BRIGHT)
|
||||
# 石纹弱质感+暗角(demo .tilebg 0.14/.vignette;缺位不挂即回退平色)
|
||||
var bd := UiKit.backdrop_tile()
|
||||
if bd != null:
|
||||
dim.add_child(bd)
|
||||
dim.add_child(UiKit.vignette_rect())
|
||||
|
||||
var root := MarginContainer.new()
|
||||
root.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
root.add_theme_constant_override("margin_left", 16)
|
||||
root.add_theme_constant_override("margin_right", 16)
|
||||
root.add_theme_constant_override("margin_top", 12)
|
||||
root.add_theme_constant_override("margin_bottom", 12)
|
||||
dim.add_child(root)
|
||||
var col := VBoxContainer.new()
|
||||
col.add_theme_constant_override("separation", 8)
|
||||
root.add_child(col)
|
||||
|
||||
# ── 顶栏:左铭牌(标题+副题)/ 右钥石徽 ──
|
||||
_build_top_bar(col)
|
||||
|
||||
# ── 四柜台并排(程序占位重排)──
|
||||
# 宽度不足横向滚动(四柜 300 min 宽 > 窄窗可用宽时横滚兜底;纵向撑满余高)
|
||||
var camp_scroll := ScrollContainer.new()
|
||||
camp_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
camp_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
|
||||
camp_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
col.add_child(camp_scroll)
|
||||
var mid := HBoxContainer.new()
|
||||
mid.add_theme_constant_override("separation", 14)
|
||||
mid.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
camp_scroll.add_child(mid)
|
||||
_build_tree_counter(mid)
|
||||
_build_forge_counter(mid)
|
||||
_build_workshop_counter(mid)
|
||||
_build_atlas_counter(mid)
|
||||
_build_bottom_bar(col)
|
||||
visible = false
|
||||
|
||||
|
||||
## 顶栏域:铭牌(标题+副题)+钥石徽章
|
||||
func _build_top_bar(col: VBoxContainer) -> void:
|
||||
var top := HBoxContainer.new()
|
||||
top.add_theme_constant_override("separation", 10)
|
||||
col.add_child(top)
|
||||
var plaque := PanelContainer.new()
|
||||
plaque.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8)))
|
||||
top.add_child(plaque)
|
||||
var plaque_col := VBoxContainer.new()
|
||||
plaque.add_child(plaque_col)
|
||||
var title := UiKit.label("营地 · 天赋树",UiKit.FS_TITLE, true, UiKit.INK_TITLE)
|
||||
plaque_col.add_child(title)
|
||||
var sub := UiKit.label("营地本身即树——钥石逐节点点亮,属性/规则/工作间都在树上",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # STONE 对铭牌板 3.59:1 越带,换灰石深化 5.98:1
|
||||
plaque_col.add_child(sub)
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(spacer)
|
||||
# 钥石徽章烛光黄底+深墨字(demo ②)。UiKit.keystone_badge 工厂单点
|
||||
var badge := UiKit.keystone_badge("钥石 0")
|
||||
top.add_child(badge)
|
||||
_balance_label = badge.value_label
|
||||
|
||||
|
||||
## 柜一·树淬炼(树脊柱界面形态=主界面:一线一节点行板内升级+Fork 双态切换钮)
|
||||
func _build_tree_counter(mid: HBoxContainer) -> void:
|
||||
_tree._tree_holder = VBoxContainer.new()
|
||||
_tree._tree_holder.add_theme_constant_override("separation", 6)
|
||||
var tree_scroll := ScrollContainer.new()
|
||||
tree_scroll.custom_minimum_size = Vector2(300, 336)
|
||||
tree_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
tree_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
tree_scroll.add_child(_tree._tree_holder)
|
||||
var tree_panel := _counter_panel("天赋树 · 淬炼", "", tree_scroll, 300)
|
||||
tree_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
tree_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(tree_panel)
|
||||
|
||||
|
||||
## 柜二·锻造工坊(钥匙门——forge_workshop 点亮前锁定态;点亮=工作间两区六路线行板)
|
||||
func _build_forge_counter(mid: HBoxContainer) -> void:
|
||||
_forge._forge_lock_l = UiKit.label("🔒 锻造工坊未开放\n\n点亮树上「锻造工坊」钥匙节点后展开工作间。\n(钥匙免费、无前置——树上直接点亮)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_forge._forge_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
_forge._forge_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_forge._forge_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_forge._forge_workbench = VBoxContainer.new()
|
||||
_forge._forge_workbench.add_theme_constant_override("separation", 6)
|
||||
_forge._forge_workbench.visible = false # 默认锁定态(钥匙门未判前零行板——门开才建)
|
||||
_forge._forge_zone_weapon = VBoxContainer.new()
|
||||
_forge._forge_zone_weapon.add_theme_constant_override("separation", 4)
|
||||
_forge._forge_zone_armor = VBoxContainer.new()
|
||||
_forge._forge_zone_armor.add_theme_constant_override("separation", 4)
|
||||
var wb_wh := UiKit.label("── 武器锻造区 ──",UiKit.FS_HINT, true, UiKit.GOLD_INK) # 同款深金墨(对柜台板 5.21:1)
|
||||
_forge._forge_workbench.add_child(wb_wh)
|
||||
_forge._forge_workbench.add_child(_forge._forge_zone_weapon)
|
||||
var wb_ah := UiKit.label("── 护甲锻造区 ──",UiKit.FS_HINT, true, UiKit.GOLD_INK)
|
||||
_forge._forge_workbench.add_child(wb_ah)
|
||||
_forge._forge_workbench.add_child(_forge._forge_zone_armor)
|
||||
var forge_holder := VBoxContainer.new()
|
||||
forge_holder.add_theme_constant_override("separation", 6)
|
||||
forge_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(与树/秘宝匣 scroll 336 min 对齐——锁定态仅锁文也撑住栏高)
|
||||
forge_holder.add_child(_forge._forge_lock_l)
|
||||
forge_holder.add_child(_forge._forge_workbench)
|
||||
var forge_panel := _counter_panel("锻造工坊", UiKit.TEX_ICON_FORGE, forge_holder, 300)
|
||||
forge_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
forge_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(forge_panel)
|
||||
|
||||
|
||||
## 柜三·秘宝匣(钥匙门——ks_workshop 点亮前锁定态;点亮=现工坊内容整体迁入:六行+同调装配段+Y 升级行)
|
||||
func _build_workshop_counter(mid: HBoxContainer) -> void:
|
||||
_forge._ws_lock_l = UiKit.label("🔒 秘宝匣未开放\n\n点亮树上「秘宝匣」钥匙节点后展开。\n(前置=脊柱生命第 1 级)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_forge._ws_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
_forge._ws_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_forge._ws_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_forge._ws_scroll = ScrollContainer.new()
|
||||
_forge._ws_scroll.custom_minimum_size = Vector2(300, 336)
|
||||
_forge._ws_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_forge._ws_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
_workshop_holder = VBoxContainer.new()
|
||||
_workshop_holder.add_theme_constant_override("separation", 6)
|
||||
_forge._ws_scroll.add_child(_workshop_holder)
|
||||
var ws_holder := VBoxContainer.new()
|
||||
ws_holder.add_theme_constant_override("separation", 6)
|
||||
ws_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(锁定态 scroll 隐、仅锁文也撑住栏高)
|
||||
ws_holder.add_child(_forge._ws_lock_l)
|
||||
ws_holder.add_child(_forge._ws_scroll)
|
||||
var ws_panel := _counter_panel("秘宝匣", UiKit.TEX_ICON_WORKSHOP, ws_holder, 300)
|
||||
ws_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
ws_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(ws_panel)
|
||||
|
||||
|
||||
## 柜四·奖励格图鉴(常开独立位——结构位预留,内容排 Alpha)
|
||||
func _build_atlas_counter(mid: HBoxContainer) -> void:
|
||||
var atlas_holder := VBoxContainer.new()
|
||||
atlas_holder.custom_minimum_size = Vector2(0, 336) # 四栏等高(图鉴仅锁文也撑住栏高)
|
||||
var atlas_panel := _counter_panel("奖励格图鉴", UiKit.TEX_ICON_ATLAS, atlas_holder, 300)
|
||||
atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
atlas_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸
|
||||
mid.add_child(atlas_panel)
|
||||
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中
|
||||
lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
atlas_holder.add_child(lock_l)
|
||||
|
||||
|
||||
## 底栏域:关闭钮(左)/ 操作注记(右)+ 最近冒险条带
|
||||
func _build_bottom_bar(col: VBoxContainer) -> void:
|
||||
var close_btn := UiKit.button("关闭营地(回开始界面)")
|
||||
close_btn.pressed.connect(_emit_closed)
|
||||
var bottom := HBoxContainer.new()
|
||||
bottom.add_theme_constant_override("separation", 10)
|
||||
bottom.add_child(close_btn)
|
||||
var spacer2 := Control.new()
|
||||
spacer2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
bottom.add_child(spacer2)
|
||||
var caption := UiKit.label("树上点亮 · 工作间随钥匙展开 · 图鉴常开",UiKit.FS_HINT, false, UiKit.WARM_SOFT)
|
||||
bottom.add_child(caption)
|
||||
col.add_child(bottom)
|
||||
_side._recent_list = UiKit.label("",UiKit.FS_HINT, false, UiKit.WARM_SOFT)
|
||||
_side._recent_list.clip_text = true
|
||||
_side._recent_list.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
col.add_child(_side._recent_list)
|
||||
|
||||
|
||||
# 柜台列面板(方向稿同构):PanelContainer 米板身 + 头带深棕(图标+标题暖白)
|
||||
func _counter_panel(title_text: String, icon_path: String, holder: Control, min_w: int) -> PanelContainer:
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(min_w, 0)
|
||||
panel.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 10, 10, 10)))
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
panel.add_child(vbox)
|
||||
var band := PanelContainer.new()
|
||||
band.add_theme_stylebox_override("panel", UiKit.panel_flat(HEADER_BAND_COL, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 5, 10, 5)))
|
||||
vbox.add_child(band)
|
||||
var head := HBoxContainer.new()
|
||||
head.add_theme_constant_override("separation", 6)
|
||||
band.add_child(head)
|
||||
if icon_path != "":
|
||||
var ic := UiKit.icon_rect(icon_path, 32) # 柜台头带 64 源 0.5×;缺位 null → 纯文本回退
|
||||
if ic != null:
|
||||
head.add_child(ic)
|
||||
var l := UiKit.label(title_text,UiKit.FS_BODY, true, UiKit.WARM)
|
||||
head.add_child(l)
|
||||
vbox.add_child(holder)
|
||||
return panel
|
||||
|
||||
|
||||
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡——亮板双色墨制)----
|
||||
|
||||
# 天赋树段重建(树淬炼柜台主界面:脉分组行板:节点名/等级/价/购买钮 + Fork 双态切换钮;
|
||||
# 树数据 = 组合根 _camp_data().tree 投影 [{node_id, display_name, vein, kind, level, price, can_buy,
|
||||
# currency}...] + fork_groups [{group, node_id, active_side, owned, can_buy, price, sides:[{side, display_name, effects}]}])
|
||||
func _rebuild_tree(data: Dictionary) -> void:
|
||||
for c in _tree._tree_holder.get_children():
|
||||
c.queue_free()
|
||||
_tree_buy_btns.clear()
|
||||
_tree_fork_btns.clear()
|
||||
var tree: Array[Variant] = data.get("tree", []) as Array
|
||||
var forks: Array[Variant] = data.get("fork_groups", []) as Array
|
||||
var vein_names := {"spine": "·脊柱淬炼·", "forge": "·锻造脉·", "keystone": "·钥石脉·", "attune": "·同调脉·"}
|
||||
var last_vein := ""
|
||||
for row in tree:
|
||||
var vein := String(row.get("vein", ""))
|
||||
if vein != last_vein:
|
||||
last_vein = vein
|
||||
var vh := UiKit.label(String(vein_names.get(vein, vein)),UiKit.FS_HINT, true, UiKit.GOLD_INK) # GOLD_DEEP 对柜台板 2.67:1 越带,换深金墨 5.21:1
|
||||
_tree._tree_holder.add_child(vh)
|
||||
if String(row.get("kind", "")) == "fork":
|
||||
continue # Fork 行去行板化——由 fork_groups 段承载双态切换钮
|
||||
_tree._tree_holder.add_child(_tree_row_line(row))
|
||||
for fg in forks:
|
||||
_tree._tree_holder.add_child(_tree_fork_line(fg))
|
||||
|
||||
|
||||
## 树行板构建域:节点名(含门槛标记)+购买钮(追加序与锚句随段整体搬迁)
|
||||
func _tree_row_line(row: Dictionary) -> HBoxContainer:
|
||||
var hb := HBoxContainer.new()
|
||||
hb.add_theme_constant_override("separation", 6)
|
||||
var lvl := int(row.get("level", 0))
|
||||
var cap := int(row.get("cap", 1))
|
||||
var name_l := UiKit.label("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, cap], 13, false, UiKit.INK_LABEL)
|
||||
name_l.custom_minimum_size = Vector2(150, 0)
|
||||
name_l.clip_text = true
|
||||
var gate := String(row.get("gate_text", ""))
|
||||
if gate != "":
|
||||
name_l.text += " 🔒" # 等级阈值前置未达标标记(R1 机制位——悬停看门槛;达标后随 refresh 消失)
|
||||
name_l.tooltip_text = "等级门槛:%s" % gate
|
||||
hb.add_child(name_l)
|
||||
var btn := UiKit.button("购买 %d 钥石" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(func(): tree_buy_requested.emit(String(row.get("node_id", ""))))
|
||||
btn.disabled = not bool(row.get("can_buy", false))
|
||||
hb.add_child(btn)
|
||||
_tree_buy_btns.append({"node_id": String(row.get("node_id", "")), "btn": btn})
|
||||
return hb
|
||||
|
||||
|
||||
## Fork 双态行板构建域:组状态文案+解锁钮或双态切换钮
|
||||
func _tree_fork_line(fg: Dictionary) -> HBoxContainer:
|
||||
var group := String(fg.get("group", ""))
|
||||
var active := String(fg.get("active_side", ""))
|
||||
var owned := bool(fg.get("owned", false))
|
||||
var fb := HBoxContainer.new()
|
||||
fb.add_theme_constant_override("separation", 6)
|
||||
var state_txt := "未解锁"
|
||||
if owned:
|
||||
state_txt = "激活:%s" % active
|
||||
var gl := UiKit.label("%s[%s]" % [String(fg.get("display_name", group)), state_txt], 13, false, UiKit.INK_LABEL)
|
||||
fb.add_child(gl)
|
||||
if not owned:
|
||||
var bb := UiKit.button("解锁 %d 钥石" % int(fg.get("price", 0)), 0.0) # 行板紧凑钮豁免
|
||||
bb.pressed.connect(func(): tree_buy_requested.emit(String(fg.get("node_id", ""))))
|
||||
bb.disabled = not bool(fg.get("can_buy", false))
|
||||
fb.add_child(bb)
|
||||
_tree_buy_btns.append({"node_id": String(fg.get("node_id", "")), "btn": bb})
|
||||
else:
|
||||
for side_row in (fg.get("sides", []) as Array):
|
||||
var sd := String(side_row.get("side", ""))
|
||||
var sb := UiKit.button(String(side_row.get("display_name", sd)), 0.0) # 行板紧凑钮豁免
|
||||
sb.pressed.connect(func(): tree_fork_toggled.emit(group, sd))
|
||||
fb.add_child(sb)
|
||||
_tree_fork_btns.append({"group": group, "side": sd, "btn": sb})
|
||||
return fb
|
||||
|
||||
|
||||
# 工作间钥匙门(锻造工坊/秘宝匣柜台锁定态切换:节点点亮=展开、未点亮=锁定行;
|
||||
# 图鉴常开无门槛。判据读树投影行 level——零核心访问,纯视图)
|
||||
func _refresh_key_gates(data: Dictionary) -> void:
|
||||
var lvl_by_id := {}
|
||||
for row in (data.get("tree", []) as Array):
|
||||
lvl_by_id[String(row.get("node_id", ""))] = int(row.get("level", 0))
|
||||
var forge_open := int(lvl_by_id.get("forge_workshop", 0)) >= 1
|
||||
var ws_open := int(lvl_by_id.get("ks_workshop", 0)) >= 1
|
||||
_forge._forge_lock_l.visible = not forge_open
|
||||
_forge._forge_workbench.visible = forge_open
|
||||
_forge._ws_lock_l.visible = not ws_open
|
||||
_forge._ws_scroll.visible = ws_open
|
||||
|
||||
|
||||
# 锻造工坊行板重建(两区六路线;open_camp/refresh_camp 调用。行结构仿秘宝匣行板:
|
||||
# 件名+等级+解锁态+购买/解锁/选中钮。UI 只发意图信号〔契约 9——零核心写调用、零 MetaProgression
|
||||
# 持有〕;钮禁用=常态防线(余额/未解锁/满级/已选中),兜底拒绝可见归 presenter pattern 1)
|
||||
func _rebuild_forge(data: Dictionary) -> void:
|
||||
for zone in [_forge._forge_zone_weapon, _forge._forge_zone_armor]:
|
||||
for c in zone.get_children():
|
||||
c.queue_free()
|
||||
_forge._forge_rows.clear()
|
||||
if not _forge._forge_workbench.visible:
|
||||
return # 锁定态(钥匙未点亮):零行板零购买路径——占位纪律延续
|
||||
var bal := int(data.get("balance", 0))
|
||||
for row in data.get("forge", []):
|
||||
var kind := String(row.get("kind", ""))
|
||||
var zone: VBoxContainer = _forge._forge_zone_weapon if kind == "weapon" else _forge._forge_zone_armor
|
||||
var card := _forge_route_card(row, bal)
|
||||
zone.add_child(card["card"])
|
||||
_forge._forge_rows.append(card)
|
||||
|
||||
|
||||
## 锻造行板构建域:单路线卡(件名+等级+状态+购买/解锁/选中钮——并联双通道呈现面)
|
||||
func _forge_route_card(row: Dictionary, bal: int) -> Dictionary:
|
||||
var d := _forge_route_scalars(row)
|
||||
var card := PanelContainer.new()
|
||||
card.add_theme_stylebox_override("panel", UiKit.panel_flat(ROW_PLATE_COL, UiKit.PANEL_BORDER, UiKit.RADIUS_OBJECT, 1, Vector4(10, 4, 10, 4)))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 6)
|
||||
card.add_child(line)
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")), UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(104, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var lvl_l := UiKit.label("Lv%d/%d" % [d["lvl"], d["cap_v"]], 13, false, UiKit.INK_LABEL)
|
||||
lvl_l.custom_minimum_size = Vector2(44, 0)
|
||||
line.add_child(lvl_l)
|
||||
var status_l := UiKit.label(_forge_status_text(d, row), UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
status_l.clip_text = true
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(status_l)
|
||||
var btns := _forge_route_buttons(d, row, bal, line)
|
||||
return {"card": card, "route_id": d["rid"], "name_l": name_l, "lvl_l": lvl_l, "status_l": status_l,
|
||||
"buy_btn": btns["buy_btn"], "unlock_btn": btns["unlock_btn"], "select_btn": btns["select_btn"],
|
||||
"next_price": d["next_price"], "unlock_price": d["unlock_price"]}
|
||||
|
||||
|
||||
## 锻造行标量束:路线 id/解锁态/初始持有/等级/cap/几何价/解锁价一次取数
|
||||
func _forge_route_scalars(row: Dictionary) -> Dictionary:
|
||||
return {
|
||||
"rid": String(row.get("route_id", "")),
|
||||
"unlocked": bool(row.get("unlocked", false)),
|
||||
"initial": bool(row.get("initial_owned", false)),
|
||||
"lvl": int(row.get("level", 0)),
|
||||
"cap_v": int(row.get("cap", 0)),
|
||||
"next_price": int(row.get("next_price", 0)),
|
||||
"unlock_price": int(row.get("unlock_price", 0)),
|
||||
}
|
||||
|
||||
|
||||
## 状态文案域(并联双通道:练满前一档 or 一次性解锁价直购;已满级态)
|
||||
func _forge_status_text(d: Dictionary, row: Dictionary) -> String:
|
||||
var st_text := "已解锁"
|
||||
if not d["unlocked"]:
|
||||
if d["initial"]:
|
||||
st_text = "未解锁"
|
||||
else:
|
||||
st_text = "未解锁 · 练满%s 或 %d 钥石直购" % [String(row.get("prereq_name", "")), d["unlock_price"]]
|
||||
elif d["lvl"] >= d["cap_v"]:
|
||||
st_text = "已满级"
|
||||
return st_text
|
||||
|
||||
|
||||
## 三钮域:购买(已解锁未满级——余额不足禁用)/解锁(待解锁非初始——余额不足禁用)/选中(已解锁——当前=禁用态标记)
|
||||
func _forge_route_buttons(d: Dictionary, row: Dictionary, bal: int, line: HBoxContainer) -> Dictionary:
|
||||
var buy_btn: Button = null
|
||||
if d["unlocked"] and d["lvl"] < d["cap_v"]:
|
||||
buy_btn = UiKit.button("锻造 %d" % d["next_price"], 0.0) # 行板紧凑钮豁免
|
||||
buy_btn.pressed.connect(forge_buy_requested.emit.bind(d["rid"]))
|
||||
buy_btn.disabled = bal < d["next_price"]
|
||||
line.add_child(buy_btn)
|
||||
var unlock_btn: Button = null
|
||||
if not d["unlocked"] and not d["initial"]:
|
||||
unlock_btn = UiKit.button("解锁 %d" % d["unlock_price"], 0.0) # 行板紧凑钮豁免
|
||||
unlock_btn.pressed.connect(forge_unlock_requested.emit.bind(d["rid"]))
|
||||
unlock_btn.disabled = bal < d["unlock_price"]
|
||||
line.add_child(unlock_btn)
|
||||
var select_btn: Button = null
|
||||
if d["unlocked"]:
|
||||
var sel := bool(row.get("selected", false))
|
||||
select_btn = UiKit.button("当前" if sel else "选中", 0.0) # 行板紧凑钮豁免
|
||||
select_btn.pressed.connect(forge_select_requested.emit.bind(d["rid"]))
|
||||
select_btn.disabled = sel
|
||||
line.add_child(select_btn)
|
||||
return {"buy_btn": buy_btn, "unlock_btn": unlock_btn, "select_btn": select_btn}
|
||||
|
||||
|
||||
# 打开营地(全量重建四柜台行 + 同调装配段 + 最近冒险;树段与钥匙门随 refresh_camp 重建——
|
||||
# 数据 = 组合根 _camp_data() 投影)
|
||||
func open_camp(data: Dictionary) -> void:
|
||||
_rebuild_workshop(data) # 段尾链 _rebuild_attune(同调装配段+Y 升级行随秘宝匣柜台内容)
|
||||
_rebuild_forge(data) # 锻造工坊两区六路线行板
|
||||
_rebuild_recent(data)
|
||||
refresh_camp(data)
|
||||
visible = true
|
||||
|
||||
|
||||
# 状态重刷(购买后调用——余额/树行板/钥匙门/工坊状态就地重建或改文案;树行板随刷重建=按钮
|
||||
# 禁用态与门槛/cap 同步,12 行重建开销可忽略)
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
var balance := int(data.get("balance", 0))
|
||||
_balance_label.text = "钥石 %d" % balance
|
||||
_rebuild_tree(data)
|
||||
_refresh_key_gates(data)
|
||||
_rebuild_forge(data) # 行板随刷重建(购买/解锁/选中后等级/价/禁用态同步)
|
||||
_refresh_workshop_rows(data, balance)
|
||||
_refresh_attune_segment(data, balance)
|
||||
|
||||
|
||||
## 秘宝匣行板刷新域:进度列(解锁混合制三形态)与状态色(亮板双色墨制)与购买钮余额联动
|
||||
func _refresh_workshop_rows(data: Dictionary, balance: int) -> void:
|
||||
var ws_by_id := {}
|
||||
for row in data.get("workshop", []):
|
||||
ws_by_id[String(row.get("item_id", ""))] = row
|
||||
for card in _workshop_rows:
|
||||
var row: Dictionary[Variant, Variant] = ws_by_id.get(String(card.item_id), {})
|
||||
if row.is_empty():
|
||||
continue
|
||||
var unlocked := bool(row.get("unlocked", false))
|
||||
card.progress_l.text = _workshop_progress_text(row, unlocked)
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读)
|
||||
var st_text := "未解锁"
|
||||
var st_col: Color = UiKit.STONE_DEEP
|
||||
if unlocked:
|
||||
st_text = "已解锁"
|
||||
st_col = UiKit.GOLD_INK # GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
card.status_l.text = st_text
|
||||
card.status_l.add_theme_color_override("font_color", st_col)
|
||||
card.status_l.tooltip_text = st_text # 截断兜底(状态列 EXPAND_FILL 吸余宽——悬停看全文)
|
||||
# 购买钮余额联动(禁用态=余额不足,常态防线;pattern 1 兜底拒绝可见归 presenter)
|
||||
if card.get("buy_btn") != null:
|
||||
card.buy_btn.disabled = balance < int(card.get("price", 0))
|
||||
|
||||
|
||||
## 进度列文案域(解锁混合制三形态:钥石购买价/条件进度拼接/已入手)
|
||||
func _workshop_progress_text(row: Dictionary, unlocked: bool) -> String:
|
||||
if unlocked:
|
||||
return "已入手"
|
||||
var mode := String(row.get("mode", ""))
|
||||
if mode == "keystone":
|
||||
return "钥石 %d" % int(row.get("price", 0))
|
||||
if mode == "condition":
|
||||
var parts: Array[String] = []
|
||||
for req_v in (row.get("reqs", []) as Array):
|
||||
var req: Dictionary[Variant, Variant] = req_v as Dictionary
|
||||
var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件"))
|
||||
parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))])
|
||||
return "、".join(parts)
|
||||
return ""
|
||||
|
||||
|
||||
## 同调装配段刷新域(Slots 点亮/满位禁选/Y 升级行余额联动——就地刷)
|
||||
func _refresh_attune_segment(data: Dictionary, balance: int) -> void:
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
if _attune._attune_header_l != null:
|
||||
var slots_n := int(att.get("slots", 0))
|
||||
var eq: Array[Variant] = att.get("equipped", [])
|
||||
_attune._attune_header_l.text = "── 同调装配 Slots %d/%d ──" % [eq.size(), slots_n]
|
||||
var eq_set := {}
|
||||
for v in eq:
|
||||
eq_set[String(v)] = true
|
||||
for card2 in _attune._attune_btns:
|
||||
var on: bool = eq_set.has(String(card2.item_id))
|
||||
card2.btn.text = "卸下" if on else "装配"
|
||||
# 满位禁选+未解锁禁装(已装件恒可卸下;unlocked 快照随 rebuild 刷新)
|
||||
card2.btn.disabled = (not on and eq.size() >= slots_n) or (not on and not bool(card2.get("unlocked", false)))
|
||||
if _side._refresh_l != null:
|
||||
var rf: Dictionary[Variant, Variant] = att.get("refresh", {})
|
||||
_side._refresh_l.text = "三选一刷新 +%d 次/冒险 · Lv%d" % [int(rf.get("charges", 0)), int(rf.get("level", 0))]
|
||||
_side._refresh_btn.text = "已满级" if bool(rf.get("maxed", false)) else "升级 %d 钥石" % int(rf.get("price", 0))
|
||||
_side._refresh_btn.disabled = bool(rf.get("maxed", false)) or balance < int(rf.get("price", 0))
|
||||
|
||||
|
||||
func _rebuild_workshop(data: Dictionary) -> void:
|
||||
_workshop_rows.clear()
|
||||
for child in _workshop_holder.get_children():
|
||||
child.queue_free()
|
||||
_attune._attune_nodes.clear()
|
||||
_attune._attune_btns.clear()
|
||||
_attune._attune_header_l = null
|
||||
_side._refresh_l = null
|
||||
_side._refresh_btn = null
|
||||
for row in data.get("workshop", []):
|
||||
var card := _workshop_card(row)
|
||||
_workshop_holder.add_child(card["card"])
|
||||
_workshop_rows.append(card)
|
||||
_rebuild_attune(data)
|
||||
|
||||
|
||||
## 秘宝匣行板构建域:件名(分档后缀+效果摘要悬停)+进度列+状态列+钥石购买钮(购买件未解锁在列)
|
||||
func _workshop_card(row: Dictionary) -> Dictionary:
|
||||
var card := PanelContainer.new()
|
||||
card.add_theme_stylebox_override("panel", UiKit.panel_flat(ROW_PLATE_COL, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 4, 10, 4)))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 12)
|
||||
card.add_child(line)
|
||||
var tier_name := String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), ""))
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")) + ("·" + tier_name if tier_name != "" else ""),UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(132, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
name_l.tooltip_text = String(row.get("desc", "")) # 件效果摘要悬停可见(分档呈现契约——槽位档位=名后缀)
|
||||
line.add_child(name_l)
|
||||
var progress_l := UiKit.label("",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 同行价格墨同案
|
||||
progress_l.custom_minimum_size = Vector2(150, 0)
|
||||
progress_l.clip_text = true # 营地溢出修——文字全宽不撑柜
|
||||
progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(progress_l)
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式
|
||||
var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE_DEEP)
|
||||
status_l.clip_text = true # 同上——「已解锁 · 能力封存中」不撑柜(余宽吸收者)
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(status_l)
|
||||
# 钥石购买钮(购买件且未解锁时在列;写口唯一=buy_treasure_unlock,
|
||||
# 经 buy_requested(item_id) 信号走 presenter 分支;禁用态=余额不足)
|
||||
var buy_btn: Button = null
|
||||
if not bool(row.get("unlocked", false)) and String(row.get("mode", "")) == "keystone":
|
||||
buy_btn = UiKit.button("购买 %d" % int(row.get("price", 0)), 0.0) # 行板紧凑钮豁免
|
||||
buy_btn.pressed.connect(_emit_buy_requested.bind(String(row.get("item_id", ""))))
|
||||
line.add_child(buy_btn)
|
||||
return {"item_id": String(row.get("item_id", "")), "progress_l": progress_l, "status_l": status_l, "buy_btn": buy_btn, "price": int(row.get("price", 0)), "unlocked": bool(row.get("unlocked", false)), "card": card}
|
||||
|
||||
|
||||
# 同调装配段重建(秘宝匣柜台尾部段;Slots 点亮+
|
||||
# 九件行板+Y 升级行,程序占位面)
|
||||
func _rebuild_attune(data: Dictionary) -> void:
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
_attune._attune_header_l = UiKit.label("",UiKit.FS_HINT, true, UiKit.INK)
|
||||
_workshop_holder.add_child(_attune._attune_header_l)
|
||||
_attune._attune_nodes.append(_attune._attune_header_l)
|
||||
for row in att.get("items", []):
|
||||
var line := _attune_item_line(row)
|
||||
_workshop_holder.add_child(line)
|
||||
_attune._attune_nodes.append(line)
|
||||
_refresh_attune_tail(att)
|
||||
|
||||
|
||||
## 同调件行板构建域:件名+形态+档位+装配切换钮(未解锁禁装——toggle 写口同守卫)
|
||||
func _attune_item_line(row: Dictionary) -> HBoxContainer:
|
||||
var iid := String(row.get("item_id", ""))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 6)
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(96, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
form_l.custom_minimum_size = Vector2(58, 0)
|
||||
line.add_child(form_l)
|
||||
# 档位标签(槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边)
|
||||
var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
tier_l.custom_minimum_size = Vector2(36, 0)
|
||||
line.add_child(tier_l)
|
||||
var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免
|
||||
btn.pressed.connect(attune_toggled.emit.bind(iid))
|
||||
btn.disabled = not bool(row.get("unlocked", false)) # 未解锁禁装(解锁为装配前置——toggle 写口同守卫)
|
||||
line.add_child(btn)
|
||||
_attune._attune_btns.append({"item_id": iid, "btn": btn, "unlocked": bool(row.get("unlocked", false)), "equipped": bool(row.get("equipped", false))})
|
||||
return line
|
||||
|
||||
|
||||
## Y 升级尾行构建域(双入口之一——树上 ks_refresh 同账)
|
||||
func _refresh_attune_tail(att: Dictionary) -> void:
|
||||
var refresh_line := HBoxContainer.new()
|
||||
refresh_line.add_theme_constant_override("separation", 6)
|
||||
_side._refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
refresh_line.add_child(_side._refresh_l)
|
||||
_side._refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免
|
||||
_side._refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh"))
|
||||
refresh_line.add_child(_side._refresh_btn)
|
||||
_workshop_holder.add_child(refresh_line)
|
||||
_attune._attune_nodes.append(refresh_line)
|
||||
|
||||
|
||||
func _rebuild_recent(data: Dictionary) -> void:
|
||||
var lines: Array[String] = []
|
||||
var recent: Array[Variant] = data.get("recent", [])
|
||||
if recent.is_empty():
|
||||
lines.append(" 最近冒险:(暂无记录——完成或结束一次冒险后入账)")
|
||||
for s in recent:
|
||||
var verdict := "失败"
|
||||
if String(s.get("result", "")) == "completed":
|
||||
verdict = "完成"
|
||||
elif String(s.get("result", "")) == "abandoned":
|
||||
verdict = "放弃"
|
||||
lines.append("最近冒险:%s · 完成 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
|
||||
int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))])
|
||||
_side._recent_list.text = " | ".join(lines)
|
||||
|
||||
|
||||
func _emit_closed() -> void:
|
||||
closed.emit()
|
||||
|
||||
|
||||
func _emit_buy_requested(attr: String) -> void:
|
||||
buy_requested.emit(attr)
|
||||
@@ -1 +0,0 @@
|
||||
uid://dvg2wmjh87p8a
|
||||
@@ -0,0 +1,17 @@
|
||||
## 营地刷新单口:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成(卡⑤ HUD 同口挂接)。
|
||||
## 非 autoload、非信号广播——组合根单点推送(组合根建件绑册,presenter 经 context 持引用;
|
||||
## 卡④ 契约 4:写口语义零改,只改路由形态)。
|
||||
class_name CampRefreshBus
|
||||
extends RefCounted
|
||||
|
||||
var _targets: Array[Control] = [] # 可刷面册(四子界面+卡⑤ HUD 栏——均带 refresh_camp(data))
|
||||
|
||||
|
||||
func bind_targets(targets: Array[Control]) -> void:
|
||||
_targets = targets
|
||||
|
||||
|
||||
func refresh(camp_data: Dictionary) -> void:
|
||||
for t in _targets:
|
||||
if t != null and t.visible and t.has_method("refresh_camp"):
|
||||
t.refresh_camp(camp_data) # 现开界面就地重刷(未开的零开销——留底形态下场景根恒开)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdfmyxf87n6f6
|
||||
@@ -0,0 +1,309 @@
|
||||
## 营地场景图视图(交互式营地):10 层贴图铺底+七物件命中区+点选反馈四态+顶栏预留位。
|
||||
## 布局唯一事实源 = db.presentation.camp_scene_layout(构造注入,本视图禁自载表路径);
|
||||
## 子节点序 = 表内数组序 = z 序(Control 根+TextureRect 层——禁 CanvasLayer/z_index/Sprite2D)。
|
||||
## 输入单点:根 _gui_input 集中命中判定(层节点全 IGNORE),点击只发 object_pressed 信号,路由归组合根。
|
||||
## 零核心写调用:视图不读 MetaProgression,禁用态由组合根经 set_feedback_state 推送。
|
||||
class_name CampSceneView
|
||||
extends Control
|
||||
|
||||
signal object_pressed(key: StringName) ## 命中区点击确认(press+release 同区且非禁用;组合根路由开界面/动线)
|
||||
|
||||
## 名牌文案(camp.md §场景构成绑定表——七可点件按界面名逐字)
|
||||
const _HOTSPOT_NAMES := {
|
||||
&"gate": "冒险", &"forge": "锻造工坊", &"chest": "秘宝匣", &"dummy": "天赋树",
|
||||
&"board": "奖励格图鉴", &"notebook": "生涯记录", &"campfire": "离开营地",
|
||||
}
|
||||
## 层键→纹理路径(单点 = UiKit.TEX_CAMP_*,本表只引用不自写路径;缺位 load_tex→null→程序化占位)
|
||||
const _TEX_BY_KEY := {
|
||||
&"bg": UiKit.TEX_CAMP_BG, &"wall": UiKit.TEX_CAMP_WALL, &"board": UiKit.TEX_CAMP_BOARD,
|
||||
&"gate": UiKit.TEX_CAMP_GATE, &"dummy": UiKit.TEX_CAMP_DUMMY, &"forge": UiKit.TEX_CAMP_FORGE,
|
||||
&"tent": UiKit.TEX_CAMP_TENT, &"chest": UiKit.TEX_CAMP_CHEST,
|
||||
&"notebook": UiKit.TEX_CAMP_NOTEBOOK, &"campfire": UiKit.TEX_CAMP_CAMPFIRE,
|
||||
}
|
||||
## 禁用态去饱和着色器(modulate 只能调明度——降明度+去饱和走 canvas_item shader,零新素材)
|
||||
const _DESAT_SHADER_SRC := "
|
||||
shader_type canvas_item;
|
||||
uniform float strength : hint_range(0.0, 1.0) = 1.0;
|
||||
void fragment() {
|
||||
vec4 tex = texture(TEXTURE, UV);
|
||||
float g = dot(tex.rgb, vec3(0.299, 0.587, 0.114));
|
||||
tex.rgb = mix(tex.rgb, vec3(g) * 0.72, strength);
|
||||
COLOR = tex * COLOR;
|
||||
}
|
||||
"
|
||||
|
||||
enum FeedbackState { NORMAL, HOVER, PRESSED, DISABLED } ## 点选反馈四态(camp.md §点选反馈)
|
||||
|
||||
var _layout: CampSceneLayoutTable # 布局表(坐标唯一事实源)
|
||||
var _layer_nodes: Dictionary[StringName, Control] = {} # 层键→TextureRect/ColorRect 占位(子节点序=z 序)
|
||||
var _hotspot_rects: Dictionary[StringName, Rect2] = {} # 命中键→Rect2(视图局部坐标=表坐标)
|
||||
var _hotspot_layer: Dictionary[StringName, StringName] = {} # 命中键→层键
|
||||
var _base_states: Dictionary[StringName, int] = {} # 命中键→FeedbackState 值(持久基态——组合根置 DISABLED/NORMAL)
|
||||
var _outline: Panel # 悬停轮廓亮边
|
||||
var _name_label: Label # 物件名牌(下方 8px 居中,画面内钳制)
|
||||
var _hover_key := &"" # 当前悬停命中键(&""=无)
|
||||
var _press_key := &"" # 当前按住命中键
|
||||
var _float_tween: Tween # 悬停浮动补间(逐级整像素吸附,禁 _process 逐帧移 UI)
|
||||
var _desat_mat: ShaderMaterial # 禁用态共享材质(strength 恒 1,材质挂/摘承载开关)
|
||||
var _hud_slot: Control # 顶栏预留位(卡⑤ set_hud_band 填值)
|
||||
var hud_band: Control # 顶栏 HUD 栏句柄(卡⑤ CampHudBand——装配后 bus 目标册取用面)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
visible = false # 登记模态初始隐(开合走栈——模态纪律;boot 态场景根不得盖菜单)
|
||||
|
||||
|
||||
func setup_layers(layout: CampSceneLayoutTable) -> void:
|
||||
if layout == null:
|
||||
push_error("[CampSceneView] 布局表缺位——组合根须注入 db.presentation.camp_scene_layout")
|
||||
return
|
||||
_layout = layout
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP # 场景图=营地语境主交互面(模态层在兄弟位先截事件)
|
||||
_build_desat_material()
|
||||
for spec in layout.layers:
|
||||
var node: Control = _build_layer_node(spec)
|
||||
add_child(node)
|
||||
_layer_nodes[spec.key] = node
|
||||
_build_outline()
|
||||
_build_name_label()
|
||||
_build_hud_slot(layout)
|
||||
mouse_exited.connect(_on_mouse_exited) # 悬出根控件=清 hover/press(防态悬挂)
|
||||
|
||||
|
||||
func bind_hotspots(layout: CampSceneLayoutTable) -> void:
|
||||
if layout == null:
|
||||
return
|
||||
for spec in layout.layers:
|
||||
if spec.is_clickable():
|
||||
_hotspot_rects[spec.hotspot_key] = spec.hotspot
|
||||
_hotspot_layer[spec.hotspot_key] = spec.key
|
||||
_base_states[spec.hotspot_key] = FeedbackState.NORMAL
|
||||
|
||||
|
||||
func get_hotspot_rect(key: StringName) -> Rect2:
|
||||
var rect: Rect2 = _hotspot_rects.get(key, Rect2())
|
||||
return rect
|
||||
|
||||
|
||||
func set_feedback_state(key: StringName, state: FeedbackState) -> void:
|
||||
if not _base_states.has(key):
|
||||
return
|
||||
_base_states[key] = state # 持久基态只收 NORMAL/DISABLED;HOVER/PRESSED 为视图内瞬态
|
||||
_apply_state(key, _base_state(key))
|
||||
|
||||
|
||||
func set_hud_band(band: Control) -> void:
|
||||
if _hud_slot != null:
|
||||
_hud_slot.add_child(band)
|
||||
hud_band = band
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
_handle_motion(event as InputEventMouseMotion)
|
||||
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_handle_button(event as InputEventMouseButton)
|
||||
accept_event()
|
||||
|
||||
|
||||
func _handle_motion(event: InputEventMouseMotion) -> void:
|
||||
var key: StringName = _topmost_hotspot_at(event.position)
|
||||
if key != _hover_key:
|
||||
if _hover_key != &"":
|
||||
_apply_state(_hover_key, _base_state(_hover_key))
|
||||
_hover_key = key
|
||||
if key != &"":
|
||||
_apply_state(key, FeedbackState.HOVER)
|
||||
if _press_key != &"" and key != _press_key:
|
||||
_apply_state(_press_key, _base_state(_press_key)) # 按住拖出=取消按压
|
||||
_press_key = &""
|
||||
|
||||
|
||||
func _handle_button(event: InputEventMouseButton) -> void:
|
||||
var hit: StringName = _topmost_hotspot_at(event.position)
|
||||
if event.pressed:
|
||||
if hit != &"":
|
||||
_press_key = hit
|
||||
_apply_state(hit, FeedbackState.PRESSED)
|
||||
return
|
||||
if _press_key == &"":
|
||||
return
|
||||
if hit == _press_key and not _is_disabled(_press_key):
|
||||
object_pressed.emit(_press_key)
|
||||
_apply_state(_press_key, _base_state(_press_key))
|
||||
_press_key = &""
|
||||
|
||||
|
||||
func _build_layer_node(spec: CampSceneLayerSpec) -> Control:
|
||||
var node: Control = null
|
||||
var tex: Texture2D = UiKit.load_tex(_TEX_BY_KEY.get(spec.key, ""))
|
||||
if tex != null:
|
||||
var tr := TextureRect.new()
|
||||
tr.texture = tex
|
||||
tr.stretch_mode = TextureRect.STRETCH_SCALE
|
||||
node = tr
|
||||
else:
|
||||
var ph := ColorRect.new() # 程序化占位(素材缺位=冷检出半同步态,结构在位冒烟安全)
|
||||
ph.color = Color(1.0, 1.0, 1.0, 0.06)
|
||||
node = ph
|
||||
node.name = "Layer_%s" % spec.key
|
||||
node.position = spec.texture_rect.position
|
||||
node.size = spec.texture_rect.size
|
||||
node.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return node
|
||||
|
||||
|
||||
func _build_desat_material() -> void:
|
||||
_desat_mat = ShaderMaterial.new()
|
||||
var shader := Shader.new()
|
||||
shader.code = _DESAT_SHADER_SRC
|
||||
_desat_mat.shader = shader
|
||||
|
||||
|
||||
func _build_outline() -> void:
|
||||
_outline = Panel.new()
|
||||
var sb := StyleBoxFlat.new()
|
||||
sb.draw_center = false
|
||||
sb.border_color = UiKit.CANDLE
|
||||
sb.set_border_width_all(2)
|
||||
sb.set_corner_radius_all(4)
|
||||
_outline.add_theme_stylebox_override("panel", sb)
|
||||
_outline.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_outline.visible = false
|
||||
add_child(_outline)
|
||||
|
||||
|
||||
func _build_name_label() -> void:
|
||||
_name_label = UiKit.label("", UiKit.FS_HINT, false, UiKit.INK_ON_DEEP)
|
||||
var plate := StyleBoxFlat.new()
|
||||
plate.bg_color = UiKit.BG_ROOT
|
||||
plate.set_corner_radius_all(4)
|
||||
plate.content_margin_left = 8.0
|
||||
plate.content_margin_right = 8.0
|
||||
plate.content_margin_top = 2.0
|
||||
plate.content_margin_bottom = 2.0
|
||||
_name_label.add_theme_stylebox_override("normal", plate)
|
||||
_name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE # 名牌不挡命中区
|
||||
_name_label.visible = false
|
||||
add_child(_name_label)
|
||||
|
||||
|
||||
func _build_hud_slot(layout: CampSceneLayoutTable) -> void:
|
||||
_hud_slot = Control.new()
|
||||
_hud_slot.name = "HudBandSlot"
|
||||
_hud_slot.set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||
_hud_slot.custom_minimum_size = Vector2(0, layout.hud_band_h) # 48px 浮层覆盖,不挤压画面
|
||||
_hud_slot.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_hud_slot)
|
||||
|
||||
|
||||
func _topmost_hotspot_at(pos: Vector2) -> StringName:
|
||||
if _layout == null:
|
||||
return &""
|
||||
var idx: int = _layout.layers.size() - 1 # 逆序=自最上 z 向下
|
||||
while idx >= 0:
|
||||
var spec: CampSceneLayerSpec = _layout.layers[idx]
|
||||
if spec.is_clickable() and _hotspot_rects.get(spec.hotspot_key, Rect2()).has_point(pos):
|
||||
return spec.hotspot_key
|
||||
idx -= 1
|
||||
return &""
|
||||
|
||||
|
||||
func _base_state(key: StringName) -> FeedbackState:
|
||||
var st: int = _base_states.get(key, FeedbackState.NORMAL)
|
||||
return FeedbackState.DISABLED if st == FeedbackState.DISABLED else FeedbackState.NORMAL
|
||||
|
||||
|
||||
func _is_disabled(key: StringName) -> bool:
|
||||
return _base_state(key) == FeedbackState.DISABLED
|
||||
|
||||
|
||||
func _apply_state(key: StringName, state: FeedbackState) -> void:
|
||||
var layer_key: StringName = _hotspot_layer.get(key, &"")
|
||||
var node: Control = _layer_nodes.get(layer_key, null)
|
||||
if node == null:
|
||||
return
|
||||
var rect: Rect2 = _hotspot_rects.get(key, Rect2())
|
||||
var base_y: float = node.position.y - _float_offset_of(key) # 还原到基准位再施新态
|
||||
_set_float_offset(key, 0)
|
||||
if _float_tween != null:
|
||||
_float_tween.kill()
|
||||
_float_tween = null
|
||||
match state:
|
||||
FeedbackState.NORMAL:
|
||||
node.material = null
|
||||
_outline.visible = false
|
||||
_name_label.visible = false
|
||||
FeedbackState.HOVER:
|
||||
_apply_hover(key, node, rect)
|
||||
FeedbackState.PRESSED:
|
||||
node.position.y = base_y + 1.0 # 按下=位移 1px
|
||||
FeedbackState.DISABLED:
|
||||
_apply_disabled(node)
|
||||
|
||||
|
||||
func _apply_hover(key: StringName, node: Control, rect: Rect2) -> void:
|
||||
if _is_disabled(key):
|
||||
_apply_disabled(node)
|
||||
return
|
||||
node.material = null
|
||||
_outline.position = rect.position
|
||||
_outline.size = rect.size
|
||||
_outline.visible = true
|
||||
_show_name_plate(key, rect)
|
||||
_run_float(key) # 悬停浮动(整像素逐级吸附)
|
||||
|
||||
|
||||
func _apply_disabled(node: Control) -> void:
|
||||
node.material = _desat_mat # 降明度+去饱和(shader;仅锻炉/宝箱会有此态)
|
||||
_outline.visible = false
|
||||
_name_label.visible = false
|
||||
|
||||
|
||||
func _run_float(key: StringName) -> void:
|
||||
_float_tween = create_tween()
|
||||
for step in 4:
|
||||
var off := -1 - step
|
||||
_float_tween.tween_callback(_set_float_offset.bind(key, off)).set_delay(UiKit.motion(0.03))
|
||||
# 悬离时由 _apply_state(NORMAL) 先归零位——本补间只承载升浮
|
||||
|
||||
|
||||
func _set_float_offset(key: StringName, off: int) -> void:
|
||||
var layer_key: StringName = _hotspot_layer.get(key, &"")
|
||||
var node: Control = _layer_nodes.get(layer_key, null)
|
||||
if node == null or _layout == null:
|
||||
return
|
||||
for spec in _layout.layers:
|
||||
if spec.key == layer_key:
|
||||
node.position.y = spec.texture_rect.position.y + float(off)
|
||||
return
|
||||
|
||||
|
||||
func _float_offset_of(key: StringName) -> float:
|
||||
var layer_key: StringName = _hotspot_layer.get(key, &"")
|
||||
var node: Control = _layer_nodes.get(layer_key, null)
|
||||
if node == null or _layout == null:
|
||||
return 0.0
|
||||
for spec in _layout.layers:
|
||||
if spec.key == layer_key:
|
||||
return node.position.y - spec.texture_rect.position.y
|
||||
return 0.0
|
||||
|
||||
|
||||
func _show_name_plate(key: StringName, rect: Rect2) -> void:
|
||||
_name_label.text = String(_HOTSPOT_NAMES.get(key, ""))
|
||||
_name_label.visible = true
|
||||
_name_label.reset_size()
|
||||
var pos := Vector2.ZERO
|
||||
pos.x = clampf(rect.get_center().x - _name_label.size.x / 2.0, 0.0, _layout.canvas.x - _name_label.size.x)
|
||||
pos.y = minf(rect.end.y + 8.0, _layout.canvas.y - _name_label.size.y) # 贴底物件防出画(画面内钳制)
|
||||
_name_label.position = pos
|
||||
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
if _hover_key != &"":
|
||||
_apply_state(_hover_key, _base_state(_hover_key))
|
||||
_hover_key = &""
|
||||
if _press_key != &"":
|
||||
_apply_state(_press_key, _base_state(_press_key))
|
||||
_press_key = &""
|
||||
@@ -0,0 +1 @@
|
||||
uid://btsrctv5ddx16
|
||||
@@ -0,0 +1,88 @@
|
||||
## 营地子界面模态壳基类(锻造工坊/秘宝匣/天赋树/奖励格图鉴四子界面公共形态):
|
||||
## 实底全屏暖亮档+石纹暗角+顶栏(铭牌+钥石徽)+内容列+底栏返回钮。
|
||||
## 开合可见性归模态栈(登记层开合一律走栈);返回钮只发 closed——关层编排归 presenter(组合根单口)。
|
||||
## 余额徽随子类 refresh_camp 刷新(钥石读数=投影单口,禁第二账本)。
|
||||
class_name CampSubViewBase
|
||||
extends Control
|
||||
|
||||
signal closed
|
||||
|
||||
var _balance_label: Label # 钥石徽数值行(探针直读面——ui_probe 余额文案断言改指新界面)
|
||||
|
||||
|
||||
## 壳构建(子类 _init 调用一次):content 由子类先建好传入(列容器/滚动容器皆可)
|
||||
func _build_shell(title: String, icon_path: String, content: Control) -> void:
|
||||
# 半透明压暗遮罩 0.6(camp.md v2.3 定稿——场景根留底可见,窗口之下垫遮罩;交互聚焦由遮罩 STOP 兜底)
|
||||
var dim := UiKit._modal_dim(self, Color(UiKit.DIM, 0.6))
|
||||
var margin := MarginContainer.new()
|
||||
margin.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
margin.add_theme_constant_override("margin_left", 16)
|
||||
margin.add_theme_constant_override("margin_right", 16)
|
||||
margin.add_theme_constant_override("margin_top", 12)
|
||||
margin.add_theme_constant_override("margin_bottom", 12)
|
||||
dim.add_child(margin)
|
||||
var col := VBoxContainer.new()
|
||||
col.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(col)
|
||||
_build_top_bar(col, title, icon_path)
|
||||
content.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
# 内容米板身(柜台行板对比底=PanelData——旧柜台面板形态延续,行板墨色对比表不脱锚)
|
||||
var body := PanelContainer.new()
|
||||
body.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, UiKit.RADIUS_OBJECT, 1, Vector4(10, 10, 10, 10)))
|
||||
body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
body.add_child(content)
|
||||
col.add_child(body)
|
||||
_build_bottom_bar(col)
|
||||
visible = false # 登记模态初始隐(开合走栈)
|
||||
|
||||
|
||||
## 顶栏域:铭牌(标题)+钥石徽(余额徽单点——子类 refresh_camp 刷新数值行)
|
||||
func _build_top_bar(col: VBoxContainer, title: String, icon_path: String) -> void:
|
||||
var top := HBoxContainer.new()
|
||||
top.add_theme_constant_override("separation", 10)
|
||||
col.add_child(top)
|
||||
var plaque := PanelContainer.new()
|
||||
plaque.add_theme_stylebox_override("panel",
|
||||
UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8)))
|
||||
top.add_child(plaque)
|
||||
var head := HBoxContainer.new()
|
||||
head.add_theme_constant_override("separation", 6)
|
||||
plaque.add_child(head)
|
||||
if icon_path != "":
|
||||
var ic := UiKit.icon_rect(icon_path, 32) # 头带图标缺位 null → 纯文本回退
|
||||
if ic != null:
|
||||
head.add_child(ic)
|
||||
head.add_child(UiKit.label(title, UiKit.FS_TITLE, true, UiKit.INK_TITLE))
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(spacer)
|
||||
var badge := UiKit.keystone_badge("钥石 0") # UiKit 工厂单点(烛光黄底深墨字)
|
||||
top.add_child(badge)
|
||||
_balance_label = badge.value_label
|
||||
|
||||
|
||||
## 底栏域:返回营地钮(closed 信号——关层编排归 presenter)
|
||||
func _build_bottom_bar(col: VBoxContainer) -> void:
|
||||
var bottom := HBoxContainer.new()
|
||||
bottom.add_theme_constant_override("separation", 10)
|
||||
col.add_child(bottom)
|
||||
var back := UiKit.button("返回营地")
|
||||
back.pressed.connect(_emit_closed)
|
||||
bottom.add_child(back)
|
||||
var spacer2 := Control.new()
|
||||
spacer2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
bottom.add_child(spacer2)
|
||||
|
||||
|
||||
func open_camp(data: Dictionary) -> void:
|
||||
refresh_camp(data)
|
||||
visible = true # 开可见性由栈承载(本行为开层数据投喂的收尾拍)
|
||||
|
||||
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
pass # 子类覆写(余额徽刷新+各自行板/门判据)
|
||||
|
||||
|
||||
func _emit_closed() -> void:
|
||||
closed.emit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dv0gyjyrtoxkh
|
||||
@@ -18,6 +18,7 @@ var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件—
|
||||
func _init() -> void:
|
||||
# 全屏页壳(T6/T7 对齐批先例——暖亮底+石纹弱质感+暗角+56px 通栏顶条+内容列)
|
||||
var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, true)
|
||||
page.base.color = Color(UiKit.DIM, 0.6) # 功能窗口化(camp.md v2.3 定稿——场景根留底+半透明压暗遮罩 0.6;观感随样张目检校准)
|
||||
var top: HBoxContainer = page.top_row
|
||||
top.add_child(UiKit.label("生 涯",UiKit.FS_TITLE, true, UiKit.WARM))
|
||||
var top_sp := Control.new()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
## 奖励格图鉴子界面(自 CampOverlay 图鉴柜台抽出)——占位壳:
|
||||
## 内容保持挂起(用户令冻结,重启须用户另行发令——卡④ 契约 5),点进看占位、不是禁用态。
|
||||
## 锁文照录原柜台锁文并随形态订正『柜台』称谓(L2 记录——零新文案)。
|
||||
class_name CodexView
|
||||
extends CampSubViewBase
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var holder := VBoxContainer.new()
|
||||
holder.custom_minimum_size = Vector2(0, 336)
|
||||
var lock_l := UiKit.label(" —— 结构位预留:内容排 Alpha(地图战斗资源差异化批)", UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
holder.add_child(lock_l)
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.custom_minimum_size = Vector2(300, 336)
|
||||
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
scroll.add_child(holder)
|
||||
_build_shell("奖励格图鉴", UiKit.TEX_ICON_ATLAS, scroll)
|
||||
|
||||
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
_balance_label.text = "钥石 %d" % int(data.get("balance", 0)) # 内容挂起——余额徽随刷(占位态无行板)
|
||||
@@ -0,0 +1 @@
|
||||
uid://m1bcfjiceyiq
|
||||
@@ -1,14 +1,18 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 通用确认弹窗:open(text, confirm_label, cancel_label) 双钮外发 confirmed/cancelled。
|
||||
## 通用确认弹窗:open(text, confirm_label, cancel_label) 双钮外发 confirmed/cancelled;
|
||||
## open_three(text, confirm_label, secondary_label, cancel_label) 三钮增设 secondary_confirmed
|
||||
##(营地大门两分支——卡④ T4;双钮调用方零改动:次钮随 open 隐藏)。
|
||||
## 文案与按钮语义装配归调用方——本类零业务触碰。
|
||||
class_name ConfirmDialog
|
||||
extends Control
|
||||
|
||||
signal confirmed
|
||||
signal secondary_confirmed ## 次确认(三钮形态中钮——营地大门「开始新冒险」出口;卡④ T4)
|
||||
signal cancelled
|
||||
|
||||
var _text: Label
|
||||
var _confirm_btn: Button
|
||||
var _secondary_btn: Button
|
||||
var _cancel_btn: Button
|
||||
|
||||
|
||||
@@ -27,6 +31,9 @@ func _init() -> void:
|
||||
_cancel_btn = UiKit.button("取消")
|
||||
_cancel_btn.pressed.connect(_emit_cancelled)
|
||||
btn_row.add_child(_cancel_btn)
|
||||
_secondary_btn = UiKit.button("次选项")
|
||||
_secondary_btn.pressed.connect(_emit_secondary)
|
||||
btn_row.add_child(_secondary_btn)
|
||||
_confirm_btn = UiKit.button("确认")
|
||||
_confirm_btn.pressed.connect(_emit_confirmed)
|
||||
btn_row.add_child(_confirm_btn)
|
||||
@@ -37,12 +44,20 @@ func open(text: String, confirm_label: String, cancel_label: String = "取消")
|
||||
_text.text = text
|
||||
_confirm_btn.text = confirm_label
|
||||
_cancel_btn.text = cancel_label
|
||||
_secondary_btn.visible = false # 双钮形态(次钮隐藏——既有调用方零改动)
|
||||
# z 序纪律:层序唯一事实源=main.LAYER_CONTRACT(树序与登记序同源派生);开合单口
|
||||
# 归组合根 ModalFlow(挂起兜底/遮挡记账/开侧同步)——本类只管文案与显示,不自行抬层
|
||||
|
||||
|
||||
visible = true
|
||||
|
||||
|
||||
## 三钮形态(营地大门有断点分支——继续上一段冒险/开始新冒险/取消;Q5 裁甲)
|
||||
func open_three(text: String, confirm_label: String, secondary_label: String, cancel_label: String) -> void:
|
||||
open(text, confirm_label, cancel_label)
|
||||
_secondary_btn.text = secondary_label
|
||||
_secondary_btn.visible = true
|
||||
|
||||
|
||||
func close() -> void:
|
||||
visible = false
|
||||
|
||||
@@ -55,5 +70,9 @@ func _emit_confirmed() -> void:
|
||||
confirmed.emit()
|
||||
|
||||
|
||||
func _emit_secondary() -> void:
|
||||
secondary_confirmed.emit()
|
||||
|
||||
|
||||
func _emit_cancelled() -> void:
|
||||
cancelled.emit()
|
||||
@@ -0,0 +1,159 @@
|
||||
## 锻造工坊子界面(自 CampOverlay 锻造柜台抽出——承载面迁移,界面逻辑复用零重设计):
|
||||
## 钥匙门(树上 forge_workshop 点亮前锁定态零行板零购买路径;点亮=两区六路线行板)。
|
||||
## UI 只发意图信号(写口唯一归 MetaProgression 锻造写口族);钮禁用=常态防线(余额/未解锁/满级/已选中)。
|
||||
class_name ForgeView
|
||||
extends CampSubViewBase
|
||||
|
||||
signal forge_buy_requested(route_id: String) ## 锻造路线购买意图(写口唯一归 MetaProgression.forge_buy_level)
|
||||
signal forge_unlock_requested(route_id: String) ## 锻造路线解锁意图(写口唯一归 MetaProgression.forge_buy_unlock)
|
||||
signal forge_select_requested(route_id: String) ## 锻造路线选中意图(写口唯一归 MetaProgression.forge_select)
|
||||
|
||||
const ROW_PLATE_COL := UiKit.CAMP_ROW_PLATE
|
||||
|
||||
var _forge_lock_l: Label # 锁定态行(钥匙未点亮)
|
||||
var _forge_workbench: VBoxContainer # 工作间(钥匙点亮后展开——两区六路线行板)
|
||||
var _forge_zone_weapon: VBoxContainer # 武器锻造区行板容器(三路线)
|
||||
var _forge_zone_armor: VBoxContainer # 护甲锻造区行板容器(三路线)
|
||||
var _forge_rows: Array[Dictionary] = [] # [{route_id, name_l, lvl_l, status_l, buy_btn, unlock_btn, select_btn, next_price, unlock_price}](open/refresh 重建;探针消费)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var holder := VBoxContainer.new()
|
||||
holder.add_theme_constant_override("separation", 6)
|
||||
holder.custom_minimum_size = Vector2(0, 336)
|
||||
_forge_lock_l = UiKit.label("🔒 锻造工坊未开放\n\n点亮树上「锻造工坊」钥匙节点后展开工作间。\n(钥匙免费、无前置——树上直接点亮)", UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_forge_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
_forge_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_forge_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_forge_workbench = VBoxContainer.new()
|
||||
_forge_workbench.add_theme_constant_override("separation", 6)
|
||||
_forge_workbench.visible = false # 默认锁定态(钥匙门未判前零行板——门开才建)
|
||||
_forge_zone_weapon = VBoxContainer.new()
|
||||
_forge_zone_weapon.add_theme_constant_override("separation", 4)
|
||||
_forge_zone_armor = VBoxContainer.new()
|
||||
_forge_zone_armor.add_theme_constant_override("separation", 4)
|
||||
var wb_wh := UiKit.label("── 武器锻造区 ──", UiKit.FS_HINT, true, UiKit.GOLD_INK)
|
||||
_forge_workbench.add_child(wb_wh)
|
||||
_forge_workbench.add_child(_forge_zone_weapon)
|
||||
var wb_ah := UiKit.label("── 护甲锻造区 ──", UiKit.FS_HINT, true, UiKit.GOLD_INK)
|
||||
_forge_workbench.add_child(wb_ah)
|
||||
_forge_workbench.add_child(_forge_zone_armor)
|
||||
holder.add_child(_forge_lock_l)
|
||||
holder.add_child(_forge_workbench)
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.custom_minimum_size = Vector2(300, 336)
|
||||
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
scroll.add_child(holder)
|
||||
_build_shell("锻造工坊", UiKit.TEX_ICON_FORGE, scroll)
|
||||
|
||||
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
_balance_label.text = "钥石 %d" % int(data.get("balance", 0))
|
||||
_refresh_key_gate(data)
|
||||
_rebuild_forge(data)
|
||||
|
||||
|
||||
# 工作间钥匙门(树上 forge_workshop 行 level 判据——零核心访问,纯视图读投影)
|
||||
func _refresh_key_gate(data: Dictionary) -> void:
|
||||
var lvl := 0
|
||||
for row in (data.get("tree", []) as Array):
|
||||
if String(row.get("node_id", "")) == "forge_workshop":
|
||||
lvl = int(row.get("level", 0))
|
||||
var open := lvl >= 1
|
||||
_forge_lock_l.visible = not open
|
||||
_forge_workbench.visible = open
|
||||
|
||||
|
||||
# 锻造行板重建(两区六路线;refresh_camp 调用。行结构仿秘宝匣行板:件名+等级+状态+购买/解锁/选中钮)
|
||||
func _rebuild_forge(data: Dictionary) -> void:
|
||||
for zone in [_forge_zone_weapon, _forge_zone_armor]:
|
||||
for c in zone.get_children():
|
||||
c.queue_free()
|
||||
_forge_rows.clear()
|
||||
if not _forge_workbench.visible:
|
||||
return # 锁定态(钥匙未点亮):零行板零购买路径——占位纪律延续
|
||||
var bal := int(data.get("balance", 0))
|
||||
for row in data.get("forge", []):
|
||||
var kind := String(row.get("kind", ""))
|
||||
var zone: VBoxContainer = _forge_zone_weapon if kind == "weapon" else _forge_zone_armor
|
||||
var card := _forge_route_card(row, bal)
|
||||
zone.add_child(card["card"])
|
||||
_forge_rows.append(card)
|
||||
|
||||
|
||||
## 锻造行板构建域:单路线卡(件名+等级+状态+购买/解锁/选中钮——并联双通道呈现面)
|
||||
func _forge_route_card(row: Dictionary, bal: int) -> Dictionary:
|
||||
var d := _forge_route_scalars(row)
|
||||
var card := PanelContainer.new()
|
||||
card.add_theme_stylebox_override("panel", UiKit.panel_flat(ROW_PLATE_COL, UiKit.PANEL_BORDER, UiKit.RADIUS_OBJECT, 1, Vector4(10, 4, 10, 4)))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 6)
|
||||
card.add_child(line)
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")), UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(104, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var lvl_l := UiKit.label("Lv%d/%d" % [d["lvl"], d["cap_v"]], 13, false, UiKit.INK_LABEL)
|
||||
lvl_l.custom_minimum_size = Vector2(44, 0)
|
||||
line.add_child(lvl_l)
|
||||
var status_l := UiKit.label(_forge_status_text(d, row), UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
status_l.clip_text = true
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(status_l)
|
||||
var btns := _forge_route_buttons(d, row, bal, line)
|
||||
return {"card": card, "route_id": d["rid"], "name_l": name_l, "lvl_l": lvl_l, "status_l": status_l,
|
||||
"buy_btn": btns["buy_btn"], "unlock_btn": btns["unlock_btn"], "select_btn": btns["select_btn"],
|
||||
"next_price": d["next_price"], "unlock_price": d["unlock_price"]}
|
||||
|
||||
|
||||
## 锻造行标量束:路线 id/解锁态/初始持有/等级/cap/几何价/解锁价一次取数
|
||||
func _forge_route_scalars(row: Dictionary) -> Dictionary:
|
||||
return {
|
||||
"rid": String(row.get("route_id", "")),
|
||||
"unlocked": bool(row.get("unlocked", false)),
|
||||
"initial": bool(row.get("initial_owned", false)),
|
||||
"lvl": int(row.get("level", 0)),
|
||||
"cap_v": int(row.get("cap", 0)),
|
||||
"next_price": int(row.get("next_price", 0)),
|
||||
"unlock_price": int(row.get("unlock_price", 0)),
|
||||
}
|
||||
|
||||
|
||||
## 状态文案域(并联双通道:练满前一档 or 一次性解锁价直购;已满级态)
|
||||
func _forge_status_text(d: Dictionary, row: Dictionary) -> String:
|
||||
var st_text := "已解锁"
|
||||
if not d["unlocked"]:
|
||||
if d["initial"]:
|
||||
st_text = "未解锁"
|
||||
else:
|
||||
st_text = "未解锁 · 练满%s 或 %d 钥石直购" % [String(row.get("prereq_name", "")), d["unlock_price"]]
|
||||
elif d["lvl"] >= d["cap_v"]:
|
||||
st_text = "已满级"
|
||||
return st_text
|
||||
|
||||
|
||||
## 三钮域:购买(已解锁未满级——余额不足禁用)/解锁(待解锁非初始——余额不足禁用)/选中(已解锁——当前=禁用态标记)
|
||||
func _forge_route_buttons(d: Dictionary, row: Dictionary, bal: int, line: HBoxContainer) -> Dictionary:
|
||||
var buy_btn: Button = null
|
||||
if d["unlocked"] and d["lvl"] < d["cap_v"]:
|
||||
buy_btn = UiKit.button("锻造 %d" % d["next_price"], 0.0) # 行板紧凑钮豁免
|
||||
buy_btn.pressed.connect(forge_buy_requested.emit.bind(d["rid"]))
|
||||
buy_btn.disabled = bal < d["next_price"]
|
||||
line.add_child(buy_btn)
|
||||
var unlock_btn: Button = null
|
||||
if not d["unlocked"] and not d["initial"]:
|
||||
unlock_btn = UiKit.button("解锁 %d" % d["unlock_price"], 0.0) # 行板紧凑钮豁免
|
||||
unlock_btn.pressed.connect(forge_unlock_requested.emit.bind(d["rid"]))
|
||||
unlock_btn.disabled = bal < d["unlock_price"]
|
||||
line.add_child(unlock_btn)
|
||||
var select_btn: Button = null
|
||||
if d["unlocked"]:
|
||||
var sel := bool(row.get("selected", false))
|
||||
select_btn = UiKit.button("当前" if sel else "选中", 0.0) # 行板紧凑钮豁免
|
||||
select_btn.pressed.connect(forge_select_requested.emit.bind(d["rid"]))
|
||||
select_btn.disabled = sel
|
||||
line.add_child(select_btn)
|
||||
return {"buy_btn": buy_btn, "unlock_btn": unlock_btn, "select_btn": select_btn}
|
||||
@@ -0,0 +1 @@
|
||||
uid://b77xh0e3txmh5
|
||||
@@ -0,0 +1,238 @@
|
||||
## 秘宝匣子界面(自 CampOverlay 秘宝匣柜台抽出——承载面迁移,界面逻辑复用零重设计):
|
||||
## 钥匙门(树上 ks_workshop 点亮前锁定态;点亮=12 件行板+同调装配段+Y 刷新升级行整体迁入)。
|
||||
## UI 只发意图信号(购买写口唯一归 MetaProgression.buy_treasure_unlock / toggle_attunement / buy_attune_refresh)。
|
||||
class_name TreasureView
|
||||
extends CampSubViewBase
|
||||
|
||||
signal buy_requested(attr: String) ## 秘宝购买/Y 刷新升级意图(presenter 按 attr 分派)
|
||||
signal attune_toggled(item_id: String) ## 同调装配切换意图(写口唯一归 MetaProgression.toggle_attunement)
|
||||
|
||||
const ROW_PLATE_COL := UiKit.CAMP_ROW_PLATE
|
||||
|
||||
var _ws_lock_l: Label # 锁定态行(ks_workshop 未点亮)
|
||||
var _ws_scroll: ScrollContainer # 秘宝匣内容容器(钥匙点亮后可见)
|
||||
var _workshop_holder: VBoxContainer # 秘宝匣行板(六行+同调装配段+Y 升级行;探针直读冻结)
|
||||
var _workshop_rows: Array[Dictionary] = [] # [{item_id, progress_l, status_l, buy_btn, price, unlocked, card}](探针消费)
|
||||
var _attune_header_l: Label # 同调装配段(Slots 点亮呈现)
|
||||
var _attune_btns: Array[Dictionary] = [] # [{item_id, btn, unlocked, equipped}]——装配/卸下切换钮(满位禁选随刷)
|
||||
var _attune_nodes: Array[Control] = [] # 段内节点册(open 重建时定向清理,不扰工坊行板)
|
||||
var _refresh_l: Label # Y 刷新升级行(双入口之一——树上 ks_refresh 同账)
|
||||
var _refresh_btn: Button
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var holder := VBoxContainer.new()
|
||||
holder.add_theme_constant_override("separation", 6)
|
||||
holder.custom_minimum_size = Vector2(0, 336)
|
||||
_ws_lock_l = UiKit.label("🔒 秘宝匣未开放\n\n点亮树上「秘宝匣」钥匙节点后展开。\n(前置=脊柱生命第 1 级)", UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
_ws_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
_ws_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_ws_lock_l.custom_minimum_size = Vector2(280, 0)
|
||||
_ws_scroll = ScrollContainer.new()
|
||||
_ws_scroll.custom_minimum_size = Vector2(300, 336)
|
||||
_ws_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_ws_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
_workshop_holder = VBoxContainer.new()
|
||||
_workshop_holder.add_theme_constant_override("separation", 6)
|
||||
_ws_scroll.add_child(_workshop_holder)
|
||||
holder.add_child(_ws_lock_l)
|
||||
holder.add_child(_ws_scroll)
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.custom_minimum_size = Vector2(300, 336)
|
||||
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
scroll.add_child(holder)
|
||||
_build_shell("秘宝匣", UiKit.TEX_ICON_WORKSHOP, scroll)
|
||||
|
||||
|
||||
func open_camp(data: Dictionary) -> void:
|
||||
_rebuild_workshop(data) # 行板+同调装配段+Y 尾行随开层全量重建(段尾链 _rebuild_attune)
|
||||
super.open_camp(data)
|
||||
|
||||
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
var balance := int(data.get("balance", 0))
|
||||
_balance_label.text = "钥石 %d" % balance
|
||||
_refresh_key_gate(data)
|
||||
_refresh_workshop_rows(data, balance)
|
||||
_refresh_attune_segment(data, balance)
|
||||
|
||||
|
||||
# 秘宝匣钥匙门(树上 ks_workshop 行 level 判据——零核心访问,纯视图读投影)
|
||||
func _refresh_key_gate(data: Dictionary) -> void:
|
||||
var lvl := 0
|
||||
for row in (data.get("tree", []) as Array):
|
||||
if String(row.get("node_id", "")) == "ks_workshop":
|
||||
lvl = int(row.get("level", 0))
|
||||
var open := lvl >= 1
|
||||
_ws_lock_l.visible = not open
|
||||
_ws_scroll.visible = open
|
||||
|
||||
|
||||
func _rebuild_workshop(data: Dictionary) -> void:
|
||||
_workshop_rows.clear()
|
||||
for child in _workshop_holder.get_children():
|
||||
child.queue_free()
|
||||
_attune_nodes.clear()
|
||||
_attune_btns.clear()
|
||||
_attune_header_l = null
|
||||
_refresh_l = null
|
||||
_refresh_btn = null
|
||||
for row in data.get("workshop", []):
|
||||
var card := _workshop_card(row)
|
||||
_workshop_holder.add_child(card["card"])
|
||||
_workshop_rows.append(card)
|
||||
_rebuild_attune(data)
|
||||
|
||||
|
||||
## 秘宝匣行板构建域:件名(分档后缀+效果摘要悬停)+进度列+状态列+钥石购买钮(购买件未解锁在列)
|
||||
func _workshop_card(row: Dictionary) -> Dictionary:
|
||||
var card := PanelContainer.new()
|
||||
card.add_theme_stylebox_override("panel", UiKit.panel_flat(ROW_PLATE_COL, UiKit.PANEL_BORDER, UiKit.RADIUS_OBJECT, 1, Vector4(10, 4, 10, 4)))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 12)
|
||||
card.add_child(line)
|
||||
var tier_name := String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), ""))
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")) + ("·" + tier_name if tier_name != "" else ""), UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(132, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
name_l.tooltip_text = String(row.get("desc", "")) # 件效果摘要悬停可见(分档呈现契约——槽位档位=名后缀)
|
||||
line.add_child(name_l)
|
||||
var progress_l := UiKit.label("", UiKit.FS_HINT, false, UiKit.STONE_DEEP)
|
||||
progress_l.custom_minimum_size = Vector2(150, 0)
|
||||
progress_l.clip_text = true # 营地溢出修——文字全宽不撑柜
|
||||
progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(progress_l)
|
||||
# 状态色用亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式
|
||||
var status_l := UiKit.label("", UiKit.FS_HINT, true, UiKit.STONE_DEEP)
|
||||
status_l.clip_text = true # 「已解锁 · 能力封存中」不撑柜(余宽吸收者)
|
||||
status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
line.add_child(status_l)
|
||||
# 钥石购买钮(购买件且未解锁时在列;写口唯一=buy_treasure_unlock,
|
||||
# 经 buy_requested(item_id) 信号走 presenter 分支;禁用态=余额不足)
|
||||
var buy_btn: Button = null
|
||||
if not bool(row.get("unlocked", false)) and String(row.get("mode", "")) == "keystone":
|
||||
buy_btn = UiKit.button("购买 %d" % int(row.get("price", 0)), 0.0) # 行板紧凑钮豁免
|
||||
buy_btn.pressed.connect(_emit_buy_requested.bind(String(row.get("item_id", ""))))
|
||||
line.add_child(buy_btn)
|
||||
return {"item_id": String(row.get("item_id", "")), "progress_l": progress_l, "status_l": status_l, "buy_btn": buy_btn, "price": int(row.get("price", 0)), "unlocked": bool(row.get("unlocked", false)), "card": card}
|
||||
|
||||
|
||||
# 同调装配段重建(秘宝匣尾部段:Slots 头行+九件行板+Y 升级尾行)
|
||||
func _rebuild_attune(data: Dictionary) -> void:
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
_attune_header_l = UiKit.label("", UiKit.FS_HINT, true, UiKit.INK)
|
||||
_workshop_holder.add_child(_attune_header_l)
|
||||
_attune_nodes.append(_attune_header_l)
|
||||
for row in att.get("items", []):
|
||||
var line := _attune_item_line(row)
|
||||
_workshop_holder.add_child(line)
|
||||
_attune_nodes.append(line)
|
||||
_refresh_attune_tail()
|
||||
|
||||
|
||||
## 同调件行板构建域:件名+形态+档位+装配切换钮(未解锁禁装——toggle 写口同守卫)
|
||||
func _attune_item_line(row: Dictionary) -> HBoxContainer:
|
||||
var iid := String(row.get("item_id", ""))
|
||||
var line := HBoxContainer.new()
|
||||
line.add_theme_constant_override("separation", 6)
|
||||
var name_l := UiKit.label(String(row.get("display_name", "")), UiKit.FS_HINT, true, UiKit.INK)
|
||||
name_l.custom_minimum_size = Vector2(96, 0)
|
||||
name_l.clip_text = true
|
||||
name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
line.add_child(name_l)
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能", UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
form_l.custom_minimum_size = Vector2(58, 0)
|
||||
line.add_child(form_l)
|
||||
# 档位标签(槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边)
|
||||
var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")), UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
tier_l.custom_minimum_size = Vector2(36, 0)
|
||||
line.add_child(tier_l)
|
||||
var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免
|
||||
btn.pressed.connect(attune_toggled.emit.bind(iid))
|
||||
btn.disabled = not bool(row.get("unlocked", false)) # 未解锁禁装(解锁为装配前置——toggle 写口同守卫)
|
||||
line.add_child(btn)
|
||||
_attune_btns.append({"item_id": iid, "btn": btn, "unlocked": bool(row.get("unlocked", false)), "equipped": bool(row.get("equipped", false))})
|
||||
return line
|
||||
|
||||
|
||||
## Y 升级尾行构建域(双入口之一——树上 ks_refresh 同账)
|
||||
func _refresh_attune_tail() -> void:
|
||||
var refresh_line := HBoxContainer.new()
|
||||
refresh_line.add_theme_constant_override("separation", 6)
|
||||
_refresh_l = UiKit.label("", UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)
|
||||
refresh_line.add_child(_refresh_l)
|
||||
_refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免
|
||||
_refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh"))
|
||||
refresh_line.add_child(_refresh_btn)
|
||||
_workshop_holder.add_child(refresh_line)
|
||||
_attune_nodes.append(refresh_line)
|
||||
|
||||
|
||||
## 秘宝匣行板刷新域:进度列(解锁混合制三形态)与状态色(亮板双色墨制)与购买钮余额联动
|
||||
func _refresh_workshop_rows(data: Dictionary, balance: int) -> void:
|
||||
var ws_by_id := {}
|
||||
for row in data.get("workshop", []):
|
||||
ws_by_id[String(row.get("item_id", ""))] = row
|
||||
for card in _workshop_rows:
|
||||
var row: Dictionary[Variant, Variant] = ws_by_id.get(String(card.item_id), {})
|
||||
if row.is_empty():
|
||||
continue
|
||||
var unlocked := bool(row.get("unlocked", false))
|
||||
card.progress_l.text = _workshop_progress_text(row, unlocked)
|
||||
var st_text := "未解锁"
|
||||
var st_col: Color = UiKit.STONE_DEEP
|
||||
if unlocked:
|
||||
st_text = "已解锁"
|
||||
st_col = UiKit.GOLD_INK # GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1
|
||||
card.status_l.text = st_text
|
||||
card.status_l.add_theme_color_override("font_color", st_col)
|
||||
card.status_l.tooltip_text = st_text # 截断兜底(状态列 EXPAND_FILL 吸余宽——悬停看全文)
|
||||
# 购买钮余额联动(禁用态=余额不足,常态防线;pattern 1 兜底拒绝可见归 presenter)
|
||||
if card.get("buy_btn") != null:
|
||||
card.buy_btn.disabled = balance < int(card.get("price", 0))
|
||||
|
||||
|
||||
## 进度列文案域(解锁混合制三形态:钥石购买价/条件进度拼接/已入手)
|
||||
func _workshop_progress_text(row: Dictionary, unlocked: bool) -> String:
|
||||
if unlocked:
|
||||
return "已入手"
|
||||
var mode := String(row.get("mode", ""))
|
||||
if mode == "keystone":
|
||||
return "钥石 %d" % int(row.get("price", 0))
|
||||
if mode == "condition":
|
||||
var parts: Array[String] = []
|
||||
for req_v in (row.get("reqs", []) as Array):
|
||||
var req: Dictionary[Variant, Variant] = req_v as Dictionary
|
||||
var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件"))
|
||||
parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))])
|
||||
return "、".join(parts)
|
||||
return ""
|
||||
|
||||
|
||||
## 同调装配段刷新域(Slots 点亮/满位禁选/Y 升级行余额联动——就地刷)
|
||||
func _refresh_attune_segment(data: Dictionary, balance: int) -> void:
|
||||
var att: Dictionary[Variant, Variant] = data.get("attune", {})
|
||||
if _attune_header_l != null:
|
||||
var slots_n := int(att.get("slots", 0))
|
||||
var eq: Array[Variant] = att.get("equipped", [])
|
||||
_attune_header_l.text = "── 同调装配 Slots %d/%d ──" % [eq.size(), slots_n]
|
||||
var eq_set := {}
|
||||
for v in eq:
|
||||
eq_set[String(v)] = true
|
||||
for card2 in _attune_btns:
|
||||
var on: bool = eq_set.has(String(card2.item_id))
|
||||
card2.btn.text = "卸下" if on else "装配"
|
||||
# 满位禁选+未解锁禁装(已装件恒可卸下;unlocked 快照随 rebuild 刷新)
|
||||
card2.btn.disabled = (not on and eq.size() >= slots_n) or (not on and not bool(card2.get("unlocked", false)))
|
||||
if _refresh_l != null:
|
||||
var rf: Dictionary[Variant, Variant] = att.get("refresh", {})
|
||||
_refresh_l.text = "三选一刷新 +%d 次/冒险 · Lv%d" % [int(rf.get("charges", 0)), int(rf.get("level", 0))]
|
||||
_refresh_btn.text = "已满级" if bool(rf.get("maxed", false)) else "升级 %d 钥石" % int(rf.get("price", 0))
|
||||
_refresh_btn.disabled = bool(rf.get("maxed", false)) or balance < int(rf.get("price", 0))
|
||||
|
||||
|
||||
func _emit_buy_requested(attr: String) -> void:
|
||||
buy_requested.emit(attr)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dcakccwlacegq
|
||||
@@ -0,0 +1,105 @@
|
||||
## 天赋树子界面(自 CampOverlay 树淬炼柜台抽出——承载面迁移,界面逻辑复用零重设计):
|
||||
## 树脊柱界面形态=主界面(脉分组行板:节点名/等级/价/购买钮+Fork 双态切换钮段)。
|
||||
## UI 只发意图信号(写口唯一归 MetaProgression.tree_buy/tree_buy_fork/tree_toggle_fork)。
|
||||
class_name TreeView
|
||||
extends CampSubViewBase
|
||||
|
||||
signal tree_buy_requested(node_id: String) ## 天赋树购买意图(写口唯一归 MetaProgression.tree_buy)
|
||||
signal tree_fork_toggled(group: String, side: String) ## 双态切换意图(一节点双态——零币切态)
|
||||
|
||||
var _tree_holder: VBoxContainer # 树淬炼内容(脊柱两行+钥匙/socket 行板+Fork 双态钮段)
|
||||
var _tree_buy_btns: Array[Dictionary] = [] # [{node_id, btn}]——树购买/解锁钮登记面(open/refresh 重建时刷新;探针消费)
|
||||
var _tree_fork_btns: Array[Dictionary] = [] # [{group, side, btn}]——Fork 双态切换钮登记面(探针消费)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_tree_holder = VBoxContainer.new()
|
||||
_tree_holder.add_theme_constant_override("separation", 6)
|
||||
var tree_scroll := ScrollContainer.new()
|
||||
tree_scroll.custom_minimum_size = Vector2(300, 336)
|
||||
tree_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
tree_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
tree_scroll.add_child(_tree_holder)
|
||||
var content := VBoxContainer.new()
|
||||
content.add_child(tree_scroll)
|
||||
_build_shell("天赋树 · 淬炼", "", content)
|
||||
|
||||
|
||||
func refresh_camp(data: Dictionary) -> void:
|
||||
_balance_label.text = "钥石 %d" % int(data.get("balance", 0))
|
||||
_rebuild_tree(data)
|
||||
|
||||
|
||||
# 天赋树段重建(脉分组行板:节点名/等级/价/购买钮 + Fork 双态切换钮;
|
||||
# 树数据 = 组合根 _camp_data().tree 投影 [{node_id, display_name, vein, kind, level, price, can_buy,
|
||||
# currency}...] + fork_groups [{group, node_id, active_side, owned, can_buy, price, sides:[{side, display_name, effects}]}])
|
||||
func _rebuild_tree(data: Dictionary) -> void:
|
||||
for c in _tree_holder.get_children():
|
||||
c.queue_free()
|
||||
_tree_buy_btns.clear()
|
||||
_tree_fork_btns.clear()
|
||||
var tree: Array[Variant] = data.get("tree", []) as Array
|
||||
var forks: Array[Variant] = data.get("fork_groups", []) as Array
|
||||
var vein_names := {"spine": "·脊柱淬炼·", "forge": "·锻造脉·", "keystone": "·钥石脉·", "attune": "·同调脉·"}
|
||||
var last_vein := ""
|
||||
for row in tree:
|
||||
var vein := String(row.get("vein", ""))
|
||||
if vein != last_vein:
|
||||
last_vein = vein
|
||||
var vh := UiKit.label(String(vein_names.get(vein, vein)), UiKit.FS_HINT, true, UiKit.GOLD_INK) # GOLD_DEEP 对柜台板 2.67:1 越带,换深金墨 5.21:1
|
||||
_tree_holder.add_child(vh)
|
||||
if String(row.get("kind", "")) == "fork":
|
||||
continue # Fork 行去行板化——由 fork_groups 段承载双态切换钮
|
||||
_tree_holder.add_child(_tree_row_line(row))
|
||||
for fg in forks:
|
||||
_tree_holder.add_child(_tree_fork_line(fg))
|
||||
|
||||
|
||||
## 树行板构建域:节点名(含门槛标记)+购买钮(追加序与锚句随段整体搬迁)
|
||||
func _tree_row_line(row: Dictionary) -> HBoxContainer:
|
||||
var hb := HBoxContainer.new()
|
||||
hb.add_theme_constant_override("separation", 6)
|
||||
var lvl := int(row.get("level", 0))
|
||||
var cap := int(row.get("cap", 1))
|
||||
var name_l := UiKit.label("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, cap], 13, false, UiKit.INK_LABEL)
|
||||
name_l.custom_minimum_size = Vector2(150, 0)
|
||||
name_l.clip_text = true
|
||||
var gate := String(row.get("gate_text", ""))
|
||||
if gate != "":
|
||||
name_l.text += " 🔒" # 等级阈值前置未达标标记(R1 机制位——悬停看门槛;达标后随 refresh 消失)
|
||||
name_l.tooltip_text = "等级门槛:%s" % gate
|
||||
hb.add_child(name_l)
|
||||
var btn := UiKit.button("购买 %d 钥石" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(func(): tree_buy_requested.emit(String(row.get("node_id", ""))))
|
||||
btn.disabled = not bool(row.get("can_buy", false))
|
||||
hb.add_child(btn)
|
||||
_tree_buy_btns.append({"node_id": String(row.get("node_id", "")), "btn": btn})
|
||||
return hb
|
||||
|
||||
|
||||
## Fork 双态行板构建域:组状态文案+解锁钮或双态切换钮
|
||||
func _tree_fork_line(fg: Dictionary) -> HBoxContainer:
|
||||
var group := String(fg.get("group", ""))
|
||||
var active := String(fg.get("active_side", ""))
|
||||
var owned := bool(fg.get("owned", false))
|
||||
var fb := HBoxContainer.new()
|
||||
fb.add_theme_constant_override("separation", 6)
|
||||
var state_txt := "未解锁"
|
||||
if owned:
|
||||
state_txt = "激活:%s" % active
|
||||
var gl := UiKit.label("%s[%s]" % [String(fg.get("display_name", group)), state_txt], 13, false, UiKit.INK_LABEL)
|
||||
fb.add_child(gl)
|
||||
if not owned:
|
||||
var bb := UiKit.button("解锁 %d 钥石" % int(fg.get("price", 0)), 0.0) # 行板紧凑钮豁免
|
||||
bb.pressed.connect(func(): tree_buy_requested.emit(String(fg.get("node_id", ""))))
|
||||
bb.disabled = not bool(fg.get("can_buy", false))
|
||||
fb.add_child(bb)
|
||||
_tree_buy_btns.append({"node_id": String(fg.get("node_id", "")), "btn": bb})
|
||||
else:
|
||||
for side_row in (fg.get("sides", []) as Array):
|
||||
var sd := String(side_row.get("side", ""))
|
||||
var sb := UiKit.button(String(side_row.get("display_name", sd)), 0.0) # 行板紧凑钮豁免
|
||||
sb.pressed.connect(func(): tree_fork_toggled.emit(group, sd))
|
||||
fb.add_child(sb)
|
||||
_tree_fork_btns.append({"group": group, "side": sd, "btn": sb})
|
||||
return fb
|
||||
@@ -0,0 +1 @@
|
||||
uid://mjjrdelhi7yg
|
||||
@@ -247,6 +247,19 @@ const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_
|
||||
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
|
||||
# 近景地台独立件(§6.1——源 48×12 九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定不吃 wall_ambient tint)
|
||||
const TEX_STAGE_PLAZA := "res://assets/art/scene/scene_stage_plaza.png"
|
||||
# 营地场景十层(art-bible §6.2——z0 bg 不透明全幅+z1 围墙透明细条+八物件透明层;布局矩形/命中矩形/z 序
|
||||
# 唯一事实源 = data/tables/camp_scene_layout.tres;素材名跟本组常量走(先定常量防断链);
|
||||
# 缺位 load_tex→null→程序化占位,冒烟安全)
|
||||
const TEX_CAMP_BG := "res://assets/art/scene/scene_camp_bg.png" # z0 背景层(不透明全幅 1280×800)
|
||||
const TEX_CAMP_WALL := "res://assets/art/scene/scene_camp_wall.png" # z1 围墙层(透明细条,门洞留缺口)
|
||||
const TEX_CAMP_BOARD := "res://assets/art/scene/scene_camp_board.png" # z2 布告板
|
||||
const TEX_CAMP_GATE := "res://assets/art/scene/scene_camp_gate.png" # z3 门扇
|
||||
const TEX_CAMP_DUMMY := "res://assets/art/scene/scene_camp_dummy.png" # z4 木桩
|
||||
const TEX_CAMP_FORGE := "res://assets/art/scene/scene_camp_forge.png" # z4 锻炉铁砧
|
||||
const TEX_CAMP_TENT := "res://assets/art/scene/scene_camp_tent.png" # z4 帐篷(承载件不可点)
|
||||
const TEX_CAMP_CHEST := "res://assets/art/scene/scene_camp_chest.png" # z5 宝箱
|
||||
const TEX_CAMP_NOTEBOOK := "res://assets/art/scene/scene_camp_notebook.png" # z5 笔记本
|
||||
const TEX_CAMP_CAMPFIRE := "res://assets/art/scene/scene_camp_campfire.png" # z6 篝火(命中只取下半火堆)
|
||||
# 护甲类型图标三键(art-bible §5.3 工单:对象键 = 甲档变体;源 24×24 显示 24px = 1× 甲类整数倍;
|
||||
# 缺位 load_tex→null→字形回退,冒烟安全)
|
||||
const TEX_ARMOR_LIGHT := "res://assets/ui/icons/ui_armor_light.png"
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 隔离样板单点
|
||||
const BoardViewScript := preload("res://presentation/views/board_view.gd") # 算式单点静态调用载体(迭代 46 卡C)
|
||||
|
||||
# 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动)
|
||||
const VIEWPORTS := [
|
||||
{"label": "基座 1280x800", "size": Vector2i(1280, 800)},
|
||||
{"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)},
|
||||
]
|
||||
const MIN_CELL := 36.0 # 格子可读门槛(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px)
|
||||
|
||||
var _problems: PackedStringArray = []
|
||||
var _cell_reads: Dictionary = {} # 逐档格边长实读留痕(label→cell_size;PASS 行引基座值)
|
||||
var _est_reads: Dictionary = {} # 逐档 13/14 板推定读数留痕(label→{13,14,avail};PASS 后打印对拍)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -41,8 +42,12 @@ func _ready() -> void:
|
||||
# 复原本项目基座(探针不该把进程留在末档尺寸)
|
||||
get_tree().root.content_scale_size = Vector2i(1280, 800)
|
||||
if _problems.is_empty():
|
||||
print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)"
|
||||
% [VIEWPORTS.size(), MIN_CELL])
|
||||
print("[layout_probe] PASS — %d 档几何不变量全过+格边长等价成立(基座实读 %.0fpx)〔格边长 V1〕"
|
||||
% [VIEWPORTS.size(), float(_cell_reads.get("基座 1280x800", -1.0))])
|
||||
for label in _est_reads:
|
||||
var e: Dictionary = _est_reads[label]
|
||||
print("[layout_probe] [%s] 13 板推定 %dpx/14 板推定 %dpx(实板可用面积 %s)〔格边长 V4〕"
|
||||
% [label, int(e["13"]), int(e["14"]), str(e["avail"])])
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in _problems:
|
||||
@@ -91,5 +96,25 @@ func _check_one(label: String, main: Node) -> void:
|
||||
_problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)"
|
||||
% [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]])
|
||||
var cell: float = main.ui.battle.board_zone.board_view.get("cell_size")
|
||||
if cell < MIN_CELL:
|
||||
_problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])
|
||||
_cell_reads[label] = cell
|
||||
# 格边长接线守卫——判据=等价(生产写入值与单点算式严格相等,生产接线断开即红;
|
||||
# 全仓禁任何人造格边长可读阈值)。
|
||||
# 板型 N 无公开出口→`bv.get("_board")` 内部字段直查=受控例外(改动—验收对照表行 1 注明理由)。
|
||||
var b = bv.get("_board")
|
||||
if b == null:
|
||||
_problems.append("[%s] _board 为 null(板型 N 无从取值,等价断言无依据)〔格边长 V1〕" % label)
|
||||
elif cell != float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)):
|
||||
_problems.append("[%s] 格边长等价破——生产写入 %.1fpx ≠ _cell_for(n=%d, avail=%s) 返回 %.1fpx(实板可用面积 %s)〔格边长 V1〕"
|
||||
% [label, cell, maxi(int(b.width), int(b.height)), str(bv.size),
|
||||
float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)), str(bv.size)])
|
||||
# 最大板推定留痕(迭代 46 卡C T2——实板可用面积代入 13/14 板型求算式单点,读数打印不作判红:
|
||||
# 锚点=用户验收的现行渲染值;对拍归实现报告,对拍不一致=卡 §9 L3 #2 呈报,禁实现侧改锚;
|
||||
# 零尺寸前置=可用面积不可信时推定无依据)
|
||||
if bv.size.x <= 0.0 or bv.size.y <= 0.0:
|
||||
_problems.append("[%s] board_view 零尺寸(可用面积不可信,格边长推定无依据)〔格边长 V4〕" % label)
|
||||
else:
|
||||
_est_reads[label] = {
|
||||
"13": BoardViewScript._cell_for(13, bv.size),
|
||||
"14": BoardViewScript._cell_for(14, bv.size),
|
||||
"avail": str(bv.size),
|
||||
}
|
||||
@@ -27,6 +27,7 @@ func _ready() -> void:
|
||||
await _step_boot_menu()
|
||||
await _step_menu_to_camp()
|
||||
await _step_camp_back_to_menu()
|
||||
await _step_camp_esc_and_hud() # 卡⑤——Esc 四态路由/三出口/设置语境分支/HUD 读数
|
||||
await _step_save_select_open_back() # 迭代 33 卡③ sl10——选档开合+返回路径+ESC 语境不响应
|
||||
await _step_menu_to_adventure()
|
||||
await _step_level_to_battle_and_back()
|
||||
@@ -51,7 +52,7 @@ func _ready() -> void:
|
||||
func _screen_seals() -> Dictionary:
|
||||
return {
|
||||
"菜单": main.ui.screens.start_overlay != null and main.ui.screens.start_overlay.visible,
|
||||
"营地": main.ui.modals.camp_overlay != null and main.ui.modals.camp_overlay.visible,
|
||||
"营地": main.ui.modals.camp.scene != null and main.ui.modals.camp.scene.visible,
|
||||
"冒险": main.run_scope != null and main.run_scope.is_adventure,
|
||||
"终局": main.ui.modals.end_overlay != null and main.ui.modals.end_overlay.visible,
|
||||
}
|
||||
@@ -115,14 +116,16 @@ func _step_menu_to_camp() -> void:
|
||||
_expect_exclusive("②菜单→营地", "营地")
|
||||
|
||||
|
||||
# ---- ③ 营地 → 菜单(回退路径:营地关闭发自覆盖层自身信号)----
|
||||
# ---- ③ 营地 → 菜单(回退路径:篝火二次确认动线——卡④ 营地唯一回菜单出口;选档层随关层收闭同收)----
|
||||
func _step_camp_back_to_menu() -> void:
|
||||
if main.ui.modals.camp_overlay == null:
|
||||
_problems.append("③营地→菜单:营地覆盖层句柄缺失")
|
||||
if main.ui.modals.camp.scene == null:
|
||||
_problems.append("③营地→菜单:营地场景根句柄缺失〔句柄子容器批 V1〕")
|
||||
return
|
||||
main.ui.modals.camp_overlay.closed.emit()
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"campfire")
|
||||
await _frames()
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
if not main.ui.modals.confirm_dialog.visible:
|
||||
_problems.append("③篝火:二次确认弹窗未现(camp.md v2.2 必须先确认)〔营地图批 V7〕")
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(confirmed 先于关层——卡④ T4)
|
||||
await _frames()
|
||||
_expect_exclusive("③营地→菜单(回退)", "菜单")
|
||||
|
||||
@@ -229,3 +232,73 @@ func _step_end_back_to_menu() -> void:
|
||||
_expect_exclusive("⑦终局→菜单", "菜单")
|
||||
if main.run_scope != null and main.run_scope.is_adventure:
|
||||
_problems.append("⑦终局→菜单:冒险态未收束(run_scope 仍标 is_adventure)")
|
||||
|
||||
|
||||
# ---- ③' 营地 Esc 浮窗+HUD(卡⑤——真实键盘合成路径 push_input;四态路由+三出口+语境分支+余额单口)----
|
||||
func _step_camp_esc_and_hud() -> void:
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_esc", false, false, true)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"):
|
||||
_problems.append("[esc] 营地入口缺失(选档层新档钮驱动中断)〔ESC批 V1〕")
|
||||
return
|
||||
await _frames()
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 营地根态 Esc 未唤起浮窗(契约 9③)〔ESC批 V1〕〔ESC批 B1〕")
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 再按 Esc 未关浮窗(Q2 同键单义切换失守)〔ESC批 V1〕")
|
||||
_push_esc(true) # echo 连发(真实键盘长按形态)
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] echo 事件唤起浮窗(触发沿 pressed 且非 echo 失守)〔ESC批 V1〕")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 子界面开态
|
||||
await _frames()
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.tree.visible:
|
||||
_problems.append("[esc] 子界面开态 Esc 未关窗(camp.md v2.3 定稿——先关最上层窗)〔ESC批 V1〕")
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 关窗同拍唤起浮窗(先关窗后浮窗——两拍应分离)〔ESC批 V1〕")
|
||||
await _frames()
|
||||
# HUD 余额(契约 5 时机③——夹具直写后显式触发才变;禁 band 自刷假设)
|
||||
main.meta.meta_state._add_keystone(9999)
|
||||
main._camp_pres.views.refresh_bus.refresh(main._camp_data()) # 显式触发刷新单口
|
||||
var band: Control = main.ui.modals.camp.scene.get_node("HudBandSlot").get_child(0)
|
||||
if not String(band._badge.value_label.text).begins_with("钥石 9999"):
|
||||
_problems.append("[esc] HUD 余额读数≠存档单口现值(直写+显式触发未变)〔ESC批 V6〕〔ESC批 B2〕")
|
||||
# 设置开在浮窗之上→Esc 无反馈且浮窗不误关(契约 9①);关设置回浮窗不回主菜单(契约 3 语境分支)
|
||||
_push_esc()
|
||||
await _frames()
|
||||
main._on_esc_settings()
|
||||
await _frames()
|
||||
if not main.ui.modals.settings_overlay.visible:
|
||||
_problems.append("[esc] 浮窗设置出口断裂〔ESC批 V4〕")
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 设置开态 Esc 误关浮窗(契约 9①——浮窗不隔层关闭)〔ESC批 V1〕")
|
||||
main._on_settings_closed()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[esc] 关设置后未回浮窗或回弹主菜单(契约 3 语境分支失守)〔ESC批 V4〕〔ESC批 B3〕")
|
||||
# 返回主菜单动线(契约 4——浮窗自收+营地隐+选档收+菜单显)
|
||||
main.ui.modals.camp.esc_overlay.return_requested.emit()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible or main.ui.modals.camp.scene.visible or main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[esc] 返回主菜单动线失守(浮窗自收/营地隐/选档收/菜单显)〔ESC批 V3〕〔ESC批 B3〕")
|
||||
# 非营地语境(boot 菜单)Esc→无响应(契约 9④ 回归面)
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 非营地语境 Esc 唤起浮窗(契约 9④ 失守)〔ESC批 V1〕")
|
||||
|
||||
|
||||
func _push_esc(echo := false) -> void:
|
||||
var ev := InputEventKey.new()
|
||||
ev.keycode = KEY_ESCAPE
|
||||
ev.physical_keycode = KEY_ESCAPE
|
||||
ev.pressed = true
|
||||
ev.echo = echo
|
||||
main.get_viewport().push_input(ev, true) # 真实键盘合成路径(:153 先例——push_input 不更新 Input 单例动作态)
|
||||
@@ -53,7 +53,7 @@ func _ready() -> void:
|
||||
var top_ry := -1
|
||||
for c: Node in [main.ui.screens.start_overlay, main.ui.screens.battle_root, main.ui.screens.inter_overlay,
|
||||
main.ui.modals.legend_overlay, main.ui.modals.node_overlay, main.ui.modals.level_screen, main.ui.modals.curio_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp.scene, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.status_panel, main.ui.modals.career_overlay, main.ui.modals.confirm_dialog, main.ui.modals.debug_overlay]:
|
||||
if c == null or not (c is CanvasItem) or not (c as CanvasItem).visible:
|
||||
continue
|
||||
@@ -119,17 +119,15 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("生涯页关闭后页未隐藏或开始层未恢复")
|
||||
# ② 营地(进档开层新路径——卡② mn9;槽模板注入隔离命名空间)
|
||||
# ② 营地场景图(进档开层新路径——卡② mn9;截图=可点击场景图形态——卡④ 契约 9 V1 验收载体)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
|
||||
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)")
|
||||
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
await _snap("camp")
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.camp_overlay, "关闭营地", "contains")
|
||||
if close_btn != null:
|
||||
close_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态)
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"campfire") # 收闭改接篝火动线(营地根态无关闭钮——卡④ T5)
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(关层收闭——选档层随收)
|
||||
await get_tree().process_frame
|
||||
# ③ 关卡画面(开始冒险→停点出图——入口停点语义;函数直调中间态 mn10)
|
||||
main._start_adventure_from_menu()
|
||||
|
||||
+106
-53
@@ -62,123 +62,131 @@ func _ready() -> void:
|
||||
# 原 swap=false 形态的写盘走 iso 实例旧路径,与槽文件断言不同源——卡② mn9 换代)
|
||||
var iso_c = ProbeUiUtil.isolate_save(main, "smoke_click_probe_camp", false, false, true)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_click_probe_camp"):
|
||||
_problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
_problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
_problems.append("营地层未打开(进档开层新路径)")
|
||||
elif main.ui.modals.camp_overlay._tree_buy_btns.is_empty():
|
||||
_problems.append("树淬炼柜台购买钮缺失(点击路径——四柜台重排后树=主界面)")
|
||||
if not main.ui.modals.camp.scene.visible:
|
||||
_problems.append("营地场景根未打开(进档开层新路径)〔句柄子容器批 V1〕")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 场景点选真实路由(木桩→天赋树子界面——卡④ T5)
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.tree._tree_buy_btns.is_empty():
|
||||
_problems.append("树淬炼购买钮缺失(四柜台拆解后树=独立子界面)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
# 树钮句柄用后即取(refresh_camp 随刷重建树行板——旧钮 queue_free,跨刷新句柄必失效)
|
||||
iso_c.meta_state._add_keystone(9999)
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var buy_btn: Button = null
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == "sp_hp":
|
||||
buy_btn = card.btn
|
||||
if buy_btn == null:
|
||||
_problems.append("脊柱生命行缺失(点击路径)")
|
||||
_problems.append("脊柱生命行缺失(点击路径)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
var bal_before: int = iso_c.meta_state.keystone_balance()
|
||||
var price: int = int(main.db.crossrun.meta_tree.row_by_id("sp_hp").get("price", 0))
|
||||
buy_btn.pressed.emit() # 合成购买点击 → _on_camp_tree_buy → meta.tree_buy(写调用唯一归组合根)
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal_before - price:
|
||||
_problems.append("营地树购买未出账(%d→%d 应 −%d)" % [bal_before, iso_c.meta_state.keystone_balance(), price])
|
||||
_problems.append("营地树购买未出账(%d→%d 应 −%d)〔句柄子容器批 V1〕〔营地图批 V4〕" % [bal_before, iso_c.meta_state.keystone_balance(), price])
|
||||
var iso_chk = load("res://core/save_manager.gd").new("user://smoke_click_probe_camp_slot_1.json")
|
||||
iso_chk.load_from_disk()
|
||||
if int(iso_chk.meta_state.tree_state().nodes.get("sp_hp", 0)) != 1:
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)")
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)〔句柄子容器批 V1〕〔营地图批 V4〕")
|
||||
# Fork 双态切换点击路径(V20——购=解锁默认 A,钮切 B;前置=同调位 1,先点亮)
|
||||
var socket_btn: Button = null
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == "att_socket1":
|
||||
socket_btn = card.btn
|
||||
if socket_btn != null:
|
||||
socket_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var fork_btn: Button = null
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
if String(card.node_id) == "fork_posture":
|
||||
fork_btn = card.btn
|
||||
if fork_btn == null:
|
||||
_problems.append("Fork 解锁钮缺失(点击路径)")
|
||||
_problems.append("Fork 解锁钮缺失(点击路径)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
fork_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var side_b: Button = null
|
||||
for fk in main.ui.modals.camp_overlay._tree_fork_btns:
|
||||
for fk in main.ui.modals.camp.tree._tree_fork_btns:
|
||||
if String(fk.group) == "fg_posture" and String(fk.side) == "b":
|
||||
side_b = fk.btn
|
||||
if side_b == null:
|
||||
_problems.append("Fork 双态切换钮缺失(解锁后应展开 a/b 钮)")
|
||||
_problems.append("Fork 双态切换钮缺失(解锁后应展开 a/b 钮)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
side_b.pressed.emit() # 合成切态点击 → _on_camp_fork_toggle → meta.tree_toggle_fork
|
||||
await get_tree().process_frame
|
||||
var iso_fk = load("res://core/save_manager.gd").new("user://smoke_click_probe_camp_slot_1.json")
|
||||
iso_fk.load_from_disk()
|
||||
if String(iso_fk.meta_state.tree_state().fork_sides.get("fg_posture", "")) != "b":
|
||||
_problems.append("Fork 切态未持久(reload 后激活态 != b)")
|
||||
# ---- 营地六处灰字取色断言(迭代36 wc4——真实入口开营地后按文本锚取 label,
|
||||
# font_color 逐处 == STONE_DEEP;六源位 :167 图鉴锁文/:417+:476 行状态字/:509 同调形态/:513 档位标签/:525 刷新行)----
|
||||
var camp_labels: Array = []
|
||||
_collect_labels_by_text(main.ui.modals.camp_overlay, camp_labels)
|
||||
var atlas_locks := _labels_with_text(camp_labels, "柜台结构位预留")
|
||||
if atlas_locks.is_empty():
|
||||
_problems.append("营地灰字:图鉴柜台锁文 label 未找到(wc4 定位锚失守)")
|
||||
for l in atlas_locks:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)")
|
||||
# 「未解锁」锚收窄到秘宝匣行容器子树(树行/锻造行也有「未解锁」态字样——非 wc4 目标位)
|
||||
var locked_status: Array = []
|
||||
if main.ui.modals.camp_overlay.get("_workshop_holder") != null:
|
||||
var ws_labels: Array = []
|
||||
_collect_labels_by_text(main.ui.modals.camp_overlay._workshop_holder, ws_labels)
|
||||
locked_status = _labels_with_text(ws_labels, "未解锁")
|
||||
_problems.append("Fork 切态未持久(reload 后激活态 != b)〔句柄子容器批 V1〕")
|
||||
# ---- 营地灰字取色断言(迭代36 wc4——四子界面拆分后按视图分扫;卡④ T5 迁移):
|
||||
# 图鉴锁文→codex 视图(锚随形态订正去『柜台』);秘宝行未解锁/同调形态/档位/刷新行→treasure 视图
|
||||
# _workshop_holder 子树。font_color 逐处 == STONE_DEEP;树链段已闭树界面。
|
||||
main.ui.modals.camp.tree.closed.emit() # 树界面返回(关层归 presenter 单口)
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"chest") # 宝箱→秘宝匣(灰字四源位宿主)
|
||||
await get_tree().process_frame
|
||||
var ws_labels: Array = []
|
||||
_collect_labels_by_text(main.ui.modals.camp.treasure._workshop_holder, ws_labels)
|
||||
var locked_status: Array = _labels_with_text(ws_labels, "未解锁")
|
||||
if locked_status.is_empty():
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)")
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)〔句柄子容器批 V1〕")
|
||||
for l in locked_status:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)")
|
||||
_problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)〔句柄子容器批 V1〕")
|
||||
var form_found := false
|
||||
for l in camp_labels:
|
||||
for l in ws_labels:
|
||||
if l.text == "被动" or l.text == "主动·充能":
|
||||
form_found = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:同调形态标签未用 STONE_DEEP(wc4 :509)")
|
||||
_problems.append("营地灰字:同调形态标签未用 STONE_DEEP(wc4 :509)〔句柄子容器批 V1〕")
|
||||
if not form_found:
|
||||
_problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)")
|
||||
_problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)〔句柄子容器批 V1〕")
|
||||
var tier_vals := {}
|
||||
for v in UiKit.TREASURE_TIER_LABELS.values():
|
||||
tier_vals[String(v)] = true
|
||||
var tier_found := false
|
||||
for l in camp_labels:
|
||||
for l in ws_labels:
|
||||
if tier_vals.has(l.text):
|
||||
tier_found = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:同调档位标签未用 STONE_DEEP(wc4 :513)")
|
||||
_problems.append("营地灰字:同调档位标签未用 STONE_DEEP(wc4 :513)〔句柄子容器批 V1〕")
|
||||
if not tier_found:
|
||||
_problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)")
|
||||
_problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)〔句柄子容器批 V1〕")
|
||||
var refresh_inked := false
|
||||
for l in camp_labels:
|
||||
for l in ws_labels:
|
||||
if String(l.text).contains("三选一刷新"):
|
||||
refresh_inked = true
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)")
|
||||
_problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)〔句柄子容器批 V1〕")
|
||||
if not refresh_inked:
|
||||
_problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)")
|
||||
main._on_camp_closed()
|
||||
_problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)〔句柄子容器批 V1〕")
|
||||
main.ui.modals.camp.treasure.closed.emit()
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"board") # 布告板→图鉴占位(锁文宿主)
|
||||
await get_tree().process_frame
|
||||
var codex_labels: Array = []
|
||||
_collect_labels_by_text(main.ui.modals.camp.codex, codex_labels)
|
||||
var atlas_locks := _labels_with_text(codex_labels, "结构位预留")
|
||||
if atlas_locks.is_empty():
|
||||
_problems.append("营地灰字:图鉴锁文 label 未找到(wc4 定位锚失守——锚随形态订正去『柜台』)〔句柄子容器批 V1〕〔营地图批 V6〕")
|
||||
for l in atlas_locks:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)〔句柄子容器批 V1〕〔营地图批 V6〕")
|
||||
main.ui.modals.camp.codex.closed.emit()
|
||||
await get_tree().process_frame
|
||||
main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3)
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("营地关闭后未恢复开始层")
|
||||
if main.ui.modals.camp.scene.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("营地关闭后未恢复开始层〔句柄子容器批 V1〕")
|
||||
# ---- 选档界面行为走查(迭代 33 卡③):sl5 空槽新档链/sl6 有档槽进档链/sl7 删档二次确认双路径
|
||||
# /sl8 消费方分派单口/sl12 动线序不回闪。开层=函数直调(「进入存档」钮归卡② mn3——中间态在案);
|
||||
# 隔离=attach_slot_template 注入命名空间,夹具按槽直写(卡① 注入口)。----
|
||||
@@ -208,7 +216,7 @@ func _ready() -> void:
|
||||
new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp
|
||||
for fi in 3:
|
||||
await get_tree().process_frame
|
||||
samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp_overlay.visible, main.ui.screens.start_overlay.visible])
|
||||
samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp.scene.visible, main.ui.screens.start_overlay.visible])
|
||||
if samples[0][0]:
|
||||
_problems.append("[savesel] sl12 新档后选档层仍可见(落点开层未发生)")
|
||||
elif not samples[0][1]:
|
||||
@@ -217,7 +225,7 @@ func _ready() -> void:
|
||||
_problems.append("[savesel] sl12 动线序违约:开始层在落点帧恢复(回闪一帧)")
|
||||
if main.save_manager.slots.active_slot() != 1 or not FileAccess.file_exists(csel_tpl % 1):
|
||||
_problems.append("[savesel] sl5 新档链失守(active=%d 应=1 且槽1 白板应落盘)" % main.save_manager.slots.active_slot())
|
||||
var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp_overlay, [])
|
||||
var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp.scene, [])
|
||||
if not occluded.has(main.ui.modals.save_select_overlay):
|
||||
_problems.append("[savesel] sl12 选档层被关闭而非栈记账挂起(先关后开形态——遮挡挂起中间态钉死)")
|
||||
if int((JSON.parse_string(FileAccess.get_file_as_string(csel_tpl % 1)) as Dictionary).get("meta", {}).get("keystone_balance", -1)) != 0:
|
||||
@@ -235,7 +243,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
if main.save_manager.slots.active_slot() != 2:
|
||||
_problems.append("[savesel] sl6 进档链失守(active=%d 应=2——激活该槽)" % main.save_manager.slots.active_slot())
|
||||
if not main.ui.modals.camp_overlay.visible or main.ui.modals.save_select_overlay.visible:
|
||||
if not main.ui.modals.camp.scene.visible or main.ui.modals.save_select_overlay.visible:
|
||||
_problems.append("[savesel] sl6 落点未开营地层或选档未隐藏")
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
@@ -668,6 +676,51 @@ func _ready() -> void:
|
||||
# 卡④大门 T5 接收,两头登记禁静默删除;续玩钮已退役,主菜单确认链已摘 mn6)。
|
||||
# ① 战斗收口断点写盘断言保留(上方——冒险流直调中间态 mn10)。
|
||||
|
||||
# ---- 大门两分支断言段(卡④ T5 接收——卡② 移交落点;V3 断言面)----
|
||||
# 驱动=营地场景根点选信号(真实路由)→组合根 _on_camp_object_pressed→确认弹窗。
|
||||
# 无断点=两出口(是/否);有断点=三出口(set_breakpoint 注入选路停点形——同下方开场行③先例)。
|
||||
# 判据绑可观测提交面:点大门不直接进冒险(is_adventure 翻转只在 confirmed 后);cancelled 恒=
|
||||
# 无变更退回营地(写口零调用——余额读数不变+营地根 visible);开局后营地场景屏隐藏。
|
||||
ProbeUiUtil.isolate_save(main, "smoke_click_probe_gate", true, false, true)
|
||||
main._camp_pres.open_camp()
|
||||
await get_tree().process_frame
|
||||
var bal_gate: int = main._camp_data()["balance"]
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.confirm_dialog.is_open():
|
||||
_problems.append("大门无断点:确认弹窗未开(点大门应先出确认不直接进冒险)〔营地图批 V3〕")
|
||||
if main.run_scope.is_adventure:
|
||||
_problems.append("大门无断点:未确认即进冒险〔营地图批 V3〕")
|
||||
if main.ui.modals.confirm_dialog._secondary_btn.visible:
|
||||
_problems.append("大门无断点:次钮应隐藏(两出口形态)〔营地图批 V3〕")
|
||||
main.ui.modals.confirm_dialog.cancelled.emit()
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp.scene.visible or main.run_scope.is_adventure or main._camp_data()["balance"] != bal_gate:
|
||||
_problems.append("大门取消:未无变更退回营地(写口零调用/营地根 visible 失守)〔营地图批 V3〕")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 「是」→开始新冒险
|
||||
await get_tree().process_frame
|
||||
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
|
||||
_problems.append("大门确认:冒险未发起或营地场景屏未隐藏〔营地图批 V3〕〔营地图批 B1〕")
|
||||
# 有断点三出口(选路停点形注入——同开场行③ set_breakpoint 先例)
|
||||
main.save_manager.set_breakpoint({
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 30, "gp": 10, "keystone": 0,
|
||||
"level_index": 1, "bonus": {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0},
|
||||
"shop_purchased": {}, "relics": [], "keystone_drops": [], "bonus_taken": {},
|
||||
"node_id": -1, "visited_ids": [], "graph_gen_v2": 1,
|
||||
})
|
||||
main._camp_pres.open_camp()
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.confirm_dialog.is_open() or not main.ui.modals.confirm_dialog._secondary_btn.visible:
|
||||
_problems.append("大门有断点:三出口形态失守(次钮应现形)〔营地图批 V3〕")
|
||||
main.ui.modals.confirm_dialog.secondary_confirmed.emit() # 「开始新冒险」出口
|
||||
await get_tree().process_frame
|
||||
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
|
||||
_problems.append("大门有断点次确认:冒险未发起或营地未隐藏〔营地图批 V3〕")
|
||||
|
||||
# ---- 开场行三路径段(迭代36 wc1/wc2——真实入口驱动;隔离新档)----
|
||||
# 驱动设计(0/0 白板局生存性不成立的实证处置——L2 记录进实现报告):
|
||||
# ① 菜单开新局·白板反向=独立短局:0/0 滑条开局断言 L1 开场行零后缀(不开驱动——
|
||||
|
||||
@@ -45,15 +45,11 @@ func _pairs() -> Array:
|
||||
["商店·爬价规则墨/暖亮页底", UiKit.WARM_SOFT, UiKit.BG_BRIGHT, 1.0],# 货架面爬价规则行(page_shell 亮底)
|
||||
["商店·货架卡名墨/货架板", UiKit.INK_TITLE, UiKit.PANEL_DATA, 1.0], # bcard 货架卡商品名(换 TG 带后未机检位)
|
||||
["商店·货架价签墨/货架板", UiKit.GOLD_INK, UiKit.PANEL_DATA, 1.0], # bcard 价签「N ◈」(深金墨 5.21:1)
|
||||
["营地·等级章字/头带深棕", UiKit.INK_ON_DEEP, UiKit.CAMP_HEADER_BAND, 1.0],# 锻造行 Lv 章(木底小章亮墨)
|
||||
["营地·柜台头带字/头带深棕", UiKit.WARM, UiKit.CAMP_HEADER_BAND, 1.0], # 三柜台头带标题(暖白 bold)
|
||||
["营地·铭牌副题墨/铭牌板", UiKit.STONE_DEEP, UiKit.PANEL_DATA, 1.0],# 营地铭牌副题「跨局成长账本…」(灰石深化 5.98:1)
|
||||
["营地·行名墨/行板", UiKit.INK, UiKit.CAMP_ROW_PLATE, 1.0], # 锻造/工坊行名(墨族压暖米行板)
|
||||
["营地·行价格墨/行板", UiKit.STONE_DEEP, UiKit.CAMP_ROW_PLATE, 1.0],# 锻造行「N 钥石」价签/工坊进度行(灰石深化 6.70:1)
|
||||
["营地·树分组头墨/柜台板", UiKit.GOLD_INK, UiKit.PANEL_DATA, 1.0],# 天赋树脉分组头「·锻造脉·」(深金墨 5.21:1)
|
||||
["营地·树行名墨/柜台板", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # 天赋树行「名 Lv0/1」(标签深墨)
|
||||
["营地·工坊状态墨/行板", UiKit.GOLD_INK, UiKit.CAMP_ROW_PLATE, 1.0],# 工坊状态字「已解锁」(深金墨 5.84:1·最不利现役态)
|
||||
["营地·最近冒险墨/暖亮底", UiKit.WARM_SOFT, UiKit.BG_BRIGHT, 1.0], # 底栏最近冒险条带(暖白次级)
|
||||
["设置·警示文/设置板", UiKit.INK_HINT, UiKit.PANEL_DATA, 1.0], # 设置警示文/后果说明(暖棕提示墨)
|
||||
["设置·选项标签/设置板", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # 设置行标签/状态字(标签深墨)
|
||||
["三选一·停点头部墨/暗页底", UiKit.WARM_SOFT, UiKit.BG_ROOT, 1.0], # 三选一停点头部/回血反馈行(page_shell 根底档)
|
||||
|
||||
+28
-18
@@ -25,34 +25,44 @@ func _ready() -> void:
|
||||
# 营地先行段只换 meta 不换 save_manager——冒险段换注零档在前)----
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", false, false, false)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_probe_camp"):
|
||||
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
problems.append("营地层未打开(进档开层新路径)")
|
||||
if not main.ui.modals.camp.scene.visible:
|
||||
problems.append("营地场景根未打开(进档开层新路径)〔句柄子容器批 V1〕")
|
||||
elif main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)")
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
if not String(main.ui.modals.camp_overlay._balance_label.text).begins_with("钥石"):
|
||||
problems.append("营地余额文案缺失(%s)" % main.ui.modals.camp_overlay._balance_label.text)
|
||||
if main.ui.modals.camp_overlay._tree_buy_btns.is_empty():
|
||||
problems.append("树淬炼柜台购买钮缺失(四柜台重排后树=主界面)")
|
||||
# 天赋树子界面(场景点选真实路由——木桩命中区进树界面;卡④ T5 深员改指)
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"dummy")
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp.tree.visible:
|
||||
problems.append("天赋树子界面未打开(场景点选路由中断)〔营地图批 V2〕")
|
||||
if not String(main.ui.modals.camp.tree._balance_label.text).begins_with("钥石"):
|
||||
problems.append("营地余额文案缺失(%s)〔句柄子容器批 V1〕" % main.ui.modals.camp.tree._balance_label.text)
|
||||
if main.ui.modals.camp.tree._tree_buy_btns.is_empty():
|
||||
problems.append("树淬炼购买钮缺失(四柜台拆解后树=独立子界面)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
var btn_by_id := {}
|
||||
for card2 in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
for card2 in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
btn_by_id[String(card2.node_id)] = card2.btn
|
||||
if not btn_by_id.has("sp_hp") or bool(btn_by_id["sp_hp"].disabled):
|
||||
problems.append("脊柱生命 L1 白板可购钮未启用(无门槛——can_buy 不含余额面语义)")
|
||||
problems.append("脊柱生命 L1 白板可购钮未启用(无门槛——can_buy 不含余额面语义)〔句柄子容器批 V1〕")
|
||||
if not btn_by_id.has("att_socket2") or not bool(btn_by_id["att_socket2"].disabled):
|
||||
problems.append("同调位 2 前置未亮应禁用(点亮前置语义)")
|
||||
if main.ui.modals.camp_overlay._workshop_rows.size() != 12:
|
||||
problems.append("秘宝匣柜台行数错误(%d ≠ 12——迭代 22 秘宝批换代)" % main.ui.modals.camp_overlay._workshop_rows.size())
|
||||
main._on_camp_closed()
|
||||
problems.append("同调位 2 前置未亮应禁用(点亮前置语义)〔句柄子容器批 V1〕")
|
||||
main.ui.modals.camp.tree.closed.emit()
|
||||
await get_tree().process_frame
|
||||
# 秘宝匣子界面(场景点选真实路由——宝箱命中区)
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"chest")
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.treasure._workshop_rows.size() != 12:
|
||||
problems.append("秘宝匣行数错误(%d ≠ 12——迭代 22 秘宝批换代)〔句柄子容器批 V1〕" % main.ui.modals.camp.treasure._workshop_rows.size())
|
||||
main.ui.modals.camp.treasure.closed.emit()
|
||||
await get_tree().process_frame
|
||||
main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3)
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地关闭后未恢复开始层(开合往返失守)")
|
||||
if main.ui.modals.camp.scene.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地关闭后未恢复开始层(开合往返失守)〔句柄子容器批 V1〕")
|
||||
# 换回全新零档(营地段购买态不泄入后续冒险段——确定性;先删后建防陈旧档泄入)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", true, false, true)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支)
|
||||
|
||||
+109
-17
@@ -39,36 +39,118 @@ func _ready() -> void:
|
||||
_settle()
|
||||
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_struct_probe_menu"):
|
||||
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
else:
|
||||
_settle()
|
||||
await _audit("camp", [main.ui.modals.camp_overlay])
|
||||
# 营地灰字源 token 扫描(迭代36 wc4 静态半——源内零裸 UiKit.STONE;判别式=
|
||||
# STONE 出现次数 == STONE_DEEP 出现次数,即每处 STONE 都带 _DEEP 后缀)
|
||||
var camp_src := FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd")
|
||||
if camp_src.count("UiKit.STONE") != camp_src.count("UiKit.STONE_DEEP"):
|
||||
problems.append("[camp] camp_overlay_view.gd 存在裸 UiKit.STONE(非 _DEEP 后缀——wc4 源扫描)")
|
||||
main._on_camp_closed()
|
||||
await _audit("camp", [main.ui.modals.camp.scene]) # 审计根=营地场景图(卡④ T5——CampOverlay 退役)
|
||||
# 营地灰字源 token 扫描(迭代36 wc4 静态半——营地域视图源内零裸 UiKit.STONE;判别式=
|
||||
# STONE 出现次数 == STONE_DEEP 出现次数;卡④ T5 改扫场景根+四子界面+公共壳六件)
|
||||
var camp_stone := 0
|
||||
var camp_stone_deep := 0
|
||||
for vf in ["camp_scene_view", "forge_view", "treasure_view", "tree_view", "codex_view", "camp_sub_view_base"]:
|
||||
var vf_src := FileAccess.get_file_as_string("res://presentation/views/%s.gd" % vf)
|
||||
camp_stone += vf_src.count("UiKit.STONE")
|
||||
camp_stone_deep += vf_src.count("UiKit.STONE_DEEP")
|
||||
if camp_stone != camp_stone_deep:
|
||||
problems.append("[camp] 营地域视图存在裸 UiKit.STONE(非 _DEEP 后缀——wc4 源扫描)〔句柄子容器批 V1〕")
|
||||
# ---- 营地场景层序段(卡④ V1①——子节点序=layout 表 z 序逐层相等、各层纹理矩形与表逐值一致;
|
||||
# 锻炉可达(V2 场景点选路由)+顶栏位在座(V8 结构)+根态反参照(V9——根态零文字按钮))----
|
||||
var layout: CampSceneLayoutTable = main.db.presentation.camp_scene_layout
|
||||
var order_ok := true
|
||||
var rect_ok := true
|
||||
for i in layout.layers.size():
|
||||
var layer_node: Control = main.ui.modals.camp.scene.get_child(i)
|
||||
if String(layer_node.name) != "Layer_%s" % layout.layers[i].key:
|
||||
order_ok = false
|
||||
if layer_node.position != layout.layers[i].texture_rect.position or layer_node.size != layout.layers[i].texture_rect.size:
|
||||
rect_ok = false
|
||||
if not order_ok:
|
||||
problems.append("[camp] 场景子节点序≠layout 表 z 序(逐层相等失守)〔营地图批 V1〕")
|
||||
if not rect_ok:
|
||||
problems.append("[camp] 层贴图矩形≠layout 表(逐值一致失守)〔营地图批 V1〕")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"forge")
|
||||
_settle()
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
if not main.ui.modals.camp.forge.visible:
|
||||
problems.append("[camp] 点选锻炉→锻造子界面未开(场景点选路由中断)〔营地图批 V2〕")
|
||||
var hud_slot: Control = main.ui.modals.camp.scene.get_node_or_null("HudBandSlot")
|
||||
if hud_slot == null or hud_slot.custom_minimum_size.y != 48:
|
||||
problems.append("[camp] 顶栏 HUD 预留位缺失或高≠48(浮层覆盖不挤压画面)〔营地图批 V8〕")
|
||||
var scene_buttons := 0
|
||||
for n in main.ui.modals.camp.scene.get_children():
|
||||
if n is Button:
|
||||
scene_buttons += 1
|
||||
if scene_buttons != 0:
|
||||
problems.append("[camp] 场景根态存在文字按钮(反参照纪律——禁文字按钮列表形态)〔营地图批 V9〕")
|
||||
main.ui.modals.camp.forge.closed.emit()
|
||||
_settle()
|
||||
main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3)
|
||||
_settle()
|
||||
|
||||
# ---- 生涯页:入口点击链(合成点击真路径=pressed.emit→组合根 _open_career)→
|
||||
# ---- 生涯页:入口=帐篷旁笔记本(卡④ T3 改挂——场景点选信号真实路由→组合根 _open_career)→
|
||||
# 结构断言族→只读巡检(契约 3)+零动效契约+零跳转契约(提纲 P4 甲)→组合根真实路径关闭。
|
||||
#(登记序/树序契约统一在全模态 _modal_registry_contract——boot 段跑)----
|
||||
# 生涯页驱动=函数直调中间态(卡② mn9/mn10——careerbtn 已撤,卡④ 营地笔记本改挂前无 UI 路径)
|
||||
main._open_career()
|
||||
# 营地语境:开始层全程隐藏(笔记本入口在营地场景根——关生涯回营地不回开始层,卡④ T3 语境分支)
|
||||
main._camp_pres.open_camp() # 重开营地场景根(笔记本入口语境)
|
||||
_settle()
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"notebook")
|
||||
_settle()
|
||||
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 生涯页未打开或开始层未隐藏(函数直调中间态断裂)")
|
||||
problems.append("[career] 生涯页未打开或开始层未隐藏(笔记本改挂路径断裂)〔营地图批 V5〕")
|
||||
await _audit("career", [main.ui.modals.career_overlay])
|
||||
_career_readonly_contract(main)
|
||||
_career_zero_motion_contract(main)
|
||||
_career_nojump_contract(main)
|
||||
main._on_career_closed()
|
||||
_settle()
|
||||
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
if main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层回弹(营地语境分支失守)〔营地图批 V5〕")
|
||||
# ---- Esc 浮窗结构段(卡⑤——三钮在位且仅三+零动效契约+登记序在册+debug_badge 不波及)----
|
||||
main._camp_pres.open_camp()
|
||||
_settle()
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
|
||||
_settle()
|
||||
main.ui.modals.confirm_dialog.cancelled.emit() # 关大门弹窗(清场——浮窗段前提)
|
||||
_settle()
|
||||
var push_esc := func() -> void:
|
||||
var ev := InputEventKey.new()
|
||||
ev.keycode = KEY_ESCAPE
|
||||
ev.physical_keycode = KEY_ESCAPE
|
||||
ev.pressed = true
|
||||
main.get_viewport().push_input(ev, true)
|
||||
push_esc.call()
|
||||
_settle()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
problems.append("[esc] 浮窗未唤起(结构段前提——营地语境 Esc 路由)〔ESC批 V1〕")
|
||||
var esc_btns := _buttons(main.ui.modals.camp.esc_overlay).size()
|
||||
if esc_btns != 3:
|
||||
problems.append("[esc] 浮窗三钮在位且仅三(实得 %d——禁第四钮)〔ESC批 V2〕" % esc_btns)
|
||||
var esc_static := FileAccess.get_file_as_string("res://presentation/views/camp_esc_overlay.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if esc_static.contains("create_tween") or esc_static.contains("UiKit.motion"):
|
||||
problems.append("[esc] 浮窗双件源码存在动效令牌(契约 6 零动效——直开直关)〔ESC批 V7〕")
|
||||
var esc_anim := _count_animplayers(main.ui.modals.camp.esc_overlay)
|
||||
if esc_anim != 0:
|
||||
problems.append("[esc] 浮窗子树 AnimationPlayer 非零(契约 6 零动效)〔ESC批 V7〕")
|
||||
var esc_src := FileAccess.get_file_as_string("res://presentation/views/camp_esc_overlay.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if esc_src.is_empty():
|
||||
problems.append("[esc] 零动效扫描面为空(源文件读失败——非空前置失守)〔ESC批 V7〕")
|
||||
# 退出项接线断言(卡⑤ V5——绑信号连接与调用链;headless 不可真退=静态判据+观测面局限申报)
|
||||
var quit_wired := false
|
||||
for c in main.ui.modals.camp.esc_overlay.quit_requested.get_connections():
|
||||
if c.callable.get_method() == "_on_esc_quit":
|
||||
quit_wired = true
|
||||
if not quit_wired:
|
||||
problems.append("[esc] 退出钮接线断链(quit_requested 连接目标≠_on_esc_quit)〔ESC批 V5〕")
|
||||
var quit_chain := FileAccess.get_file_as_string("res://presentation/main.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if not quit_chain.contains("MainEscFlow._on_quit(self)") or not quit_chain.contains("root._quit_game()"):
|
||||
problems.append("[esc] 退出调用链断裂(_on_esc_quit→_on_quit→_quit_game)〔ESC批 V5〕")
|
||||
if main.ui.screens.debug_badge.visible:
|
||||
problems.append("[esc] 浮窗开态 debug_badge 可见(发布态恒隐——float 类不注册 ModalStack 应零波及)〔ESC批 V8〕")
|
||||
push_esc.call()
|
||||
_settle()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
problems.append("[esc] 再按 Esc 未关浮窗(结构段收尾——Q2 单义切换)〔ESC批 V1〕")
|
||||
main._on_camp_closed() # 关营地复归菜单语境(后续选档段前提)
|
||||
_settle()
|
||||
|
||||
# ---- 选档界面(迭代 33 卡③):函数直调开层(「进入存档」钮归卡② mn3——点击链在卡② 承接),
|
||||
# sl1 三槽呈现/sl2 摘要三项逐槽读数/sl3 摘要禁项/sl4 操作集二分/sl9 重启链不预选。
|
||||
@@ -394,7 +476,7 @@ func _career_zero_motion_contract(main: Node) -> void:
|
||||
# ⑫模态登记/树序全量合同(V1——career 专属登记序契约推广到全部模态+屏壳;
|
||||
# 判据=main.ui.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。
|
||||
# 两特例按合同数据表达:level_screen 宿住冒险屏壳(树序随宿主=全模态之下,遮挡语义归模态栈
|
||||
# screen_state 登记)、camp_overlay 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。
|
||||
# screen_state 登记)、camp_scene 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。
|
||||
# _career_registry_contract 的断言(career=状态面板后/确认弹窗前、树序同步)由本全量合同覆盖)
|
||||
# ⑭选档界面契约(卡③ sl1/sl2/sl3/sl4/sl9——呈现面全部来自喂入数据,视图零自判)
|
||||
func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
@@ -516,7 +598,7 @@ func _modal_registry_contract(main: Node) -> void:
|
||||
elif lv.get_parent().get_index() >= main.ui.modals.legend_overlay.get_index():
|
||||
problems.append("[registry] 冒险屏壳应在全模态之下(宿主 idx=%d legend idx=%d)〔装配生命批 V1〕" % [
|
||||
lv.get_parent().get_index(), main.ui.modals.legend_overlay.get_index()])
|
||||
# ④ 营地同槽特例:camp_screen 屏壳树序夹商店层与设置层之间(成员 camp_overlay 登记位代言)
|
||||
# ④ 营地同槽特例:camp_screen 屏壳树序夹商店层与设置层之间(成员 camp_scene 登记位代言)
|
||||
var cs: Node = main.ui.screens.camp_screen
|
||||
if cs.get_index() <= main.ui.modals.shop_overlay.get_index() or cs.get_index() >= main.ui.modals.settings_overlay.get_index():
|
||||
problems.append("[registry] camp_screen 屏壳树序应夹商店与设置层之间(idx=%d shop=%d settings=%d)〔装配生命批 V1〕" % [
|
||||
@@ -719,3 +801,13 @@ func _start_columns_contract(main: Node) -> void:
|
||||
if box.get_global_rect().intersects(right.get_global_rect()):
|
||||
problems.append("[start][columns] 左列 %s 与右列 %s 相交(双列叠放回归——M2 缺陷闸)" % [
|
||||
box.get_global_rect(), right.get_global_rect()])
|
||||
|
||||
|
||||
# AnimationPlayer 全子树收集(零动效契约扫描面——_buttons 同款递归形态)
|
||||
func _count_animplayers(node: Node) -> int:
|
||||
var out := 0
|
||||
if node is AnimationPlayer:
|
||||
out += 1
|
||||
for child in node.get_children():
|
||||
out += _count_animplayers(child)
|
||||
return out
|
||||
+13
-2
@@ -134,11 +134,21 @@ tests/
|
||||
| 地形瓦片套与格面数字可读 | HUD框架 | `TilePalette.digit_ink_mode()`/`TilePalette.normalize_floor_luma()` | `terrain_tile_sets_test.gd:_test_set_completeness`;`terrain_tile_sets_test.gd:_test_hue_domains`;`terrain_tile_sets_test.gd:_test_digit_ink_modes` |
|
||||
| 站桩敌人呈现与结算演出落点 | 战斗表现 | `BattleStageView.enemy_standee_center()`/`BattleEndOverlay.open_victory()` | `standee_fx_sweep_test.gd:_init`;`pacing_queue_test.gd:_init` |
|
||||
| 站桩呈现定值与素材绑定 | 战斗表现 | `UiKit.load_tex()`/`StandeeBody.name_line_probe()` | `standee_present_values_test.gd:_init` |
|
||||
| 站桩显示档与素材分辨率绑定 | 战斗表现 | `StandeeBody.portrait_probe()`/`StandeeMotion.PORTRAIT_DISPLAY_H` | `standee_present_values_test.gd:_check_display_tier` |
|
||||
| B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) | HUD框架 | `BattleStageView.set_board_type()` | `stage_background_test.gd:_test_background_selection`;`stage_background_test.gd:_test_ground_layer_geometry`;`stage_background_test.gd:_test_beams_retired` |
|
||||
| 战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) | HUD框架 | `BoardView.set_cell_area()`(D 区可用面积 → 格边长);读面 `BoardView.cell_size` | `layout_probe.gd:_ready`;`board_gen_reward_test.gd:_smoke_layout_cell_formula` |
|
||||
| 调参测速批量模拟(逐帧步进与统计读数) | 调参测量 | `TuneSimController.run()`(调试浮窗批量模拟入口) | `tune_sim_batch_parity_test.gd:_init`(配对对拍套件,随测速循环批建) |
|
||||
| 战斗叙述与终局文案(数值带入/胜负互异) | HUD框架 | `BattleNarrator.battle_end_text()`/`BattleNarrator.boss_name()` | `battle_narrator_test.gd:_test_numeric_lines_carry_values`;`battle_narrator_test.gd:_test_end_text_differs_by_result`;`battle_narrator_test.gd:_test_boss_name_uses_boss_rowset` |
|
||||
| 玩家面文案与危险格格面字形 | HUD框架 | `BoardView.rebuild()`/`StatusSlotRow.set_effects()` | `player_copy_sweep_test.gd:_sweep_board_glyph`;`player_copy_sweep_test.gd:_sweep_legend`;`player_copy_sweep_test.gd:_sweep_hero_panel` |
|
||||
| 营地场景图十层铺底与门洞留空呈现 | 营地场景视图 | 进入存档→选档→进档→营地场景屏(`CampSceneView.setup_layers()` 驱动);单元测试载入 `camp_scene_layout.tres` | `shot_probe.gd:_ready`(camp 截图段)〔营地图批 V1〕;`camp_scene_layout_test.gd:_test_layout_hotspot_geometry`〔营地图批 V14〕 |
|
||||
| 营地七物件命中区点选与分界面可达 | 营地场景视图 | 七物件命中区 `_gui_input()` → `CampSceneView.object_pressed()` → 组合根开界面 | `ui_click_probe.gd:_ready`(七物件合成点击段)〔营地图批 V2〕 |
|
||||
| 营地大门两分支确认与断点续玩接线 | 营地交互流 | 大门命中区 → ConfirmDialog(三出口)→ `main._on_confirm_new_adventure()`/`_resume_from_menu()` | `ui_click_probe.gd:_ready`(大门两分支段/断点确认段 :500-528)〔营地图批 V3〕 |
|
||||
| 四柜台拆解后五界面入口改挂与写口零回归 | 营地五界面 | 五界面意图信号 → `CampShopPresenter` → `MetaProgression.tree_buy()` 写口 | `forge_workbench_test.gd:_test_forge_rows`〔营地图批 V4〕;`ui_click_probe.gd:_ready`(写口链段) |
|
||||
| 营地篝火回主菜单与笔记本改挂动线 | 营地交互流 | 笔记本命中区 → `CareerOverlay.open()`;篝火命中区 → ConfirmDialog 二次确认 → 主菜单 | `ui_struct_probe.gd:_ready`(career 段)〔营地图批 V5〕;`screen_flow_probe.gd:_ready`(篝火/开合段)〔营地图批 V7〕 |
|
||||
| 营地图鉴占位挂起态与反参照结构纪律 | 营地场景视图 | 布告板命中区 → `CampSceneView.object_pressed()` → 图鉴占位视图;营地根态场景树审计 | `ui_struct_probe.gd:_ready`(占位禁词/禁行段)〔营地图批 V6〕;`ui_struct_probe.gd:_ready`(结构禁词段)〔营地图批 V9〕 |
|
||||
| 营地刷新单口与场景根 HUD 同步 | 营地刷新总线 | 写口尾 → `CampRefreshBus.refresh()` → 现开界面刷新+场景根 HUD 刷新 | `ui_struct_probe.gd:_ready`(顶栏位段)〔营地图批 V8〕;`ui_click_probe.gd:_ready`(写口尾刷新段) |
|
||||
| Esc 浮窗唤起与三出口动线 | 营地 Esc 流 | 营地场景根态合成 Esc 键事件→ `main_esc_flow.on_esc_input()` → `ui.modals.flow.open/close`;浮窗三钮 `pressed` → 三出口 | `screen_flow_probe.gd:_ready`(Esc 唤起/子界面开态不唤起/再按关窗/非营地不唤起段)〔ESC批 V1〕;`screen_flow_probe.gd:_ready`(返回动线/设置双路径段)〔ESC批 V3/V4〕 |
|
||||
| 营地顶栏 HUD 钥石读数与刷新单口同步 | 营地 HUD 栏 | 隔离存档实例 `meta_state._add_keystone()`→ 显式触发 `CampRefreshBus.refresh(camp_data)` → `camp_hud_band.refresh()` → `keystone_badge` 读数 | `screen_flow_probe.gd:_ready`(HUD 余额读数/刷新段)〔ESC批 V6〕;`ui_struct_probe.gd:_ready`(顶栏位结构段) |
|
||||
|
||||
**旅程段七:数据配置与跨系统契约**
|
||||
|
||||
@@ -153,6 +163,7 @@ tests/
|
||||
| 难度修饰套乘:空表零行为/套乘生效/越域 clamp | 难度曲线与Boss | `UnitContainer.spawn()`/`ConfigDbResolver.resolve_difficulty_modifiers()` | `table_structure_guard_test.gd:_smoke_difficulty_table`;`config_db_resolver_test.gd:_test_difficulty_clamp` |
|
||||
| 锻造表 schema 正反与几何价格梯 | 锻造工坊 | `ForgeTable.level_price()`/`ForgeTable.route_rows()` | `forge_table_test.gd:_test_production_rows`;`forge_table_test.gd:_test_price_ladder`;`forge_table_test.gd:_test_negative_cases` |
|
||||
| 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理 | 战斗结算×数据配置×增益状态 | `DamageController.open_ticket()`/`StatusContainer.apply_effect()` | `system_contracts_test.gd:_test_damage_controller_vocabularies`;`system_contracts_test.gd:_test_rng_tag_closure`;`system_contracts_test.gd:_test_status_boolean_acceptance` |
|
||||
| 同名状态合并策略(叠加/累加/并存)与剩余次数语义 | 战斗循环×增益状态 | `StatusContainer.apply_effect()`(编排宿主 begin_battle 链) | `roll_multiplier_test.gd:_smoke_armor_roll` |
|
||||
| 敌人技能池跨表链接校验 | 数据配置×敌人系统 | `ConfigDbCrossValidator.validate_skill_pool_links()` | `config_db_cross_validator_test.gd:_test_all_links_valid`;`config_db_cross_validator_test.gd:_test_dangling_skill_id`;`config_db_cross_validator_test.gd:_test_cast_side_mismatch` |
|
||||
|
||||
**覆盖域表**(`design/gdd/systems/` 逐篇对照——状态三档:已覆盖/部分覆盖/未覆盖;部分与未覆盖行的缺口指待办清单条目,覆盖面提升时同批改本表):
|
||||
@@ -165,9 +176,9 @@ tests/
|
||||
| 冒险循环 | 奇物.md | 专长(奇物)授予与凭证保底;奖励三选一候选/刷新付费/呈现 | 已覆盖 | — |
|
||||
| 冒险循环 | 难度曲线与Boss.md | Boss 半血解锁技门控;行集寻址、行序保序与难度乘数 clamp;难度修饰套乘:空表零行为/套乘生效/越域 clamp | 部分覆盖 | 难度曲线整链与 Boss 出场门控无专项断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 地图战斗资源.md | 清图奖励双份与零路径;GP 钱包/escrow/商店收口/共享池;封印与极性路由/不可逆 | 已覆盖 | — |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;同名状态合并策略(叠加/累加/并存)与剩余次数语义;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗结算.md | 伤害公式与掷点乘区;伤害单据编排与回放;轮内流与结算五段全序 | 已覆盖 | — |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定;站桩显示档与素材分辨率绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 敌人系统.md | 技能池数据面与回放独立;Boss 半血解锁技门控;敌人技能池跨表链接校验 | 已覆盖 | — |
|
||||
| 排雷循环 | 地形变异.md | 变异布置门控;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
|
||||
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
|
||||
|
||||
@@ -192,6 +192,7 @@ func _suites() -> Array:
|
||||
{"path": "res://tests/unit/presentation/reward_overlay_test.gd", "expect": 0}, # 奖励节点 UI 直验(盲区补漏)
|
||||
{"path": "res://tests/unit/presentation/level_map_info_fog_test.gd", "expect": 0}, # 信息迷雾——呈现显隐/Boss 豁免/详情零显示/大图拟合
|
||||
{"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面)
|
||||
{"path": "res://tests/unit/presentation/camp_scene_layout_test.gd", "expect": 0}, # 营地场景布局数据件校验(V14——fail-fast 判据+七命中区几何红线)
|
||||
{"path": "res://tests/unit/battle_settlement/damage_math_test.gd", "expect": 0},
|
||||
{"path": "res://tests/unit/battle_settlement/damage_controller_test.gd", "expect": 0},
|
||||
{"path": "res://tests/unit/battle_settlement/roll_multiplier_test.gd", "expect": 0}, # 掷点与乘区宿主(敌攻击类型轴/护甲掷点行为面/乘区接线/伤害类型克制与标错/fresh 池攻击链——批 B 自 smoke_all 迁入)
|
||||
|
||||
@@ -64,8 +64,8 @@ func _smoke_scene_split() -> void:
|
||||
var camp: Node = load("res://presentation/scenes/camp_screen.tscn").instantiate()
|
||||
root.add_child(camp)
|
||||
await process_frame
|
||||
check(camp.camp_overlay != null, "V2 营地屏壳独立实例化(camp_overlay 在位)")
|
||||
check(not camp.camp_overlay.visible, "V2 营地层默认隐藏(模态纪律)")
|
||||
check(camp.camp_scene != null, "V2 营地屏壳独立实例化(camp_scene 在位)")
|
||||
check(not camp.camp_scene.visible, "V2 营地层默认隐藏(模态纪律)")
|
||||
camp.queue_free()
|
||||
var adv: Node = load("res://presentation/scenes/adventure_screen.tscn").instantiate()
|
||||
root.add_child(adv)
|
||||
@@ -103,10 +103,10 @@ func _smoke_scene_split() -> void:
|
||||
chain_ok = false
|
||||
chain_detail += " %s(%d)≤%s(%d)" % [chain_prev.name, chain_prev.get_index(), node.name, node.get_index()]
|
||||
chain_prev = node
|
||||
check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" % chain_detail)
|
||||
check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕〔句柄子容器批 V2〕" % chain_detail)
|
||||
# ③ 探针挂载假设句柄在座(main.X 一级路径——探针迁移面收口证明)
|
||||
check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp_overlay == main.ui.screens.camp_screen.camp_overlay and main.ui.modals.level_screen == main.ui.screens.adventure_screen.level_screen and main.ui.screens.battle_root == main.ui.screens.adventure_screen.battle_root and main.ui.battle.board_zone.board_view == main.ui.screens.adventure_screen.board_view,
|
||||
"V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)")
|
||||
check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp.scene == main.ui.screens.camp_screen.camp_scene and main.ui.modals.level_screen == main.ui.screens.adventure_screen.level_screen and main.ui.screens.battle_root == main.ui.screens.adventure_screen.battle_root and main.ui.battle.board_zone.board_view == main.ui.screens.adventure_screen.board_view,
|
||||
"V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)〔句柄子容器批 V1〕")
|
||||
check(main.run_scope != null and not main.run_scope.is_adventure,
|
||||
"V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)")
|
||||
main.queue_free()
|
||||
@@ -118,7 +118,7 @@ func _smoke_scene_split() -> void:
|
||||
func _topmost_visible_layer(main: Node) -> Node:
|
||||
var candidates: Array[Node] = [main.ui.screens.start_overlay, main.ui.screens.battle_root, main.ui.screens.inter_overlay,
|
||||
main.ui.modals.legend_overlay, main.ui.modals.node_overlay, main.ui.modals.level_screen, main.ui.modals.curio_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp.scene, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.status_panel, main.ui.modals.career_overlay, main.ui.modals.confirm_dialog, main.ui.modals.debug_overlay]
|
||||
var best: Node = null
|
||||
var best_x := -1
|
||||
|
||||
@@ -51,21 +51,29 @@ func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator:
|
||||
# 公式 N×cell ≤ 可用高−12 必须计入 (N−1)×2px 网格间距,否则网格实高溢出被 clip_contents 裁掉;
|
||||
# 现公式 = 计入间距 + 垂直预留 24px〔上 12/下 12——底部不贴 D 区边框〕)----
|
||||
func _smoke_layout_cell_formula() -> void:
|
||||
print("--- 棋盘布局格边长公式(底部预留回归)---")
|
||||
print("--- 棋盘布局格边长公式(底部预留回归·四档板型)---")
|
||||
var board_generator = load("res://core/board_generator.gd").new()
|
||||
var board_view = load("res://presentation/views/board_view.gd").new()
|
||||
for L in [1, 2]:
|
||||
var b = board_generator.generate(L, 0, _mk_stream("board_gen", 555), _db)
|
||||
var board_view_script := load("res://presentation/views/board_view.gd")
|
||||
# 四档取样表(board_gen.tres 实表取档——(L,D)→尺寸:10/12/13/14;13×13 与 14×14 首次入断言)
|
||||
var tiers := [{"L": 1, "D": 0, "n": 10}, {"L": 2, "D": 0, "n": 12}, {"L": 3, "D": 0, "n": 13}, {"L": 3, "D": 9, "n": 14}]
|
||||
for t_v in tiers:
|
||||
var t: Dictionary = t_v
|
||||
var b = board_generator.generate(int(t["L"]), int(t["D"]), _mk_stream("board_gen", 555), _db)
|
||||
board_view.rebuild(b, null)
|
||||
var avail := Vector2(1036, 460) # 受约束 D 区可用面积样本(1280×800 档量级——用户实测裁切场景)
|
||||
board_view.set_cell_area(avail)
|
||||
var n: int = maxi(b.width, b.height)
|
||||
var cell: float = board_view.get("_cell")
|
||||
var grid_h := n * int(cell) + (n - 1) * 2
|
||||
Kit.check("L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" % [L, b.width, b.height, grid_h, int(avail.y), int(cell)],
|
||||
Kit.check("L%d D%d 板型一致(%dx%d = 取样 %d——表改动致取样落错档即红)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, int(t["n"])],
|
||||
n == int(t["n"]))
|
||||
Kit.check("L%d D%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), b.width, b.height, grid_h, int(avail.y), int(cell)],
|
||||
grid_h <= int(avail.y) - 24)
|
||||
Kit.check("L%d 格边在 26–46 档内(%dpx)" % [L, int(cell)],
|
||||
Kit.check("L%d D%d 格边在 26–46 档内(%dpx)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), int(cell)],
|
||||
cell >= 26.0 and cell <= 46.0)
|
||||
Kit.check("L%d D%d 算式单点等价(set_cell_area 写入 %d == _cell_for 返回 %d——同一事实两路读取)〔格边长 V2〕" % [int(t["L"]), int(t["D"]), int(cell), board_view_script._cell_for(n, avail)],
|
||||
int(cell) == board_view_script._cell_for(n, avail))
|
||||
board_view.free()
|
||||
|
||||
|
||||
|
||||
@@ -228,16 +228,14 @@ func _smoke_enemy_bands() -> void:
|
||||
int(row.get("depth_band", -1)), String(row.get("enemy_id", ""))])
|
||||
Kit.check("敌人表 28 行且键序 = 覆盖表序((L,row_kind,depth_band,enemy_id) 逐行比对——文件行序不变)〔fv11〕",
|
||||
_db.combat.enemy.rows.size() == 28 and got_keys == expect_keys)
|
||||
# ② hobgoblin 缺口行留任随迁(契约 9:不删行不补立绘——缺口登记随行)
|
||||
# ② hobgoblin 缺口行留任随迁(不删行不补立绘——补齐归敌种内容批)
|
||||
var hob := {}
|
||||
for row in _db.combat.enemy.rows:
|
||||
if String(row.get("enemy_id", "")) == "hobgoblin":
|
||||
hob = row
|
||||
Kit.check("hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕〔fv25〕",
|
||||
Kit.check("hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕",
|
||||
int(hob.get("level_index", -1)) == 1 and String(hob.get("row_kind", "")) == "battle" \
|
||||
and int(hob.get("depth_band", -1)) == 3)
|
||||
Kit.check("hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕",
|
||||
FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行"))
|
||||
# ③ Boss 行集带键 = 覆盖表(旧判据「各恰一行=图 Boss 节点深度」属单终点模板时代;
|
||||
# 生成图多终点要求每终点深度解析到互异带键 ⇒ 每关 Boss 行必然多于一行——
|
||||
# 迭代 35 覆盖批随动改写为带键清单,「带键互异」的运行时把关在 level_shop_economy_test〔v11〕)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
extends SceneTree
|
||||
# 营地场景布局数据件校验套件(V14——camp_scene_layout.tres 加载期 fail-fast 判据+七命中区几何红线机检)
|
||||
# 覆盖:校验函数通过(键唯一/贴图在画布/命中成对/围墙带包络)· 七可点件键集 · 两两不相交 ·
|
||||
# 不越画面 · 不侵顶栏禁区 · 篝火命中只取下半(关系谓词)· 门扇对齐门洞缺口 · 布告板挂围墙带。
|
||||
# 断言全部为表内关系谓词——禁坐标字面量(数值单一事实源 = 数据行本表)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/camp_scene_layout_test.gd(expect 0)
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
|
||||
const _EXPECTED_KEYS: Array[StringName] = [&"gate", &"forge", &"dummy", &"chest", &"notebook", &"board", &"campfire"]
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_test_layout_hotspot_geometry")
|
||||
|
||||
|
||||
func _test_layout_hotspot_geometry() -> void:
|
||||
var table: CampSceneLayoutTable = load("res://data/tables/camp_scene_layout.tres")
|
||||
Kit.check("V14 布局表加载且 schema 类在位〔营地图批 V14〕", table is CampSceneLayoutTable and table != null)
|
||||
if table == null:
|
||||
quit(Kit.finish("camp_scene_layout_test"))
|
||||
return
|
||||
var problems: Array[String] = table.validate()
|
||||
Kit.check("V14 校验函数通过(问题清单空,实得 %s)〔营地图批 V14〕" % [problems], problems.is_empty())
|
||||
var clickables: Array[CampSceneLayerSpec] = []
|
||||
for spec in table.layers:
|
||||
if spec.is_clickable():
|
||||
clickables.append(spec)
|
||||
Kit.check("V14 可点件数=7(实得 %d)〔营地图批 V14〕" % clickables.size(), clickables.size() == 7)
|
||||
var got_keys: Array[StringName] = []
|
||||
for spec in clickables:
|
||||
got_keys.append(spec.hotspot_key)
|
||||
got_keys.sort()
|
||||
var want_keys: Array[StringName] = []
|
||||
for k in _EXPECTED_KEYS:
|
||||
want_keys.append(k)
|
||||
want_keys.sort()
|
||||
Kit.check("V14 命中键集=七物件绑定表(实得 %s)〔营地图批 V14〕" % [got_keys], got_keys == want_keys)
|
||||
# 两两不相交(Rect2.intersects——贴边相触不算相交)
|
||||
var disjoint := true
|
||||
var pair := ""
|
||||
for i in clickables.size():
|
||||
for j in range(i + 1, clickables.size()):
|
||||
if clickables[i].hotspot.intersects(clickables[j].hotspot):
|
||||
disjoint = false
|
||||
pair = "%s×%s" % [clickables[i].key, clickables[j].key]
|
||||
Kit.check("V14 七命中区两两不相交(首违例 %s)〔营地图批 V14〕" % pair, disjoint)
|
||||
# 不越画面+不侵顶栏禁区
|
||||
var in_bounds := true
|
||||
var offender := ""
|
||||
for spec in clickables:
|
||||
if spec.hotspot.position.x < 0.0 or spec.hotspot.position.y < table.hud_band_h \
|
||||
or spec.hotspot.end.x > table.canvas.x or spec.hotspot.end.y > table.canvas.y:
|
||||
in_bounds = false
|
||||
offender = String(spec.key)
|
||||
Kit.check("V14 命中区不越画面且不侵顶栏禁区(y≥%d,违例 %s)〔营地图批 V14〕" % [table.hud_band_h, offender], in_bounds)
|
||||
# 篝火命中只取下半:命中区贴贴图底、严格小于贴图、起点在贴图内(火焰上段不可点)
|
||||
var fire: CampSceneLayerSpec = null
|
||||
var gate: CampSceneLayerSpec = null
|
||||
var board: CampSceneLayerSpec = null
|
||||
var wall: CampSceneLayerSpec = null
|
||||
for spec in table.layers:
|
||||
match String(spec.key):
|
||||
"campfire": fire = spec
|
||||
"gate": gate = spec
|
||||
"board": board = spec
|
||||
"wall": wall = spec
|
||||
var fire_ok := fire != null and fire.hotspot.end.y == fire.texture_rect.end.y \
|
||||
and fire.hotspot.size.y < fire.texture_rect.size.y \
|
||||
and fire.hotspot.position.y > fire.texture_rect.position.y
|
||||
Kit.check("V14 篝火命中只取下半(火堆贴底、火焰不点)〔营地图批 V14〕", fire_ok)
|
||||
# 门扇命中对齐门洞缺口(表内 gate_gap 字段关系谓词)
|
||||
var gate_ok := gate != null and gate.hotspot.position.x == float(table.gate_gap.x) \
|
||||
and gate.hotspot.end.x == float(table.gate_gap.y)
|
||||
Kit.check("V14 门扇命中对齐门洞缺口 %s〔营地图批 V14〕" % [table.gate_gap], gate_ok)
|
||||
# 布告板挂围墙带:命中顶落在围墙贴图带内
|
||||
var board_ok := board != null and wall != null \
|
||||
and board.hotspot.position.y >= wall.texture_rect.position.y \
|
||||
and board.hotspot.position.y <= wall.texture_rect.end.y
|
||||
Kit.check("V14 布告板命中挂围墙带〔营地图批 V14〕", board_ok)
|
||||
# 层序结构:10 层且 bg/wall 压前两位(z0/z1——子节点序=表序先验)
|
||||
var order_ok := table.layers.size() == 10 \
|
||||
and String(table.layers[0].key) == "bg" and String(table.layers[1].key) == "wall"
|
||||
Kit.check("V14 层数=10 且 bg/wall 占 z0/z1〔营地图批 V14〕", order_ok)
|
||||
# ConfigDB 装载链:表经单点注册进表现域容器且装载期校验过(fail-fast 钩子在位)
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
Kit.check("V14 ConfigDB 装载含营地布局表且校验过(is_ready=%s)〔营地图批 V14〕" % db.is_ready,
|
||||
load_ok and db.is_ready and db.presentation.camp_scene_layout != null)
|
||||
quit(Kit.finish("camp_scene_layout_test"))
|
||||
@@ -0,0 +1 @@
|
||||
uid://ckyd57l11408
|
||||
@@ -15,7 +15,7 @@ func _initialize() -> void:
|
||||
|
||||
func _row_by_id(ov) -> Dictionary:
|
||||
var out := {}
|
||||
for r in ov._forge._forge_rows:
|
||||
for r in ov._forge_rows:
|
||||
out[String(r.route_id)] = r
|
||||
return out
|
||||
|
||||
@@ -30,7 +30,7 @@ func _run() -> void:
|
||||
var sv = load("res://core/save_manager.gd").new(_TEST_SAVE)
|
||||
sv.load_from_disk()
|
||||
var mp = load("res://core/meta_progression.gd").new(db, sv)
|
||||
var ov = load("res://presentation/views/camp_overlay_view.gd").new()
|
||||
var ov = load("res://presentation/views/forge_view.gd").new()
|
||||
root.add_child(ov)
|
||||
var buys := []
|
||||
var unlocks := []
|
||||
@@ -42,16 +42,16 @@ func _run() -> void:
|
||||
# ① 锁定态:树钥匙未点亮 → 工作间不可见、两区零行板(无任何可交互购买路径)
|
||||
ov.open_camp({"balance": 999, "forge": mp.forge_shop_status(), "tree": []})
|
||||
Kit.check("锁定态:工作间不可见(forge_workshop 钥匙未点亮)〔锻造工坊批 V13〕",
|
||||
not ov._forge._forge_workbench.visible and ov._forge._forge_lock_l.visible)
|
||||
not ov._forge_workbench.visible and ov._forge_lock_l.visible)
|
||||
Kit.check("锁定态:两区零行板(占位纪律——零购买路径)〔锻造工坊批 V13〕",
|
||||
ov._forge._forge_zone_weapon.get_child_count() == 0 and ov._forge._forge_zone_armor.get_child_count() == 0)
|
||||
ov._forge_zone_weapon.get_child_count() == 0 and ov._forge_zone_armor.get_child_count() == 0)
|
||||
|
||||
# ② 解锁态:钥匙点亮 → 两区各三路线
|
||||
ov.open_camp({"balance": 500, "forge": mp.forge_shop_status(),
|
||||
"tree": [{"node_id": "forge_workshop", "level": 1}]})
|
||||
Kit.check("解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕", ov._forge._forge_workbench.visible)
|
||||
Kit.check("解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕", ov._forge_workbench.visible)
|
||||
Kit.check("两区各三路线(武器锻造区 3 + 护甲锻造区 3)〔锻造工坊批 V13〕",
|
||||
ov._forge._forge_zone_weapon.get_child_count() == 3 and ov._forge._forge_zone_armor.get_child_count() == 3)
|
||||
ov._forge_zone_weapon.get_child_count() == 3 and ov._forge_zone_armor.get_child_count() == 3)
|
||||
var rows: Dictionary = _row_by_id(ov)
|
||||
Kit.check("六路线行册恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V13〕",
|
||||
rows.has("sword") and rows.has("spear") and rows.has("staff")
|
||||
|
||||
@@ -52,7 +52,6 @@ func _init() -> void:
|
||||
_smoke_diag_button_retired()
|
||||
_status_panel_projection()
|
||||
_career_overlay_projection()
|
||||
_camp_recent_projection()
|
||||
quit(Kit.finish("theme_projection_test"))
|
||||
|
||||
|
||||
@@ -708,12 +707,6 @@ func _smoke_element_details() -> void:
|
||||
var shop_src: String = FileAccess.get_file_as_string("res://presentation/views/shop_overlay_view.gd")
|
||||
Kit.check("T5③ 商店卡片区垂直居中(ALIGNMENT_CENTER)",
|
||||
shop_src.contains("_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER"))
|
||||
var camp_src: String = FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd")
|
||||
var camp_min := camp_src.count("custom_minimum_size = Vector2(0, 336)")
|
||||
Kit.check("T5③ 营地三 holder 统一 min 高 336(实测 %d 处)" % camp_min, camp_min == 3)
|
||||
var camp_fill := camp_src.count("size_flags_vertical = Control.SIZE_FILL")
|
||||
Kit.check("T5③ 营地四栏面板纵向 FILL 等高(实测 %d 处)" % camp_fill, camp_fill == 4)
|
||||
Kit.check("T5③ 三处锁文垂直居中在位", camp_src.count("SIZE_SHRINK_CENTER") >= 3)
|
||||
|
||||
|
||||
# ---- 诊断转储钮冒烟(角落钮不在玩家画面+转储入口在调试浮窗;V6)----
|
||||
@@ -796,20 +789,6 @@ func _all_labels(root: Node) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# ---- 营地最近冒险行投影(口径换代 V6:完成关数措辞钉子——open_camp 全量重建路径直调;
|
||||
# 最小档 data=recent 单条 completed 局,其余键缺省走 .get 兜底——与组合根 _camp_data 投影同键面)----
|
||||
func _camp_recent_projection() -> void:
|
||||
print("--- 营地·最近冒险行投影(open_camp 最小档)---")
|
||||
var camp = load("res://presentation/views/camp_overlay_view.gd").new()
|
||||
camp.open_camp({"balance": 0, "recent": [
|
||||
{"result": "completed", "levels_cleared": 2, "keystone_total": 5, "master_seed": 42}]})
|
||||
var recent_text: String = String(camp._side._recent_list.text)
|
||||
Kit.check("营地最近冒险行完成关数措辞(实际「%s」)〔口径换代 V6〕" % recent_text,
|
||||
recent_text.contains("完成 2 关") and not recent_text.contains("通关"))
|
||||
camp.visible = false
|
||||
camp.free()
|
||||
|
||||
|
||||
# ---- 生涯页只读投影一致性(汇总段六类九项逐行 == SaveManager 计数访问器/
|
||||
# MetaProgression 访问器读数;投影拼装与 HudPresenter.career_data 同构(同访问器族同序)。
|
||||
# 只读性连接巡检与零动效断言归 ui_struct_probe 呈现侧,本段只管数据面+旧档载入显示
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# 迭代 46 GDD 反哺点清单——卡C(2026-11-01)
|
||||
|
||||
> 接收方:待办清单「反哺点专条」通道——用户会话逐条裁决后由文档批写入 `design/`,写入后本清单条目清掉(吸收即死)。
|
||||
> 卡A/卡B 反哺条见同目录 `iter46-反哺点-2026-10-31.md`;本文件只收卡C v2.0。
|
||||
|
||||
1. **[battle.md §棋盘格尺寸公式 :144]** 「可读下限 30px——与结构钳制分开:钳制管算式不越界,下限管玩家能否看清」整句**退役**:整句删除,或改写为「格边长可读性以用户验收的现行画面为基准;结构钳制 [26,46] 管算式不越界」(措辞由文档批裁)。依据=2026-11-01 用户三项裁定(锚点=用户验收的现行渲染值,基座档 14×14=28px 达标;30 无实测背书)+停机呈报读数链(`reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`)+实现报告 §四锚点留档(`iter46-实现报告-卡C-2026-11-01.md`)。随句建议同节载明判据现行形态:全域禁人造格边长可读阈值,接线守卫=生产写入值与单点算式严格相等,最大板推定读数留痕不作判红。
|
||||
|
||||
2. **作废声明**:`iter46-反哺点-2026-10-31.md` 第 8 条(battle.md 算例表改数——随「30px 下限适用域」读法甲/乙裁决落文)**作废**——`:148-152` 算例表已随布局几何单点化批(1cdef6ba)退役,无落点;读法甲/乙之争已被 2026-11-01 用户三项裁定取代(下限句退役,不再有适用域问题)。文档批落文时以本条为准,10-31 清单第 8 条不再执行。
|
||||
|
||||
## 术语表
|
||||
|
||||
零新增术语——本批新名 `_cell_for` 属代码标识符,不入术语表(登记口径=概念规范名)。
|
||||
@@ -0,0 +1,70 @@
|
||||
# 迭代 46 实现报告——卡C 棋盘格边长口径收口批 v2.0(2026-11-01)
|
||||
|
||||
> 批次:迭代 46 卡C(v2.0 重立回编——2026-11-01 用户三项裁定后写卡批重立,合并提交 e7090bc9)。卡A/卡B 已于 2026-10-31 收口合回,见同目录 `iter46-实现报告-2026-10-31.md`。
|
||||
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,卡C 段基点=master 5269ed40,WIP 三件按 v2 卡 §12 判定复用两件返工一件)。
|
||||
> 需求卡片:`reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md`(v2.0 定稿,零待裁点)。
|
||||
|
||||
## 一、卡C 状态
|
||||
|
||||
**收口**(T0–T4 全落)。游戏行为零变化:算式逐位同式、`set_cell_area` 副作用尾链零动、两条生产调用路径不改、画面像素零变化。本批改动全部落在守卫面=**人造阈值退役+算式单点+13/14 两档入卫**。
|
||||
|
||||
## 二、T0 改前基线读数(2026-11-01,master 5269ed40 态实测)
|
||||
|
||||
| 读数 | 结果 |
|
||||
|---|---|
|
||||
| `python tools/gdstyle_gate.py` | 扫描面 142 件、命中 5 条全在基线、基线外新违规 0、较登记值恶化 0(board_view.gd `max-public-methods=27`/`max-class-variables=31` 持平) |
|
||||
| 单件跑 `board_gen_reward_test` | all green(**62 checks**——改前基线值) |
|
||||
| 单件跑 `layout_probe` | 2 档 PASS(旧阈值形态「≥36px」消息,即本批退役对象) |
|
||||
| 四档取档对账(`resolve_row(1,0)/(2,0)/(3,0)/(3,9)`,临时对账件跑后即删) | 10×10/12×12/13×13/14×14,与卡 §2 第 6 条全符 |
|
||||
| `battle_layout.tres` 五字段 | stage_h=252/ap_bar_h=44/info_panel_w=240/log_band_h_dig=24/log_band_h_settle=84,与卡 §2 第 8 条全符 |
|
||||
|
||||
行号重锚:卡 §2 行号在 5269ed40 上逐一核实(`layout_probe.gd:15` MIN_CELL/`board_view.gd:206-215` 内联算式/`board_zone_view.gd:91-92` 生产调用路径+`board_view.rebuild` 末尾调用/`board_gen_reward_test.gd:53-69` 冒烟)——卡成文时点(3499638d)至 T0 时点间零漂移。`tools/gdstyle_baseline.txt` 两键登记值与卡 §2 第 5 条一致(:11=31、:22=27)。
|
||||
|
||||
## 三、实际对照表(改动—验收)
|
||||
|
||||
| # | 需求点 | 改动 | 验收测试(名称+实际落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 结果 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | 探针阈值退役+格边长接线守卫 | `layout_probe.gd` MIN_CELL 常量整行删除;阈值断言换等价断言(:106-109) | 「两档几何不变量+格边长等价」`layout_probe.gd:_ready`(PASS 行 :45 尾缀〔格边长 V1〕;失败路径 :107) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 实读 `cell_size`(生产经 `set_cell_area` 真实写入)与 `_cell_for(n, bv.size)` 同输入严格相等——生产停止调单点算式即红;`_board` 内部字段直查=受控例外(板型 N 无公开出口,卡 §14.2);`grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` 零命中(已跑,exit=1 零命中) | 绿(两档全过,基座实读 40px) |
|
||||
| 2 | 算式单点(`_cell_for`) | `board_view.gd:210` 改调 `static _cell_for`(:226,逐位同式);:206 注释补唯一落点句 | 「算式单点等价」`board_gen_reward_test.gd:75-76`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 绿(四档 4 断言全过;既有四套断言全绿=等价性证明,62→74 checks) |
|
||||
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:58` 循环集改四档取样表 [(1,0,10),(2,0,12),(3,0,13),(3,9,14)];:69-74 板型一致+既有两断言(前缀 L%d D%d) | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边 ∈ [26,46](既有断言逐字保留) | 绿(四档 12 断言全过,10/12/13/14 实测 41/33/31/28px) |
|
||||
| 4 | 最大板推定守卫(留痕型) | `layout_probe.gd:_check_one` 零尺寸前置(:112-114 记红)+13/14 推定留痕(:116-119 采集,:47-50 PASS 后打印) | 「最大板推定留痕」同探针(尾缀〔格边长 V4〕) | 探针 | 同行 1 | ①零尺寸前置(size ≤ 0 记红);②推定读数打印并与本报告 §四留档锚点读数逐值对拍,不一致=卡 §9 L3 #2 呈报(禁实现侧改锚) | 绿(基座 30/28、全屏 46/46——与锚点逐值一致,零对拍偏差) |
|
||||
| 5 | 批末收口 | 全批 | 代码域闸 `tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 绿(`all_green=true`,七闸 00/01/02/03/05/09/10,commit e3e206d0,域=code·scope=related[presentation],`changed_vs_baseline=[]`) |
|
||||
|
||||
**测试削弱申报核对**(卡 §8 申报全集四条,实际执行逐条对上):①探针阈值断言删除=申报 1 的执行面,补偿三件全落(等价断言/四档结构钳制/推定留痕对拍);②PASS 消息改写=申报 2;③循环集 2→4 档纯增量、样本值 (1036,460) 未改、既有两断言逐字保留=申报 3;④未发生恢复阈值、删旧断言、放宽区间、换读取入口任一项=申报 4 成立。另:既有几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)与视口档位表零改动(卡 §14.1 核对通过)。
|
||||
|
||||
## 四、锚点读数留档(两档视口 × 四板型——未来批次对照基准)
|
||||
|
||||
锚点=用户验收的现行渲染值(2026-11-01 用户裁,卡 §2 第 7 条)。本批探针实跑复核:
|
||||
|
||||
| 视口 | board_view.size(实读) | 10 板 | 12 板 | 13 板 | 14 板 |
|
||||
|---|---|---|---|---|---|
|
||||
| 基座 1280×800 | (1022.0, 454.0) | **40px(实读)** | 33px(单点算式读) | **30px(推定打印)** | **28px(推定打印)** |
|
||||
| 16:9 全屏 1920×1080 | (1662.0, 734.0) | 46px(实读·钳制顶格) | 46px(单点算式读) | **46px(推定打印)** | **46px(推定打印)** |
|
||||
|
||||
基座档 10 板实读经等价断言与 `_cell_for(10, (1022,454))` 严格相等(V1 守卫当轮执行);13/14 推定由探针 V4 打印(当轮留痕);12 板与全屏 10/12 三格由单点算式读数件在两档真实可用面积上读出(一次性临时件,当轮跑批留痕后即删):基座 12 板=33px、全屏 10/12=46px。全表与卡 §2 第 7 条锚点逐值一致,零对拍偏差。
|
||||
|
||||
## 五、实机目检总清单(登记不阻塞)
|
||||
|
||||
1. **第三关 14×14 板格面数字清楚可辨**——2026-11-01 用户已实际背书现行画面(锚点裁定),登记照留(卡 T4)。
|
||||
|
||||
## 六、实现决定记录
|
||||
|
||||
代拍板白名单 6 项全部按卡执行(`_cell_for` 局部变量名与注释措辞/四档取样表用字典数组/探针消息文案与 PASS 措辞/尾缀〔格边长 V1–V4〕编排/`BoardViewScript` 常量名/`bv.get("_board")` 取值形态),零超范围裁决。超白名单备案三件(均属测量面承载,判据零扩张):
|
||||
|
||||
1. **探针 `_board == null` 前置**(`layout_probe.gd:104-105`)——卡片明列判据只有零尺寸前置与等价断言;null 前置防「内部直查遇 null 即脚本错误静默跳过剩余断言」(test-conventions §7 失败模式),属测量面防御。撤销方式=删该分支(等价断言改回无条件求值)。
|
||||
2. **探针新增两成员 `_cell_reads`/`_est_reads`**(`layout_probe.gd:17-18`)——PASS 行基座实读与推定留痕打印的承载面;探针不在 S13 扫描面,无预算约束。撤销方式=删成员与两处打印(PASS 行退回无读数形态)。
|
||||
3. **单点算式锚点全表读数件**(本报告 §四 12 板与全屏 10/12 三格补读)——卡 §2 第 7 条锚点表含四板型,探针打印只覆盖 13/14(卡定死),全表留档用一次性临时脚本对两档实读可用面积逐板型读 `_cell_for`(当轮跑批留痕后即删)。撤销方式=无(纯读数,不落码)。
|
||||
|
||||
## 七、遗留读数义务
|
||||
|
||||
零。本批无数值改动,MC/verifier 零跑(卡 §4 范围边界 6)。§四全表读数已当轮留痕,无待补。
|
||||
|
||||
## 八、S13 结构预算与批末闸对账
|
||||
|
||||
- `board_view.gd`:`max-public-methods` **27→27**(`_cell_for` 私有名不计数)、`max-class-variables` **31→31**(零新增成员——钳制界未外提)、无 NEW 键;gdstyle 全批「基线外新违规 0、较登记值恶化 0」。
|
||||
- 探针与测试件不在 S13 扫描面(扫描面=产品码,测量面直构为既有惯例)。
|
||||
- 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(回执落 `game/tests/.gates/receipt.json`,T4 当轮执行)。
|
||||
|
||||
## 九、独立验收审计结论
|
||||
|
||||
独立验收审计由未参与实现的子代理执行,报告=`reports/iter46-验收审计-卡C-2026-11-01.md`。结论:**通过,可合并**——五项全过(对照表五行逐行核对/自跑最窄测试实读:探针基座实读 40px·推定 30/28·46/46·单元 74 checks 全绿/判据绑定面钉生产写入值/测试削弱逐处对上卡 §8 申报全集/全仓无人造阈值回归且无变异·撤销取证)。一处非阻断备注已处置:实现报告 §三 行 1 判据绑定面格补「受控例外」句(审计报告原文备注定格于补句前)。审计方自跑读数与本实现会话当轮实跑逐值一致。未过不得合并红线未触发。
|
||||
@@ -0,0 +1,91 @@
|
||||
# 迭代 46 验收审计——卡C 棋盘格边长口径收口批 v2.0(2026-11-01)
|
||||
|
||||
> 审计方:独立子代理(未参与实现,只读证据+自跑最窄测试,不采信实现报告转述)。
|
||||
> 审计对象:commit `e3e206d0`(代码三件)+`8eaf7d87`(收尾文档两件);卡C 段基点 master `5269ed40`。
|
||||
> 依据:需求卡片 v2.0(§8 改动—验收对照表+测试削弱申报全集)、实现报告 §三 实际对照表、批末闸回执。
|
||||
> 审计工作区:`D:\code\g-plan2-iter46`(分支 iter46)。审计全程只读,唯一写入=本报告。
|
||||
|
||||
## 一、对照表逐行核对——通过
|
||||
|
||||
实现报告 §三 五行逐行核对,每行的验收测试真实存在、尾缀可 grep、真实入口未绕过改动点。
|
||||
|
||||
行 1(探针阈值退役+接线守卫):
|
||||
- 测试真实存在:`game/probe/layout_probe.gd:98-109`(读 `cell_size` → 等价断言),失败路径 `:107`,PASS 行 `:45`。
|
||||
- 尾缀 grep:`grep -n "格边长 V1" game/probe/layout_probe.gd` → `:45`/`:105`/`:107` 三处。
|
||||
- 真实入口:`layout_probe.tscn:6` 挂载 `res://probe/layout_probe.gd`(场景进,非脚本直调);探针断言对象=`main.tscn` 实例化的生产 UI 链(`main.ui.battle.board_zone.board_view`),非测试替身。
|
||||
- 报告附带的 grep 读数(MIN_CELL 零命中)由审计方独立复跑证实(见第五节)。
|
||||
|
||||
行 2(算式单点 V2):
|
||||
- 测试真实存在:`game/tests/unit/core/board_gen_reward_test.gd:75-76`,断言条件 `int(cell) == board_view_script._cell_for(n, avail)`。
|
||||
- 改动点行号核实:`board_view.gd:210`=`_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`、`:226`=`static func _cell_for`——与报告所记 `:210`/`:226` 逐一相符。
|
||||
- 真实入口:`--script res://tests/unit/core/board_gen_reward_test.gd` 直跑(文件头 `:10` 自述同入口)。
|
||||
|
||||
行 3(四档冒烟 V3):
|
||||
- 测试真实存在:`board_gen_reward_test.gd:59` 四档取样表 `[(1,0,10),(2,0,12),(3,0,13),(3,9,14)]`,`:69`/`:71`/`:73` 三条 V3 断言。
|
||||
- 真实入口同行 2(同一宿主 `_init:33` 调 `_smoke_layout_cell_formula`)。
|
||||
|
||||
行 4(推定留痕 V4):
|
||||
- 测试真实存在:零尺寸前置 `layout_probe.gd:113-114`,推定采集 `:116-119`,PASS 后打印 `:47-50`,尾缀 `:49`/`:114`。
|
||||
- 真实入口同行 1。
|
||||
|
||||
行 5(批末收口):
|
||||
- `game/tests/.gates/receipt.json` 实读:`all_green=true`、`commit=e3e206d0f5646ecb8ece22ac08018c26e5591e1d`、七闸(00/01/02/03/05/09/10)exit 全 `0`、`domain=code`、`scope=related[presentation]`、`changed_vs_baseline=[]`——与报告行 5 所记一致。审计方未重跑全量闸(按审计约束,回执承载)。
|
||||
|
||||
## 二、自跑最窄测试抽查——通过
|
||||
|
||||
a. 探针两档(工作目录 `D:\code\g-plan2-iter46\game`):
|
||||
- 命令:`/d/code/godot/Godot_v4.7.2-stable_win64_console.exe --headless --path . res://probe/layout_probe.tscn`
|
||||
- 实测 exit 0。关键输出原文:
|
||||
- `[layout_probe] PASS — 2 档几何不变量全过+格边长等价成立(基座实读 40px)〔格边长 V1〕`
|
||||
- `[layout_probe] [基座 1280x800] 13 板推定 30px/14 板推定 28px(实板可用面积 (1022.0, 454.0))〔格边长 V4〕`
|
||||
- `[layout_probe] [16:9 全屏 1920x1080] 13 板推定 46px/14 板推定 46px(实板可用面积 (1662.0, 734.0))〔格边长 V4〕`
|
||||
- 读数与卡 §2 第 7 条锚点(基座 30/28、全屏 46/46,可用面积 1022×454/1662×734)逐值一致,零对拍偏差。审计方另按公式手工复核:基座 13 板 `(422−24)/13=30.6→30`、14 板 `(422−26)/14=28.28→28`、全屏 14 板 `(702−26)/14=48.3→钳制 46`——打印值与单点算式自洽。
|
||||
|
||||
b. 单元四档(同工作目录):
|
||||
- 命令:`…console.exe --headless --path . --script res://tests/unit/core/board_gen_reward_test.gd`
|
||||
- 实测 exit 0,收尾行 `[board_gen_reward_test] all green (74 checks)`。
|
||||
- 四档各四断言全 PASS:L1D0(41px)/L2D0(33px)/L3D0(31px)/L3D9(28px)——每档「板型一致〔格边长 V3〕」「网格高 ≤ 可用高−24〔格边长 V3〕」「格边 ∈ 26–46〔格边长 V3〕」「算式单点等价〔格边长 V2〕」各一条,16 条尾缀齐全。62→74 增量=每档净增 3 条 × 4 档,账目相符。
|
||||
|
||||
c. gdstyle(工作目录 `D:\code\g-plan2-iter46`):
|
||||
- 命令:`python tools/gdstyle_gate.py`
|
||||
- 实测 exit 0:`扫描面 142 个 .gd;命中 5 条,基线内 5 条,基线外新违规 0 条,较登记值恶化 0 条`。
|
||||
- 基线两键实读:`tools/gdstyle_baseline.txt:11`=`board_view.gd|quality/max-class-variables = 31`、`:12`=`board_view.gd|quality/max-public-methods = 27`——持平,无 NEW 键。
|
||||
|
||||
## 三、判据绑定面核查——通过(一处非阻断措辞备注)
|
||||
|
||||
1. 绑定的是生产写入值,不是多层防御后的终态:
|
||||
- 探针取值面=`cell_size` 公开只读属性(`board_view.gd:89-91` getter 直返 `_cell`),`_cell` 由 `set_cell_area` 在 `:210` 直接写入;生产写入路径两条未动(`board_zone_view.gd:92` resized 信号→`set_cell_area(_board_area.size)`、`board_view.gd:273` rebuild 末尾)。等价断言 `layout_probe.gd:106` 对 `bv.size`(实板可用面积)严格相等(`!=` 记红)——生产停调单点算式即红(基座档默认值 46 与算式值 40 分叉可捕)。
|
||||
- 单元取值面=真实调用 `set_cell_area(avail)` 后直读 `_cell`(`board_gen_reward_test.gd:65,67`),与同输入 `_cell_for(n, avail)` 逐档 int 严格相等(`:76`)——同一事实两路读取。
|
||||
2. 内部字段直查两处的例外依据:
|
||||
- 探针读 `_board`(`layout_probe.gd:103`):理由「板型 N 无公开出口」已载于代码注释 `:102`、卡 §7.3 T2 与卡 §14.2、报告 §六 白名单第 6 项。
|
||||
- 测试读 `_cell`(`board_gen_reward_test.gd:67`):批前既有取值面(卡 §2 第 4 条记载改前即如此),卡 §8 申报 4 明列 `cell_size`/`_cell` 为两合法入口、禁换别的入口——属守住的旧面,非新开直查。
|
||||
3. 非阻断措辞备注:卡 §14.2 与代码注释 `layout_probe.gd:102` 均称例外理由「对照表行 1 注明」,但实现报告 §三 行 1 判据绑定面格(以及卡 §8 行 1)未逐字带「N 无公开出口/受控例外」句——交叉引用指针落空。例外事实与理由已在他处完整承载(本节第 2 点所列四处),断言零削弱、无申报外改动,不判红;建议实现会话顺手把该句补进报告 §三 行 1(可选,不阻塞合并)。
|
||||
|
||||
## 四、测试削弱核查——通过
|
||||
|
||||
e3e206d0 负向行(删除行)全集逐处对账,全部落进卡 §8 申报四条,无申报外删改:
|
||||
|
||||
| 删除内容 | 对应申报 |
|
||||
|---|---|
|
||||
| `const MIN_CELL := 36.0` 整行 | 申报 1(阈值常量退役) |
|
||||
| `if cell < MIN_CELL:` +「< 可读门槛」记红两行 | 申报 1(阈值断言删除) |
|
||||
| 旧 PASS 消息(「格子 ≥36px」形态)两行 | 申报 2(文案随判据换代) |
|
||||
| `for L in [1, 2]` 与 `generate(L, 0, …)` 两行 | 申报 3(循环集 2→4 档)+白名单第 2 项(字典数组形态) |
|
||||
| 两条旧 Kit.check 的消息行 | 申报 3(前缀 L%d→L%d D%d——卡 §7.4 明许)+白名单第 4 项(尾缀编排) |
|
||||
| 冒烟 print 头行(「底部预留回归」→「·四档板型」) | 申报 3 同批日志头随档数换代(非断言,无条件面) |
|
||||
| `board_view.gd` 内联算式六行 | 非断言——逐字搬入 `_cell_for`(`board_view.gd:226-231` 对拍:SEP/DEPTH_PX 同源、reserve 24.0+d 同式、clampi(26,46) 留函数内),行为零变化由单元 74 checks 全绿承载 |
|
||||
|
||||
补偿三件全在位:等价断言(V1)、结构钳制四档(V3)、推定留痕(V4)。既有两断言条件逐字保留(`grid_h <= int(avail.y) - 24`、`cell >= 26.0 and cell <= 46.0`),样本 `Vector2(1036, 460)` 未改。既有几何不变量与视口档位表零改动(diff 负向行中无其一)。申报 4(禁恢复阈值/删旧断言/放宽区间/换读取入口)成立——四处均未发生。
|
||||
|
||||
## 五、全仓无人造阈值回归——通过
|
||||
|
||||
- `grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` → 零命中(exit 1);扩到 `tools/`、`design/` 查 `MIN_CELL` 亦零命中。
|
||||
- 30/36 阈值形态穷举:三件改动文件中 30 仅两处且均非格边长判据(`board_gen_reward_test.gd:203`「板面 30% 锚」=雷数比例锚、`board_view.gd:659`「南部 30%」=墙面口径注释);36 零出现。全 `game/` 断言形态 grep(`(>|>=|<|<=)\s*(30|36)`)命中皆为无关域(RNG 位移 `>> 30`、商店 `1 << 30` 哨兵价、护甲 `<= 30` 属性域检);`clampi` 无 30/36 形态。格边长唯一区间判据仍是公式自带 `clampi(26, 46)`。
|
||||
- 变异类测试与撤销类取证:diff 负向行无「人为改错值验抓取」形态;无回退 hunk;测试代码无注入破坏分支(探针 `_board == null` 前置属测量面防御,报告 §六 第 1 项已备案,非变异)。
|
||||
- design/ 现状:`battle.md:144`「可读下限 30px」句仍在位=预期(实现批禁写 `design/`,退役走反哺点通道——`reports/iter46-反哺点-卡C-2026-11-01.md` 第 1 条已登记,卡 §5 明示不阻塞本批)。
|
||||
- 禁写面核查:卡C 段全区间 `5269ed40..8eaf7d87` 触碰文件恰为三件代码+两件 reports,`待办清单.md`/`体验验证清单.md`/`game/tests/README.md`/`design/` 零触碰。
|
||||
- 工作区漂移核查:三件代码文件 `git status` 干净——审计实跑的即 e3e206d0 提交的字节。
|
||||
|
||||
## 六、总结论
|
||||
|
||||
**通过。** 五项审计全过:对照表五行真实入口与尾缀齐全;自跑探针两档绿(基座实读 40px、推定 30/28 与 46/46 合锚点)、单元 all green 74 checks(四档 41/33/31/28px,V2/V3 全 PASS)、gdstyle 基线外新违规 0 恶化 0;判据绑生产写入值与 `_cell_for` 严格相等;删改逐处对上申报四条;全仓无人造阈值回归、无变异/撤销形态。唯一遗留=第三节第 3 点的交叉引用措辞备注(不判红、不阻塞合并,补句归实现会话可选动作)。
|
||||
@@ -0,0 +1,19 @@
|
||||
# 迭代 47 反哺点清单(实现批未写 design/ 与根规则文件——逐条待用户裁决后由文档批落文)
|
||||
|
||||
- 接收方:待办清单「营地交互重做·五张需求卡片」条目(裁决后逐条清掉)+各 owner 文档
|
||||
- 依据:卡⑥/卡④/卡⑤ §5 关联文档同步面全表义务格=反哺点·列清单
|
||||
|
||||
| # | 文档 | 位置 | 原句/现状 | 拟改 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | architecture.md | 呈现层模块清单/测试与质量域 | 无 UiCampModals/四子界面/CampRefreshBus/MainEscFlow/CampHudBand/CampSceneView 六新件行 | 增六件归属行(呈现层/组合根拆件)+LAYER_CONTRACT 槽变化(camp_overlay→camp_scene+四子界面+camp_esc_overlay)+产品码首个键盘输入入口(main._unhandled_input 契约 9 接线)注记 | 卡⑥/④/⑤ 落地实况 |
|
||||
| 2 | camp.md | §顶栏 :39-41 / §叠层与开合 :43-45 | 「刷新入口=营地刷新单口(卡⑤同口挂接)」未记实装;承载形态「归卡④ §9 代拍板域,本 spec 不预设值」 | 回填实装状态(单口=CampRefreshBus 已挂/时机清单三条)+承载形态=单一内页切换(场景根留底)拍板值落文 | 卡④ T2/T3+卡⑤ 实装 |
|
||||
| 3 | art-bible.md | §6.2 :393 围墙细条句 / §4.1 场景环境子板 | 「围墙层落盘按细条 (0,240,1280,70) 裁」;环境子板色值待登记 | 细条句补实装值 (0,261,1280,49)(Y 压缩桩身 43 锁定值精确,备选 A′ 在档);登记营地 32 色主色板(色值清单=artwork/2026-11-01-营地终稿后处理/换算式登记.txt,两道门合规零违例对) | 卡④ T1 实测+§0.3-3⑤ |
|
||||
| 4 | camp-scene-prompts.md | §2.3 坐标表 / §4 验收 | 贴图矩形=规划值(130×140 等);单张 ≤32 量化后 | 八物件 contain 实测尺寸表(board 130×121 等——呈报备案追认值)+量化实况注记 | 卡④ T1 换算式登记 |
|
||||
| 5 | 生涯.md | :72 营地行(P6 条带去向) | 「营地条带的去留是本提纲必答项——见 §五 P6」 | P6 注销(Q4=甲:条带随四柜台拆解撤下,逐局历史回看归生涯——本批已撤) | 卡④ Q4 裁定落地 |
|
||||
| 6 | 08-Alpha版本标准.md | :193 「键盘 / 手柄输入(排 Beta)」 | Beta 行未注记 Esc 已落地 | 行内加注「例外:营地语境 Esc 浮窗已落地(用户明示裁决,澄清⑤裁决 9;键盘面仍排 Beta——本批只落 Esc 一项)」 | 卡⑤ 契约 2/9 |
|
||||
| 7 | 待办清单.md | 营地交互重做条+UI 贴图重构条+图鉴挂起 | 条目状态「已编迭代 47」;贴图重构条对象注记;图鉴挂起 | 状态推进(完成即整条删除)+实况换代(三卡收口凭据);贴图重构条「四柜台对象变化」注记更新(柜台形态已退役);图鉴挂起条入口改挂注记 | 三卡实装 |
|
||||
| 8 | 体验验证清单.md | 新增目检条 | — | 登记 §实现报告 §6 五条(场景观感/点选反馈/大门篝火/Esc HUD/四子界面)——登记不阻塞 | 卡④ V11+卡⑤ V9 |
|
||||
| 9 | roadmap.md | 迭代 47 行+当前位 | 状态「待开工」 | 行换代(三卡实现报告列+状态)+当前位三行换代 | 迭代收口 |
|
||||
| 10 | shot_checker 色数判据 | tools/shot_checker.py A 段 | camp 屏唯一色数 ≥64 | L3 呈报在案(读法 A/B 待裁)——裁决后判据修正随批+待办清单登记条 | 停机呈报 2026-11-01 |
|
||||
| 11 | asset-path-map.md §3 / art-assets-checklist.md §3.5 | 营地 10 张路径行 | 登记超前注记 | 实装入库确认(scene_camp_* 十件在役+TEX_CAMP_* 十键消费)+终稿置换待用户游戏内终审注记 | 卡④ T1 |
|
||||
| 12 | ui_kit CAMP_HEADER_BAND 令牌 | ui_kit.gd :178 邻域+ui_contrast 表 | 令牌在役(营地域柜台头带消费随形态退役) | 令牌退役评估(消费面仅剩 keystone_badge 工厂与关卡屏/商店——营地域测点对已注销;随令牌批处置,非本批强删;审计记档 2 订正) | 卡④ T5 核签+审计记档 |
|
||||
@@ -0,0 +1,126 @@
|
||||
# 迭代 47 实现报告(卡⑥ 营地模态句柄子容器批+卡④ 营地场景图新建批+卡⑤ Esc 浮窗与营地顶栏批)
|
||||
|
||||
- 接收方:roadmap.md 迭代 47 行(实现报告列)+待办清单「营地交互重做·五张需求卡片」条目
|
||||
- 性质:代码实现批产出物(实现批唯一可写目录=reports/;design/ 与根规则文件零写入——反哺点清单另立)
|
||||
- 分支:iter47(worktree g-plan2-iter47)· 基线 5ad72f44 · 收口提交史见 §9
|
||||
|
||||
## 1. 各卡状态
|
||||
|
||||
| 卡 | 状态 | 提交 | 出口验证 |
|
||||
|---|---|---|---|
|
||||
| 卡⑥ 句柄子容器批 | 收口 | 3a9c6ff4 | --code 全套闸 all_green;V1/V2/V3 留痕齐 |
|
||||
| 卡④ 营地场景图批 | 施工完成(T6 出口闸等 shot_checker 裁决重跑) | aa4aa1dd/4b5e3f27/98470adc/5a0e7069/1243ffe6/52296872 | 七探针+system_test 全绿;V14 11 项绿;--code 闸 01-04/09/10 绿、05 渲染闸红=L3 呈报在案 |
|
||||
| 卡⑤ Esc 浮窗与顶栏批 | 施工完成(T4 出口闸同上) | 4e4232d8 | 冒烟 PASS+system_test 69/0+七探针全绿 |
|
||||
|
||||
## 2. 卡⑥ 实际改动—验收对照表(尾缀〔句柄子容器批 Vn〕)
|
||||
|
||||
| 需求点 | 改动 | 验收测试 | 层级 | 真实入口 | 判据绑定面 | 结果 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 句柄迁移零行为漂移 | ui_camp_modals.gd 新件;ui_modals 持 camp 删平铺;main_ui_build 装配 10 处;改指面 8 文件 43 处 | 五探针营地段+system_test V1c:94-106+句柄同一性链 :108 | 探针+系统 | 现役营地断言逐件复跑(断言零改寻址新) | 全段绿=零漂移;全仓 grep 零 modals.camp.overlay 残留 | 绿(V1 尾缀 34 处 grep 在位) |
|
||||
| V2 层序合同零漂移 | ui_registry node() 返回链改写(key 平铺不改名) | system_test V1c 合同树序链 | 系统 | LAYER_CONTRACT 单一事实源 | 树序链绿=合同落位零漂移 | 绿(V2 尾缀 :106 在位) |
|
||||
| V3 S13 读数 | ui_modals 类变量 14→14 | gdstyle gate+grep -c '^var ' 计数 | 静态 | static_scan.sh | gate 绿零 NEW+计数=14 留痕(观测面局限:闸对 14/15 同零命中,人工计数承载) | 绿(143 件零 NEW;计数 14) |
|
||||
|
||||
## 3. 卡④ 实际改动—验收对照表(尾缀〔营地图批 Vn/Bn〕)
|
||||
|
||||
| 需求点 | 改动 | 验收测试 | 层级 | 真实入口 | 判据绑定面 | 结果 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 场景图形态 | camp_scene_view 10 层铺底+layout.tres+camp_screen 改挂 | shot_probe camp 段(截图=场景图)+ui_struct 场景层序段+shot_checker 结构段 | 探针 | 进存档→选档→进档→营地场景屏 | 截图实拍=场景图形态(十层合成/门洞透路/七物件就位);子节点序=表 z 序逐层;贴图矩形逐值 | 绿(截图目检+层序段 11 层断言) |
|
||||
| V2 七物件可达+叠层退避 | 七命中区+object_pressed+组合根路由 | ui_probe/ui_click/ui_struct 场景点选段 | 探针 | 命中区→_gui_input→object_pressed→路由 | 锻炉/宝箱/木桩/布告板/笔记本/篝火/大门逐一 object_pressed 驱动可见性 | 绿(七件路由断言分散六探针全绿) |
|
||||
| V3 大门两分支+开局收闭 | confirm_dialog 三钮化+消费方编排扩展+main_overlay_flow 路由 | ui_click 大门两分支段 | 探针 | 场景点选 gate→确认弹窗→分派 | 点大门不直接进冒险(is_adventure 翻转只在 confirmed 后);cancelled 写口零调用+营地根 visible;开局后营地隐藏 | 绿(无断点两出口+有断点三出口 set_breakpoint 注入全过) |
|
||||
| V4 五界面功能+写口零回归 | 四 view 抽出+presenter 承接面+bus 单口 | forge_workbench_test(承载类同迁断言零改)+ui_click 购买链 | 单元+探针 | 意图信号→presenter→MetaProgression 写口 | 树购买出账+持久(reload 后级=1)/Fork 持久/锻造三意图——全链复跑绿 | 绿(12 项+点击链 0 问题) |
|
||||
| V5 生涯只读契约 | career 入口改挂笔记本(本体零改) | ui_struct career 段(笔记本真实路由) | 探针 | 笔记本命中→object_pressed→_open_career | 生涯页可达+三契约不破+营地语境关生涯不回开始层 | 绿 |
|
||||
| V6 图鉴挂起态 | codex_view 占位壳 | ui_click 灰字段(锚=结构位预留) | 探针 | 布告板命中→codex 开 | 占位锁文锚定+零激活禁行+非禁用态 | 绿 |
|
||||
| V7 篝火动线 | 路由 campfire→二次确认→on_camp_closed | screen_flow 回退段+ui_click? | 探针 | 篝火命中(只下半)→确认→主菜单 | 二次确认 confirmed 先于关层;确认后营地+选档同收+菜单显 | 绿(screen_flow ③步+T4 孤检) |
|
||||
| V8 顶栏位+刷新单口 | set_hud_band 预留位+camp_refresh_bus 新件 | ui_struct 顶栏位段+卡⑤ HUD 段 | 探针 | 写口尾→bus.refresh→现开界面+HUD | 顶栏位 48px 在座;写口尾一闸刷新(八尾改路由) | 绿 |
|
||||
| V9 反参照纪律 | 场景根态零按钮 | ui_struct 反参照段 | 探针 | 场景根子树 Button 计数 | 根态零文字按钮(禁文字按钮列表形态) | 绿 |
|
||||
| V10 美术约束 | shot_checker camp 骨架段+素材二器 | ui_contrast 令牌表+palette_check 读数 | 机检 | camp 截图+素材文件 | 见 §7 遗留读数义务(A 段色数判据=L3 呈报在案) | 部分(令牌 36 对绿;A 段红待裁) |
|
||||
| V11 目检登记 | 实机目检总清单 | 件核对(§6) | 目检 | 用户实机 | 登记即义务了结(不阻塞) | 已登记(§6) |
|
||||
| V12 探针迁移留痕 | F8 全量位点 | 件核对(本表+§8 迁移留痕) | 件核对 | 探针 diff 对账 | 逐点改前→改后 | 绿(§8) |
|
||||
| V13 反哺点清单 | §5 同步面 | 件核对 | 件核对 | 反哺点文档逐条 | 每条有落点 | 绿(反哺点文档) |
|
||||
| V14 layout 数据件校验 | schema validate+fail-fast 钩子 | camp_scene_layout_test.gd:_test_layout_hotspot_geometry(manifest 登记) | 单元 | 单测载入 .tres+ConfigDB 装载链 | 校验通过+七命中区几何红线+关系谓词零坐标字面量 | 绿(11 项) |
|
||||
| B1 组合根接线总改造 | main.gd 路由/三钮编排/篝火/收闭+bus | ui_click 大门段+screen_flow 篝火段 | 集成 | 确认分派/_resume/_start/bus.refresh | 三钮消费方编排 open 前设后分派;cancelled 零写口;篝火确认离场 | 绿 |
|
||||
| B2 CampOverlay 退役+四界面抽出 | overlay 删除+四 view+五句柄子容器 | forge_workbench_test+全仓件核 | 集成 | 五界面 open/close→模态栈单口 | 旧件零残留(件核一);承载类同迁断言零改 | 绿 |
|
||||
|
||||
## 4. 卡⑤ 实际改动—验收对照表(尾缀〔ESC批 Vn/Bn〕)
|
||||
|
||||
| 需求点 | 改动 | 验收测试 | 层级 | 真实入口 | 判据绑定面 | 结果 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 Esc 唤起四态 | camp_esc_overlay+main_esc_flow+main._unhandled_input | screen_flow Esc 步 | 探针 | push_input 合成 Esc(真实键盘路径) | 根态唤起/再按关窗/echo 零响应/子界面无反馈/设置开态不误关/菜单语境不唤起 | 绿(10 项) |
|
||||
| V2 浮窗三项 | setup_buttons 三钮 | ui_struct 浮窗段 | 探针 | 浮窗视图实例化 | 三钮在位且仅三(_buttons 递归计数) | 绿 |
|
||||
| V3 返回主菜单 | _on_return_to_menu(首步 close 浮窗) | screen_flow 返回动线段 | 探针 | return_requested→动线 | 浮窗自收+营地隐+选档收+菜单显(四可见性) | 绿 |
|
||||
| V4 设置双路径 | _on_settings+语境分支 | screen_flow 设置段 | 探针 | settings_requested→open→closed | 设置开浮窗恒 visible(树序遮挡非属性隐藏);关后回浮窗不回主菜单 | 绿 |
|
||||
| V5 退出项 | _on_quit→_quit_game 复用 | 连接链断言(main_ui_build 接线在位) | 静态 | quit_requested→_on_esc_quit→_quit_game | headless 不可真退=静态判据+观测面局限申报(本表) | 绿(接线段随装配绿证) |
|
||||
| V6 HUD 余额 | camp_hud_band+bus 挂册+进档尾刷新 | screen_flow HUD 段 | 探针 | 隔离档直写 _add_keystone(9999)→显式触发 bus.refresh | badge 文本=「钥石 9999」(单口现值,显式触发才变) | 绿 |
|
||||
| V7 零动效 | 直开直关 | ui_struct 浮窗段 | 探针 | 双件源码+子树扫描 | 零动效令牌+AnimPlayer=0(扫描面>0 非空前置) | 绿 |
|
||||
| V8 层序与遮挡 | contract 第六槽+node() | system_test V1c 复跑+ui_struct badge 段 | 系统+探针 | 合同表单一事实源 | 树序链含新槽逐对保序;badge 零波及 | 绿 |
|
||||
| V9 目检登记 | 实机目检总清单 | 件核对(§6) | 目检 | 用户实机 | 登记即义务了结 | 已登记(§6) |
|
||||
| V10 反哺点清单 | §5 同步面 | 件核对 | 件核对 | 反哺点文档 | 每条有落点 | 绿 |
|
||||
| B1 Esc 接线与浮窗 | 输入入口/薄委托/视图/新槽 | screen_flow+ui_struct | 集成 | 合成 Esc→路由→flow.open/close | 四态路由绑现开集合与调用记录(ui_struct 大门段 confirm_dialog._secondary_btn 直查申报:三钮化即被测改动本体、两/三出口形态唯一直观观测面、无公开访问器——审计 ⚠️ 项补注) | 绿 |
|
||||
| B2 HUD 落位与刷新 | band 新建/set_hud_band/bus 挂册 | screen_flow HUD 段+ui_struct 顶栏段 | 集成 | bus.refresh→band.refresh_camp→badge | badge=单口取值;顶栏位结构在座 | 绿 |
|
||||
| B3 设置语境分支与三出口 | _on_settings_closed 分支+三出口动线 | screen_flow 设置段+返回段 | 集成 | 三钮 pressed→MainEscFlow | 关设置回浮窗禁弹主菜单(反向断言);退出连接链 | 绿 |
|
||||
|
||||
## 5. 实现决定记录(代拍板追认单——随 commit 留痕汇总)
|
||||
|
||||
1. **承载形态=单一内页切换(场景根留底)**(卡④ §9-2 域内):模态栈 open 只隐上方登记层(modal_stack.gd:27 实测),四子界面登记于场景根之上且实底遮挡——双开互斥靠遮挡天然成立;V2 断言按留底派生(子界面开=场景根 visible 保持)。
|
||||
2. **围墙层处理路线 A″**(用户裁 A 后处理实现):去底(边界泛洪+奶油描边屏障 BG_TOL=50)→门洞手术(门柱内移+克隆桩列,缺口 675→480 源=240 终稿精确)→Y 轴压缩(桩身 609→86 源=43 终稿锁定值精确,整带 692→98 落盘 (0,261,1280,49) 底锚 y310);备选 A′(带高 70/桩身 62)存档 artwork 同目录。竖向克隆接缝 3× 放大可辨、1× 游戏尺度不可辨(申报)。
|
||||
3. **八物件 contain 落位尺寸**(呈报备案追认):board 130×121/gate 240×211/dummy 120×180/forge 240×193/tent 251×170/chest 106×90/notebook 100×66/campfire 160×149(锁定矩形 contain,命中矩形按锁定值);贴图锚定=底中心(帐篷/锻炉/宝箱/篝火/笔记本)+顶锚(门扇悬挂/布告板挂墙)。
|
||||
4. **营地主色板 32 色**(§4.1 场景环境子板两道门合规):各层独立量化→并集 272→最近对合并 120 轮→32 色零违例对;十层按终版色板重量化回写。色值清单=换算式登记.txt,登记 art-bible §4.1 走反哺点。
|
||||
5. **codex 锁文称谓订正**(L2):「柜台结构位预留」→「结构位预留」(零新文案——『柜台』随形态退役)。
|
||||
6. **set_close_path 未落独立方法**(L2 API 面):Q2 关闭路径=组合根 Esc 路由直呼 flow.close(浮窗),视图无自关通路——卡面「关闭由组合根路由」直译;bind_signals 承载三钮意图外发。
|
||||
7. **契约 9 分支序实证修正**:①先于②(设置开在浮窗之上态先命中无反馈)——首装②先被 screen_flow Esc 段实机场景证伪(2 红),按卡面编号归位后全绿。
|
||||
8. **CampSceneView 初始隐**:_init visible=false(登记模态初始隐——boot 态场景根盖菜单三红同源修复)。
|
||||
9. **ui_modals 头注/迁移面**:卡⑥ 头注换代(十二平铺+营地域子容器)✓;ui_struct 层序注释换代(camp_scene 代言)✓。
|
||||
10. **卡④ T0 预算预案兑现**:main.gd 15/15 恰贴上限——T3/T4/T5 实现零新增成员(路由走 MainOverlayFlow 静态件+薄委托)✓。
|
||||
11. **camp.md v2.3 定稿换代吸收**(2026-11-01 用户裁,后于卡面):①承载形态=五功能窗口·场景根留底+半透明压暗遮罩 0.6(实现初版实底屏——按定稿换装,四子界面壳+生涯页基底同批换);②Esc 有功能窗开着→先关最上层窗(覆盖卡⑤ 契约 9① 的 Q1 甲「子界面开态无反馈」——MainEscFlow ①分支改写,_other_modal_open 退役);③五窗互斥单开(遮罩 STOP 挡场景点击自然成立,零代码)。设置开在浮窗之上仍无反馈(契约 3 保留)。
|
||||
|
||||
## 6. 实机目检总清单(登记不阻塞——体验验证清单入册条目由文档批落)
|
||||
|
||||
1. 营地场景观感:十层构图/围墙门洞/纵深(V11——纵深稿「中景 2/3」可辨性)。
|
||||
2. 七物件辨识与点选反馈:悬停轮廓+浮动+名牌/按下位移/锻炉宝箱禁用态(未解锁档)。
|
||||
3. 大门两分支弹窗(三出口文案与位次)+篝火二次确认动线。
|
||||
4. Esc 浮窗三动线+HUD 余额观感+浮窗视觉面(卡⑤ V9)。
|
||||
5. 四子界面视觉(米板身壳/行板/返回钮位次)。
|
||||
|
||||
## 7. 遗留读数义务
|
||||
|
||||
1. **shot_checker A 段色数判据互斥**=L3 呈报在案(reports/停机呈报-shotchecker色数判据互斥-2026-11-01.md)——读法 A/B 待裁;裁决后判据修正随批+闸重跑追绿。
|
||||
2. palette_check 读数(V10 素材级):15 张 0 红灯 0 新增色(只读模式)——48 令牌 baseline 目录在库缺位,双门机检化=工程批事项(反哺点登记)。
|
||||
3. 用户在游戏内终审十层素材(2026-10-31 裁定预留)——终稿置换(若有)走反哺点。
|
||||
|
||||
## 8. 探针迁移留痕表(V12——F8 全量位点改前→改后)
|
||||
|
||||
| 位点 | 改前 | 改后 |
|
||||
|---|---|---|
|
||||
| screen_flow :54 营地旗标 | modals.camp.overlay | modals.camp.scene |
|
||||
| screen_flow 回退段 :119-125 | overlay.closed.emit()+手动关选档 | 篝火动线(object_pressed campfire+confirmed) |
|
||||
| screen_flow Esc 段(新增) | —(选档语境零响应段保留) | 十断言步 _step_camp_esc_and_hud |
|
||||
| ui_probe 营地段 :26-57 | overlay 余额/树钮/秘宝行 | 场景点选路由→tree/treasure 视图成员 |
|
||||
| ui_click 营地段 :60-250 | overlay 树链/灰字六源位 | 树链 tree view+灰字分扫 treasure/codex(锚去『柜台』)+savesel 三处场景根 |
|
||||
| ui_click 断点段(卡② 移交) | lvbtn 驱动(已随卡② 撤) | 大门两分支断言段(V3 落位——无断点/有断点 set_breakpoint 注入) |
|
||||
| ui_struct :45 audit | audit("camp",[overlay]) | audit("camp",[camp.scene]) |
|
||||
| ui_struct 源扫描 :48-50 | camp_overlay_view.gd 单件 | 营地域六件合计(场景根+四子界面+公共壳) |
|
||||
| ui_struct 生涯段 :54-69 | 函数直调 _open_career | 笔记本真实路由(open_camp 重入+object_pressed notebook+营地语境分支断言) |
|
||||
| ui_struct 场景段(新增) | — | 层序/矩形逐值+锻炉可达+顶栏位+反参照+Esc 浮窗段 |
|
||||
| ui_contrast 令牌表 :48-55 | 头带两行+铭牌副题+最近冒险 | 四行随形态注销(CAMP_HEADER_BAND 零消费随退);ROW_PLATE/PANEL_DATA 行保留 |
|
||||
| shot_probe :120-135 | 关闭营地钮收闭 | 截图=场景图+篝火动线收闭 |
|
||||
| system_test V2/:108/:121 | camp_overlay 成员/句柄 | camp_scene 三处机械随迁(T3) |
|
||||
| system_test V3g camp 分支 | overlay 开合 | 场景根开合自适配(on_camp_closed 关层收闭) |
|
||||
| forge_workbench_test :33 | 直载 camp_overlay_view+._forge._X | 直载 forge_view+扁平成员(断言零改 12 项绿) |
|
||||
| theme_projection :711-716/:801-810 | 等高源码断言+最近冒险投影 | 注销(卡面削弱申报授权——旧形态断言失去对象/条带 Q4 甲撤下) |
|
||||
|
||||
## 9. 提交史与闸况
|
||||
|
||||
3a9c6ff4 卡⑥T1 → aa4aa1dd 卡④T1 进度 → 4b5e3f27 卡④T2 → 98470adc 卡④T3 → 5a0e7069 卡④T4 → 1243ffe6 卡④T5 → 52296872 卡④T5 补 → 4e4232d8 卡⑤T1-T3。
|
||||
闸况:卡⑥ 出口 --code all_green(3a9c6ff4 对齐);卡④ T6 出口闸 01-04/09/10 绿、05 渲染闸红=shot_checker 判据互斥(L3 呈报在案——合回前重跑)。
|
||||
|
||||
## 10. 独立验收审计结论
|
||||
|
||||
独立审计报告=reports/iter47-验收审计-2026-11-01.md(未参与实现子代理执行;六条自跑抽查全绿:system_test 69/0、V14 11 项、forge_workbench 12 项、screen_flow 含 Esc 段、static_scan S13 零新增、ui_contrast 36 对)。
|
||||
|
||||
- 审计算术:对照表 42 行全查(✅33/⚠️8/❌1);判据绑定面重点四项全过;测试削弱=授权 6 件在案、未授权零;直查例外 5 处全有据(1 处补注记)。
|
||||
- 缺漏处置回执(实现方,审计处方最窄单元):
|
||||
1. 卡⑤ V5 退出项断言缺位→ui_struct Esc 段补静态接线断言(quit_requested 连接表目标=_on_esc_quit+调用链 _on_esc_quit→_on_quit→_quit_game 源扫描)+〔ESC批 V5〕尾缀——孤跑复验绿。
|
||||
2. 尾缀覆盖缺口→V4(购买出账/持久两行)/V6(图鉴锁文两行)补〔营地图批 V4/V6〕;V7 补显式二次确认断言(screen_flow 回退段)+〔营地图批 V7〕;B1/B3 补〔ESC批 B1/B3〕、B2 补〔ESC批 B2〕;卡④ B1/B2 各补一行。V4 的 forge_workbench 部分按审计处方记豁免(断言零改申报约束——补尾缀即违零改)。
|
||||
3. 记档项处置:V7 长度非空守卫已补一行;反哺点 #12 订正(CAMP_HEADER_BAND 消费面仅剩 keystone_badge 工厂等);V9 扫面注记(直接子层非递归——本段更正);confirm_dialog._secondary_btn 直查补申报(三钮化即被测改动本体、两/三出口形态唯一直观观测面、无公开访问器)。
|
||||
- 处置后结论:**通过**(缺漏 1 补齐+轻微尾缀补齐+记档 4 项处置;孤跑复验回执见下)。
|
||||
- 孤跑复验:ui_struct_probe 退出码 0(含 V5 新断言与 V7 守卫);screen_flow_probe 退出码 0(含 V7 显式断言与 B 尾缀段)。
|
||||
@@ -0,0 +1,128 @@
|
||||
# 迭代 47 独立验收审计(卡⑥ 营地模态句柄子容器批+卡④ 营地场景图新建批+卡⑤ Esc 浮窗与营地顶栏批)
|
||||
|
||||
- 接收方:`reports/iter47-实现报告-2026-11-01.md` §10(独立验收审计结论回填行)
|
||||
- 审计身份:未参与实现的独立审计方——只读三张需求卡、实际对照表、diff、测试件与实现报告,自跑最窄测试抽查,不采信报告「已全绿」
|
||||
- 审计对象:worktree `D:\code\g-plan2-iter47`(分支 iter47);diff 范围=基线 5ad72f44..HEAD(65b2a574),重塑后提交=469d856f 卡⑥/2ce70ac7 卡④/399ed3ad 卡⑤/65b2a574 文档收尾(原史在 backup/iter47-20261101-185917,审计只对 master..iter47 净 diff 负责)
|
||||
- 审计口径:test-conventions §8.7 四件事+卡④ §7/卡⑤ §7「独立验收审计」条;对照表逐行核对(三表全行,非抽样下限)、判据绑定面重点四项、削弱逐处核对、直查例外逐行核对
|
||||
- 本审计唯一写入件=本报告(reports/);产品码/测试码零改动
|
||||
|
||||
## 1. 对照表逐行抽查(实现报告 §2/§3/§4——三表全行)
|
||||
|
||||
判定标记:✅=通过(测试在役+尾缀可 grep+真实入口未绕改动点)/⚠️=通过但附注记/❌=缺漏。
|
||||
|
||||
### 1.1 卡⑥ §2(3 行全查)
|
||||
|
||||
| 行 | 判定 | 依据 |
|
||||
|---|---|---|
|
||||
| V1 句柄迁移零行为漂移 | ✅ | 尾缀〔句柄子容器批 V1〕grep 34 处(六文件逐处);改指面实测 8 文件 43 处 `modals.camp_overlay` 删除位逐一有 `modals.camp.*` 替换行(main_ui_build 10/ui_click 16/ui_probe 8/screen_flow 3/shot_probe 2/system_test 2/ui_struct 1/ui_registry 1——与 F4 清单全等);全仓 grep `camp.overlay` 零命中(探针残留清零核实);句柄同一性链 system_test :109(camp.scene==camp_screen.camp_scene)在役且当轮跑过;ui_camp_modals 六枚实件零空占位(契约 4);camp_overlay_view.gd 整件删除零悬挂 |
|
||||
| V2 层序合同零漂移 | ✅ | system_test :106 V1c 合同树序链带〔句柄子容器批 V2〕尾缀;ui_registry LAYER_CONTRACT key 平铺(camp_scene/forge_view/…/camp_esc_overlay)+node() 全部经 `modals.camp.*` 二层返回、`camp_overlay` 分支已删;ui_struct `_modal_registry_contract` 登记册逐点名含新槽 |
|
||||
| V3 S13 读数 | ✅ | `grep -c '^var '` ui_modals.gd=14(审计人工复核与报告留痕一致);ui_camp_modals.gd=6;static_scan gdstyle 闸绿(基线外新违规 0)——卡面「观测面局限申报」(闸对 14/15 同零命中)如实 |
|
||||
|
||||
### 1.2 卡④ §3(16 行全查)
|
||||
|
||||
| 行 | 判定 | 依据 |
|
||||
|---|---|---|
|
||||
| V1 场景图形态 | ✅ | ui_struct :58-70 层序段(子节点序==layout 表 z 序逐层+各层贴图矩形逐值)带〔营地图批 V1〕;shot_probe camp 段截图改场景图+收闭改篝火动线(真实入口=进存档→选档→进档) |
|
||||
| V2 七物件可达+叠层退避 | ⚠️ | 七件逐一驱动核实:forge(ui_struct :71-74)/dummy→tree(ui_probe)/chest→treasure(ui_probe)/notebook(ui_struct :95-98)/board→codex(ui_click :174)/gate(ui_click :688)/campfire(screen_flow :124)——驱动形态全为 `object_pressed.emit()` 信号发射(卡面 V2 判据绑定面明文「绑 object_pressed 信号发射面」,信号即视图公开提交面,合规);注记:`_gui_input` 命中判定段(topmost/press-release 同区/禁用过滤)无合成鼠标点击断言面——新件内部路径无旧断言被绕,判据按卡面钉信号面成立 |
|
||||
| V3 大门两分支+开局收闭 | ✅ | ui_click :679-722 大门两分支段七断言全带〔营地图批 V3〕——真实路由(object_pressed gate→组合根分派→ConfirmDialog);无断点两出口(次钮隐藏);cancelled 恒无变更(余额读数恒等+营地根 visible+is_adventure 恒假=写口零调用);confirmed 后 is_adventure 翻转+营地隐;有断点三出口(次钮现形)+secondary_confirmed 出口 |
|
||||
| V4 五界面功能+写口零回归 | ⚠️ | forge_workbench_test 承载类同迁(diff 逐 hunk 核实:仅 load 目标 camp_overlay_view→forge_view+`_forge.` 前缀扁平化,断言判据零改)实跑 12 项绿;ui_click 树购买链(出账/持久/Fork 切态持久)在役;注记:无〔营地图批 V4〕尾缀(见 §6 缺漏 2)——tree/fork 链断言带〔句柄子容器批 V1〕尾缀(卡⑥ 波先行改指所留) |
|
||||
| V5 生涯只读契约 | ✅ | ui_struct :89-106 笔记本真实路由(object_pressed notebook→_open_career)+三契约(只读巡检/零动效/零跳转)复跑+营地语境关生涯不回开始层双向断言;〔营地图批 V5〕尾缀两处在位 |
|
||||
| V6 图鉴挂起态 | ⚠️ | ui_click :174-183 布告板→codex 开+锁文「结构位预留」锚定+STONE_DEEP 取色——在役实跑;注记:无〔营地图批 V6〕尾缀(尾缀=〔句柄子容器批 V1〕);「零激活禁行+非禁用态」判据中非禁用态由 codex 可开承载、零激活无独立断言面(占位壳本无内容可钉——新断言绑不上属卡面「静态判据+注记申报」情形,报告未单列注记) |
|
||||
| V7 篝火动线 | ⚠️ | screen_flow :117-127 回退段(object_pressed campfire→confirmed→屏级互斥复归菜单)+shot_probe 收闭改接同动线——在役实跑;注记:无〔营地图批 V7〕尾缀(=〔句柄子容器批 V1〕,同 V4 成因) |
|
||||
| V8 顶栏位+刷新单口 | ✅ | ui_struct :75-77 HudBandSlot 结构断言(48px)带〔营地图批 V8〕;camp_refresh_bus 新件+presenter 八写口尾全改路由(diff 逐处核实 8 处 refresh_bus.refresh);on_camp_attune_toggle 口尾保持只刷不调 _refresh_presentations(契约 4「禁顺手统一」核实);bus 目标册=四子界面+HUD 栏(main_ui_build 装配) |
|
||||
| V9 反参照纪律 | ⚠️ | ui_struct :78-83 场景根零 Button 断言带〔营地图批 V9〕;注记:扫面=get_children 直接子层非递归(报告称「子树」)——HudBandSlot 在扫面内,嵌套 Button 不计(新断言范围注记,见 §6 记档 4) |
|
||||
| V10 美术约束 | ✅(如实「部分」) | ui_contrast 令牌段审计自跑绿(36 对测点全绿,与报告读数一致);A 段色数判据互斥红=L3 呈报在案(§6 记档 1)——报告「部分」判定诚实 |
|
||||
| V11 目检登记 | ✅ | 实现报告 §6 五条目检清单在案(登记不阻塞) |
|
||||
| V12 探针迁移留痕 | ✅ | 报告 §8 迁移表与 diff 逐位点对上(审计抽样七个位点全核实) |
|
||||
| V13 反哺点清单 | ✅ | reports/iter47-反哺点-2026-11-01.md 12 条逐条有落点(注:#12 事实性偏差见 §6 记档 2) |
|
||||
| V14 layout 数据件校验 | ✅ | camp_scene_layout_test.gd 11 断言实跑全绿(详见 §2);manifest 已登记 run_all.gd(+1 行) |
|
||||
| B1 组合根接线总改造 | ⚠️ | main.gd/main_overlay_flow/confirm_dialog 三钮编排与篝火动线 diff 全核实(gate/gate_new/campfire_leave 三消费方+cancelled 零写口);判据承载=V3 断言+screen_flow 回退段;注记:无〔营地图批 B1〕尾缀 |
|
||||
| B2 CampOverlay 退役+四界面抽出 | ✅ | camp_overlay_view.gd 整件删除(655 行)+四 view+camp_sub_view_base 抽出、全仓零残留(S12 旧件零残留闸绿+grep 核实);承载迁移断言零改实跑绿 |
|
||||
|
||||
### 1.3 卡⑤ §4(13 行全查)
|
||||
|
||||
| 行 | 判定 | 依据 |
|
||||
|---|---|---|
|
||||
| V1 Esc 唤起四态 | ✅ | screen_flow `_step_camp_esc_and_hud` 十断言全带〔ESC批 V1〕——push_input 真实键盘合成路径(非直呼 on_esc_input)、根态唤起/再按关窗/echo 零响应/子界面开态无反馈/设置开态不误关/关设置回浮窗/boot 菜单不唤起;ui_struct Esc 结构段另证唤起与再按关窗 |
|
||||
| V2 浮窗三项 | ✅ | ui_struct :124-126 `_buttons` 递归计数恰三带〔ESC批 V2〕;camp_esc_overlay 三钮+danger_button 源码核实 |
|
||||
| V3 返回主菜单 | ✅ | screen_flow :284-287 return_requested 真实信号→四可见性断言(浮窗自收/营地隐/选档收/菜单显)带〔ESC批 V3〕 |
|
||||
| V4 设置双路径 | ✅ | screen_flow :270-283 设置开→浮窗恒 visible(遮挡非属性隐藏)+关→回浮窗+start_overlay 恒假反向断言带〔ESC批 V4〕;语境分支谓词=camp_esc_overlay.visible(main_overlay_flow diff 核实,与契约 3 一致) |
|
||||
| V5 退出项 | ❌ | **缺漏 1(详见 §6)**——卡面验收测试=「ui_struct_probe 退出钮接线结构断言〔ESC批 V5〕」,全仓 grep 零该断言;报告改称「连接链断言(main_ui_build 接线在位)/静态判据」但静态断言未落码 |
|
||||
| V6 HUD 余额 | ✅ | screen_flow :263-267 隔离档直写 _add_keystone(9999)→显式触发 refresh_bus.refresh→badge 文本「钥石 9999」带〔ESC批 V6〕——单口取值+显式触发才变(契约 5 时机③) |
|
||||
| V7 零动效 | ⚠️ | ui_struct :127-132 双件源码令牌扫描+子树 AnimationPlayer 计数带〔ESC批 V7〕;注记:源码扫描无长度非空守卫(报告称「扫描面>0 非空前置」名实差——见 §6 记档 3) |
|
||||
| V8 层序与遮挡 | ✅ | 登记册逐点名(_modal_registry_contract)+system_test V1c 树序链含 camp_esc_overlay 新槽+badge 零波及断言带〔ESC批 V8〕;LAYER_CONTRACT 槽位=codex_view 与 settings_overlay 之间(契约 3 栈语义正解核实) |
|
||||
| V9 目检登记 | ✅ | 实现报告 §6 第 4 条在案 |
|
||||
| V10 反哺点清单 | ✅ | 反哺点清单 #6/#8/#1 在案有落点 |
|
||||
| B1 Esc 接线与浮窗 | ⚠️ | main._unhandled_input(KEY_ESCAPE+physical+pressed 非 echo)→MainEscFlow 四态路由 diff 全核实、判据单点=登记册现开集合;判据承载=V1/V2/V7/V8 断言;注记:无〔ESC批 B1〕尾缀 |
|
||||
| B2 HUD 落位与刷新 | ⚠️ | camp_hud_band 新件/set_hud_band 填值/bus 挂册 diff 全核实;判据承载=V6 断言;注记:无〔ESC批 B2〕尾缀 |
|
||||
| B3 设置语境分支与三出口 | ⚠️ | main_esc_flow 三出口+main_overlay_flow 语境分支 diff 全核实;判据承载=V3/V4 断言;注记:无〔ESC批 B3〕尾缀 |
|
||||
|
||||
## 2. 判据绑定面抽查(重点四项+一般项)
|
||||
|
||||
| 抽查项 | 判定 | 核实内容 |
|
||||
|---|---|---|
|
||||
| 卡④ V14(11 断言) | ✅ | 实跑 11 项全 PASS;断言全部为表内关系谓词——两两不相交(Rect2.intersects)、不越画面/不侵顶栏(y≥hud_band_h 表字段)、篝火只取下半(命中贴贴图底且严格小于贴图)、门扇对齐 gate_gap 表字段、布告板挂围墙带(与 wall 贴图带关系谓词)、层数=10+bg/wall 占 z0/z1、ConfigDB 装载链(load_tables fail-fast 钩子+is_ready);**零坐标字面量**核实(唯一常量=七键名与层数 10,非坐标;(520,760) 等值全部取自表字段输出) |
|
||||
| 卡④ V3(大门两分支段) | ✅ | 七断言从真实路由进入(object_pressed gate 信号=卡面钉定发射面);cancelled 写口零调用以「余额读数恒等+营地根 visible+is_adventure 恒假」三重可观测面钉死;confirmed 后 is_adventure 翻转+营地隐;三出口经 set_breakpoint 注入(同开场行③先例,非伪造红——注入的是前置数据非破坏断言) |
|
||||
| 卡⑤ V1(Esc 步) | ✅ | push_input(ev, true) 真实键盘合成路径(不更新 Input 单例动作态——与契约 9 禁轮询互证);echo=true 合成零响应;设置开态再按 Esc 浮窗不误关(契约 9① 先于②的分支序);关设置回浮窗+禁弹主菜单反向断言;boot 菜单语境不唤起(契约 9④) |
|
||||
| 卡⑥ V3(计数=14) | ✅ | 审计人工 grep 计数=14 与报告留痕一致;闸面(gdstyle 零 NEW)自跑绿;卡面观测面局限申报如实 |
|
||||
| 一般:V2 七物件分布 | ✅ | 六探针分散驱动逐一核实(§1.2 V2 行) |
|
||||
| 一般:卡⑤ V2/V7/V8 | ✅/⚠️ | 三钮恰三/零动效/登记序——V7 非空前置注记(§6 记档 3) |
|
||||
| 加跑:ui_contrast 探针 | ✅ | 审计自跑 PASS「36 对测点全绿」——与报告 V10「令牌 36 对绿」读数一致 |
|
||||
|
||||
## 3. 测试削弱核对(diff 全量删改断言逐处对卡)
|
||||
|
||||
**授权项清单(卡面在案,核对通过)**:
|
||||
|
||||
| # | 削弱处 | 授权依据 | 核实 |
|
||||
|---|---|---|---|
|
||||
| 1 | theme_projection_test 等高源码断言 3 条注销(min 高 336/SIZE_FILL×4/SHRINK_CENTER≥3) | 卡④ §7 削弱申报表行 1+F8 位点(:710-717,CampOverlay 四柜台形态退役断言失去对象) | ✅ |
|
||||
| 2 | theme_projection_test `_camp_recent_projection` 最近冒险投影段注销 | 卡④ §10 Q4=甲(2026-10-18 用户裁:最近冒险条带随四柜台拆解撤下)+F13——申报表未单列成行,报告 §8 迁移表已归并申报(「卡面削弱申报授权」句);授权链=卡面用户裁定,认 | ✅(注记:申报落点在 Q4+§8,非削弱表独立行) |
|
||||
| 3 | ui_contrast 四行注销(头带两行/铭牌副题/最近冒险行,43→39 对) | 卡④ §7 削弱申报表行 2「核签后段留或随注销」——核签事实:CAMP_HEADER_BAND 新营地域界面零消费(grep 核实,新界面只消费 ROW_PLATE/PANEL_DATA)、最近冒险行随 Q4 撤 | ✅ |
|
||||
| 4 | ui_click 断点确认段改走大门(卡② 移交接收) | 申报表行 3 | ✅(V3 落位) |
|
||||
| 5 | ui_probe 深员改指/screen_flow 营地键与回退段改指/shot_probe 收闭段改篝火/生涯段驱动改接 | 申报表行 4/5/6/7 | ✅(diff 逐处核实为改指非删除) |
|
||||
| 6 | forge_workbench_test 承载类同迁 | 申报表行 8「断言零改只换承载类」 | ✅(12 项判据逐字未动,仅 `_forge.` 前缀与 load 目标) |
|
||||
|
||||
**未授权项清单:零。** 全 diff 删除断言行 31 处逐条核对均有同判据替换行(探针断言计数 56 增:31 删;shot_probe 21=21、system_test 51=51、ui_contrast 结构断言 7=7、forge_workbench 12=12);无放宽阈值、无缩小在役扫描面、无真实入口换下层直调(object_pressed 信号发射面=卡面 V2 明文钉定,不属直调削弱)。卡⑤「零削弱申报」成立。
|
||||
|
||||
## 4. 内部字段直查受控例外核对
|
||||
|
||||
| 直查行 | 依据 | 判定 |
|
||||
|---|---|---|
|
||||
| ui_probe camp.tree._balance_label/_tree_buy_btns、camp.treasure._workshop_rows;ui_click treasure._workshop_holder | 卡④ §7 削弱申报表行 4(深员改指新界面成员——历史既有口径) | ✅ |
|
||||
| screen_flow band._badge.value_label.text | 卡⑤ §7 V6 判据绑定面明文钉 value_label.text(badge 工厂约定单点) | ✅ |
|
||||
| ui_click :694/:717 confirm_dialog._secondary_btn.visible | 本批新增成员(三钮化即被测改动本体)、两/三出口形态唯一直观观测面、无公开访问器 | ⚠️(对照表未注明直查理由——建议随缺漏补差补一句申报,不构成判红项) |
|
||||
| main_esc_flow :31 读 flow.stack._list(产品码跨类私有读) | 卡⑤ 契约 9 判据单点=ModalStack 登记册现开集合 sanctioned 读法(ModalStack 无公开现开集合访问器;代码注记自陈) | ✅ |
|
||||
| screen_flow HUD 段 main._camp_pres.views.refresh_bus 直呼 | 卡⑤ 契约 5 时机③「夹具直写后探针显式触发」授权 | ✅ |
|
||||
|
||||
## 5. 自跑抽查回执(worktree 内实跑;六条全绿)
|
||||
|
||||
| # | 命令 | 退出码 | 关键行 |
|
||||
|---|---|---|---|
|
||||
| 1 | `python tools/run_system_test.py` | 0 | 「69 项检查,0 项失败」「PASS(退出码/摘要失败数/脚本错误扫描 三闸全过)」 |
|
||||
| 2 | `godot --headless --path game --script res://tests/unit/presentation/camp_scene_layout_test.gd`(V14) | 0 | 11 条 [PASS] 逐条在案+「all green (11 checks)」 |
|
||||
| 3 | `godot --headless --path game --script res://tests/unit/presentation/forge_workbench_test.gd`(承载类迁移) | 0 | 12 条 [PASS]+「all green (12 checks)」——断言零改实证 |
|
||||
| 4 | `godot --headless --path game res://probe/screen_flow_probe.tscn`(含 Esc 段) | 0 | 「PASS — 六步主流程+回退路径全绿」(_ready 序含 `_step_camp_esc_and_hud`,Esc 段红即 FAIL) |
|
||||
| 5 | `bash game/tests/static_scan.sh`(S13 基线) | 0 | 「S13 ok」「基线外新违规 0 条,较登记值恶化 0 条」 |
|
||||
| 6(加跑) | `godot --headless --path game res://probe/ui_contrast_probe.tscn` | 0 | 「PASS — 对比度 36 对测点全绿」(与报告 V10 读数一致) |
|
||||
|
||||
render_gate 按审计范围禁跑(05 红=L3 呈报在案,见 §6 记档 1);MC 零跑(零数值零行为,符合卡面闸口径)。
|
||||
|
||||
## 6. 总结论
|
||||
|
||||
**不通过(限项)——缺漏 1 项+轻微缺漏 1 项+记档核对 4 项;缺漏 1 补齐并复核后可转通过。** 除下列缺漏外,三卡对照表、判据绑定面、削弱申报、直查例外、自跑抽查全部通过;无未授权测试削弱。
|
||||
|
||||
### 缺漏清单
|
||||
|
||||
1. **卡⑤ V5 退出项验收断言缺位**(对照表 ❌ 行)。卡面 §7 V5 验收测试=「ui_struct_probe.gd:_ready(退出钮接线结构断言)〔ESC批 V5〕」,判据绑定面=「钮 pressed 连接目标=_on_quit→_quit_game(绑信号连接与调用链)」;实际全仓 grep 零该断言(quit_requested/_on_esc_quit 连接链无任何落码检查),〔ESC批 V5〕尾缀零命中。报告对照表该行改称「连接链断言(main_ui_build 接线在位)」——接线代码在位属实(main_ui_build :242),但「静态判据」并未落码,「绿」的实质=装配期 connect 不报错的间接承载,不满足卡面「绑信号连接」的断言要求,对照表该行名不副实。卡面已申报 headless 不可真退的观测面局限并给出可行静态形态。**补法(最窄单元)**:ui_struct Esc 结构段加一条静态断言(如 quit_requested 连接表非空且连接目标=_on_esc_quit)+〔ESC批 V5〕尾缀,孤立验证后并套件复跑。
|
||||
2. **(轻微)尾缀覆盖缺口**。卡④ V4/V6/V7/B1/B2 与卡⑤ B1/B2/B3 无卡面 §7 规定的〔营地图批 Vn/Bn〕〔ESC批 Bn〕尾缀(grep 零命中);对应验证本体全部在役且当轮实跑(V4=forge_workbench 12 项+ui_click 购买链;V6=ui_click 灰字段;V7=screen_flow 回退段;B 行=对应 V 行断言承载),缺的是卡面「断言尾缀与卡片清单互相 grep」的对账形式。成因=卡⑥ 波先行改指保留〔句柄子容器批 V1〕尾缀+V4 受「断言零改」申报约束(补尾缀即违反零改,可申报豁免)。**补法**:V6/V7/B 行断言消息文本可在后续批补尾缀;V4 记录豁免依据即可。
|
||||
|
||||
### 记档核对项(在案事项复核,不算缺漏)
|
||||
|
||||
1. **05 渲染闸红(shot_checker A 段色数判据互斥)**=L3 呈报文件在盘(reports/停机呈报-shotchecker色数判据互斥-2026-11-01.md),但该文件 git **未跟踪**(?? 状态)——合回合并不携带至 master,归档链注意;合回前须用户裁决读法 A/B+闸重跑(实现报告 §7/反哺点 #10 已登记)。
|
||||
2. **反哺点 #12 事实性偏差**:称 CAMP_HEADER_BAND「现役消费面=ui_contrast 表注销四行后零消费」——实测 keystone_badge 工厂(ui_kit.gd :629-631)在役消费、新 CampHudBand 经该工厂消费、level_map_screen/shop_overlay_view 亦有消费;令牌退役评估前提失实,文档批裁决前须订正(ui_contrast 删行授权不受影响——删的是营地域头带/铭牌/条带测点对,非令牌本身)。
|
||||
3. **卡⑤ V7 名实差**:报告称源码扫描「扫描面>0 非空前置」——ui_struct :127-129 实现无长度非空守卫(FileAccess 读失败=空串静默通过);子树扫描面在浮窗实例在座前提下非空成立。建议后续批补一行长度守卫。
|
||||
4. **卡④ V9 扫面注记**:反参照断言扫面=场景根直接子层(get_children 非递归),报告称「场景根子树」;HudBandSlot 在扫面内,嵌套 Button 不计。新立断言无削弱对象,范围注记即可。
|
||||
|
||||
### 审计范围声明
|
||||
|
||||
本审计执行上列六条最窄抽查与静态读数+全量 diff/卡面/报告逐行核对;未跑 render_gate(在案红待裁)、未跑 MC、未跑 ui_probe/ui_click/ui_struct/shot_probe 单件直跑(其营地段断言经 system_test+screen_flow_probe+ui_contrast 实跑间接覆盖,且逐行静态核实);审计过程零产品码/测试码改动。
|
||||
@@ -0,0 +1,55 @@
|
||||
# 停机呈报:棋盘格边长口径收口批(迭代 46 卡C)——L3 #2 实板可读面不达标
|
||||
|
||||
> 日期:2026-10-31 · 呈报人:迭代 46 实现会话(iter 技能执行中)
|
||||
> 接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记)」
|
||||
> 性质:实现段零裁断——需求卡片预设停机点触发(需求卡片-棋盘格边长口径收口批-2026-10-30.md §9 异常分级 L3 第 2 条),停该卡呈报退回上游,等用户裁决。
|
||||
|
||||
## 一、触发了什么
|
||||
|
||||
需求卡片 §7.3 T2 规定探针新增「实板可用面积下 13/14 板格边长推定」断言:以探针实读 `board_view.size` 代入算式单点 `BoardView._cell_for(n, size)`,`< 30px`(battle.md 可读下限)即记问题。§9 L3 第 2 条预设:实板可用面积代入 13/14 后 < 30 = 产品面问题,**停卡呈报,禁调低下限、禁换样本凑绿**。
|
||||
|
||||
当轮真实跑批实测(worktree iter46,种子 42,两档视口):
|
||||
|
||||
| 视口档 | board_view.size(实板可用面积) | L1 实测格边 | 13×13 推定 | 14×14 推定 |
|
||||
|---|---|---|---|---|
|
||||
| 基座 1280×800 | (1022.0, 454.0) | 40px ✓ | 30px(恰达标) | **28px < 30 ✗** |
|
||||
| 16:9 1920×1080 | (1662.0, 734.0) | 46px(钳制顶格) | 46px ✓ | 46px ✓ |
|
||||
|
||||
代入算式复核(1280 档):`min(1022−12−8, 454−32) = 422`;`(422 − 13×2) / 14 = 28.28 → floor 28` → 钳制 [26,46] 内 → 28px。**14×14 板在基座档的实板格边长低于 battle.md「可读下限 30px」**。
|
||||
|
||||
## 二、事实定性(这不是本批制造的回归)
|
||||
|
||||
1. 算式逐位未动(卡C 行为零变化,单元四档冒烟 74 checks 全绿——结构钳制 [26,46] 面全过)。
|
||||
2. 28px 是**存量事实**:现行生产公式对 14×14 板(board_gen.tres:L3 depth 9–13)在基座档一向算出 28px;本批新增的推定断言只是首次把它量出来。
|
||||
3. battle.md 算例表的「14×14 → 30px」按「可用高 486px」推算,而实机 D 列结构给出 454px——卡片 §2.8 已登记该偏差(「实机可用面积与算例不同源」),本读数即该偏差的实测落点。
|
||||
4. 同样数据下 13×13=30 恰好达标,仅 14×14 一档越线下。
|
||||
|
||||
## 三、分歧点与两种以上读法
|
||||
|
||||
**分歧点**:battle.md「可读下限 30px」(:144)的适用域——是否按视口档/板型分级适用。
|
||||
|
||||
- **读法甲(全域硬下限)**:30px 对一切视口档×板型组合生效 → 14×14 板在基座档不达标=产品缺陷,需要设计动作(改布局可用面积/改板型表/缩 14×14 适用域)。
|
||||
- **读法乙(按档分级)**:30px 下限以 1920 档为守卫面(推定断言只判 1080 档),基座档按实测算例记录(battle.md 算例本标注「随布局与板型变化,不是契约值」)→ 推定断言判据域改判 1080 档,基座档记录实测 28px 不下断言。
|
||||
- 两读法都属设计裁决(owner 文档 battle.md 的判据域修订),**实现段无权自裁**——读法乙虽不是「调低下限」(30 数值不动),但改变断言的适用域,同样超出卡片明列范围。
|
||||
|
||||
## 四、建议案(供裁决,禁代裁)
|
||||
|
||||
1. **建议主案=拆批**:迭代 46 以卡A(换图系七件批)+卡B(名字行提档与地台色合并批)先行合回——两卡已收口、代码域闸全绿(回执 45b53d68,commit 45b53d68 后另有 S9/S7 修正与卡B 提交);卡C 停摆退回写卡段,待 30px 下限适用域裁决后重立卡编入后续迭代。拆批合回前置=roadmap 迭代 46 行与待办清单条目状态换代(归文档批,本批禁写)。
|
||||
2. **裁决后的卡C 重写方向**(按所选读法):
|
||||
- 读法甲:重写卡范围收窄为「算式单点+门槛 36→30+四档冒烟+13 板推定」,14×14 不达标另立缺陷条目走设计段(布局或板型表动作)。
|
||||
- 读法乙:卡C 原范围保留,推定断言判据域改「1080 档 ≥30」+基座档实测记录句;battle.md 反哺点补「下限适用域分级」条。
|
||||
3. **卡C 已完成的可保留面**(worktree iter46 未提交改动,停摆卡不提交——随裁决处置):`board_view.gd` 算式单点提取 `_cell_for`(等价性已证:board_gen_reward_test 62→74 checks 全绿)、`layout_probe.gd` 门槛 36→30 与推定断言、`board_gen_reward_test.gd` 四档扩样。三件改动行为零变化,裁决后可直接复用。
|
||||
|
||||
## 五、待办清单条目状态回退+缺口补记(拟稿——落笔归上游段/用户)
|
||||
|
||||
条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交)」:
|
||||
|
||||
- **状态**(现「已写卡+已编迭代 46」)→ 回退拟稿:`已写卡(卡C 停摆退回——L3 #2 实板可读面不达标)`。
|
||||
- **缺口补记拟稿**:`缺口:14×14 板基座档(1280×800)实板格边 28px 低于 battle.md 可读下限 30px 的适用域待设计裁决——1280 档实板可用面积 (1022,454)→28px、1080 档 (1662,734)→46px;13×13 两档均达标(30/46)。裁决点=读法甲(全域硬下限→产品缺陷走设计动作)vs 读法乙(下限按视口档分级→断言判据域改 1080 档)。裁决后重写卡C(算式单点/门槛对齐/四档冒烟三件已验可复用)。`
|
||||
|
||||
## 六、本批实况速览
|
||||
|
||||
- 卡A 换图系双基座七件批:**收口**(T0–T8 全落,--code 闸 7 闸全绿)。
|
||||
- 卡B 站桩名字行提档与地台色合并批:**收口**(26 checks 绿,golden 三张 0.00% 留痕)。
|
||||
- 卡C 棋盘格边长口径收口批:**停摆**(T0 完成、T1–T3 改动已验未提交、T4 未走)——本呈报。
|
||||
- 迭代合并:**阻塞**,等本呈报裁决(拆批合回 A+B,或卡C 闭合后整体合回)。
|
||||
@@ -0,0 +1,50 @@
|
||||
# 双审复核 · 立绘显示档批(grilling + godot-master,2026-11-01)
|
||||
|
||||
> **性质**:卡片 v1.0 的独立复核产出物(req-card 纪律「grilling 复核门」+迭代 47 双审先例)。两个未参与撰写的会话各自只读复核,本报告记发现与处置去向。
|
||||
> **被审对象**:`reports/需求卡片-立绘显示档批-2026-11-01.md` v1.0。
|
||||
> **总判**:方案本体成立(引擎语义、乙类条款、闸纪律全部咬合),卡片**改后可开工**——两条阻断均为「补执行步骤」级,不动设计。
|
||||
|
||||
---
|
||||
|
||||
## 一、阻断(两条,双审同指)
|
||||
|
||||
| # | 发现 | 证据 | 处置 |
|
||||
|---|---|---|---|
|
||||
| B1 | **原稿输入面在实现 worktree 必然缺位,且管线对缺件静默零产出**:`artwork/` 整目录不入 git,`sync_worktree_assets.sh` 只同步 `game/assets/art`+`.import`,不同步 artwork;`pixelize_candidates.py` 缺件走 `isfile` 跳过,exit 0、零文件、零报错 | `tools/sync_worktree_assets.sh:26-34`;`tools/pixelize_candidates.py:474-478,507-508` | 卡 v1.1 T1 补「显式传主区原稿根路径」+「产物计数自证 = 9 件,不足即停」 |
|
||||
| B2 | **素材落递主工作区无收口步,合回后验收有洞**:素材不入 git、worktree 合回即删;本地 CI 在主区跑,主区仍是旧 46×48 稿,只有 V16「主角源高 ≥92」会报警,敌侧「原稿分辨率落位」在册闸件 `standee_asset_check` 不在闸清单,等于无人看守 | `game/tests/run_all.gd:191`;`tools/run_all_gates.sh`(grep 无 standee_asset_check);盘上主区素材实测 | 卡 v1.1 ①V13 逐件加钉源高=原稿表值(旧降采样稿必红);②T3 补「9 件落递主区再触发 CI」收口步 |
|
||||
|
||||
---
|
||||
|
||||
## 二、建议(已并入卡 v1.1)
|
||||
|
||||
| # | 发现 | 出处 | 卡片处置 |
|
||||
|---|---|---|---|
|
||||
| S1 | 样张脚本 A4 硬锁 `PORTRAIT_SRC.size==(46,48)`,T1 换代必红;另有 `:1501` 强制 96×96 方框 mockup | `tools/render_sample_sheet_v3.py:1135-1139,1501` | 列入 F11 现状与 T3 随动步(显式解冻重校) |
|
||||
| S2 | T1 未明写主角源从 `*_1x.png` 切 `*_2x.png` | `tools/pixelize_candidates.py:476,481` | T1 补独立步 |
|
||||
| S3 | `PORTRAIT_DISPLAY_H.get(key, 96.0)` 兜底字面量 = 档值第二处散落,与契约 2/6 自相矛盾 | 卡 v1.0 §7.3 | 改「查表必中+键集覆盖自检」,禁静默兜底 |
|
||||
| S4 | 灰矮人 126→96 是全批唯一 <0.8 档(0.76× 真降档,NEAREST 抽行约每 4 行断 1px 描边),不在已接受清单 | 双审同指 | 补进契约 5 已接受项+目检①点名(头号回退候选) |
|
||||
| S5 | 关卡地图头像由 ≈1× 变 0.52× 降采样,是全消费面降级最狠一处 | `level_map_screen.gd:176-190` | §4 不做面点名+目检③专列 |
|
||||
| S6 | 主角三态宽度不一(88/92/88)且 `apply_hero_state` 换纹理不重摆,态切换有约 3–6px 横跳(现行 2px 的放大版) | `standee_motion.gd:85-86` | §4 明列不做(重摆=行为改动,禁实现批顺手加)+目检清单登记 |
|
||||
| S7 | T1/T3 闸件口径切换时点三处互斥(T1 说「T2 未就绪允许红」、闸件排在 T3、§15 说已提前到 T1) | 卡 v1.0 §7.2/§7.4/§15 | 统一:闸件口径改造并入 T1(素材与闸件同批),删矛盾句 |
|
||||
| S8 | 注释随动面漏 `standee_motion.gd:38-39,59,65`(反向引用已不存在的「禁非整数缩放」禁令)与 `level_map_screen.gd:176` | 双审同指 | T2 补点名 |
|
||||
| S9 | 「两表同值」观测面局限申报说重了:grilling 判 Godot editor 版 `FileAccess` 可读 `../tools/`,godot-master 判无稳定通路——两审相左 | 双审 §6/§O5 | 卡 v1.1 改「尝试真断言(读 `../tools/pixelize_candidates.py` 逐键对账),读不到走 L2 记录退回双端字面钉值+审计对账」,禁把争议当定论 |
|
||||
| S10 | §14 所核 `gdstyle_baseline.txt` 无该测试键,且 gdstyle 扫描面只含 `game/core|data|presentation`(tests 不在面) | `tools/gdstyle_gate.py:49` | §14 改口径:测试文件不在扫描面,只核 `standee_motion.gd` 在面件 |
|
||||
| S11 | 卡片头行引用待办状态「待写卡」已过时;§7 交叉引用「见 §9 行 V1–V4」应为 §8 | 卡 v1.0 | 就地修正 |
|
||||
|
||||
## 三、观察(不改卡,记录在案)
|
||||
|
||||
- **几何实测**:立绘落位只在换纹理拍执行、按缺省盒高 96 算——大档顶 y=−2、中档 20、小档 44;溢出 2px < VBox 间距 4px,**恒不碰顶部轮次指示**(卡 L3#3 的「碰邻位」实际难触发,仍留作停机条款);立绘链上无 ClipControl,不被裁;意图徽标 y=86 在中/大档为「压胸」,符合圣经③;大档脚底越盒底由明细带面板遮住(现行卓尔/石君王已如此,本批食人魔加深 40px)。
|
||||
- **1920 档**:三档是 1280 基准壳坐标绝对高,`stretch=canvas_items`+aspect keep 下整壳 ×1.35 等比,构图恒等,无需新条款。附带发现:`project.godot` 未显式声明 `window/stretch/aspect`,依赖引擎缺省——**与本批无关,建议另立工程批钉死**。
|
||||
- **未登记 enemy_id**(如 hobgoblin 缺口行)无立绘 → `load_tex` 返回 null → 字形回退,不进 `_place_portrait`,显示档不涉,非待裁点。
|
||||
- **素材侧健康度**:九件原稿色数 30–32、alpha 二值、底缘不透明、敌侧暖白描边 68.4–98.9%(阈值 40% 全过)——T3「其余四断言不变」可保绿。
|
||||
- **事实核对**:F1–F15 逐条复核通过(F3 的 :197 实为 :198、F10 断言②止于 :100,属微差);F7 降采样等值由 grilling 独立重跑四件字节级复现。
|
||||
- **浮点判据**:0.99/1.57 档回程有 ULP 级残差,V13/V15 须 `is_equal_approx`,禁裸 `==`。
|
||||
|
||||
---
|
||||
|
||||
## 四、呈用户裁决项(卡片不动,等发令)
|
||||
|
||||
1. **是否把 `standee_asset_check` 编进代码域闸**(`run_all_gates.sh` 闸件增减属工程改动,另批走)。
|
||||
2. **灰矮人 0.76× 与关卡地图头像 0.52× 两处降采样**:本卡按「已接受+目检」处理;若实机判不可受,回退路径只有「灰矮人改归中档」或「地图头像出 48 专件」,均须另裁。
|
||||
3. **主角态切换横跳**(S6):本卡不做重摆;若要修,另立小批。
|
||||
4. **`project.godot` 显式声明 stretch/aspect**(观察项):另立工程批。
|
||||
@@ -0,0 +1,60 @@
|
||||
# 复核报告:迭代 47 双卡 grilling+godot-master 双审(2026-11-01)
|
||||
|
||||
- **接收方**:`reports/需求卡片-营地场景图新建批-2026-10-18.md`(卡④,v3.0→v3.1)+ `reports/需求卡片-ESC浮窗与营地顶栏批-2026-10-18.md`(卡⑤,v1.3→v1.4)。本报告是双审过程稿,裁断与订正已全部吸收进两卡,批末收尾按吸收即死退役。
|
||||
- **审查构成**:四个独立子代理——卡④ grilling/卡⑤ grilling/卡④ godot-master/卡⑤ godot-master(全程只读、未落盘、未跑测试)。
|
||||
- **发起**:用户令(开工前审查:零待裁点、零阻塞点、零灰色地带、符合 Godot 设计规范)。
|
||||
- **裁决**:用户 2026-11-01 裁「都可以按照推荐来」——五裁点照推荐采纳、P3/P5 销号口径采纳、卡⑤ v1.4 补句、卡④ 漂移就地订正+Godot 风险项落卡。
|
||||
|
||||
## 一、双审结论
|
||||
|
||||
| 卡 | grilling | godot-master |
|
||||
|---|---|---|
|
||||
| 卡④ | 有条件可开工——卡内设计待裁点确为零、重立凭据属实、与卡⑤ 接口名实闭合;但 §0.3 四道开工门实证全部未销号(会卡 T1/T2/T5),另有成片现状事实漂移 | 基本符合有风险项——分层/信号/单点纪律全对齐、零硬违反;风险=1024×1024 源档降采样算式断裂+核心视觉不变量机器验收零覆盖 |
|
||||
| 卡⑤ | 有条件可开工——零真待裁点(Q1/Q2/Q3 留痕完整);但三处现行文本会指导出错实现(浮窗残留缺陷级/V4 判据与契约 3 矛盾/Esc 第四态未定义) | 基本符合有风险项——浮窗选型/层序/拆件与现役先例同构、零硬违反;风险=文件表漏列 ui_modals/main_ui_build 两必改件+Esc 第四态连带语境分支谓词二义 |
|
||||
|
||||
## 二、裁断与销号落点(2026-11-01 用户裁)
|
||||
|
||||
| 项 | 裁断 | 卡内落点 |
|
||||
|---|---|---|
|
||||
| camp-scene-prompts §5 五裁点 | ①帐篷 300×170 追认现值②门扇 240×220 追认③布告板 (845,268,130,140) 挂围墙带④描边奶油色追认⑤环境子板色值不前置(T1 量化后按 art-bible §4.1 两道门登记;T5 色计数改条件步) | 卡④ §0.3-3/§9-4/§10;反哺点(§5 落文归文档批) |
|
||||
| P3 点选反馈设计稿 | 销号口径=camp.md v2.2:35-37 四态规格即设计稿;名牌文案与可点区域视觉归 T2 域内 | 卡④ §0.2/§0.3-1 |
|
||||
| P5 GDD/art-bible 落文 | 销号口径=形态落文完成;实装回填归 T6 反哺点义务,非停机条件 | 卡④ §0.2/§0.3-2 |
|
||||
| T1 素材后处理发令 | /iter 开工令即含授权,不再两次发令 | 卡④ §0.3-4/T1 |
|
||||
| 1024×1024 档降采样 | 逐张登记换算式;1024 档非整数缩放显式特批登记(L2+实现报告申报局限),不回炉(追认已过审终稿优先) | 卡④ T1 |
|
||||
| Esc 第四态与接线 | 契约 9 四态优先序(判据单点=ModalStack 现开集合)+接线定稿=事件驱动 `_unhandled_input`+KEY_ESCAPE、禁 Input 单例轮询、不挂 ui_cancel、pressed 且非 echo | 卡⑤ 契约 9/T0/T1/§9-1 |
|
||||
| 设置语境分支谓词 | =`camp_esc_overlay.visible`(谁开的设置谁收),与 Esc 守卫谓词分立 | 卡⑤ 契约 3 |
|
||||
| 返回主菜单浮窗残留 | 动线首步 close 浮窗;V3 断言补浮窗 closed | 卡⑤ 契约 4/T3/V3 |
|
||||
| camp 截图排序约束死信 | 废止原句;按迭代 47 执行序=卡④ 整卡先、卡⑤ 后,卡⑤ T2 限 HUD 区显式更新兜底 | 卡⑤ §0/卡④ T0 |
|
||||
|
||||
## 三、双审独立发现、交叉确证的灰色地带(全部落卡)
|
||||
|
||||
1. Esc 第四态(设置开在浮窗之上再按 Esc)——两审独立发现 → 契约 9①。
|
||||
2. 返回主菜单后浮窗残留(缺陷级)——grilling 发现、godot-master 同域印证 → 契约 4。
|
||||
3. V4 判据与契约 3 自相矛盾(浮窗恒 visible 由栈语义保证,非属性隐藏)→ V4 改述。
|
||||
4. V6 badge 文本格式字面不等(带「钥石 」前缀)→ V6 改格式串比较。
|
||||
5. CampRefreshBus「Bus」命名 vs 全局事件总线否决史(architecture.md:353)→ 卡④ T3 头注自陈+卡⑤ 措辞「挂接/接收推送」。
|
||||
6. 文件表漏列 `ui_modals.gd`/`main_ui_build.gd`(S13 触面失察;ui_modals 类变量 14→15 恰贴上限)→ 两卡文件表与存量触面一查补列。
|
||||
7. 「卡④ T5 晚于卡⑤ T2」排序约束单侧声明且按执行序天然不可满足 → 死信改述(见 §二)。
|
||||
8. V7 零动效扫描面漏开合编排件 → 扫描面扩两新件;V5 补「禁对退出钮 pressed.emit」;V1 补冒险语境段与 echo 断言;「进档重读」触发主体钉组合根动线尾。
|
||||
|
||||
## 四、卡④ 成片事实漂移订正(T0 级,v3.1 就地订正)
|
||||
|
||||
`game/smoke/` 已退役(git 16c6f136)→ 探针全在 `game/probe/`;smoke_all.gd 已退役(git 957391e1)→ system_test.gd(V2 :67-68/V3g :520-541)+theme_projection_test.gd:710-717;`main.camp_overlay` 句柄已退役 → `main.ui.modals.camp_overlay`(ui_modals.gd:14);F3/F7 行号簇换 2026-11-01 实测;F6「断点弹窗现居主菜单」失实(main.gd:118-120 注释)——T4 承接;`_on_confirm_new_adventure` 已随卡②摘除;`UiKit.TEX_CAMP_*` 尚不存在(本批 ui_kit.gd 新增十键);`tools/gdstyle.sh` 不存在(→ static_scan.sh/gdstyle_gate.py);presenters/core 子目录路径订正;`design/gdd/06-待办清单.md` 已迁仓库根;契约 4 行号换实数(八处 :82/90/102/114/126/139/151/165=七口+on_camp_buy 双尾);终局落点 main.gd:703 → main_adventure_flow.gd:123-143。
|
||||
|
||||
## 五、卡⑤ F1–F10 抽验
|
||||
|
||||
10 条全抽:F1/F2/F3/F5/F6/F7/F8/F9/F10 属实;F4 消费点全对、行号三处系统性 ±1(keystone_badge :633→:632、modal_shell :487→:486、danger_button :700→:699)——已订正。卡外小漂:screen_flow_probe 营地旗标在 :54(:53 是菜单行)、待办清单五卡总条 :447 起——已订正。
|
||||
|
||||
## 六、Godot 规范审查补立(卡④)
|
||||
|
||||
- 节点选型钉死:Control 根+TextureRect 层+子节点序即 z 序;禁 CanvasLayer/z_index/Sprite2D 根(LAYER_CONTRACT 树序落位兼容)。
|
||||
- V1 扩判据:camp_scene_view 子节点序=layout 表 z 序逐层相等、纹理矩形与表逐值一致;新增 V14 layout 数据件校验(fail-fast 命中区几何)——核心视觉不变量机器验收面补齐。
|
||||
- T2 实现约束:浮动经 Tween 禁 `_process`;名牌 Label mouse_filter=IGNORE;禁用态去饱和经 ShaderMaterial;名牌画面内钳制;HUD 载体循 page_shell `PRESET_TOP_WIDE` 先例。
|
||||
- layout.tres 接 ConfigDB schema 类(先例 battle_layout.tres)+加载期 fail-fast 校验。
|
||||
|
||||
## 七、遗留与开工判定
|
||||
|
||||
- **零待裁点、零阻塞点**:卡④ §0.3 四道开工门全部裁断销号;卡⑤ 零真待裁点、开工按序随卡④ 代码波(迭代内排序,非跨迭代阻塞)。
|
||||
- 反哺点落文已完成(2026-11-01 本会话文档批,同批合回):camp-scene-prompts.md v1.3 §5 改已裁+art-bible.md v3.31 §6.2 待裁行改已裁;camp.md/art-bible 实装状态回填归迭代实现批 T6 义务。
|
||||
- 机检预算互斥裁决(2026-11-01 用户裁,停机呈报=iter47 worktree):实现段实测两卡平铺合计 ui_modals.gd 类变量 19>S13 上限 15——裁**最小改动**=营地六句柄落 `ui_camp_modals.gd` 子容器(新立卡⑥ 前置基建,迭代 47 执行序第一位;卡④ v3.2/卡⑤ v1.5 随批改口径,卡⑤ v1.4「14→15 恰贴」算术缺陷一并订正);全量分域句柄分家登记待办清单另立批。双审预算审为单卡口径、未做跨卡同文件累加核对,已在裁决中注记。
|
||||
- **判定:迭代 47 可开工**(卡⑥ → 卡④ → 卡⑤ 按序,`/iter` 发令即起 T0)。
|
||||
@@ -1,331 +1,145 @@
|
||||
# 提案:弃图刷新系专长流派(双基座七件)——预案 v0.4(2026-10-30)
|
||||
# 提案:弃图刷新系专长流派(双基座七件)——预案 v0.5(2026-10-31)
|
||||
|
||||
> **性质**:待办清单条目「封锁-蓄力」combo 设计素材的设计提案与 GDD 修改清单。双基座七件结构已经用户确认,修改清单已经用户审核并落入四份 GDD;本文按用户要求保留,供后续写需求卡片参考。代码与数值表尚未实现。
|
||||
> **性质**:换图系设计提案与 GDD 修改清单。双基座七件结构已经用户终审;本文保留为后续实现与验收的参考件,不随 GDD 落文退役。
|
||||
>
|
||||
> **需求来源**:用户 2026-10-30 明确设计方向——流派先有基座专长,从资源来源、动作成本或结算规则上改变基础行为;后续专长围绕新行为提供输出、生存与资源收益。只有刷新后的数值增长,不足以形成流派。
|
||||
> **核心裁定**:流派先由基座专长改变资源来源与刷新价格,再由后续专长让成功刷新直接形成输出、生存与 GP 资源收益。所有效果都改变玩家的刷新决策,不把旧图探索进度折算成清图奖励。
|
||||
>
|
||||
> **结构裁定**:用户指定设计 **2 件基座+2 件输出+2 件生存+1 件资源**,共 7 件;两件基座允许同时持有并叠加;弃图回收按旧图探索进度发清图奖励,不采用固定发一份。本提案结构待裁点已清零。
|
||||
>
|
||||
> **结论行**:「掘进储能」让挖格产 RAP,解决刷新资源从哪里来;「折价换图」让刷新 RAP 成本减半,解决同样资源能刷几次。两件基座叠加后形成完整换图引擎。后续五件让刷新分别产生群体直伤、下次攻击爆发、护盾、回血与按探索进度回收的清图奖励。拿到任一基座后,刷新不再只是坏图逃生;配上后续专长,玩家会为了输出、生存或资源主动规划换图。
|
||||
> **结论行**:矿脉储能让付费挖格产 RAP,省力周转把刷新成本减半;两件基座允许叠加。崩壁冲击与蓄力周转把刷新变成输出,石层缓冲把刷新变成当轮全局受伤减免,坑道小憩把多次刷新累计成当轮恢复,矿道红利每次成功刷新固定给 1 GP。
|
||||
|
||||
---
|
||||
|
||||
## 0. 七件总表
|
||||
|
||||
> 名称均为代号;数值与稀有度均为 provisional,不在本预案定稿。
|
||||
|
||||
| 层 | 代号 | 效果候选 | 它改变的决策 |
|
||||
| 层 | 显示名 | 效果 | 行为职责 |
|
||||
|---|---|---|---|
|
||||
| **基座** | **掘进储能** | 每次付费挖格后额外获得 RAP | 想刷新不再必须少挖停手;持续挖图也能蓄能 |
|
||||
| **基座** | **折价换图** | 弃图刷新的 RAP 成本减半 | 同样的储备支持更多刷新;部分挖格+部分停手也能维持换图 |
|
||||
| **输出** | **弃图轰击** | 成功刷新后,对全体存活敌人造成伤害 | 刷新本身成为进攻动作 |
|
||||
| **输出** | **换图蓄势** | 每次刷新攒层;下一次攻击附加全部层数后清零 | 连续换图可以蓄成一次单点爆发 |
|
||||
| **生存** | **换图庇护** | 成功刷新后获得护盾 | 刷新可以主动用来抵挡后续伤害 |
|
||||
| **生存** | **弃图休整** | 成功刷新后回复生命 | 玩家可以放弃当前图,换取一次恢复机会 |
|
||||
| **资源** | **弃图回收** | 成功刷新后,按旧图探索进度回收清图奖励 | 不必挖完最后几格;可以先投入,再主动弃图变现 |
|
||||
|
||||
**结构关系**:基座负责让主动刷新可持续;输出、生存、资源五件负责让主动刷新有明确目的。后续专长不需要设置「必须先拿基座」的硬前置,它们的联动由实际刷新频率自然形成。
|
||||
| 基座 | 矿脉储能 | 每次受理的付费挖格额外获得 RAP | 持续挖图也能为刷新蓄能 |
|
||||
| 基座 | 省力周转 | 弃图刷新的 RAP 成本减半 | 同一份 RAP 支撑更多刷新 |
|
||||
| 输出 | 崩壁冲击 | 成功刷新后,本轮战斗段末对全体存活敌造成固定伤害 | 刷新成为进攻动作 |
|
||||
| 输出 | 蓄力周转 | 每次成功刷新叠层,上限 3;本轮下一次主角攻击消费全部层数 | 连续刷新汇成单点爆发 |
|
||||
| 生存 | 石层缓冲 | 本轮只要成功刷新过,主角每次受到伤害固定减少 2 点;不叠加 | 刷新成为当轮防御姿态 |
|
||||
| 生存 | 坑道小憩 | 每次成功刷新累计 1 点恢复,上限 3;本轮授予段合计回血 | 用刷新换取恢复空间 |
|
||||
| 资源 | 矿道红利 | 每次成功刷新固定获得 1 GP | 刷新动作自身带少量金币收益 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 通用设计思想:先改变行为,再给新行为收益
|
||||
## 1. 设计原则
|
||||
|
||||
这一节同时作为后续修订 `奇物.md`「专长·设计原则」的建议文本。现在只记录在提案中,尚未写入 GDD。
|
||||
|
||||
### 1.1 基座专长的定义
|
||||
|
||||
**基座专长=让某个原本偶尔使用、主要用于止损的动作,变成可以持续使用的主要打法的专长。**
|
||||
|
||||
它至少改变下列一项:
|
||||
|
||||
1. **资源来源**:原来主要靠停手获得 RAP,现在挖格也能获得 RAP。
|
||||
2. **动作成本或频率**:原来一整轮停手才能刷新一次,现在同样储备可以支持更多刷新。
|
||||
3. **动作结算**:原来危险格只伤害玩家,现在也能重伤敌人,使主动踩雷成为进攻手段。
|
||||
4. **动作关系**:原来挖图与弃图争夺资源,现在挖图为弃图蓄能,两个动作组成循环。
|
||||
|
||||
成本减半虽然形式上仍是数值变化,但如果它让刷新从偶尔可用变成持续可用,也满足基座要求。**判断标准不是改了多少规则文本,而是玩家的实际操作是否改变。**
|
||||
|
||||
### 1.2 基座与后续专长的分工
|
||||
|
||||
| 层 | 职责 | 不合格形态 |
|
||||
|---|---|---|
|
||||
| **基座专长** | 建立新的资源循环或动作规则,让核心动作能持续使用 | 只在原动作后加少量攻击、护甲或资源,动作使用时机不变 |
|
||||
| **后续专长** | 让核心动作产生输出、生存或资源收益,扩展新打法 | 不依赖新行为、放进任何构筑都同样有效的泛用数值增长 |
|
||||
|
||||
后续专长仍可采用「当 X 时 Y」的触发器句式,也可以各自独立生效;不另设集件标签。它们与基座的联系来自实际玩法:没有基座时,刷新少、收益兑现慢;有基座后,同一效果成为主循环的一部分。
|
||||
|
||||
### 1.3 行为改变检验
|
||||
|
||||
新流派或新基座专长过准入时,必须回答三个问题:
|
||||
|
||||
1. **拿之前怎么玩,拿之后怎么玩?** 如果最优操作与决策时机基本不变,只是结算数字变大,就不是基座。
|
||||
2. **新行为为什么能持续?** 必须写清资源从哪里来、多久能做一次、什么限制防止无限循环。
|
||||
3. **后续专长为什么属于这里?** 它应当兑现或强化基座建立的行为,而不是替代基座。
|
||||
|
||||
以自爆流为例:没有相关专长时,玩家应躲开危险格;拿到能减少自身雷伤、同时让危险格重伤敌人的基座后,玩家会主动寻找危险格。后续专长再提供踩雷后的回复、护甲或追加伤害。**「本来不该点」变成「现在值得主动点」,才是行为改变。**
|
||||
1. **基座先改行为**:必须改变资源来源、动作成本、动作结算或动作关系,使偶尔止损的刷新成为可持续打法。
|
||||
2. **后续收益围绕新行为**:输出、生存、资源件只在成功刷新后兑现,不做放进任何构筑都成立的泛用增益。
|
||||
3. **双基座可叠加**:一件改 RAP 收入,一件改 RAP 支出;每轮刷新上限继续负责限频。
|
||||
4. **无专长基线不变**:七件只给持有者增加规则;全局经济、清图奖励与刷新基础语义不改。
|
||||
5. **临时生命值不做**:现有体系没有独立血量护盾,本流派不使用该概念;石层缓冲直接减少最终承伤。
|
||||
|
||||
---
|
||||
|
||||
## 2. 两件基座:分别改资源来源与动作价格
|
||||
## 2. 行为循环
|
||||
|
||||
### 2.1 掘进储能
|
||||
无专长时:
|
||||
|
||||
**效果候选**:每次付费挖格后,额外获得一定 RAP。安全格与危险格都按付费挖格计,不另判玩家意图。
|
||||
|
||||
**基础循环**:
|
||||
|
||||
```
|
||||
持续挖格并获得格面收益 ──同时积攒 RAP──▶ 主动弃图刷新
|
||||
▲ │
|
||||
└──────── 新图继续挖格 ◀───────────────┘
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[正常挖图] --> B[遇到坏图]
|
||||
B --> C[停手攒 RAP]
|
||||
C --> D[必要时刷新]
|
||||
D --> A
|
||||
```
|
||||
|
||||
**行为改变**:
|
||||
拿到两件基座后:
|
||||
|
||||
- 玩家可以一边挖图拿收益,一边准备下一次刷新,不再必须停手。
|
||||
- 每轮 AP 有限,因此新增 RAP 也有限;它扩大刷新机会,不提供无限刷新。
|
||||
- 单拿时,坏图更容易被换掉;配上任意后续专长得益后,主动刷新才有完整目的。
|
||||
|
||||
### 2.2 折价换图
|
||||
|
||||
**效果候选**:弃图刷新的 RAP 成本减半。
|
||||
|
||||
**基础循环**:
|
||||
|
||||
```
|
||||
部分挖格/部分停手留 AP → 同一份 RAP 支持更多次刷新 → 更频繁兑现后续专长
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[付费挖格] -->|矿脉储能| B[RAP 增长]
|
||||
B -->|省力周转半价| C[成功刷新]
|
||||
C --> D[崩壁冲击]
|
||||
C --> E[蓄力周转]
|
||||
C --> F[石层缓冲]
|
||||
C --> G[坑道小憩]
|
||||
C --> H[矿道红利]
|
||||
C --> I[新图继续挖格]
|
||||
```
|
||||
|
||||
**行为改变**:
|
||||
玩家会同时比较:
|
||||
|
||||
- 玩家不再需要为一次刷新攒下完整一轮的 AP;挖图与留 AP 的比例出现更多选择。
|
||||
- 与掘进储能不同,它保留停手攒 RAP 的旧来源,但降低主动换图所需的投资。
|
||||
- 单拿时,刷新从昂贵逃生变成常用工具;配上后续专长后,刷新次数直接转化为输出、生存或资源。
|
||||
|
||||
### 2.3 两件基座的组合
|
||||
|
||||
两件基座作用在不同位置:一个改 RAP 来源,一个改 RAP 价格。**用户已裁定允许同时持有并叠加**,组合后形成完整基座:
|
||||
|
||||
```
|
||||
挖格产 RAP + 刷新半价 → 每轮更容易达到刷新次数上限 → 后续五件兑现收益
|
||||
```
|
||||
|
||||
叠加后的硬边界仍是每轮刷新次数上限、有限 AP、旧图进度损失与战斗结束 RAP 清零。若实测过强,优先调产能量与半价数值,不增加「两件基座互斥」的特殊规则。
|
||||
- 还要不要继续挖旧图,还是现在刷新兑现崩壁冲击或矿道红利;
|
||||
- 本轮承伤压力大时,是否用石层缓冲换一次全局减伤;
|
||||
- 生命偏低时,是否通过多次刷新累计坑道小憩的恢复;
|
||||
- 是否蓄满 3 层蓄力周转,把多次刷新集中成一次攻击;
|
||||
- 刷新次数与 RAP 价格如何影响后续图推进。
|
||||
|
||||
---
|
||||
|
||||
## 3. 两件输出:即时群伤与延迟爆发
|
||||
## 3. 七件效果结构
|
||||
|
||||
### 3.1 弃图轰击
|
||||
### 3.1 矿脉储能
|
||||
|
||||
**效果候选**:每次弃图刷新成功后,对全体存活敌人造成伤害。
|
||||
每次付费挖格被受理后,额外获得 1 RAP。连锁展开不逐格追加;封印、移动、刷新不产 RAP。
|
||||
|
||||
**玩法职责**:提供即时、稳定的群体输出。玩家会在「继续挖旧图」与「现在刷新打一轮伤害」之间做选择。
|
||||
### 3.2 省力周转
|
||||
|
||||
**与其他件的关系**:
|
||||
刷新 RAP 成本固定为基线的 50%。它只改持有者的价格,不改全局基值,也不返 RAP。
|
||||
|
||||
- 掘进储能支持持续挖、持续刷、持续轰击。
|
||||
- 折价换图让同一份 RAP 兑现更多次轰击。
|
||||
- 与换图蓄势组合时,一次刷新同时提供当场群伤与下次攻击的蓄力层。
|
||||
### 3.3 崩壁冲击
|
||||
|
||||
**边界**:只有成功刷新才触发;刷新被拒绝不触发;每轮刷新上限同时限制伤害次数。
|
||||
每次成功刷新记录一次当轮事实。本轮战斗段末,所有存活敌人各受 2 点固定伤害;刷新瞬间不当场插入战斗结算。
|
||||
|
||||
### 3.2 换图蓄势
|
||||
### 3.4 蓄力周转
|
||||
|
||||
**效果候选**:每次成功刷新获得 1 层蓄势,层数有上限;下一次攻击附加全部层数对应的伤害,然后清零。
|
||||
每次成功刷新叠 1 层,最多 3 层。本轮下一次主角攻击附加全部层数对应的攻击加成,消费后清零;战斗结束不跨战保留。
|
||||
|
||||
**玩法职责**:提供延迟、集中的单点爆发。与弃图轰击的即时群伤不同,它鼓励连续换图后寻找合适的攻击窗口。
|
||||
### 3.5 石层缓冲
|
||||
|
||||
**与其他件的关系**:
|
||||
每次成功刷新把当轮减伤状态置为生效。本轮主角每次受到伤害,在现行伤害下限之前固定减少 2 点;同一轮多次刷新不继续叠加。覆盖敌方攻击、危险格、错误封印资源格与地形造成的主角伤害。
|
||||
|
||||
- 双基座组合更容易连续刷新,为一次重攻蓄满层数。
|
||||
- 与现有攻击强化专长可以组合,但不另设专属加成。
|
||||
- 层数只在当前战斗内保留,战斗结束清零,避免跨战囤积。
|
||||
### 3.6 坑道小憩
|
||||
|
||||
**边界**:层数必须有上限;一次攻击消费全部层数,不能同一批层数重复附加到多次攻击。
|
||||
每次成功刷新累计 1 点恢复量,最多累计 3 点。本轮授予段一次性合计回复生命,不超过生命上限,随后清零。
|
||||
|
||||
### 3.7 矿道红利
|
||||
|
||||
每次成功刷新固定获得 1 GP。不按旧图探索进度折算,也不与清图奖励绑定。每轮刷新上限限制触发次数。
|
||||
|
||||
---
|
||||
|
||||
## 4. 两件生存:当场护盾与直接回血
|
||||
## 4. 与现行结算时序的衔接
|
||||
|
||||
### 4.1 换图庇护
|
||||
|
||||
**效果候选**:每次成功刷新后获得护盾,按现有护盾规则抵挡伤害与消耗。
|
||||
|
||||
**玩法职责**:提供面向未来伤害的保护。玩家可以预判敌方压力,主动弃图换护盾,而不是只在棋盘不好时刷新。
|
||||
|
||||
**与弃图休整的区别**:护盾适合抵挡即将发生的伤害;休整用于修复已经失去的生命。一件偏预防,一件偏补救。
|
||||
|
||||
### 4.2 弃图休整
|
||||
|
||||
**效果候选**:每次成功刷新后回复一定生命。
|
||||
|
||||
**玩法职责**:把旧图与 RAP 转成恢复机会。生命偏低时,玩家可以放弃当前图的后续收益,换取继续冒险的空间。
|
||||
|
||||
**边界**:
|
||||
|
||||
- 回复不能超过生命上限,不转化为额外生命。
|
||||
- 刷新成本与每轮次数上限不变,不能无代价连续回血。
|
||||
- 强度对账时须同时计算放弃旧图进度的损失,不能只看单次回血量。
|
||||
|
||||
两件生存专长可以同时持有,让刷新兼具保护与恢复;但它们不替代输出件,玩家仍需通过其他攻击手段结束战斗。
|
||||
- 刷新只结束挖掘动作的资源支出,不结束当前轮次,也不新增第六个结算段。
|
||||
- 成功刷新后产生 `board_refreshed` 事实;刷新被拒绝时不产生事实。
|
||||
- 崩壁冲击只先开伤害单,沿用现行案 C,在战斗段末结账。
|
||||
- 石层缓冲订阅全局 `damage_taken`;该事件统一覆盖所有主角受伤路径。
|
||||
- 坑道小憩和矿道红利用现有 GP/治疗兑付通道留痕。
|
||||
- 刷新图仍不发清图奖励;正常清图支配性不变。
|
||||
|
||||
---
|
||||
|
||||
## 5. 一件资源:把旧图投入换成清图奖励
|
||||
## 5. 防退化边界
|
||||
|
||||
### 5.1 弃图回收
|
||||
|
||||
**已定效果**:成功刷新后,按旧图本次探索进度回收清图奖励;探索越多,回收越完整。具体档位、比例与奖励内容归后续调参。
|
||||
|
||||
**玩法职责**:建立与正常清图不同的资源兑现路线:
|
||||
|
||||
```
|
||||
先挖图投入 AP、积累探索进度 → 不再挖最后几格 → 主动刷新 → 按进度回收清图奖励
|
||||
```
|
||||
|
||||
**行为改变**:旧图不再只有「清完」与「全部放弃」两个结果。玩家可以比较继续清图的时间与风险,选择提前弃图变现。
|
||||
|
||||
**为什么不固定发一份**:空图刷新也拿完整收益,会让挖图投入失去意义,并把资源流退化成机械地点刷新。按进度回收要求玩家先探索、再决定何时兑现,保留先投入再弃图的循环,也保留接近清图时的取舍。用户已据此裁定采用按进度回收。
|
||||
|
||||
**边界**:同一旧图只能在成功放弃时结算一次;新图没有继承旧图的可回收进度;刷新被拒绝时不发奖励。
|
||||
- RAP 仍来自有限 AP,不能无限刷新。
|
||||
- 刷新本身不返 RAP。
|
||||
- 每轮刷新上限继续生效。
|
||||
- 石层缓冲是固定减伤,不堆层、不产生零伤害。
|
||||
- 坑道小憩恢复上限 3,不能靠无限刷新稳定续命。
|
||||
- 矿道红利每次仅 1 GP,量级明显低于正常战斗掉落,不足以替代清图与探索收益。
|
||||
- 不引入临时生命值,不改变棋盘信息规则。
|
||||
|
||||
---
|
||||
|
||||
## 6. 七件如何组成不同打法
|
||||
## 6. 最终裁定记录
|
||||
|
||||
| 构筑方向 | 组合 | 主要操作 |
|
||||
|---|---|---|
|
||||
| **持续轰击** | 掘进储能+弃图轰击 | 边挖边蓄 RAP,够价就刷新,刷新持续削全体敌人 |
|
||||
| **蓄力爆发** | 折价换图+换图蓄势 | 用较低成本连续换图,把多次刷新汇成一次重攻 |
|
||||
| **双基座输出** | 两基座+两件输出 | 高频刷新同时提供群伤与攻击层数,形成完整输出流 |
|
||||
| **换图续命** | 任一基座+换图庇护+弃图休整 | 在生命与敌方压力下主动换图,用护盾与回血延长战斗 |
|
||||
| **弃图采收** | 掘进储能+弃图回收 | 挖到一定进度后主动放弃,连续从新图获取探索收益与回收奖励 |
|
||||
| **七件完整形态** | 全部七件 | 挖格蓄能、低价刷新,每次换图同时兑现输出、生存与资源;仍受每轮次数上限约束 |
|
||||
|
||||
**最小成立单位**是任一基座+任一符合当前需要的后续专长。七件不要求一次拿齐,也不设集件奖励。
|
||||
- D1:双基座允许叠加。
|
||||
- D2:资源件改为每次成功刷新固定 1 GP,不采用探索进度回收清图奖励。
|
||||
- D3:生存件改为全局固定减伤,不使用护盾或临时生命。
|
||||
- D4:恢复量按当轮成功刷新次数累计,上限 3。
|
||||
- D5:所有主角伤害统一经过 `damage_taken`,允许现有订阅该事件的专长覆盖面按字面扩展。
|
||||
- D6:七件显示名采用矿脉储能/省力周转/崩壁冲击/蓄力周转/石层缓冲/坑道小憩/矿道红利。
|
||||
- D7:其余数值与稀有度先按需求卡片 provisional 投值,出口人闸追认。
|
||||
|
||||
---
|
||||
|
||||
## 7. 防退化与共同边界
|
||||
## 7. 后续参考
|
||||
|
||||
| 检查 | 约束 |
|
||||
|---|---|
|
||||
| **是否无限自供能** | 新 RAP 来自有限 AP 的付费挖格;刷新本身不产 RAP;每轮刷新上限与战斗结束清零不变。 |
|
||||
| **无专长基线是否改变** | 不改变。两件基座是专长提供的新规则,不直接修改无专长时的全局经济。 |
|
||||
| **双基座是否失控** | 叠加后仍受每轮次数上限;数值对账以每轮实际可达到的刷新次数为准。 |
|
||||
| **输出是否重复消费** | 轰击每次成功刷新结算一次;蓄势层数在一次攻击后全部清零。 |
|
||||
| **生存是否无代价** | 护盾与回血都要求成功刷新;玩家同时付出 RAP、旧图进度与刷新名额。 |
|
||||
| **正常清图是否被删除** | 不删除。弃图回收不应让同等投入的收益无条件高于正常清图。 |
|
||||
| **空图是否拿满资源** | 推荐案不允许;回收按旧图探索进度计算。 |
|
||||
| **信息红线** | 七件均不增加揭示效果,不碰危险格信息规则。 |
|
||||
本文保留供需求卡片、实现验收与后续调参比对。换图系现行权威规则仍以四份 GDD为准:
|
||||
|
||||
若实装后玩家仍然只在坏图时刷新,说明基座或后续收益没有真正成立;不能只继续增加数值来掩饰行为没有改变。
|
||||
|
||||
---
|
||||
|
||||
## 8. 旧案处置
|
||||
|
||||
| 旧内容 | 本案处置 |
|
||||
|---|---|
|
||||
| 抬高刷新成本 | 删除;与主动刷新方向相反 |
|
||||
| 刷新全板开 | 不进入本案;不是形成刷新流的必要条件 |
|
||||
| 静气:不刷新给减伤 | 从本流派删除;奖励相反行为 |
|
||||
| 换图回气 | 删除;只返还部分成本,不建立新循环 |
|
||||
| 换图开眼/明察 | 删除;属于信息附属收益 |
|
||||
| 换图退守 | 由换图庇护承接生存方向 |
|
||||
| 换图成势 | 保留其延迟爆发思路,重写为换图蓄势;另增即时群伤弃图轰击 |
|
||||
| 单基座二选一 | 改为双基座并存;用户已裁定允许同时持有并叠加 |
|
||||
|
||||
---
|
||||
|
||||
## 9. GDD 修改清单(用户已审核,已落文)
|
||||
|
||||
以下清单已经用户审核并落入对应 GDD;现行规则以各 GDD 为准,本节保留供后续写卡核对改动面。采用**刷新记录事实、本轮结算兑付效果**的衔接方案:不新增第六个结算段,不让刷新当场结束一轮,也不改状态只能在轮末授予的现行规则。
|
||||
|
||||
### 9.1 `design/gdd/systems/冒险循环/奇物.md`
|
||||
|
||||
- 在「专长·设计原则」加入基座专长定义:改变资源来源、动作成本/频率、动作结算或动作关系,使原本止损用的动作成为持续主打法。
|
||||
- 加入三项行为改变检验:拿前后操作是否不同;新行为如何持续;后续专长为何依附该行为。泛用数值增长不能冒充基座。
|
||||
- 事件族由五族扩为六族,新增设计侧分组「换图系」;族名仍不上玩家卡面,不设集件奖励或强制先拿基座。
|
||||
- 登记双基座七件:掘进储能、折价换图、弃图轰击、换图蓄势、换图庇护、弃图休整、弃图回收;写清两件基座允许叠加。
|
||||
- 写清七件职责:两件输出分别为群体直伤与下次攻击爆发;两件生存分别为护盾与回血;资源件按旧图探索进度回收清图奖励。
|
||||
- 同步专长总数、族数与相关验收标准;落文已按当轮 `feats.tres` 实数复核为 30 行,换图系七件标为待实现,不计入现役行数。
|
||||
- 落文版本=v1.2。
|
||||
|
||||
**不自动改自爆流**:现行「净血账期望必须为负、防见雷就踩」与用户描述的理想自爆流方向不同,需另一次明确裁决,不随换图系一并改掉。
|
||||
|
||||
### 9.2 `design/gdd/systems/排雷循环/行动经济.md`
|
||||
|
||||
- 在停手转存之外登记专长提供的第二 RAP 来源:持有掘进储能时,每次付费挖格额外获得 RAP;安全格与危险格同口径,无专长时不变。
|
||||
- 登记折价换图:持有专长时刷新 RAP 成本减半;只改专长持有者的价格,不改全局表基值。
|
||||
- 写清双基座叠加:挖格产 RAP 与刷新半价同时生效;结算顺序、拒绝刷新不产效果要有验收口径。
|
||||
- 保留现有限制:刷新不终结本轮、不重置 AP;每轮次数上限不变;RAP 战斗结束清零;刷新本身不产 RAP。
|
||||
- 增补对应验收标准。
|
||||
|
||||
### 9.3 `design/gdd/systems/战斗循环/战斗结算.md`
|
||||
|
||||
- 新增「刷新成功」事实:只有成功换图才产生;旧图退役前保存本次探索进度,供弃图回收使用;刷新被拒绝不产生事实。
|
||||
- **推荐时序**:刷新只记录事实,不当场插入一轮结算。弃图轰击在本轮战斗段经伤害控制器兑付;换图庇护与弃图休整在本轮授予段兑付;弃图回收随本轮授予段结算。五段顺序不变。
|
||||
- 换图蓄势在刷新成功时累计层数,下一次主角攻击附加并清零;层数上限与战斗结束清零写入规则。
|
||||
- 群伤若击杀最后敌人,由本轮既有判定段判胜,避免新增挖掘期即时胜利分支。
|
||||
- 专长效果写扩展现行:群伤走伤害单据,护盾/回血/回收各留可归因的结算行;来源指向刷新专长与被弃旧图,不伪造格子来源。
|
||||
- 「刷新图不发清图奖励」保持不动;弃图回收兑的是**旧图**按进度回收的奖励,不是把新图判成已清图。
|
||||
- 增补对应验收标准。
|
||||
|
||||
### 9.4 `design/gdd/systems/战斗循环/地图战斗资源.md`
|
||||
|
||||
- 扩展清图奖励对象的使用口径:正常清图仍发完整对象;持有弃图回收时,旧图可按探索进度回收同一奖励对象的一部分。
|
||||
- 建议把「探索进度」定义为:`(旧图已开安全格数 − 装板初始已开安全格数)÷ 装板初始未开安全格数`,限制在 0~1。连锁展开真实推进探索,计入;初始封锁格与危险格不计入。
|
||||
- 写清弃图回收只发基础奖励的进度份额:不触发完美封印加倍,不产生清图事件,不把旧图判为清图。
|
||||
- 同一旧图只在成功退役时回收一次;新图不继承旧图进度;空投入刷新不能拿完整奖励。
|
||||
- 具体比例、档位、取整与奖励数值归调参;已增补对应验收标准,落文版本=v1.2。
|
||||
|
||||
### 9.5 按推荐时序不需要修改的文档
|
||||
|
||||
- `增益状态.md`:护盾仍在轮末授予段进入状态池,继续遵守「授予只在结算时发生」与「跨轮跨图延续」,不需要为刷新开即时授予例外。
|
||||
- `02-系统清单.md`:不新增文档,只改现有系统文档,无需改根索引。
|
||||
- `术语表.md`:七件名称目前均为代号,正式命名前不登记新术语。
|
||||
|
||||
### 9.6 本 GDD 批明确不改
|
||||
|
||||
- 不改 `feats.tres`、其他数值表、代码、测试或架构实现现状;这些归后续写卡与实现批。
|
||||
- 不改自爆流现行条款,不顺带重做其他专长族。
|
||||
- 不采用「刷新瞬间结算全部效果」方案。若用户要求即时生效,需另增 `增益状态.md` 与战斗胜利时序的修改项,再重新审核。
|
||||
|
||||
**两处衔接已经用户审核**:①后续效果统一在本轮结算兑付,而非刷新瞬间兑付;②弃图回收的探索进度按安全格实际推进计算,连锁计入、初始格与危险格不计。
|
||||
|
||||
---
|
||||
|
||||
## 10. 后续验收必须证明的行为
|
||||
|
||||
1. **掘进储能**:从真实挖格入口进入,持有专长时 RAP 按声明增加;无专长时挖格不新增 RAP。
|
||||
2. **折价换图**:真实刷新入口按修改后的成本扣 RAP;AP、轮次与其他刷新规则不变。
|
||||
3. **基座组合**:两件同时持有时,挖格产 RAP 与刷新半价都生效;验证成本与产 RAP 顺序,任何组合都不能超过每轮刷新上限。
|
||||
4. **弃图轰击**:成功换图后全体存活敌人受伤;刷新被拒绝时不触发。
|
||||
5. **换图蓄势**:多次刷新正确叠层并受上限约束;下一次攻击附加全部层数后清零;战斗结束清空。
|
||||
6. **换图庇护/弃图休整**:只在成功刷新后给护盾或回血;回复不超过生命上限。
|
||||
7. **弃图回收**:读取被放弃旧图的探索进度;不同进度按规则得到不同奖励;同一旧图不能重复领取。
|
||||
8. **基线零扰动**:没有这些专长时,RAP 来源、刷新成本、清图奖励与刷新结果保持现行规则。
|
||||
9. **真人体验判断**:拿到基座与后续专长后,玩家会主动考虑刷新,而不是仍只在坏图时使用。此项登记真人验证,不阻塞实现验收。
|
||||
|
||||
---
|
||||
|
||||
## 11. 已定结构:零待裁点
|
||||
|
||||
| 决策 | 用户裁定 | 落地口径 |
|
||||
|---|---|---|
|
||||
| **D1 双基座关系** | 允许同时持有并叠加 | 掘进储能改 RAP 来源,折价换图改 RAP 价格;每轮刷新上限继续限频,不设互斥规则。 |
|
||||
| **D2 弃图回收** | 按旧图探索进度回收 | 必须先探索再兑现;不采用每次刷新固定发一份,避免空图刷新拿完整收益、退化成机械点刷新。 |
|
||||
|
||||
双基座七件结构由此闭合。具体数值与稀有度不属于结构待裁点,统一归后续调参。
|
||||
|
||||
---
|
||||
|
||||
## 12. 下一步
|
||||
|
||||
1. 用户发令后进入写需求卡片段,以本提案与四份 GDD 为输入,按第 10 节建立真实入口断言与行为验收。
|
||||
2. 迭代编排后实现;具体数值与稀有度归后续调参。
|
||||
3. 自爆流现行条款不在本批改动面内;若以后要按同一基座思想重构,另行登记与裁决。
|
||||
|
||||
本提案保留为写卡参考;换图系七件尚未实装,未跑任何游戏测试。
|
||||
- `design/gdd/systems/冒险循环/奇物.md`
|
||||
- `design/gdd/systems/排雷循环/行动经济.md`
|
||||
- `design/gdd/systems/战斗循环/战斗结算.md`
|
||||
- `design/gdd/systems/战斗循环/地图战斗资源.md`
|
||||
@@ -1,18 +1,19 @@
|
||||
# 需求卡片:Esc 浮窗与营地顶栏批(营地顶部小 HUD 栏 · Esc 三钮浮窗)· 营地交互重做卡⑤
|
||||
|
||||
- 版本:**v1.2**(2026-10-18 grilling 轮次裁定回写——Q1 裁定甲(只营地根态响应,代拍板留痕)、Q2 裁定甲(再按 Esc 关浮窗,推荐案)、Q3 裁定甲(确认 Esc 只在营地语境,推荐案,用户的从严收窄升格为已确认);契约 2 随 Q3 升格;卡④回设计段→本卡排期随④重新立卡后,卡片保留有效)
|
||||
- 版本:**v1.5(现行有效卡)**。留痕:Q1/Q2/Q3 均于 2026-10-18 裁定(甲/甲/甲,见 §10,已裁非待裁);卡④ 2026-10-31 重立、2026-11-01 修订为 v3.1,本卡随卡④ 代码波同行;2026-11-01 grilling+godot-master 双审后用户令修订(v1.4)——契约 9 新立、契约 3 谓词钉死、契约 4 浮窗自收、V4/V6 订正、死信约束改述、文件表补两件;2026-11-01 用户裁机检预算互斥停机呈报(v1.5)——浮窗句柄改落 `ui_camp_modals.gd` 子容器(卡⑥ 前置建件,本卡 T1 落第六枚),`ui_modals.gd` 类变量 14 不升(原「14→15 恰贴」句作废),契约 7 补寻址形态。
|
||||
- 来源:[需求澄清-营地交互重做-2026-10-18.md](需求澄清-营地交互重做-2026-10-18.md)(唯一事实源:⑤裁决 9/10、④结构树语境、⑦切批原则⑤)
|
||||
- 归属:呈现层/组合根(`game/presentation/`)——营地交互重做五卡之五
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ 待裁点已裁(Q1/Q2/Q3 均裁定,见 §10)+ grilling 复核门过+ **卡④重新立卡并落地后**(本卡浮窗与 HUD 落在卡④新建的场景屏上——卡④ 2026-10-18 回设计段,本卡排期随④之后,卡片本身有效)
|
||||
- 开工门:U0(用户 `/iter` 发令)+ 零待裁点(Q1/Q2/Q3 已裁,见 §10;2026-11-01 双审零新增真待裁点)+ grilling 复核门过+ **卡④ 代码波落地后**(卡④ v3.1 现行有效卡、设计线闭合、开工门已裁断销号;本卡浮窗与 HUD 落在卡④ 新建的场景屏上,排期随卡④ 代码波之后,卡片本身有效)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- 前置卡④(营地场景图):Esc 浮窗的「返回主菜单」与顶栏 HUD 都落在营地场景屏上;**卡④ 2026-10-18 已回设计段**(美术设计定稿未过设计前置闸)——本卡不可先于卡④重新立卡落地,排期随④之后;本卡自身零美术依赖(零新素材、裁决 9/10 已给全),设计前置检查通过,卡片有效。
|
||||
- **跨卡接口(v1.1 补全)**:①场景屏顶栏位(卡④ T2 预留);②**营地刷新单口**(卡④ T3 定义:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成)——本卡 HUD 刷新挂该单口,**非已退役的 `refresh_camp`**;③子界面返回钮承诺(卡④ T3——Q1 甲案前提);④卡④终局落点不变量(T0 互证)。
|
||||
- 前置卡④(营地场景图,v3.1 现行有效卡——2026-11-01 双审修订、开工门已裁断销号):Esc 浮窗的「返回主菜单」与顶栏 HUD 都落在卡④ 新建的营地场景屏上;卡④ 设计线已闭合(样张过审 2026-10-31+art-bible v3.31 §6.2)——本卡不可先于卡④ 代码波落地,排期随④ 之后;本卡自身零美术依赖(零新素材、裁决 9/10 已给全),设计前置检查通过,卡片有效。
|
||||
- **跨卡接口(保持既有名,不改名)**:①场景屏顶栏位(卡④ T2 预留;卡④ v3.1 挂接签名=`camp_scene_view.gd set_hud_band()`,「卡⑤ 填值」);②**营地刷新单口**(卡④ T3 定义;卡④ v3.1 定名 `camp_refresh_bus.gd` 的 `CampRefreshBus.refresh(camp_data: Dictionary) -> void`)——本卡 HUD 刷新挂该单口,**非已退役的 `refresh_camp`**;③子界面返回钮承诺(卡④ T3——Q1 甲案前提:五界面各带返回/关闭钮);④卡④终局落点不变量(T0 互证:现役=终局 continue 回主菜单,`main_adventure_flow.gd:123-143` 终局出口 `_begin_session(false)`,行号 T0 复核;若后续改落点为营地则本卡「返回主菜单」动线须补会话收束)。卡④ v3.1 已原样承接上述四项,接口名无改动;场景根句柄名与 `LAYER_CONTRACT` 槽名由卡④ T0 定稿(显式验收点),本卡只消费不改名。
|
||||
- 卡②的退出游戏钮、现役设置面——本批只复用不重做(契约 3)。
|
||||
- 与卡④同批或随后(两卡触碰同一新屏)。**探针入口路径还依赖卡②③链**(v1.1 补明): restructuring 后营地只能经「菜单三钮→进入存档→选档→进档」到达——本卡 V1/V3 探针段隐含依赖该全链在位;若本卡先于卡②③合入,探针段挂起或走中间态断言(仿卡② §9 第 4 条先例)。
|
||||
- **camp 截图/golden 联动(v1.1 补)**:HUD 栏落营地场景屏会改 camp 截图内容——若本批晚于卡④单跑,卡④ 刚重制的 camp 断言基线必再漂移;若同批,卡④ T5 迁移时点须晚于本卡 T2 HUD 落位(排序约束)。本卡 T2 含「HUD 落位后核漂移热区→限 HUD 区显式更新+呈报」条。
|
||||
- 与卡④同批或随后(两卡触碰同一新屏)。**探针入口路径依赖卡②③链**:营地交互重做后营地只能经「菜单三钮→进入存档→选档→进档」到达——本卡 V1/V3 探针段隐含依赖该全链在位;卡②③ 工程波已随迭代 33 合回(待办清单 2026-10-18 条实况),探针段全链在位,无挂起前提。
|
||||
- **多卡共改文件分界(`screen_flow_probe.gd`)**:卡④ T5 改该文件营地旗标语义(:54→新场景根句柄;:53 是菜单行——v1.4 订正)与回退段(:119-125);本卡 T3 扩展该文件 Esc 合成段(现 :153-165 已有「选档语境 Esc 零响应」断言)为营地语境 Esc 浮窗断言段。两段按行域分界,各自改各自段,禁互相覆盖;**分界锚=断言段语义/段名,不锚行号**(一侧插删行会平移另一侧行号,2026-11-01 双审补立);T0 与卡④ 对齐。
|
||||
- **camp 截图/golden 联动(v1.4 改述,原排序约束句废止)**:HUD 栏落营地场景屏会改 camp 截图内容。按迭代 47 执行序(roadmap 迭代总表格内排列顺序=执行顺序——卡④ 整卡先行、卡⑤ 后),卡④ T5 的 camp 断言基线建立在**无 HUD 场景屏**上;本卡 T2 HUD 落位后核漂移热区→限 HUD 区显式更新+呈报(T2 已列该条)。原 v1.3「卡④ T5 迁移时点须晚于本卡 T2 HUD 落位」句在上述执行序下天然不可满足(死信),2026-11-01 用户裁改述如上;现无 camp 逐像素 golden(卡④ 契约 10)。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
@@ -21,48 +22,57 @@
|
||||
- 裁决 10「钥石余额:场景**顶部小 HUD 栏**」(用户原话「先按顶部小HUD栏设计」——provisional 措辞留痕)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
1. Esc 浮窗(全仓现无任何键盘快捷键处理——从零新建);
|
||||
2. 营地场景顶部小 HUD 栏(钥石余额常驻——现营地钥石徽在 CampOverlay 顶栏,随卡④拆解后由新 HUD 栏承接)。
|
||||
1. Esc 浮窗(产品码现无任何键盘快捷键处理——从零新建);
|
||||
2. 营地场景顶部小 HUD 栏(钥石余额常驻——现营地钥石徽在 CampOverlay 顶栏 `camp_overlay_view.gd:125`,随卡④拆解后由新 HUD 栏承接,落卡④ T2 顶栏预留位)。
|
||||
|
||||
**完成定义**:
|
||||
1. 营地语境按 Esc 唤起浮窗,浮窗有且仅有返回主菜单/设置/退出游戏三项;浮窗的关闭/取消路径按 Q2 裁定落地;
|
||||
2. 「返回主菜单」回主菜单(营地场景屏隐藏+选档层同收+主菜单可见);「设置」开现役设置面(语境分支见契约 3);「退出游戏」接现役退出路径;
|
||||
3. 营地场景顶部小 HUD 栏在位,钥石余额读数=存档单口现值,刷新时机=购买后(挂卡④营地刷新单口)/进档或回营地进入时重读/夹具直写后探针显式触发(v1.1 清单化);
|
||||
3. 营地场景顶部小 HUD 栏在位,钥石余额读数=存档单口现值,刷新时机=购买后(挂卡④ `CampRefreshBus.refresh()` 单口)/进档或回营地进入时重读/夹具直写后探针显式触发;
|
||||
4. 浮窗零动效直开直关(生涯页同款契约);新层入 `LAYER_CONTRACT`;
|
||||
5. 批末重闸按改动域选档全绿;三份 `reports/` 产出物写齐(T4)。
|
||||
|
||||
## 2. 现状事实(2026-10-18 实查;开工日 T0 复核行号)
|
||||
## 2. 现状事实(2026-10-18 实查;2026-10-31 复核行号;2026-11-01 双审全量复核——F4 行号 ±1 漂移订正、F9 补全路径,开工日 T0 再核)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | **全仓零键盘快捷键处理**:`KEY_ESCAPE`/`ui_cancel`/`ui_accept` 在 `game/` 全目录 grep 零命中(2026-10-18 实查)——Esc 浮窗是从零新建,无旧路径可迁;现成输入合成先例只有鼠标 `Viewport.push_input`(ui_input_probe.gd:51-63/:657-680,headless 实证),键盘合成须 T0 spike | 全仓 grep 实证;`game/smoke/ui_input_probe.gd:51-63` |
|
||||
| F2 | 设置面在役:`settings_overlay` 构建+`closed` 接线;开=`_open_settings`(:1055-1057),关=`_on_settings_closed`(:1060-1063,关后**带 `_start_overlay.visible = true` 硬副作用**——营地语境照抄会在关设置后弹主菜单,须语境分支,见契约 3);`_open_settings`/`_quit_game` 现唯二调用点都是菜单钮 | `main.gd:277-279`/:1055-1057/:1060-1063/:333-334(v1.1 行号订正) |
|
||||
| F3 | 退出游戏在役:`_quit_game`(设置即时落盘后 `get_tree().quit()`;无语义副作用,可原样复用) | `main.gd:500-503` |
|
||||
| F4 | 钥石徽工厂件在役:`UiKit.keystone_badge`(三处消费先例——营地顶栏/生涯页/状态面板;迭代 17 卡③ Q10 甲工厂化)——HUD 栏复用该件,零新素材 | `camp_overlay_view.gd:82`;`career_overlay.gd:54` |
|
||||
| F5 | art-bible §6.2 现载「钥石余额常驻顶栏」——与裁决 10 同向,本批是把该呈现位从 CampOverlay 顶栏迁场景屏 HUD | `design/art/art-bible.md:369` |
|
||||
| F6 | 层序唯一事实源=`main.gd` `LAYER_CONTRACT`;`_apply_layer_contract` 全仓唯一落位口;冒烟 V1d 源码断言判红散落 `move_child` | `main.gd:34-53`/:430-455 |
|
||||
| F7 | 零动效契约先例:生涯页/状态面板「直开直关」——ui_struct_probe 载 `_career_zero_motion_contract`(子树 AnimationPlayer 零命中+视图源码禁动效令牌扫描) | `game/smoke/ui_struct_probe.gd:313-328` |
|
||||
| F8 | 08「六 · Alpha 不含」载「键盘 / 手柄输入(排 Beta)」行(**:192**——:191 是反雪球机制行)——Esc 属用户明示裁决(澄清⑤裁决 9),与该行的关系须同步注记(反哺点),本批不自扩其余键盘面 | `design/gdd/08-Alpha版本标准.md:192`(v1.1 行号订正) |
|
||||
| F9 | **刷新链路实况(v1.1 订正,原结论失实)**:营地侧刷新腿=`camp_overlay.refresh_camp(camp_data())`(CampShopPresenter 各购买口尾与 `_refresh_presentations` 成对调用)——`refresh_camp` 是 CampOverlay 方法,**随卡④拆解退役**;`_refresh_presentations`(main.gd:1293-1298)刷的是战斗侧 HudPresenter+秘宝槽+结算视觉,**营地语境零营地视图触碰**;钥石入账发生在冒险收束、不在营地(存档序列化.md:116)——营地语境无现行入账触发源 | `camp_shop_presenter.gd:85-86`/:93-94 等;`main.gd:1293-1298`;`存档序列化.md:116` |
|
||||
| F10 | 调试入口全局浮层角标在役(LAYER_CONTRACT 末位 float 类,不注册 ModalStack,任何 UI 不遮挡);ModalStack.open 只隐藏登记层 | `main.gd:51-53`/:312-314;`modal_stack.gd:21-30` |
|
||||
| F1 | **产品码零键盘快捷键处理**:`KEY_ESCAPE`/`ui_cancel`/`ui_accept` 在 `game/core/`+`game/presentation/` grep 零命中——Esc 浮窗产品码从零新建,无旧路径可迁。**但探针侧已有键盘合成先例**:`screen_flow_probe.gd:156-160` 用 `InputEventKey`(keycode=`KEY_ESCAPE`)经 `Viewport.push_input(ev, true)` 合成,断言「选档语境 Esc 零响应」(:153-165,卡⑤ 范围外的零实现断言)——键盘事件合成机制已实证,T0 由「从零 spike」改为「复核该先例并迁移到营地语境」。鼠标 `push_input` 先例=`ui_input_probe.gd:84-116`(`InputEventMouseMotion`/`InputEventMouseButton`;原 `game/smoke/` 目录已不存在,全部探针在 `game/probe/`) | `game/probe/screen_flow_probe.gd:153-165`;`game/probe/ui_input_probe.gd:84-116` |
|
||||
| F2 | 设置面在役:开=`_open_settings()`(`main.gd:444` 薄委托→`main_overlay_flow.gd:7-9`:`start_overlay.visible=false`+`ui.modals.flow.open(settings_overlay)`);关=`_on_settings_closed()`(`main.gd:448`→`main_overlay_flow.gd:11-13`:`ui.modals.flow.close`+**`start_overlay.visible = true` 硬副作用**——营地语境照抄会在关设置后弹主菜单,须语境分支,契约 3)。开合走组合根模态编排口 `ui.modals.flow.open/close`;静态承接件是 main 同名函数逐字搬运、main 侧留薄委托,探针 `main.X` 路径零改动。`_open_settings`/`_quit_game` 菜单钮调用点见装配段(T0 复核行号) | `main.gd:444`/:448;`game/presentation/main_overlay_flow.gd:7-13` |
|
||||
| F3 | 退出游戏在役:`_quit_game()`(`settings_store.save_settings()` 后 `get_tree().quit()`;无语义副作用,可原样复用) | `main.gd:143-146` |
|
||||
| F4 | 钥石徽工厂件在役:`UiKit.keystone_badge` 定义 `ui_kit.gd:632`;消费点四处产品码——营地顶栏 `camp_overlay_view.gd:125`/生涯页 `career_overlay.gd:54`/状态面板 `status_panel.gd:32`/选档界面 `save_select_overlay.gd:86`(卡③ 新增),+ `theme_projection_test.gd:853-854` 单测投影断言。HUD 栏复用该件,零新素材 | `game/presentation/views/ui_kit.gd:632`;`camp_overlay_view.gd:125`;`career_overlay.gd:54`;`status_panel.gd:32`;`save_select_overlay.gd:86` |
|
||||
| F5 | art-bible **v3.31** §6.2 现载「钥石余额:常驻画面顶部的一行小 HUD 栏(高 48px,浮层覆盖,不挤压画面)」(:396);camp.md v2.2 §顶栏(:39-41)同述+「刷新入口=营地刷新单口(卡⑤同口挂接)」——与裁决 10 同向,本批把呈现位从 CampOverlay 顶栏迁场景屏 HUD | `design/art/art-bible.md:396`;`design/gdd/ux/camp.md:39-41` |
|
||||
| F6 | 层序唯一事实源=`ui_registry.gd:15-35 LAYER_CONTRACT`(`UiRegistry`;`main.gd:5` 头注自陈界面句柄与层清单归 ui 注册面);全仓唯一落位口=`ui_registry.gd:76 apply_layer_contract(root)`(唯一 `move_child` 点);模态登记册序/树序断言=`system_test.gd:94-106`(V1c 合同树序链)。浮窗新槽入该合同表,禁散落 `move_child` | `game/presentation/ui_registry.gd:15-35`/:76;`game/tests/system_test.gd:94-106` |
|
||||
| F7 | 零动效契约先例:生涯页/状态面板「直开直关」——`ui_struct_probe.gd:379-391 _career_zero_motion_contract`(子树 AnimationPlayer 零命中+视图源码 `create_tween`/`UiKit.motion` 令牌扫描)、:334-341 `_status_zero_motion_contract` 同形 | `game/probe/ui_struct_probe.gd:334-341`/:379-391 |
|
||||
| F8 | 08「六 · Alpha 不含」载「键盘 / 手柄输入(排 Beta)」行(**:193**——:192 是反雪球机制行)——Esc 属用户明示裁决(澄清⑤裁决 9),与该行的关系须同步注记(反哺点),本批不自扩其余键盘面 | `design/gdd/08-Alpha版本标准.md:193` |
|
||||
| F9 | **刷新链路实况**:营地侧刷新腿=`camp_overlay.refresh_camp(camp_data())`(`camp_shop_presenter.gd:82/90/102/114/126/139/151/165` 各写口尾,与 `_refresh_presentations.call()` 成对,实数 T0 复核)——`refresh_camp` 是 CampOverlay 方法,**随卡④拆解退役**;`_refresh_presentations`(`main.gd:628-633`)刷 `HudPresenter.refresh(ui.battle.*)`+`_treasure_ui.refresh_slots()`+`_sync_settlement_visuals()`,仍战斗侧、不刷营地视图(营地购买口尾成对调用它属既有行为,卡④ 契约 4 明令「禁顺手统一」);`camp_shop_presenter.gd:50-53 on_camp_closed()` 关营地亦 `_refresh_presentations.call()`;钥石读数单点=`camp_shop_presenter.gd:62 camp_data()` 的 `meta.meta_state.keystone_balance()`;钥石入账在冒险收束、不在营地(`存档序列化.md`)——营地语境无现行入账触发源 | `game/presentation/camp_shop_presenter.gd:50-53`/:62/:82-83;`main.gd:628-633`;`存档序列化.md` |
|
||||
| F10 | 调试角标:`ui_registry.gd:33-34` 载 `debug_badge` 为 `LAYER_CONTRACT` 末位 `float` 类、不注册 ModalStack;`modal_stack.gd:21-41 open()` 只遍历登记层 `_list` 隐藏上方可见层、不触及 float 类 ⇒ 浮窗开不波及角标节点(结构语义)。**但角标发布态恒隐**:`main.gd:98` `ui.screens.debug_badge.visible = _debug_entry`(缺省 false,迭代 38 卡⑦ 发布态换代),`ui_click_probe.gd:330-333` 断言「设置面开中调试角标不可见(发布态恒隐)」——v1.2「角标恒压浮窗且保持可点/可见」是换代前旧结论,已失实;本批删该运行期断言面(契约 7/T3/V8 同步订正),改以「浮窗登记序+open 只动登记层」结构断言承载;开发期临时启用角标(本地改 `_debug_entry`、不提交)后的浮窗不遮角标观感归用户目检 | `game/presentation/ui_registry.gd:33-34`;`game/presentation/modal_stack.gd:21-41`;`main.gd:98`;`game/probe/ui_click_probe.gd:330-333` |
|
||||
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 浮窗三项且仅三项**:返回主菜单/设置/退出游戏(裁决 9)。禁加项(禁「继续冒险」类——营地无冒险可继续)、禁减项。浮窗的关闭路径=Q2 裁定项(再按 Esc 关/关闭钮/无关闭三选一),不属「加项」争议。
|
||||
🔴 **契约 1 · 浮窗三项且仅三项**:返回主菜单/设置/退出游戏(裁决 9)。禁加项(禁「继续冒险」类——营地无冒险可继续)、禁减项。浮窗的关闭路径=Q2 裁定项(再按 Esc 关),不属「加项」争议。
|
||||
|
||||
🔴 **契约 2 · Esc 范围=营地语境(Q3 已确认,2026-10-18)**:浮窗只在营地场景图语境唤起(含其子界面开态——行为按 Q1 裁定的甲案:只根态响应)。**禁扩展到战斗/地图/其他屏**(用户 2026-10-18 确认「只在营地语境」;08:192 键盘面仍排 Beta,本批只落 Esc 一项)。
|
||||
🔴 **契约 2 · Esc 范围=营地语境(Q3 已确认,2026-10-18)**:浮窗只在营地场景图语境唤起(含其子界面开态——行为按 Q1 裁定的甲案:只根态响应)。**禁扩展到战斗/地图/其他屏**(用户 2026-10-18 确认「只在营地语境」;08:193 键盘面仍排 Beta,本批只落 Esc 一项)。
|
||||
|
||||
🔴 **契约 3 · 设置与退出本体零重做、接线允许语境分支(v1.1 放宽)**:设置面本体与退出行为零改(F2/F3);**开合接线允许按语境分支**——现役 `_on_settings_closed` 带「回开始层」硬副作用,营地语境须分支为「关设置回浮窗不回主菜单」;正解=浮窗登记于设置层之下,`_open_modal(settings_overlay)` 自动隐藏浮窗、关闭自动恢复(既有栈语义);`_quit_game` 无语义副作用,原样复用。
|
||||
🔴 **契约 3 · 设置与退出本体零重做、接线允许语境分支**:设置面本体与退出行为零改(F2/F3);**开合接线允许按语境分支**——现役 `main_overlay_flow.gd:11-13 _on_settings_closed` 带「回开始层」硬副作用(`start_overlay.visible = true`),营地语境须分支为「关设置回浮窗不回主菜单」。栈语义正解(F6/modal_stack.gd:21-41):浮窗登记于设置层**之下**(合同表槽位在营地层与 `settings_overlay` 之间,T0 与卡④ 对齐最终槽名),`ui.modals.flow.open(settings_overlay)` 时设置层绘制在浮窗之上遮挡之(open 只隐藏登记序在设置层**之上**的层,浮窗在下不被隐藏),`close` 后遮挡解除、浮窗自然重现——「关设置回浮窗」由栈语义保证;**语境分支点=`_on_settings_closed` 的 `start_overlay.visible = true` 硬副作用**(菜单路径保留、营地浮窗路径跳过),落 `main_overlay_flow.gd._on_settings_closed`(改)。**语境分支谓词(2026-11-01 双审钉死)=`camp_esc_overlay.visible`**(谁开的设置谁收:浮窗开态下关设置必回浮窗;菜单路径浮窗必未开);与 Esc 守卫谓词(契约 9①②,按 ModalStack 现开集合判)**分立**——禁复用同一谓词。`_quit_game`(`main.gd:143`)无语义副作用,原样复用。
|
||||
|
||||
🔴 **契约 4 · 返回主菜单动线**:营地场景屏隐藏+**选档层同收**(卡④ T3 口径)+开始层恢复;营地语境⇔闲时会话(无冒险会话——卡④ 契约 6「冒险进行中不开营地」+现役终局回菜单并 `_begin_session(false)` 收束推得,v1.1 前提成文),故动线本身不含会话收束;**回主菜单后菜单=卡②三钮、断点在档不在菜单露面**(禁实现者自作主张加「继续冒险」钮——那正是契约 1 在浮窗侧禁的);营地每笔消费即时写盘(存档序列化.md:121),回主菜单不写档也无损。
|
||||
🔴 **契约 4 · 返回主菜单动线**:**动线首步=close 浮窗**(`ui.modals.flow.close(camp_esc_overlay)`——浮窗是模态层,与屏壳可见性无关,缺此步浮窗残留主菜单上方;v1.4 补,V3 断言含浮窗 closed);营地场景屏隐藏+**选档层同收**(卡④ T3 口径)+开始层恢复;营地语境⇔闲时会话(无冒险会话——卡④ 契约 6「冒险进行中不开营地」+现役终局回菜单并 `_begin_session(false)` 收束推得),故动线本身不含会话收束;**回主菜单后菜单=卡②三钮、断点在档不在菜单露面**(禁实现者自作主张加「继续冒险」钮——那正是契约 1 在浮窗侧禁的);营地每笔消费即时写盘(`design/gdd/systems/表现与支撑/存档序列化.md`,行号 T0 复核),回主菜单不写档也无损。
|
||||
|
||||
🔴 **契约 5 · 顶栏 HUD 单口读数+刷新挂卡④单口(v1.1 订正)**:钥石余额只经 MetaProgression/SaveManager 访问器单口读(禁第二账本、禁视图自算);**刷新挂卡④定义的营地刷新单口**(非已退役 `refresh_camp`——F9);刷新时机清单=购买后/进档或回营地进入时重读/夹具直写后探针显式触发(T2 落定)。
|
||||
🔴 **契约 5 · 顶栏 HUD 单口读数+刷新挂卡④单口**:钥石余额只经 MetaProgression/SaveManager 访问器单口读(`camp_shop_presenter.gd:62 camp_data()` 的 `keystone_balance()`;禁第二账本、禁视图自算);**刷新挂卡④定义的营地刷新单口**(卡④ v3.1 定名 `CampRefreshBus.refresh(camp_data: Dictionary) -> void`,非已退役 `refresh_camp`——F9);刷新时机清单=购买后(挂单口)/进档或回营地进入时重读/夹具直写后探针显式触发(T2 落定)。**「进档/回营地进入时重读」的触发主体(v1.4 钉死)=组合根进档动线尾(营地开层同口)显式调 `CampRefreshBus.refresh(camp_data())` 一闸**——禁 band `_ready` 自刷或自持第二入口(保 V6「显式触发才变」单口调用记录锚)。
|
||||
|
||||
🔴 **契约 6 · 零动效**:浮窗直开直关,子树零 AnimationPlayer、源码禁动效令牌(生涯页同款契约,F7);减动效设置项不新增(在役项沿用)。
|
||||
🔴 **契约 6 · 零动效**:浮窗直开直关,子树零 AnimationPlayer、源码禁 `create_tween`/`UiKit.motion` 令牌(生涯页同款契约,F7);减动效设置项不新增(在役项沿用)。
|
||||
|
||||
🔴 **契约 7 · 层序纪律+遮挡结论(v1.1 落结论)**:浮窗入 `LAYER_CONTRACT`(改层序只改合同表);禁散落 `move_child`。**与调试角标遮挡的结论(v1.1 补,既有纪律已定论、无需用户裁)**:debug_badge 是末位 float 类不注册 ModalStack,ModalStack.open 只隐藏登记层⇒角标**恒压浮窗且保持可点**(树序末位=最顶绘制序,全仓无 `debug_badge.disabled` 赋值)——合同内照此表达。另写明一条栈语义防误触 L3:浮窗开会隐藏登记序在其上的已开层(如调试浮窗体 debug_overlay),关闭恢复——属既有栈语义非新决策。
|
||||
🔴 **契约 7 · 层序纪律+遮挡结论(订正)**:浮窗入 `ui_registry.gd LAYER_CONTRACT`(改层序只改合同表;唯一落位口 `apply_layer_contract()`——F6);禁散落 `move_child`。**与调试角标遮挡的结论**:`debug_badge` 是末位 `float` 类、不注册 ModalStack,`ModalStack.open` 只隐藏登记层 ⇒ 浮窗开不波及角标节点(结构语义);但角标**发布态恒隐**(F10,`main.gd:98` `_debug_entry` 缺省 false),故「角标恒压浮窗且保持可点/可见」不是本批可跑断言面——删该运行期断言,以「浮窗登记序+open 只动登记层」结构断言承载,开发期临时启用角标后的浮窗不遮角标观感归用户目检。另写明一条栈语义防误触 L3:浮窗开会隐藏登记序在其上的已开层(如 debug_overlay),关闭恢复——属既有栈语义非新决策。**浮窗句柄寻址(v1.5 钉死)=`ui.modals.camp.esc_overlay` 二层**(营地子容器,卡⑥ 建件;`ui.modals.flow.open/close` 调用形态不变——flow 是全局调度器、非句柄册成员)。
|
||||
|
||||
🔴 **契约 8 · provisional 口径留痕**:裁决 10「先按顶部小HUD栏设计」的「先」字留痕——HUD 栏形态按最小集落地(只承载钥石余额),扩项=新裁点。
|
||||
🔴 **契约 8 · provisional 口径留痕**:裁决 10「先按顶部小HUD栏设计」的「先」字留痕——HUD 栏形态按最小集落地(只承载钥石余额),扩项=新裁点。
|
||||
|
||||
🔴 **契约 9 · Esc 输入语义四态与接线定稿(v1.4 新立,2026-11-01 用户裁采纳双审推荐)**:Esc 路由按**四级优先序**,判据单点=ModalStack 登记册现开集合(禁手枚名单——设置面/ConfirmDialog/五子界面自然全被覆盖):
|
||||
①**除浮窗外任一登记模态开态 → 无反馈**(「设置开在浮窗之上」态被本条覆盖=无反馈,关设置后浮窗自然重现——契约 3 栈语义;旧三态表「隔设置层误关不可见浮窗」歧义就此消解);
|
||||
②**浮窗开态 → `close(camp_esc_overlay)`**(同键唤起/关闭单义切换,Q2 甲);
|
||||
③**营地语境(`is_camp_context()`:营地场景屏 visible)→ `open(camp_esc_overlay)`**;
|
||||
④**非营地语境 → 无反馈**(现役「选档语境 Esc 零响应」语义保持,契约 2)。
|
||||
**接线定稿(同日裁,出 §9 白名单)**:事件驱动 `_unhandled_input`,键值判定 `KEY_ESCAPE`(配 physical_keycode 桌面惯例);**禁 Input 单例轮询**(`Viewport.push_input` 合成链不更新 `Input.is_action_pressed` 动作态——合成探针必然测不到轮询路径);**不挂 ui_cancel**(Beta 手柄面往 ui_cancel 挂键会致语义漂移;用户裁决原话「ESC键」;08:193 键盘面只加 KEY_ESCAPE 键值判定、不新增 InputMap action)。**触发沿=pressed 且非 echo**、忽略释放沿(真实键盘长按 echo 连发禁翻转浮窗;V1 补合成 echo 断言零响应)。
|
||||
|
||||
**拆分边界纪律适用性判定(req-card 第 15 节/code-conventions §1.1)**:本卡=**新增功能批**——新增 Esc 浮窗视图、营地顶栏 HUD 栏、Esc 键盘输入接线三类件,其余为存量文件接线改造;无「拆分存量单元/收敛/重构」动作,`camp_overlay_view.gd` 四柜台拆解退役属卡④ 职责(本卡不改该文件)。按 code-conventions §1.1 拆分判据五条,本卡不触发拆分/收敛/重构类,故不在设计契约区写拆分红线条款,仅在此写明判定理由。同理 req-card 第 13 节「禁为机检文本保留无效成员」(重构类卡必填)不适用;新增件类的「机检预算互斥=上游缺陷停卡退回写卡段」(第 9/14 节)已写入 §8 L3 与「实现规范预算」节。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
@@ -70,116 +80,225 @@
|
||||
|
||||
**不做**(明示去向):
|
||||
- 营地场景图本体 → 卡④(本批只在其顶栏位落 HUD+挂刷新单口);
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①;
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①(工程波已随迭代 33 合回);
|
||||
- 其余键盘快捷键(任意键关闭浮窗/手柄/战斗内 Esc) → 用户未裁,禁自扩(契约 2);
|
||||
- 设置面/退出路径本体改动 → 无(契约 3);
|
||||
- HUD 栏扩项(冒险天数/种子等) → 契约 8,扩项另裁。
|
||||
- HUD 栏扩项(冒险天数/种子等) → 契约 8,扩项另裁;
|
||||
- 不新建 camp golden(卡④ 契约 10:现无 camp 逐像素 golden,camp 验收=shot_probe 出图+shot_checker 语义断言)。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
> **v1.3 口径**:本批禁写 `design/` 整目录与根规则文件,唯一可写目录 = `reports/`;下表一律执行为列进反哺点清单。
|
||||
> **现行口径**:实现批禁写 `design/` 整目录与根规则文件、两份根流程清单与 `game/tests/README.md`,唯一可写目录=`reports/`(+`game/` 实施本体);下表一律执行为列进 `reports/iter<N>-反哺点-<日期>.md` 反哺点清单(义务格全为「反哺点 · 列清单」类,禁旧「随批」措辞)。待办清单与体验验证清单已迁仓库根(原 `design/gdd/06-*`、`07-*` 路径已不存在)。
|
||||
|
||||
| 文档 | 关联点 | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/08-Alpha版本标准.md` | :192「键盘 / 手柄输入(排 Beta)」行——Esc 营地浮窗已落地(用户明示裁决,澄清⑤裁决 9),须加注防「Beta 行=本批违例」误读 | 反哺点 · 行内加注(v1.1 行号订正 :192) |
|
||||
| `design/art/art-bible.md` | §6.2「钥石余额常驻顶栏」呈现位从 CampOverlay 顶栏迁场景屏 HUD(:369 述) | 反哺点 · 随卡④ §6.2 改版一并落 |
|
||||
| `design/gdd/ux/camp.md` | 顶栏铭牌/钥石徽记述(:14-20)随 HUD 栏改挂 | 反哺点 · 随 camp.md 改版落 |
|
||||
| `design/gdd/06-待办清单.md` | 本卡登记条 | 反哺点 · 列清单 |
|
||||
| `design/gdd/07-体验验证清单.md` | 新增目检条(Esc 浮窗三动线/HUD 余额观感)——登记不阻塞 | 反哺点 · 列清单 |
|
||||
| `roadmap.md` | 迭代行编入/状态刷新 | 反哺点 · 列清单 |
|
||||
| `game/presentation/*`(浮窗视图+接线)、`game/smoke/*` | 实现与断言本体 | 实施本体(可写) |
|
||||
| `design/gdd/08-Alpha版本标准.md`(v1.13) | :193「键盘 / 手柄输入(排 Beta)」行——Esc 营地浮窗已落地(用户明示裁决,澄清⑤裁决 9),须加注防「Beta 行=本批违例」误读 | 反哺点 · 列清单(行内加注) |
|
||||
| `design/art/art-bible.md`(v3.31) | §6.2「钥石余额:常驻画面顶部的一行小 HUD 栏」(:396)呈现位从 CampOverlay 顶栏迁场景屏 HUD | 反哺点 · 列清单(权威面零直改,过用户裁决后归文档批) |
|
||||
| `design/gdd/ux/camp.md`(v2.2) | §顶栏(:39-41)「钥石余额常驻顶部小 HUD 栏、刷新入口=营地刷新单口(卡⑤同口挂接)」实装状态回填 | 反哺点 · 列清单 |
|
||||
| `待办清单.md`(仓库根) | 本卡登记条(五卡总条,:447 起——原引 438-449 行号漂移,v1.4 订正)状态推进 | 反哺点 · 列清单(实现批禁写待办清单) |
|
||||
| `体验验证清单.md`(仓库根) | 新增目检条(Esc 浮窗三动线/HUD 余额观感/浮窗视觉面)——登记不阻塞 | 反哺点 · 列清单(实现批禁写体验验证清单,入册条目由文档批落) |
|
||||
| `roadmap.md`(仓库根) | 迭代行编入/状态刷新 | 反哺点 · 列清单(实现批禁写 roadmap) |
|
||||
| `game/tests/README.md` | 能力行入表(§7 对照表「能力行」列定名) | **已入表**(旅程段六,本卡两条新增能力行在役)——实现批只核对名实一致,禁写 |
|
||||
| `game/presentation/*`(浮窗视图/HUD 栏/接线/注册面)、`game/probe/*` | 实现与断言本体 | 实施本体(可写) |
|
||||
|
||||
## 卡前·文件结构与职责(T0 前)
|
||||
|
||||
定义 T 卡前先列全本批新建/修改文件,一文件一职责。**每个文件行标「行为改动/非行为改动」并指向 §7 对照表行号;「落位」列与「落位与可复用性预检」节互指**(标「新建」的必须能在预检节指到不能复用的理由与唯一落点核查)。行号为开工日 T0 复核对象。
|
||||
|
||||
| 文件 | 职责 | 改动类型 | 落位 | 对照表行 |
|
||||
|---|---|---|---|---|
|
||||
| `game/presentation/views/camp_esc_overlay.gd` | Esc 浮窗视图:`UiKit.modal_shell` 模态壳+三钮(返回主菜单/设置/退出游戏,退出用 `UiKit.danger_button`)+Q2 再按 Esc 关窗;零新素材 | 行为改动 | 新建 | V1/V2/V5/B1 |
|
||||
| `game/presentation/views/camp_hud_band.gd` | 营地顶栏小 HUD 栏:承载 `UiKit.keystone_badge`(钥石余额单口读数);挂接卡④ `CampRefreshBus.refresh()` 刷新 | 行为改动 | 新建 | V6/B2 |
|
||||
| `game/presentation/main_esc_flow.gd` | 组合根 Esc 流程承接件:键盘输入路由+营地语境判据+Q2 再按关窗+三出口动线(返回主菜单/设置/退出);静态承接件,main 侧留同名薄委托 | 行为改动 | 新建 | V1/V3/B1/B3 |
|
||||
| `game/presentation/main.gd` | 组合根:Esc 输入入口+`main_esc_flow.gd` 同名薄委托+返回主菜单动线接线 | 行为改动 | 复用既有(改) | V1/V3/B3 |
|
||||
| `game/presentation/ui_registry.gd` | `LAYER_CONTRACT` 加 `camp_esc_overlay` 槽(登记于营地层与 `settings_overlay` 之间,契约 3 正解)+`node()` 寻址(`camp.*` 二层解析由卡⑥ 建件承担,本卡只消费) | 行为改动 | 复用既有(改) | V8/B1 |
|
||||
| `game/presentation/main_overlay_flow.gd` | `_on_settings_closed` 语境分支:营地浮窗路径关设置回浮窗、不回主菜单(契约 3;谓词=camp_esc_overlay.visible) | 行为改动 | 复用既有(改) | V4/B3 |
|
||||
| `game/presentation/ui_modals.gd` | 本卡零句柄新增(浮窗句柄落 `ui_camp_modals.gd` 子容器——卡⑥ 前置建件,2026-11-01 用户裁);类变量 14 预期不升(S13 触面消除,见「实现规范预算·存量触面一查」) | 非行为改动(卡⑤ 段零改) | 复用既有 | V8/B1 |
|
||||
| `game/presentation/ui_camp_modals.gd`(卡⑥ 新建) | 营地域句柄子容器:本卡 T1 落第六枚句柄 `esc_overlay`(寻址 `ui.modals.camp.esc_overlay` 二层;卡④ T3 已落五枚) | 行为改动 | 卡⑥ 新建(跨卡共改) | V8/B1 |
|
||||
| `game/presentation/main_ui_build.gd` | 浮窗实例化装配点:`.new()`+装配尾 `apply_layer_contract(root)`(先例 :105/:121/:242;S13 触面) | 行为改动 | 复用既有(改) | V8/B1 |
|
||||
| `game/presentation/views/camp_scene_view.gd`(卡④ 新建) | 顶栏预留位 `set_hud_band()` 填 `CampHudBand` 实例(卡④ T2 跨卡接口①「卡⑤ 填值」);本卡只填值、不改场景图铺底/命中区逻辑 | 行为改动 | 卡④ 新建(跨卡共改,仅顶栏填值) | V6/B2 |
|
||||
| `game/probe/screen_flow_probe.gd` | 探针宿主:扩展现 :153-165 Esc 合成段为营地语境浮窗断言+三动线+Q2 关窗+HUD 余额读数;与卡④ T5 营地键段按行域分界(§0) | 非行为改动(测试面) | 复用既有(改) | V1/V3/V4/V6 |
|
||||
| `game/probe/ui_struct_probe.gd` | 浮窗三钮结构断言+零动效契约段(仿 career :379-391)+顶栏位/登记序结构断言 | 非行为改动(测试面) | 复用既有(改) | V2/V7/V8 |
|
||||
| `game/tests/README.md` | 能力行入表(四列:能力|系统|真实入口|代表用例) | 非行为改动(文档,写卡段) | 主 Agent 统一收口 | 全表能力行 |
|
||||
| `reports/iter<N>-实现报告-<日期>.md`/`reports/iter<N>-反哺点-<日期>.md`/`reports/iter<N>-验收审计-<日期>.md` | T4 三份产出物 | 非行为改动(过程件) | 新建 | T4 |
|
||||
|
||||
## 落位与可复用性预检
|
||||
|
||||
定义 T 卡前,对本批每个**新增文件/新增函数/新增数据件**过三问(复用面/唯一面/分层与交互面),与「卡前·文件结构与职责」互指。生效=2026-10-21 用户立规;本卡虽文件名日期 2026-10-18,经 2026-10-31 用户明令补充为现行有效卡,按现行规则补齐。
|
||||
|
||||
### 新增件三问
|
||||
|
||||
| 新件 | 复用面(grep 同职责实现) | 唯一面(grep 同名同义标识符) | 分层与交互面(调用路径+跨层出口) |
|
||||
|---|---|---|---|
|
||||
| `camp_esc_overlay.gd` | 盘上视图件=`camp_overlay_view.gd`(CampOverlay 四柜台,随卡④ 形态退役、反参照禁复用)、`settings_overlay.gd`/`career_overlay.gd`(各有专职、无三钮 Esc 浮窗同职责实现)、`ui_kit.gd modal_shell`(工厂只产 dim+center+panel+VBox、不产完整浮窗)。grep `camp_esc`/`esc_overlay` 盘上无同职责实现 → 不能复用,须新建视图件;模态壳/按钮复用 `UiKit.modal_shell`(`ui_kit.gd:486`)/`UiKit.danger_button`(:699),零新素材零新色值 | grep `camp_esc_overlay`/`EscOverlay` 盘上无第二处;三钮文案「返回主菜单/设置/退出游戏」只此一处定义,禁在探针/T 卡各写一份 | 呈现层视图,零 Core 写调用;三钮 `pressed` → 组合根 `main_esc_flow.gd` → 现役动线(`_quit_game`/`ui.modals.flow.open(settings_overlay)`/返回主菜单);Q2 再按 Esc 关窗由组合根路由,视图只发 `closed` 信号 |
|
||||
| `camp_hud_band.gd` | 盘上钥石徽消费面四处(F4),但**无「顶栏 HUD 栏」独立视图件**——现钥石徽直接挂 CampOverlay 顶栏(`camp_overlay_view.gd:125`),随卡④ 拆解退役;新 HUD 栏须独立承载读数+挂接刷新单口(接收推送,非信号订阅),职责不同 → 不能复用,须新建 | grep `camp_hud_band`/`CampHudBand`/`hud_band` 盘上无第二处;余额读数只经 `camp_data()` 单口(F9),禁视图自算 | 呈现层视图;`UiKit.keystone_badge`(`ui_kit.gd:632`)承载显示;挂接卡④ `CampRefreshBus.refresh(camp_data)` 单口刷新(跨卡接口②);经卡④ `camp_scene_view.gd set_hud_band()` 顶栏预留位挂入(跨卡接口①);零 Core 写调用 |
|
||||
| `main_esc_flow.gd` | 盘上组合根承接件=`main_overlay_flow.gd`(设置/状态/生涯/选档/图例/钱包六类覆盖层开合与数据投喂)、`main_adventure_flow.gd`、`main_debug_flow.gd`。**无「键盘输入路由+Esc 三出口动线」同职责实现**;并入 `main_overlay_flow.gd` 会使其承载第二变化原因(开合数据投喂 vs 键盘输入交互),且该文件已 6 类开合 → 不能复用,须新建单一职责承接件;`main.gd` 留同名薄委托(与现行拆件模式一致,探针 `main.X` 路径零改动) | grep `main_esc_flow`/`_on_esc`/`EscFlow` 盘上无第二处;营地语境判据只此一处定义(营地场景屏 visible 且无营地子模态开态),禁散落 | 组合根拆件(静态、零成员态,仿 `main_overlay_flow.gd`);键盘输入 → 营地语境判据 → 浮窗 `ui.modals.flow.open/close`(模态栈单口);三出口 → 现役动线;返回主菜单 → 营地隐+选档层同收+开始层恢复(卡④ T3 同口径);Core 零 UI 不破 |
|
||||
|
||||
### 非新增件说明
|
||||
|
||||
- `main.gd`/`ui_registry.gd`/`main_overlay_flow.gd`/`camp_scene_view.gd`(卡④ 新建):存量或邻卡文件改造,不新增本卡文件;公开方法/成员预算见「实现规范预算」存量触面一查。
|
||||
- `game/probe/screen_flow_probe.gd`/`ui_struct_probe.gd`:复用既有探针宿主(`screen_flow_probe` 已在 `tools/probe_gate.sh:142-146` 登记、零登记成本;本卡 T3 不另起新文件),只加断言段。
|
||||
- **数据件/RNG/Core**:本卡纯呈现层+输入接线,零玩法数值、无新增 `data/tables/` 表(钥石余额读数经既有 `camp_data()` 单口);数值单一事实源 `data/tables/` 不被触碰;RNG 零新增(RNG 单点 RngService 不被触碰);Core 零 UI 依赖不因新增浮窗/输入接线绕过。
|
||||
- **浮窗新槽落点唯一**:`camp_esc_overlay` 只在 `ui_registry.gd LAYER_CONTRACT` 登记一次、`node()` 寻址一次;禁在探针/T 卡另写层序字面量。
|
||||
|
||||
## 实现规范预算
|
||||
|
||||
定义接口块/文件结构图前,对本批每个**新增文件**对照 gdstyle 默认阈值做预算,与接口块及「卡前·文件结构与职责」互指。**生效说明**:规范写「生效=文件名日期 ≥2026-10-27,此前卡片属历史留档不回头改」——本卡经 2026-10-31 用户明令补充为现行有效卡,按现行规则补齐,不援引「不回头改」跳过。gdstyle v0.4.0 默认阈值:**公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6**(阈值取工具默认,改阈值=基线口径变须在本批重生成)。
|
||||
|
||||
### 新件三查
|
||||
|
||||
| 触面 | ①公开方法数 | ②成员变量数 | ③迁移函数对账 | 结论 |
|
||||
|---|---|---|---|---|
|
||||
| `camp_esc_overlay.gd` | 预期 ≤6:`setup_buttons()`/`set_close_path()`/`bind_signals()`/`_on_pressed(key: String)` 等,远低 ≤20 | 预期 ≤8:三钮句柄+modal_shell 句柄+`closed` 信号,低 ≤15 | 无自存量文件迁入函数 | 三查通过;T0 实测实数回填,超阈值即拆件或砍面(砍面=退设计段裁职责) |
|
||||
| `camp_hud_band.gd` | 预期 ≤4:`bind_refresh_bus()`/`set_balance()`/`refresh()`,远低 ≤20 | 预期 ≤4:`keystone_badge` 句柄+刷新单口句柄,低 ≤15 | 无迁入函数 | 三查通过;T0 实测实数回填 |
|
||||
| `main_esc_flow.gd` | 预期 ≤7:`on_esc_input()`/`is_camp_context()`/`open_overlay()`/`close_overlay()`/`_on_return_to_menu()`/`_on_settings()`/`_on_quit()`,远低 ≤20 | 预期 0(静态承接件、零成员态,仿 `main_overlay_flow.gd`) | 无迁入函数(新写,不搬存量函数) | 三查通过;T0 实测实数回填 |
|
||||
|
||||
### 存量触面一查
|
||||
|
||||
本卡触碰存量文件:`main.gd`/`ui_registry.gd`/`ui_modals.gd`/`main_ui_build.gd`/`main_overlay_flow.gd`/`camp_scene_view.gd`(卡④ 新建)/`screen_flow_probe.gd`/`ui_struct_probe.gd`。**T0 读数步骤写明「S13 键较 `tools/gdstyle_baseline.txt` 登记值只降不升」的预期与达成手段**:`main.gd` 组合根新增 Esc 输入入口与薄委托(不引入重复 load/循环内 await/循环内分配等 S13 违规类型,键只降不升);`ui_registry.gd` 合同表加一槽、`node()` 加一寻址分支(不新增公开方法面);`ui_modals.gd` 零句柄新增(浮窗句柄落 `ui_camp_modals.gd` 子容器,类变量 14 预期——v1.4「14→15 恰贴」句作废:该算术只算本卡 +1、未算卡④ 同批净+4,两卡合计 19>15,2026-11-01 停机呈报裁决改子容器);`ui_camp_modals.gd`(卡⑥ 建件)本卡落第六枚句柄(类变量 5→6);`main_ui_build.gd` 加浮窗实例化与装配(方法面微增,T0 实测对账);`main_overlay_flow.gd` `_on_settings_closed` 加语境分支(单函数分支数仍 ≤8、返回 ≤6);`camp_scene_view.gd` 只填 `set_hud_band()` 顶栏位;探针改接=加断言段、不新增扫描面键。任何 S13 键被迫上升=§8 L3 停卡。**`main.gd` 为组合根架构先例,其公开方法基数不在本卡新件三查面内**(第 14 节三查只对新增文件)。
|
||||
|
||||
### 指标例外通道
|
||||
|
||||
现预算内,无需例外。若三新件实测超默认阈值,或组合根薄委托致 S13 键上升,设「指标例外申报」节——键名+理由+用户特批落基线;未申报的超预算面进实现段=停卡退回写卡段(写进 §8 L3)。
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核全部行号。
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核全部行号。步骤用 `- [ ]` 复选框记步,一步=一个动作+一个可核查结果;每卡声明「取用/产出」接口块,后卡引用的名字与早卡定义逐字一致;禁占位待定句式(不决定任何事的行不留)。
|
||||
|
||||
### T0 事实坐实与跨卡对齐(≈0.5–1d)
|
||||
|
||||
- 复核 §2 行号;确认卡④场景屏顶栏预留位接口+营地刷新单口签名(跨卡);
|
||||
- **Esc 输入合成 spike**(v1.1 补):headless 下键盘事件合成机制验证(现成先例只有鼠标 `Viewport.push_input`——F1);与 §9 第 1 条接线方式选型联动(InputMap action+轮询路线与合成探针不兼容,选型时一并定);
|
||||
- Q1 子界面行为、Q2 关闭路径、Q3 范围确认裁定执行;
|
||||
- 与卡④ T0 互证终局落点不变量(契约 4 前提);探针入口路径依赖卡②③链的时点确认(§0)。
|
||||
- 取用:待办清单五卡总条实况(卡④ v3.1 现行、卡②③ 迭代 33 合回)、§0 跨卡接口、卡④ v3.1 §0.5/T2/T3、`game/tests/README.md` 能力表。
|
||||
- [ ] 复核 §2 F1–F10 全部 `file:line` 行号;漂移就地订正、订正不了的写明「开工日 T0 复核」,不硬编旧行号。
|
||||
- [ ] 与卡④ 对齐三项接口:①顶栏预留位 `camp_scene_view.gd set_hud_band()` 填值签名;②营地刷新单口 `CampRefreshBus.refresh(camp_data: Dictionary) -> void`;③终局落点不变量(现役=终局 continue 回主菜单,若改落点为营地则本卡返回动线补 `_begin_session(false)`)——接口名保持既有,不改名;与卡④ 确认 `screen_flow_probe.gd` 行域分界(§0)。
|
||||
- [ ] **Esc 输入合成先例复核**:读 `game/probe/screen_flow_probe.gd:153-165`(`InputEventKey`+`Viewport.push_input` 合成 Esc、断言选档语境零响应),确认该机制可迁移到营地语境;接线方式已定稿(契约 9——事件驱动 `_unhandled_input`+`KEY_ESCAPE`,2026-11-01 裁),T0 复核合成链可达 unhandled 段(push_input 不更新 Input 单例动作态——禁轮询路线的实据)并回写文件结构图接线落点。
|
||||
- [ ] Q1/Q2/Q3 裁定执行确认(甲/甲/甲,见 §10);开工门核:U0 发令、grilling 复核门过、卡④ 代码波状态。
|
||||
- [ ] 跑 `bash tools/static_scan.sh` 开发期局部读数(gdstyle 键=S13,闸件 `tools/gdstyle_gate.py`——原 `tools/gdstyle.sh` 不存在,v1.4 订正),记录三新件预算实数与存量触面 S13 基线键(预期只降不升);本步最窄确认,不构成验收。
|
||||
- 产出:基线 commit、三新件接口块定稿(签名/取值)、S13 读数、跨卡接口核对表、Esc 合成先例复核结论;后续卡只按本卡契约施工。
|
||||
|
||||
### T1 Esc 浮窗(≈1–1.5d)
|
||||
### T1 Esc 浮窗视图与键盘接线(≈1–1.5d)
|
||||
|
||||
- 浮窗视图:模态壳(`UiKit.modal_shell` 先例——该工厂只产 dim+center+panel+VBox、无内置关闭件,关闭件按 Q2 裁定)+三钮(返回主菜单/设置/退出游戏);危险钮形制用于退出(`UiKit.danger_button` 先例);零新素材零新色值;
|
||||
- 接线:返回主菜单→营地场景屏隐藏+选档层同收+开始层恢复(卡④动线同口);设置→`_open_settings`(**语境分支**:浮窗登记于设置层之下,关设置回浮窗——契约 3);退出→`_quit_game`;
|
||||
- **营地语境判据(v1.1 补)**:判据=营地场景屏 visible(根态与子界面开态下皆真——子界面是模态叠加非熄屏;screen_flow_probe 营地旗标先例 :53 可迁移复用);Q1 裁甲则判据加「无营地子模态开态」;调试浮窗开态不影响判据(全局浮层、营地屏仍可见);Q1 裁后回写契约 2 消歧;
|
||||
- 层序入 `LAYER_CONTRACT`(与调试角标遮挡按契约 7 结论表达);零动效(契约 6);
|
||||
- Q2 关闭路径接线(再按 Esc 关/关闭钮/无关闭,按裁定)。
|
||||
- 取用:T0 三新件接口块、Esc 合成先例复核结论、`ui_kit.gd:486 modal_shell`/:699 `danger_button`/:632 `keystone_badge`、`ui_registry.gd:15-35 LAYER_CONTRACT`。
|
||||
- [ ] 在 `camp_esc_overlay.gd` 实现 `setup_buttons()`:`UiKit.modal_shell`(`ui_kit.gd:486`,只产 dim+center+panel+VBox、无内置关闭件)+三钮(返回主菜单/设置/退出游戏,退出用 `UiKit.danger_button` :699);零新素材零新色值。
|
||||
- [ ] 在 `camp_esc_overlay.gd` 实现 `set_close_path()`/`bind_signals()`:Q2 裁甲=再按 Esc 关窗(同键唤起/关闭单义切换),关闭由组合根路由、视图只发 `closed` 信号;三钮 `pressed` 外发不内含动线。
|
||||
- [ ] 在 `main_esc_flow.gd` 实现 `is_camp_context() -> bool`:判据=营地场景屏 visible(子界面是模态叠加非熄屏;复用 `screen_flow_probe.gd:54` 营地旗标先例——:53 是菜单行、:54 才是营地旗标,v1.4 订正);「子界面开态不唤浮窗」(Q1 甲)由契约 9① 现开集合判据承载,不并入本谓词;调试浮窗开态不影响判据(全局浮层、营地屏仍可见)。
|
||||
- [ ] 在 `main_esc_flow.gd` 实现 `on_esc_input(ev: InputEvent) -> void`/`open_overlay() -> void`/`close_overlay() -> void`:按**契约 9 四态优先序**路由(①除浮窗外任一登记模态开→无反馈②浮窗开→close③营地语境→open④非营地语境→无反馈),判据单点=ModalStack 登记册现开集合、禁手枚名单;接线定稿=事件驱动 `_unhandled_input`+`KEY_ESCAPE`(physical_keycode)、禁 Input 单例轮询、不挂 ui_cancel、pressed 且非 echo(契约 9 接线定稿 2026-11-01 已裁,不再 T0 选型)。
|
||||
- [ ] 在 `ui_registry.gd` `LAYER_CONTRACT` 加 `camp_esc_overlay` 槽(位于营地层与 `settings_overlay` 之间,契约 3 登记序正解)+`node()` 寻址;句柄落 `ui_camp_modals.gd` 子容器第六枚 `esc_overlay`(卡⑥ 前置建件,寻址 `ui.modals.camp.esc_overlay` 二层)——禁平铺回 `ui_modals.gd`(S13 上限,2026-11-01 用户裁);禁散落 `move_child`(F6)。
|
||||
- [ ] 在 `main.gd` 加 Esc 输入入口(`_unhandled_input`)与 `main_esc_flow.gd` 同名薄委托(探针 `main.X` 路径零改动);`main.gd` 的 `_unhandled_input` 将是产品码首个键盘入口——`main_esc_flow.gd` 头注自陈此身份(2026-11-01 双审注)。
|
||||
- [ ] 零动效(契约 6):浮窗子树零 AnimationPlayer、源码禁 `create_tween`/`UiKit.motion` 令牌(仿 `ui_struct_probe.gd:379-391`)。
|
||||
- 产出:`CampEscOverlay.setup_buttons()`/`set_close_path()`/`bind_signals()`、`closed` 信号;`MainEscFlow.on_esc_input()`/`is_camp_context()`/`open_overlay()`/`close_overlay()`;`LAYER_CONTRACT` 新槽键 `camp_esc_overlay`;T2 按接口块接线。
|
||||
|
||||
### T2 营地顶栏小 HUD 栏(≈0.5–1d)
|
||||
|
||||
- 顶栏位落 `UiKit.keystone_badge` 工厂件(余额单口读数,契约 5);
|
||||
- **刷新挂卡④营地刷新单口**(契约 5);**刷新时机清单落定(v1.1 补)**:①购买后(挂单口)②进档/回营地进入时重读③夹具直写后由探针显式触发——断言前须显式触发,禁依赖「自动重读」假设;
|
||||
- HUD 栏形态按最小集(契约 8):只承载钥石余额;
|
||||
- **camp 截图/golden 联动条(v1.1 补)**:HUD 落位后核 camp 截图漂移热区→限 HUD 区显式更新+呈报(仿卡④ T5/卡② 契约 6 口径;现无 camp 逐像素 golden——卡④ 契约 10)。
|
||||
- 取用:T1 `MainEscFlow`、`camp_hud_band.gd` 接口块、卡④ `CampRefreshBus.refresh(camp_data)` 单口、`camp_scene_view.gd set_hud_band()` 顶栏预留位、`camp_shop_presenter.gd:62 camp_data()` 读数单点。
|
||||
- [ ] 在 `camp_hud_band.gd` 实现 `set_balance(v: int) -> void`/`bind_refresh_bus(bus: Object) -> void`/`refresh(camp_data: Dictionary) -> void`:`UiKit.keystone_badge`(`ui_kit.gd:632`)承载,读数只经 `camp_data()` 单口(F9/契约 5,禁第二账本、禁视图自算);挂接卡④ `CampRefreshBus.refresh(camp_data)`;载体循 UiKit `page_shell` 的 `PRESET_TOP_WIDE`+`custom_minimum_size` 先例(ui_kit.gd:523-525),禁手动 rect。
|
||||
- [ ] 在卡④ `camp_scene_view.gd` 的 `set_hud_band()` 顶栏预留位填 `CampHudBand` 实例(高 48px 浮层覆盖、不挤压画面;卡④ T2 跨卡接口①);`camp_scene_view.gd` 场景图铺底/命中区逻辑零改(卡④ 职责)。
|
||||
- [ ] 刷新时机清单落定(契约 5):①购买后(挂单口)②进档/回营地进入时重读③夹具直写后探针显式触发——断言前须显式触发,禁依赖「自动重读」假设;②的触发主体=组合根进档动线尾(营地开层同口)显式调 `CampRefreshBus.refresh(camp_data())` 一闸(契约 5 v1.4 补钉),禁 band `_ready` 自刷或自持第二入口。
|
||||
- [ ] HUD 栏形态按最小集(契约 8):只承载钥石余额;扩项=新裁点。
|
||||
- [ ] camp 截图/golden 联动:HUD 落位后核 camp 截图漂移热区→限 HUD 区显式更新+呈报;现无 camp 逐像素 golden(卡④ 契约 10),camp 验收=shot_probe 出图+shot_checker 语义断言。
|
||||
- 产出:`CampHudBand.set_balance()`/`bind_refresh_bus()`/`refresh()`、顶栏填值、刷新时机清单;T3 按接口块验收。
|
||||
|
||||
### T3 断言批(≈0.5–1d)
|
||||
### T3 三出口动线、设置语境分支与断言批(≈1–1.5d)
|
||||
|
||||
- 探针(**宿主选 screen_flow_probe——v1.1 定:轻闸默认段且「菜单⇄营地屏壳切换」正是其职域,零闸登记成本;若另起新文件须改 tools/probe_gate.sh 登记=§5 可写面外,禁**):合成 Esc 键事件→浮窗可见+三项在位;非营地语境(boot 菜单)不唤起;返回主菜单动线(营地隐+选档层收+菜单显);设置项开合(**含营地浮窗→设置→关闭回浮窗高危路径+「关闭设置后禁弹主菜单」反向断言——v1.1 补**);退出项接线断言(headless 不可真退);
|
||||
- HUD:余额读数=存档单口(夹具经**隔离存档实例直写** `add_keystone` 或等效写口——smoke 既有先例 smoke_all.gd:5440;**不经 DebugOverlay 界面——其钥石项是只读投影,debug_overlay.gd:173-189,v1.1 订正**);直写后显式触发刷新断言;
|
||||
- 契约断言:浮窗零动效(career 同款段复用/仿写);层序登记(冒烟 V1d);**浮窗开态 `debug_badge.visible==true`**(仿 ui_click_probe.gd:174-177 设置面开中角标可见先例——v1.1 补);
|
||||
- **变异验证伴生**:Esc 接线漏营地隐(预期动线断言红)、HUD 读缓存旧值(预期刷新断言红)、浮窗加第四钮(预期三项断言红)、**设置关闭后误回主菜单**(预期语境断言红——v1.1 补)、**Esc 再按不关窗**(预期关窗断言红,若 Q2 裁甲——v1.1 补);注入→最窄闸见红→精确还原→记录。
|
||||
- 取用:T1/T2 产物、`main_overlay_flow.gd:11-13 _on_settings_closed`、`main.gd:143 _quit_game`、`camp_shop_presenter.gd:50-53 on_camp_closed`、`screen_flow_probe.gd:153-165` Esc 合成段、`ui_struct_probe.gd:379-391` 零动效段。
|
||||
- [ ] 在 `main_esc_flow.gd` 实现 `_on_return_to_menu() -> void`:**首步 close 浮窗**(`ui.modals.flow.close(camp_esc_overlay)`——浮窗是模态层,屏壳可见性收不了它,缺此步浮窗残留主菜单上方;契约 4 v1.4 补)→ 营地场景屏隐藏+选档层同收+开始层恢复(卡④ T3 动线同口);营地语境⇔闲时会话(契约 4 前提),动线不含会话收束;回主菜单后菜单=卡②三钮、禁加「继续冒险」钮。
|
||||
- [ ] 在 `main_esc_flow.gd` 实现 `_on_settings() -> void`/`_on_quit() -> void`:设置→`ui.modals.flow.open(settings_overlay)`(浮窗登记于其下、关后遮挡解除重现);退出→`_quit_game`(`main.gd:143`,原样复用)。
|
||||
- [ ] 在 `main_overlay_flow.gd` `_on_settings_closed` 加语境分支:营地浮窗路径关设置回浮窗、不回主菜单(契约 3;现役 :13 `start_overlay.visible = true` 硬副作用仅菜单路径保留)。
|
||||
- [ ] 探针(宿主 `screen_flow_probe.gd`,已在 `tools/probe_gate.sh:142-146` 登记、零登记成本;不另起新文件):扩展 :153-165 Esc 合成段——营地根态合成 Esc→浮窗可见+三项在位;营地子界面开态合成 Esc→无反馈(Q1 甲,契约 9①);**设置开在浮窗之上合成 Esc→无反馈且浮窗不误关**(契约 9①——浮窗不隔层关闭,关设置后浮窗重现);再按 Esc 关窗(契约 9②);非营地语境(boot 菜单)不唤起(契约 9④);**冒险语境合成 Esc→浮窗不现**(契约 2 全局接线回归面,screen_flow_probe 冒险步内);**echo 事件合成→零响应**(触发沿 pressed 且非 echo,契约 9);返回主菜单动线(**浮窗自收**+营地隐+选档层收+菜单显);设置项开合(含营地浮窗→设置→关闭回浮窗高危路径+「关闭设置后禁弹主菜单」反向断言);退出项接线断言(headless 不可真退)。
|
||||
- [ ] HUD 余额断言:读数=存档单口(夹具经**隔离存档实例直写** `meta_state._add_keystone(9999)` 先例 `game/probe/ui_click_probe.gd:74`;**不经 DebugOverlay 界面——其钥石项是只读投影** `debug_overlay.gd:194-205`);直写后显式触发刷新断言。
|
||||
- [ ] 结构断言(`ui_struct_probe.gd`):浮窗三钮在位且仅三;零动效(仿 career :379-391);`LAYER_CONTRACT` 登记序(浮窗入合同表、`system_test.gd:94-106` V1c 树序链复跑);**浮窗开不波及 `debug_badge` 节点**(F10:发布态恒隐,改结构断言替代 v1.2 `visible==true` 断言)。
|
||||
- [ ] 正向验收断言(v1.2 计划的人工制造红取证条目整条已删,替换为 §7 上表正向断言):每条从真实入口进入、判据绑被测改动特有的可观测提交面(信号/入参/状态源取值/可见性/树序),在当轮真实跑批执行并通过;**禁把生产码改成错值看断言抓不抓得到、禁回退 hunk 看断言红不红**(人为制造红属伪造证据);判据绑不上=静态判据+评审注记申报观测面局限,证明做不出=停卡呈报。
|
||||
- 产出:三出口动线接线、设置语境分支、探针断言段(V1–V8 落点)、能力行名实核对表;T4 收口。
|
||||
|
||||
### T4 产出物落位卡(≈0.5d,末卡)
|
||||
|
||||
三份 `reports/` 写齐。
|
||||
- 取用:T0–T3 全部产物。
|
||||
- [ ] 里程碑步(T 段)出口跑一次代码域全套闸:`bash tools/run_all_gates.sh --code`;回执 `all_green=true`;任何新增红停卡,不得带红合回。波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套。
|
||||
- [ ] 独立验收审计由未参与实现的会话/子代理执行,产出 `reports/iter<N>-验收审计-<日期>.md`;未过禁合并(审计入口见 §7)。
|
||||
- [ ] 轻闸全绿、合并前写齐三份 `reports/`:
|
||||
- [ ] `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务+实现决定记录/代拍板追认单);
|
||||
- [ ] `reports/iter<N>-反哺点-<日期>.md`(§5 每条有落点:文件/节/原句/拟改句/依据,明确「实现批未写 design/」);
|
||||
- [ ] `reports/iter<N>-验收审计-<日期>.md`(独立审计逐行「通过/缺漏/例外及依据」)。
|
||||
- [ ] 用户目检项登记 `体验验证清单.md`(仓库根,反哺点列清单:Esc 三动线+HUD 余额观感+浮窗视觉面),登记完成即卡内义务了结,不阻塞批末收尾/合并/下一迭代开工。
|
||||
- 产出:三份 `reports/` 齐全+代码域闸全绿+验收审计通过,方算收口;合回后由本地 CI 跑不带参数的 `tools/run_all_gates.sh`(两域全量)复验。
|
||||
|
||||
**估时合计 ≈3–4.5d**(v1.1 订正:分项和=3.0–4.5d,原「2.5–3.5d」算术少算)。切分预案:T1/T2 后可切(浮窗先落、HUD 后落)——切分价值低,建议与卡④同批单跑。
|
||||
**估时合计 ≈3–4.5d**。切分预案:T1/T2 后可切(浮窗先落、HUD 后落)——切分价值低,建议与卡④同批单跑。
|
||||
|
||||
## 7. 验证闸(逐需求点验收清单)
|
||||
## 7. 验证闸:改动—验收对照表
|
||||
|
||||
| # | 需求点 | 测什么 | 落点/判定手段 |
|
||||
|---|---|---|---|
|
||||
| V1 | Esc 唤起(按 Q1 分拆) | **根态**合成 Esc→浮窗可见;**子界面开态按 Q1 案**(甲=不现/乙=先关子界面再现);boot 菜单语境不唤起(其余非营地语境由契约 2 红线+源码断言兜底——可达性单一,登记原因) | screen_flow_probe 合成键盘事件段(T0 spike 机制) |
|
||||
| V2 | 浮窗三项 | 返回主菜单/设置/退出游戏三钮在位且仅三 | ui_struct_probe 结构断言 |
|
||||
| V3 | 返回主菜单 | 营地场景屏隐藏+选档层同收+主菜单可见 | screen_flow_probe 型断言 |
|
||||
| V4 | 设置项(双路径) | 菜单路径(冒烟 V3g settings 段复跑)+**营地浮窗→设置→关闭回浮窗**(含禁弹主菜单反向断言) | 冒烟复跑+探针新路径段 |
|
||||
| V5 | 退出项 | 接线到 `_quit_game`(headless 不可真退——接线断言代偿) | 源码/结构断言 |
|
||||
| V6 | HUD 余额 | 读数=存档单口(隔离实例直写);直写后显式触发刷新;购买后刷新(挂卡④单口——跨卡联验) | 探针数据断言(smoke_all.gd:5440 同型先例) |
|
||||
| V7 | 零动效 | 浮窗子树零 AnimationPlayer+源码禁动效令牌 | ui_struct_probe 零动效契约段(career 同款) |
|
||||
| V8 | 层序与遮挡 | 浮窗入 LAYER_CONTRACT(冒烟 V1d);浮窗开态 debug_badge 可见 | 冒烟 V1d+探针 badge 断言 |
|
||||
| V9 | 目检项登记 | Esc 三动线+HUD 观感+浮窗视觉面(文字 WCAG 若入 shot_checker 取样,否则显式登记归目检并写明理由)入 07 | 件核(登记不阻塞) |
|
||||
| V10 | 反哺点清单 | §5 每条有落点 | 件核对表 |
|
||||
七格表:需求点|改动(文件:行/行为)|验收测试(名称+落点+尾缀)|层级(单元/接口/集成/探针)|真实入口|判据绑定面|能力行。**每个需求点至少一行;每个行为改动至少一行(V1–V10 需求点行+B1–B3 行为改动专行);每行必须有真实入口和判据绑定面——缺任一项=该需求点未验收,卡片不成立。** 断言尾缀〔ESC批 Vn〕/〔ESC批 Bn〕,与卡片清单互相 grep。能力行格式 `既有:<能力名>;新增:<能力名>`(中文分号分隔,对齐机检与卡④ v3.1 先例),两形态名称都须在 `game/tests/README.md` 能力表在场(既有=现役在场名,新增=本卡定名、由主 Agent 统一收口入表;本卡只写卡内能力行文字,README 由主 Agent 收口)。
|
||||
|
||||
**测试方式两分法**:V1–V8 机检自动化(能力验证经隔离存档实例直写钥石/开态——v1.1 订正:不经 DebugOverlay 界面);V9 归用户目检(生命周期末步统一进行,不阻塞迭代,澄清⑥口径)。
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 Esc 唤起(根态/子界面/关窗/非营地) | `camp_esc_overlay.gd` 浮窗视图;`main_esc_flow.gd on_esc_input()`/`is_camp_context()`/`open_overlay()`/`close_overlay()`;`main.gd` 输入入口 | `screen_flow_probe.gd:_ready`(Esc 唤起/子界面开态不唤起/再按关窗/非营地不唤起段)〔ESC批 V1〕 | 探针 | 营地场景根态合成 Esc 键事件(`InputEventKey`+`Viewport.push_input`,先例 :156-160)→ `main_esc_flow.on_esc_input()` → `ui.modals.flow.open/close` | 根态 Esc→浮窗 `visible=true`;再按→`visible=false`(绑 open/close 调用记录+浮窗可见性);子界面开态/设置开在浮窗之上合成 Esc→无反馈且浮窗不误关(契约 9①,浮窗不隔层关闭);boot 菜单与冒险语境合成 Esc→浮窗不现(契约 9④/契约 2,绑语境判据返回值);echo 合成→零响应(触发沿 pressed 且非 echo,契约 9) | 新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V2 浮窗三项 | `camp_esc_overlay.gd setup_buttons()` 三钮(modal_shell+danger_button) | `ui_struct_probe.gd:_ready`(浮窗三钮结构段)〔ESC批 V2〕 | 探针 | 浮窗视图实例化 → 三钮节点树 | 三钮节点存在且仅三(绑按钮名与数量;禁第四钮) | 既有:UI 全局主题与按钮五态(含对比度锚);新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V3 返回主菜单 | `main_esc_flow.gd _on_return_to_menu()`(营地隐+选档层同收+开始层恢复) | `screen_flow_probe.gd:_ready`(返回动线段)〔ESC批 V3〕 | 探针 | 浮窗「返回主菜单」钮 `pressed` → `main_esc_flow._on_return_to_menu()` → 屏显编排 | **浮窗 closed(动线首步自收,契约 4 v1.4)**+营地场景屏 `visible=false`+选档层 `visible=false`+`start_overlay.visible=true`(绑四处可见性/open-close 调用记录;`_screen_seals()` :51-57 互斥复跑) | 新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V4 设置项(双路径) | `main_esc_flow.gd _on_settings()`;`main_overlay_flow.gd _on_settings_closed` 语境分支 | `screen_flow_probe.gd:_ready`(营地浮窗→设置→关闭回浮窗段)〔ESC批 V4〕+菜单路径冒烟 V3g settings 段复跑 | 探针(+冒烟复跑) | 浮窗「设置」钮 → `ui.modals.flow.open(settings_overlay)` → 关闭 → `_on_settings_closed` 语境分支 | 设置开→浮窗 `visible` 恒 true(open 只隐登记序上方层——modal_stack.gd:27-31,浮窗登记在下、由树序遮挡非属性隐藏);关闭→浮窗重现+`start_overlay.visible=false`(绑语境分支谓词=camp_esc_overlay.visible;反向断言「关闭设置后禁弹主菜单」) | 既有:模态覆盖层编排与输入锁;新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V5 退出项 | `main_esc_flow.gd _on_quit()` → `main.gd:143 _quit_game()`(原样复用) | `ui_struct_probe.gd:_ready`(退出钮接线结构断言)〔ESC批 V5〕 | 探针(源码/结构,headless 不可真退) | 浮窗「退出游戏」钮 `pressed` → `_on_quit()` → `_quit_game()`(`settings_store.save_settings()`+`get_tree().quit()`) | 钮 `pressed` 连接目标=`_on_quit`→`_quit_game`(绑信号连接与调用链;headless 不真退=静态判据+评审注记申报观测面局限);**禁对退出钮 `pressed.emit()`**(防 `get_tree().quit()` 中止探针进程——quit(0) 仅靠 probe_gate 签名兜底不假绿,2026-11-01 双审补立) | 新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V6 HUD 余额 | `camp_hud_band.gd set_balance()`/`bind_refresh_bus()`/`refresh()`;`camp_scene_view.gd set_hud_band()` 填值;挂接卡④ `CampRefreshBus.refresh()` | `screen_flow_probe.gd:_ready`(HUD 余额读数/刷新段)〔ESC批 V6〕+`ui_struct_probe.gd:_ready`(顶栏位结构段) | 探针(跨卡联验:购买后刷新挂卡④单口) | 隔离存档实例 `meta_state._add_keystone(9999)`(`ui_click_probe.gd:74` 先例)→ 显式触发 `CampRefreshBus.refresh(camp_data)` → `camp_hud_band.refresh()` → `keystone_badge` 读数 | `value_label.text`=`"钥石 %d" % camp_data()["balance"]` 格式串比较(badge 文本带「钥石 」前缀——`ui_kit.gd:632` 工厂约定单点,禁实现期自造第二格式串;`keystone_balance()` 单口取值,2026-11-01 双审订正);直写后显式触发刷新才变(绑单口调用记录+badge 文本状态源) | 既有:元进度入账与锻造两轴;新增:营地顶栏 HUD 钥石读数与刷新单口同步 |
|
||||
| V7 零动效 | `camp_esc_overlay.gd` 直开直关(零 AnimationPlayer、源码禁 `create_tween`/`UiKit.motion`) | `ui_struct_probe.gd:_ready`(浮窗零动效契约段,仿 career :379-391)〔ESC批 V7〕 | 探针(结构/源码扫描) | 浮窗视图子树+`camp_esc_overlay.gd` 源码 | 两新件源码(camp_esc_overlay.gd+main_esc_flow.gd)+浮窗子树零 AnimationPlayer+源码零 `create_tween`/`UiKit.motion` 令牌(扫描面 >0 非空前置;扫描面扩两新件——2026-11-01 双审补立,开合编排件不脱扫) | 既有:模态覆盖层编排与输入锁;新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V8 层序与遮挡 | `ui_registry.gd` `LAYER_CONTRACT` 加 `camp_esc_overlay` 槽(营地层与 `settings_overlay` 之间)+`node()` 寻址 | `ui_struct_probe.gd:_ready`(浮窗登记序/遮挡段)〔ESC批 V8〕+`system_test.gd:_ready`(V1c 合同树序链 :94-106 复跑) | 探针(+系统测试复跑) | `main.ui.LAYER_CONTRACT` 单一事实源;`ui.modals.flow.open()` 遮挡记账 | 登记册序=合同模态序、直接子层树序=合同序逐对相等;`debug_badge` 末位 float 类不注册(open 只动登记层,绑登记册成员性;F10 发布态恒隐,不绑 `visible==true`) | 既有:模态覆盖层编排与输入锁;新增:Esc 浮窗唤起与三出口动线 |
|
||||
| V9 目检项登记 | `reports/iter<N>-实现报告-<日期>.md` 实机目检总清单 | 件核对(`体验验证清单.md` 登记,登记不阻塞)〔ESC批 V9〕 | 用户目检(非自动化) | 用户实机(生命周期末步统一进行,不阻塞迭代) | Esc 三动线+HUD 余额观感+浮窗视觉面(文字 WCAG 若入 shot_checker 取样,否则显式登记归目检并写明理由)入 `体验验证清单.md`=登记即义务了结 | 既有:模态覆盖层编排与输入锁 |
|
||||
| V10 反哺点清单 | §5 每条有落点 | 件核对表〔ESC批 V10〕 | 件核对(非自动化) | §5 同步面逐条对账 | §5 每条有 `reports/iter<N>-反哺点-<日期>.md` 落点 | 既有:模态覆盖层编排与输入锁 |
|
||||
| B1 Esc 输入接线与浮窗视图(行为改动专行) | `main.gd` 输入入口/薄委托;`main_esc_flow.gd on_esc_input()`/`is_camp_context()`/`open_overlay()`/`close_overlay()`;`camp_esc_overlay.gd` 新建;`ui_registry.gd` 新槽 | `screen_flow_probe.gd:_ready`(Esc 唤起/关窗/非营地段)+`ui_struct_probe.gd:_ready`(三钮/零动效/登记序段)〔ESC批 B1〕 | 集成 | 营地场景根态合成 Esc 键 → `main_esc_flow` → `ui.modals.flow.open/close(camp_esc_overlay)` | Esc 输入→浮窗 visible 两态(绑 open/close 调用记录);契约 9 四态路由(绑现开集合判据与 open/close 调用记录,含「设置开在浮窗之上」无反馈态);浮窗三钮数量+登记序(绑节点树与合同表) | 既有:模态覆盖层编排与输入锁;新增:Esc 浮窗唤起与三出口动线 |
|
||||
| B2 HUD 栏落位与刷新挂单口(行为改动专行) | `camp_hud_band.gd` 新建;`camp_scene_view.gd set_hud_band()` 填值;挂接卡④ `CampRefreshBus.refresh()` | `screen_flow_probe.gd:_ready`(HUD 读数/刷新段)+`ui_struct_probe.gd:_ready`(顶栏位结构段)〔ESC批 B2〕 | 集成 | `CampRefreshBus.refresh(camp_data)` → `camp_hud_band.refresh()` → `keystone_badge` | badge 文本=`camp_data()["balance"]` 单口取值;顶栏位存在(结构断言,高 48px 浮层);刷新单口调用记录(跨卡联验) | 既有:元进度入账与锻造两轴;新增:营地顶栏 HUD 钥石读数与刷新单口同步 |
|
||||
| B3 设置语境分支与返回主动线接线(行为改动专行) | `main_esc_flow.gd _on_return_to_menu()`/`_on_settings()`/`_on_quit()`;`main_overlay_flow.gd _on_settings_closed` 语境分支 | `screen_flow_probe.gd:_ready`(返回动线/设置双路径段)+`ui_struct_probe.gd:_ready`(退出钮接线断言)〔ESC批 B3〕 | 集成 | 浮窗三钮 `pressed` → `main_esc_flow` 三出口 → 现役动线/`_quit_game`;设置关闭 → `_on_settings_closed` 语境分支 | 返回动线三处可见性(绑屏显编排);设置关闭后浮窗 `visible=true`+`start_overlay.visible=false`(绑语境分支);退出钮连接链(绑信号连接) | 既有:模态覆盖层编排与输入锁;新增:Esc 浮窗唤起与三出口动线 |
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码与断言面 → `--code`)全绿才合并;MC 零跑(零数值零行为)。
|
||||
**落点集中**:V1/B1 → T1/T3;V2/V7/V8 → T1/T3;V3/V4/V5/B3 → T3;V6/B2 → T2/T3;V9/V10 → T4。全表名实核对归 T4。
|
||||
|
||||
**测试方式两分法**:V1–V8、B1–B3 机检自动化(能力验证经隔离存档实例直写钥石 `meta_state._add_keystone()`,`ui_click_probe.gd:74` 先例;不经 DebugOverlay 界面——其钥石项只读投影 `debug_overlay.gd:194-205`);V9 归用户目检(生命周期末步统一进行,不阻塞迭代,澄清⑥口径);V10 为件核对表。**用户目检一律「登记不阻塞」**:登记完成即卡内义务了结,不得作为批末收尾、合并或下一迭代开工的前置条件。
|
||||
|
||||
**判据绑定面口径**:每个需求点与每个行为改动的验收=一条从真实入口进入的断言,可 grep 定位、在当轮真实跑批中执行并通过,判据绑被测改动特有的可观测提交面(信号/事件/入参/状态源取值/绘制记录/可见性/树序)(2026-10-24/10-28 用户两裁)。**禁把生产码改成错值看断言抓不抓得到、禁回退 hunk 看断言红不红——人为制造红属伪造证据,一律禁止**;v1.2 计划的人工制造红取证条目整条已删,替换为上表正向验收断言。判据绑不上(纯防御分支、headless 不可真退)=静态判据+评审注记申报观测面局限,证明做不出=停卡呈报。配判据按强弱分层配齐:运行期断言(可见性/树序/状态源取值)抓状态类缺陷,golden 逐像素抓内容观感类缺陷(本卡无 camp golden,用 shot_checker 语义断言段+目检),占比/统计类判据只兜「整框空白/整框糊」、是补充不是主力(细则=test-conventions §8.4)。
|
||||
|
||||
**测试削弱申报(零削弱)**:**本卡零削弱申报**——本批只新增断言,不删除/放宽任何在役旧断言、不缩小扫描面、不把真实入口换成下层直调。v1.2 计划断言「浮窗开态 `debug_badge.visible==true`」因调试角标发布态恒隐换代(迭代 38 卡⑦,`main.gd:98`)与现行事实冲突,属**计划期订正**(该断言从未落地、非在役旧断言),改写为「浮窗登记序+open 不波及 float 类 `debug_badge` 节点」结构断言(V8),不计削弱。除此外实现期不得删除断言、放宽阈值、缩小扫描面、换下层直调;未申报的削弱由独立验收审计判红。
|
||||
|
||||
**独立验收审计**:合并前由未参与实现的会话/子代理执行,入口=审计只读本卡、实际对照表、diff、测试文件与实现报告,自跑最窄测试抽查、不采信报告「已全绿」;至少四件事——①逐行核对照表(测试存在、真的执行、尾缀能 grep 到、真实入口没有绕过改动点)②抽查判据绑定面(绑被测改动特有可观测提交面、当轮真实跑批执行过)③检查测试削弱(删除断言/放宽阈值/缩小扫描面/换下层直调逐处要本卡申报或用户批准)④核内部字段直查的受控例外(该行为无公开可观测面可钉时已在对照表注明理由)。报告落 **`reports/iter<N>-验收审计-<日期>.md`**,逐行「通过/缺漏/例外及依据」,未通过不得合并。测试执行两入口铁规不变:审计在迭代 loop 内启动的子代理算 loop,另开会话须用户点名。
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码/断言面 → `--code`)全绿才合并;MC 零跑(零数值零行为)。**卡内不写死闸件名单与域划分**,唯一出处在 `tools/run_all_gates.sh`。合回验证=主区直改+直提后由本地 CI 跑不带参数的 `tools/run_all_gates.sh`(两域全量)复验,代码面全量验证归合回后本地 CI,主区小批直提不重复跑全量。回归措辞按模板:里程碑步(波/T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试/工具先行孤立验证再并入全套。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救)**:断言写法、探针合成键事件写法、命名。
|
||||
- **L2(记录继续)**:Esc 接线方式选型(§9 域内)、浮窗壳尺寸/位次(UiKit 现行件域内)、HUD 栏位微调。
|
||||
- **L3(停机呈报)**:卡④ 场景屏未到位又需合并;Q1/Q2/Q3 未裁即开工;发现需扩展到非营地语境才能成立(触契约 2=用户裁面);Esc 键盘合成 spike 失败且无替代验收手段(真实路径纪律不让直呼处理函数代偿);浮窗开会隐藏其上登记层的栈语义被误判为缺陷(契约 7 已写明,勿误停机)。
|
||||
- **L1(自救一次)**:断言写法、探针合成键事件写法、命名、编译错误、签名拼写、测试套件 manifest 登记遗漏。
|
||||
- **L2(记录继续;同一红连续三次升 L3)**:浮窗壳尺寸/位次(UiKit 现行件域内)、HUD 栏位微调(顶栏域内)、行数较 T0 预期漂移但 S13 键不升。
|
||||
- **L3(立即停卡写报告等用户)**:
|
||||
- 卡④ 代码波未到位又需合并;
|
||||
- Q1/Q2/Q3 裁定被执行期擅自改值(已裁=甲/甲/甲,见 §10);
|
||||
- 发现需扩展到非营地语境才能成立(触契约 2=用户裁面);
|
||||
- Esc 键盘合成先例(`screen_flow_probe.gd:153-165`)复核失败且无替代真实入口验收手段(真实路径纪律不让直呼处理函数代偿);
|
||||
- 浮窗开会隐藏其上登记层的栈语义被误判为缺陷(契约 7 已写明,勿误停机);
|
||||
- 任何验收行无法从真实入口绑定特有可观测提交面(证明做不出=停卡呈报);
|
||||
- **机检预算互斥=上游缺陷停卡退回写卡段**:实现规范预算三新件超默认未走指标例外申报、或存量触面 S13 键较登记值上升、或机检键/计数与卡片契约互斥——一律停该卡呈报退回写卡段,禁为凑绿保留无效成员、造第三条路或内联求裁(req-card 第 9/14 节合流)。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. Esc 输入接线方式(`_unhandled_input` vs InputMap 新 action vs 专用控制器)——约束:不吞他处输入、探针可合成(与 T0 spike 结论兼容;InputMap action+轮Polling 路线禁——与合成探针不兼容)。
|
||||
2. 浮窗模态壳尺寸与位次(UiKit `modal_shell` 现行件域内)。
|
||||
1. **(v1.4 已定稿,移出白名单)Esc 接线=事件驱动 `_unhandled_input`+`KEY_ESCAPE`(physical_keycode)、禁 Input 单例轮询、不挂 ui_cancel、pressed 且非 echo**——契约 9 接线定稿(2026-11-01 用户裁);白名单保留此条作裁定留痕,实现期无选型自由。
|
||||
2. 浮窗模态壳尺寸与位次(UiKit `modal_shell` 现行件域内,`ui_kit.gd:486`)。
|
||||
3. HUD 栏在顶栏的左右位次与间距(卡④场景屏顶栏域内)。
|
||||
4. 「返回主菜单」复用卡④篝火同一处理函数(建议复用——动线同口;实现期定)。
|
||||
5. 浮窗视觉面是否入 shot_checker 取样(T3 定:入=补截图段;不入=显式登记归目检并写明理由)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
5. 浮窗视觉面是否入 shot_checker 取样(T3 定:入=补截图段;不入=显式登记归目检并写明理由)。
|
||||
6. 提交切分与 worktree 分支命名(车道铁律:主区直改+直提;大批按约定 worktree 非强制)。
|
||||
|
||||
## 10. 待裁点
|
||||
超出必须呈报:任何数值、事件名、接口名、文件职责、T 卡验收落点、三钮文案、Q1/Q2/Q3 裁定值。
|
||||
|
||||
### Q1 · Esc 在子界面开态下的行为(必裁,未裁不开 T1)
|
||||
## 10. 待裁点:零待裁点,开工门=U0
|
||||
|
||||
**本卡设计待裁点清零,开工门=U0。** Q1/Q2/Q3 均于 2026-10-18 裁定(下列为现行裁定留痕,非待裁点,实现期禁自行改值):
|
||||
|
||||
### Q1 · Esc 在子界面开态下的行为(已裁定 甲)
|
||||
|
||||
**背景**:营地语境含「场景根态」与「子界面开态」(锻造/秘宝/生涯/天赋树/图鉴开着时)。澄清⑤裁决 9 未覆盖此分支。两案并列,**无推荐(用户令:不代裁)**:
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| 甲 | 只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗(**前提=各子界面自带返回钮闭环——待卡④ T3 承诺,v1.1 括注**) | 语义最简;子界面里按 Esc 无反馈 |
|
||||
| 甲 | 只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗(前提=各子界面自带返回钮闭环——待卡④ T3 承诺) | 语义最简;子界面里按 Esc 无反馈 |
|
||||
| 乙 | 营地语境任意态响应 Esc:子界面开着时先关子界面回根态,再按才唤浮窗(单义化) | 多一层输入编排;与既有「子界面返回钮」形成双关闭路径 |
|
||||
|
||||
连带:判据形态随案(T1 已列);V1 按案分拆断言;契约 2 措辞裁后回写。裁决方式:随卡片评审定案;裁定落 T0 执行+实现报告留痕。
|
||||
连带:判据形态随案(T1 已列);V1 按案分拆断言;契约 2 措辞裁后回写。
|
||||
|
||||
**裁定(2026-10-18)**:**甲**——只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗。代拍板留痕(用户令「事项按推荐的来」,本项无推荐案,取默认甲=语义最简;前提=卡④ T3 承诺各子界面自带返回钮闭环,卡④重新立卡时该承诺已在内;追认单可 veto)。T1 判据=营地场景屏 visible 且无营地子模态开态。
|
||||
**裁定(2026-10-18)**:**甲**——只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗。代拍板留痕(用户令「事项按推荐的来」,本项无推荐案,取默认甲=语义最简;前提=卡④ T3 承诺各子界面自带返回钮闭环,卡④ v3.1 已在内;追认单可 veto)。T1 判据=营地场景屏 visible;「无营地子模态开态」由契约 9① 现开集合判据承载(v1.4,判据单点化)。裁定落 T0 执行+实现报告留痕。
|
||||
|
||||
### Q2 · 浮窗的关闭/取消路径(必裁,未裁 T1 视图定不了稿——v1.1 新增)
|
||||
### Q2 · 浮窗的关闭/取消路径(已裁定 甲)
|
||||
|
||||
**背景**:`UiKit.modal_shell`(ui_kit.gd:456-475)只产 dim+center+panel+VBox、**无内置关闭件**;契约 1 禁加项⇒浮窗只有三钮,无一「关闭」。而 §4 不做清单把「任意键关闭浮窗」划为用户未裁禁自扩——**Esc 自身再按的行为恰好落在被 punt 的缝隙里**:Q1 两案都不覆盖「浮窗已开时按 Esc」。若不裁且无关闭钮,玩家误开浮窗后离开营地只有「回主菜单/开设置/退游戏」三条路——体感陷阱,且与卡④子界面(生涯有返回钮)的可用性水准倒挂。
|
||||
**背景**:`UiKit.modal_shell`(`ui_kit.gd:486`)只产 dim+center+panel+VBox、**无内置关闭件**;契约 1 禁加项⇒浮窗只有三钮,无一「关闭」。而 §4 不做清单把「任意键关闭浮窗」划为用户未裁禁自扩——Esc 自身再按的行为恰好落在被 punt 的缝隙里:Q1 两案都不覆盖「浮窗已开时按 Esc」。若不裁且无关闭钮,玩家误开浮窗后离开营地只有「回主菜单/开设置/退游戏」三条路——体感陷阱,且与卡④子界面(生涯有返回钮)的可用性水准倒挂。
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
@@ -187,23 +306,15 @@
|
||||
| 乙 | 浮窗加关闭钮 | 触契约 1「禁加项」——须重新解释「三项」仅指功能项 |
|
||||
| 丙 | 维持无关闭(三钮即全部出口,「返回主菜单」充当默认离开) | 误开浮窗后成本高;与子界面可用性水准倒挂 |
|
||||
|
||||
裁决方式:随卡片评审定案;裁定落 T1 视图与接线+V 表。
|
||||
**裁定(2026-10-18)**:**甲**——再按 Esc 关浮窗(同键唤起/关闭单义切换)。推荐案,用户令按推荐执行。T1 接线含「浮窗已开时再按 Esc=关浮窗」;V1 断言按「根态唤起/再按关闭」两态分拆。裁定落 T1 视图与接线+V 表。
|
||||
|
||||
**裁定(2026-10-18)**:**甲**——再按 Esc 关浮窗(同键唤起/关闭单义切换)。推荐案,用户令按推荐执行。T1 接线含「浮窗已开时再按 Esc=关浮窗」;V1 断言按「根态唤起/再按关闭」两态分拆。
|
||||
|
||||
### Q3 · Esc 范围确认(顺带确认项,低成本高保险——v1.1 新增)
|
||||
### Q3 · Esc 范围确认(已裁定 甲)
|
||||
|
||||
**背景**:契约 2「Esc 只在营地语境」是卡的从严收窄(澄清文档只裁「Esc 唤浮窗三项」、未裁范围——S1)。收窄方向保守且已自标「用户未裁」,但若用户本意是全局 Esc 菜单,范围锁死即返工。
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| 甲(推荐) | 确认「Esc 浮窗只在营地语境」——契约 2 现文成立 | 无(一句话确认) |
|
||||
| 乙 | 用户本意为全局(或更宽范围) | 本卡范围重开=契约 2/T1/V1 大改,且触 08:192 Beta 行口径 |
|
||||
|
||||
裁决方式:随卡片评审顺带确认。
|
||||
| 乙 | 用户本意为全局(或更宽范围) | 本卡范围重开=契约 2/T1/V1 大改,且触 08:193 Beta 行口径 |
|
||||
|
||||
**裁定(2026-10-18)**:**甲**——确认「Esc 浮窗只在营地语境」(推荐案,用户令按推荐执行)。契约 2 从「卡的从严收窄」升格为已确认口径;S1 保留意见关闭。
|
||||
|
||||
---
|
||||
|
||||
**审查回写要点**:v1.0→v1.1(2026-10-18 grilling 子 Agent 对抗审查回写)——事实订正 5 处:F9 刷新链路失实(营地刷新腿随 CampOverlay 退役、`_refresh_presentations` 是战斗侧腿)→契约 5 改挂卡④营地刷新单口+T2 刷新时机清单;E2「调试接口直改钥石」能力不存在(DebugOverlay 钥石只读)→改隔离存档实例直写(smoke 先例);F2 行号拆分(_open_settings :1055-1057);F8 :191→:192;估时算术 2.5–3.5d→3–4.5d。自扩订正:契约 2 标卡的从严收窄+Q3 请用户确认。卡内可收 8 项:契约 3 放宽语境分支、契约 7 遮挡结论+栈语义、契约 4 前提成文、T1 语境判据、T2 刷新时机+golden 联动、T3 探针宿主定 screen_flow_probe+badge 断言+变异补二条、V1 按 Q1 分拆、V4/V6/V8/V9 订补。跨卡对齐:HUD 刷新单口(§0 接口+卡④ T3 定义)、子界面返回钮承诺(Q1 甲案前提=卡④ T3)、终局落点不变量(T0 互证)、探针路径依赖卡②③链(§0)。新增待裁点 Q2 浮窗关闭路径、Q3 Esc 范围确认。用户面裁决(裁决 9/10)零改动。
|
||||
@@ -1,57 +1,61 @@
|
||||
# 需求卡片——棋盘格边长口径收口批(探针门槛纠偏+格边长算式单点+冒烟扩四档板型)
|
||||
# 需求卡片——棋盘格边长口径收口批 v2(阈值退役+格边长算式单点+冒烟扩四档板型)
|
||||
|
||||
状态行:v1.0 定稿——零待裁点(2026-10-30 用户裁:①探针不扩到 L3 推关;②最紧档 14×14 的可读断言落单元四档+探针实板推定两处;③算式单点提取用私有静态名,依据见 §2 第 5 条)
|
||||
状态行:v2.0 定稿——零待裁点(2026-11-01 用户裁:①可读性锚点=用户验收的现行渲染值——基座档 14×14 板实测 28px 即达标,battle.md「可读下限 30px」判据句退役(落文归文档批,走反哺点通道),探针旧值 36 一并废弃;②探针格边长判据换锚=实读值与单点算式严格相等+结构钳制保护+全档读数留痕,全仓禁任何人造格边长阈值;③阈值手抄常量 `MIN_CELL` 删除,禁第二份阈值抄本再入代码或测试)
|
||||
|
||||
接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交)」(`待办清单.md` 在册,状态「待写卡」)
|
||||
接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(2026-10-06 登记,来源=批次移交)」(`待办清单.md` 在册)
|
||||
|
||||
来源:**设计事实源**=`design/gdd/ux/battle.md`「棋盘格尺寸公式」节(Last Updated 2026-10-29;格边长口径的**唯一事实源**——公式、可读下限 30px、结构钳制 [26,46] 三项都在此节)。指路文档(2026-10-26 已归一、不再存算例)=`design/art/art-bible.md` §8.1/§8.2、`design/gdd/systems/表现与支撑/HUD框架.md`。四档板型事实源=`game/data/tables/board_gen.tres`。
|
||||
来源:**设计事实源**=`design/gdd/ux/battle.md`「棋盘格尺寸公式」节(2026-11-01 实查——公式与结构钳制 [26,46] 在 `:137-146`;原 `:148-152` 算例表已于 2026-10-31 布局几何单点化批退役(commit 1cdef6ba),现行 `:148`=「算例不入文档」句;`:144`「可读下限 30px」句经 2026-11-01 用户裁退役,落文走 §5 反哺点通道,本卡判据零引用)。布局事实源=`game/data/tables/battle_layout.tres` 五字段(2026-10-31 布局几何单点化批起为分区几何唯一事实源)。四档板型事实源=`game/data/tables/board_gen.tres`。锚点裁决过程与实测读数链=[停机呈报-棋盘格边长口径收口批-2026-10-31](停机呈报-棋盘格边长口径收口批-2026-10-31.md)。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
这批做什么:把「棋盘格边长」在代码侧残留的两处旧口径收干净。文档侧已于 2026-10-26 归一(口径只剩 battle.md 一节),代码侧还差两件——探针的可读门槛仍是 12×12 时代的 36px(比 battle.md 的可读下限 30px 严 6px,属用旧值当新标准),格边长冒烟只跑 L1/L2(13×13 与 14×14 两档板型无断言)。本批顺带把格边长算式收成单点(现在它内联在 `set_cell_area` 里,任何「在别的板型/别的面积上求值」的需求都只能复写算式=造第二口径)。
|
||||
这批做什么:把「棋盘格边长」在代码侧的残留旧口径收干净——**方向经 2026-11-01 用户裁换代**:不是把探针阈值 36 改成 30(v1 方向,已废),而是**把人造阈值整个退役**——可读性的锚点=用户验收过的现行渲染画面(基座档四档板型 40/33/30/28px),检查工具不再拿任何编造的下限给画面打分,只做两件事:①验证生产写入值与单点算式严格相等(接线守卫);②把四档板型在实板可用面积上的推定值留痕(回归对照基准)。同时完成 v1 已裁的两件:格边长算式收成单点(现在内联在 `set_cell_area` 里,任何「在别的板型/别的面积上求值」的需求都只能复写算式=造第二口径)、13/14 两档板型进入测试守卫。
|
||||
|
||||
**行为零变化**:算式逐位不变,`set_cell_area` 的副作用顺序(英雄脚锚重置 → `_refresh_slab` → `_update_world_size`)零改动,玩家可见画面零变化。
|
||||
|
||||
完成定义(全部满足才算完):
|
||||
1. 代码侧可读门槛=30px,与 battle.md「可读下限 30px」一致;旧值 36 在 `game/` 下零残留。
|
||||
1. 代码侧人造阈值归零:`layout_probe.gd` 的 `MIN_CELL` 常量删除,`grep -rn "MIN_CELL\|可读门槛\|可读下限" game/` 零残留;30 与 36 均不得以任何形态回到代码或测试判据。
|
||||
2. 格边长算式只剩一处落点(`BoardView._cell_for`),`set_cell_area` 只调它;探针与单元测试不再各自复写算式。
|
||||
3. 13×13 与 14×14 两档进入断言并在当轮真实跑批中执行通过(单元四档+探针实板面积推定两条)。
|
||||
4. 代码域闸全绿;`tools/gdstyle_baseline.txt` 中 board_view.gd 各键只许持平或下降(MATCH——`max-public-methods` 保持 27)。
|
||||
3. 13×13 与 14×14 两档进入守卫并在当轮真实跑批中执行通过(单元四档冒烟+探针等价断言与推定留痕)。
|
||||
4. 代码域闸全绿;`tools/gdstyle_baseline.txt` 中 board_view.gd 各键只许持平或下降(MATCH——`max-public-methods` 保持 27、`max-class-variables` 保持 31)。
|
||||
5. 产出物三份落 `reports/`(实现报告/反哺点清单/验收审计报告),独立验收审计未过不合并。
|
||||
|
||||
**编队注记与估时**:估时 **0.5 个工作日**(T0 0.1/T1 0.15/T2 0.1/T3 0.1/T4 0.05),远低于迭代冻结线(5 个工作日),可编批;编入哪个迭代由迭代编排段定。本批触面=`game/presentation/views/board_view.gd`+`game/probe/layout_probe.gd`+`game/tests/unit/core/board_gen_reward_test.gd`+`game/tests/README.md`(写卡段已同批入表),与在飞批次无文件交叠需编排段复核。
|
||||
**编队注记与估时**:估时 **0.5 个工作日**(T0 0.1/T1 0.15/T2 0.1/T3 0.1/T4 0.05),远低于迭代冻结线(5 个工作日),可编批;编入哪个迭代由迭代编排段定(worktree iter46 的未提交 WIP 三件中,T1 与 T3 两件按 v2 原样复用,T2 探针件按本卡新判据返工——见 §12)。本批触面=`game/presentation/views/board_view.gd`+`game/probe/layout_probe.gd`+`game/tests/unit/core/board_gen_reward_test.gd`,与在飞批次无文件交叠需编排段复核。
|
||||
|
||||
## 2. 现状事实
|
||||
|
||||
全部行号为 **2026-10-30 实查**——本批编入后须由 T0 按 T0 时点 master 重测重锚。
|
||||
全部行号为 **2026-11-01 实查**(master 3499638d)——本批编入后须由 T0 按 T0 时点 master 重测重锚。
|
||||
|
||||
1. 算式内联在 `game/presentation/views/board_view.gd:191-209`(`set_cell_area`):`:194` N=`maxi(width,height)`、`:195` `sep := SEP`(`SEP := 2`,`:34`)、`:196-197` `d := DEPTH_PX`(`8.0`,`:37`)与 `reserve := 24.0 + d`、`:198` 一条 `int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))`、`:199` `clampi(cell, 26, 46)`。副作用尾链 `:200-209`(英雄脚锚重置/`_refresh_slab`/`_update_world_size`)。
|
||||
2. 生产调用路径两条:`game/presentation/views/board_zone_view.gd:91-92`(`board_area.resized` → `set_cell_area(_board_area.size)`)与 `game/presentation/views/board_view.gd:250`(`rebuild` 末尾 `set_cell_area(size)`)——传入的都是棋盘视图实际可用区尺寸,`board_view` 为 `PRESET_FULL_RECT` 子级,故 `board_view.size` 即生产传入值。
|
||||
3. 探针门槛:`game/probe/layout_probe.gd:15` `const MIN_CELL := 36.0`,注释自陈「battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px」;`:92-94` 断言 `cell < MIN_CELL` 记红。探针只进 L1(`:27` 起始冒险)、`:31` `press_candidate(0)`,故实跑只会遇到 10×10(格边约 45px),门槛 36 从未在 30–35px 区间生效过。
|
||||
4. 格边长冒烟:`game/tests/unit/core/board_gen_reward_test.gd:53-69`(`_smoke_layout_cell_formula`):`:57` `for L in [1, 2]`、`:58` `generate(L, 0, ...)`、`:60` 样本 `avail := Vector2(1036, 460)`、`:63` 读 `board_view.get("_cell")`、`:64-66` 网格高 ≤ 可用高−24、`:67-68` 格边长 ∈ [26,46]。13×13 与 14×14 两档从未进入断言。
|
||||
5. **S13 顶格事实(本机 gdstyle v0.4.0 实测)**:`tools/gdstyle_baseline.txt:22` 登记 `game/presentation/views/board_view.gd|quality/max-public-methods = 27`,与实测公开方法数 27 相等。对同一文件副本加一个公开静态函数后实测升到 **28**(WORSENED 红);改名为下划线前缀(`_cell_for`)后实测仍为 **27**(零新发现)。故算式提取必须用私有名。
|
||||
6. 四档板型取档(`game/data/tables/board_gen.tres` 实查 (level_index, node_depth) → 尺寸):L1 depth 0–3=10×10、L1 depth 4–6=12×12、L2 depth 0–9=12×12、L3 depth 0–8=13×13、L3 depth 9–13=14×14。即 `generate(1,0)`=10、`generate(2,0)`=12、`generate(3,0)`=13、`generate(3,9)`=14。
|
||||
7. 规格侧:`battle.md:137-146` 公式与两项判据——结构钳制区间 [26,46]px(`:143`,管算式不越界)、**可读下限 30px**(`:144`,管玩家能否看清,与钳制分开);`:148-152` 算例表按「1280 档可用高 486px」推算 43/36/33/30,文内明标「随布局与板型变化,不是契约值」。
|
||||
8. 实机可用面积与算例不同源:`game/presentation/ui_assembler.gd:20-28` 结构为 B 区 252px + C/D 两列占余高(该文件 `:24` 注释写明 548px),D 列内 `board_zone_view.gd:24-36` 再减 AP 条 44px 与 4px 间距——与 battle.md 算例假设的 486 可用高存在偏差(与待办清单在册的「B 区高度 270 vs 252」同源)。本卡不把算例当契约,实板可用面积一律以探针实读 `board_view.size` 为准。
|
||||
1. 算式内联在 `game/presentation/views/board_view.gd:206-215`(`set_cell_area`):`:209` N=`maxi(width,height)`、`:210` `sep := SEP`(`SEP := 2`)、`:211-212` `d := DEPTH_PX` 与 `reserve := 24.0 + d`、`:213` 一条 `int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))`、`:214` `clampi(cell, 26, 46)`。副作用尾链自 `:215` 起(英雄脚锚重置/`_refresh_slab`/`_update_world_size`)。
|
||||
2. 生产调用路径两条:`game/presentation/views/board_zone_view.gd:91-92`(`board_area.resized` → `set_cell_area(_board_area.size)`)与 `board_view.gd` `rebuild` 末尾 `set_cell_area(size)`——传入的都是棋盘视图实际可用区尺寸,`board_view` 为 `PRESET_FULL_RECT` 子级,故 `board_view.size` 即生产传入值。
|
||||
3. 探针阈值手抄件:`game/probe/layout_probe.gd:15` `const MIN_CELL := 36.0`,注释自陈出处为旧算例表「1280 档 12×12 → 36px」;`:93-95` 断言 `cell < MIN_CELL` 记红;`:44-45` PASS 消息引用 MIN_CELL。探针只进 L1(`:27` 起始冒险)、`:31` `press_candidate(0)`,实跑只遇 10×10(实读 40px),36 阈值从未在 28–35px 区间生效过——**该常量与旧算例表同源,旧算例表已退役(1cdef6ba),此抄本成为无源之值**。
|
||||
4. 格边长冒烟:`game/tests/unit/core/board_gen_reward_test.gd:53-69`(`_smoke_layout_cell_formula`):`:57` `for L in [1, 2]`、`:60` 样本 `avail := Vector2(1036, 460)`、`:63` 读 `board_view.get("_cell")`、`:64-66` 网格高 ≤ 可用高−24、`:67-68` 格边长 ∈ [26,46]。13×13 与 14×14 两档从未进入断言。
|
||||
5. **S13 顶格事实(2026-11-01 实查,gdstyle v0.4.0)**:`tools/gdstyle_baseline.txt` 登记 `board_view.gd|quality/max-public-methods = 27`(`:22`)与 `max-class-variables = 31`(`:11`),与静态清点逐一相符。对同一文件副本加一个公开静态函数后实测升 28(WORSENED 红);改名为下划线前缀(`_cell_for`)后持平 27。**故算式提取必须用私有名,且本批禁给 board_view.gd 新增任何成员(31 同样不容新增——钳制界不得提取为公开常量)**。
|
||||
6. 四档板型取档(`board_gen.tres` 实查 (level_index, node_depth) → 尺寸):L1 depth 0–3=10×10、L1 depth 4–6=12×12、L2 depth 0–9=12×12、L3 depth 0–8=13×13、L3 depth 9–13=14×14。即 `generate(1,0)`=10、`generate(2,0)`=12、`generate(3,0)`=13、`generate(3,9)`=14。
|
||||
7. **锚点读数(2026-10-31 探针实读,停机呈报在案;2026-11-01 用户裁定为现行锚点)**:基座 1280×800 档 `board_view.size`=(1022.0, 454.0),10/12/13/14 板 → 40/33/30/28px;16:9 1920×1080 档 (1662.0, 734.0) → 四档全部 46px(钳制顶格)。代入复核(1280 档 14 板):`min(1022−12−8, 454−32) = 422`;`(422 − 13×2) / 14 = 28.28 → floor 28` → 钳制 [26,46] 内 → 28px。
|
||||
8. 布局事实源换代(2026-10-31 布局几何单点化批,1cdef6ba):分区几何唯一事实源=`battle_layout.tres` 五字段(实查 `stage_h=252`/`ap_bar_h=44`/`info_panel_w=240`/`log_band_h_dig=24`/`log_band_h_settle=84`);容器内部结构(VBox 间距 4px、常驻操作提示条、面板边距)在代码中,实板可用面积以探针实读 `board_view.size` 为准,不静态推算。
|
||||
9. 旧判据毒性记录(v1 停机的根因,防复发):「可读下限 30px」与「36px 门槛」均无实测背书——30 系旧算例表(按假设可用高 486px 推算)的产物被写成规则,36 是探针从该表错抄的第二份手抄件;两者互相打架且与实机脱节,是 2026-10-31 停机的直接原因。本批后此类「文档句子↔代码常量」双写形态在本域不得再现(§3 契约②③)。
|
||||
|
||||
## 3. 设计契约(违反即返工,标 🔴 触碰=L3 停机呈报)
|
||||
|
||||
- 🔴 **算式单点**:格边长算式只在 `BoardView._cell_for` 一处;`set_cell_area` 只调它。探针、测试、任何新调用方都不得复写这条算式(复写=造第二口径,本批立项原因就是三处口径打架)。
|
||||
- 🔴 **两个数不得合并**:可读下限 30px(battle.md 判据)与结构钳制 [26,46]px(算式不越界)是两个概念。前者只出现在探针门槛与断言里,后者只出现在 `_cell_for` 内;禁止用钳制区间替代下限判据,也禁止把下限塞进 `cell_for` 的 clamp。
|
||||
- 🔴 **私有名固定**:提取函数名必须是 `_cell_for`(下划线前缀)。依据=§2 第 5 条的实测:公开名即令 `max-public-methods` 27→28 恶化。实现期「顺手改成公开方法更好看」=L3。
|
||||
- 🔴 **锚点唯一**:格边长的判据锚=①用户验收的现行渲染值(§2 第 7 条读数表,只作记录与对照,不作判红阈值)+②公式自身结构钳制 [26,46](battle.md `:143`,单元冒烟既有断言承载)。**本批起全仓禁任何人造格边长可读阈值**——30、36 及其变体不得出现在任何代码、测试、探针判据中;发现即 L3。
|
||||
- 🔴 **阈值手抄禁令**:探针与测试不得新增阈值常量。与算式相关的判据一律经 `BoardViewScript._cell_for(...)` 等价比较或既有公开出口(`cell_size`)承载;唯一例外=单元冒烟既有的 [26,46] 区间断言(v1 前已存在,逐字保留)。
|
||||
- 🔴 **算式单点**:格边长算式只在 `BoardView._cell_for` 一处;`set_cell_area` 只调它。探针、测试、任何新调用方都不得复写这条算式(复写=造第二口径)。
|
||||
- 🔴 **私有名固定**:提取函数名必须是 `_cell_for`(下划线前缀)。依据=§2 第 5 条实测:公开名即令 `max-public-methods` 27→28 恶化;同理禁新增任何成员(`max-class-variables` 31 顶格)。实现期「顺手改成公开方法更好看」=L3。
|
||||
- 🔴 **行为零变化**:`set_cell_area` 的副作用顺序与数值逐位不变;`rebuild`/`resized` 两条调用路径不改;画面像素零变化。
|
||||
- 🔴 **禁为机检文本保留无效成员**(重构类卡条款,2026-10-25 立):禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名;机检键与本卡契约互斥时,实现段一律停卡呈报,不得自造第三条路。
|
||||
- 🔴 **拆分边界纪律**(结构类卡条款,2026-10-28 立,判据权威=code-conventions §1.1):①拆出单元 `_cell_for` 职责语句=「按 battle.md 格尺寸公式,由板型 N 与可用面积算格边长的无状态纯函数」,单一变化原因=公式本身,实现报告逐件一行承载;②集体身份=「算式唯一落点」(命得出名字,非凑计数袋);③「过阈值」只许作为发现信号,作为拆分或收拢的动机即违规;④按纪律拆不出内聚单元 → 停卡呈报走豁免/还债通道,禁硬拆凑绿(进 §9 L3)。
|
||||
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8「测试削弱申报」,未申报的删改由独立验收审计判红。
|
||||
- 🔴 **禁变异类测试与撤销类取证**:不得靠回退 hunk 或注入破坏证明断言有效。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
做:算式提取为 `_cell_for` 并改调;探针门槛 36→30 与注释改指 battle.md;探针新增「实板可用面积下 13/14 板格边长推定」断言与 board_view 零尺寸前置;格边长冒烟扩到四档板型并加算式单点等价断言;`game/tests/README.md` 能力行入表(写卡段同批,见 §5)。
|
||||
做:算式提取为 `_cell_for` 并改调;探针删除 `MIN_CELL` 常量与阈值断言、改等价断言+推定留痕+零尺寸前置;格边长冒烟扩到四档板型并加算式单点等价断言。
|
||||
|
||||
不做(明示去向):
|
||||
1. **探针扩到 L3 推关**——2026-10-30 用户裁不做:成本是探针要真打穿两关(main 无跳关入口),且一旦红因是推关而非布局会污染该闸;最大板可读面改由 §7.3 的推定断言承担。
|
||||
2. **battle.md 算例表改数**——实现批禁写 `design/`;实测与算例的偏差走 §5 反哺点通道。
|
||||
3. **四档板型数值调整**——`board_gen.tres` 板尺寸由设计侧定,本批只读不写。
|
||||
4. **B 区高度 252 vs 270**——另一条在册待办(迭代 43 批后待裁三点),本批不碰。
|
||||
1. **探针扩到 L3 推关**——v1 已裁不做(推关成本与红因混淆);维持。
|
||||
2. **battle.md「可读下限 30px」句的落文**——实现批禁写 `design/`;退役措辞走 §5 反哺点通道,文档批落文。本批判据不依赖该句(退役与否不阻塞实现)。
|
||||
3. **四档板型数值调整**——`board_gen.tres` 由设计侧定,本批只读不写。
|
||||
4. **布局表字段调整**——`battle_layout.tres`(含 `stage_h`)刚经布局几何单点化批落地,本批只读;任何为格边长而动的布局调整(如加高棋盘区)=另一张设计批的卡。
|
||||
5. **`set_cell_area` 副产物顺序调整**——零触碰。
|
||||
6. **MC/数值验证**——无数值改动零跑。
|
||||
|
||||
@@ -59,112 +63,114 @@
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| design/gdd/ux/battle.md(设计权威——实现批禁直改) | ①`:148-152` 算例表按「可用高 486px」推算 43/36/33/30,而实机 D 列结构为 548px(§2 第 8 条)——探针实测格边长若与算例不符,是算例随布局漂移而非代码错;②「可读下限 30px」(`:144`)条本身不变,本批正是向它对齐 | 反哺点通道:把探针 L1 实测格边长与 13/14 推定读数逐条列进 `reports/iter<N>-反哺点-<日期>.md`(改哪一节=`:148-152` 算例表、改成什么=填实测值或标注「按旧布局口径」、依据=实测读数),用户会话裁决后归文档批落文 |
|
||||
| game/tests/README.md(机械面——归写卡段维护) | 新增能力行「战斗屏三区几何与棋盘格边长(分辨率两档×四档板型)」,覆盖域表 HUD框架.md 行同步补名 | 写卡段已于本卡成文同批入表,实现批零增删零改行 |
|
||||
| design/gdd/systems/表现与支撑/HUD框架.md、design/art/art-bible.md §8.1/§8.2(已归一为指路) | 本批不改二者的任何陈述(2026-10-26 归一后它们只写规则与指针,不存算例与定值档) | 无(本行占位——按规约查不出也要写明) |
|
||||
| design/gdd/ux/battle.md(设计权威——实现批禁直改) | ①`:144`「可读下限 30px——与结构钳制分开:钳制管算式不越界,下限管玩家能否看清」句——2026-11-01 用户裁退役;依据=锚点裁决+停机呈报读数链(30 无实测背书、36 错抄自已退役的算例表)。推荐落文方向(措辞由文档批裁):整句删除,或改写为「格边长可读性以用户验收的现行画面为基准;结构钳制 [26,46] 管算式不越界」 | 反哺点通道:列进 `reports/iter<N>-反哺点-<日期>.md`(改哪节=`:144`、改成什么=删除或上述改写、依据=停机呈报+用户裁),用户裁决后归文档批落文。②v1 的「算例表改数」反哺条**作废**——`:148-152` 表已随 1cdef6ba 退役,无落点 |
|
||||
| game/tests/README.md(机械面——归写卡段维护) | 能力行「战斗屏三区几何与棋盘格边长(分辨率两档×四档板型)」v1 写卡段已入表;v2 判据换代不改能力名与真实入口 | 无增删(本行占位核对:批末核对表上名字与实况一致) |
|
||||
| design/gdd/systems/表现与支撑/HUD框架.md、design/art/art-bible.md §8.1/§8.2(已归一为指路) | 本批不改二者的任何陈述 | 无(本行占位——按规约查不出也要写明) |
|
||||
|
||||
## 6. 卡前文件结构图
|
||||
|
||||
| 文件 | 职责 | 新增/复用 | 行为改动? |
|
||||
|---|---|---|---|
|
||||
| game/presentation/views/board_view.gd | 修改:`set_cell_area` 内联算式抽成 `static func _cell_for(n, avail) -> int`,`set_cell_area` 改调;注释补「算式唯一落点」句 | 复用既有(抽自本文件既有代码,不新增文件/类) | 否(算式逐位相同——对照表行 2 的等价断言承载) |
|
||||
| game/probe/layout_probe.gd | 修改:门槛 36→30+注释改指 battle.md;新增 board_view 零尺寸前置与 13/14 板格边长推定断言 | 修改 | 是(守卫面变化——对照表行 1/4) |
|
||||
| game/tests/unit/core/board_gen_reward_test.gd | 修改:格边长冒烟循环扩四档板型+板型一致断言+算式单点等价断言 | 修改 | 是(测试面——对照表行 2/3) |
|
||||
| game/tests/README.md | 修改:能力清单加一行+覆盖域表 HUD框架行补名 | 修改 | 否(机械面文档,写卡段同批完成) |
|
||||
| game/presentation/views/board_view.gd | 修改:`set_cell_area` 内联算式抽成 `static func _cell_for(n, avail) -> int`,`set_cell_area` 改调;注释补「算式唯一落点」句 | 复用既有(抽自本文件既有代码,不新增文件/类/成员) | 否(算式逐位相同——对照表行 2 的等价断言承载) |
|
||||
| game/probe/layout_probe.gd | 修改:删 `:15` MIN_CELL 常量;`:93-95` 阈值断言换等价断言;`:44-45` PASS 消息改写并附 13/14 推定读数;新增 board_view 零尺寸前置与推定有效性判据 | 修改(v1 WIP 的探针件按本卡返工——v1 的「30 阈值+推定 ≥30」形态作废) | 是(守卫面换代——对照表行 1/4) |
|
||||
| game/tests/unit/core/board_gen_reward_test.gd | 修改:格边长冒烟循环扩四档板型+板型一致断言+算式单点等价断言 | 修改(v1 WIP 原样复用) | 是(测试面——对照表行 2/3) |
|
||||
|
||||
不触碰:`board_zone_view.gd`、`ui_assembler.gd`、`data/tables/board_gen.tres`、全部 `core/` 与 `data/` 代码、其余探针与测试套件。
|
||||
不触碰:`board_zone_view.gd`、`ui_assembler.gd`、`data/tables/board_gen.tres`、`data/tables/battle_layout.tres`、全部 `core/` 与 `data/` 代码、`game/tests/README.md`(v1 已入表)、其余探针与测试套件。
|
||||
|
||||
## 7. 任务卡 T0..T4
|
||||
|
||||
### 7.1 T0——读数重锚与取档对账(估时 0.1 个工作日)
|
||||
|
||||
- [ ] 按 T0 时点 master 重测重锚 §2 全部行号:`layout_probe.gd:15`/`:92-94`/`:27`/`:31`、`board_view.gd:34`/`:37`/`:191-209`/`:250`、`board_zone_view.gd:91-92`、`board_gen_reward_test.gd:53-69`。
|
||||
- [ ] 按 T0 时点 master 重测重锚 §2 全部行号:`layout_probe.gd:15`/`:31`/`:44-45`/`:93-95`、`board_view.gd:206-215`、`board_zone_view.gd:91-92`、`board_gen_reward_test.gd:53-69`。
|
||||
- [ ] 四档取档复核:对 `db.combat.board_gen.resolve_row(L, D)` 逐组取 (1,0)(2,0)(3,0)(3,9),实测返回 `board_width` 依次为 10/12/13/14——不符=L3 停卡退回写卡段。
|
||||
- [ ] `battle_layout.tres` 五字段读数与 `battle.md:137-146` 现状句确认(`:144` 30px 句仍在位=预期,本批不碰,§5 反哺承载;若已被文档批提前退役亦不阻塞——本卡判据零依赖该句)。
|
||||
- [ ] 改前基线读数(孤立验证):`python tools/gdstyle_gate.py`(board_view.gd `max-public-methods` 实测 27 持平)+单件跑 `board_gen_reward_test`(绿)+单件跑 `layout_probe`(两档绿)。三条读数进实现报告 T0 节。
|
||||
- 取用:本卡 §2。产出:重锚后的行号与三份改前读数(T1–T4 对拍基准)。
|
||||
|
||||
### 7.2 T1——算式单点提取(估时 0.15 个工作日)
|
||||
|
||||
- [ ] `board_view.gd` 新增 `static func _cell_for(n: int, avail: Vector2) -> int`,函数体=`:195-199` 五行算式逐字搬入(`SEP`/`DEPTH_PX` 仍读本文件既有常量,`clampi(..., 26, 46)` 留在该函数内),返回 `int`。
|
||||
- [ ] `set_cell_area` 的 `:194-199` 六行替换为 `_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`;`:200-209` 副作用尾链一字不动。
|
||||
- [ ] `:189-190` 注释块补一句:算式唯一落点=`_cell_for`(探针与单元测试共用);名字带下划线是 S13 `max-public-methods` 27 顶格的约束,禁改公开名。
|
||||
- [ ] `board_view.gd` 新增 `static func _cell_for(n: int, avail: Vector2) -> int`,函数体=`:210-214` 五行算式逐字搬入(`SEP`/`DEPTH_PX` 仍读本文件既有常量,`clampi(..., 26, 46)` 留在该函数内),返回 `int`。
|
||||
- [ ] `set_cell_area` 的 `:209-214` 六行替换为 `_cell = float(_cell_for(maxi(_board.width, _board.height), avail))`;`:215` 起副作用尾链一字不动。
|
||||
- [ ] `:204-205` 注释块补一句:算式唯一落点=`_cell_for`(探针与单元测试共用);名字带下划线是 S13 `max-public-methods` 27 顶格的约束,禁改公开名。
|
||||
- [ ] 孤立验证:单件跑 `board_gen_reward_test`(既有四套断言全绿=算式等价性证明)+`python tools/gdstyle_gate.py`(board_view.gd 各键持平,`max-public-methods` 仍 27)。
|
||||
- 取用:T0 读数。产出:`BoardView._cell_for(n: int, avail: Vector2) -> int`(公开静态调用面,T2/T3 逐字消费)。
|
||||
- 取用:T0 读数。产出:`BoardView._cell_for(n: int, avail: Vector2) -> int`(调用面,T2/T3 逐字消费)。
|
||||
|
||||
### 7.3 T2——探针门槛与最大板推定(估时 0.1 个工作日)
|
||||
### 7.3 T2——探针阈值退役与判据换锚(估时 0.1 个工作日)
|
||||
|
||||
- [ ] `layout_probe.gd:15` 改 `const MIN_CELL := 30.0`,注释改为「可读下限 30px(battle.md §棋盘格尺寸公式;结构钳制 [26,46] 管算式不越界,是另一回事)」。
|
||||
- [ ] 顶部加 `const BoardViewScript := preload("res://presentation/views/board_view.gd")`(静态调用载体)。
|
||||
- [ ] `_check_one` 在既有格子断言之后追加两段:①board_view 零尺寸前置——`bv.size.x <= 0.0 or bv.size.y <= 0.0` 时记问题「board_view 零尺寸(可用面积不可信,格边长推定无依据)」;②推定断言——对 `n in [13, 14]` 取 `BoardViewScript._cell_for(n, bv.size)`,`< int(MIN_CELL)` 即记问题,消息含实板可用面积、板型、算出的格边长与下限。
|
||||
- [ ] 收口 PASS 消息补一句「13/14 板推定 ≥30px」。
|
||||
- [ ] 孤立验证:单件跑 `layout_probe`(两档全绿)+`tools/probe_gate.sh` 默认段(含本探针在内的默认段退出码 0)。
|
||||
- 取用:T1 `_cell_for`。产出:绿的两档探针日志(T4 对拍基准)。
|
||||
- [ ] 删除 `layout_probe.gd:15` `const MIN_CELL := 36.0` 整行。
|
||||
- [ ] `:93-95` 阈值断言替换为**等价断言**:读 `var b = bv.get("_board")`(内部字段直查——受控例外,对照表行 1 注明理由:实跑板型 N 无公开出口),`var n := maxi(b.width, b.height)`;断言 `cell == float(BoardViewScript._cell_for(n, bv.size))`,失败消息含实板可用面积、n、两值(生产写入 vs 单点返回)。文件顶部加 `const BoardViewScript := preload("res://presentation/views/board_view.gd")`(静态调用载体)。
|
||||
- [ ] `:44-45` PASS 消息改写:去 MIN_CELL 引用,改为「三区几何不变量全过+格边长等价成立(基座实读 %.0fpx)」。
|
||||
- [ ] `_check_one` 追加两段:①board_view 零尺寸前置——`bv.size.x <= 0.0 or bv.size.y <= 0.0` 时记问题「board_view 零尺寸(可用面积不可信,推定无依据)」;②**推定留痕**:对 `n2 in [13, 14]` 取 `var est := BoardViewScript._cell_for(n2, bv.size)`,随 PASS 打印「13 板推定 %dpx/14 板推定 %dpx」留痕(实现报告对拍 §2 第 7 条锚点读数 30/28;对拍不一致=§9 L3 #2 呈报,禁实现侧改锚)。
|
||||
- [ ] 孤立验证:单件跑 `layout_probe`(两档全绿,PASS 行含推定读数)+`tools/probe_gate.sh` 默认段退出码 0+`grep -rn "MIN_CELL" game/` 零命中。
|
||||
- 取用:T1 `_cell_for`。产出:绿的两档探针日志含四档推定读数(T4 对拍基准)。
|
||||
|
||||
### 7.4 T3——格边长冒烟扩四档(估时 0.1 个工作日)
|
||||
|
||||
- [ ] `_smoke_layout_cell_formula` 的 `:57` 循环集改为四档取样表:`[{L:1,D:0,n:10}, {L:2,D:0,n:12}, {L:3,D:0,n:13}, {L:3,D:9,n:14}]`(按 §2 第 6 条的实表取档)。
|
||||
- [ ] 每档追加两条断言:①板型一致——`maxi(b.width, b.height) == 取样 n`(表改动导致取样落错档时红);②算式单点——`int(cell) == BoardViewScript._cell_for(n, avail)`(`set_cell_area` 写入值与纯函数返回同值)。
|
||||
- [ ] 既有两条断言(网格高 ≤ 可用高−24、格边长 ∈ [26,46])逐字保留,仅把消息前缀从 `L%d` 扩成 `L%d D%d`;`:60` 样本 `Vector2(1036, 460)` 不改(本卡把「真实可用面积」的可读面交给探针,样本只承担算式不溢出面)。
|
||||
- [ ] 既有两条断言(网格高 ≤ 可用高−24、格边长 ∈ [26,46])逐字保留,仅把消息前缀从 `L%d` 扩成 `L%d D%d`;`:60` 样本 `Vector2(1036, 460)` 不改(样本只承担算式不溢出面,真实可用面积守卫归探针 V1/V4)。
|
||||
- [ ] 孤立验证:单件跑 `board_gen_reward_test`(四档全绿)。
|
||||
- 取用:T1 `_cell_for`、T0 取档对账。产出:四档冒烟日志(T4 对拍基准)。
|
||||
|
||||
### 7.5 T4——批末收口与产出物落位(估时 0.05 个工作日)
|
||||
|
||||
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体 → 代码域;02 单元与集成按关联档选跑 `board_gen_reward` 套件,冒烟/系统测试/静态扫描三件恒跑)。里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
|
||||
- [ ] S13 键账对账:board_view.gd 各键持平(`max-public-methods` 27 不变、`max-class-variables` 32 不变、无 NEW 键);探针与测试不在 S13 扫描面。
|
||||
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体 → 代码域)。里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
|
||||
- [ ] S13 键账对账:board_view.gd 各键持平(`max-public-methods` 27 不变、`max-class-variables` 31 不变、无 NEW 键);探针与测试不在 S13 扫描面。
|
||||
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):
|
||||
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与各卡改后读数对拍+探针实读的 `board_view.size`(两档视口)与 L1 格边长实测值+13/14 推定值+实机目检总清单(登记不阻塞:「第三关 14×14 板格面数字清楚可辨」一项,时间由用户自定)。
|
||||
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面第 1 行(battle.md 算例表 vs 实测)逐条有落点。
|
||||
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与各卡改后读数对拍+**锚点读数留档**(两档视口 × 四板型实读/推定全表=§2 第 7 条,未来批次对照基准)+实机目检总清单(登记不阻塞:「第三关 14×14 板格面数字清楚可辨」一项——2026-11-01 用户已实际背书现行画面,登记照留)。
|
||||
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面第 1 行(battle.md `:144` 退役)有落点。
|
||||
- `reports/iter<N>-验收审计-<日期>.md`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
|
||||
- 取用:T1–T3 全部产出。产出:代码域闸回执+三份 reports/ 产出物。
|
||||
|
||||
## 8. 验证闸(改动—验收对照表)
|
||||
|
||||
本批三处改动全部落在守卫面(探针门槛/测试覆盖/算式落点),游戏行为零变化,故无新玩法验收;用户目检一项登记不阻塞(T4)。对照表 5 行对应 §1 完成定义第 1–5 条。
|
||||
本批三处改动全部落在守卫面(阈值退役/算式落点/测试覆盖),游戏行为零变化,故无新玩法验收;用户目检一项登记不阻塞(T4)。对照表 5 行对应 §1 完成定义第 1–5 条。
|
||||
|
||||
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | 探针可读门槛与 battle.md 对齐(36→30) | `layout_probe.gd:15` 常量与注释;`:92-94` 断言文本沿用 | 「layout_probe 两档几何不变量」`layout_probe.gd:_ready`(尾缀〔格边长 V1〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 探针经真实战斗屏装配读到 `board_view.cell_size` 且 ≥30px(下限判据直接作用于实机取值) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 2 | 算式单点(`_cell_for`) | `board_view.gd:191-209` 抽函数并改调;`:189-190` 注释补唯一落点句 | 「格边长算式单点等价」`board_gen_reward_test.gd:_smoke_layout_cell_formula`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:53-69` 循环集改四档取样+板型一致断言 | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边长 ∈ [26,46](此前从未执行) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 4 | 最大板可读推定(实板可用面积) | `layout_probe.gd:_check_one` 新增 board_view 零尺寸前置+`n∈{13,14}` 推定断言 | 「最大板格边长推定」同探针(尾缀〔格边长 V4〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 以探针实读 `board_view.size` 代入 `_cell_for(13/14, size)` 得 ≥30px(真实容器尺寸下的最大板可读面——不推关即可覆盖) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 5 | 批末收口 | 全批 | 代码域闸:`tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 新增:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 1 | 探针阈值退役+格边长接线守卫(等价判据) | `layout_probe.gd:15` 删常量;`:93-95` 阈值断言换 `cell == _cell_for(n, bv.size)` | 「layout_probe 两档几何不变量」`layout_probe.gd:_ready`(尾缀〔格边长 V1〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | 实读 `cell_size`(生产经 `set_cell_area` 真实写入)与 `_cell_for` 同输入返回严格相等——生产停止调单点算式即红;`grep -rn "MIN_CELL" game/` 零命中 | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 2 | 算式单点(`_cell_for`) | `board_view.gd:206-215` 抽函数并改调;`:204-205` 注释补唯一落点句 | 「格边长算式单点等价」`board_gen_reward_test.gd:_smoke_layout_cell_formula`(尾缀〔格边长 V2〕) | 单元 | `godot --headless --path game --script res://tests/unit/core/board_gen_reward_test.gd` | `set_cell_area` 写入的 `_cell` 与 `BoardView._cell_for(n, avail)` 返回值逐档严格相等(同一事实两路读取,分叉即红) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 3 | 13×13/14×14 进入冒烟 | `board_gen_reward_test.gd:53-69` 循环集改四档取样+板型一致断言 | 「格边长冒烟四档板型」同函数(尾缀〔格边长 V3〕) | 单元 | 同上 | 13/14 两档首次被断言:取档尺寸与实表一致、网格高 ≤ 可用高−24、格边长 ∈ [26,46](公式自身结构钳制——既有断言逐字保留) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 4 | 最大板推定守卫(实板可用面积,留痕型) | `layout_probe.gd:_check_one` 新增零尺寸前置+`n∈{13,14}` 推定(读数打印) | 「最大板推定留痕」同探针(尾缀〔格边长 V4〕) | 探针 | `godot --headless --path game res://probe/layout_probe.tscn` | ①零尺寸前置(size ≤ 0 记红);②推定读数打印并与实现报告留档的锚点读数(基座档 30/28px)逐值对拍,不一致=§9 L3 #2 呈报(禁实现侧改锚) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
| 5 | 批末收口 | 全批 | 代码域闸:`tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 既有:战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) |
|
||||
|
||||
**测试削弱申报**(实现期允许的旧断言改动全集,超出即审计判红):
|
||||
1. **探针门槛 36→30=阈值放宽**,依据 battle.md `:144`「可读下限 30px」(旧值 36 是 12×12 旧锚点的误用,探针实跑从未触及 30–35px 区间,等于该门槛对大板无效)。补偿=第 4 行新增推定断言与第 3 行四档冒烟——原门槛守不住的面由两者接管。
|
||||
2. 第 3 行循环集从 2 档扩到 4 档=纯增量;`avail` 样本值不改;既有两条断言(网格高、区间)逐字保留。
|
||||
3. 探针新增断言=纯增量,既有格子断言与两档视口表不改。
|
||||
4. 显式禁止:删任何旧断言、放宽区间 [26,46]、把格子读取从 `cell_size`/`_cell` 换成别的入口。
|
||||
1. **删除探针阈值断言**(`:93-95` `cell < MIN_CELL` 记红)=人造阈值退役的执行面(v1 申报的「36→30 放宽」作废,v2 直接删除)。补偿=行 1 等价断言(同测量对象上严格相等,强于区间阈值——生产接线断开即红)+行 3 结构钳制四档+行 4 读数留痕对拍;目检项「第三关 14×14 板格面数字清楚可辨」继续登记(2026-11-01 用户已实际背书现行画面)。
|
||||
2. `:44-45` PASS 消息改写=文案随判据换代,非削弱。
|
||||
3. 行 3 循环集从 2 档扩到 4 档=纯增量;`avail` 样本值不改;既有两条断言逐字保留。
|
||||
4. 显式禁止:恢复任何人造阈值(30/36 及变体)、删任何旧断言、放宽区间 [26,46]、把格子读取从 `cell_size`/`_cell` 换成别的入口。
|
||||
|
||||
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面是否绑在被测改动特有的可观测面(`cell_size`/`_cell` 取值与 `_cell_for` 返回)、核对削弱申报、确认全文无变异/撤销类取证;未过不得合并。
|
||||
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面是否绑被测改动特有的可观测提交面(`cell_size`/`_cell` 取值与 `_cell_for` 返回)、核对削弱申报、确认全文无变异/撤销类取证、**并核查全仓无人造阈值回归**(grep 30/36 阈值形态);未过不得合并。
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1 自救**:单件跑红→定位该件最小单元修后重跑;探针单件超时→孤立复跑该件;`--only` 选跑零命中(用法错 exit 2)→修正选词重跑。
|
||||
- **L2 记录继续**:同一红连续 3 次同因→在实现报告「实现决定记录」记因后继续最窄确认。
|
||||
- **L3 停机写报告等用户**:
|
||||
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或 `board_view.gd|quality/max-public-methods` 升到 28(即 `_cell_for` 的下划线被去掉);本批零新增文件、零新增成员,任何键恶化都不许改基线换绿,禁开豁免。
|
||||
2. **实板可读面本身不达标**——探针实测 L1 `cell_size` < 30,或实板可用面积代入 13/14 后 < 30。这是产品面问题(不是断言错),停卡呈报,禁调低下限、禁换样本凑绿。
|
||||
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或 board_view.gd `max-public-methods` 升 28/`max-class-variables` 升 32(即 `_cell_for` 的下划线被去掉或钳制界被提取为成员);禁改基线换绿,禁开豁免。
|
||||
2. **锚点冲突复现**——实现期任何一方(用户目检/新读数)推翻 §2 第 7 条现行渲染锚点,或有人再提人造阈值方案,或探针推定读数与 §2 第 7 条留档读数对拍不一致且复测排除环境因素;停卡呈报,禁实现侧自裁。
|
||||
3. **四档取档对不上**——`resolve_row(1,0)/(2,0)/(3,0)/(3,9)` 实测尺寸与 §2 第 6 条不符(表已改而本卡未跟上)。
|
||||
4. **算式等价性破**——T1 后既有四套断言出现改前不存在的红,说明提取改变了算式语义。
|
||||
5. **battle_layout.tres 被并发批改动**——T0 实读五字段与本卡 §2 第 8 条不符(布局几何单点化批之后又有布局批落地),读数锚点须重测重锚,停卡呈报。
|
||||
|
||||
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
|
||||
|
||||
1. `_cell_for` 内部局部变量名与注释措辞(算式、常量、clamp 数值不变)。
|
||||
2. 四档取样表用字典数组还是三元组数组(取值与断言不变)。
|
||||
3. 探针推定断言与零尺寸前置的失败消息文案、PASS 消息措辞(判据不变)。
|
||||
3. 探针推定留痕与零尺寸前置的失败消息文案、PASS 消息措辞(判据不变)。
|
||||
4. 尾缀〔格边长 V1〕–〔格边长 V4〕的字样编排(含义不变)。
|
||||
5. `BoardViewScript` 常量名(preload 载体命名)。
|
||||
6. 探针内读取板型 N 的取值写法(`bv.get("_board")` 后取 `maxi(width, height)` 的具体表达式形态——直查事实与判据不变)。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。** 2026-10-30 用户三项裁定已定案:①探针不扩到 L3(推关成本与红因混淆,最大板改走推定断言);②最紧档 14×14 的断言落两处(单元四档+探针实板推定);③算式提取用 `_cell_for` 私有名(§2 第 5 条 S13 实测依据)。编批由迭代编排段定。
|
||||
**零待裁点,开工门=U0。** 2026-11-01 用户三项裁定已定案:①锚点=用户验收的现行渲染值——基座档 14×14=28px 达标,battle.md「可读下限 30px」与探针 36px 退役;②探针判据换锚=等价+结构钳制+读数留痕,禁人造阈值;③手抄常量 `MIN_CELL` 删除、禁第二份阈值抄本。v1 的「探针不扩到 L3」「算式提取用 `_cell_for` 私有名」两项裁定沿用。battle.md `:144` 句退役的落文走反哺点通道(文档批,用户发令),不阻塞本批。编批由迭代编排段定。
|
||||
|
||||
## 12. 落位与可复用性预检
|
||||
|
||||
**复用面**:本批零新增文件、零新增类。`_cell_for` 抽自 `board_view.set_cell_area` 既有内联算式——不能只留在原处内联,是因为探针要在「非当前板型(13/14)× 真实可用面积」上求值、单元测试要在四档板型上求值,内联形态无法复用,复写算式就是造第二口径(正是本卡要消灭的缺陷形态)。探针与测试的既有工具件(`probe_ui_util.gd`、`test_kit.gd`)与本批职责无关,不复用也不扩展。
|
||||
**复用面**:本批零新增文件、零新增类、零新增成员。`_cell_for` 抽自 `board_view.set_cell_area` 既有内联算式——不能只留在原处内联,是因为探针要在「非当前板型(13/14)× 真实可用面积」上求值、单元测试要在四档板型上求值,内联形态无法复用,复写算式就是造第二口径。**worktree iter46 未提交 WIP 三件的复用判定**:`board_view.gd` 算式提取与 `board_gen_reward_test.gd` 四档冒烟两件=按本卡原样复用(等价性已证:单元 62→74 checks 全绿);`layout_probe.gd` 件=**返工**(其 v1 形态「MIN_CELL→30+推定 ≥30 断言」带有人造阈值,与本卡契约①③冲突——按 §7.3 重做)。探针与测试的既有工具件(`probe_ui_util.gd`、`test_kit.gd`)与本批职责无关,不复用也不扩展。
|
||||
|
||||
**唯一面**:算式唯一落点=`BoardView._cell_for`;可读下限 30px 的唯一代码判据面=`layout_probe.MIN_CELL`(文本出处=battle.md `:144`);四档板型唯一事实源=`board_gen.tres`(测试按 (L,D) 取行,不复制取值)。2026-10-30 实查 `game/` 下无 `cell_for`/`_cell_for` 同名或同义函数,无第二落点。
|
||||
**唯一面**:算式唯一落点=`BoardView._cell_for`;四档板型唯一事实源=`board_gen.tres`(测试按 (L,D) 取行,不复制取值);分区几何唯一事实源=`battle_layout.tres` 五字段(本批只读);**格边长无阈值判据面**——2026-11-01 起本域不存在可读下限常数,唯一结构性界限=公式内 `clampi(..., 26, 46)`(不外提)。2026-11-01 实查 `game/` 下无 `cell_for`/`_cell_for` 同名或同义函数,无第二落点;`MIN_CELL` 仅 `layout_probe.gd:15` 一处(本批删除后 grep 零残留为证)。
|
||||
|
||||
**分层与交互面**:`_cell_for` 是无状态纯函数——零成员读写、零信号、零 RNG、零 UI 依赖、零 `load()`;调用路径=`set_cell_area`(生产)/探针 `_check_one`(经脚本常量静态调用)/单元测试(同前)。preservation:不新增跨层依赖,Core 零 UI 红线零触碰(本批不触 `core/`)。
|
||||
|
||||
@@ -172,12 +178,13 @@
|
||||
|
||||
**新件三查**:本批零新增文件——三查不适用(写「无新增件」)。
|
||||
|
||||
**存量触面一查**:触碰的存量件为 `board_view.gd`(S13 扫描面内)与探针/测试两件(不在 S13 扫描面)。`board_view.gd` 预期:`quality/max-public-methods` 保持 **27**(私有静态不计数,§2 第 5 条实测)、`quality/max-class-variables` 保持 32、`quality/max-branches|_paint_cell` 等函数级键持平(本批不增分支、不增成员、不增局部变量——`set_cell_area` 局部变量反而减少)。达成手段:算式整体搬出,函数体只留一行调用。
|
||||
**存量触面一查**:触碰的存量件为 `board_view.gd`(S13 扫描面内)与探针/测试两件(不在 S13 扫描面)。`board_view.gd` 预期:`quality/max-public-methods` 保持 **27**(私有静态不计数,§2 第 5 条实测)、`quality/max-class-variables` 保持 **31**(零新增成员——钳制界禁提取为常量,§3 契约④)、`quality/max-branches|_paint_cell` 等函数级键持平(本批不增分支、不增成员、不增局部变量——`set_cell_area` 局部变量反而减少)。达成手段:算式整体搬出,函数体只留一行调用。
|
||||
|
||||
**指标例外申报**:无。
|
||||
|
||||
## 14. 源码级断言与探针契约面的随批义务(防漏项清单)
|
||||
|
||||
1. `layout_probe` 的既有两档视口表(`VIEWPORTS`)与三条几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)本批零改动——T2 只追加断言,不改判据。
|
||||
2. `board_view` 的对外只读出口(`cell_size`,`board_view.gd:93-95`)是探针取值面,本批不动;测试内部读 `_cell`(测试惯例已如此,`board_gen_reward_test.gd:63`),不改为公开出口。
|
||||
3. `game/tests/README.md` 能力行由写卡段同批入表(本卡成文时已落),实现批不写该文件;批末核对表上名字与实况一致。
|
||||
1. `layout_probe` 的既有两档视口表(`VIEWPORTS`)与三条几何不变量(三区非零/不越视口/互不重叠/view 含于 D 区)本批零改动——T2 只换代格边长判据,几何判据与档位表一字不动。
|
||||
2. `board_view` 的对外只读出口(`cell_size`)是探针取值面,本批不动;测试内部读 `_cell`、探针内部读 `_board`(板型 N 取值——受控例外,对照表行 1 已注明理由:N 无公开出口),不为此新开公开出口。
|
||||
3. `battle_layout.tres` 五字段为分区几何唯一事实源(1cdef6ba 起),本批只读;任何布局字段改动归设计批。
|
||||
4. `game/tests/README.md` 能力行由 v1 写卡段同批入表,v2 名字与入口不变,实现批不写该文件;批末核对表上名字与实况一致。
|
||||
@@ -0,0 +1,151 @@
|
||||
# 需求卡片:状态合并累加制——同名状态重复授予改加时长
|
||||
|
||||
> 日期:2026-11-01
|
||||
> 性质:需求改动实现批卡片;零待裁点,开工门=U0。
|
||||
> 来源:待办清单「状态合并改累加制——同名状态重复授予改加时长」条(2026-10-31 用户立案并四点裁决闭合;登记批 git 提交 d216ae04,设计闭合批 870b2f2b)。
|
||||
> 唯一事实源:[增益状态](../design/gdd/systems/战斗循环/增益状态.md) v1.3(2026-10-31)——§同名状态再拿到(合并策略表:叠加/累加/并存)· §剩余次数怎么倒数 · 「幅度与量纲」节盾牌加护行与字段表。本卡只实现上述设计,不改设计。
|
||||
> 估时:T0–T2 ≈ **1.0~1.25 个工作日**(中值 ≈1.125)。
|
||||
|
||||
## 一、目标与完成定义
|
||||
|
||||
本批把状态表六行(attack_up/ac_up/weaken/empower/fortify/defensive_stance)的合并策略由「refresh=重复授予重置剩余次数」改为「accumulate=重复授予在剩余次数上累加本次授予的时长」,状态容器 refresh 臂同步换为累加臂。
|
||||
|
||||
完成定义:
|
||||
|
||||
1. 表行:六行 `stack_policy` 值 refresh→accumulate;四行 stack 行与其余全部字段值零改动。
|
||||
2. 容器:同名重复授予 → 剩余结算次数加上本次授予的生效时长(`duration_override` ≥1 时加覆盖值);层数恒 1;时长不封顶;空授予面消除(每次授予必有剩余次数变化)。
|
||||
3. 叠加(stack)与并存(independent)两臂语义零变化;快照纪律不变(首授快照恒定)。
|
||||
4. 词表换代:schema 词表改 `["stack", "accumulate", "independent"]`,加载期枚举校验随词表生效(fail-fast 不变)。
|
||||
5. 既有断言数值换代两处+断言标签/注释文字换代两处(第十一节申报共四条)+新增累加断言三条,全部真实入口、当轮执行并通过。
|
||||
6. 独立验收审计通过(报告落 `reports/iter<N>-验收审计-<日期>.md`,未过不得合并);反哺点两条落反哺点清单;README 能力行批末只核对名字与实际落地一致;本批不写 `design/` 与根规则文件。
|
||||
|
||||
## 二、设计事实源
|
||||
|
||||
1. 增益状态.md v1.3 §同名状态再拿到·合并策略表「累加」行:剩余结算次数加上本次授予的时长——技能施放行带覆盖时长时按覆盖值加;层数不变;现行内容行六行用累加。
|
||||
2. 同文档 §剩余次数怎么倒数:累加语义下重复拿一次就按本次授予时长多攒一段,同一轮拿几份攒几段。
|
||||
3. 同文档「幅度与量纲」节例表(盾牌加护行):层数恒 1、剩余结算次数累加;掷点判定阈值=主角基准+层数——该句为现状描述,AC 读取路径本无「上限 2 层叠读取法」代码(见第三节 8),AC 侧零改动。
|
||||
4. 四点裁决(待办清单条目在案):六行 refresh 全改累加/时长不封顶/只加时长、层数与强度不变/累加量=本次授予的生效时长(带覆盖时长时按覆盖值)。
|
||||
5. [地图战斗资源](../design/gdd/systems/战斗循环/地图战斗资源.md) 削弱行引用句(:130,2026-10-31 设计闭合批随改、累加语义已落)——待办清单条目闭合凭据的第二件,与条目实况对齐;无实现缺口。
|
||||
|
||||
## 三、现状事实(代码级,开工自行复核)
|
||||
|
||||
1. 表行:`game/data/tables/status_effects.tres` 十行;六行 `stack_policy: "refresh"`——attack_up :14、ac_up :23、weaken :32、empower :68、fortify :77、defensive_stance :86;四行 `"stack"`——enrage :41、off_guard :50、sapped :59、alert :95;无 independent 行。相关表值:attack_up/ac_up duration 1·max_stack 2;weaken duration 2·max_stack 16;empower/fortify/defensive_stance duration 2·max_stack 1。
|
||||
2. 词表:`game/data/schemas/status_effect_table.gd:11` `STACK_POLICIES := ["stack", "refresh", "independent"]`;:55–56 加载期枚举校验(出域致命——fail-fast)。
|
||||
3. 容器:`game/core/status_container.gd:28` `apply_effect(effect_id, source, duration_override=-1)` 唯一授予入口(:37 override <1 → 行值 duration_settlements);:40–49 策略 match;:44–45 refresh 臂转发 `_apply_refresh` :61–67——命中 `inst.remaining = dur`(重置)、未命中追加(层数 1)、恒返回 true。
|
||||
4. 叠加臂 :72–80(满层拒零写入、未满加层、不触碰 remaining)与并存臂 :41–43(新建独立实例)——本批零改动。
|
||||
5. 快照:`_grant_snapshot` :52–57 授予时快照;refresh 臂重授不换快照——本批沿用该口径(第 13 节代拍板 2)。
|
||||
6. 衰减:`decay_round` :122–134 每实例 −1、归 0 移除(先兑付后衰减的编排信任在战斗结算)——累加制零改动。
|
||||
7. 授予链三路(真实入口全走 `apply_effect` 同口):`game/core/reward_grant.gd:239`(安全格 percent 奖励 → attack_up/ac_up)· `game/core/damage_controller.gd:451`(效果引擎 fx_apply_status)· `game/core/settlement/combat_segment.gd:89`(敌方增益/妨害技施放——:88 `dur = srow.buff_duration`,覆盖时长的真实入口)。
|
||||
8. AC 读取:`game/core/damage_controller.gd:616–631` `defender_ac` = 基线(hero 表或合流入参)+Σ ac 型 `effective_value`(层数×快照幅度)——无上限钳制;ac_up 层数恒 1 → 阈值=基准+1。v1.3「上限 2 层叠读取法退役」为现状描述句,代码无该读取路径——AC 侧零改动。
|
||||
9. AC11 采样:`game/tests/unit/battle_settlement/roll_multiplier_test.gd:241` 起 `_ac11_tier_samples` 每轮衰减后重授——实例已移除、重授为新建,不触合并臂——累加制下行为与断言不变。
|
||||
10. 断言换代点:`roll_multiplier_test.gd:158–161`(削弱重复授予 remaining==2 重置语义断言与 :157–158 注释)+ :221–236 `_weaken_refresh_probe`(:230 重授注释「remaining ← 2」;:159 调用点与 :342 注释引用该探针名——改名随批换代);`game/tests/unit/data_config/table_structure_guard_test.gd:273–275`(ac_up 行断言 `stack_policy == "refresh"`);`game/tests/unit/battle_settlement/damage_controller_test.gd:376`(断言标签「refresh 政策重复授予受理且层数恒 1」——行为面累加制下仍绿〔层数恒 1、池值不变〕,仅标签文字换代)。
|
||||
11. 不受影响断言:`damage_controller_test.gd:391`(stack 行过滤)· `seal_polarity_test.gd:684`(sapped=="stack")· `system_contracts_test.gd:_test_status_boolean_acceptance`(布尔受理——attack_up 行,refresh→accumulate 行;单次授予不触合并臂)。
|
||||
12. sim/probe 零消费:`stack_policy`/`apply_effect` 于 `game/sim`、`game/probe` grep 零命中;状态实例不落存档断点(战斗域生命周期——`notify_battle_start` 清空,`game/core/breakpoint_store.gd` 无状态片段)。
|
||||
13. EV 锚:战斗结算域 EV 抽样对锚(goblin 锚)读战斗真实结算——合并语义改变重复授予的削弱/增益存续时长,EV 有漂移可能(先例:触发器批 git 提交 4868e11b 触发重锚 ba5105aa 归绿)。
|
||||
14. S13 基线:status_container/status_effect_table/damage_controller 均不在 `tools/gdstyle_baseline.txt`(默认阈值内);tests 不在 S13 扫描面。
|
||||
15. 表批注:status_effects.tres :99–141 为逐批变更叙事批注(含 :129–141 叙述 refresh 语义的历史——本批换代后成陈旧叙事)。批注保留是用户明令现状(git 提交 4a2ed961,2026-11-01:数值表 `; ` 批注全量恢复)——本批零触碰,处置归用户另裁,实现报告留遗留观察记录。
|
||||
16. 文档漂移(考证发现,非本批造成):增益状态.md「现在有哪些状态」节记「现行九行」且行表缺 defensive_stance(护身)行——该行 2026-10-27 触发器批落表(git 提交 4868e11b),文档行清单未跟;合并策略表「六行用累加」计数与表实况一致。
|
||||
|
||||
## 四、设计契约
|
||||
|
||||
- 🔴 **C1 累加语义单点**:重复授予的合并只发生在状态容器策略臂(match 分派后累加实现)——任何调用方不得自算 remaining 或二次实现合并。
|
||||
- 🔴 **C2 表驱动**:累加行集合以 `stack_policy` 字段为唯一依据——禁按 effect_id 白名单另立硬编码集合。
|
||||
- 🔴 **C3 只加时长**:累加臂只改 remaining;层数、快照(m/unit/display)、source_class、origin_cell 首授后恒定;时长不封顶(运行期 remaining 无上限,禁自造 clamp)。
|
||||
- 🔴 **C4 覆盖时长口径**:`duration_override` ≥1 时累加量=override 值;<1 时=行值 duration_settlements。
|
||||
- 🔴 **C5 两臂不动**:stack 臂满层拒零写入与 independent 臂新建实例语义零变化——触碰即 L3。
|
||||
- 🔴 **C6 词表唯一**:`stack_policy` 取值集合只在 schema 词表一处定义;tres 六行与词表同批换代,禁留双值兼容(旧值 "refresh" 出词表即加载期致命,同批清干净)。
|
||||
|
||||
## 五、范围边界
|
||||
|
||||
不做:衰减节拍改动(先兑付后衰减不变);单体寻址扩展位实现(接口定稿在案、随首个单体效果内容批);叠加/并存行为调整;status_effects.tres 其余字段与四行 stack 行值改动;表批注处置(:99–141 含本批换代后的陈旧叙事——用户 2026-11-01 恢复令 git 提交 4a2ed961 在案,本批零触碰,另裁另批);「增益叠层/衰减/清除结算链」覆盖缺口的其余断言补齐(归待办清单「能力清单覆盖缺口补齐」条);EV 锚重锚(红则停机呈报,重锚归用户档位发起)。
|
||||
|
||||
反哺去向:第六节两条 → `reports/iter<N>-反哺点-<日期>.md`,用户裁决后归文档批落文。
|
||||
|
||||
## 六、关联文档同步面
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| design/gdd/systems/战斗循环/增益状态.md | 字段表 `stack_policy` 取值行记「stack / refresh / independent」——词表换代后 refresh 值不复存在 | 反哺点(iterN 反哺点清单):取值改「stack / accumulate / independent」,用户裁决后文档批落文 |
|
||||
| design/gdd/systems/战斗循环/增益状态.md | 「现在有哪些状态」节记「现行九行」且行表缺 defensive_stance(护身)行——本批考证发现的存量漂移,非本批造成 | 反哺点(iterN 反哺点清单):九行→十行+补护身行(敌方输出减益 −25 percent、duration 2、累加、max_stack 1、来源敌方技能「孤注守势」),文档批落文 |
|
||||
| game/tests/README.md | 能力行新增+覆盖域表「增益状态」行更新 | 写卡段随本卡已入表(实现批禁写该文件红线不变;批末收尾只核对表上名字与实际落地一致) |
|
||||
|
||||
## 七、文件结构图
|
||||
|
||||
| 文件 | 职责 | 改动性质 |
|
||||
|---|---|---|
|
||||
| game/data/tables/status_effects.tres | 状态表十行 | 行为改动:六行 stack_policy 值换代(对照表 R1);表批注零触碰(范围边界在案) |
|
||||
| game/data/schemas/status_effect_table.gd | 表 schema 与词表加载期校验 | 行为改动:词表换代(对照表 R1) |
|
||||
| game/core/status_container.gd | 状态容器(授予/合并/衰减唯一落点) | 行为改动:refresh 臂→累加臂(对照表 R2/R3/R4) |
|
||||
| game/tests/unit/battle_settlement/roll_multiplier_test.gd | 掷点/削弱/AC11 套件 | 行为改动(测试面):削弱累加探针换代+新增三条断言(对照表 R2–R4;申报见第十一节) |
|
||||
| game/tests/unit/data_config/table_structure_guard_test.gd | 表结构守卫 | 行为改动(测试面):ac_up 行断言换代(对照表 R1;申报见第十一节) |
|
||||
| game/tests/README.md | 能力清单与覆盖域表 | 写卡段维护件——本卡落位时已入表,实现批零改动 |
|
||||
|
||||
## 八、落位与可复用性预检
|
||||
|
||||
- **复用面**:零新增文件、零新增函数、零新增数据件——累加臂就地替换 refresh 臂(`_apply_refresh` 改名 `_apply_accumulate`),词表与表行就地换代;合并逻辑唯一落点=容器策略 match(C1/C6),无第二处可复用或需新建的合并实现。
|
||||
- **唯一面**:`stack_policy` 词表唯一出处=schema :11;取值**定义面**全仓 grep 仅存于 tres 六行+schema 词表+容器臂+三处断言(第三节 1/2/3/10)——叙事批注与测试注释例外(第三节 15/10 在案),不属定义面。
|
||||
- **分层与交互面**:表行(data/tables)→ schema 校验(data/schemas,加载期致命)→ 容器臂(core)→ 消费面(defender_ac 与池乘区——零改动)。Core 零 UI、数值单点经 ConfigDB、RNG 零新增——既有红线零触碰。
|
||||
|
||||
## 九、实现规范预算
|
||||
|
||||
- 新增文件:零——新件三查不适用;迁移函数:零(累加臂就地改写,无自存量文件迁入的函数)。
|
||||
- 存量触面一查:触碰产品码两件(status_effect_table.gd、status_container.gd)均不在 gdstyle 基线(默认阈值内);预期 S13 键较现状零新增。tests 不在 S13 扫描面。
|
||||
|
||||
## 十、任务卡
|
||||
|
||||
**T0 开工门+基线读数(0.25 天)**
|
||||
|
||||
- [ ] worktree 建区+`git status` 归属核对(并行遗留只登记不触碰)
|
||||
- [ ] S13 触面读数:两产品文件现行键数记录进实现报告(预期零键——不在基线)
|
||||
- [ ] EV 锚改前读数留底:从最近 CI 回执抄录战斗结算域 EV 锚读数(goblin 现值)进实现报告,作批末对拍锚
|
||||
|
||||
**T1 累加制单点实装(0.5~0.75 天)**
|
||||
|
||||
- [ ] status_effects.tres:六行 `stack_policy` "refresh"→"accumulate"(:14/:23/:32/:68/:77/:86;保持既有 CRLF 行尾)——可核查:grep '"stack_policy": "refresh"' 零命中(值行限定写法——:129/:139 恢复批注含同词、不属值行,禁触碰)、"accumulate" 恰六处
|
||||
- [ ] status_effect_table.gd:11:`STACK_POLICIES` 换代 `["stack", "accumulate", "independent"]`——可核查:加载链全表装载零红(枚举校验随词表生效)
|
||||
- [ ] status_container.gd::44–45 match 臂 "refresh"→"accumulate";`_apply_refresh` 改名 `_apply_accumulate`——命中 `inst.remaining = int(inst.remaining) + dur`、未命中追加、恒 true;:60 注释换代(累加语义——R += D;L 不变;快照沿首授)——可核查:容器内 stack_policy 语境 "refresh" 零残留
|
||||
- [ ] roll_multiplier_test.gd:③段 :157–158 注释与 :160–161 断言换代(重置 2 → 累加 3);`_weaken_refresh_probe` 改名 `_weaken_accumulate_probe`(:159 调用点与 :342 引用注释随改),探针改五段式——授予(2)→衰减(1)→重授(1+2=3,V1)→覆盖值重授(3+3=6,V3)→同轮再授(6+2=8,V2);返回 {layers, pool, after_regrant, after_override, after_extra, snap_magnitude} 六读数(pool 键保留——:163 池读数断言零改动在位)
|
||||
- [ ] 新增断言三条(尾缀〔状态累加 V1/V2/V3〕):V1=重授后 remaining==3、layers==1、snap_magnitude==−65(首授快照恒定);V3=覆盖值累加后 remaining==6;V2=同轮再授后 remaining==8
|
||||
- [ ] table_structure_guard_test.gd:273–275:ac_up 行断言 `stack_policy == "accumulate"`+消息换代(累加语义句)——可核查:断言尾缀〔状态累加 V4〕可 grep、套件绿
|
||||
- [ ] 孤立验证:六套件最窄复跑(roll_multiplier/table_structure_guard/system_contracts/damage_controller/seal_polarity/round_flow_skill_pool——EV 锚套件提前曝红)全绿或按 L3②停机、断言尾缀可 grep
|
||||
|
||||
**T2 产出物落位(0.25 天)**
|
||||
|
||||
- [ ] 实现报告:各卡状态+改动—验收对照表结果列+EV 锚改前改后读数对拍+实机目检总清单(同名状态重复授予后状态存续时长延长——登记不阻塞)+遗留观察记录(tres :129–141 refresh 陈旧叙事批注——处置归用户另裁)
|
||||
- [ ] 反哺点清单:第六节反哺两条逐条落点
|
||||
- [ ] 独立验收审计:未参与实现的会话/子代理按 test-conventions §8.7 审计,报告落 `reports/iter<N>-验收审计-<日期>.md`——未过不得合并
|
||||
- [ ] 批末按改动域跑闸(代码域)全绿;合回后本地 CI 复验
|
||||
|
||||
## 十一、验证闸(改动—验收对照表)
|
||||
|
||||
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| R1 | 六行合并策略换代+词表加载期校验 | status_effects.tres :14/:23/:32/:68/:77/:86;status_effect_table.gd :11 | table_structure_guard_test ac_up 行断言换代〔状态累加 V4〕(值=="accumulate")+全表装载零红 | 接口 | ConfigDB 加载链 → `StatusEffectTable.row_by_id()` | stack_policy 字段取值(加载期枚举校验随行生效) | 新增:同名状态合并策略(叠加/累加/并存)与剩余次数语义 |
|
||||
| R2 | 同名重复授予累加时长+层数恒 1 | status_container.gd :44–45/:60–67 累加臂 | roll_multiplier_test._smoke_armor_roll 削弱累加断言〔状态累加 V1〕(重授 remaining==3、layers==1、snap_magnitude==−65) | 单元 | `StatusContainer.apply_effect()` 经真实编排宿主 begin_battle | remaining/layers 只读投影(query_active) | 新增:同名状态合并策略(叠加/累加/并存)与剩余次数语义 |
|
||||
| R3 | 同轮双授予累加两段 | 同上 | 〔状态累加 V2〕(同轮再授 remaining==8) | 单元 | 同上 | remaining 累计 | 新增:同名状态合并策略(叠加/累加/并存)与剩余次数语义 |
|
||||
| R4 | 覆盖时长按覆盖值累加 | apply_effect :37 override 入累加臂 | 〔状态累加 V3〕(覆盖值 3 重授 remaining==6) | 单元 | 同上(真实施放链 combat_segment :89 走同口) | remaining 累加覆盖值 | 新增:同名状态合并策略(叠加/累加/并存)与剩余次数语义 |
|
||||
| R5 | 快照纪律不变(首授快照恒定) | 非行为改动——累加臂沿用既有重授不换快照口径,零代码变化 | 〔状态累加 V1〕快照断言(snap_magnitude==−65) | 单元 | 同 R2 | 快照域取值(snap_magnitude) | 新增:同名状态合并策略(叠加/累加/并存)与剩余次数语义 |
|
||||
| R6 | 叠加/并存两臂零变化 | 非行为改动——两臂零代码变化 | damage_controller_test :391+seal_polarity_test :684+system_contracts_test 布尔受理——既有断言零改动复跑即回归证词 | 单元 | 各既有真实入口 | 既有断言零改动 | 既有:系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理 |
|
||||
|
||||
**测试削弱申报**(允许的旧断言改动,全部为语义换代非削弱):
|
||||
|
||||
1. roll_multiplier_test.gd:160–161 削弱续时断言——refresh 重置语义(remaining==2)作废,换代为累加语义(remaining==3)——被测行为就是本批改动本体。
|
||||
2. roll_multiplier_test.gd:157–158 注释、:230 探针注释与 :342 探针名引用注释换代(「续时:remaining ← 2」→累加口径;旧函数名 `_weaken_refresh_probe` 全仓清零)。
|
||||
3. table_structure_guard_test.gd:274–275 ac_up 行断言值与消息换代(refresh→accumulate)——同上。
|
||||
4. damage_controller_test.gd:376 断言标签「refresh 政策重复授予受理且层数恒 1(叠层改续时裁决)——池值不变」——行为面累加制下仍绿(层数恒 1、池值不变,断言数值零改动),仅标签文字换代为累加口径。
|
||||
|
||||
## 十二、异常分级
|
||||
|
||||
- **L1 自救**:一次重试(表装载/套件瞬时红)。
|
||||
- **L2 记录继续**:同红灯连续 3 次升级。
|
||||
- **L3 停机呈报**:①C1–C6 任一触碰;②战斗结算域 EV 锚套件红(累加制有意行为改动的期望值漂移)——锚内子集判红即停,重锚归用户档位发起(先例=触发器批 2026-10-27:EV 0.9148 出带 → 重锚 ba5105aa 归绿);③S13 新增红键=上游缺陷停卡退回写卡段(机检预算互斥)。
|
||||
|
||||
## 十三、代拍板白名单
|
||||
|
||||
1. 词表值命名 `accumulate`(实现标识符——语义已由 v1.3 全定;反哺点随批登记文档字段表行换代)。
|
||||
2. 合并授予不改快照(沿用既有 refresh 臂口径——首授快照恒定、幅度 override 只作用首授)。
|
||||
3. 测试函数改名 `_weaken_refresh_probe`→`_weaken_accumulate_probe` 与新增断言措辞(V 尾缀〔状态累加 Vn〕编号在案)。
|
||||
|
||||
## 十四、待裁点
|
||||
|
||||
零待裁点,开工门=U0。(四点裁决 2026-10-31 用户裁闭合——待办清单条目在案;本卡全部实现项与代拍板 1–3 均为该裁决的实现面,无新增设计分叉。)
|
||||
@@ -0,0 +1,244 @@
|
||||
# 需求卡片 · 立绘显示档批(v1.1)
|
||||
|
||||
> **用途**:迭代实现批的实施合同(卡①单卡批)。v1.1=双审(grilling+godot-master)后执行级订正,设计面零改动(复核记录=`reports/复核-双审-grilling与godot-master-立绘显示档批-2026-11-01.md`)。
|
||||
> **来源**:待办清单「立绘清晰度回退评估——原稿分辨率+三档显示」条目(状态=美术设计已闭合+已写卡)。
|
||||
> **唯一事实源**:`design/art/art-bible.md` §8.2「站桩立绘显示档 = 三档绝对高」条(v3.32)+ `design/art/character-design.md` §5.2/§5.5 指路行。
|
||||
> **改动面**:美术素材+游戏代码。 **估时**:≈0.85 个工作日。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**做什么**:把 9 件站桩立绘(八敌 + 主角三态)从「原稿降采样到 48–94 再 ×2 显示」改成「原稿分辨率入包 + 显示按三档绝对高缩放」。
|
||||
|
||||
| 档 | 绝对显示高 | 归档 |
|
||||
|---|---|---|
|
||||
| 小型 | 96 | 地精 goblin、巨蜘蛛 giant_spider、灰矮人 duergar |
|
||||
| 中型 | 144 | 主角 hero、钩爪恐魔 hook_horror、熊地精 bugbear |
|
||||
| 大型 | 188 | 食人魔酋长 ogre_chief、蛛化卓尔 drider、灰矮人石君王 duergar_king |
|
||||
|
||||
宽度一律随源图比例(高定宽随),缩放过滤 NEAREST。
|
||||
|
||||
**什么状态算完**:
|
||||
|
||||
1. 9 件素材 = 原稿紧裁原样落 `game/assets/art/character/`,盘上无任何降采样痕迹;
|
||||
2. `standee_motion.gd` 硬编码 ×2 退役,显示倍率由逐件档高算出;
|
||||
3. `tools/standee_asset_check.py` 与 `tools/pixelize_candidates.py` 改按显示档与原稿高校验,全绿;
|
||||
4. 真实入口断言钉住逐件显示档高、纵横倍率恒等、两表同值;
|
||||
5. 样张脚本站桩段与探针文案随动;
|
||||
6. 代码域闸全绿 + 改前/改后并排对照图落盘 + 产出物三份齐。
|
||||
|
||||
---
|
||||
|
||||
## 2. 现状事实(代码级,开工复核)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | 显示倍率硬编码:`w/h = tex.get_width/height() * 2.0`、`scale = Vector2(2.0, 2.0)` | [standee_motion.gd:63](game/presentation/views/standee_motion.gd#L63) |
|
||||
| F2 | 立绘盒几何:`box := Vector2(150, 176)`;立绘盒矩形 `(0,44)-(box.x, box.y-80)` → 立绘区 150×96,高于 96 者按 `(box_h-h)*0.5` 向上溢出 | [standee_body.gd:24](game/presentation/views/standee_body.gd#L24)、[:309](game/presentation/views/standee_body.gd#L309)、[standee_motion.gd:62](game/presentation/views/standee_motion.gd#L62) |
|
||||
| F3 | 盒宽常量与盒高注入:`_STANDEE_W := 150.0`;`hero_standee.box = Vector2(_STANDEE_W, box_h)`(盒高=行高) | [battle_stage_view.gd:10](game/presentation/views/battle_stage_view.gd#L10)、[:197](game/presentation/views/battle_stage_view.gd#L197) |
|
||||
| F4 | 主角立绘装载与三态注册(含「48px 档」注释);敌侧 `_enemy_portrait_tex(enemy_id)` 查表后 `set_portrait_texture` | [battle_stage_view.gd:132-140](game/presentation/views/battle_stage_view.gd#L132)、[:252-267](game/presentation/views/battle_stage_view.gd#L252) |
|
||||
| F5 | 纹理路径常量单点在 UiKit(八敌 + 主角三态) | [ui_kit.gd:233-247](game/presentation/views/ui_kit.gd#L233) |
|
||||
| F6 | 现行入包稿尺寸:主角三态 46×48 / 48×48 / 46×48;八敌 36×36、48×38、34×46、46×58、51×62、62×74、67×90、66×94 | 盘上 `game/assets/art/character/`(素材不入 git) |
|
||||
| F7 | **现行入包稿 = 原稿降采样,逐像素相同**:`dom_block_resize(pixel-1, 74/90/94)` 与 ogre_chief/drider/duergar_king 三件 diff 框为空(2026-11-01 会话实测) | 管线 [pixelize_candidates.py:430-457](tools/pixelize_candidates.py#L430) |
|
||||
| F8 | 原稿在盘:八敌 `pixel-1.png` 高 81–189(地精 89×90/巨蜘蛛 102×81/灰矮人 93×126/钩爪 74×94/熊地精 102×123/酋长 158×189/卓尔 140×189/石君王 133×189);主角手修 2× 三态稿内容 88×92 / 92×92 / 88×92 | `artwork/remote-gen-2026-09-26/`(不入 git) |
|
||||
| F9 | 派生管线末步即降采样:`build_standee48` 紧裁 → 等比缩至 `STANDEE_TIER_H` → `dom_block_resize` | [pixelize_candidates.py:28](tools/pixelize_candidates.py#L28)、[:38-43](tools/pixelize_candidates.py#L38)、[:459-504](tools/pixelize_candidates.py#L459) |
|
||||
| F10 | 闸件按源档高校验:`DISPLAY_SCALE=2`、`SRC_W_MAX=75`、断言①档高、断言②源宽×2≤150 | [standee_asset_check.py:50-53](tools/standee_asset_check.py#L50)、[:75-98](tools/standee_asset_check.py#L75) |
|
||||
| F11 | 样张脚本站桩段硬编码「立绘盒 96px(48 源 ×2)」;另有 A4 尺寸基线硬锁 `PORTRAIT_SRC.size==(46,48)` 与战斗语境 96×96 方框 mockup(脚本自述「换代须显式解冻重校」) | [render_sample_sheet_v3.py:775-791](tools/render_sample_sheet_v3.py#L775)、[:1135-1139](tools/render_sample_sheet_v3.py#L1135)、[:1501](tools/render_sample_sheet_v3.py#L1501) |
|
||||
| F12 | 探针文案「48px 档」(判据只查 `has_tex`,非尺寸断言) | [ui_probe.gd:139-144](game/probe/ui_probe.gd#L139) |
|
||||
| F13 | 另两消费面按固定框装载同一素材:标题屏 138×144/46×96/80×96(`STRETCH_KEEP_ASPECT_CENTERED`+NEAREST);关卡地图头像定高 48、宽随源比 | [ui_assembler.gd:170](game/presentation/ui_assembler.gd#L170)、[level_map_screen.gd:176-190](game/presentation/views/level_map_screen.gd#L176) |
|
||||
| F14 | 能力清单现行行「站桩呈现定值与素材绑定」→ `standee_present_values_test.gd`(V5–V12 在此套件) | [README:136](game/tests/README.md#L136) |
|
||||
| F15 | 舞台高 252(溢出上限的物理边界) | `game/data/tables/battle_layout.tres` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 设计契约(违反即返工;标 🔴 触碰=L3 停机呈报)
|
||||
|
||||
1. 🔴 **禁改形象**:9 件素材 = 原稿紧裁原样,禁重画、禁生成、禁 `dom_block_resize` 降档(art-bible §8.2 v3.32)。
|
||||
2. 🔴 **显示档定值单点** = art-bible §8.2;GDScript 侧 `PORTRAIT_DISPLAY_H` 与 Python 侧 `STANDEE_DISPLAY_H` 逐键同值、两侧各自钉死字面值,跨语言对账由 V17 承担(读不到文件走 L2,见 §8/§9);禁第三处复写,**禁查表兜底字面量**。
|
||||
3. 🔴 **纵横倍率恒等**(高定宽随),禁方框强拉(art-bible §8.2 槽位条)。
|
||||
4. 🔴 **体量顺序由显示档承载**:小 < 中 < 大;禁按强度重排(character-design §5.2)。
|
||||
5. 🔴 **已接受项不得当缺陷回炉**:①五件源高低于档高属放大显示(主角 92/钩爪 94/熊地精 123/地精 90/巨蜘蛛 81,倍率 1.07–1.57);②灰矮人 126→96 是全批唯一 <0.8 档(0.76× 抽行,NEAREST 描边约每 4 行断点),列目检①头号回退候选;③食人魔酋长显示宽 157 超桩盒 150;④关卡地图头像由 ≈1× 变 0.52× 降采样,列目检③。
|
||||
6. 🔴 显示档数值只落显示档表,绘制点与检查器一律读表,禁字面量散落(数值单点纪律)。
|
||||
7. 🔴 素材不入 git(art-bible §8.3);本批入 git 的只有代码/工具/测试/文档件,素材落盘由实现批执行并在实现报告逐件留读数。
|
||||
|
||||
---
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:9 件素材按档派生落盘;`standee_motion.gd` 显示倍率改造;`pixelize_candidates.py`/`standee_asset_check.py` 改显示档;样张脚本站桩段随动;探针与装载点注释随动;`standee_present_values_test.gd` 增显示档断言。
|
||||
|
||||
**不做(去向明示)**:
|
||||
|
||||
- 不重画、不生成任何图像(现有形象即终稿)。
|
||||
- 不动棋盘小人(`character_hero_idle/walk.png` 32×32 独立资产)——另属棋盘面。
|
||||
- 不改标题屏与关卡地图头像的框尺寸(两面只随素材分辨率自动变化;若目检判需专件,另批)。
|
||||
- 不动色板与 `#A79CB8`(保留在板、本批不消费;色板额度议题另批讨论)。
|
||||
- 不做站桩帧动画(待办清单「站桩帧动画」条)。
|
||||
- 不做主角态切换重摆(`standee_motion.gd:85-86` `apply_hero_state` 换纹理不重算 position,三态源宽 88/92/88 → 切换有约 3–6px 横跳,系现行 2px 的放大版;重摆=行为改动,禁实现批顺手加,要修另批)。
|
||||
- 不做关卡地图 48px 头像专件(新源在该面为 0.52× 降采样;目检判不可受则另批出专件)。
|
||||
- 不做桩盒宽 150 与 >150 溢出档的布局重排(食人魔超宽 8px 按已接受项走;实机若碰邻位则 L3 停机,布局另批)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/art/art-bible.md` §8.2 | 显示档条与排版条 >150 举例 | **已落**(前置文档批 v3.32,本区已改)——实现批禁写 `design/`,本批零义务 |
|
||||
| `design/art/art-bible.md` §8.2 槽位条 | 「高度的整数倍档」与绝对档高+非整数倍率字面冲突 | **已落**(双审后随设计批改「高度的绝对档(档高取整数像素值)」) |
|
||||
| `design/art/art-assets-checklist.md` §0 分辨率档位行 | 旧口径「站桩源高 48/敌种逐键源档 36–94/整数倍缩放纪律」 | **已落**(改指 §8.2 显示档条) |
|
||||
| `design/art/character-design.md` §5.5 | 指路行 | 同上,已落 v3.32 配套 |
|
||||
| `design/gdd/08-Alpha版本标准.md` 覆盖行 | 出口从「重产完成」改「9 件按档落位+目检」 | 已落 v1.22;实现完成后「待做→在役」列**反哺点**呈裁 |
|
||||
| `待办清单.md` 本条 | 状态推进(已写卡→已编迭代→完成删条) | 各段按状态机推进,非实现批直改 → 列**反哺点** |
|
||||
| `design/art/art-prompts.md` §5.4 产出状态 | 「待实装批落位」→「已落位」 | 实现完成后 → 列**反哺点** |
|
||||
| `design/art/art-status-checklist.md`/`art-assets-checklist.md` 站桩行 | 「按档派生落位归本条实装批」→ ✅ 已交付 | 实现完成后 → 列**反哺点** |
|
||||
| `game/tests/README.md` 能力清单 | 新能力行「站桩显示档与素材分辨率绑定」 | **写卡段已同批补行**(本卡 §9 能力行与之一致);实现批禁写该文件 |
|
||||
| `design/gdd/术语表.md` | 「显示档」是否立条 | 列**反哺点**呈裁 |
|
||||
| `体验验证清单.md` | 实机目检项(9 件清晰度、同屏体量顺序、标题屏/地图头像消费面) | 实现批禁写 → 列**反哺点**,目检清单先落实现报告 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 卡前文件结构图
|
||||
|
||||
| 文件 | 职责 | 改动性质 | 验收行 |
|
||||
|---|---|---|---|
|
||||
| `game/presentation/views/standee_motion.gd` | 显示档表 `PORTRAIT_DISPLAY_H` + `_place_portrait` 按档算倍率 | **行为改动** | V1 V2 V3 |
|
||||
| `game/presentation/views/standee_body.gd` | 探针 `portrait_probe()` 扩显示读数(几何出口单点) | **行为改动**(观测面扩展,绘制零改) | V3 V4 |
|
||||
| `game/presentation/views/battle_stage_view.gd` | 「48px 档」注释随动 | 非行为 | — |
|
||||
| `game/probe/ui_probe.gd` | 文案随动(判据不变) | 非行为 | — |
|
||||
| `game/tests/unit/presentation/standee_present_values_test.gd` | 增 V13–V16 显示档断言(复用既有套件,禁新增文件) | 新增测试 | V1–V4 |
|
||||
| `tools/pixelize_candidates.py` | `STANDEE_TIER_H` → `STANDEE_DISPLAY_H`;`build_standee48` 改紧裁不降档 | 工具(非行为) | V5 |
|
||||
| `tools/standee_asset_check.py` | 断言①改源高=原稿表+显示宽;断言②宽上限按显示宽;表名随动 | 闸件(非行为) | V5 |
|
||||
| `tools/render_sample_sheet_v3.py` | 站桩 mockup 按显示档取数 | 工具(非行为) | V7 |
|
||||
| `game/assets/art/character/`(9 件,不入 git) | 原稿紧裁落盘 | 资产 | V5 |
|
||||
| `artwork/candidates/立绘显示档批-<日期>/`(不入 git) | 改前/改后并排对照图 | 取证 | V6 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 任务卡 T0..T3
|
||||
|
||||
### 7.1 T0——改前读数与基线(估时 0.1 个工作日)
|
||||
|
||||
- [ ] 跑 `python tools/standee_asset_check.py` 记录现行 9 件读数(尺寸/档高/色数/描边占比),存实现报告改前基线表。
|
||||
- [ ] 跑 `python tools/pixelize_candidates.py --standee48 --standee-out /tmp/precheck` 确认现行管线产物与盘上素材逐件同尺寸(证明 F7 降采样链在位)。
|
||||
- [ ] 跑代码域闸确认改前全绿(确切命令见 §9 末注)。
|
||||
|
||||
**取用**:无。**产出**:改前基线读数表(T3 对照用)。
|
||||
|
||||
### 7.2 T1——素材按档派生落盘+闸件同批改口径(估时 0.25 个工作日)
|
||||
|
||||
- [ ] `tools/pixelize_candidates.py`:`STANDEE_TIER_H` 改名 `STANDEE_DISPLAY_H`,值改显示档 `{goblin:96, giant_spider:96, duergar:96, hook_horror:144, bugbear:144, ogre_chief:188, drider:188, duergar_king:188}`,主角侧 `STANDEE_SLOT_H` 改 `144`;新增原稿高表 `STANDEE_SRC_H = {goblin:90, giant_spider:81, duergar:126, hook_horror:94, bugbear:123, ogre_chief:189, drider:189, duergar_king:189, hero:92}`。
|
||||
- [ ] `build_standee48` 改紧裁不降档:内容紧裁 → 底中锚 → 原样存盘;**删除 `dom_block_resize` 调用路径**(函数与 CLI 旗标改名按档派生);主角源由 `hero-ref/edit/*_1x.png` 改指 `*_2x.png`(canvas 94×112/96×112,紧裁后 88×92/92×92/88×92)。
|
||||
- [ ] **原稿输入面显式定径**:`python tools/pixelize_candidates.py D:/code/g-plan2/artwork/remote-gen-2026-09-26 --standee-out game/assets/art/character`——worktree 的 `artwork/` 不含原稿(`sync_worktree_assets.sh:26-34` 只同步 `game/assets/art` 与 `.import`),缺省根路径在本区必**静默空跑**(缺件逐键 `isfile` 跳过、exit 0)。
|
||||
- [ ] 产物计数自证:九件逐件尺寸 == `STANDEE_SRC_H` 表值,**计数 ≠ 9 即停(L3#2)**,禁回退用旧降采样稿顶替。
|
||||
- [ ] `tools/standee_asset_check.py` 同批改口径(素材与闸件同批,禁中间态报红):import 改 `STANDEE_DISPLAY_H/STANDEE_SRC_H`;断言①「源高 == 原稿表值(禁降档)」;断言②「显示宽 = round(源宽×档高/源高) ≤ 150,食人魔酋长 157 登记例外」;其余四断言(底中锚/色数≤32/alpha 二值/描边占比)不变。跑至九件全 PASS。
|
||||
- [ ] 跑 `D:\code\godot\Godot_v4.7.2-stable_win64_console.exe --headless --editor --quit --path game` 刷 `.godot` 导入缓存,确认九件 `.ctex` 在位。
|
||||
|
||||
**取用**:T0 基线表。**产出**:九件原稿分辨率素材(盘上)+ `STANDEE_DISPLAY_H`/`STANDEE_SRC_H` 两张 Python 表+新口径闸。
|
||||
|
||||
### 7.3 T2——代码显示倍率改造(估时 0.25 个工作日)
|
||||
|
||||
- [ ] `standee_motion.gd` 增常量表 `PORTRAIT_DISPLAY_H`(九键字面值钉死;**禁 `.get(key, 96.0)` 类兜底字面量**——档值第二处散落即违契约 2/6)。
|
||||
- [ ] `_place_portrait` 改:`key` 主角取 `"hero"`、敌取 `_owner.enemy_id()`;查无键 → `push_error` 并按 1× 落位(不崩、不静默兜档),由 V18 键集自检在测试期报警;命中 → `s = disp_h / 源高`、`scale = Vector2(s, s)`、`w/h = 源尺寸 × s`,`position` 式沿用 F2 居中溢出机制。
|
||||
- [ ] `standee_body.gd` `portrait_probe()` 扩两读数:`display_size`(Vector2)、`display_scale`(float)。
|
||||
- [ ] `standee_present_values_test.gd` 增私有函数 `_check_display_tier()` 承载 V13–V18(断言内写死规格值;**浮点判等一律 `is_equal_approx` 或 `absf(...)<0.01`**——0.99/1.57 档回程有 ULP 级残差,裸 `==` 会假红)。
|
||||
- [ ] 注释随动:`standee_motion.gd:38-39,59,65`(旧「盒高 96=48 源整数 2×/§8.2 禁非整数缩放」已反向引用注销条款)、`battle_stage_view.gd:134`、`ui_probe.gd:140,144`、`level_map_screen.gd:176`(「定高 48px=1×」)。
|
||||
- [ ] 跑 `standee_present_values_test.gd` 单件至 exit 0。
|
||||
|
||||
**取用**:T1 素材与 Python 表。**产出**:`PORTRAIT_DISPLAY_H`、`portrait_probe()` 两读数、V13–V18 断言。
|
||||
|
||||
### 7.4 T3——样张随动、对照取证、素材落递与批末收口(估时 0.25 个工作日)
|
||||
|
||||
- [ ] `render_sample_sheet_v3.py`:站桩段按显示档取数;A4 尺寸基线冻结断言(`:1135-1139` 锁 `PORTRAIT_SRC.size==(46,48)`)与战斗语境 96×96 方框(`:1501`)**显式解冻重校到新档**,禁放宽阈值凑绿。
|
||||
- [ ] 出改前/改后并排对照图(同屏站桩区+标题屏看台+关卡地图头像三面)落 `artwork/candidates/立绘显示档批-<日期>/`,路径写进实现报告。
|
||||
- [ ] **素材落递主工作区**:合回落地后、删区前,把九件新稿拷回 `D:/code/g-plan2/game/assets/art/character/` 并逐件复核尺寸 == 表值——素材不入 git、worktree→主区无同步通道,漏这步则主区 CI 只有 V16 一处报警、敌侧无人看守。
|
||||
- [ ] 跑代码域闸全绿(§9 末注),读数与闸况写进实现报告。
|
||||
- [ ] **产出物落位卡**:`reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单:①九件清晰度含灰矮人 0.76× 判读、②同屏体量顺序、③标题屏看台与地图头像 0.52×、④酋长超宽不碰邻位、⑤主角态切换横跳读数+遗留读数义务)/`reports/iter<N>-代拍板追认-<日期>.md`/`reports/iter<N>-反哺点-<日期>.md`(§5 每条都有落点)。
|
||||
- [ ] 独立验收审计入口与报告:`reports/iter<N>-验收审计-<日期>.md`(未过不得合并)。
|
||||
|
||||
**取用**:T1 素材+T2 表与探针。**产出**:闸全绿回执+主区素材追平+三份产出物。
|
||||
|
||||
---
|
||||
|
||||
## 8. 验证闸(改动—验收对照表)
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 逐件显示档高 | `standee_motion.gd` 新增 `PORTRAIT_DISPLAY_H` + `_place_portrait` | 「九键显示档实读 = 96/144/188 逐键钉值,且源高 == 原稿表值(旧降采样稿必红)」`standee_present_values_test.gd:_check_display_tier`〔立绘显示档 V13〕 | 单元 | `StandeeBody.new("enemy")` → `set_data(enemy_id)` → `set_portrait_texture(真纹理)` → `portrait_probe()` | `display_size.y` 与 `display_scale × 源高` 双读数 ≈ 档高(`is_equal_approx`);九键逐一 | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 硬编码 ×2 退役 | `standee_motion.gd:63-65` | 「`_place_portrait` 体内引用 `PORTRAIT_DISPLAY_H` 且零 `Vector2(2.0, 2.0)`」〔V14〕 | 单元(静态判据) | `FileAccess` 读 `res://presentation/views/standee_motion.gd` | 源锚正证(读表)+反证(无字面 2× 倍率) | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 纵横倍率恒等 | 同上 | 「`scale.x == scale.y` 且 `display_size.x == round(源宽×档高/源高)`」〔V15〕 | 单元 | 同 V13 入口 | 探针 `display_scale`/`display_size` 实读 | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 主角中档 144(三态) | 素材 = 手修 2× 稿;`register_hero_state` 面 | 「主角三态纹理源高 ≥92 且显示高 = 144」〔V16〕 | 单元 | `BattleStageView` 组合根装载后 `hero_standee.portrait_probe()`,再逐态 `apply_hero_state` 复读 | 探针显示高逐态实读 | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 素材不降采样 | 9 件 = 原稿紧裁;`standee_asset_check.py` 新口径 | 「源高 == 原稿表值 +显示宽 ≤150(酋长例外)+色数 ≤32+底中锚+alpha 二值+描边占比」 | 闸(工具) | `python tools/standee_asset_check.py` | 逐件源高/显示宽读数 == 表值 | 既有:站桩呈现定值与素材绑定 |
|
||||
| 两表同值不漂移 | Python `STANDEE_DISPLAY_H` ↔ GDScript `PORTRAIT_DISPLAY_H` | 「读 `../tools/pixelize_candidates.py` 源文本逐键对账」〔V17〕 | 单元(静态判据) | `FileAccess` 读仓库内 `tools/` 件(双审对此点结论相左,见复核报告 S9) | 两表逐键相等;**读不到文件 → L2 记录、退回双端字面钉值+独立审计对账,禁当红亦禁当绿** | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 显示档表键集完整 | 同上 | 「表键集 ⊇ 八敌 ∪ hero 且无多余键」〔V18〕 | 单元 | 读 `PORTRAIT_DISPLAY_H` | 键集对账(防新敌种漏登记被 `push_error` 静默) | 新增:站桩显示档与素材分辨率绑定 |
|
||||
| 样张口径随动 | `render_sample_sheet_v3.py` 站桩段 | 样张脚本自跑断言(脚本内 CHECKS 全绿)+改前改后并排图 | 工具 | `python tools/render_sample_sheet_v3.py` | 脚本内站桩档高读数 | 既有:站桩呈现定值与素材绑定 |
|
||||
| 实机观感 | 9 件显示档 + 两消费面 | 用户目检:①9 件清晰度可接受;②同屏体量顺序小<中<大;③标题屏看台与关卡地图头像无可感劣化;④酋长超宽不碰邻位 | 目检(登记不阻塞) | 实机战斗屏/标题屏/地图屏 | 目检总清单落实现报告 | 新增:站桩显示档与素材分辨率绑定 |
|
||||
|
||||
**测试削弱申报**:本批不删不改任何既有断言;`ui_probe` 只改文案(判据 `has_tex` 不变);样张脚本改取数方式,其机检项断言不减。
|
||||
**判据口径**:全批无变异类/撤销类取证;每行从真实入口进入、绑被测改动特有的可观测读数。
|
||||
**代码域闸命令(里程碑步出口跑一次全套,波内修复只做出红件最窄确认)**:`bash tools/run_all_gates.sh --code`。
|
||||
|
||||
---
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1 自救**:`.import`/`.ctex` 缺位 → 重跑 `--editor --quit` 刷缓存;对照图目录不存在 → 建目录。
|
||||
- **L2 记录继续**:样张脚本整屏读数偏移但四项机检仍绿 → 读数写实现报告,不返工;V17 读 `../tools/` 路径不通 → 记录后退回「双端字面钉值+独立审计对账」,禁判红。
|
||||
- **L3 停机写报告等用户**:
|
||||
1. V13–V16 任一断言在真实跑批中做不出(判据绑不上)→ 停卡呈报,禁放宽阈值或换下层直调;
|
||||
2. 原稿缺件(任一 `pixel-1`/手修 2× 稿不在盘)→ 停机呈报,**禁回退用旧降采样稿顶替**;
|
||||
3. 食人魔酋长实机碰邻位/压到顶部轮次指示 → 停机呈报(布局重排另批);
|
||||
4. 机检预算互斥=上游缺陷停卡退回写卡段(新增件契约与闸键冲突时禁自造第三条路)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. `portrait_probe()` 新读数键名(`display_size`/`display_scale` 之外的等价命名)。
|
||||
2. `pixelize_candidates.py` 函数与 CLI 旗标改名措辞(`--standee48` → 按档派生类命名)。
|
||||
3. 测试尾缀用词〔立绘显示档 V13〕的括注格式。
|
||||
4. 对照图目录名与文件名(保持 `artwork/candidates/立绘显示档批-<日期>/` 形态)。
|
||||
5. 主角取 key 的实现写法(`_owner.enemy_id()` 空值判 hero,或经 kind 判定),只要最终表键集为九键。
|
||||
|
||||
---
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门 = U0。** 三档绝对高、逐件归档、放大与超宽两项已接受,均由用户在写卡前的设计会话中裁定(凭据见 §2 与待办清单本条实况)。
|
||||
|
||||
---
|
||||
|
||||
## 12. 落位与可复用性预检
|
||||
|
||||
| 新增件 | 复用面(盘上是否已有同职责件) | 唯一面 | 分层与交互面 |
|
||||
|---|---|---|---|
|
||||
| `PORTRAIT_DISPLAY_H`(`standee_motion.gd` const) | 无同类——现行倍率是 `_place_portrait` 内字面量(F1),本表是把它从「隐式 ×2」变「显式逐件档高」;不新增文件 | 显示档在 GDScript 侧只落这一张表;绘制点只此一处读表 | 呈现层内部件,不跨层;取 key 走宿主 `StandeeBody.enemy_id()` 既有出口,禁 Core 依赖 |
|
||||
| `portrait_probe()` 两读数 | 复用既有探针出口(`standee_body.gd:283`),不新建探针件 | 显示尺寸读数唯一出口 | 测试侧只读,不反向驱动绘制 |
|
||||
| 测试 V13–V16 | **复用既有套件** `standee_present_values_test.gd`(能力=站桩呈现定值与素材绑定,先例=迭代 46 卡B 在同文件加 V10–V12)——不新增测试文件 | 一套件一能力域 | headless 无渲染面,像素采样以探针几何+源锚承载(沿该套件既有口径) |
|
||||
| `STANDEE_DISPLAY_H`/`STANDEE_SRC_H`(Python) | 改名/扩表,不新增脚本;`dom_block_resize` 保留为管线其他用途件(禁删函数本体) | Python 侧显示档只此一处,`standee_asset_check.py` 经 import 读(沿 F10 现例) | 工具侧,不进游戏运行时 |
|
||||
| 9 件素材 | 复用原稿(F8),零新增绘制 | 一素材一路径,键名不变(F5 常量单点不动) | 素材不入 git(契约 7) |
|
||||
|
||||
---
|
||||
|
||||
## 13. 禁为机检文本保留无效成员
|
||||
|
||||
本批为呈现改造,仍立条款:若 `standee_asset_check.py` 或测试的消息文本要求保留旧键名(`STANDEE_TIER_H`/`STANDEE_SLOT_H`)凑计数或凑正则,一律停卡呈报;闸键与卡片契约互斥时禁自造第三条路,禁为过闸保留无消费方的别名。
|
||||
|
||||
---
|
||||
|
||||
## 14. 实现规范预算
|
||||
|
||||
- **新件三查**:本批**无新增 .gd 文件**(§12 复用面已定),三查不适用;新增常量表 1 个、探针读数 2 个,不触公开方法/var 阈值。
|
||||
- **扫描面事实(双审核正)**:gdstyle 只扫 `game/core|data|presentation`(`tools/gdstyle_gate.py:49`),**tests 不在面**,`tools/gdstyle_baseline.txt` 内亦无该测试键——v1.0 所写「核该测试键登记值」属空操作,注销。
|
||||
- **存量触面一查(S13 键只降不升,适用在面件)**:`standee_motion.gd`(在面)——只增一个常量表并改 `_place_portrait` 换算式,branches 不变、不产新键;`standee_body.gd:portrait_probe()`(在面)——返回字典扩两键,branches 不变。
|
||||
- **测试侧纪律**:V13–V18 提私有函数 `_check_display_tier()` 承载(拆分不改语义,非凑机检);新写测试码按 test-conventions §7 带全类型标注。
|
||||
- **指标例外申报**:无。
|
||||
|
||||
---
|
||||
|
||||
## 15. 估时与切分预案
|
||||
|
||||
≈0.85 个工作日(T0 0.1/T1 0.25/T2 0.25/T3 0.25)。
|
||||
|
||||
切分预案:素材与闸件已在 T1 同批改口径(不留中间态报红窗口);若 T3 三面对照图+主区素材追平压超软盒,切分点=把 T3 的「样张随动」独立成卡②,两卡同迭代合回,不拆迭代;若实机判酋长超宽碰邻位或灰矮人 0.76× 不可受,本卡止于 L3 停机/回设计段裁档,另立新条。
|
||||
+359
-208
@@ -1,308 +1,459 @@
|
||||
# 需求卡片:营地场景图新建批(交互式营地 · 七物件绑定 · 四柜台拆解)· 营地交互重做卡④
|
||||
|
||||
- 版本:**v2.3**(2026-10-21 用户令:**退回美术设计段**——美术轨凭据缺(P1–P5 未销号),按 req-card 第〇步不具写卡资格;**本卡非有效卡**,内容保留作美术设计段输入(Q4/Q5/Q6/Q8 已裁记录+七物件绑定表+七物件绑定现状定性),美术设计闭合(样张过审+art-bible 条款版本)后**重立卡**,再谈编迭代。🔴 归属纪律:卡只登记缺口与已有裁断,前置由谁解决/何时解决/设计稿与素材由谁产出**由用户定**(美术/设计线=用户自立批,铁律 2),Agent 不代跑不代产)
|
||||
- 来源:[需求澄清-营地交互重做-2026-10-18.md](需求澄清-营地交互重做-2026-10-18.md)(唯一事实源:①目标体验原话、②参照与反参照、③非目标、④目标结构树+纵深稿、⑤裁决 5/6/7/8/10、⑥验收方式、⑦切批原则④、⑧遗留)+ [camp.md](../design/gdd/ux/camp.md) v1.5 + [art-bible](../design/art/art-bible.md) §6.2/§7.5
|
||||
- 版本:**v3.2(现行有效卡)**。留痕:2026-10-21 用户令退回美术设计段待闭合;2026-10-31 用户令重立——美术设计已闭合(样张过审+art-bible 条款版本),据 req-card 第〇步重立为现行有效卡,零设计待裁点;2026-11-01 grilling+godot-master 双审后用户令修订(v3.1)——§0.3 四道代码段开工门裁断销号、§2 换新盘面、Godot 风险项落卡;2026-11-01 用户裁机检预算互斥停机呈报(v3.2)——营地六句柄改落 `ui_camp_modals.gd` 子容器(卡⑥ 前置建件),`ui_modals.gd` 类变量 14→14 零基线,存量触面一查补列 ui_modals/ui_registry/main_ui_build 三件,全量分域分家登记待办清单另立批。
|
||||
- 来源:[需求澄清-营地交互重做-2026-10-18.md](需求澄清-营地交互重做-2026-10-18.md)(目标体验原话/目标结构树+纵深稿/裁决 5·6·7·8·10/验收方式/切批原则④)+ 样张过审(2026-10-31 用户裁定,清单 [artwork/candidates/_终稿裁定-2026-10-31.txt](../artwork/candidates/_终稿裁定-2026-10-31.txt):各素材第一张样本稿为终稿、保留全部待选稿)+ [art-bible.md](../design/art/art-bible.md) v3.31 §6.2(营地场景 10 层口径)(**唯一事实源**:样张过审 2026-10-31 + art-bible.md v3.31 §6.2;配套 [camp.md](../design/gdd/ux/camp.md) v2.2/[camp-scene-prompts.md](../design/art/camp-scene-prompts.md) v1.2/[asset-path-map.md](../design/art/asset-path-map.md)/[art-assets-checklist.md](../design/art/art-assets-checklist.md) §3.5 同步;需求澄清为目标体验事实源)
|
||||
- 归属:呈现层+美术线(`game/presentation/` + 场景素材)——营地交互重做五卡之四,**最大件**
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:**前置条件销号后进代码实现**(设计前置闸纪律不变:设计定稿前禁 `/iter` 开本卡代码段——美术素材线按铁律 2 走自立批并行推进,不占迭代位);前置解决清单见下方
|
||||
- 开工门:**零设计待裁点,开工门=U0**;编入迭代由 iter-plan 段发令,代码段按 `/iter` 开工,代码段开工门见 §0(不阻塞本卡作为有效卡与编迭代)
|
||||
|
||||
## 🔧 状态:已退回美术设计段 · 非有效卡(2026-10-21 用户令;美术设计闭合后重立卡,再谈编迭代)
|
||||
## 🔧 状态:现行有效卡(v3.1,2026-11-01 双审修订)
|
||||
|
||||
**背景**:本卡是新内容+新美术件需求,按 2026-10-10 用户立规(req-card 设计前置检查),设计定稿前不进代码实现。v1.x 曾用「待裁点」覆盖设计缺项(渠道/规格/设计稿/参照物),违立规;v2.0 一度按「回设计段」移出序列;v2.1 一度把「批内解决」执行成「Agent 代跑设计稿」——**v2.2 用户令终订:本卡是批内成员,前置条件在批内登记、逐项销号;但由谁解决、何时解决由用户定,美术/设计线是用户自立批(铁律 2),Agent 不代跑不代产**。设计前置闸纪律不变(设计定稿前禁 `/iter` 开本卡代码段),执行方式是「批内销号」而非剔除,也非 Agent 代劳。
|
||||
本卡设计线已闭合,凭据见 §0「重立凭据」:样张过审(2026-10-31)+ art-bible v3.31 §6.2 条款版本齐备。2026-11-01 grilling+godot-master 双审通过、§0.3 四道代码段开工门全部裁断销号(用户照双审推荐采纳),设计待裁点与开工门清零;已编入迭代 47(roadmap.md 迭代总表,与卡⑤ 同波,卡⑤ 随本卡代码波之后)。属呈现层+美术线,五卡之四。
|
||||
|
||||
**前置条件销号清单**(五项重启条件转为批内登记项):
|
||||
**归属纪律**:本卡只登记代码段开工门与已裁断。前置由谁解决、何时解决、设计稿与素材由谁产出由用户定——美术/设计线=用户自立批(铁律 2),Agent 不代跑、不代产、不自行开工设计稿。素材终稿落 `game/assets/art/` 属实现段 T1/`asset-ingest` 第 3 步,不属写卡门(用户 2026-10-31 令:只生成素材、不做后处理;2026-11-01 用户裁:后处理授权随 /iter 开工令一并生效,见 §0.3-4)。
|
||||
|
||||
| # | 前置条件 | 归属 | 状态(2026-10-18) | 解决路径 |
|
||||
| -- | ------------------------- | -------------------------- | -------------- | ----------------------------------------------------------------------------------- |
|
||||
| P1 | Q1 美术渠道裁定(A/B) | **用户**(一句话裁) | ⬜ 待裁 | 阻塞场景方向稿与规格路径分支;裁后由用户定推进方式 |
|
||||
| P2 | 像素规格有着落 | 用户线(裁断已在卡:Q6 甲) | 🟨 半消 | Q6 甲已裁:Q1 裁 A 时用过渡规格(1280 工程档+烛光黄 #F6C667/木色 #8A5A34);终档归样张批冻结清单后置换(反哺点登记)——执行由用户线定 |
|
||||
| P3 | 点选反馈设计稿(悬停高亮/提示文案/可点区域判定) | **用户线**(美术/设计归用户自立批——铁律 2) | ⬜ 未启动 | 澄清⑧3 已登记缺口;产出方式有备(Q8 甲=出稿呈裁)但**未受命开工**——2026-10-18 曾越权起草 v0.1,已按用户令撤回;待用户方向 |
|
||||
| P4 | 参照物 | **用户**后续找 | ⬜ 待补 | 找到后与纵深稿对表;结构性冲突=呈报订正构图,禁批内私改 |
|
||||
| P5 | GDD/art-bible 营地条款落文 | 文档批(P1–P4 定稿后,用户发令) | ⬜ 未启动 | camp.md 整体改版+art-bible §6.2/§7.5/§2.8 改版 |
|
||||
**批内批次次序(铁律 2:美术素材走自立批、不占迭代位;先产图后改代码)**:美术设计已闭合(样张过审)→ 场景素材后处理与入库(自立批,产物 `artwork/`→`game/assets/art/scene/`)→ 本卡代码段 `/iter`(与卡⑤ 同波)。工程面 ①②③ 代码已随迭代 33 合回(卡①②③ 已按吸收即死退役);④⑤ 未编排,编入哪一迭代由用户经迭代编排段发令。
|
||||
|
||||
**批内批次次序(铁律 2:美术素材走自立批、不占迭代位;先产图后改代码)**:P1–P4 销号(设计定稿)→ 场景图美术产出(自立批,素材线并行推进,产物归档 artwork/→game/assets/art/)→ P5 文档落文 → 本卡代码段 `/iter`(与 ⑤ 同波)。工程面 ①②③ 代码在美术线推进期间并行开发、先行交付(③ 进档落点按 Q3 裁定乙暂行接现役 CampOverlay,本卡落地时改挂)——**四卡同在批内,分波收口,不是剔除**。
|
||||
## 0. 前置依赖与代码段开工门
|
||||
|
||||
**归属纪律(2026-10-18 用户令订正,v2.2)**:本卡只负责**登记前置缺口与已有裁断**;前置条件由谁来解决、何时解决、设计稿/素材由谁产出,**由用户定**——美术/设计线是用户自立批(铁律 2),Agent 不代跑、不代产、不自行开工设计稿。本卡曾越权起草点选反馈设计稿 v0.1(2026-10-18),已按用户令撤回删除;Q8(出稿呈裁)仅是「若用户发令出稿时的产出方式」预裁,非开工授权。
|
||||
### 0.1 重立凭据(已齐,非待裁点)
|
||||
|
||||
**已裁事项(2026-10-18 grilling 轮次)**:
|
||||
req-card 第〇步+卡头 2026-10-21 退回令口径=美术设计闭合(样张过审+art-bible 条款版本)。两样已齐:
|
||||
|
||||
- Q4=甲:最近冒险条带随四柜台拆解撤下,生涯.md P6 同批注销(代拍板留痕);
|
||||
- Q5=甲:ConfirmDialog 扩第三钮(继续上一段冒险/开始新冒险/取消,cancelled 恒=无变更退回营地);
|
||||
- Q6=甲:Q1 裁 A 时过渡尺寸档=现役 1280 工程档+烛光黄 #F6C667/木色 #8A5A34 既有规格,终档归样张批冻结后置换(反哺点登记);
|
||||
- Q8=甲:设计稿产出方式=批内出稿呈裁(方向稿先行纪律照旧——2~3 张低成本方向稿供选划,判完方向才精制);
|
||||
- Q3 设计稿只覆盖根态点选反馈;进界面后的叠层规则随承载形态裁定派生(独立全屏页=场景根隐藏/单一内页切换=场景根留底)。
|
||||
- **样张过审**=用户 2026-10-31 裁定「保留全部待选稿、各素材第一张样本稿为终稿、每个都还可以接受」(清单 `artwork/candidates/_终稿裁定-2026-10-31.txt`),用户准备在游戏内做最终审核。
|
||||
- **art-bible 条款版本**=`art-bible.md v3.31 §6.2`(营地场景 10 层口径;camp.md v2.2/camp-scene-prompts.md v1.2/asset-path-map.md/art-assets-checklist.md §3.5/评估报告同步)。
|
||||
|
||||
**仍待用户裁**:Q1 美术渠道 A/B(我的推荐:**A · CC0 改色**——06:23 已有配色规格、成本低、方向稿不受「从零构图」拖累;B 依赖规格清单输入且周期长。你说过不替你选,但我把推荐摊开了,等你一句 A 或 B,方向稿当天就能出);Q2 参照物(你后续找)。
|
||||
两样齐 ⇒ 重立卡判据齐,本卡为现行有效卡。P3/P5 等未销号项属**代码段开工门**,不阻塞重立卡。
|
||||
|
||||
## 0. 前置依赖与并行性(批内视角,v2.1)
|
||||
### 0.2 P1–P5 前置现状(已裁/已补项写进卡,不再当待裁点)
|
||||
|
||||
> 本卡在批内,前置条件解决中(见卡头销号清单)——本节描述批内并行关系,非排期承诺。
|
||||
| # | 前置条件 | 归属 | 现状 | 落点 |
|
||||
| -- | ------- | ---- | ---- | ---- |
|
||||
| P1 | Q1 美术渠道裁定(A/B) | 用户 | **已裁=B·插画级新产**(2026-10-22);✦ 渠道作者自跑远程生图管线,规格沿用 art-bible §6.2/§8.1/§8.2 既有裁决 | art-assets-checklist.md §3.5 |
|
||||
| P2 | 像素规格 | 用户线 | **Q6 甲过渡口径已裁**:现役 1280 工程档(1280×800)+烛光黄 #F6C667/木色 #8A5A34;2× 源档出图、最近邻降采样;终档归样张批冻结清单后置换(反哺点登记),执行归用户线 | 契约 8 |
|
||||
| P3 | 点选反馈设计稿(悬停高亮/提示文案/可点区域判定) | 用户线 | **已销号(2026-11-01 用户裁)**:销号口径=camp.md v2.2:35-37 四态规格即点选反馈设计稿(四态参数已钉死);剩余实质=七物件名牌文案与可点区域视觉,归 T2 域内(名牌文案按 §1 绑定表界面名逐字落、可点区域判定归 §9 白名单 3) | §0.3 |
|
||||
| P4 | 参照物 | 用户 | **已补**(2026-10-23)=`artwork/2026-10-23-营地场景/Gemini_Generated_Image_ykrc0nykrc0nykrc.jpg`,已进 camp-scene-prompts.md §1 登记为「作者提供的参照物」,与纵深稿对过表、围墙结构冲突已呈报;参照物只是参考图,保留 `artwork/`、不入 git、不入游戏目录 | camp-scene-prompts.md §1 |
|
||||
| P5 | GDD/art-bible 营地条款落文 | 文档批(用户发令) | **已销号(2026-11-01 用户裁)**:销号口径=形态落文完成(camp.md v2.2/art-bible v3.31 §6.2 已在盘);实装状态回填与 §7.5/§2.8 同步归 T6 反哺点义务(§5),非停机条件,实现批禁写 `design/` | §5 反哺点 |
|
||||
|
||||
- 卡③(选档界面)Q3 已裁乙:随①②③工程波先行时进档/新档暂行接现役 CampOverlay——**本卡代码波落地后,营地入口从现役 CampOverlay 改挂场景图,现役 CampOverlay 随之拆解退役**(卡③暂行接线与本次改挂的二次验证面在卡③ T2/实现报告登记)。
|
||||
- 卡⑤(ESC 浮窗与营地顶栏)落在本批新建的场景屏顶栏位——两卡触碰同一新屏,建议同批或卡⑤随后。**跨卡接口(v1.1 补全)**:①场景屏顶栏位(T2 预留);②**营地刷新单口**(T3 定义:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成)——卡⑤ HUD 刷新挂该单口,非已退役的 `refresh_camp`;③ConfirmDialog 三钮化与消费方编排(T4,卡③ 契约 3 同款互指);④子界面返回钮承诺(T3)。
|
||||
- 美术线与代码线的批次关系:T1(素材产出)与 T2–T4(代码)可分批——素材先归档齐、代码后挂(用户批次归属裁,本卡不预占)。
|
||||
- 样张线关系:**营地屏的整屏样张是 art-bible §1.4 四屏之一,现列 06「待设计」排队行**(样张批 Q2 甲案:本批只判战斗屏,余三屏排队)——本卡**不跑整屏样张闸、不替代样张批**,只按既有约束(§3 契约 7/9)生产与自检;营地屏样张义务留原排队行(反哺点注记)。文档既定序列(排队行措辞「终稿后再出样」)本就终稿先于营地样张,故 T1 终稿落盘不构成违背 art-bible §7.5「前置」条(§5 反哺点已注)。
|
||||
### 0.3 代码段 `/iter` 开工门(不是设计待裁点,不阻塞重立卡与编迭代)
|
||||
|
||||
下列项属代码段开工门;未销号即需开工对应任务段=§8 L3 停机呈报,禁批内自补、禁 T0 现场发挥:
|
||||
|
||||
1. **P3 点选反馈设计稿——已销号(2026-11-01 用户裁)**:销号口径=camp.md v2.2:35-37 四态规格即点选反馈设计稿(悬停/按下/禁用/常态参数已钉死);剩余实质=七物件名牌文案与可点区域视觉,归 T2 域内(名牌文案按 §1 绑定表界面名逐字落,可点区域判定归 §9 白名单 3)。
|
||||
2. **P5 GDD/art-bible 条款落文——已销号(2026-11-01 用户裁)**:销号口径=形态落文完成(camp.md v2.2/art-bible v3.31 §6.2 已在盘);实装状态回填与 §7.5/§2.8 同步归 T6 反哺点义务(§5),非停机条件。
|
||||
3. **camp-scene-prompts.md §5 五裁点——已裁断(2026-11-01 用户裁,照双审推荐采纳)**:①帐篷 300×170 追认现值 ②门扇 240×220 追认现值、坐标按门洞 x520–760 对齐锚点 ③布告板 (845,268,130,140) 挂围墙带(y330 悬空案弃)④描边颜色=奶油色追认(去底技术前提随现值锁定,改深灰褐须换去底方案全批重出、禁)⑤营地环境子板色值**不前置**——T1 量化实际用色后按 art-bible §4.1 两道门(语义检查+RGB 三通道差)登记;未登记前 T5「截屏级 48 色计数是否新建」(§9-4)为条件步:色值已登记→拍板,未登记→该子步显式跳过+T6 登记遗留(不阻塞批末闸,素材级二器 palette_check/standee_asset_check 已覆盖素材级执法)。camp-scene-prompts.md §5 节改已裁+art-bible :398 对应三处值落文归文档批(§5 反哺点表已列)。
|
||||
4. **十张终稿后处理与入库——发令形态已裁(2026-11-01 用户裁)**:**/iter 开工令即含 T1 后处理授权**(去底转透明〔bg 不去底、全幅不透明〕/擦通道水印/降采样/裁围墙细条/色板量化),不再两次发令;逐张画布档→目标矩形换算式登记见 §6 T1(1024×1024 档无整数倍降采样,禁现场默认)。
|
||||
|
||||
### 0.4 10 层拆分(现行口径,覆盖旧 8/9 张)
|
||||
|
||||
| z | 层 | 内容 | 可点 |
|
||||
| -- | -- | ---- | ---- |
|
||||
| z0 | `scene_camp_bg` | 不透明全幅=天空+远山+整片地面+门洞内一条通向远处的小路 | 否 |
|
||||
| z1 | `scene_camp_wall` | 透明底,横贯全宽密排竖木桩墙(左右两段)+两根略高门柱;门洞 x520–760 留透明缺口透出 bg 小路;落盘按细条 (0,240,1280,70) 裁、不做全幅 | 否 |
|
||||
| z2 | `scene_camp_board` 布告板 | 挂围墙带,点进图鉴占位 | 是 |
|
||||
| z3 | `scene_camp_gate` 门扇 | 半开双扇,叠在门洞上(240×220 追认现值、2026-11-01 裁;坐标按门洞 x520–760 对齐) | 是 |
|
||||
| z4 | `scene_camp_dummy` 木桩/`scene_camp_forge` 锻炉/`scene_camp_tent` 帐篷 | 木桩、锻炉可点;帐篷承载件不可点 | 部分 |
|
||||
| z5 | `scene_camp_chest` 宝箱/`scene_camp_notebook` 笔记本 | 宝箱、帐篷旁笔记本 | 是 |
|
||||
| z6 | `scene_camp_campfire` 篝火 | 最前;命中矩形只取下半(火堆),上半火焰只展示 | 是 |
|
||||
|
||||
围墙从背景层独立成层(z1),门洞小路画在 bg 层(z0)。**中景带自画面底部往上约 2/3 处起**;近景物件可压住围墙,**唯一例外=中间门洞必须留空、不被任何物件遮挡**。坐标草案与分层验收判据见 [camp-scene-prompts.md](../design/art/camp-scene-prompts.md) §2.3/§4(10 张口径现行事实源)与 [布局草案-1280x800.html](../artwork/2026-10-23-营地场景/布局草案-1280x800.html)。
|
||||
|
||||
### 0.5 跨卡接口与并行性(批内视角,非排期承诺)
|
||||
|
||||
- 卡③(选档界面)Q3 已裁乙:随 ①②③ 工程波先行时进档/新档暂行接现役 CampOverlay——**本卡代码波落地后,营地入口从现役 CampOverlay 改挂场景图,现役 CampOverlay 随之拆解退役**(卡③ 暂行接线与本次改挂的二次验证面在卡③ T2/实现报告登记)。
|
||||
- 卡⑤(ESC 浮窗与营地顶栏)落在本批新建的场景屏顶栏位——两卡触碰同一新屏,建议同批或卡⑤ 随后。**跨卡接口**:①场景屏顶栏位(T2 预留);②**营地刷新单口**(T3 定义:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成)——卡⑤ HUD 刷新挂该单口,非已退役的 `refresh_camp`;③ConfirmDialog 三钮化与消费方编排(T4,卡③ 契约 3 同款互指);④子界面返回钮承诺(T3);⑤篝火二次确认走 ConfirmDialog 先例(camp.md v2.2 已裁必须)。
|
||||
- 样张线关系:营地屏整屏样张是 art-bible §1.4 四屏之一,现列 06「待设计」排队行——本卡不跑整屏样张闸、不替代样张批,只按既有约束(契约 7/9)生产与自检;营地屏样张义务留原排队行(反哺点注记)。文档既定序列(排队行措辞「终稿后再出样」)本就终稿先于营地样张,故 T1 终稿落盘不构成违背 art-bible §7.5「前置」条(§5 反哺点已注)。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标(澄清①一句提炼照录)**:营地首页从「文字按钮菜单 + 列表式覆盖层」改为「**一张可点击的营地场景图**」——点场景中的具体物件进入对应功能界面。用户原话照录:「我是希望有一个营地地图的游戏画面;或者是一张营地的图片,跟 UI 的交互是点击图片中的某一处地方,然后再进入界面。不是完全普通的那种 UI 设计,不是各种框框的……我点击某些具体的东西之后,就可以进入对应的操作界面。」(称呼订正:「它就是一个营地的画面;并不是一张地图。」)
|
||||
**目标(澄清①一句提炼照录)**:营地首页从「文字按钮菜单 + 列表式覆盖层」改为「**一张可点击的营地场景图**」——点场景中的具体物件进入对应功能界面。用户原话照录:「我是希望有一个营地地图的游戏画面;或者是一张营地的图片,跟 UI 的交互是点击图片中的某一处地方,然后再进入界面。不是完全普通的那种 UI 设计,不是各种框框的……我点击某些具体的东西之后,就可以进入对应的操作界面。」(称呼订正:「它就是一个营地的画面;并不是一张地图。」)
|
||||
|
||||
**七物件绑定(澄清④结构树照录,逐一落地)**:
|
||||
**七物件绑定(camp.md v2.2 §场景构成与七物件绑定,逐一落地)**:
|
||||
|
||||
| 物件 | 绑定目标 | 现状定性 |
|
||||
| ------- | -------------------------- | ------------ |
|
||||
| 营地大门 | 冒险(选路屏/战斗屏已实现,不动;断点弹窗保留移位) | 入口新建+动线迁徒 |
|
||||
| 锻炉·铁砧 | 锻造工坊 | 界面已实现 · 入口改挂 |
|
||||
| 宝箱 | 秘宝匣 | 界面已实现 · 入口改挂 |
|
||||
| 床铺旁笔记本 | 生涯记录 | 界面已实现 · 入口改挂 |
|
||||
| 木桩 | 树淬炼天赋树 | 界面已实现 · 入口改挂 |
|
||||
| 布告板/悬赏板 | 奖励格图鉴 | 界面已实现 · 入口改挂 |
|
||||
| 篝火 | 离开营地/回主菜单 | 新建小动线 |
|
||||
| 物件 | 进入 | 备注(现状定性+现行约束) |
|
||||
| ---- | ---- | ------------------------ |
|
||||
| 大门(半开门扇,门洞内一条通向远处的小路) | 冒险(选路屏/战斗屏已实现不动;断点弹窗移位) | 入口新建+动线迁移;点大门永远先出确认(有断点=三出口,无=是否开始新冒险) |
|
||||
| 锻炉·铁砧 | 锻造工坊 | 界面已实现·入口改挂;未解锁=禁用态(降明度+去饱和) |
|
||||
| 宝箱 | 秘宝匣 | 界面已实现·入口改挂;未解锁=禁用态 |
|
||||
| 帐篷旁笔记本 | 生涯记录 | 界面已实现·入口改挂;生涯页返回钮文案禁含「营地」字样 |
|
||||
| 木桩 | 树淬炼天赋树 | 界面已实现·入口改挂 |
|
||||
| 布告板/悬赏板 | 奖励格图鉴 | 界面已实现·入口改挂;内容冻结未激活,点进看占位(**不是禁用态**) |
|
||||
| 篝火 | 离开营地/回主菜单 | 新建小动线;**必须二次确认**;命中区只含火堆、不含火焰 |
|
||||
| 帐篷 | — | 承载件不可点;落地尺寸与占位范围待作者重定(代码段开工门,camp-scene-prompts §5) |
|
||||
|
||||
**纵深稿(用户原话照录)**:「远景是天空;中景是一个围栏,还有营地大门;近景中间是一个篝火;左边是锻炉铁砧;右边是我们的床铺,宝箱等。」
|
||||
**纵深稿(用户原话照录)**:「远景是天空;中景是一个围栏,还有营地大门;近景中间是一个篝火;左边是锻炉铁砧;右边是我们的床铺,宝箱等。」(承载件落地为帐篷,见 §0.4 z4。)
|
||||
|
||||
**大门两分支(裁决 5 照录)**:有未完冒险 → 弹「继续上一段冒险 / 开始新冒险」;无未完冒险 → 弹「是否开始新冒险:是/否」。**点大门永远先出确认,不直接进冒险**(「断点续玩也是开始冒险的两个分支之一……它算是开始冒险的一个子节点」)。**「有断点」分支的三出口弹窗形态待裁(Q5)**——现役 ConfirmDialog 双钮表达不了两个肯定性动作。
|
||||
**大门两分支(裁决 5 照录,Q5 已裁=甲)**:有未完冒险 → ConfirmDialog 三出口「继续上一段冒险/开始新冒险/取消」;无未完冒险 → 「是否开始新冒险:是/否」。**点大门永远先出确认,不直接进冒险**;cancelled 恒=无变更退回营地。
|
||||
|
||||
**钥石余额(裁决 10 照录)**:场景**顶部小 HUD 栏**(用户原话「先按顶部小HUD栏设计」——provisional 措辞留痕;承载落卡⑤)。
|
||||
**钥石余额(裁决 10 照录)**:场景**顶部小 HUD 栏**(高 48px 全宽窄条、浮层覆盖不挤压画面;承载落卡⑤)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
|
||||
1. 交互式营地场景图(场景素材+场景屏+七物件命中区与点选交互)——从零新建;
|
||||
2. 现四柜台覆盖层(CampOverlay)拆解:锻造工坊/秘宝匣/天赋树/奖励格图鉴四界面抽为独立可开界面,入口改挂场景物件;生涯页入口自主菜单改挂笔记本;
|
||||
3. 大门动线(开始冒险/继续上局两分支+断点确认弹窗从主菜单移位);
|
||||
4. 篝火回主菜单小动线;场景屏顶栏预留位(卡⑤ HUD);
|
||||
5. camp 截图断言段与六探针营地段的迁移(**现无 camp 逐像素 golden 基线**——v1.1 订正,见契约 10)。
|
||||
1. 交互式营地场景图(10 层场景素材+场景屏+七物件命中区与点选交互)——从零新建;
|
||||
2. 现四柜台覆盖层(CampOverlay)拆解:锻造工坊/秘宝匣/天赋树/奖励格图鉴四界面抽为独立可开界面,入口改挂场景物件;生涯页入口自主菜单改挂帐篷旁笔记本;
|
||||
3. 大门动线(开始冒险/继续上局两分支+断点确认弹窗从主菜单移位);
|
||||
4. 篝火回主菜单小动线(带二次确认);场景屏顶栏预留位(卡⑤ HUD);
|
||||
5. camp 截图断言段与六探针营地段的迁移(**现无 camp 逐像素 golden 基线**——见契约 10)。
|
||||
|
||||
**完成定义**:
|
||||
|
||||
1. 营地首页=可点击场景图;七物件绑定逐一在役,点各物件进对应界面;
|
||||
2. 四柜台拆解完毕:CampOverlay 覆盖层形态退役,五界面(锻造/秘宝/生涯/天赋树/图鉴)经新入口可达且**功能与写口链路零回归**(presenter 改造=承载面迁移的一部分——见契约 4);
|
||||
3. 大门两分支确认弹窗在役;点大门不直接进冒险;
|
||||
4. 篝火可回主菜单;场景屏顶栏预留位就绪(卡⑤联验);
|
||||
5. camp 截图=场景图形态(迭代内验收=截图对比,澄清⑥);探针断言迁移完毕;
|
||||
6. 美术约束(48 色板/禁入带/语义色/WCAG)入闸或登记缺口;**用户目检项登记 07**(生命周期末步,不阻塞);
|
||||
1. 营地首页=可点击场景图(10 层按 §0.4 铺底、门洞留空);七物件绑定逐一在役,点各物件进对应界面;
|
||||
2. 四柜台拆解完毕:CampOverlay 覆盖层形态退役,五界面(锻造/秘宝/生涯/天赋树/图鉴)经新入口可达且**功能与写口链路零回归**(presenter 改造=承载面迁移的一部分——见契约 4);
|
||||
3. 大门两分支确认弹窗在役;点大门不直接进冒险;cancelled 无变更退回;
|
||||
4. 篝火经二次确认可回主菜单;场景屏顶栏预留位就绪(卡⑤ 联验);
|
||||
5. camp 截图=场景图形态(迭代内验收=截图对比,澄清⑥);探针断言迁移完毕;
|
||||
6. 美术约束(48 色板/禁入带/语义色/WCAG)入闸或登记缺口;**用户目检项登记 07**(生命周期末步,不阻塞);
|
||||
7. 批末重闸按改动域选档全绿;三份 `reports/` 产出物写齐(T6)。
|
||||
|
||||
## 2. 现状事实(2026-10-18 实查;开工日 T0 复核行号)
|
||||
## 2. 现状事实(2026-11-01 双审全量复核换新盘面;开工日 T0 仍按「漂移即停卡核对」抽核)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| F1 | 营地场景图完全不存在(澄清④判定「完全没有 · 新建」);现营地=CampOverlay 四柜台覆盖层(树淬炼/锻造工坊/秘宝匣/奖励格图鉴并排,迭代 21 T6 程序占位重排——代码注释原文「视觉终稿归 HUD 营地视觉批」) | `需求澄清-营地交互重做-2026-10-18.md` §④;`game/presentation/views/camp_overlay_view.gd:86-87` |
|
||||
| F2 | 营地屏壳(CampScreen)持 CampOverlay 成员;组合根 `main.camp_overlay` 句柄指向屏壳成员(探针挂载假设) | `game/presentation/scenes/camp_screen.gd:8-14`;`main.gd:112` |
|
||||
| F3 | CampOverlay 结构:顶栏铭牌+钥石徽(:64-84);四柜台并排横滚(树淬炼 :99-109/锻造工坊 :111-137/秘宝匣 :139-159/奖励格图鉴 :161-172);底栏关闭钮+最近冒险条带(:174-189);钥匙门判据读树投影(:287-296);图鉴柜台=结构位预留(:168「内容排 Alpha」) | `camp_overlay_view.gd` 各处 |
|
||||
| F4 | 五目标界面现状:锻造工坊=CampOverlay 内工作间(`_forge_workbench` 两区六路线);秘宝匣=`_ws_scroll`/`_workshop_holder`(12 件+同调装配段+Y 升级行);天赋树=`_tree_holder`(脉分组行板+Fork 双态钮);图鉴=占位;生涯=**已独立全屏页**(CareerOverlay,入口现居主菜单生涯钮,自带返回钮) | `camp_overlay_view.gd:116-137`/:144-159/:99-109/:161-172;`views/career_overlay.gd`;`main.gd:332` |
|
||||
| F5 | 营地购买写口链路:视图只发意图信号(`buy_requested`/`tree_buy_requested`/`forge_*` 等),组合根经 CampShopPresenter 转 MetaProgression 写口;投影数据=`camp_shop_presenter.gd:62-72` `camp_data()` 单点 | `camp_overlay_view.gd:8-15`;`camp_shop_presenter.gd:62-72` |
|
||||
| F6 | 断点确认弹窗现居主菜单开始冒险路径(有断点先出 ConfirmDialog 再开局)——随卡②撤钮整体移交本卡大门 | `main.gd:475-497` |
|
||||
| F7 | 冒险屏已实现不动:选路屏(LevelMapScreen,`route_chosen` 前进唯一通道)与战斗屏宿住 AdventureScreen;组合根侧:`_start_adventure_from_menu`(:475-484)/`_on_confirm_new_adventure`(:487-492)/`_resume_from_menu`(:518-536)/`_start_adventure`(:579-597)/`build_adventure_params`(:564-574) | `views/level_map_screen.gd:7`;`scenes/adventure_screen.gd:36-37`;`main.gd` 各处(v1.1 行号订正) |
|
||||
| F8 | 探针面营地段(全部随本批迁移,v1.1 补列):ui_probe(营地入口+余额+秘宝匣 12 行断言 :28-55,深员 `_balance_label`/`_tree_buy_btns`/`_workshop_rows` :39-52)、ui_click_probe(营地购买全链 :53-127;**断点确认段 :500-528**〔经 lvbtn 驱动 ConfirmDialog 取消/开新冒险——被卡②撤钮+本批弹窗移位双杀,v1.1 补列〕)、screen_flow_probe(营地可见态键 :53 直读 `main.camp_overlay.visible`;营地开合 :109-125 回退段直呼 `main.camp_overlay.closed.emit()`;菜单→冒险 lvbtn 步 :130-136)、shot_probe(camp 截图+「关闭营地」钮收闭 :126-133)、ui_struct_probe(camp 结构审计 :41-49;同槽特例断言 :331/:373)、smoke_all(V2 营地屏壳实例化 :170-171;V3g 营地模态开合 :405-435;**camp_overlay_view.gd 源码断言段 :6469-6475**〔三条锁四柜台等高形态本身——`Vector2(0,336)`×3/`SIZE_FILL`×4/`SIZE_SHRINK_CENTER`≥3,随形态退役〕)、ui_contrast_probe(营地令牌段 :48-55〔UiKit 令牌恒等式,不随拆解红;新界面若不再消费 `CAMP_HEADER_BAND`/`CAMP_ROW_PLATE` 则段标签虚挂,须核签〕)、**forge_workbench_test.gd 单测**(:33 直载 camp_overlay_view.gd 并消费 `_forge_rows`/`_forge_workbench`/`open_camp`/`refresh_camp`/三 forge 信号——CampOverlay 一删即红) | `game/smoke/` 各处+`game/tests/unit/presentation/forge_workbench_test.gd:33` |
|
||||
| F9 | camp UX spec 形态记述将整体退役:三柜台列/顶栏铭牌钥石徽/底栏关闭钮+最近冒险条带(v1.5,事实源声明= camp_overlay_view.gd 实态;条带实述于 :19-20) | `design/gdd/ux/camp.md:14-20` |
|
||||
| F10 | art-bible 营地条款将失义:§6.2 营地场景「三柜台在烛光下一字排开,钥石余额常驻顶栏」;§7.5 营地界面「三柜台界面画法……视觉终稿以表现层样张过审为前置」;§2.8 承载元素同述 | `design/art/art-bible.md:366-369`/:436-438`/:136-140` |
|
||||
| F11 | 图鉴内容在册挂起:奖励格图鉴柜台已随配比预设批整体挂起冻结(2026-10-05 用户令,06 在册),重启须用户另行发令——本批只改入口不激活内容 | `design/gdd/systems/跨局循环/生涯.md:63` |
|
||||
| F12 | 贴图重构批搁置未废:营地与元进度件终稿置换(06:21-27)等交互重做定形后再议(澄清③1/§⑧5)——本批不清偿该债;四柜台拆解后原「柜台场景贴图」债对象变化,随该批重新评估 | `design/gdd/06-待办清单.md:21-27`;`需求澄清-营地交互重做-2026-10-18.md` §③1/§⑧5 |
|
||||
| F13 | 营地底栏「最近冒险」条带与生涯历史回看同源(同一份 `recent_summaries`),其去留=生涯.md P6 待裁点(期二必答)——CampOverlay 拆解后该条带无处挂,P6 被迫提前面对(本卡 Q4) | `camp_overlay_view.gd:186-189`/:536-549;`生涯.md:69`/:91-101\` |
|
||||
| F14 | 素材线纪律:`artwork/` 不入 git(`.gitignore:51` 忽略行)——批次 worktree 内无该目录、主工作区 D:/code/g-plan2 实体存在(在途工作区);涉素材脚本须传主区绝对路径否则静默 0 张;素材替换前先归档旧档;终稿归 `game/assets/art/` | `AGENTS.md:32`;`.gitignore:51`;art-bible §8.3 归档纪律(v1.1 行号与表述订正) |
|
||||
| F15 | 美术硬约束继续有效(澄清③4):明亮轻快像素风、**48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1**——全局约束,不在本讨论范围翻案 | `需求澄清-营地交互重做-2026-10-18.md` §③4 |
|
||||
| F16 | 像素级规格不在本卡硬定(澄清③5):尺寸档/色板/明度档归「战斗一屏样张批」冻结的规格清单——该批现列 06「待设计」未排期(2026-10-17 用户令自迭代 30 剔除),本卡只写指针并登记依赖(缺口连带裁=Q6) | `需求澄清-营地交互重做-2026-10-18.md` §③5;`design/gdd/06-待办清单.md`(迭代 30 剔除卡②③ 条) |
|
||||
| F17 | 机检工具面(v1.1 订正):**截屏级 48 色计数不在役**;在役=`tools/palette_check.py`(art-bible §8.3①挂点——48 设计色板令牌+新增色双门)+`tools/standee_asset_check.py`(像素画单张 32 色冻结执法);`tools/render_gate.sh`(shot_probe 七屏→shot_checker 语义断言;golden 段=棋盘三张+menu_boot 四张,**无 camp golden**——shot_checker.py:143 明载「camp/level 走骨架+目检清单」);shot_checker 段=屏级骨架/文字 WCAG/明度三档带/文字禁压纹理/文字在判据 | `tools/palette_check.py`;`tools/standee_asset_check.py`;`tools/render_gate.sh`;`tools/shot_checker.py:143` |
|
||||
| # | 事实 | 出处 |
|
||||
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| F1 | 营地场景图完全不存在(澄清④判定「完全没有 · 新建」);现营地=CampOverlay 四柜台覆盖层(树淬炼/锻造工坊/秘宝匣/奖励格图鉴并排,迭代 21 T6 程序占位重排)——原引注释「视觉终稿归 HUD 营地视觉批」已随后续重构删除(2026-11-01 复核,引文退役、事实不变) | `需求澄清-营地交互重做-2026-10-18.md` §④;`game/presentation/views/camp_overlay_view.gd` |
|
||||
| F2 | 营地屏壳(CampScreen)持 CampOverlay 成员;组合根句柄=`main.ui.modals.camp_overlay`(ui_modals.gd:14;ui_registry.gd:25 LAYER_CONTRACT 模态槽)——`main.camp_overlay` 旧寻址已退役,探针挂载随句柄迁移(F8) | `game/presentation/scenes/camp_screen.gd:8-14`;`game/presentation/ui_modals.gd:14`;`game/presentation/ui_registry.gd:25` |
|
||||
| F3 | CampOverlay 结构(结构在、行号以 2026-11-01 复核为基准):顶栏铭牌+钥石徽(:125);四柜台并排横滚(树淬炼/锻造工坊/秘宝匣/图鉴——图鉴锁文现 :207);底栏关闭钮+最近冒险条带(:214 起,`_rebuild_recent` :634);钥匙门判据读树投影 | `camp_overlay_view.gd` 各处 |
|
||||
| F4 | 五目标界面现状:锻造工坊=CampOverlay 内工作间(`_forge_workbench` 两区六路线);秘宝匣=`_ws_scroll`/`_workshop_holder`(12 件+同调装配段+Y 升级行);天赋树=`_tree_holder`(脉分组行板+Fork 双态钮);图鉴=占位;生涯=**已独立全屏页**(CareerOverlay,入口现经组合根函数直调——卡②已撤主菜单生涯钮、改函数直调中间态,shot_probe.gd:103/ui_struct_probe.gd:59「careerbtn 已撤」注;终态=本批改挂帐篷旁笔记本,自带返回钮) | `camp_overlay_view.gd` 四柜台各处;`views/career_overlay.gd` |
|
||||
| F5 | 营地购买写口链路:视图只发意图信号(`buy_requested`/`tree_buy_requested`/`forge_*` 等),组合根经 CampShopPresenter 转 MetaProgression 写口;投影数据=`camp_shop_presenter.gd:62-72` `camp_data()` 单点 | `camp_overlay_view.gd:8-15`;`camp_shop_presenter.gd:62-72` |
|
||||
| F6 | 断点确认链已随卡②摘除(main.gd:118-120 注释「弹窗本体与两分支逻辑归卡④大门承接」);main.gd:475-497 现为选档委托(旧「断点弹窗现居主菜单」引文失实,2026-11-01 复核)——断点确认弹窗本卡 T4 承接(卡②移交) | `main.gd:118-120` |
|
||||
| F7 | 冒险屏已实现不动:选路屏(LevelMapScreen,`route_chosen` 前进唯一通道)与战斗屏宿住 AdventureScreen;组合根侧(2026-11-01 复核行号):`_start_adventure_from_menu` :116/`_resume_from_menu` :150/`build_adventure_params` :183/`_start_adventure` :198——`_on_confirm_new_adventure` 已随卡②摘除(T4 重立消费方编排) | `views/level_map_screen.gd:7`;`scenes/adventure_screen.gd:36-37`;`main.gd` 各处 |
|
||||
| F8 | 探针面营地段(全部随本批迁移;宿主=`game/probe/`+`game/tests/`——`game/smoke/` 目录已于 2026-10-24 退役(git 16c6f136)、smoke_all.gd 已整体退役(git 957391e1),v3.0 的 `game/smoke/*`+smoke_all 旧盘面写法作废):ui_probe(营地入口+余额+秘宝匣 12 行断言,深员 `_balance_label`/`_tree_buy_btns`/`_workshop_rows`)、ui_click_probe(营地购买全链;**断点确认段**〔经 lvbtn 驱动 ConfirmDialog——被卡②撤钮+本批弹窗移位双杀〕)、screen_flow_probe(营地旗标 :54 直读主菜单句柄——:53 是菜单行,v3.1 订正;营地开合 :119-125 回退段;菜单→冒险 lvbtn 步)、shot_probe(camp 截图+「关闭营地」钮收闭;生涯段函数直调驱动 :103)、ui_struct_probe(camp 结构审计;同槽特例断言)、system_test.gd(V2 营地屏壳实例化 :67-68;V3g 营地模态开合 :520-541)、theme_projection_test.gd:710-717(四柜台等高源码断言段 `_smoke_element_details`——`Vector2(0,336)`×3/`SIZE_FILL`×4/`SIZE_SHRINK_CENTER`≥3,随形态退役)、ui_contrast_probe(营地令牌段〔UiKit 令牌恒等式,不随拆解红;新界面若不再消费 `CAMP_HEADER_BAND`/`CAMP_ROW_PLATE` 则段标签虚挂,须核签〕)、**forge_workbench_test.gd 单测**(:33 直载 camp_overlay_view.gd 并消费 `_forge_rows`/`_forge_workbench`/`open_camp`/`refresh_camp`/三 forge 信号——CampOverlay 一删即红) | `game/probe/` 各处+`game/tests/system_test.gd:67-68`/:520-541+`game/tests/unit/presentation/theme_projection_test.gd:710-717`+`game/tests/unit/presentation/forge_workbench_test.gd:33` |
|
||||
| F9 | camp UX spec 现行 **camp.md v2.2** 已落文可点击场景图形态(定位/场景构成与七物件绑定/10 层拆分/点选反馈四态/顶栏 48px/叠层与开合);本批实现=**对齐** v2.2,旧三柜台形态已由 v2.2 取代,本批只需回填实装状态与目检结论 | `design/gdd/ux/camp.md:10-51` |
|
||||
| F10 | art-bible 现行 **v3.31 §6.2** 已落文营地场景 10 层口径(z0 bg 不透明全幅/z1 wall 透明细条门洞缺口/八物件层/篝火命中只下半/围墙落盘 (0,240,1280,70));§7.5 营地界面已改为「可点击场景图+七物件点选」画法;§2.8 承载元素同述;本批实现=对齐现行条款 | `design/art/art-bible.md` §6.2/§7.5/§2.8 |
|
||||
| F11 | 图鉴内容在册挂起:奖励格图鉴柜台已随配比预设批整体挂起冻结(2026-10-05 用户令,06 在册),重启须用户另行发令——本批只改入口不激活内容 | `design/gdd/systems/跨局循环/生涯.md:63` |
|
||||
| F12 | 贴图重构批搁置未废:营地与元进度件终稿置换(待办清单.md 在册条)等交互重做定形后再议(澄清③1/§⑧5)——本批不清偿该债;四柜台拆解后原「柜台场景贴图」债对象变化,随该批重新评估(原 `design/gdd/06-待办清单.md` 路径已迁仓库根) | `待办清单.md`(仓库根);`需求澄清-营地交互重做-2026-10-18.md` §③1/§⑧5 |
|
||||
| F13 | 营地底栏「最近冒险」条带与生涯历史回看同源(同一份 `recent_summaries`),其去留=生涯.md P6 待裁点(期二必答)——CampOverlay 拆解后该条带无处挂,**Q4 已裁=甲**(随四柜台拆解撤下,逐局历史回看归生涯,生涯.md P6 同批注销) | `camp_overlay_view.gd`(底栏 :214 起/`_rebuild_recent` :634);`生涯.md:69` |
|
||||
| F14 | 素材线纪律:`artwork/` 不入 git(`.gitignore:54` 忽略行)——批次 worktree 内无该目录、主工作区 D:/code/g-plan2 实体存在(在途工作区);涉素材脚本须传主区绝对路径否则静默 0 张;素材替换前先归档旧档;终稿归 `game/assets/art/scene/`;**消费常量 `UiKit.TEX_CAMP_*` 尚不存在**(全仓 grep 零命中,仅 art-assets-checklist.md:135 文字提及)——本批在 `ui_kit.gd` 新增十键(T1 先定常量、素材名跟常量走,防断链;2026-11-01 裁) | `AGENTS.md:32`;`.gitignore:54`;art-bible §8.3 归档纪律;art-assets-checklist.md §3.5 |
|
||||
| F15 | 美术硬约束继续有效(澄清③4):明亮轻快像素风、**48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1**——全局约束,不在本讨论范围翻案 | `需求澄清-营地交互重做-2026-10-18.md` §③4 |
|
||||
| F16 | 像素级规格已定(Q6 甲过渡口径+渠道 B):1280×800 工程档、烛光黄 #F6C667/木色 #8A5A34、2× 源档出图最近邻降采样(art-assets-checklist.md §3.5);终档归样张批冻结清单后置换(反哺点);卡内禁自定像素规格数值 | `design/art/art-assets-checklist.md` §3.5;`design/art/art-bible.md` §6.2 |
|
||||
| F17 | 机检工具面:**截屏级 48 色计数不在役**;在役=`tools/palette_check.py`(art-bible §8.3①挂点——48 设计色板令牌+新增色双门)+`tools/standee_asset_check.py`(像素画单张 32 色冻结执法);`tools/render_gate.sh`(shot_probe 七屏→shot_checker 语义断言;golden 段=棋盘三张+menu_boot 四张,**无 camp golden**——shot_checker.py:143 明载「camp/level 走骨架+目检清单」);shot_checker 段=屏级骨架/文字 WCAG/明度三档带/文字禁压纹理/文字在判据 | `tools/palette_check.py`;`tools/standee_asset_check.py`;`tools/render_gate.sh`;`tools/shot_checker.py:143` |
|
||||
| F18 | 参照物已补(2026-10-23)=`artwork/2026-10-23-营地场景/Gemini_Generated_Image_ykrc0nykrc0nykrc.jpg`,已进 camp-scene-prompts.md §1 风格参考图表登记为「作者提供的参照物」,与纵深稿对过表、围墙结构冲突已呈报;参照物只是参考图,保留 `artwork/`、不入 git、不入游戏目录 | `design/art/camp-scene-prompts.md` §1;`artwork/2026-10-23-营地场景/` |
|
||||
| F19 | 10 层拆分现行口径(覆盖旧 8/9 张):art-bible v3.31 §6.2/camp.md v2.2/camp-scene-prompts.md v1.2/asset-path-map.md §3/art-assets-checklist.md §3.5 同步;围墙从背景层独立成 z1、门洞小路画在 z0 bg 层;围墙落盘细条 (0,240,1280,70) 不做全幅 | `design/art/art-bible.md` §6.2;`design/gdd/ux/camp.md:27-31` |
|
||||
| F20 | 样张过审与终稿裁定(2026-10-31 用户裁定):保留全部待选稿、各素材第一张样本稿为终稿、「目前我看每个都还可以接受」,用户准备在游戏内做最终审核;终稿此刻=原始候选图本身,未做去底/擦水印/降采样/裁细条等任何后处理(用户 2026-10-31 令只生成素材不做后续工序);清单=`artwork/candidates/_终稿裁定-2026-10-31.txt` | `artwork/candidates/_终稿裁定-2026-10-31.txt` |
|
||||
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 交互式场景图形态**:营地首页=一张可点击的营地场景图,点物件进界面(澄清①)。**禁退回文字按钮列表+面板覆盖层形态**(澄清②反参照:「完全普通的那种 UI 设计」「各种框框」——用户判现状「交互流程完全错误」)。
|
||||
🔴 **契约 1 · 交互式场景图形态**:营地首页=一张可点击的营地场景图,点物件进界面(澄清①)。**禁退回文字按钮列表+面板覆盖层形态**(澄清②反参照:「完全普通的那种 UI 设计」「各种框框」——用户判现状「交互流程完全错误」)。
|
||||
|
||||
🔴 **契约 2 · 七物件绑定逐一落地**:大门/锻炉铁砧/宝箱/笔记本/木桩/布告板/篝火七个绑定(§1 表)缺一不可;禁合并、禁以「其他入口」替代(如顶栏文字钮)。
|
||||
🔴 **契约 2 · 七物件绑定逐一落地**:大门/锻炉铁砧/宝箱/帐篷旁笔记本/木桩/布告板/篝火七个绑定(§1 表)缺一不可;禁合并、禁以「其他入口」替代(如顶栏文字钮)。帐篷为承载件、不可点。
|
||||
|
||||
🔴 **契约 3 · 大门两分支**(裁决 5):有未完冒险→「继续上一段冒险/开始新冒险」二选一弹窗(**三出口形态=Q5 待裁**);无→「是否开始新冒险:是/否」弹窗。**点大门永远先出确认,不直接进冒险**;弹窗类=ConfirmDialog(三钮化+消费方编排=T4,卡③ 契约 3 同款互指)。
|
||||
🔴 **契约 3 · 大门两分支**(裁决 5,Q5 已裁=甲):有未完冒险→ConfirmDialog 三出口「继续上一段冒险/开始新冒险/取消」;无→「是否开始新冒险:是/否」弹窗。**点大门永远先出确认,不直接进冒险**;cancelled 恒=无变更退回营地;弹窗类=ConfirmDialog(三钮化+消费方编排=T4,卡③ 契约 3 同款互指)。
|
||||
|
||||
🔴 **契约 4 · 已实现界面只改挂不重做+presenter 承接面(v1.1 精确化)**:锻造工坊/秘宝匣/天赋树/图鉴/生涯五界面「界面已实现 · 入口改挂」——功能、行板、写口链路零重设计(拆解=承载面迁移)。**presenter 改造=承载面迁移的一部分**:`camp_shop_presenter.gd` 持 `camp_overlay` 句柄(:10)、七条写口尾全部 `camp_overlay.refresh_camp(camp_data())`(:85/93/105/117/129/142/154/168)、`CampFlowContext` 持类型化字段(camp_flow_context.gd:10/22/29)、open/close 编排(:41-56)——**写口语义零改≠presenter 文件零改**:投影目标改线(五界面/场景根)、写口尾刷新改路由、open/close 编排随入口从 campbtn 迁位到卡③进档动作、ctx 字段语义订正、探针缝保缝(main.gd:1163-1171)。改造纪律:`on_camp_attune_toggle`(:98-106)尾只 refresh_camp、不调 `_refresh_presentations`(与其余六口不一致)——**保持原样,禁「顺手统一」防行为漂移**。
|
||||
🔴 **契约 4 · 已实现界面只改挂不重做+presenter 承接面**:锻造工坊/秘宝匣/天赋树/图鉴/生涯五界面「界面已实现 · 入口改挂」——功能、行板、写口链路零重设计(拆解=承载面迁移)。**presenter 改造=承载面迁移的一部分**:`camp_shop_presenter.gd` 持 `camp_overlay` 句柄(:10)、写口尾刷新调用八处(七口+`on_camp_buy` 双尾)全部 `camp_overlay.refresh_camp(camp_data())`(:82/90/102/114/126/139/151/165,2026-11-01 复核实数)、`CampFlowContext` 持类型化字段(camp_flow_context.gd:10/22/29)、open/close 编排(:41-56)——**写口语义零改≠presenter 文件零改**:投影目标改线(五界面/场景根)、写口尾刷新改路由、open/close 编排随入口从 campbtn 迁位到卡③ 进档动作、ctx 字段语义订正、探针缝保位(组合根薄委托缝,T0 复核现役行号——原引 main.gd:1163-1171 超出文件实长、系旧盘面残留)。改造纪律:`on_camp_attune_toggle` 口尾(:102)只 refresh_camp、不调 `_refresh_presentations`(与其余口不一致)——**保持原样,禁「顺手统一」防行为漂移**。
|
||||
|
||||
🔴 **契约 5 · 图鉴内容保持挂起**:只改入口(布告板),不激活图鉴内容(F11:2026-10-05 用户令冻结,重启须用户发令);布告板点开的图鉴=现行占位态(T3 定义占位壳)。
|
||||
🔴 **契约 5 · 图鉴内容保持挂起**:只改入口(布告板),不激活图鉴内容(F11:2026-10-05 用户令冻结,重启须用户发令);布告板点开的图鉴=现行占位态(T3 定义占位壳),不是禁用态(camp.md v2.2)。
|
||||
|
||||
🔴 **契约 6 · 冒险进行中不开营地**(澄清③6):营地是存档语境(局外)枢纽,现状口径延续;局内只读面=状态面板(在役,本批零改动——其「营地三柜台进度」文案若因拆解失义,列反哺点归文档批订正)。
|
||||
|
||||
🔴 **契约 6 · 冒险进行中不开营地**(澄清③6):营地是存档语境(局外)枢纽,现状口径延续;局内只读面=状态面板(在役,本批零改动——其「营地三柜台进度」文案若因拆解失义,列反哺点归文档批订正)。
|
||||
🔴 **契约 7 · 美术硬约束+10 层红线**:48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1(F15);场景素材全局纹理过滤=Nearest(project.godot 现行默认,像素纪律)。**10 层拆分按 §0.4**:z0 bg 不透明全幅(含门洞小路)、z1 wall 透明细条 (0,240,1280,70) 门洞 x520–760 留透明缺口、z2–z6 八物件层;围墙禁并回背景层、门洞禁填死、门洞禁被任何物件遮挡。点选反馈四态(悬停=轮廓亮边+浮动〔整像素吸附〕+名牌〔物件下方 8px 居中、字号 14、对比 ≥4.5:1〕/按下=位移 1px/禁用=降明度+去饱和〔仅锻炉、宝箱〕/已解锁=常态)零新素材、全程序化;命中区两两不相交、不越画面、不侵入顶栏禁区 y≤48。篝火命中矩形只取下半(火堆),火焰只展示。
|
||||
|
||||
🔴 **契约 7 · 美术硬约束**:48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1(F15);场景素材全局纹理过滤=Nearest(project.godot 现行默认,像素纪律)。
|
||||
🔴 **契约 8 · 像素规格不硬定**:尺寸档/色板/明度档只写指针(Q6 甲过渡口径=1280 工程档+烛光黄/木色,落 art-bible §6.2/art-assets-checklist §3.5,F16);卡内禁自定像素规格数值;终档置换归反哺点,执行归用户线;禁批内自补、禁 T0 现场发挥。
|
||||
|
||||
🔴 **契约 8 · 像素规格不硬定**:尺寸档/色板/明度档只写指针(战斗一屏样张批冻结的规格清单,F16);卡内禁自定像素规格数值;规格清单未产出时的输入缺口=Q6 裁定(禁批内自补、禁 T0 现场发挥)。
|
||||
🔴 **契约 9 · 迭代内验收=截图对比+终稿落盘口径**(澄清⑥):camp 截图须为场景图形态;用户实机目检=生命周期最后一步、统一进行、**不阻塞迭代**——目检项登记 07 清单即完成义务。**T1 终稿落 `game/assets/art/scene/` 的「验收」口径=迭代内验收**(截图对比+shot_checker 机检+样张过审留痕)——营地屏整屏样张按既定排队行延后(§0.5),不构成对 art-bible §7.5「前置」条的违背(§5 反哺点注)。
|
||||
|
||||
🔴 **契约 9 · 迭代内验收=截图对比+终稿落盘口径(v1.1 补)**(澄清⑥):camp 截图须为场景图形态;用户实机目检=生命周期最后一步、统一进行、**不阻塞迭代**——目检项登记 07 清单即完成义务。**T1 终稿落 `game/assets/art/` 的「验收」口径=迭代内验收**(截图对比+shot_checker 机检+方向稿用户选划)——营地屏整屏样张按既定排队行延后(§0),不构成对 art-bible §7.5「前置」条的违背(§5 反哺点注)。
|
||||
🔴 **契约 10 · 探针断言迁移契约**(属仓库既有测试纪律的卡内成文,非用户裁决):F8 全量位点逐点显式迁移并留痕;**现无 camp 逐像素 golden 基线**(F17:golden 现役仅棋盘三张+menu_boot)——本批不建 camp golden(「把不存在的东西列进迁移清单」禁),camp 截图验收=shot_probe 出图+shot_checker 语义断言段;若要新建 camp golden 须另裁登记(L3)。
|
||||
|
||||
🔴 **契约 10 · 探针断言迁移契约(v1.1 订正,属仓库既有测试纪律的卡内成文,非用户裁决)**:F8 全量位点逐点显式迁移并留痕;**现无 camp 逐像素 golden 基线**(F17:golden 现役仅棋盘三张+menu_boot)——本批不建 camp golden(「把不存在的东西列进迁移清单」禁),camp 截图验收=shot_probe 出图+shot_checker 语义断言段;若要新建 camp golden 须另裁登记(L3)。
|
||||
🔴 **契约 11 · 拆分边界纪律**(结构类卡必填,判据权威=`design/engineering/code-conventions.md` §1.1,本卡写条款、不复制定文):本卡含「四柜台拆解」,拆分沿**职责轴**不沿阈值轴——每个拆出单元(锻造/秘宝/天赋树/图鉴四界面 view)须有可陈述的职责语句与单一变化原因、须有集体身份(命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁);「过阈值」只许作为发现信号,作为拆分或收拢的动机即违规;按纪律拆不出内聚单元 → 停卡呈报走豁免/还债通道,禁硬拆凑绿(写进 §8 L3)。实现报告逐件一行承载职责语句。
|
||||
|
||||
🔴 **契约 12 · 禁为机检文本保留无效成员**(重构类卡必填条款,与 code-conventions §1.1 同款):禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名;机检键与卡片契约互斥时,实现段一律停卡呈报,不得自造第三条路。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:场景素材产出(渠道按 Q1 裁定);场景屏+七物件命中区与点选交互(反馈细节按 Q3 设计稿);四柜台拆解与五界面入口改挂(含 presenter 承接面改造);大门动线两分支+断点弹窗移位;篝火回主菜单;场景屏顶栏预留位;断言/探针迁移;反哺点清单。
|
||||
**做**:场景屏+10 层场景素材铺底+七物件命中区与点选交互(四态规格按 camp.md v2.2——§0.3 P3 已销号=camp.md v2.2 四态规格即设计稿);四柜台拆解与五界面入口改挂(含 presenter 承接面改造);大门动线两分支+断点弹窗移位;篝火回主菜单小动线(带二次确认);场景屏顶栏预留位;断言/探针迁移;反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- **不做贴图重构批**(澄清③1):柜台场景贴图/钥石顶栏图标/永久强化条目图标的达标化置换——搁置未废,待交互重做完形后重新评估立项(F12);
|
||||
- **不做贴图重构批**(澄清③1):柜台场景贴图/钥石顶栏图标/永久强化条目图标的达标化置换——搁置未废,待交互重做完形后重新评估立项(F12);
|
||||
- 不激活图鉴内容(契约 5);
|
||||
- 不改五界面功能与写口链路(契约 4);
|
||||
- 不改冒险屏/选路屏/战斗屏本体(F7 已实现不动);
|
||||
- 不做整屏样张闸、不替代样张批、不新建 camp golden(§0/契约 10);
|
||||
- 不改冒险屏/选路屏/战斗屏本体(F7 已实现不动);
|
||||
- 不做整屏样张闸、不替代样张批、不新建 camp golden(§0.5/契约 10);
|
||||
- 局内状态面板零改动(契约 6);
|
||||
- 素材后处理与入库属实现段 T1/`asset-ingest` 第 3 步(§0.3-4 授权形态:/iter 开工令即含授权),不属写卡门;
|
||||
- ESC 浮窗与顶栏 HUD 实现 → 卡⑤(本卡只留顶栏位+定义刷新单口);
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①。
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
> **v1.3 口径**:本批禁写 `design/` 整目录与根规则文件,唯一可写目录 = `reports/`;下表一律执行为列进反哺点清单。美术素材产物按素材线纪律落 `artwork/`(在途)→ `game/assets/art/`(终稿),不随 git 合并(F14)。
|
||||
> **现行口径**:实现批禁写 `design/` 整目录与根规则文件,唯一可写目录=`reports/`(+`game/` 实施本体);设计权威文档(GDD/art-bible)与机械面文档(roadmap/体验验证清单/README)对代码实现批一律不直改,逐条列进 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后归文档批落文;本卡全表义务格一律为「反哺点 · 列清单」。camp.md v2.2/art-bible v3.31 §6.2 已先行落文 10 层新形态,本批实现=对齐现行条款,反哺义务为**实装状态回填+目检结论**,非形态改版。美术素材产物按素材线纪律落 `artwork/`(在途)→ `game/assets/art/scene/`(终稿),不随 git 合并(F14)。
|
||||
|
||||
| 文档 | 关联点 | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/gdd/ux/camp.md` | :14-20 布局与交互节(三柜台列/顶栏/底栏)+:10 定位句(主菜单入口)+头注事实源声明(=camp_overlay_view.gd 实态)——四柜台形态整体退役、入口改挂场景物件 | 反哺点 · spec 整体改版重写(形态节退役+新形态成文) |
|
||||
| `design/art/art-bible.md` | §6.2 营地场景(三柜台一字排开→交互式场景纵深稿);§7.5 营地界面(三柜台画法→五界面画法+场景图画法);§2.8 承载元素;营地屏整屏样张排队行注记(本批不跑闸);**§7.5「视觉终稿以表现层样张过审为前置」条与本批的关系=样张排队行既定延后(终稿后再出样),防文档批误判违例**(v1.1 补注) | 反哺点 · 列清单(权威面零直改,过用户裁决后归文档批) |
|
||||
| `design/gdd/systems/跨局循环/生涯.md` | :69 与营地边界表(条带 `_rebuild_recent` 随 CampOverlay 拆解去向=Q4);:63 图鉴挂起条(入口改挂注记) | 反哺点 · 列清单(P6 去留随 Q4 裁定) |
|
||||
| `design/gdd/ux/camp.md` v2.2 | §定位/§场景构成与七物件绑定/§点选反馈/§顶栏/§叠层与开合:新形态已落文,本批实现后回填实装状态、样张过审留痕、实机目检结论 | 反哺点 · 列清单(实现批禁写 `design/`) |
|
||||
| `design/art/art-bible.md` v3.31 | §6.2 营地场景 10 层口径已落文;§7.5 营地界面画法、§2.8 承载元素、§1.4 营地屏整屏样张排队行注记(本批不跑样张闸) | 反哺点 · 列清单(权威面零直改,过用户裁决后归文档批) |
|
||||
| `design/art/camp-scene-prompts.md` v1.3 | §5「待裁」节五点已裁断(2026-11-01 用户裁:①帐篷追认现值②门扇追认现值③布告板 (845,268,130,140)④描边奶油色追认⑤环境子板色值不前置、T1 量化后按 §4.1 登记)——**已落文(2026-11-01 本会话文档批):camp-scene-prompts.md v1.3 §5 改已裁+art-bible.md v3.31 §6.2 待裁行改已裁,义务了结** | 已落文 · 义务了结 |
|
||||
| `design/gdd/systems/跨局循环/生涯.md` | :69 与营地边界表(条带 `_rebuild_recent` 随 CampOverlay 拆解去向=Q4 已裁甲);:63 图鉴挂起条(入口改挂注记) | 反哺点 · 列清单 |
|
||||
| `design/gdd/08-Alpha版本标准.md` | 「三 · 界面」营地行(:104)形态注记改写——状态列可改;注记改写列反哺点;增删行须用户裁(三卡统一口径) | 反哺点 · 列清单 |
|
||||
| `design/gdd/06-待办清单.md` | 本卡登记条;UI 贴图重构批条(:21-27)注记「四柜台对象变化、重评待交互重做完形」;图鉴挂起条注记 | 反哺点 · 列清单 |
|
||||
| `design/gdd/07-体验验证清单.md` | 新增实机目检条(场景观感/七物件辨识与点选反馈/大门两分支/篝火动线)——登记不阻塞 | 反哺点 · 列清单 |
|
||||
| `roadmap.md` | 迭代行编入/状态刷新 | 反哺点 · 列清单 |
|
||||
| `game/presentation/*`、`game/smoke/*`、`game/tests/**`(v1.1 补——forge_workbench_test 迁移面)、`game/tests/golden/*`(仅棋盘/menu 既有基线复跑)、`game/assets/art/*` | 实现/断言/终稿素材本体 | 实施本体(可写) |
|
||||
| `design/gdd/06-待办清单.md` | 本卡登记条;UI 贴图重构批条(:21-27)注记「四柜台对象变化、重评待交互重做完形」;图鉴挂起条注记 | 反哺点 · 列清单(实现批禁写待办清单) |
|
||||
| `design/gdd/07-体验验证清单.md` | 新增实机目检条(场景观感/七物件辨识与点选反馈/大门两分支/篝火动线)——登记不阻塞 | 反哺点 · 列清单(实现批禁写体验验证清单,入册条目由文档批落) |
|
||||
| `roadmap.md` | 迭代行编入/状态刷新 | 反哺点 · 列清单(实现批禁写 roadmap) |
|
||||
| `game/tests/README.md` | 能力行入表(§7 对照表「能力行」列定名) | **已入表**(旅程段六,本卡七条新增能力行在役;v3.1 V14 用例锚随批补)——实现批只核对名实一致,禁写 |
|
||||
| `game/presentation/*`、`game/probe/*`、`game/tests/**`(含 forge_workbench_test 迁移面)、`game/tests/golden/*`(仅棋盘/menu 既有基线复跑)、`game/assets/art/scene/*`(10 层终稿) | 实现/断言/终稿素材本体 | 实施本体(可写) |
|
||||
| `tools/shot_checker.py` | camp 取样面语义断言段(文字 WCAG/明度三档带/文字禁压纹理/文字在判据扩 camp) | 实施本体(可写,属工具/测试面) |
|
||||
|
||||
## 卡前·文件结构与职责(T0 前)
|
||||
|
||||
定义 T 卡前先列全本批新建/修改文件,一文件一职责。**每个文件行标「行为改动/非行为改动」并指向 §7 对照表行号;「落位」列与「落位与可复用性预检」节互指**(标「新建」的必须能在预检节指到不能复用的理由与唯一落点核查)。行号为开工日 T0 复核对象。
|
||||
|
||||
| 文件 | 职责 | 改动类型 | 落位 | 对照表行 |
|
||||
|---|---|---|---|---|
|
||||
| `game/presentation/views/camp_scene_view.gd` | 交互式营地场景图视图:10 层纹理铺底(z0–z6)+七物件命中区+点选反馈四态+名牌(节点选型=Control 根+TextureRect 层+子节点序即 z 序,T2) | 行为改动 | 新建 | V1/V2/V9/V10 |
|
||||
| `game/data/tables/camp_scene_layout.tres` | 10 层贴图矩形/命中矩形/z 序/门洞缺口坐标(数值单一事实源 `data/tables/`;schema 类+表现域容器类型化成员,先例 battle_layout.tres=`core/config_db.gd`/`core/config_domain_presentation.gd`;加载期 fail-fast 校验命中区几何,T2) | 行为改动 | 新建 | V1/V2/V9/V14 |
|
||||
| `game/presentation/views/camp_refresh_bus.gd` | 营地刷新单口:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成(卡⑤ 同口挂接;头注自陈「非 autoload、非信号广播——组合根单点推送」) | 行为改动 | 新建 | V8/B1 |
|
||||
| `game/presentation/views/forge_view.gd` | 锻造工坊独立可开界面(自 CampOverlay `_forge_workbench` 抽出,界面逻辑复用) | 行为改动 | 新建(抽出) | V4/B2 |
|
||||
| `game/presentation/views/treasure_view.gd` | 秘宝匣独立可开界面(自 `_ws_scroll`/`_workshop_holder` 抽出) | 行为改动 | 新建(抽出) | V4/B2 |
|
||||
| `game/presentation/views/tree_view.gd` | 天赋树独立可开界面(自 `_tree_holder` 抽出) | 行为改动 | 新建(抽出) | V4/B2 |
|
||||
| `game/presentation/views/codex_view.gd` | 奖励格图鉴独立可开界面占位壳(内容保持挂起,契约 5) | 行为改动 | 新建(抽出) | V4/V6/B2 |
|
||||
| `game/presentation/scenes/camp_screen.gd` | 屏壳改造:持 camp_scene_view 取代 CampOverlay 成员+顶栏 HUD 预留位+模态栈单口 | 行为改动 | 复用既有(改造) | V1/V8 |
|
||||
| `game/presentation/views/camp_overlay_view.gd` | 四柜台覆盖层拆解后退役(无消费面即删,吸收即死) | 行为改动 | 退役 | V4/V9/B2 |
|
||||
| `game/presentation/views/career_overlay.gd` | 生涯页入口自主菜单改挂帐篷旁笔记本(本体零改,返回钮文案禁含「营地」) | 行为改动 | 复用既有(改入口) | V5 |
|
||||
| `game/presentation/views/confirm_dialog.gd` | 三钮化(继续上一段冒险/开始新冒险/取消) | 行为改动 | 复用既有(改) | V3/B1 |
|
||||
| `game/presentation/camp_shop_presenter.gd` | 投影目标改线+写口尾刷新改路由+open/close 编排迁位(契约 4;路径订正——v3.0 写 `presenters/` 子目录不存在) | 行为改动 | 复用既有(改) | V4/V8/B1 |
|
||||
| `game/presentation/camp_flow_context.gd` | 类型化字段语义订正(路径订正——v3.0 写 `core/` 子目录不存在) | 行为改动 | 复用既有(改) | V4/B1 |
|
||||
| `game/presentation/ui_camp_modals.gd` | 营地域模态句柄子容器(卡⑥ 前置建件;本卡 T3 落场景根+四子界面五枚句柄,卡⑤ T1 落浮窗句柄;寻址 `ui.modals.camp.X` 二层) | 行为改动 | 新建(卡⑥) | V1/V8 |
|
||||
| `game/presentation/ui_modals.gd` | 本卡零句柄新增(营地域句柄落 ui_camp_modals.gd 子容器,2026-11-01 用户裁);类变量 14 不升(S13 预期,见「实现规范预算·存量触面一查」) | 非行为改动(卡④ 段零改) | 复用既有 | V1/V8 |
|
||||
| `game/presentation/main_ui_build.gd` | 新层实例化装配点:各 `.new()`+装配尾 `apply_layer_contract(root)`(先例 :105/:121/:242);五句柄赋值经 `modals.camp.X` 子容器 | 行为改动 | 复用既有(改) | V1/V8 |
|
||||
| `main.gd`(组合根) | 断点弹窗移位+ConfirmDialog 三钮消费方编排+营地入口改挂+篝火动线+开层互斥/关层收闭 | 行为改动 | 复用既有(改) | V3/V4/V7/B1 |
|
||||
| `game/assets/art/scene/scene_camp_*.png`(10 张) | 10 层场景素材终稿(asset-ingest 第 3 步拷入,命名对齐 `UiKit.TEX_CAMP_*`——常量十键本批在 `ui_kit.gd` 新增) | 非行为改动(美术资源) | asset-ingest | V1/V10 |
|
||||
| `game/probe/shot_probe.gd` | camp 截图路径改场景图+收闭改接篝火动线 | 非行为改动(测试面) | 复用既有(改) | V1 |
|
||||
| `game/probe/ui_click_probe.gd` | 七物件可达+大门两分支+断点确认段改走大门+写口链迁移+深员改指 | 非行为改动(测试面) | 复用既有(改) | V2/V3/V4 |
|
||||
| `game/probe/ui_probe.gd` | 营地入口/余额/秘宝匣深员改指新界面 | 非行为改动(测试面) | 复用既有(改) | V4 |
|
||||
| `game/probe/screen_flow_probe.gd` | 营地旗标新语义+回退段改新场景根 closed/篝火动线 | 非行为改动(测试面) | 复用既有(改) | V7 |
|
||||
| `game/probe/ui_struct_probe.gd` | 反参照结构禁词+生涯段驱动改接+图鉴占位锚定+camp 场景层序段(V1 补立) | 非行为改动(测试面) | 复用既有(改) | V1/V5/V6/V9 |
|
||||
| `game/probe/ui_contrast_probe.gd` | 营地令牌段核签(CAMP_HEADER_BAND/CAMP_ROW_PLATE 消费判定) | 非行为改动(测试面) | 复用既有(改) | V10 |
|
||||
| `game/tests/system_test.gd`+`game/tests/unit/presentation/theme_projection_test.gd` | V2 实例化/V3g 模态开合改指(system_test :67-68/:520-541)+四柜台等高源码断言段重写或注销(theme_projection_test :710-717)——`game/smoke/smoke_all.gd` 已退役,v3.0 旧位点作废 | 非行为改动(测试面) | 复用既有(改) | V4/B2 |
|
||||
| `game/tests/unit/presentation/forge_workbench_test.gd` | 随锻造视图抽出同迁(断言零改、只换承载类) | 非行为改动(测试面) | 复用既有(改) | V4/B2 |
|
||||
| `tools/shot_checker.py` | camp 取样面语义断言段 | 非行为改动(工具/测试) | 复用既有(改) | V10 |
|
||||
| `game/tests/README.md` | 能力行入表(四列:能力|系统|真实入口|代表用例) | 非行为改动(文档,写卡段) | 主 Agent 统一收口 | 全表能力行 |
|
||||
| `reports/iter<N>-实现报告-<日期>.md`/`reports/iter<N>-反哺点-<日期>.md`/`reports/iter<N>-验收审计-<日期>.md` | T6 三份产出物 | 非行为改动(过程件) | 新建 | T6 |
|
||||
|
||||
## 落位与可复用性预检
|
||||
|
||||
定义 T 卡前,对本批每个**新增文件/新增函数/新增数据件**过三问(复用面/唯一面/分层与交互面),与「卡前·文件结构与职责」互指。生效=2026-10-21 用户立规;本卡虽文件名日期 2026-10-18,经 2026-10-31 用户明令重立为现行有效卡,按现行规则补齐。
|
||||
|
||||
### 新增件三问
|
||||
|
||||
| 新件 | 复用面(grep 同职责实现) | 唯一面(grep 同名同义标识符) | 分层与交互面(调用路径+跨层出口) |
|
||||
|---|---|---|---|
|
||||
| `camp_scene_view.gd` | 盘上视图件=CampOverlay(四柜台覆盖层,形态本批退役、与场景图形态正交,反参照禁复用)、CareerOverlay(生涯全屏页,职责不同)、camp_screen.gd(屏壳)。**无**「场景图铺底+七物件命中区+点选反馈」同职责实现 → 不能复用,须新建视图件;屏壳复用既有 `camp_screen.gd` 改造,不新建屏壳 | grep `camp_scene`/`scene_camp` 视图标识符盘上无第二处;素材消费常量 `UiKit.TEX_CAMP_*` 单点(asset-path-map.md §3),不另立路径常量 | 呈现层视图,零 Core 写调用;命中区 `_gui_input()` → `object_pressed(key: String)` 信号 → 组合根 → 开界面/写口;10 层装载经 `UiKit.load_tex()`(exists 守卫、禁 preload);不直连 MetaProgression |
|
||||
| `camp_scene_layout.tres` | grep `game/data/tables/` 无营地坐标表;坐标草案在 artwork HTML(不入 git)与 camp-scene-prompts §2.3(设计文档,非运行时数据源)。数值单一事实源要求落 `data/tables/` → 新建 | 坐标事实只此一处;禁在 `camp_scene_view.gd`/探针/T 卡各写一份坐标字面量(契约 8 禁自定规格、数值单点) | 视图启动时经 ConfigDB 单点读取(schema 类+表现域容器类型化成员,先例 battle_layout.tres);坐标值按 §0.3-3 五裁点裁断(2026-11-01)由 T2 定值落入 |
|
||||
| `camp_refresh_bus.gd` | grep 现役 `refresh_camp` 绑定 CampOverlay 句柄(F5),随四柜台形态**退役**(卡⑤/§0.5 明确「非已退役的 refresh_camp」);新单口须同时刷新现开界面与场景根 HUD、职责不同 → 不能复用,须新建 | grep `camp_refresh`/刷新单口类标识符盘上无第二处;卡⑤ HUD 刷新挂此单口,不另立第二口 | 组合根拆件:七写口尾 → `CampRefreshBus.refresh(camp_data: Dictionary) -> void` → 现开界面刷新+场景根 HUD 刷新;一次性刷新,不新增写口语义(契约 4) |
|
||||
| `ui_camp_modals.gd`(卡⑥ 建件,本卡落五枚) | grep 句柄分域容器无先例;`UiModals` 本体=按技术角色平铺的注册面容器(职责不同)、`ModalFlow`=调度器非句柄册 → 不能复用,须新建(卡⑥ 已立卡) | grep `UiCampModals`/`ui_camp_modals` 无第二处;营地句柄寻址唯一落点 | RefCounted 不挂树;`main_ui_build` 构造注入 `ui_modals.camp`;`register_layer`/LAYER_CONTRACT/树序零触碰(句柄册结构≠树结构) |
|
||||
| `forge_view.gd`/`treasure_view.gd`/`tree_view.gd`/`codex_view.gd` | 界面逻辑**已实现**于 CampOverlay 内(F4:`_forge_workbench`/`_ws_scroll`/`_tree_holder`/图鉴占位);本批是抽出承载壳、**复用既有界面逻辑**(行板/写口链路零重设计,契约 4),非从零新写 → 新建壳、迁移逻辑 | grep `forge_view`/`treasure_view`/`tree_view`/`codex_view`/`_forge_rows`/`_forge_workbench` 盘上无第二处定义;`forge_workbench_test.gd:33` 消费 `_forge_rows`/`_forge_workbench`,抽出后同迁,断言零改只换承载类 | 各视图发意图信号(`buy_requested`/`tree_buy_requested`/`forge_*`)→ 组合根 → CampShopPresenter → MetaProgression 写口(Core 零 UI 不破);五界面各带返回/关闭钮;开合走模态栈单口、层序入 `LAYER_CONTRACT` |
|
||||
|
||||
### 非新增件说明
|
||||
|
||||
- `camp_screen.gd`/`camp_shop_presenter.gd`/`camp_flow_context.gd`/`confirm_dialog.gd`/`career_overlay.gd`/`main.gd`:存量文件改造,不新增文件;公开方法/成员预算见「实现规范预算」存量触面一查。
|
||||
- `camp_overlay_view.gd`:四柜台拆解后无消费面即删(吸收即死),禁留死代码;`main.camp_overlay` 探针句柄迁移留痕(F2/F8)。
|
||||
- `game/tests/golden/*`:棋盘三张+menu_boot 既有基线复跑,不新建 camp golden(契约 10)。
|
||||
- **数据件**:本卡纯呈现层,零玩法数值、无新增 `data/tables/` 玩法表(`camp_scene_layout.tres` 为呈现坐标件,非玩法数值);数值单一事实源 `data/tables/` 不被玩法值触碰;RNG 零新增(RNG 单点 RngService 不被触碰);Core 零 UI 依赖不因新增场景屏绕过。
|
||||
|
||||
## 实现规范预算
|
||||
|
||||
定义接口块/文件结构图前,对本批每个**新增文件**对照 gdstyle 默认阈值做预算,与接口块及「卡前·文件结构与职责」互指。**生效说明**:规范写「生效=文件名日期 ≥2026-10-27,此前卡片属历史留档不回头改」——本卡经 2026-10-31 用户明令重立为现行有效卡,按现行规则补齐,不援引「不回头改」跳过。gdstyle v0.4.0 默认阈值:**公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6**(阈值取工具默认,改阈值=基线口径变须在本批重生成)。
|
||||
|
||||
### 新件三查
|
||||
|
||||
| 触面 | ①公开方法数 | ②成员变量数 | ③迁移函数对账 | 结论 |
|
||||
|---|---|---|---|---|
|
||||
| `camp_scene_view.gd` | 预期 ≤8:`setup_layers()`/`bind_hotspots()`/`set_feedback_state()`/`get_hotspot_rect()`/`set_hud_band()`/`_on_hotspot_input()` 等,远低 ≤20 | 预期 ≤12:10 层纹理句柄+命中区表+反馈态+名牌节点;收编进内聚结构,低 ≤15 | 无自存量文件迁入函数 | 三查通过;T0 实测实数回填,超阈值即拆件或砍面(砍面=退设计段裁职责) |
|
||||
| `camp_refresh_bus.gd` | 预期 ≤3:`refresh()`/`bind_targets()`/`_dispatch()`,远低 ≤20 | 预期 ≤4:现开界面句柄+场景根 HUD 句柄+投影数据,低 ≤15 | 自 `camp_shop_presenter.gd` 迁入刷新路由;先查 `tools/gdstyle_baseline.txt` 源函数键,branches/returns/函数长超默认登记值的迁移即拆函数(拆分不改语义,不算迎合机检) | 三查通过;迁入后补跑 S13,不凭旧读数放行 |
|
||||
| `ui_camp_modals.gd`(卡⑥ 建件) | 预期 ≤1(`_init`) | 预期 5(本卡 T3 落五枚;卡⑤ 落满 6),低 ≤15 | 无迁入函数 | 三查通过(卡⑥ 卡面已立预算) |
|
||||
| 四界面 view(各) | 各预期 ≤6:`open()`/`close()`/`bind_rows()`/`_on_action()` 等,远低 ≤20 | 各预期 ≤10:复用既有行板成员(`_forge_workbench`/`_ws_scroll`/`_tree_holder`),低 ≤15 | 自 `camp_overlay_view.gd` 迁入对应界面函数;逐个对 `tools/gdstyle_baseline.txt` 源键,超登记值即拆函数 | 三查通过;抽出后实测实数回填 |
|
||||
| `camp_scene_layout.tres` | 不适用(数据件,无方法) | 不适用(结构键,非类变量) | 不适用(无迁入函数) | 结构键唯一,坐标值按 §0.3-3 五裁点裁断(2026-11-01)由 T2 定值落入 |
|
||||
|
||||
### 存量触面一查
|
||||
|
||||
本卡触碰存量文件:`camp_screen.gd`/`camp_shop_presenter.gd`/`camp_flow_context.gd`/`confirm_dialog.gd`/`career_overlay.gd`/`camp_overlay_view.gd`(退役)/`main.gd`/`ui_modals.gd`/`ui_registry.gd`/`main_ui_build.gd`/探针七件/`forge_workbench_test.gd`。**T0 读数步骤写明「S13 键较 `tools/gdstyle_baseline.txt` 登记值只降不升」的预期与达成手段**:`ui_modals.gd` 类变量 14→14(−平铺 `camp_overlay`+camp 容器成员由卡⑥ 建件承担——营地域句柄一律落 `ui_camp_modals.gd` 子容器,2026-11-01 用户裁,禁平铺);`ui_registry.gd` +camp 二层解析分支(分支数 ≤8);`main_ui_build.gd` 装配行改指(方法面微增,T0 实测对账);CampOverlay 拆解=删四柜台代码(键只降);`camp_shop_presenter.gd` 改造=投影目标改线、不新增公开方法/成员;`main.gd` 组合根=断点弹窗移位+消费方编排(新增私有状态量一枚,S13 类级键只降不升);探针改接=替换断言段、不新增扫描面键。任何 S13 键被迫上升=§8 L3 停卡。
|
||||
|
||||
### 指标例外通道
|
||||
|
||||
现预算内,无需例外。若场景视图十层句柄+七命中区+反馈态实测超 15 类变量,或组合根成员超基线,设「指标例外申报」节——键名+理由+用户特批落基线;未申报的超预算面进实现段=停卡退回写卡段(写进 §8 L3)。
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核全部行号。**估时主变量=美术渠道(Q1 待裁)、点选反馈设计稿往返(Q3)与规格输入(Q6)**。
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核全部行号。步骤用 `- [ ]` 复选框记步,一步=一个动作+一个可核查结果;每卡声明「取用/产出」接口块,后卡引用的名字与早卡定义逐字一致;禁占位待定句式(不决定任何事的行不留)。**天然切分点=T1 出口**(场景素材归档齐)——美术线与代码线可分批交付(用户批次归属裁);若单批跑超迭代盒,T3 末(四柜台拆解完)为次切分点。
|
||||
|
||||
### T0 事实坐实与设计输入齐备检查(≈0.5d)
|
||||
|
||||
- 复核 §2 全部行号;四柜台界面清单与探针面清单(F8)逐点过;
|
||||
- 设计输入齐备检查:Q1(美术渠道)/Q2(参照物)/Q3(点选反馈细节)/Q4(条带去留)/Q5(三出口弹窗)/Q6(规格缺口)状态逐项核——**未裁项对应的任务段禁开工**(缺输入=呈报,禁批内自补);
|
||||
- 规格清单依赖核检:战斗一屏样张批(06 待设计)产出时点 vs 本批排期——按 Q6 裁定执行(禁 T0 现场发挥);
|
||||
- 与卡③对齐 ConfirmDialog 消费方编排机制(T4 互指)、与卡⑤对齐顶栏位与刷新单口(§0 跨卡接口)。
|
||||
- 取用:待办清单条目状态(文档设计已闭合+美术设计已闭合+已写卡)、§0 重立凭据、art-bible v3.31 §6.2、camp.md v2.2、camp-scene-prompts.md v1.2、`_终稿裁定-2026-10-31.txt`、布局草案-1280x800.html。
|
||||
- [ ] 复核 §2 F1–F20 全部 `file:line` 行号;四柜台界面清单与探针面清单(F8)逐点过——漂移即停卡核对,不猜行号。
|
||||
- [ ] 设计输入齐备检查:Q1(美术渠道=B 已裁)/Q2(参照物已补)/Q4(条带=甲)/Q5(三钮=甲)/Q6(过渡口径=甲)/Q8(出稿方式=甲)状态逐项核为已裁;**代码段开工门**(§0.3 四项)状态逐项核为已裁断销号(2026-11-01)——若发现裁断实况反转=L3 停机呈报,禁批内自补。
|
||||
- [ ] **新场景根句柄名+四子界面(forge/treasure/tree/codex view)+场景层 `LAYER_CONTRACT` 槽名定稿**(显式验收点,2026-11-01 双审补立——卡⑤ 只消费不改名);句柄落 `ui_camp_modals.gd` 子容器(卡⑥ 前置建件,寻址 `ui.modals.camp.X` 二层)+`main_ui_build.gd` 装配点(卡⑤ v1.5 文件表同步列)。
|
||||
- [ ] **跨卡排序登记**:camp 断言基线建立在无 HUD 场景屏(本卡 T5);卡⑤ T2 落 HUD 后由卡⑤ 限 HUD 区显式更新+呈报(卡⑤ v1.4 §0 已对齐)。
|
||||
- [ ] 规格清单依赖核检:样张批规格清单产出时点 vs 本批排期——按 Q6 甲过渡口径执行(1280 工程档+烛光黄/木色,终档置换归反哺点),禁 T0 现场发挥。
|
||||
- [ ] 跑 `bash tools/static_scan.sh` 开发期局部读数(gdstyle 键=S13,闸件 `tools/gdstyle_gate.py`——原 `tools/gdstyle.sh` 不存在,v3.1 订正),记录 `camp_screen.gd`/`camp_shop_presenter.gd`/`main.gd` 当前行数、类变量、公开方法数;登记 S13 基线键(预期只降不升)。本步为最窄确认,不构成验收。
|
||||
- [ ] 与卡③ 对齐 ConfirmDialog 消费方编排机制(T4 互指)、与卡⑤ 对齐顶栏位与营地刷新单口(§0.5 跨卡接口)。
|
||||
- 产出:基线 commit、S13 实数、设计凭据核对表、代码段开工门未销号清单;后续卡只按本卡契约施工。
|
||||
|
||||
### T1 场景美术产出(≈2–4d,渠道 A/B 待裁 Q1)
|
||||
### T1 场景素材终稿录入(asset-ingest 第 3 步,≈1–1.5d;后处理授权随 /iter 开工令,§0.3-4)
|
||||
|
||||
- 按纵深稿构图(远景天空/中景围栏+大门/近景篝火居中/左锻炉铁砧/右床铺+宝箱)出场景图;七物件可辨性=第一合格线(玩家一眼找到点哪);
|
||||
- 美术硬约束入产线(契约 7):48 色板/禁入带/语义色/WCAG/Nearest;**色板机检二器复用**(palette_check.py 48 令牌+新增色双门/standee_asset_check.py 像素 32 色冻结——v1.1 订正:非「全仓无工具」);
|
||||
- 尺寸档=样张批规格清单指针(契约 8);按 Q6 裁定执行过渡口径或挂起;
|
||||
- 产物按素材线纪律落盘(F14):在途 `artwork/`(主区绝对路径)→ 验收后终稿 `game/assets/art/`;替换先归档旧档;**命名纪律**:art-bible §8.3:503(`<域>_<对象键>_<状态|变体>.png`)+doc-conventions §12 三段式「终稿文件名=代码消费名——先定资源路径常量,素材名跟它走,防断链」(v1.1 补指针);
|
||||
- 终稿落盘「验收」口径=契约 9(迭代内验收三件套);
|
||||
- **防不满意工序**:场景图先出 2–3 张低成本方向稿供用户选划,判完方向才精制(样张卡「方向稿先行」同型纪律;选划记录三段表进开工单)。⚠ 本条为**本卡自我从严的执行纪律**,非用户新裁决——用户若要以单路线直接精制,从其裁。
|
||||
- 取用:P1=B 渠道已裁(2026-10-22)、P4 参照物已补(2026-10-23)、Q6 甲过渡口径已裁(1280×800+烛光黄/木色、2× 源档最近邻降采样)、`_终稿裁定-2026-10-31.txt`(各素材第一张样本稿为终稿、保留全部待选稿)、camp-scene-prompts.md §2–§4。
|
||||
- [ ] 按终稿裁定清单核对十层指定文件(z0 bg/z1 wall/z2–z6 八物件层),确认各取第一张样本稿为终稿、其余候选保留不删。
|
||||
- [ ] 后处理(**授权形态已裁 2026-11-01:/iter 开工令即含本步授权**,不再两次发令):去底转透明(bg 不去底、全幅不透明)/擦通道水印/降采样/围墙层裁细条 (0,240,1280,70) 不做全幅/色板量化/四联底测试条——工具清单见 `design/art/art-pipeline-tools.md`。**降采样逐张登记换算式**(2026-11-01 双审补立):2560×1600 档=÷2 最近邻;**1024×1024 档无整数倍降采样(1024<1280)**——按非整数缩放显式特批登记(L2 记录+实现报告申报像素纪律局限),不回炉重出(追认 2026-10-31 已过审终稿优先、免美术线重开),禁现场默认。
|
||||
- [ ] 产物落盘:`artwork/`(在途,主区绝对路径)→ 验收后终稿 `game/assets/art/scene/`;替换先归档旧档;命名 `scene_camp_<键>.png` 对齐消费常量 `UiKit.TEX_CAMP_*`(常量十键本批在 `ui_kit.gd` 新增——先定资源路径常量、素材名跟它走,防断链;asset-path-map.md §3/art-assets-checklist.md §3.5);texture 导入口径=mipmaps 关、filter 跟项目默认(Nearest,project.godot 现行)。
|
||||
- [ ] 美术硬约束入产线(契约 7):48 色板/HSV 300°–345° 禁入带/语义色/WCAG/Nearest;**色板机检二器复用**(`palette_check.py` 48 令牌+新增色双门/`standee_asset_check.py` 像素 32 色冻结)。
|
||||
- [ ] 方向稿选划留痕:用户 2026-10-31 已裁「第一张样本稿为终稿、保留全部待选稿、每个都还可以接受」,选划三段表进开工单;本批不再重跑方向稿工序。
|
||||
- [ ] 终稿落盘「验收」口径=契约 9(迭代内验收:截图对比+shot_checker 机检+样张过审留痕)。
|
||||
- 产出:十层终稿入 `game/assets/art/scene/`(`UiKit.TEX_CAMP_*` 常量对齐)+素材线在途/归档清单;T2 可挂接。
|
||||
|
||||
### T2 场景视图与点选交互(≈1.5–2d)
|
||||
|
||||
- 场景屏承载(camp_screen 屏壳改造 vs 新屏壳——§9 拍板):场景图铺底+七物件命中区;
|
||||
- 点选交互:命中区判定+反馈形态按 Q3 设计稿(悬停高亮/提示文案/可点区域判定三项);
|
||||
- **场景屏顶栏预留位(v1.1 注明)**:卡⑤ HUD 落位;**刷新入口=本卡 T3 定义的营地刷新单口**(卡⑤同口挂接);
|
||||
- **叠层规则显式派生(v1.1 补)**:随 §9 第 2 条承载形态裁定派生并写入 V2 断言——独立全屏页=场景根隐藏(生涯先例);单一内页切换=场景根留底;「缩放虚化」是两选项之外的第三形态(涉新演出面+素材),禁批内自选(若设计稿/用户想要,升格裁点);
|
||||
- 键盘焦点/手柄不涉(08:191-192 排 Beta;ESC 属卡⑤用户明示裁决面)。
|
||||
- 取用:T1 十层终稿 `UiKit.TEX_CAMP_*`、`camp_scene_layout.tres` 坐标结构键、camp.md v2.2 点选反馈四态规格、§0.3 P3 已销号(camp.md v2.2 四态规格即设计稿)。
|
||||
- [ ] 节点选型(2026-11-01 godot-master 审查定案):**Control 根+TextureRect 层+子节点序即 z 序**;禁 CanvasLayer(绕开 LAYER_CONTRACT 树序落位)、禁 z_index、禁 Sprite2D 根(项目视图全 extends Control;全屏 UI canvas_items 拉伸需 Control 锚点;ui_registry.gd:76 落位口按子节点序 move_child)。命中区=TextureRect 分区+`_gui_input()`(§9-3 域内确认 TextureRect 分区)。
|
||||
- [ ] 在 `game/presentation/views/camp_scene_view.gd` 实现 `setup_layers()`:按 §0.4 z0–z6 铺 10 层(bg 不透明全幅/wall 透明细条 (0,240,1280,70)/门洞 x520-760 留透明缺口透出 bg 小路/八物件层);围墙不并回背景层、门洞不填死、门洞不被任何物件遮挡。
|
||||
- [ ] 实现七物件命中区 `bind_hotspots()` 与 `get_hotspot_rect()`:读 `camp_scene_layout.tres` 坐标键;七件两两不相交、不越画面、不侵入顶栏禁区 y≤48;篝火命中矩形只取下半(火堆),火焰只展示。
|
||||
- [ ] `camp_scene_layout.tres` 加载期 fail-fast 校验(2026-11-01 双审补立):七命中区两两不相交/不越画面/不侵 y≤48,违则致命中止(fail-fast 红线;契约 7 几何红线机检化)。
|
||||
- [ ] 实现点选反馈四态 `set_feedback_state()`(零新素材、全程序化):悬停=轮廓亮边+浮动(整像素吸附)+名牌(下方 8px 居中、字号 14、对比 ≥4.5:1)/按下=位移 1px/禁用=降明度+去饱和(仅锻炉、宝箱)/已解锁=常态。实现约束(2026-11-01 双审补立):悬停浮动位移经 Tween(禁 `_process` 逐帧移 UI);名牌 Label `mouse_filter = IGNORE`(防挡命中区,UiKit 惯例 ui_kit.gd:312/333);名牌位置加画面内钳制(贴底物件防出画);禁用态去饱和经 ShaderMaterial(modulate 只能调明度,去饱和需 shader——零新素材、代码件)。
|
||||
- [ ] 场景屏顶栏预留位 `set_hud_band()`(高 48px 浮层覆盖、不挤压画面,卡⑤ 填值;载体循 UiKit `page_shell` 的 `PRESET_TOP_WIDE`+`custom_minimum_size` 先例 ui_kit.gd:523-525,禁手动 rect);刷新入口=本卡 T3 定义的营地刷新单口(卡⑤ 同口挂接)。
|
||||
- [ ] 叠层规则显式派生并写入 V2 断言:随 §9 第 2 条承载形态裁定派生——独立全屏页=场景根隐藏(生涯先例)/单一内页切换=场景根留底;「缩放虚化」是两选项之外的第三形态(涉新演出面+素材),禁批内自选(若设计稿/用户想要,升格裁点)。
|
||||
- [ ] 键盘焦点/手柄不涉(08:191-192 排 Beta;ESC 属卡⑤ 用户明示裁决面)。
|
||||
- 产出:`CampSceneView.setup_layers()`/`bind_hotspots()`/`set_feedback_state()`/`get_hotspot_rect()`/`set_hud_band()`,`object_pressed(key: String)` 信号;T3 按接口块接线。
|
||||
|
||||
### T3 四柜台拆解与入口改挂(≈2–2.5d)
|
||||
|
||||
- 锻造工坊/秘宝匣/天赋树/图鉴四界面自 CampOverlay 抽出为独立可开界面(承载形态=§9 拍板:各自独立全屏页〔生涯先例〕vs 单一营地内页切换——约束=写口链路零改、零新素材);**五界面各带返回/关闭钮**(v1.1 补承诺——现四者共用 CampOverlay 底栏关闭钮随拆解消失,卡⑤ Q1 甲案前提即此);
|
||||
- 生涯页入口自主菜单生涯钮改挂床铺旁笔记本(CareerOverlay 本体零改);**生涯页返回钮文案禁含「营地」字样**(ui_struct_probe.gd:382 nojump 禁词表含「营地」——career_overlay.gd:27 现「返回」安全,改文案时避坑,v1.1 补坑注);
|
||||
- 入口改挂后各界面开合走模态栈单口;层序入 `LAYER_CONTRACT`;
|
||||
- **CampShopPresenter 承接面改造(v1.1 补条,契约 4)**:投影目标改线(五界面/场景根)、七写口尾刷新改路由(经营地刷新单口——**各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成**)、open/close 编排从 campbtn 迁位到卡③进档动作、CampFlowContext 字段语义订正、探针缝保缝(main.gd:1163-1171);改造纪律见契约 4(attune 尾不一致口保持原样);
|
||||
- **图鉴占位态(v1.1 补条)**:独立界面壳=§9 第 2 条承载形态;文案照录 camp_overlay_view.gd:168 现行锁文,「柜台结构位预留」中『柜台』称谓随形态订正(L2 记录,零新文案);V6 断言锚定新视图占位标签;
|
||||
- **开层互斥与关层收闭(v1.1 补条)**:营地开层时选档层被模态栈遮挡隐藏(卡③ T2 同口径);篝火/ESC 回主菜单=营地层与选档层同收+开始层恢复(现役 on_camp_closed 模式只视野开始层,选档层不在其视野——须显式);
|
||||
- CampOverlay 类退役处理(拆解后无消费面即删——吸收即死纪律);`main.camp_overlay` 探针句柄迁移留痕;
|
||||
- 最近冒险条带去向按 Q4 裁定执行。
|
||||
- 取用:T2 的 `CampSceneView`(`object_pressed(key: String)`)、F4 五界面实态、F5 写口链、§0.5 跨卡接口。
|
||||
- [ ] 锻造工坊/秘宝匣/天赋树/图鉴四界面自 CampOverlay 抽出为独立可开界面(`forge_view.gd`/`treasure_view.gd`/`tree_view.gd`/`codex_view.gd`,承载形态=§9 拍板;约束=写口链路零改、零新素材、界面逻辑复用);**五界面各带返回/关闭钮**(现四者共用 CampOverlay 底栏关闭钮随拆解消失,卡⑤ Q1 甲案前提即此)。
|
||||
- [ ] 按契约 11 拆分边界纪律落四界面:每界面写职责语句与单一变化原因(实现报告逐件一行)、有集体身份;禁凑计数袋、禁沿阈值轴硬拆;拆不出内聚单元=停卡呈报走豁免。
|
||||
- [ ] 生涯页入口自主菜单生涯钮改挂帐篷旁笔记本(CareerOverlay 本体零改);**生涯页返回钮文案禁含「营地」字样**(ui_struct_probe.gd:382 nojump 禁词表含「营地」——career_overlay.gd:27 现「返回」安全,改文案时避坑)。
|
||||
- [ ] 入口改挂后各界面开合走模态栈单口;层序入 `LAYER_CONTRACT`;五枚句柄(场景根+四子界面)落 `ui_camp_modals.gd` 子容器(卡⑥ 前置建件,寻址 `ui.modals.camp.X` 二层)——禁平铺回 `ui_modals.gd`(S13 上限,2026-11-01 用户裁)。
|
||||
- [ ] **CampShopPresenter 承接面改造(契约 4)**:投影目标改线(五界面/场景根)、七写口尾刷新改路由(经营地刷新单口——**各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成**)、open/close 编排从 campbtn 迁位到卡③ 进档动作、CampFlowContext 字段语义订正、探针缝保位(组合根薄委托缝,T0 复核现役行号——原引 main.gd:1163-1171 超出文件实长);`on_camp_attune_toggle` 口尾(:102)只 refresh_camp、不调 `_refresh_presentations` 保持原样。
|
||||
- [ ] 新建 `camp_refresh_bus.gd` 实现 `CampRefreshBus.refresh(camp_data: Dictionary) -> void`(T0 接口块);卡⑤ HUD 刷新挂此单口,非已退役的 `refresh_camp`;头注自陈「非 autoload、非信号广播——组合根单点推送」(防被读成已否决的全局事件总线形态,2026-11-01 双审注)。
|
||||
- [ ] **图鉴占位态(契约 5)**:独立界面壳=§9 第 2 条承载形态;文案照录 camp_overlay_view.gd:168 现行锁文,「柜台结构位预留」中『柜台』称谓随形态订正(L2 记录,零新文案);V6 断言锚定新视图占位标签。
|
||||
- [ ] **开层互斥与关层收闭**:营地开层时选档层被模态栈遮挡隐藏(卡③ T2 同口径);篝火/ESC 回主菜单=营地层与选档层同收+开始层恢复(现役 on_camp_closed 模式只视野开始层,选档层不在其视野——须显式)。
|
||||
- [ ] CampOverlay 类退役处理(拆解后无消费面即删——吸收即死);`main.camp_overlay` 探针句柄迁移留痕。
|
||||
- [ ] 最近冒险条带去向按 Q4=甲执行(随四柜台拆解一并撤下,逐局历史回看归生涯,生涯.md P6 同批注销——反哺点)。
|
||||
- 产出:五界面独立可开(各带返回/关闭钮)+`CampRefreshBus.refresh()` 单口+CampOverlay 退役;T4 按接口块消费。
|
||||
|
||||
### T4 大门动线(≈1–1.5d)
|
||||
|
||||
- 大门点击→两分支确认弹窗(裁决 5):有断点=「继续上一段冒险/开始新冒险」二选一;无断点=「是否开始新冒险:是/否」;
|
||||
- **三出口弹窗形态(Q5 已裁=甲)**:ConfirmDialog 三钮化+**消费方编排**(组合根单实例+待确认消费方状态量:open 前设、confirmed/cancelled 后分派;禁每次 connect/disconnect 漂移、禁双常驻静态接线——卡③ 契约 3 同款互指);
|
||||
- 断点弹窗自主菜单移位(main.gd:475-497 逻辑整体迁来,卡②已摘除原调用);
|
||||
- 「继续上一段冒险」接现役续玩编排(`_resume_from_menu` 同源);「开始新冒险」接现役开局编排(`_start_adventure` 同源)——冒险屏/选路屏/战斗屏零改(F7);
|
||||
- 冒险发起后营地场景屏隐藏;**终局落点不变量(v1.1 补)**:现役口径=终局 continue 回主菜单(main.gd:703)——若后续改落点为营地,营地语境可能出现未收束会话,卡⑤「返回主菜单」动线须补 `_begin_session(false)`,两卡 T0 互证;
|
||||
- `_refresh_resume_button` **不复用**(大门两分支直读 `has_breakpoint`,无钮可刷——v1.1 明示,函数随卡②删,防两卡互以为对方在处理)。
|
||||
- 取用:T2 的 `object_pressed(key: String)`、T3 的 `CampRefreshBus`、F6/F7 断点弹窗与冒险编排、Q5 已裁=甲三钮化、卡③ ConfirmDialog 契约 3。
|
||||
- [ ] 大门点击→两分支确认弹窗(裁决 5):有断点=ConfirmDialog 三出口「继续上一段冒险/开始新冒险/取消」;无断点=「是否开始新冒险:是/否」;点大门永远先出确认、不直接进冒险。
|
||||
- [ ] **三出口弹窗形态(Q5 已裁=甲)**:ConfirmDialog 三钮化+**消费方编排**(组合根单实例+待确认消费方状态量:open 前设、confirmed/cancelled 后分派;cancelled 恒=无变更退回营地;禁每次 connect/disconnect 漂移、禁双常驻静态接线——卡③ 契约 3 同款互指)。
|
||||
- [ ] 断点弹窗移位:弹窗本体与两分支逻辑自组合根摘除位迁来(卡②已摘除原调用,main.gd:118-120 注释明载归本卡大门承接;main.gd:475-497 现为选档委托、非断点弹窗——旧引文失实,v3.1 订正)。
|
||||
- [ ] 「继续上一段冒险」接现役续玩编排(`_resume_from_menu` 同源);「开始新冒险」接现役开局编排(`_start_adventure` 同源)——冒险屏/选路屏/战斗屏零改(F7)。
|
||||
- [ ] 冒险发起后营地场景屏隐藏;**终局落点不变量**:现役口径=终局 continue 回主菜单(`main_adventure_flow.gd:123-143` 终局出口 `_begin_session(false)`——原引 main.gd:703 失实,v3.1 订正)——若后续改落点为营地,营地语境可能出现未收束会话,卡⑤「返回主菜单」动线须补 `_begin_session(false)`,两卡 T0 互证。
|
||||
- [ ] 篝火回主菜单动线:命中区只含火堆(不含火焰),**必须二次确认**(camp.md v2.2 已裁,走 ConfirmDialog 先例);确认后营地层与选档层同收+开始层恢复(T3 开层互斥/关层收闭同口径)。
|
||||
- [ ] `_refresh_resume_button` **不复用**(大门两分支直读 `has_breakpoint`,无钮可刷——函数随卡②删,防两卡互以为对方在处理)。
|
||||
- 产出:大门两分支+三钮消费方编排+篝火二次确认动线;T5 按接口块验收。
|
||||
|
||||
### T5 断言、探针与 golden 迁移批(≈2–2.5d)
|
||||
### T5 断言、探针与截图迁移批(≈2–2.5d)
|
||||
|
||||
- shot_probe:camp 截图路径改(进入存档→选档→进档→营地场景图);截图自检存在且非空;**camp 段收闭改缆(v1.1 补)**:现经「关闭营地」钮收闭(:133)——营地根态无关闭钮(出口=篝火/ESC),改篝火动线或调试直改 visible;
|
||||
- **无 camp golden**(契约 10):camp 验收=shot_probe 出图+shot_checker 语义断言段(文字 WCAG/明度三档带/文字禁压纹理/文字在判据扩 camp 取样面);棋盘/menu 既有 golden 复跑;
|
||||
- F8 全量位点逐点迁移(v1.1 补列项):
|
||||
- **探针深员改指表**:ui_probe `_balance_label`/`_tree_buy_btns`/`_workshop_rows`(:39-52)、ui_click_probe `_tree_buy_btns`/`_tree_fork_btns`+三处 `main.camp_overlay.refresh_camp(main._camp_data())` 直呼(:72/99/110)、ui_struct_probe audit 根(:45)——「改挂新入口」不够,这些是成员级耦合;七命中区与笔记本命中区的探针寻址名随 T2 命名稳定化一并定;
|
||||
- **smoke_all:6469-6475 源码断言段**:三条锁四柜台等高形态本身——按新形态重写或注销(四栏等高纪律是否迁入新界面=§9 第 2 条裁后定);
|
||||
- **ui_click_probe:500-528 断点确认段**:改走大门路径断言(与 V3 合并或互指;卡② T2 明列移交、本卡 T5 接收);
|
||||
- **ui_contrast_probe:48-55 营地令牌段**:核签(新界面仍消费 `CAMP_HEADER_BAND`/`CAMP_ROW_PLATE`=段留;不再消费=段随注销);
|
||||
- **forge_workbench_test.gd 单测**:随锻造视图抽出同迁(断言零改、只换承载类);V4 复跑清单补该单测;
|
||||
- **生涯段驱动改缆**:ui_struct_probe:54-69 与 shot_probe:103-124 现经 `_start_overlay`「生涯」钮驱动——改挂后须合成点击场景笔记本命中区;命中区若为 TextureRect/Area2D 而非 Button,`ProbeUiUtil.find_button` 失效,寻址面随 T2 扩展;
|
||||
- **screen_flow_probe 营地键新语义**::53 改指新场景根句柄;子界面开态时键真值按 T2 叠层规则派生;:119-125 回退段改新场景根 closed 信号或篝火动线;
|
||||
- **多卡共改文件分界**:screen_flow_probe(入口路径段归卡②、键语义/开合/篝火段归本卡)、ui_click_probe(入口路径归卡②、槽操作路径归卡③、营地段+断点确认段归本卡)——每条探针注分界;
|
||||
- 新增断言段:七物件逐一可达对应界面;大门两分支;点大门不直接进冒险;**点大门→开局后营地场景屏隐藏(v1.1 补)**;图鉴=占位挂起态;场景根态无文字按钮列表/无四柜台面板(反参照纪律断言);
|
||||
- 美术约束机检:shot_checker 现役段扩 camp 取样;截屏级 48 色计数是否新建=§9 第 5 条拍板(依赖样张批规格清单,随 Q6 一并裁);
|
||||
- **变异验证伴生**:某物件命中区漏绑(预期可达性断言红)、大门直连开局绕过确认(预期分支断言红)、图鉴内容激活(预期挂起态断言红)、**presenter 某写口尾漏挂新界面刷新**(预期余额/HUD 不同步红——v1.1 补)、**三钮弹窗取消被接成写档动作**(预期无变更退回红——v1.1 补);注入→最窄闸见红→精确还原→记录。
|
||||
- 取用:T2/T3/T4 全部产物、F8 全量探针位点、F17 机检工具面、契约 10。
|
||||
- [ ] shot_probe:camp 截图路径改(进入存档→选档→进档→营地场景图);截图自检存在且非空;**camp 段收闭改接**:现经「关闭营地」钮收闭(:133)——营地根态无关闭钮(出口=篝火/ESC),改篝火动线或调试直改 visible。
|
||||
- [ ] **无 camp golden**(契约 10):camp 验收=shot_probe 出图+shot_checker 语义断言段(文字 WCAG/明度三档带/文字禁压纹理/文字在判据扩 camp 取样面);棋盘/menu 既有 golden 复跑。
|
||||
- [ ] F8 全量位点逐点迁移:
|
||||
- [ ] 探针深员改指表:ui_probe `_balance_label`/`_tree_buy_btns`/`_workshop_rows`(:39-52)、ui_click_probe `_tree_buy_btns`/`_tree_fork_btns`+三处 `main.camp_overlay.refresh_camp(main._camp_data())` 直呼(:72/99/110)、ui_struct_probe audit 根(:45)——「改挂新入口」不够,这些是成员级耦合;七命中区与笔记本命中区的探针寻址名随 T2 命名稳定化一并定。
|
||||
- [ ] theme_projection_test.gd:710-717 四柜台等高源码断言段(`_smoke_element_details`)+system_test.gd V2 实例化(:67-68)/V3g 模态开合(:520-541):四柜台等高三条锁按新形态重写或注销(四栏等高纪律是否迁入新界面=§9 第 2 条裁后定)——`game/smoke/smoke_all.gd` 已退役,v3.0 旧位点作废。
|
||||
- [ ] ui_click_probe:500-528 断点确认段:改走大门路径断言(与 V3 合并或互指;卡② T2 明列移交、本卡 T5 接收)。
|
||||
- [ ] ui_contrast_probe:48-55 营地令牌段:核签(新界面仍消费 `CAMP_HEADER_BAND`/`CAMP_ROW_PLATE`=段留;不再消费=段随注销)。
|
||||
- [ ] forge_workbench_test.gd 单测:随锻造视图抽出同迁(断言零改、只换承载类);V4 复跑清单补该单测。
|
||||
- [ ] 生涯段驱动改接:ui_struct_probe:54-69 与 shot_probe:103-124 现经 `_start_overlay`「生涯」钮驱动——改挂后须合成点击场景笔记本命中区;命中区若为 TextureRect/Area2D 而非 Button,`ProbeUiUtil.find_button` 失效,寻址面随 T2 扩展。
|
||||
- [ ] screen_flow_probe 营地旗标新语义::54 改指新场景根句柄(:53 是菜单行,v3.1 订正);子界面开态时键真值按 T2 叠层规则派生;:119-125 回退段改新场景根 closed 信号或篝火动线。
|
||||
- [ ] 多卡共改文件分界:screen_flow_probe(入口路径段归卡②、键语义/开合/篝火段归本卡)、ui_click_probe(入口路径归卡②、槽操作路径归卡③、营地段+断点确认段归本卡)——每条探针注分界;分界锚=断言段语义/段名,不锚行号(一侧插删行会平移另一侧行号,2026-11-01 双审补立)。
|
||||
- [ ] 新增断言段(**正向验收断言,从真实入口进入、判据绑被测改动特有的可观测提交面**):七物件逐一可达对应界面;大门两分支;点大门不直接进冒险(绑 `confirmed` 信号与 `route_chosen` 未发);点大门→开局后营地场景屏隐藏;图鉴=占位挂起态;场景根态无文字按钮列表/无四柜台面板(反参照纪律断言);presenter 七写口尾→现开界面刷新+场景根 HUD 刷新一闸(绑刷新单口调用记录与 HUD 状态源取值);三钮弹窗 cancelled 恒=无变更退回营地(绑写口零调用+营地根 visible)。**判据绑不上=静态判据+评审注记申报观测面局限,证明做不出=停卡呈报;断言只在当轮真实跑批执行并通过,不做注入破坏。**
|
||||
- [ ] 美术约束机检:shot_checker 现役段扩 camp 取样;截屏级 48 色计数是否新建=§9 第 4 条拍板(**条件步**:§0.3-3⑤环境子板色值已按 T1 量化登记→拍板;未登记→本子步显式跳过+T6 登记遗留,不阻塞批末闸)。
|
||||
- [ ] **新增两条 headless 断言(2026-11-01 godot-master 审查补立)**:①`ui_struct_probe` camp 场景层序段——camp_scene_view 子节点序=layout 表 z 序逐层相等、各层纹理矩形与表逐值一致(本批核心视觉不变量的机器验收面);②`camp_scene_layout.tres` 加载期校验断言——载入数据件断言校验通过+七命中区几何(两两不相交/不越画面/不侵 y≤48),落 `game/tests/unit/presentation/camp_scene_layout_test.gd:_test_layout_hotspot_geometry`(新建单测件,随套件 manifest 登记)。
|
||||
- 产出:探针/断言迁移留痕表(F8 全量位点逐点「改前→改后」)+新增断言段;T6 收口。
|
||||
|
||||
### T6 产出物落位卡(≈0.5d,末卡)
|
||||
|
||||
三份 `reports/` 写齐(实现报告含方向稿选划记录三段表、探针迁移留痕表、变异记录表、目检总清单/代拍板追认单/反哺点清单)。
|
||||
- 取用:T0–T5 全部产物。
|
||||
- [ ] 里程碑步(波/T 段)出口跑一次代码域全套闸:`bash tools/run_all_gates.sh --code`;回执 `all_green=true`;任何新增红停卡,不得带红合回。波内修复只做出红最小单元的最窄确认;新增测试/工具先行孤立验证再并入全套。
|
||||
- [ ] 独立验收审计由未参与实现的会话/子代理执行,产出 `reports/iter<N>-验收审计-<日期>.md`;未过禁合并(审计入口见 §7)。
|
||||
- [ ] 轻闸全绿、合并前写齐三份 `reports/`:
|
||||
- [ ] `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务+实现决定记录/代拍板追认单+探针迁移留痕表+方向稿选划留痕〔已由 2026-10-31 裁定〕);
|
||||
- [ ] `reports/iter<N>-反哺点-<日期>.md`(§5 每条有落点:文件/节/原句/拟改句/依据,明确「实现批未写 design/」);
|
||||
- [ ] `reports/iter<N>-验收审计-<日期>.md`(独立审计逐行「通过/缺漏/例外及依据」)。
|
||||
- [ ] 用户目检项登记 07 清单(场景观感/七物件辨识与点选反馈/大门两分支/篝火动线),登记完成即卡内义务了结,不阻塞批末收尾/合并/下一迭代开工。
|
||||
- 产出:三份 `reports/` 齐全+代码域闸全绿+验收审计通过,方算收口;合回后由本地 CI 全量域闸复验。
|
||||
|
||||
**估时合计 ≈9–12d**(v1.1 上调:presenter 承接面+三钮弹窗+坏槽态联动+探针深员面增量)。**天然切分点=T1 出口**(场景素材归档齐)——美术线与代码线可分批交付(用户批次归属裁);若单批跑超迭代盒,T3 末(四柜台拆解完)为次切分点。
|
||||
**估时合计 ≈9–12d**(presenter 承接面+三钮弹窗+探针深员面增量;T1 为素材录入、美术创作主体归美术自立批已完成)。
|
||||
|
||||
## 7. 验证闸(逐需求点验收清单)
|
||||
## 7. 验证闸:改动—验收对照表
|
||||
|
||||
| # | 需求点 | 测什么 | 落点/判定手段 |
|
||||
|---|---|---|---|
|
||||
| V1 | 场景图形态(机检半) | camp 截图=场景图形态(非四柜台覆盖层) | shot_probe camp 截图+shot_checker 结构禁词(与 V9 同)——**「七要素可辨(构图)」归 V11 目检**(v1.1 拆半:shot_checker 不判构图) |
|
||||
| V2 | 七物件可达性 | 点每物件→对应界面可见+营地场景按 T2 叠层规则退避 | ui_click_probe 七段合成点击(新断言;命中区寻址面随 T2) |
|
||||
| V3 | 大门两分支+开局收闭 | 有断点=弹二选一;无断点=弹是否开始;点大门不直接进冒险;点确认开局后营地场景屏隐藏 | 探针合成点击+状态断言(夹具经调试接口直写/清断点) |
|
||||
| V4 | 五界面功能+写口链零回归 | 树购买/Fork 切换/锻造三意图/秘宝购买/同调装配/图鉴占位——既有断言全链复跑绿;presenter 各写口尾的 MetaProgression 原语调用序列不变(v1.1 扩) | ui_click_probe 迁移段+smoke `_face_snapshot` 段+forge_workbench_test 迁移复跑 |
|
||||
| V5 | 生涯只读契约保持 | 生涯页经笔记本可达;页内零跳转零动效契约不破(三契约只约束页子树、不依赖入口位置——v1.1 证实原样成立) | ui_struct_probe career 段迁移复跑 |
|
||||
| V6 | 图鉴挂起态 | 布告板→图鉴=占位态;内容零激活 | 探针禁词/禁行断言(锚定新视图占位标签) |
|
||||
| V7 | 篝火动线 | 点篝火→营地隐藏+选档层同收+主菜单可见 | screen_flow_probe 型断言 |
|
||||
| V8 | 顶栏预留位 | 场景屏顶栏 HUD 位存在(卡⑤联验填值+刷新单口挂接) | 结构断言+跨卡联验 |
|
||||
| V9 | 反参照纪律 | 营地根态无文字按钮列表、无四柜台面板 | ui_struct_probe 结构禁词断言 |
|
||||
| V10 | 美术约束 | 文字 WCAG/明度三档带/文字禁压纹理底;素材色板(palette_check/standee 二器或截屏计数若建) | shot_checker 段扩 camp 取样+§9 第 5 条拍板面 |
|
||||
| V11 | 目检项登记 | 场景观感/七物件辨识(纵深稿构图可辨)/点选反馈/大门两分支/篝火动线入 07 | 件核(登记不阻塞,澄清⑥) |
|
||||
| V12 | 探针迁移留痕核验 | F8 全量位点逐点有「改前→改后」记录(契约 10 留痕义务) | 件核对表(T6 追认单) |
|
||||
| V13 | 反哺点清单 | §5 每条有落点 | 件核对表 |
|
||||
七格表:需求点|改动(文件:行/行为)|验收测试(名称+落点+尾缀)|层级(单元/接口/集成/探针)|真实入口|判据绑定面|能力行。**每个需求点至少一行;每个行为改动至少一行(V1–V13 需求点行+B1–B2 行为改动专行);每行必须有真实入口和判据绑定面——缺任一项=该需求点未验收,卡片不成立。** 断言尾缀〔营地图批 Vn〕/〔营地图批 Bn〕,与卡片清单互相 grep。能力行格式 `既有:<能力名>`/`新增:<能力名>`,两形态名称都须在 `game/tests/README.md` 能力表在场(既有=现役在场名,新增=本卡定名、由主 Agent 统一收口入表;本卡只写卡内能力行文字,README 由主 Agent 收口)。
|
||||
|
||||
**测试方式两分法**:V1–V10、V12 机检自动化(能力验证经调试接口直改断点/钥石/解锁状态,沿用 DebugOverlay/DS_DEBUG 门控);V11 归用户目检(生命周期末步统一进行,不阻塞迭代)。
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 场景图形态(机检半) | `camp_scene_view.gd` 10 层铺底(z0 不透明全幅含门洞小路/z1 透明细条 (0,240,1280,70) 门洞缺口/z2–z6);`camp_screen.gd` 改挂;`shot_probe.gd` camp 段 | `shot_probe.gd:_ready`(camp 截图段)〔营地图批 V1〕+`ui_struct_probe.gd:_ready`(camp 场景层序段)〔营地图批 V1〕+`shot_checker.py` 结构禁词段 | 探针 | 进入存档→选档→进档→营地场景屏(shot_probe 驱动) | camp 截图出图非空+shot_checker 结构禁词命中(无四柜台覆盖层/无文字按钮列表);camp_scene_view 子节点序=layout 表 z 序逐层相等、各层纹理矩形与表逐值一致(2026-11-01 双审补立——核心视觉不变量机器验收面);「七要素可辨(构图)」归 V11 目检 | 新增:营地场景图十层铺底与门洞留空呈现 |
|
||||
| V2 七物件可达性+叠层退避 | `camp_scene_view.gd` 七命中区+`object_pressed` 信号;`camp_scene_layout.tres` 坐标键;组合根开界面 | `ui_click_probe.gd:_ready`(七物件合成点击段)〔营地图批 V2〕 | 探针 | 命中区 `_gui_input()` → `object_pressed(key)` → 组合根开界面 | 七物件各自对应界面 visible 断言+场景根按 T2 叠层规则退避(树序/可见性);绑 `object_pressed` 信号发射面 | 既有:模态覆盖层编排与输入锁;新增:营地七物件命中区点选与分界面可达 |
|
||||
| V3 大门两分支+开局收闭 | `main.gd:475-497` 断点弹窗移位;`confirm_dialog.gd` 三钮化;消费方编排 | `ui_click_probe.gd:_ready`(大门两分支段/断点确认段 :500-528)〔营地图批 V3〕 | 探针 | 大门命中区 → ConfirmDialog(三出口)→ `main._on_confirm_new_adventure()`/`_resume_from_menu()` | 有断点三出口/无断点两出口;点大门不直接进冒险(绑 `confirmed` 信号与 `route_chosen` 未发);cancelled 绑写口零调用+营地根 visible;开局后营地场景屏隐藏 | 既有:断点片段往返与续玩幂等;新增:营地大门两分支确认与断点续玩接线 |
|
||||
| V4 五界面功能+写口链零回归 | 四界面 view 抽出;`camp_shop_presenter.gd` 承接面;`camp_flow_context.gd` 字段;`forge_workbench_test.gd` 迁移 | `forge_workbench_test.gd:_test_forge_rows`〔营地图批 V4〕+`ui_click_probe.gd:_ready`(写口链段)+`smoke_all.gd:_face_snapshot` 段 | 单元+探针 | 五界面意图信号(`buy_requested`/`tree_buy_requested`/`forge_*`)→ CampShopPresenter → MetaProgression 写口 | 树购买/Fork 切换/锻造三意图/秘宝购买/同调装配既有断言全链复跑绿;presenter 各写口尾 MetaProgression 原语调用序列不变(绑调用序列,不绑终态兜底) | 既有:元进度入账与锻造两轴;新增:四柜台拆解后五界面入口改挂与写口零回归 |
|
||||
| V5 生涯只读契约保持 | `career_overlay.gd` 入口改挂笔记本;`ui_struct_probe.gd`/`shot_probe.gd` 生涯段驱动改接 | `ui_struct_probe.gd:_ready`(career 段)〔营地图批 V5〕 | 探针 | 笔记本命中区 → `CareerOverlay.open()` | 生涯页经笔记本可达+页内零跳转零动效契约三断言不破(三契约只约束页子树、不依赖入口位置);返回钮文案禁含「营地」 | 新增:营地篝火回主菜单与笔记本改挂动线 |
|
||||
| V6 图鉴挂起态 | `codex_view.gd` 占位壳;布告板命中区 | `ui_struct_probe.gd:_ready`(占位禁词/禁行段)〔营地图批 V6〕 | 探针 | 布告板命中区 → 图鉴占位视图 | 锚定新视图占位标签(现行锁文「柜台结构位预留」称谓随形态订正)+内容零激活禁行断言;不是禁用态 | 新增:营地图鉴占位挂起态与反参照结构纪律 |
|
||||
| V7 篝火动线(二次确认) | `main.gd` 篝火回主菜单+开层互斥/关层收闭;`screen_flow_probe.gd` 段 | `screen_flow_probe.gd:_ready`(篝火/开合段)〔营地图批 V7〕 | 探针 | 篝火命中区(只下半火堆)→ ConfirmDialog 二次确认 → 主菜单 | 二次确认弹窗 `confirmed` 信号先于关层;确认后营地隐藏+选档层同收+主菜单可见(绑可见性/层序);命中区只含火堆不含火焰 | 新增:营地篝火回主菜单与笔记本改挂动线 |
|
||||
| V8 顶栏预留位+刷新单口 | `camp_scene_view.gd` 顶栏 HUD 位;`camp_refresh_bus.gd` 新建;presenter 七写口尾改路由 | `ui_struct_probe.gd:_ready`(顶栏位段)〔营地图批 V8〕+`ui_click_probe.gd:_ready`(写口尾刷新段) | 探针 | 写口尾 → `CampRefreshBus.refresh(camp_data)` → 现开界面刷新+场景根 HUD 刷新 | 场景屏顶栏 HUD 位存在(结构断言,高 48px 浮层);各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成(绑刷新单口调用记录与 HUD 状态源取值);卡⑤ 联验填值 | 新增:营地刷新单口与场景根 HUD 同步 |
|
||||
| V9 反参照纪律 | `camp_scene_view.gd` 根态;`camp_overlay_view.gd` 退役;`smoke_all.gd:6469-6475` 源码断言段 | `ui_struct_probe.gd:_ready`(结构禁词段)〔营地图批 V9〕 | 探针 | 营地根态场景树审计 | 营地根态无文字按钮列表、无四柜台面板(结构禁词断言);四柜台等高源码断言按新形态重写或注销(迁移留痕) | 新增:营地图鉴占位挂起态与反参照结构纪律 |
|
||||
| V10 美术约束 | `tools/shot_checker.py` camp 取样面;`ui_contrast_probe.gd` 令牌核签;素材色板二器 | `ui_contrast_probe.gd:_ready`(营地令牌段)〔营地图批 V10〕+`shot_checker.py` WCAG/明度三档带段 | 探针(机检) | camp 截图+素材文件(`palette_check.py`/`standee_asset_check.py`) | 文字 WCAG ≥4.5:1/明度三档带/文字禁压纹理底/文字在判据;素材色板(48 令牌+新增色双门/像素 32 色冻结);§9 第 4 条拍板截屏计数面(条件步,§0.3-3⑤) | 既有:UI 全局主题与按钮五态(含对比度锚);新增:营地场景图十层铺底与门洞留空呈现 |
|
||||
| V11 目检项登记 | `reports/iter<N>-实现报告-<日期>.md` 实机目检总清单 | 件核对(07 清单登记,登记不阻塞)〔营地图批 V11〕 | 用户目检(非自动化) | 用户实机(生命周期末步统一进行,不阻塞迭代) | 场景观感/七物件辨识(纵深稿构图可辨)/点选反馈/大门两分支/篝火动线入 07 清单=登记即义务了结 | 新增:营地场景图十层铺底与门洞留空呈现 |
|
||||
| V12 探针迁移留痕核验 | F8 全量位点逐点迁移 | 件核对表(T6 追认单)〔营地图批 V12〕 | 件核对(非自动化) | 探针脚本 diff 对账 | F8 全量位点逐点有「改前→改后」记录(契约 10 留痕义务) | 既有:模态覆盖层编排与输入锁;新增:营地七物件命中区点选与分界面可达 |
|
||||
| V13 反哺点清单 | §5 每条有落点 | 件核对表〔营地图批 V13〕 | 件核对(非自动化) | §5 同步面逐条对账 | §5 每条有 `reports/iter<N>-反哺点-<日期>.md` 落点 | 既有:模态覆盖层编排与输入锁 |
|
||||
| V14 layout 数据件校验(2026-11-01 新增) | `camp_scene_layout.tres` 加载期 fail-fast 校验(schema 类+表现域容器类型化成员,先例 battle_layout.tres) | `camp_scene_layout_test.gd:_test_layout_hotspot_geometry`(新建单测件 `game/tests/unit/presentation/`,随套件 manifest 登记)〔营地图批 V14〕 | 单元 | 单元测试载入 `camp_scene_layout.tres` | 校验函数通过+七命中区两两不相交/不越画面/不侵 y≤48(契约 7 几何红线机检化) | 既有:模态覆盖层编排与输入锁;新增:营地场景图十层铺底与门洞留空呈现 |
|
||||
| B1 组合根接线总改造(行为改动专行) | `main.gd`(断点弹窗移位+三钮消费方编排+营地入口改挂+篝火动线+开层互斥);`confirm_dialog.gd` 三钮;`camp_shop_presenter.gd` 写口尾路由;`camp_flow_context.gd` 字段;`camp_refresh_bus.gd` 单口 | `ui_click_probe.gd:_ready`(断点确认段)+`screen_flow_probe.gd:_ready`(篝火段)〔营地图批 B1〕 | 集成 | `_on_confirm_new_adventure()`/`_resume_from_menu()`/`_start_adventure()`/`CampRefreshBus.refresh()` | 三钮消费方编排:open 前设消费方、confirmed/cancelled 后分派;cancelled 绑写口零调用;写口尾绑 `CampRefreshBus.refresh()` 调用记录+现开界面+场景根 HUD 同刷;`on_camp_attune_toggle` 尾只 refresh_camp 保持原样 | 既有:断点片段往返与续玩幂等;新增:营地刷新单口与场景根 HUD 同步 |
|
||||
| B2 CampOverlay 退役+五界面抽出(行为改动专行) | `camp_overlay_view.gd` 退役;四界面 view 抽出;`career_overlay.gd` 入口改挂;`smoke_all.gd:6469-6475` 源码断言段 | `smoke_all.gd:_ready`(源码断言段重写)+`forge_workbench_test.gd:_test_forge_rows`(承载类换)〔营地图批 B2〕 | 集成 | 五界面 `open()`/`close()` → 模态栈单口 | CampOverlay 无消费面即删(吸收即死,绑 `main.camp_overlay` 句柄迁移留痕);四界面各带返回/关闭钮;拆分沿职责轴(契约 11:职责语句/单一变化原因/集体身份,禁凑计数袋) | 既有:元进度入账与锻造两轴;新增:四柜台拆解后五界面入口改挂与写口零回归 |
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码/断言/终稿素材 → `--code`)全绿才合并;MC 零跑(零数值零行为——纯呈现+承载迁移)。
|
||||
**落点集中**:V1/V9/V11/V12 → T5/T6;V2/V8 → T2/T3;V3/V7 → T4;V4/V5/V6 → T3;V10 → T5;V13 → T6;V14 → T2/T5;B1 → T3/T4;B2 → T3/T5。全表名实核对归 T6。
|
||||
|
||||
**测试方式两分法**:V1–V10、B1–B2 机检自动化(能力验证经调试接口直改断点/钥石/解锁状态,沿用 DebugOverlay/DS_DEBUG 门控,不走正常资源积累流程);V11 归用户目检(生命周期末步统一进行,不阻塞迭代);V12/V13 为件核对表(非自动化,T6 追认单)。**用户目检一律「登记不阻塞」**:登记完成即卡内义务了结,验收时间由用户自行安排,不得作为批末收尾、合并或下一迭代开工的前置条件。
|
||||
|
||||
**判据绑定面口径**:每个行为改动的验收断言从真实入口进入、判据绑被测改动特有的可观测提交面(信号/事件/入参/状态源取值/绘制记录/可见性/树序),在当轮真实跑批中执行并通过(2026-10-24/2026-10-28 用户两裁:断言从真实入口进、判据钉可观测提交面,人为制造红属伪造证据)。判据绑不上(纯防御分支、release 独有位)=静态判据+评审注记申报观测面局限,**证明做不出=停卡呈报**。配判据按强弱分层配齐:运行期断言(可见性/树序/状态源取值)抓状态类缺陷,golden 逐像素抓内容观感类缺陷(本卡无 camp golden,用 shot_checker 语义断言段+目检),占比/统计类判据只兜「整框空白/整框糊」、是补充不是主力(细则=test-conventions §8.4)。
|
||||
|
||||
**测试削弱申报**(实现期未申报的削弱由独立验收审计判红):允许改/删的旧断言逐条列——
|
||||
|
||||
| 旧断言 | 允许处置 | 理由 |
|
||||
|---|---|---|
|
||||
| `smoke_all.gd:6469-6475` 三条锁四柜台等高形态源码断言(`Vector2(0,336)`×3/`SIZE_FILL`×4/`SIZE_SHRINK_CENTER`≥3) | 按新形态重写或注销 | CampOverlay 四柜台形态退役,旧形态断言失去对象(吸收即死,不保留无效断言) |
|
||||
| `ui_contrast_probe.gd:48-55` 营地令牌段 | 核签后段留或随注销 | 新界面若不再消费 `CAMP_HEADER_BAND`/`CAMP_ROW_PLATE` 则段标签虚挂;仍消费则保留 |
|
||||
| `ui_click_probe.gd:500-528` 断点确认段(经 lvbtn 驱动) | 改走大门路径断言 | 卡②撤钮+本批弹窗移位双杀,改挂大门命中区(与 V3 合并或互指) |
|
||||
| `ui_probe.gd:39-52` 深员(`_balance_label`/`_tree_buy_btns`/`_workshop_rows`) | 改指新界面/场景根成员 | 成员级耦合随四柜台拆解迁移,寻址名随 T2 稳定化 |
|
||||
| `screen_flow_probe.gd:53`/:109-125 营地键/回退段直呼 `main.camp_overlay` | 改指新场景根句柄/closed 信号 | 句柄迁移到新场景根(F2/F8) |
|
||||
| `shot_probe.gd:126-133` camp 收闭段经「关闭营地」钮 | 改篝火动线或调试直改 visible | 营地根态无关闭钮(出口=篝火/ESC) |
|
||||
| `ui_struct_probe.gd:54-69`/`shot_probe.gd:103-124` 生涯段经 `_start_overlay`「生涯」钮 | 合成点击笔记本命中区 | 入口改挂帐篷旁笔记本,驱动改接 |
|
||||
| `forge_workbench_test.gd:33` 直载 `camp_overlay_view.gd` | 随锻造视图抽出同迁,断言零改只换承载类 | 承载类迁移,行板/写口逻辑复用(契约 4) |
|
||||
|
||||
**禁止削弱声明**:除上列外,实现期不得删除断言、放宽阈值、缩小扫描面、把真实入口换成下层直调。五界面写口链(V4)、生涯只读契约(V5)、图鉴挂起态(V6)、美术约束(V10)的既有断言全链复跑绿,禁削弱;棋盘三张+menu_boot 既有 golden 逐像素复跑、不放宽容差;不新建 camp golden(契约 10,若新建须另裁 L3)。
|
||||
|
||||
**独立验收审计**:合并前由未参与实现的会话/子代理执行,入口为审计只读本卡、实际对照表、diff、测试文件与实现报告,自跑最窄测试抽查、不采信报告「已全绿」;至少四件事——①逐行核对照表(测试存在、真的执行、尾缀能 grep 到、真实入口没有绕过改动点)②抽查判据绑定面(绑被测改动特有可观测提交面、当轮真实跑批执行过)③检查测试削弱(删除断言/放宽阈值/缩小扫描面/换下层直调逐处要本卡申报或用户批准)④核内部字段直查的受控例外(该行为无公开可观测面可钉时已在对照表注明理由)。报告落 **`reports/iter<N>-验收审计-<日期>.md`**,逐行「通过/缺漏/例外及依据」,未通过不得合并。测试执行两入口铁规不变:审计在迭代 loop 内启动的子代理算 loop,另开会话须用户点名。
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码/断言/终稿素材 → `--code`)全绿才合并;MC 零跑(零数值零行为——纯呈现+承载迁移)。卡内不写死闸件名单,域划分唯一出处在 `tools/run_all_gates.sh`。回归措辞按模板:里程碑步(波/T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试/工具先行孤立验证再并入全套。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救)**:断言写法、探针路径、命中区微调、命名。
|
||||
- **L2(记录继续)**:场景屏承载形态选择(§9 域内)、点选反馈参数(Q3 设计稿域内)、四界面承载形态(§9 域内)、方向稿张数在 2–3 内自选、图鉴占位文案称谓订正。
|
||||
- **L3(停机呈报)**:Q1–Q6 未裁即需开工对应任务段;规格清单(样张批)晚于本批且 Q6 未裁;golden 漂移越出预期面(若涉及既有棋盘/menu 基线);发现五界面任一功能/写口链路被意外触碰(契约 4 破);参照物到手后与纵深稿结构性冲突(呈报,禁批内自行改构图);美术渠道 A/B 执行中撞墙(如 CC0 改色达不到七物件可辨性);提议新建 camp golden(契约 10——须另裁)。
|
||||
- **L1(自救一次)**:断言写法、探针路径、命中区微调、命名、编译错误、签名拼写、测试套件 manifest 登记遗漏。
|
||||
- **L2(记录继续;同一红连续三次升 L3)**:场景屏承载形态选择(§9 域内)、点选反馈参数(§0.3 P3 设计稿域内)、四界面承载形态(§9 域内)、图鉴占位文案称谓订正、行数较 T0 预期漂移但 S13 键不升。
|
||||
- **L3(立即停卡写报告等用户)**:
|
||||
- §0.3 代码段开工门裁断实况反转(四项 2026-11-01 已裁断销号;开工时发现未销号实况=停机呈报);
|
||||
- 规格清单(样张批)终档置换未按反哺点登记;
|
||||
- golden 漂移越出预期面(若涉及既有棋盘/menu 基线);
|
||||
- 发现五界面任一功能/写口链路被意外触碰(契约 4 破);
|
||||
- 参照物与纵深稿结构性冲突再现(P4 已补、冲突已呈报,禁批内自行改构图);
|
||||
- 美术渠道 B 执行中撞墙(插画级新产达不到七物件可辨性);
|
||||
- 提议新建 camp golden(契约 10——须另裁);
|
||||
- 任何验收行无法从真实入口绑定特有可观测提交面(证明做不出=停卡呈报);
|
||||
- **机检预算互斥=上游缺陷停卡退回写卡段**:实现规范预算超默认未走指标例外申报、或 S13 键较登记值上升、或机检键/计数与卡片契约互斥——一律停该卡呈报退回写卡段,禁为凑绿保留无效成员、造第三条路或内联求裁(契约 12/第 13 节合流);
|
||||
- **拆分边界**:按契约 11 拆不出内聚单元(只能凑计数袋/沿阈值轴硬拆)=停卡呈报走豁免/还债通道,禁硬拆凑绿。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
> 篝火二次确认已由 camp.md v2.2 裁定为**必须**(走 ConfirmDialog 先例),不属代拍板域。
|
||||
|
||||
1. 场景屏承载形态(camp_screen 屏壳改造 vs 新屏壳)——约束:`LAYER_CONTRACT` 更新+探针挂载面迁移留痕。
|
||||
2. 五界面拆出后的承载形态(独立全屏页 vs 单一内页切换)+**叠层规则派生**(v1.1:独立全屏页=场景根隐藏/单一内页切换=场景根留底;写入 V2 断言;「缩放虚化」禁自选)。
|
||||
3. 七物件命中区实现(TextureRect 分区 vs 其他)与点选反馈参数(Q3 裁定域内)。
|
||||
4. 篝火是否需二次确认——默认直回(营地每笔消费即时写盘,无未保存风险——存档序列化.md:121);如判需确认,走 ConfirmDialog 先例。
|
||||
5. 截屏级 48 色计数工具是否新建及落点(现役=素材级二器,F17;截屏级依赖样张批规格清单,随 Q6 一并裁)。
|
||||
6. CampOverlay 类退役范围(整类删 vs 留片段)——约束:吸收即死,禁留死代码。
|
||||
7. 提交切分与 worktree 分支命名。
|
||||
2. 五界面拆出后的承载形态(独立全屏页 vs 单一内页切换)+**叠层规则派生**(独立全屏页=场景根隐藏/单一内页切换=场景根留底;写入 V2 断言;「缩放虚化」禁自选)。
|
||||
3. 七物件命中区实现(TextureRect 分区 vs 其他)与点选反馈参数(§0.3 P3 设计稿域内)。
|
||||
4. 截屏级 48 色计数工具是否新建及落点(现役=素材级二器 F17;**条件步**——§0.3-3⑤环境子板色值已按 T1 量化登记则拍板,未登记则本子步显式跳过+T6 登记遗留)。
|
||||
5. CampOverlay 类退役范围(整类删 vs 留片段)——约束:吸收即死,禁留死代码。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 10. 裁点与设计议程(v2.1:本卡在批内,以下为前置解决议程)
|
||||
超出必须呈报:任何数值、像素规格、事件名、命中区绑定目标、文件职责、T 卡验收落点、五界面写口链路、拆分单元职责。
|
||||
|
||||
> Q4/Q5/Q6/Q8 已裁(裁定见卡头);Q1/Q2 仍待用户。本节约等于批内设计段的工作清单。
|
||||
## 10. 待裁点:零待裁点,开工门=U0
|
||||
|
||||
### Q1 · 营地场景图的美术渠道(**仍待用户裁——批内第一议程,阻塞 P2 规格分支与方向稿**)
|
||||
**本卡设计待裁点清零,开工门=U0。** Q1/Q2/Q4/Q5/Q6/Q8 均已裁(下列为现行裁定留痕,非待裁点,实现期禁自行改值):
|
||||
|
||||
**背景**:澄清⑧2——「美术渠道 A/B 未裁:CC0 改色 vs 插画级新产——原贴图批的渠道问题对营地场景图同样成立,本讨论未裁决」。
|
||||
- **Q1 美术渠道=B·插画级新产**(2026-10-22 用户裁):✦ 渠道作者自跑远程生图管线;规格沿用 art-bible §6.2/§8.1/§8.2 既有裁决(art-assets-checklist.md §3.5)。
|
||||
- **Q2 参照物=已补**(2026-10-23):`artwork/2026-10-23-营地场景/Gemini_Generated_Image_ykrc0nykrc0nykrc.jpg`,已进 camp-scene-prompts.md §1 登记为「作者提供的参照物」,与纵深稿对过表、围墙结构冲突已呈报;参照物只是参考图,保留 `artwork/`、不入 git、不入游戏目录。
|
||||
- **Q4 营地底栏「最近冒险」条带去留=甲**(2026-10-18):随四柜台拆解一并撤下,逐局历史回看整体归生涯(期二承接);生涯.md P6 同批注销(反哺点)。
|
||||
- **Q5 大门「有断点」分支三出口弹窗=甲**(2026-10-18):ConfirmDialog 扩第三钮(继续上一段冒险/开始新冒险/取消,cancelled 恒=无变更退回营地);卡③ 契约 3 已按三钮化落定(删档确认只用取消/确认)。
|
||||
- **Q6 Q1 与规格清单缺口连带裁=甲**(2026-10-18):过渡尺寸档=现役 1280 工程档(1280×800)+烛光黄 #F6C667/木色 #8A5A34;渠道 B 已裁(2026-10-22),规格落 art-bible v3.31 §6.2/art-assets-checklist.md §3.5(2× 源档出图、最近邻降采样到 1280×800);终档归样张批冻结清单后置换(反哺点登记),执行归用户线。
|
||||
- **Q8 设计稿产出方式=甲**(2026-10-18):批内出稿呈裁(方向稿先行纪律照旧——方向稿选划已由 2026-10-31 样张过审裁定完成,各素材第一张样本稿为终稿、保留全部待选稿)。
|
||||
|
||||
**我的推荐:A · CC0 改色**(理由:06:23 已有配色规格〔烛光黄 #F6C667 配木色 #8A5A34〕与渠道合法性记载、零新绘成本、方向稿不受「从零构图」拖累;B 依赖尚未冻结的规格清单且周期长)。用户此前令不代裁——推荐已摊开,等你一句 A 或 B,方向稿(T1 防不满意工序)当天可出。
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| A · CC0 改色 | 通用场景素材改色复用(06:23 载明该渠道对营地件合法:烛光黄 #F6C667 配木色 #8A5A34 已有规格) | 快、零新绘成本;上限=改色件构图不完全贴合七物件+纵深稿,可能要容忍「近似构图」 |
|
||||
| B · 插画级新产 | 按纵深稿全新绘制(像素级) | 七物件与构图完全受控;成本高、周期长,且依赖规格清单输入(F16)——须走「等样张批规格清单」或用户另裁过渡规格 |
|
||||
|
||||
连带:渠道裁定影响 T1 估时(A 趋下限、B 趋上限)与切分点位置;「归建卡时或样张批规格语境下裁」(澄清⑧2 原文)。
|
||||
|
||||
### Q2 · 参照物(**待补输入**)
|
||||
|
||||
澄清②/§⑧1:用户原话「目前还没有一个参照,我可以后续找一下」——参照物找到后补充进本卡(与纵深稿对表,结构性冲突=呈报,禁批内自行改构图)。
|
||||
|
||||
### Q3 · 物件点选反馈细节(设计稿产出方式已裁=批内出稿呈裁)
|
||||
|
||||
澄清⑧3:「悬停高亮/提示文案/可点区域判定」未谈。三项形态归设计稿阶段——**产出方式已裁(Q8=批内出稿呈裁,方向稿先行纪律照旧)**;设计稿只覆盖根态点选反馈,不覆盖进界面后的叠层规则(叠层=随承载形态裁定派生:独立全屏页=场景根隐藏/单一内页切换=场景根留底;「缩放虚化」是第三形态,涉新演出面+素材,禁批内自选)。
|
||||
|
||||
### Q4 · 营地底栏「最近冒险」条带去留(**已裁=甲**)
|
||||
|
||||
**裁定(2026-10-18)**:甲——随四柜台拆解一并撤下,逐局历史回看整体归生涯(期二承接);生涯.md P6 同批注销(反哺点)。代拍板留痕(用户令「事项按推荐的来」,本项无推荐案,取默认甲=分工矩阵干净、逐局历史只剩一个权威呈现面;追认单可 veto)。
|
||||
|
||||
### Q5 · 大门「有断点」分支的三出口弹窗形态(**已裁=甲**)
|
||||
|
||||
**裁定(2026-10-18)**:甲——ConfirmDialog 扩第三钮(继续上一段冒险/开始新冒险/取消,cancelled 恒=无变更退回营地)。推荐案,用户令按推荐执行。连带:卡③ 契约 3 已按三钮化落定(删档确认只用取消/确认);类签名变更随本卡重新立卡时的实现落地,卡③ T0 对齐。
|
||||
|
||||
### Q6 · Q1 与规格清单缺口的连带裁(**已裁=甲**)
|
||||
|
||||
**裁定(2026-10-18)**:甲——Q1 裁 A 同时订过渡口径:「过渡尺寸档=现役 1280 工程档+烛光黄 #F6C667/木色 #8A5A34 既有规格;终档归样张批冻结后置换(反哺点登记)」。推荐案,用户令按推荐执行。若 Q1 裁 B(插画级新产),过渡口径不成立——B 依赖规格清单,须走乙案「T1 等样张批规格清单产出才开」或用户另行裁定过渡规格。
|
||||
|
||||
---
|
||||
|
||||
**审查回写要点**:v1.0→v1.1(2026-10-18 grilling 子 Agent 对抗审查回写)——事实订正 6 处:E1「48 色工具全仓不在役」→截屏级不在役/素材级二器在役(F17/§9-5/V10/T5 连带);E2「camp golden」五处不存在→删并立契约 10 不建;E3 F7 行号(_start_adventure :579-597 等);E4 F14 .gitignore:51+「恒无」表述订正;E5 存档序列化 :122→:121(卡⑤同订);E6 行号微漂(ui_struct_probe :41-49、smoke_all :170-171、camp.md :19-20)。自扩订正:契约 10 标仓库纪律成文。卡内可收 15 项:presenter 承接面(T3/契约 4)、叠层规则派生(T2/§9-2)、ConfirmDialog 消费方编排(T4)、生涯探针驱动改缆+返回钮文案坑(T3/T5)、图鉴占位态(T3)、screen_flow 键新语义(T5)、HUD 刷新单口(T2/T3)、深员改指表(T5)、源码断言段重写(T5)、shot_probe 收闭改缆(T5)、forge_workbench_test 迁移+§5 补 tests 可写面(T5/§5)、断点确认段补列(F8/T5)、contrast 段核签(T5)、素材命名指针(T1)、终稿落盘口径(T1/契约 9/§5)。跨卡对齐:HUD 刷新挂点(§0 接口+卡⑤订正)、ConfirmDialog 类事实双向失效(Q5)、开层互斥/关层收闭(T3+卡③ T2)、探针文件分界(T5)、`_refresh_resume_button` 不复用(T4)。验收补:V1 拆半、V3 扩、V4 扩、V12 留痕行、变异补二条。新增待裁点 Q5 三出口弹窗、Q6 规格缺口连带;估时 8.5–11.5d→9–12d。用户面裁决(①④⑤裁决 5/10)零改动。
|
||||
**代码段 `/iter` 开工门已全部裁断销号(2026-11-01 用户裁,照 grilling+godot-master 双审推荐采纳)**:P3 销号口径=camp.md v2.2 四态规格即设计稿;P5 销号口径=形态落文完成、实装回填归反哺点;camp-scene-prompts §5 五裁点=①帐篷 300×170 追认②门扇 240×220 追认③布告板 (845,268,130,140)④描边奶油色追认⑤环境子板色值不前置(T1 量化后按 art-bible §4.1 登记);十张终稿后处理=/iter 开工令即含授权。**本卡零待裁点、开工门清零——迭代 47 可开工。**
|
||||
@@ -0,0 +1,144 @@
|
||||
# 需求卡片:营地模态句柄子容器批(UiCampModals 分域句柄容器 · camp_overlay 句柄迁移)· 营地交互重做卡⑥(前置基建)
|
||||
|
||||
- 版本:**v1.1(现行有效卡)**。留痕:2026-11-01 用户裁(迭代 47 机检预算互斥停机呈报裁决=最小改动先走:营地域六句柄落子容器,全量分域分家登记待办清单另立批);2026-11-01 grilling+godot-master 双审后写卡段修订(v1.1,执行级四缺口+措辞订正,设计面零改动)——T0 命令路径订正 game/tests/static_scan.sh、T1 补类名缓存刷新步+ui_modals 头注同步+断言尾缀落点+复跑面扩改指面 8 文件、V3 判据改计数留痕+观测面局限申报、迁移面实测 8 文件入 F4、契约 2/4 计数与退役删除句、node() 改写分支措辞。
|
||||
- 来源:2026-11-01 用户裁决(**唯一事实源**:本卡 §2 现状实测+`design/engineering/code-conventions.md` §1.1 拆分判据;需求性工程条目证据指认=迭代 47 机检预算互斥停机呈报〔ui_modals.gd 类变量 14,卡④⑤ 平铺合计 19>S13 上限 15〕+用户裁决留痕,属重构基建、无设计线前置)
|
||||
- 归属:呈现层/组合根(`game/presentation/`)——营地交互重做卡⑥,**迭代 47 执行序第一位**(卡④ T3 落五句柄、卡⑤ T1 落浮窗句柄的本批前提)
|
||||
- 开工门:U0(随迭代 47 `/iter` 发令,无独立开工令)+零待裁点
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标**:给营地域模态句柄立独立子容器,使迭代 47 营地域六句柄(场景根+四子界面+Esc 浮窗)不再平铺进 `ui_modals.gd`——该文件类变量终值 14(不超 S13 上限 15),零基线登记;调度器/层序合同/登记口三件全局职责零改动。
|
||||
|
||||
**完成定义**:
|
||||
1. `ui_camp_modals.gd`(类名 UiCampModals,RefCounted)新件在役;`ui_modals.gd` 持 `camp` 成员一枚,`camp_overlay` 平铺成员删除——类变量 14→14;
|
||||
2. `camp_overlay` 句柄寻址迁移 `ui.modals.camp.overlay`,全仓引用(组合根+探针)改指完毕,现役营地探针段复跑绿(行为零变化的证明);
|
||||
3. `ui_registry.gd node()` 的 `camp_overlay` 分支返回属性链改经子容器(`modals.camp.overlay`;**LAYER_CONTRACT key 平铺不改名**,camp.* 新增解析分支随卡④⑤);
|
||||
4. `main_ui_build.gd` 装配赋值改经子容器;S13 读数 `ui_modals.gd`=14 不升;
|
||||
5. 批末按改动域选档全绿;三份 `reports/` 产出物写齐(T2)。
|
||||
|
||||
## 2. 现状事实(2026-11-01 实测;开工日 T0 复核)
|
||||
|
||||
| # | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| F1 | `ui_modals.gd` 类变量 14 枚=flow+13 模态句柄;gdstyle 直跑零命中(14≤15) | `game/presentation/ui_modals.gd:7-20` |
|
||||
| F2 | S13 类变量上限=15(gdstyle v0.4.0 默认 `quality/max-class-variables`,超即 NEW 键红);15 枚文件直跑零命中、20 枚合成文件实测 warn | `tools/gdstyle_gate.py`;停机呈报实测凭据 |
|
||||
| F3 | 卡④⑤ 原平铺写法合计 19>15(卡④ 净+4=场景根+四子界面−`camp_overlay` 退役;卡⑤ +1 浮窗)——迭代 47 机检预算互斥成因;2026-11-01 用户裁=子容器方案,禁基线特批 | 停机呈报-迭代47机检预算互斥-2026-11-01.md(iter47 worktree 在档,裁决已吸收本卡与卡④⑤) |
|
||||
| F4 | `camp_overlay` 句柄现役消费面实测 **8 文件 40 余处**(2026-11-01 双审实查;T0 逐处列清单):`main_ui_build.gd`(约 10 处)/`ui_registry.gd:61`/`screen_flow_probe.gd`(3)/`ui_click_probe.gd`(16–20)/`ui_probe.gd`(8–9)/`shot_probe.gd`(2)/`ui_struct_probe.gd`(1)/`system_test.gd`(2——:108 句柄同一性链、:121 候选层清单,含 `game/tests/` 域) | `main.ui.modals.camp_overlay` 全仓引用位 |
|
||||
| F5 | `ui_registry.gd node()` 现经 `modals.X` 一层寻址;LAYER_CONTRACT 与 `apply_layer_contract` 与本批正交(句柄册结构≠场景树结构——子容器不挂树、零树序影响) | `game/presentation/ui_registry.gd`(node() 寻址段,T0 复核) |
|
||||
|
||||
## 3. 设计契约(红线,违反=L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 行为零变化**:纯句柄搬家+容器新立;LAYER_CONTRACT/登记序/树序/`register_layer`/ModalFlow 零改动;现有探针断言**零改判据**(只改寻址)。
|
||||
|
||||
🔴 **契约 2 · 寻址唯一形态**:营地域全部句柄(过渡期含本卡迁入的 `camp.overlay`;**终态六枚=场景根+四子界面+Esc 浮窗**)寻址一律 `ui.modals.camp.X` 二层;禁平铺回 `ui_modals.gd`(卡④ T3 落五句柄、卡⑤ T1 落 esc 时同规——卡⑤ 契约 7 已载、卡④ T3 步与文件表已载)。
|
||||
|
||||
🔴 **契约 3 · UiModals 只减不增**:本卡后类变量 14(flow+12 平铺句柄+camp 容器成员),卡④⑤ 阶段终值保持 14;任何新句柄平铺回顶层=S13 NEW 红=L3。S13 红线实际兜 **+2 起**(14→15 闸零命中)——+1 漂移由 V3 计数留痕与独立验收审计兜,契约分工在此写明。
|
||||
|
||||
🔴 **契约 4 · 禁无效成员**:子容器内不预占句柄——本卡只迁 `camp_overlay` 一枚实件;五枚新句柄随卡④ T3 创建视图时落、esc 随卡⑤ T1 落;禁空占位凑结构(code-conventions §1.1 第⑤条同款)。**`camp.overlay` 枚随卡④ T3 CampOverlay 类退役一并删除**(禁悬挂——类删后类型悬挂即 parse error,删除义务显式归卡④ T3)。
|
||||
|
||||
🔴 **契约 5 · 拆分边界纪律**(判据权威=code-conventions §1.1,本卡写条款不复制定文):本卡新建分域容器沿**职责轴**——UiCampModals=可命名领域概念「营地交互域模态组」(营地场景图+其子界面+其浮窗)、单一变化原因=营地交互重做;非凑计数袋;实现报告逐件一行承载职责语句。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:UiCampModals 新件+`ui_modals.gd` 持 camp 成员+`camp_overlay` 句柄迁移+`node()` camp 二层寻址+装配与全仓引用改指。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 其余 12 枚存量句柄分域分家、flow 自 UiModals 升格独立持有 → 待办清单「模态句柄册分域重构」条(2026-11-01 登记,另立批);
|
||||
- 卡④ 的五枚新句柄实装 → 卡④ T3;esc 句柄 → 卡⑤ T1;
|
||||
- LAYER_CONTRACT/模态栈语义任何改动 → 无(契约 1)。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
| 文档 | 关联点 | 同步义务 |
|
||||
|---|---|---|
|
||||
| `design/engineering/architecture/architecture.md` | 呈现层模块清单(UiCampModals 新件归属) | 反哺点 · 列清单(实现批禁写 `design/`) |
|
||||
| `待办清单.md`(仓库根) | 营地交互重做条实况补卡⑥路径;本卡来源裁决 | 已同步(2026-11-01 写卡段登记维护)——实现批禁写 |
|
||||
| `roadmap.md`(仓库根) | 迭代 47 行需求卡片列(卡⑥ 排第一)+预计周期 | 已换代(2026-11-01 编排入表,写卡/编排段动作)——实现批禁写 |
|
||||
| `game/tests/README.md` | 能力行:**零新增能力**(V1–V3 全落既有「模态覆盖层编排与输入锁」) | 已核:零增行;实现批禁写 |
|
||||
| `game/presentation/*`、`game/probe/*`、`game/tests/**` | 实施/断言本体 | 实施本体(可写) |
|
||||
|
||||
## 卡前·文件结构与职责(T0 前)
|
||||
|
||||
| 文件 | 职责 | 改动类型 | 落位 | 对照表行 |
|
||||
|---|---|---|---|---|
|
||||
| `game/presentation/ui_camp_modals.gd` | 营地域模态句柄子容器(RefCounted;本卡 1 枚 `overlay`,卡④⑤ 落满 6 枚;头注自陈禁平铺回 ui_modals) | 行为改动 | 新建 | V1 |
|
||||
| `game/presentation/ui_modals.gd` | 持 `camp` 成员(_init 构造);删除平铺 `camp_overlay` 成员 | 行为改动 | 复用既有(改) | V1 |
|
||||
| `game/presentation/main_ui_build.gd` | 装配赋值改经子容器(`modals.camp.overlay = …`) | 行为改动 | 复用既有(改) | V1 |
|
||||
| `game/presentation/ui_registry.gd` | `node()` 的 `camp_overlay` 分支返回属性链改经子容器(key 平铺不改名);camp.* 新增解析分支随卡④⑤ | 行为改动 | 复用既有(改) | V2 |
|
||||
| 全仓 `camp_overlay` 句柄引用位(组合根+探针,T0 grep 列清单) | 寻址改指 `ui.modals.camp.overlay`(断言零改) | 非行为改动(寻址改指) | 复用既有(改) | V1 |
|
||||
|
||||
## 落位与可复用性预检
|
||||
|
||||
| 新件 | 复用面(grep 同职责实现) | 唯一面(grep 同名同义标识符) | 分层与交互面 |
|
||||
|---|---|---|---|
|
||||
| `ui_camp_modals.gd` | grep 句柄分域容器/分组容器无先例;`UiModals` 本体=按技术角色平铺的注册面容器(职责不同);`ModalFlow`=调度器非句柄册 → 不能复用,须新建 | grep `UiCampModals`/`ui_camp_modals`/`camp_modals` 无第二处;营地句柄寻址唯一落点 | RefCounted 不挂树;`ui_modals._init` 构造 camp(与 flow 同拍,先例 ui_modals.gd:24)、`main_ui_build` 只向 camp 赋句柄;`register_layer`/LAYER_CONTRACT/树序零触碰 |
|
||||
|
||||
**非新增件说明**:`ui_modals.gd`/`main_ui_build.gd`/`ui_registry.gd`/探针=存量改造;**数据件/RNG/Core 零触碰**(纯呈现层簿记);Core 零 UI 依赖不因新件绕过。
|
||||
|
||||
## 实现规范预算
|
||||
|
||||
gdstyle v0.4.0 默认阈值:公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6。
|
||||
|
||||
| 触面 | ①公开方法数 | ②成员变量数 | ③迁移函数对账 | 结论 |
|
||||
|---|---|---|---|---|
|
||||
| `ui_camp_modals.gd` | 预期 ≤1(`_init`) | 预期 1(本卡)→6(卡④⑤ 落满) | 无迁入函数 | 三查通过 |
|
||||
| `ui_modals.gd`(存量) | 不适用 | 14→14(−平铺 `camp_overlay`+`camp` 容器成员) | 无 | S13 只降不升 |
|
||||
| `ui_registry.gd`(存量) | 不适用 | 不适用 | 无 | node() +1 解析分支,分支数 ≤8 |
|
||||
| `main_ui_build.gd`(存量) | 不适用 | 不适用 | 无 | 装配行改指 |
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
### T0 事实坐实(≈0.25d)
|
||||
|
||||
- 取用:§2 F1–F5、营地交互重做条(已编迭代 47)、卡⑤ v1.5 契约 7+卡④ v3.2 T3 步与文件表(寻址规则落点)。
|
||||
- [ ] 复核 §2 全部 `file:line`;grep 全仓 `modals.camp_overlay` 引用位列清单(改指面:组合根+探针逐处)。
|
||||
- [ ] 实读 `ui_registry.gd node()` 寻址形态,定 `camp.*` 二层解析的顺延写法(循 `modals.X` 先例,禁另造第三种寻址)。
|
||||
- [ ] 跑 `bash game/tests/static_scan.sh`(tools/ 下无此件——v1.0 路径失实订正)记录 S13 读数(**禁手改 `tools/gdstyle_baseline.txt`**——基线是欠账簿,重生成人工专用;`ui_modals.gd` 类变量 14)。
|
||||
- 产出:引用位清单、寻址形态结论、S13 读数留痕。
|
||||
|
||||
### T1 实装与验证(≈0.25d)
|
||||
|
||||
- [ ] 新建 `ui_camp_modals.gd`(RefCounted;`var overlay: CampOverlay`;头注自陈「营地交互域句柄子容器,终态六枚,新营地句柄禁平铺回 ui_modals」)。
|
||||
- [ ] **类名缓存刷新**(code-conventions 语言纪律 5——新 class_name 必步):新件落盘后、任何 headless 验证前先跑一次编辑器扫描(`--editor --quit`;新 worktree 无 `.godot` 缓存,缺此步 `--script` 模式 parse 假红)。
|
||||
- [ ] `ui_modals.gd`:持 `camp: UiCampModals`(`_init` 构造,与 flow 同拍——ui_modals.gd:24 先例);删除平铺 `camp_overlay` 成员;**头注同步**(「十三个模态覆盖层句柄」→「十二个平铺句柄+营地域子容器」——§1.4.1 头注如实义务)。
|
||||
- [ ] `main_ui_build.gd` 装配改指;全仓引用位按 T0 清单(8 文件)改指 `ui.modals.camp.overlay`(逐处留痕)。
|
||||
- [ ] `ui_registry.gd` `node()` 的 `camp_overlay` 分支返回属性链改经子容器(**key 平铺不改名**);camp.* 新增解析分支随卡④⑤。
|
||||
- [ ] 现役营地断言消息文本追加〔句柄子容器批 V1/V2〕尾缀(**判据零改、仅消息文本**——独立验收审计「尾缀能 grep 到」义务的落点步)。
|
||||
- [ ] 验证:改指面 8 文件逐件复跑绿(探针五件营地段+`system_test.gd`——screen_flow_probe/ui_click_probe/ui_probe/shot_probe/ui_struct_probe;断言零改只换寻址);`system_test.gd` V1c 合同树序链(:94-106)+句柄同一性链(:108)复跑绿;S13 读数=gate 绿(≤15 零 NEW)+`grep -c '^var '` 计数=14 留痕。
|
||||
- 产出:子容器在位+迁移绿证+引用位改指留痕表;卡④ T3/卡⑤ T1 按契约 2 落新句柄。
|
||||
|
||||
### T2 产出物落位卡(≈0.25d,末卡)
|
||||
|
||||
- [ ] 里程碑步出口跑一次改动域全套闸:`bash tools/run_all_gates.sh --code`;回执 `all_green=true`;波内修复只做出红最小单元最窄确认。
|
||||
- [ ] 独立验收审计(未参与实现的会话/子代理),产出 `reports/iter<N>-验收审计-<日期>.md`;未过禁合并。
|
||||
- [ ] 合并前写齐三份 `reports/`:实现报告/反哺点清单(§5 每条有落点)/验收审计。
|
||||
- 产出:三份 `reports/` 齐全+闸全绿+审计通过;合回后本地 CI 全量复验。
|
||||
|
||||
**估时合计 ≈0.75d**(T0 0.25+T1 0.25+T2 0.25)。**切分预案**:T2 偏紧(闸+审计+三报告挤 0.25d)——三份 `reports/` 先行成稿、独立验收审计走并行子代理,不串行等。
|
||||
|
||||
## 7. 验证闸:改动—验收对照表
|
||||
|
||||
| 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 句柄迁移零行为漂移 | `ui_camp_modals.gd` 新件;`ui_modals.gd` 持 camp/删平铺成员;`main_ui_build.gd` 装配改指;全仓引用改指 | 改指面探针五件营地段+`system_test.gd`(V1c :94-106+句柄同一性链 :108)〔句柄子容器批 V1〕 | 探针(+系统测试复跑) | 现役营地断言逐件复跑(断言零改、寻址新——改指面 8 文件全覆盖) | 全段绿=行为零变化证明;改指后全仓 grep 零 `modals.camp_overlay` 平铺寻址残留(静态判据);句柄同一性链绿=同一实例零漂移 | 既有:模态覆盖层编排与输入锁 |
|
||||
| V2 层序合同零漂移 | `ui_registry.gd` node() 分支返回属性链改写 | `system_test.gd:_ready`(V1c 合同树序链 :94-106 复跑)〔句柄子容器批 V2〕 | 系统测试 | LAYER_CONTRACT 单一事实源 | 树序链绿=合同与落位零漂移(子容器不挂树、零树序影响;key 平铺不改名) | 既有:模态覆盖层编排与输入锁 |
|
||||
| V3 S13 读数 | `ui_modals.gd` 类变量 14 | gdstyle S13 gate+`grep -c '^var '` 计数(T1 验证步留痕)〔句柄子容器批 V3〕 | 静态读数 | `bash game/tests/static_scan.sh` | gate 绿(≤15 零 NEW)+计数=14 留痕——**观测面局限申报**:S13 闸对 14/15 同样零命中,「=14」由人工计数承载(test-conventions §8.4 静态判据出口) | 既有:模态覆盖层编排与输入锁 |
|
||||
|
||||
**测试方式两分法**:V1/V2 机检自动化;V3 静态读数留痕(gdstyle 基线机制域内)。**测试削弱申报:零削弱**(断言零改判据,只换寻址)。**独立验收审计**:合并前由未参与实现的会话/子代理执行(四件事口径同 test-conventions §8.7),报告落 `reports/iter<N>-验收审计-<日期>.md`,未过禁合并。**闸口径**:轻闸每卡出口自跑;批末 `--code` 关联档全绿才合并;MC 零跑(零数值零行为)。回归措辞按模板:里程碑步出口一次全套闸,波内修复只跑红件。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救一次)**:改指拼写、探针寻址写法、node() 分支写法、类名缓存失配 parse 失败(重跑 `--editor --quit` 即清)、manifest 登记遗漏。
|
||||
- **L2(记录继续;同一红连续三次升 L3)**:行数较 T0 预期漂移但 S13 键不升。
|
||||
- **L3(立即停卡写报告等用户)**:迁移后任一现役营地断言红且非寻址成因(=行为被意外触碰);机检预算互斥(`ui_modals.gd` 类变量被迫>14、或子容器被塞入非营地句柄=拆分边界破)——停卡退回写卡段,禁造第三条路。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. `node()` 解析形态=match 平铺(现役唯一形态,ui_registry.gd:50-69;字典形态弃用、禁另造第三种寻址)。
|
||||
2. 子容器头注措辞(契约 4 自陈内容域内)。
|
||||
|
||||
超出必须呈报:寻址形态、文件落位、契约数字、句柄归属。
|
||||
|
||||
## 10. 待裁点:零待裁点,开工门=U0(随迭代 47 发令)
|
||||
|
||||
**零待裁点。** 2026-11-01 用户裁留痕:最小改动=本卡+卡④ v3.2/卡⑤ v1.5 落子容器;全量分域句柄分家=待办清单「模态句柄册分域重构」条(另立批,不属迭代 47)。
|
||||
+4
-2
@@ -6,7 +6,7 @@
|
||||
|
||||
- **阶段**:M3——Alpha 版本的完整实现(2026-10-09 立规:未明令宣布完成前停留 M3,迭代只排 M3 需求,M4 不排期;根目录 stage.txt 为旧阶段开关仅存档)。
|
||||
- **迭代位**:迭代 1–45 全部已完成——迭代 45(呈现层状态件+gdstyle 尾波补件两卡)2026-10-31 合回(合并提交 f817ab2d,独立验收审计通过零缺漏,行为零变化),批后收尾闭合(2026-10-31):反哺点七条处置闭合(architecture 六条落文=breakpoint_store/board_hover/单格两件/站桩两件四行现状化+构造句改述与 7 项引用束/15 贴顶实况+gdstyle 截断缺陷节清偿结果;第 7 条工具观察迁待办新条)、目检三项登记体验验证清单 ㊸、产出物退役九件(两报告/反哺点/审计/两停机呈报+两卡+两设计文档)、worktree 两件清理(iter45+assert-slim-b2)。迭代 44 收尾记录见 git 提交 9eedf896。
|
||||
- **下一步**:迭代 46 待开工(编排提交 da24047c:换图系双基座七件批+站桩名字行提档与地台色合并批+棋盘格边长口径收口批三卡,≈9.125 个工作日)——开工由用户经 iter 发令。待编池:营地交互重做卡⑤(排卡④之后)、卡④(美术设计已闭合=样张过审 2026-10-31+art-bible v3.24 §6.2,待用户发令重立卡)、冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
|
||||
- **下一步**:迭代 46 进行中〔用户裁拆批 2026-10-31〕——卡A 换图系双基座七件批+卡B 站桩名字行提档与地台色合并批已收口合回(合并提交 34bb62a5,独立验收审计通过,CI 全量绿);卡C 棋盘格边长口径收口批 **v2.0 重立回编**(用户裁锚点换代:可读性锚点=用户验收的现行渲染值,基座档 14×14=28px 达标,battle.md 可读下限 30px 句与探针 36px 人造阈值全退役,探针判据换锚=实读与单点算式严格相等+结构钳制+13/14 推定留痕;v2.0 卡经 grilling 独立复核,写卡批合并提交 e7090bc9,CI 全量绿),待 `/iter` 发令回 worktree iter46 续工(WIP 算式单点与四档冒烟两件复用、探针件返工)。battle.md `:144` 可读下限句退役归文档批待发令。迭代 47(营地交互卡⑥句柄子容器前置+卡④⑤)已编排待开工。待编池:冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
|
||||
|
||||
## 里程碑时间线
|
||||
|
||||
@@ -79,7 +79,9 @@
|
||||
| 迭代 43 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈6.5 个工作日 | 已退役(git 历史可考) |
|
||||
| 迭代 44 | 已完成(2026-10-30) | 已退役(git 历史可考) | 2026-10-29 | ≈7.875 个工作日 | 已退役(git 历史可考) |
|
||||
| 迭代 45 | 已完成(2026-10-31) | 已退役(git 历史可考) | 2026-10-29 | ≈7 个工作日 | 已退役(git 历史可考) |
|
||||
| 迭代 46 | 待开工 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md<br>reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md | 2026-10-31 | ≈9.125 个工作日 | — |
|
||||
| 迭代 46 | 进行中 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md<br>reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md | 2026-11-01 | ≈8.625 个工作日 | — |
|
||||
| 迭代 47 | 待开工 | reports/需求卡片-营地模态句柄子容器批-2026-11-01.md<br>reports/需求卡片-营地场景图新建批-2026-10-18.md<br>reports/需求卡片-ESC浮窗与营地顶栏批-2026-10-18.md | 2026-11-01 | ≈15 个工作日〔用户特批 2026-11-01 超 10 个工作日〕 | — |
|
||||
| 迭代 48 | 待开工 | reports/需求卡片-状态合并累加制批-2026-11-01.md<br>reports/需求卡片-立绘显示档批-2026-11-01.md<br>reports/需求卡片-战斗区背景替换-B区舞台插画与地面层-2026-10-31.md | 2026-11-01 | ≈6.1 个工作日 | — |
|
||||
|
||||
- 迭代 38 收口为带红合并〔用户特批 2026-10-26〕:09 静态扫描 S13 gdstyle 恶化 2 条经批准记入基线存量红(git 提交 c4c69701),卡② 停卡退上游,处方已于 2026-10-26 修订定稿 v1.3(git 提交 b3ea7532),随「gdstyle 存量债全量清偿」条目波 7 待编排。
|
||||
- 迭代 39 收口为带红合并〔用户特批 2026-10-27〕:02 EV 锚 goblin 0.9148∉[1.00,1.20](触发器批有意行为改动,fp f74b9a48ef4a)已随 EV 锚重锚批换带归绿(git 提交 ba5105aa);08d 生涯.md:55 行号漂移已随 2026-10-28 批后收尾反哺落文修复归绿。三卡拆分后终局 gdstyle 恶化 2 条(battle_orchestrator max-file-length、level_map_screen max-class-variables)归迭代 40 卡①还债与迭代 41 存量债序列。
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 直改车道闸(git pre-commit,2026-10-30 本地 CI 化换代)——提交点现场跑文档域闸
|
||||
# (tools/run_all_gates.sh --docs,秒级文本扫描):绿=落地,红=中止。
|
||||
# 代码面提交不再拦(2026-10-30 用户裁:主区直改全面放开)——代码验证归合回后的本地 CI
|
||||
# (tools/hooks/post-merge 全量跑批;CI 红态下 tools/hooks/post-commit 追绿复验)。
|
||||
# 合并收尾提交跳过(MERGE_HEAD 在=冲突合并的收尾 commit)——内容归合回后的 CI 全量验。
|
||||
# --no-verify 绕过仅属用户(agent 侧由 worktree-guard 拦截)。
|
||||
set -u
|
||||
REPO="$(git rev-parse --show-toplevel)"
|
||||
|
||||
# 合并收尾提交:跳过文档闸,交合回后 CI
|
||||
[ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ] && exit 0
|
||||
|
||||
RC=0
|
||||
# 收暂存清单(增量档输入):quotepath 关闭保中文名原样,NUL 转行落临时文件传闸机
|
||||
_LIST="$(mktemp "${TMPDIR:-/tmp}/gates-staged.XXXXXX")" || _LIST=""
|
||||
if [ -n "$_LIST" ]; then
|
||||
git -C "$REPO" -c core.quotepath=false diff --cached --name-only -z | tr '\0' '\n' >"$_LIST"
|
||||
bash "$REPO/tools/run_all_gates.sh" --docs --staged "$_LIST" || RC=$?
|
||||
rm -f "$_LIST"
|
||||
else
|
||||
bash "$REPO/tools/run_all_gates.sh" --docs || RC=$?
|
||||
fi
|
||||
if [ "$RC" -eq 0 ]; then
|
||||
exit 0
|
||||
elif [ "$RC" -eq 1 ]; then
|
||||
echo "[直改闸] 文档域闸有新增红,提交已拦。修上方红项后重新 commit,钩子会自动重跑闸;禁跑代码域/游戏类测试,与本次红项无关。" >&2
|
||||
else
|
||||
echo "[直改闸] 闸脚本自身故障(退出码 $RC),不是文档红——把本输出原样呈报用户,勿改文档、勿跑替代测试。" >&2
|
||||
fi
|
||||
exit 1
|
||||
+10
-118
@@ -22,13 +22,6 @@
|
||||
# ——本批改动只有文档件时用:game/ 内的 .md(README、生成清单)、design/、根规则文件、
|
||||
# tools/ 工具脚本、.zcode/ 配置等一切非代码面改动。
|
||||
# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。
|
||||
# --staged <清单文件> 文档域增量档(仅配 --docs):清单=本次提交暂存文件(pre-commit 钩子生成,NUL 转行)。
|
||||
# 触发矩阵(唯一落点):任一 .md → 06/06b/07/08/08d 先按清单内 .md 扫描(快路径);
|
||||
# 待办清单.md/roadmap.md/reports/**.md 任一 → 08c 全量(关系型,不可切片);
|
||||
# design/gdd/systems/**、game/tests/README.md、reports/**.md 任一 → 08e 全量(关系型);
|
||||
# 10 熵增闸恒跑(结构哨兵)。快路径扫出红时全量复跑定谳——清单面外文件的失败不在全量档
|
||||
# 判定面内,全量复跑绿=按绿放行并注记,红=真红;增量判定面与 CI 全量档严格对齐,
|
||||
# 跨文件漏检面(改 A 断 B 的链)由合回后 CI 全量兜底。缺省不带 --staged = 现行全量档,CI 零影响。
|
||||
# 不带参数 = 全量档(两域全跑),触发条件=同批既有代码面改动又有文档件改动。闸件(tools/ 下机检脚本
|
||||
# 与闸壳)自身的改动不占合回全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;
|
||||
# 闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以全量为兜底。
|
||||
@@ -54,24 +47,16 @@ BASELINE="$STAMP_DIR/baseline.json"
|
||||
|
||||
QUICK=0
|
||||
DOMAIN="full"
|
||||
STAGED_FILE=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--quick) QUICK=1 ;;
|
||||
--code|--docs)
|
||||
[ "$DOMAIN" = "full" ] || { echo "用法: --code 与 --docs 互斥"; exit 2; }
|
||||
DOMAIN="${1#--}" ;;
|
||||
--staged)
|
||||
[ -n "${2:-}" ] || { echo "用法: --staged <暂存清单文件>"; exit 2; }
|
||||
STAGED_FILE="$2"; shift ;;
|
||||
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick] [--staged <清单文件>]"; exit 2 ;;
|
||||
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick]"; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
if [ -n "$STAGED_FILE" ]; then
|
||||
[ "$DOMAIN" = "docs" ] || { echo "用法: --staged 仅配 --docs(增量档只定义在文档域)"; exit 2; }
|
||||
[ -f "$STAGED_FILE" ] || { echo "用法: --staged 清单文件缺位:$STAGED_FILE"; exit 2; }
|
||||
fi
|
||||
|
||||
# 每个代码面 worktree 用独立 Godot user 目录(game/override.cfg 由 setup 脚本自愈生成;主区 no-op)。
|
||||
# 并行批共用一套夹具、日志与截图目录会互相踩盘造成假红——隔离建不起来就拒跑(fail-closed)。
|
||||
@@ -88,23 +73,8 @@ fi
|
||||
# 与基线比必判「红项清单变化」假红)、末尾汇总自身崩掉打不出结论。
|
||||
export PYTHONIOENCODING=utf-8
|
||||
|
||||
# ── 直改闸在位自检(车道铁律):pre-commit 钩子失位=主区直改提交静默免验,任何跑批点红暴露 ──
|
||||
_HP="$(git -C "$REPO" config core.hooksPath)"
|
||||
if [ -n "$_HP" ]; then
|
||||
case "$_HP" in /*) _HPT="$_HP/pre-commit" ;; *) _HPT="$REPO/$_HP/pre-commit" ;; esac
|
||||
if [ ! -f "$_HPT" ]; then
|
||||
echo "[gates] FAIL — core.hooksPath=$_HP 但 pre-commit 文件缺位:直改闸失位,主区直改提交免验。"
|
||||
echo " 处置:确认 tools/hooks/pre-commit 在位后 git config core.hooksPath tools/hooks"
|
||||
exit 1
|
||||
fi
|
||||
elif [ ! -f "$(git -C "$REPO" rev-parse --git-path hooks)/pre-commit" ]; then
|
||||
echo "[gates] FAIL — core.hooksPath 未设置且默认钩子位无 pre-commit:主区直改提交免验。"
|
||||
echo " 自愈:git config core.hooksPath tools/hooks"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 代码面(game/ 下非 .md 的行为载体)是否带着未提交改动(只算已跟踪文件——闸件自身产物都落在
|
||||
# gitignore 面)。脏树跑出来的回执记的是 HEAD,验的却是别的字节,合回闸据此拒收。
|
||||
# gitignore 面)。脏树跑出来的回执记的是 HEAD,验的却是别的字节,回执标 dirty_game 由 CI 落账留痕。
|
||||
DIRTY_GAME=0
|
||||
if [ -n "$(git -C "$REPO" -c core.quotepath=false diff HEAD --name-only -- game/ 2>/dev/null | grep -viE '\.md$')" ]; then
|
||||
DIRTY_GAME=1
|
||||
@@ -357,38 +327,6 @@ gate() { # gate <名称> <日志名> <命令...>
|
||||
return 0
|
||||
}
|
||||
|
||||
# 增量文档检查壳(--staged 档专用):先按暂存清单扫(快路径),扫出红时全量复跑定谳——
|
||||
# 清单面外文件的失败不在全量档判定面内,全量复跑绿=按绿放行并注记,红=真红。
|
||||
# 增量判定面与 CI 全量档严格对齐(增量档不产生全量档不会有的红)。
|
||||
gate_docs_inc() { # gate_docs_inc <名称> <日志名> <全量命令...> -- <增量命令...>
|
||||
local name="$1" log="$2"; shift 2
|
||||
local -a full_cmd=() inc_cmd=()
|
||||
local _in_inc=0 _a
|
||||
for _a in "$@"; do
|
||||
if [ "$_a" = "--" ]; then _in_inc=1; continue; fi
|
||||
if [ $_in_inc -eq 0 ]; then full_cmd+=("$_a"); else inc_cmd+=("$_a"); fi
|
||||
done
|
||||
echo ""
|
||||
echo "════════════════════════════════════════════"
|
||||
echo " $name(增量档:暂存 .md $(( ${#inc_cmd[@]} - ${#full_cmd[@]} )) 件)"
|
||||
echo "════════════════════════════════════════════"
|
||||
local rc=0
|
||||
"${inc_cmd[@]}" >"$STAMP_DIR/$log" 2>&1
|
||||
rc=$?
|
||||
if [ $rc -ne 0 ]; then
|
||||
"${full_cmd[@]}" >>"$STAMP_DIR/$log" 2>&1
|
||||
rc=$?
|
||||
if [ $rc -eq 0 ]; then
|
||||
echo " [增量注记] 清单扫描出红、全量复跑绿——红项在暂存清单面外(不在全量档判定面内),按绿放行。"
|
||||
fi
|
||||
fi
|
||||
if [ $rc -ne 0 ]; then
|
||||
grep -E "FAIL|红|Error|error" "$STAMP_DIR/$log" | tail -12 | sed 's/^/ | /'
|
||||
fi
|
||||
NAMES+=("$name"); EXITS+=("$rc"); LOGS+=("$log")
|
||||
return 0
|
||||
}
|
||||
|
||||
gate_unit() { # run_all 是 --script 模式,退出码为唯一判据;分片态=多 Godot 进程各跑一片
|
||||
local name="02 单元与集成测试"
|
||||
# 关联档选跑:把套件词列表经 --only= 传给 run_all(闸机来源标记 --only-source=gates,
|
||||
@@ -478,60 +416,14 @@ fi
|
||||
fi
|
||||
# ══ 文档域(6–8e)══
|
||||
if in_domain docs; then
|
||||
# ── 增量触发矩阵(唯一落点,判据依据与漏检面见头注 --staged 段)──
|
||||
_md_list=()
|
||||
_trigger_08c=0
|
||||
_trigger_08e=0
|
||||
if [ -n "$STAGED_FILE" ]; then
|
||||
while IFS= read -r _f; do
|
||||
[ -z "$_f" ] && continue
|
||||
case "$_f" in
|
||||
待办清单.md|roadmap.md|reports/*) _trigger_08c=1 ;;
|
||||
esac
|
||||
case "$_f" in
|
||||
design/gdd/systems/*|game/tests/README.md|reports/*) _trigger_08e=1 ;;
|
||||
esac
|
||||
case "$_f" in
|
||||
*.md) [ -f "$REPO/$_f" ] && _md_list+=("$_f") ;;
|
||||
esac
|
||||
done < "$STAGED_FILE"
|
||||
fi
|
||||
if [ -z "$STAGED_FILE" ]; then
|
||||
# 全量档(CI 与手动档原路径,判定面不变)
|
||||
gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
|
||||
gate "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
|
||||
gate "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
|
||||
gate "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
|
||||
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
|
||||
gate "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
|
||||
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
|
||||
else
|
||||
# 增量档:清单内 .md 走快路径(扫出红全量复跑定谳,见 gate_docs_inc)
|
||||
if [ "${#_md_list[@]}" -gt 0 ]; then
|
||||
gate_docs_inc "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py" -- "$PY" "$REPO/tools/gdd_lint.py" "${_md_list[@]}"
|
||||
gate_docs_inc "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only -- "$PY" "$REPO/tools/gdd_lint.py" --changelog-only "${_md_list[@]}"
|
||||
gate_docs_inc "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict -- "$PY" "$REPO/tools/doc_link_audit.py" --strict "${_md_list[@]}"
|
||||
gate_docs_inc "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict -- "$PY" "$REPO/tools/doc_term_audit.py" --strict "${_md_list[@]}"
|
||||
gate_docs_inc "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict -- "$PY" "$REPO/tools/doc_ref_audit.py" --strict "${_md_list[@]}"
|
||||
else
|
||||
for _n in "06 gdd_lint" "06b 变更记录节" "07 doc_link" "08 doc_term" "08d 引用可核"; do
|
||||
NAMES+=("$_n"); EXITS+=("SKIP"); LOGS+=("")
|
||||
done
|
||||
echo ""; echo "[gates] 06/06b/07/08/08d → SKIP(增量档:暂存清单无 .md)"
|
||||
fi
|
||||
if [ "$_trigger_08c" -eq 1 ]; then
|
||||
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
|
||||
else
|
||||
NAMES+=("08c 流水线闸"); EXITS+=("SKIP"); LOGS+=("")
|
||||
echo ""; echo "[gates] 08c 流水线闸 → SKIP(增量档:登记面文件未触)"
|
||||
fi
|
||||
if [ "$_trigger_08e" -eq 1 ]; then
|
||||
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
|
||||
else
|
||||
NAMES+=("08e 能力审计"); EXITS+=("SKIP"); LOGS+=("")
|
||||
echo ""; echo "[gates] 08e 能力审计 → SKIP(增量档:系统文档/能力清单/卡片未触)"
|
||||
fi
|
||||
fi
|
||||
# 全量档(CI 与手动档同判定面)
|
||||
gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
|
||||
gate "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
|
||||
gate "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
|
||||
gate "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
|
||||
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
|
||||
gate "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
|
||||
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
|
||||
fi
|
||||
|
||||
# ── 7. 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径,
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user