Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e5eba3122 | ||
|
|
da0c60323a | ||
|
|
1c9f5d36b1 | ||
|
|
b0b8c9ebd0 | ||
|
|
45b53d6897 | ||
|
|
f498d2ec85 | ||
|
|
bf7aa514b6 | ||
|
|
208e5652db | ||
|
|
be7ef54e30 | ||
|
|
c062b62d12 | ||
|
|
b44d3234b8 | ||
|
|
94a05afd49 |
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-merge 钩子复验追绿(红态修复走新开修复 worktree,`fix-<任务名>` 分支,免重塑免数卡;post-commit 钩子保留为红态主区直提兜底触发,绿态与无状态零触发)——批内提交零钩子检查、合回只验一次,同一份字节禁止第二次人手跑。CI 红不回滚合并,红项在案、修复合回追绿;开工先读 CI 状态,红未修不开新批。跑批起手自动 `--editor --quit` 刷类名缓存(.godot 缓存不入 git,防合并后失配假红)。`tools/run_all_gates.sh` 仍是全部验证闸的**唯一入口与唯一清单,域划分与选闸算法只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单;`--code`/`--docs`/`--quick` 手动档只服务开发期按域跑批(开发期窄测纪律见 process-conventions §6),不构成验收;全量回归由用户自发发起。**回执等待一律后台(2026-11-02 用户裁)**:合回落地先向用户呈报本批总结,随即 run_in_background 运行 `tools/ci_watch.sh <合并提交>` 等回执、唤醒后再续,禁前台 sleep/轮询等 CI(细则=process-conventions §1 第 3 条)
|
||||
- **合回验证 = 本地 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),不构成验收;全量回归由用户自发发起
|
||||
- 单件直跑(调试期最窄确认用,**不构成验收**):冒烟测试 `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 @@
|
||||
|
||||
**判据**:描述含多个实体、实体间有明确关系(先后顺序、包含、依赖、状态转移、布局对比)的内容时,直接画图说明,不写长段文字;单点事实(一句装得下)不画,纯数据对照用表格。
|
||||
|
||||
**载体**:用 diagram-design 技能出 HTML/SVG 内联交付(流程/状态/结构/实体关系与空间对比、像素级示意、界面草图同一通道),大图精修与导出用 archify 技能;查看端不支持内联渲染时用字符画。
|
||||
**载体**:默认用 visualize 技能出 HTML/SVG(会话内联渲染,表现力优先),流程/状态/结构/实体关系与空间对比、像素级示意、界面草图同一通道;查看端不支持内联渲染时退回 Mermaid 代码块,仍不行用字符画。
|
||||
|
||||
**边界**:硬数值与前提条件留在文字里,图不承载唯一数值依据;图与文禁重复说同一件事,文字只留图说不清的部分。
|
||||
|
||||
@@ -93,12 +93,12 @@
|
||||
|
||||
## 硬性红线(勿违反;操作细则在 process-conventions.md 与各规约)
|
||||
|
||||
- 车道铁律(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)
|
||||
- 车道铁律(判据与改动域划分同一,按文件性质切;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)
|
||||
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
|
||||
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
|
||||
- 合回后验证 = 本地 CI 全量域闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。文档面批与代码面批验收同走合回 CI 全量(`--docs` 仅属开发期手动档,process-conventions §1)
|
||||
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由本地 CI 全量验证(红项修复合回追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令走独立 worktree 直改+合回(车道铁律,commit 点零钩子检查);大体量改动(含触及代码面的工程改动如纯测试维护)同车道无特殊通道;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
|
||||
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI(车道细则=process-conventions §1)
|
||||
- 合回后验证 = 本地 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)
|
||||
- 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**(棋盘小人 32×32 二头身独立源档;站桩立绘=原稿分辨率入包、显示按三档绝对高 96/144/188 缩放,定值单点与逐件归档见 §8.2 站桩立绘显示档条)。
|
||||
- **分辨率档位已定案:1280×800 + 1920×1080 两档**,窗口不可调、格边长按档位计算,后续调整走修订;像素画布基准见 **art-bible §8.2**(主角人设原图 48×72〔人设侧画布〕;站桩立绘/战斗状态图源高 48;棋盘小人 32×32 二头身;敌种逐键源档 36–94;整数倍缩放纪律;终值归样张冻结,待办清单已登记)。
|
||||
- 程序化占位是现行结构冻结条款:资产未就位的槽位保持占位渲染,不空窗。
|
||||
- **复用闸(♻ 渠道四道验收,一道不过不入包)**:① 色板量化——改色对齐 art-bible §4.1 token,不保留原包杂色;② 吸色检查——无 HSV 色相 300°–345°(紫赤禁入带);③ 灰度/色弱可辨——去色相后仍可辨且不与语义件混淆;④ 授权干净——CC0 优先,CC-BY 署名记入设置/关于页,license 不明的一律不用。
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
|
||||
## 2. P1 —— 角色级(样张通过后开工,ComfyUI)
|
||||
|
||||
> 提示词规格卡 art-prompts.md 现行为 **v2.13**(主角三态+八敌种逐卡提示词仍在册;站桩立绘走 §5.4 按档派生、不重画)。
|
||||
> 提示词规格卡 art-prompts.md 现行为 **v2.0**(主角三态+八敌种,逐资产自包含完整提示词,整段可直贴)。
|
||||
|
||||
### 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;显示档三档绝对高 96/144/188、源=原稿不降采样(art-bible §8.2 v3.32);护甲类型视觉区分方案已落 art-bible §5.3(已定稿)。
|
||||
- **通用规格**:亮色描边+落影底盘轮廓通道硬规则;透明底 PNG;每敌种 2–3 版候选;护甲类型视觉区分方案已落 art-bible §5.3(已定稿)。
|
||||
- **来源**:✦ ComfyUI(prompt 卡 v2.0 §3,八敌种+L2 熊地精变体共 9 卡);理由同 §2.1——不复用。
|
||||
- **联动**:显示名以 `data/tables/enemy.tres` 与敌人系统文档为准(L2 熊地精=bugbear 复用,显示名熊地精系)。
|
||||
|
||||
@@ -132,8 +132,8 @@
|
||||
|
||||
- **要什么**:营地场景图 10 张——`scene_camp_bg`(背景层,不透明全幅=天空+远山+整片地面+门洞内一条通向远处的小路)+ `scene_camp_wall`(围墙层,透明底=横贯全宽密排木桩墙+两根门柱,门洞留透明缺口)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件)。钥石顶栏图标沿用现役 `icon_keystone`。
|
||||
- **来源/规格**:**✦ 插画级**(作者自跑远程生图管线);烛光黄 `#F6C667` + 木色 `#8A5A34`(art-bible §6.2);2× 源档出图、最近邻降采样到 1280×800;后处理=抠底/降采样/色板量化/描边统一。**出图登记照 [remote-gen-prompts](remote-gen-prompts.md) 头注轻量口径执行:用途+生成时间必注,其余参数有则记。**
|
||||
- **排期**:已执行(随迭代 47 卡④,2026-11-01 合回)。终稿落 `game/assets/art/scene/`,消费常量 `UiKit.TEX_CAMP_*`。
|
||||
- **现状**:已入包(2026-11-01 迭代 47 卡④——十件按锁定矩形 contain 落位、营地主色板 32 色重量化;用户游戏内终审如有置换意见,按同径替换)。
|
||||
- **排期**:营地交互重做**美术自立批**(不占迭代位;UI 贴图重构批已搁置)。终稿落 `game/assets/art/scene/`,消费常量 `UiKit.TEX_CAMP_*`。
|
||||
- **现状**:全占位。
|
||||
|
||||
### 3.6 字体(全局 UI 像素字,已定稿单套)
|
||||
|
||||
@@ -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);特效走运动+色相(现行取法——明度分层不动);现行占位 = 封印黑「旗」字形;印记常显形态可与 §3.3 旗面像素化合并出 | ✦/⚙ 自产(精灵+粒子,烟尘光点可 ♻ 改色) |
|
||||
| 封印特效 | 该动作的呈现名 = **封印**(art-bible §3.2):**施印瞬间特效 + 印记常显形态**两件 | 印记旗面走封印黑 `#000000`(现行封印色,色史归 art-bible §4.1);特效走运动+色相(§2:状态/情绪不走明度,空间光照可走);现行占位 = 封印黑「旗」字形;印记常显形态可与 §3.3 旗面像素化合并出 | ✦/⚙ 自产(精灵+粒子,烟尘光点可 ♻ 改色) |
|
||||
| 弹道/退场等动画密度 | 战斗表现输入清单,方案未定 | 样张锁动画方案后启动 | ♻ 通用弹道/光点/消散粒子改色为主 |
|
||||
|
||||
**现状**:三关舞台背景层与连续地面层已交付(2026-10-31 入包 `game/assets/art/scene/`:dungeon v10c / underdark v10c / deep_underdark v10c,均 1280×252;共用地面 v12b,1280×100,按作者裁保留原亮度 L≈0.132);小石台件已在役。
|
||||
@@ -187,7 +187,7 @@
|
||||
└─> 敌种呈现形态裁决 ✅已完成(全站桩,卡片式已弃)
|
||||
徽章(3.1)/节点图(3.2)/效果图标(3.4) —— 独立线,可随时插队
|
||||
♻ 复用件(3.2/3.4/3.5/3.6/演出粒子) —— 不依赖样张,可随时插队(入包闸 = §0 复用四道)
|
||||
字体(3.6)接入归表现批;营地场景图(3.5)已随迭代 47 卡④ 入包(2026-11-01)
|
||||
字体(3.6)接入归表现批;营地场景图(3.5)归营地交互重做美术自立批
|
||||
分辨率档位 ✅已定案(1280×800 + 1920×1080,2026-09-22 用户确认);art-bible §8 资产规范 ✅已成文(2026-09-23)——像素基准/画布/命名/帧数候选值就位,终值归样张批冻结
|
||||
```
|
||||
|
||||
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
# 美术圣经(Art Bible)— Dungeon Sweeper
|
||||
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.35,历次修订明细见 git 提交史
|
||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**;现行为 v3.30,历次修订明细见 git 提交史
|
||||
>
|
||||
> **Art Director Sign-Off(AD-ART-BIBLE)**:历次批均未签核——此为 Lean/Solo 模式(非 PHASE-GATE)下的现行治理状态;full 门禁签核时机待用户表态。
|
||||
> 风格基调:地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身。
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
**原则二|明亮为皮,明度分档为骨**(取自 Moonlighter、Peglin;锚定 P1 可读性前提、P4 探险氛围)
|
||||
|
||||
取 Moonlighter 的暖质地砖与亲和冒险者气质、Peglin 的明快全年龄洞穴色彩:全局暖倾向、无压抑。可读性由**格底明度分档**承载——已开格(数字所在格)明度走世界底层档(§2 明度分层;格态语义见《排雷玩法》;美术侧据此未开格走受光墙顶档、已开格走挖出空间沉暗档,与 §8.5 图底分离同轴),亮族数字墨直接画在格底上,直画对比 ≥4.5:1 恒成立。**数字色牌为后备手段**——仅复杂格底(物件纹样/变异纹带/微光叠加致格底对比不足)时垫牌后备(画法 §3.3、色值 §4.1)。格子本体美术放开:纹理、材质、装饰变体、类型纹样皆可入画;格底参数域见 §4.1 硬指标(分档明度域、饱和上限),仍避 Boss 禁入色相带。与参考之别:ITB 以半透明压暗板承载信息;本作格底功能分档(信息可读性服务)。验收:机检〈1–8 全档数字对所在已开格底对比 ≥4.5:1〉
|
||||
取 Moonlighter 的暖质地砖与亲和冒险者气质、Peglin 的明快全年龄洞穴色彩:全局暖倾向、无压抑。可读性由**格底明度分档**承载——已开格(数字所在格)明度走世界底层档(§2 明度分层;格态语义见《排雷玩法》;美术侧据此未开格走受光墙顶档、已开格走挖出空间沉暗档,与 §8.5 图底分离同轴),亮族数字墨直接画在格底上,直画对比 ≥4.5:1 恒成立。**数字色牌为后备手段**——仅复杂格底(物件纹样/变异纹带/微光叠加致格底对比不足)时垫牌后备(画法 §3.3、色值 §4.1)。格子本体美术放开:纹理、材质、装饰变体、类型纹样皆可入画;格底参数域见 §4.1 硬指标(分档明度域、饱和上限),仍避 Boss 禁入色相带。与参考之别:ITB 以半透明压暗板承载信息;本作格底功能分档(信息可读性服务,属 §2 明度分层允许域),情绪压暗仍禁。验收:机检〈1–8 全档数字对所在已开格底对比 ≥4.5:1〉
|
||||
|
||||
*设计测试:当格子美术与数字可读性冲突时,先调格底明度档,复杂格底垫色牌后备,不动格子画。*
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
> 本节服从第 1 节宣言「明快的地牢,轻快的扫荡,仔细的推理」。能量值为全作统一 0–10 刻度:0 静谧,10 沸腾。
|
||||
>
|
||||
> **明度分层(硬规则)**:明度按视觉层分带——**信息面层**(数字/UI 数据/预告/语义标识)最高、**世界中层**(格底顶面/角色/物件)居中、**世界底层**(背景/纵深/远景/已开空间)可更低;层间明度差服务空间纵深与光照(Moonlighter 式洞穴层次)。类型微光的明度梯度(§4.2)属信息面层内梯度,不扰世界层明度档。验收:目检〈三档明度带层间灰度可分〉
|
||||
> **明度分层(硬规则)**:明度按视觉层分带——**信息面层**(数字/UI 数据/预告/语义标识)最高、**世界中层**(格底顶面/角色/物件)居中、**世界底层**(背景/纵深/远景/已开空间)可更低;层间明度差服务空间纵深与光照(Moonlighter 式洞穴层次)。**情绪明暗禁令**:明度变化禁止用于**情绪表达**——画面禁止靠压暗/渐晕阴沉下来;roguelike 失败重开,世界无光暗记忆;Darkest Dungeon 式压暗仍在 §9.2 反面清单。**状态表达不受本禁令约束**:状态可用明度、色相、饱和、运动、元素任一手段承载。**允许域**:空间纵深、光照氛围、形体塑造。类型微光的明度梯度(§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 +84,15 @@
|
||||
### 2.2 轮末自动战斗结算
|
||||
|
||||
- **主情绪**:延迟兑现的清算爽感——扫雷之因逐行兑现为战果;完美封印(概念定义与兑付结构见《地图战斗资源》)的高光兑现——结算带高光行,以其为全场喝彩级的最高反馈档
|
||||
- **色彩性格**:情绪由色相与饱和承载——金橙主导、饱和拉高,明度分层不动;数字色转译为弹道色沿行推进
|
||||
- **色彩性格**:情绪不走明度(§2 禁令),色彩转向金橙主导、饱和拉高;数字色转译为弹道色沿行推进
|
||||
- **氛围形容词**:酣畅、递进、一气呵成
|
||||
- **能量**:7/10,随连击爬升
|
||||
- **承载元素**:兑现光带逐行横扫棋盘,开格化箭雨、印记化暴击砸向敌人,因果全程可见可归因。验收:目检〈兑现光带逐行横扫、因果可见可归因〉
|
||||
- **承载元素**:兑现光带逐行横扫棋盘,开格化箭雨、印记化暴击砸向敌人,因果全程可见可归因。验收:目检〈兑现光带逐行横扫、因果可见可归因;情绪表达不走压暗〉
|
||||
|
||||
### 2.3 清图奖励与换图
|
||||
|
||||
- **主情绪**:短促丰收——宝箱揭盖式的喜悦,三五秒即收,不拖节奏
|
||||
- **色彩性格**:全屏饱和峰值:金粒喷泉与彩纸迸发,饱和瞬时拉满后回落;明度分层不动
|
||||
- **色彩性格**:全屏饱和峰值:金粒喷泉与彩纸迸发,饱和瞬时拉满后回落;明度不动(§2 情绪禁令)
|
||||
- **氛围形容词**:丰收、轻快、干脆
|
||||
- **能量**:8/10,尖峰后速降
|
||||
- **承载元素**:最后一格碎砖中喷出的 GP 喷泉,叮当落入 UI 钱袋。验收:目检〈金粒喷泉短促收敛,饱和峰值后回落〉
|
||||
@@ -108,7 +108,7 @@
|
||||
### 2.5 战斗失败(冒险终结)
|
||||
|
||||
- **主情绪**:温柔的惋惜与「再来一次」的邀请——有分量、不惩罚,像合上一册冒险日志
|
||||
- **色彩性格**:情绪由色相承载——色相转暖灰掺暮蓝,明度分层不动;全作禁血红与黑场,唯此一拍最静
|
||||
- **色彩性格**:情绪不走明度(§2 禁令),色相转暖灰掺暮蓝;全作禁血红与黑场,唯此一拍最静
|
||||
- **氛围形容词**:惋惜、安静、余温、可再来
|
||||
- **能量**:1/10
|
||||
- **承载元素**:小人坐在碎砖上摇头掸灰,落下的封印印记逐枚化作光点收回行囊。验收:目检〈无血红与黑场;色相转暖灰掺暮蓝〉
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
### 3.2 环境几何
|
||||
|
||||
- **雷图棋盘呈现基准**:战棋地块质感 × 正俯视角——取 ITB 参考图(`references/itb_1.jpg`)的地表质感骨架:地块纹理多样、起伏体块感、岛缘/砖缘厚度、格上装饰小立牌;但相机一律正上方垂直向下,格线屏轴对齐,**禁等距/斜视角与菱形格**;形状语言仍守总纲圆角方形。砖缘厚度与岛缘立体面属形体塑造(顶面格底仍守 §4.1 参数域)。装饰小立牌属 ITB 参考的观感元素,本作不采用;装饰变体走氛围层纪律(纹理级/物件级形状家族,见下方装饰变体条),零信息纪律以原则三为准。验收:目检〈棋盘正俯视、格线屏轴对齐、无菱形格〉 ←原二;原三;§2
|
||||
- **雷图棋盘呈现基准**:战棋地块质感 × 正俯视角——取 ITB 参考图(`references/itb_1.jpg`)的地表质感骨架:地块纹理多样、起伏体块感、岛缘/砖缘厚度、格上装饰小立牌;但相机一律正上方垂直向下,格线屏轴对齐,**禁等距/斜视角与菱形格**;形状语言仍守总纲圆角方形。砖缘厚度与岛缘立体面属形体塑造,不属状态/情绪表达,不受 §2 情绪明暗禁令约束(属 §2 明度分层允许域;顶面格底仍守 §4.1 参数域)。装饰小立牌属 ITB 参考的观感元素,本作不采用;装饰变体走氛围层纪律(纹理级/物件级形状家族,见下方装饰变体条),零信息纪律以原则三为准。验收:目检〈棋盘正俯视、格线屏轴对齐、无菱形格〉 ←原二;原三;§2
|
||||
- **地砖**:一切环境体由同一圆角方形衍生:未开 = 完整凸砖,已开 = 同形缺角碎裂,雷 = 碎裂加巢穴纹(危险格玩家说法见《排雷玩法》);只以"完整度"叙事,不为状态新造形。验收:目检〈三格态仅以完整度区分,无新造状态形〉 ←原三;P4
|
||||
- **装饰变体(未开格氛围层)**:覆尘完整砖之上叠加裂纹/碎石斑/物件级点缀等装饰。物件级装饰(小石块/陶片/骨殖等)须走**专属装饰形状家族**(与信息元素形状语言隔离、灰度下不与任何逻辑元素同形),数量配额按图类型定(候选 3–5 种,样张已冻结,§1.4),一律残缺、内凹、低对比而自然后退(§3.4);零信息纪律(分布随机、与雷/安/类型零相关、灰度下禁止制造新分类特征、分布每板一次跨轮不复掷)以原则三为准,此处不赘。验收:目检〈物件级装饰不与任何逻辑元素同形;装饰残缺低对比后退〉 ←原三
|
||||
- **结果整格表达(已开格)**:已开格按当格结果整格表达(结果清单见原则三);画法——产资源格的家族物件纹样用 §3.1 道具点剪影语言(直线几何体)角嵌于砖缘,砖心与数字字形占位区留白(已开格仍承载数字);危险格巢穴纹较已开格更凶一档,形状+颜色双通道。验收:目检〈产资源格嵌家族纹样且砖心留白;巢穴纹更凶一档〉 ←原三;P3
|
||||
@@ -172,7 +172,7 @@
|
||||
- **高墙瓦片体系|光照纪律(单一北受光)**:北缘 = 墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘 = 墨线后直进纹理、不加修饰;南缘 = 墨线 + 立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。验收:目检〈北缘亮棱、南缘窄暗条;无宽亮带、无竖条纹栅栏感〉
|
||||
- **高墙瓦片体系|凹角收头**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出。验收:目检〈凹角收头与转角竖线同列,无悬空突出〉
|
||||
- **高墙瓦片体系|现行定稿**:`game/assets/art/board/terrain_*`(16 唯一墙瓦片 + 5 地面瓦片变体;词表与查表逻辑落码于 `board_world.gd`)。验收:机检〈terrain_* 素材 16 墙瓦片 + 5 地面变体在库,查表逻辑与词表一致〉
|
||||
- **高墙瓦片体系|高墙感 D 投影档与整板悬崖**:南临空墙格 = 立面口沿高光 2px `#C9AF83`(201,175,131——代码色 `BRICK`,与掩尘枚同源单点) + 双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);北邻墙地面格 = 三段渐隐投影(顶带 0.22 格边,α0.28 起);整板悬崖 = 板底/右侧 `DEPTH_PX 8px` 暗面 + 列分隔竖缝 + 岩层横纹 + 口沿高光 + 接触 AO + 底线收边 + 悬浮影(游戏 `board_world.gd` 同款结构件,样张脚本与游戏渲染常量同源)。验收:目检〈南临空墙格呈口沿高光+双段暗带;北邻墙地面格呈三段渐隐投影;整板悬崖结构件齐备〉
|
||||
- **高墙瓦片体系|高墙感 D 投影档与整板悬崖**:南临空墙格 = 立面口沿高光 2px `Color8(196,172,132)` + 双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);北邻墙地面格 = 三段渐隐投影(顶带 0.22 格边,α0.28 起);整板悬崖 = 板底/右侧 `DEPTH_PX 8px` 暗面 + 列分隔竖缝 + 岩层横纹 + 口沿高光 + 接触 AO + 底线收边 + 悬浮影(游戏 `board_world.gd` 同款结构件,样张脚本与游戏渲染常量同源)。验收:目检〈南临空墙格呈口沿高光+双段暗带;北邻墙地面格呈三段渐隐投影;整板悬崖结构件齐备〉
|
||||
- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准 = **灰度截图下仍可分类**。种类清单 = 地形变异六类型已定稿(地形变异 GDD;荆棘丛/藤蔓缠绕/岩浆/烬覆/落石/矿尘):**纹带形按轴只分两种**——伤害轴(荆棘丛/岩浆/落石)= 刺/缺排斥形,信息轴(藤蔓缠绕/烬覆/矿尘)= 遮蔽/缠绕形;同轴跨图类型只做色板变体(§4.3 `variant_tint` 槽),不做新纹形。岩浆纹带橙红须与危险红互辨(变异格不是雷,色相邻近有混淆风险);信息轴遮蔽呈现禁止侵入数字字形占位区(§1.4 验收 1 延伸)。验收:目检〈六类型纹带灰度下仍可分类,同轴不新增纹形;岩浆纹带与危险红互辨〉 ←原一(纹理位次第 4,让位于数字/小人/旗);原三
|
||||
|
||||
### 3.3 UI 形状语法(方向指导,细则见 §7.1)
|
||||
@@ -209,11 +209,10 @@
|
||||
- **图标槽柔和档 `#806A4E`(SLOT_BACKDROP_SOFT)**|BCard 图标槽底:对米色卡面 PANEL_DATA 3.69:1(深档 SLOT_BACKDROP #3E362C 8.53:1 过强、像硬贴补丁,故槽底另立柔和档),槽内白图标 5.14:1,满足 ≥3:1 图形参考线硬约束;仅 BCard 图标槽换档,B 区状态槽仍用深档 SLOT_BACKDROP。验收:机检〈槽内白图标对槽底 ≥3:1〉
|
||||
- **群伤微光暗档 `#A61D37`(TG_GLOW_AOE)**|类型微光群伤组的**明度档**(UI token 板,占 1 枚额度,**余量 3→2**):色相 349° 与亮档 `#FF2D55` 零移动,明度落 V=0.65 档——即 §4.2「微光 = 色相+明度双码」里明度侧的落法;与 §4.2「红只说危险」不相撞(微光组色相刻意错开语义红,见 §4.2「类型微光色带」条)。已在役(`ui_kit.gd` 常量与 `type_glow_test.gd` 守门断言),本节为登记转正。验收:机检〈ui_kit.gd TG_GLOW_AOE 与 type_glow_test 断言的 H/S 逐位一致、V 落 0.65 档〉
|
||||
- **站桩石板地台板身 `#968264`(STAND_STONE)**|战斗屏场景环境子板(分层②,不占 48 token 额度):B 区站桩地台板身与顶缘的材质色,跨三关恒定、不吃 §4.3 `wall_ambient` tint(地台是 §6.1 定死的世界中层承载面,随关卡变会破 §2 明度分层的层间关系)。转正前即在役。验收:机检〈战斗屏场景环境子板色数 ≤32 且本值在册〉
|
||||
- **营地场景环境子板(分层②,不占 48 token 额度)**:营地场景十层环境色按子板制落地——各层独立量化并集 272 色经最近对合并收敛至 **32 色、违例对 0**,十层按终版色板重量化回写(2026-11-01 迭代 47 卡④)。色值不另立逐值清单,事实源=入包素材 `scene_camp_*` 十件的量化产物;换算与合并过程登记于 artwork 工作区换算式登记(git 提交 3443890d 一线可考)。验收:机检〈营地屏唯一色数判据(shot_checker 闸件承载,2026-11-01 用户裁口径)〉
|
||||
- **重甲冷灰金属材质色 `#868C9A`(ARMOR_PLATE_COLD,token 名暂拟、随实装批登记)**|语义/UI token 板(**分层①**,占 1 枚额度,**余量 2→1**;用户裁):重甲档甲材质色;character-design §5.3 通道 = 甲面覆盖率 × 甲材质色,甲材质色按甲档统一,故石君王岩石板甲**同引本枚、不另计额度**;材质色跨屏恒定,落分层①不落分层②(环境色才随场景走)。过门情况:第二道门按宽松口径(禁止三通道全 <8)与在册色逐一比对,**无三通道全 <8 的对 → 过门**;§4.2 语义检查过门(色相 222°,不占红/金/蓝/紫赤/绿语义段、不在 300°–345° 禁入带;材质色不进信息层与语义物件岗)。对比实测:对深素底 `#282220` 4.65:1、对舞台底 `#3E362C` 3.52:1(图形 ≥3:1 门槛);相对亮度 L≈0.261 落 §2 世界中层 0.22–0.45 带。验收:机检〈本值入板后全板色对中三通道差全部 <8/255 的对数 = 0〉
|
||||
- **卓尔浅紫灰皮 `#A79CB8`(DRIDER_SKIN,token 名暂拟、随实装批登记)**|语义/UI token 板(**分层①**,占 1 枚额度,**余量 1→0,额度用尽**;用户裁):蛛化卓尔精灵上体皮色(体块主色,L≈0.355 落 §2 世界中层带)。过门情况:第二道门同上行口径,**无三通道全 <8 的对 → 过门**;§4.2 语义检查过门(色相 264°,避开 300°–345° 禁入带,紫赤独占仍归 `#8F2F6A`;不占本节五语义段)。对比实测:对深素底 `#282220` **6.04:1**、对舞台底 `#3E362C` **4.57:1**——**两处均过图形 ≥3:1 门槛**(本轮新算)。验收:机检〈本值入板后全板色对中三通道差全部 <8/255 的对数 = 0〉
|
||||
- **换色合并登记五处(用户裁,部件色落值)**|按本表「不过门即触发换色合并、禁加色绕行」执行,五处**零新增额度**(各件原拟新增候选一律不取):① 主角铲刃浅铁灰(原拟 `#A8AEB8`)→ 并入 `FOG_MEMORY_INK #C4C4C4`,**部件取值 = `#C4C4C4`**;② 主角提灯铜色(原拟 `#C08A3A`)→ 并入 `GOLD_DEEP #B07A14`,**部件取值 = `#B07A14`**;③ 食人魔酋长暖褐皮(原拟 `#B08050` / `#A97C4E`)→ 并入 `暖石 #B8A582`,**部件取值 = `#B8A582`**;④ 石君王灰皮(原拟 `#9A968C` / `#949089`,按**次级皮**口径——板甲视同体块主色、灰皮属次级皮故不受 L 0.22–0.45 约束)→ 并入 `STONE #786E62`,**部件取值 = `#786E62`**;⑤ 轻甲护甲图标木色 `#8A5A34`(深素底下 2.72:1 不过门;原拟新增木色亮档 `#C49A5E`)→ 并入 `WARM_FAINT #CDB88C`,**部件取值 = `#CDB88C`**(8.08:1)。**一色两岗代价已随裁定接受**:① 的 `#C4C4C4` 语义岗 = 迷雾记忆墨(§6.5)、② 的 `#B07A14` 属金族(§4.2 金只说收益与兑现)——两者均作角色随身件材质色用,不进信息层与语义物件岗;③④⑤ 无语义岗冲突。验收:机检〈五处部件取值点全部改指合并目标色,四枚原拟新增候选值零入板〉
|
||||
- **换色合并登记三处**|按本表「不过门即触发换色合并、禁加色绕行」执行,三处零新增额度:①地台顶缘受光线 `#C4AC84` 已并入 `#C9AF83`(掩尘枚 `BRICK`,与 §8.2 高墙口沿高光同枚)——源常量已删除(全仓零引用,退役断言测试串为预期例外),绘制点全部改指 `#C9AF83`;②地台描边 `#5A4C3A` 已并入 `#4C4038`(代码 `PLAZA_EDGE`,ui_kit 唯一落点)——源常量已删除,绘制点全部改指;③舞台底 `#3E342A`(代码 `STAGE_BG`)→ 并入 `#3E362C`(状态槽底)——随 §6.1 舞台背景插画批一并处置:背景资产接管该绘制面时本常量随绘制点退役或改指,禁单独为改值出批。程序化回退绘制与已落合并值的贴图保持一致。验收:机检〈①②两枚源常量全仓零引用,绘制点全部改指合并目标色〉
|
||||
- **换色合并登记三处**|按本表「不过门即触发换色合并、禁加色绕行」执行,三处零新增额度:①`#C4AC84`(地台顶缘受光线,代码 `STAND_STONE_RIM`)→ 并入 `#C9AF83`(掩尘,并与 §8.2 高墙口沿高光 `Color8(196,172,132)` 统一为同一枚);②`#5A4C3A`(地台描边,代码 `STAND_STONE_EDGE`)→ 并入 `#4C4038`;③`#3E342A`(舞台底,代码 `STAGE_BG`)→ 并入 `#3E362C`(状态槽底)。①②的绘制点全部改指合并目标色、源常量删除——程序化回退绘制与已落合并值的贴图保持一致;③随 §6.1 舞台背景插画批一并处置:背景资产接管该绘制面时本常量随绘制点退役或改指,禁单独为改值出批。验收:机检〈①②两枚源常量全仓零引用,绘制点全部改指合并目标色〉
|
||||
|
||||
**对比硬指标(可测)**:数字可读性由**格底明度分档**承载——棋盘数字亮墨 **L≥0.75** 对**已开格底 L 0.08–0.14** ⇒ 直画对比 **≥4.5:1 恒成立**(WCAG AA,样张逐组合抽测冻结);**色牌后备**:色牌素色恒定 **L≥0.70**、牌上数字深墨 **L≤0.027** ⇒ 任意牌墨组合 ≥4.5:1(实测样例:#33261B/#F5EAD3 = 12.3:1)。未开格底槽(含全部图类型)相对亮度 **L≥0.30**(墙顶面受光档)、HSV 饱和 **≤0.55**、避 Boss 禁入色相带;已开格底走世界底层档(见上)。物件识别不依赖格底(§5.1 轮廓通道),旗面、金色不作识别依赖。验收:机检〈亮墨对已开格底 ≥4.5:1;未开格底 L≥0.30、饱和 ≤0.55;任意牌墨组合 ≥4.5:1〉 ←原二;地形变异 AC-9
|
||||
|
||||
@@ -378,7 +377,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〉
|
||||
- **明度分层适用**:空间纵深与洞穴感走明度分层——世界底层档、光照氛围、色相饱和与形体承载(§3.2 同款逻辑)。验收:方向性
|
||||
- **明度分层适用**:背景氛围的情绪表达仍禁止以压暗/渐晕实现(§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` 槽〉
|
||||
@@ -391,12 +390,12 @@
|
||||
- **情绪与色域**:承载 §2.8——全作最暖:烛光黄 `#F6C667` × 木色 `#8A5A34`(§7.2),中等饱和,无棋盘式高对比。
|
||||
- **场景构成**:一张可点击的营地场景图(1280×800,与工程档同幅面)。远景=天空与远山;中景=**独立成层的木围墙**(密排竖木桩、桩顶削尖、不透缝无横木)与**半开的营地大门**;近景=中央篝火、左侧锻炉铁砧、右侧帐篷与宝箱、帐篷旁的笔记本,另有木桩与挂在围墙上的布告板。**中景带自底部往上约 2/3 处起**;**近景物件可覆盖围墙,唯一例外=中间门洞必须留空、不被任何物件遮挡**。七个可点物件的功能绑定归 `camp.md`,本节只定画法。
|
||||
- **图层拆分(10 张)**:场景按「远景/中景/近景要素各自成层」拆——`scene_camp_bg` 背景层(z0,不透明全幅)=天空+远山+整片地面+门洞内一条通向远处的小路;`scene_camp_wall` 围墙层(z1,透明底)=横贯全宽密排木桩墙(左右两段)+两根略高的门柱,**门洞处留透明缺口**透出背景层的小路;`scene_camp_board` 布告板(z2)、`scene_camp_gate` 门扇(z3)、`scene_camp_dummy` 木桩/`scene_camp_forge` 锻炉/`scene_camp_tent` 帐篷(z4)、`scene_camp_chest` 宝箱/`scene_camp_notebook` 笔记本(z5)、`scene_camp_campfire` 篝火(z6,最前)=八个物件层,各透明底单件一张。**帐篷**为承载件、不可点。围墙独立成层的好处:桩高可单独锁死、门洞做透明缺口、背景层不混入暖木色。提示词与坐标 = `camp-scene-prompts.md`,消费常量 = `asset-path-map`。
|
||||
- **交付形态**:**背景层 1 张(不透明全幅)+ 围墙层 1 张 + 八个物件层 = 10 张**,其中 9 张透明底、1 张不透明全幅 + 一份坐标表(贴图矩形/命中矩形/z 序)。**篝火例外**:命中矩形只取下半(火堆),上半(火焰)只作特效展示。围墙层落盘按细条裁,不做全幅(全幅约 3.9 MB 显存,细条约 0.35 MB);**实装落盘矩形 `(0, 261, 1280, 49)`、底锚 y310**(Y 压缩按桩身 43px 终稿锁定值精确,落带内〔规划带 (0,240,1280,70)〕;备选 A′〔带高 70/桩身 62〕存档 artwork 同目录——2026-11-01 迭代 47 卡④ 实装)。
|
||||
- **交付形态**:**背景层 1 张(不透明全幅)+ 围墙层 1 张 + 八个物件层 = 10 张**,其中 9 张透明底、1 张不透明全幅 + 一份坐标表(贴图矩形/命中矩形/z 序)。**篝火例外**:命中矩形只取下半(火堆),上半(火焰)只作特效展示。围墙层落盘按细条 `(0, 240, 1280, 70)` 裁,不做全幅(全幅约 3.9 MB 显存,细条约 0.35 MB)。
|
||||
- **色彩与氛围依据**:营地场景的冷暖分工(冷的大面积后退、暖的小面积前进)、空气透视三档与大面积用色规则 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`。本节不复述色值;新增环境色按 §4.1 场景环境子板条款登记(每屏一套、≤32 色、过 §4.2 语义检查与 RGB 三通道差 ≥8/255 两道门)。
|
||||
- **交互四态(零新素材,全部程序化)**:悬停=**轮廓亮边 + 浮动**(浮动须整像素吸附);按下=位移 1px;**禁用/未解锁=降明度 + 去饱和**(§2 已缩窄至情绪域,状态表达不受限),**仅锻炉(锻造工坊)与宝箱(秘宝匣)两件会有此态**;已解锁=常态。
|
||||
- **钥石余额**:常驻画面顶部的一行小 HUD 栏(高 48px,**浮层覆盖,不挤压画面**)。
|
||||
- **事实源边界**:解锁条件/价格/存档写入归元进度 GDD 与 `MetaProgression`;素材命名与路径归 `asset-path-map`;清单登记归 `art-assets-checklist` §3.5。
|
||||
- **已裁(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 追认现值(各素材第一张样本稿已过审为终稿,重定即触发重出,不裁)。
|
||||
- **待裁(本节不填数,由作者定)**:① **营地环境子板色值**——色彩脚本只给定色方向,落地色值属 §4.1 单点,须过 §4.2 语义检查与 RGB 三通道差 ≥8/255 两道门;② **门扇与门洞的高宽比例**——中景带自底部往上约 2/3 处起之后高度有限,门扇高度远超围墙带,两者关系待作者重定;③ **帐篷的落地尺寸**——帐篷体量大于被替换的旧承载件,尺寸待作者重定;坐标草案的现行出口见 `camp.md` 的指路句。
|
||||
|
||||
### 6.3 三关图类型色板候选(board_type 换挡方向)
|
||||
|
||||
@@ -423,6 +422,7 @@
|
||||
- **记忆态可读性判据**:数字对合成底**归一后(真实渲染面)三关 ≥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 +516,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 倍,正落本节乙类非整数缩放许可范围,**连豁免都不用再办**),待决标注注销;
|
||||
- **站桩立绘显示档 = 三档绝对高**:小型 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 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
|
||||
- **站桩立绘源档 = 八敌逐键定档**:主角 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 与盒内居中在高盒下拉开血条-立绘空洞。验收:目检〈血条一线对齐、大体档溢出不压轮次指示〉
|
||||
- **站桩石板地台件**:`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 +557,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 +566,7 @@
|
||||
|
||||
| 反面参考 | 违反条款 | 立法理由 |
|
||||
|---|---|---|
|
||||
| **Darkest Dungeon** 式压暗 | §9.2 反面基准(风格取向记录,非禁令——压暗属普通设计决策,逐案走样张与目检) | 失败重开,世界无光暗记忆——明亮基调下不以整屏变暗作常态表达 |
|
||||
| **Darkest Dungeon** 式压暗 | §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(取值在圣经 §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。
|
||||
**判据或口径**: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。
|
||||
|
||||
**已知坑与适用边界**:截图必须由窗口化 `shot_probe` 产出;headless 没有渲染面,截图是空的。`shot_meta.json` 缺失时 B/C/D 直接判 meta 几何缺失,不会跳过。尺寸断言按「任一边等于 1280」放行;1920×1080 截图两边都不等于 1280,会被判非工程档,与文件头「1920 档 1.5×」说明不一致,按源码为准。焦点预算是目检项,机检不判。
|
||||
|
||||
|
||||
+40
-33
@@ -1,4 +1,4 @@
|
||||
# 生图提示词卡(角色级资产)v2.13
|
||||
# 生图提示词卡(角色级资产)v2.12
|
||||
|
||||
> **性质**:美术清单生图通道(✦ 用户自跑,现走线上 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,27 +375,25 @@ 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 组四 · 站桩立绘按档派生(9 件,不重画)
|
||||
### 5.4 组四 · 主角与三 Boss 立绘重产(4 件)
|
||||
|
||||
> 本节是站桩立绘的**按档派生**工单:现有形象即终稿、禁改形象;清晰度靠保留原稿分辨率+显示按三档绝对高缩放解决,不重画、不生成。
|
||||
|
||||
**九件共用字段**
|
||||
**四件共用字段**
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| ① 路径与命名 | `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 现役写法) |
|
||||
| ② 尺寸档与像素网格 | **源 = 原稿像素网格**(八敌 `pixel-1`、主角手修 2× 三态稿),紧裁后原样入包,**禁再降采样**;**显示档 = 三档绝对高**——小 96(地精/巨蜘蛛/灰矮人)、中 144(主角/钩爪恐魔/熊地精)、大 188(食人魔酋长/蛛化卓尔/灰矮人石君王),定值单点 = art-bible §8.2;显示倍率 = 档高 ÷ 源高,NEAREST,宽随源图比例(高定宽随、纵横倍率恒等,禁方框强拉);九宫格 = 否 |
|
||||
| ③ 色板约束 | 色值以**现役稿**为准,本批零新增色、零换色;单张像素域色数 ≤32(`pixel-1` 与手修 2× 稿实测均 ≤32)。`#A79CB8` 保留在板、本批不消费,色板额度议题另批讨论 |
|
||||
| ② 尺寸档与像素网格 | **源档高逐键** = 主角 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 |
|
||||
| ④ 明度带归属 | **世界中层 L 0.22–0.45**(§2 世界中层 = 格底顶面 / 角色 / 物件)——判定对象 = 角色体块主色。描边与落影底盘属轮廓通道(character-design §5.1)、紫赤点缀属 §4.2 色相独占的语义色,两者都不按明度带判定 |
|
||||
| ⑤ 是否程序化件 | 否 → **零绘制**:源 = 现有原稿,本批不生成、不重画(现有形象即终稿)。§7.1 占位完成条款对本组不适用——现役稿即终稿 |
|
||||
| ⑤ 是否程序化件 | 否 → **须手绘**。口径 = **精细手绘像素**(2026-10-14 用户裁:仍是像素风,手绘像素簇 + 更高源密度;不是插画、不是 HD-2D、不回退高清图);**禁 AI 降采样当终稿**。现行 AI 降采样立绘 = 占位态,按 §7.1 占位完成条款须在终稿闸前换代 |
|
||||
| ⑥ 验收判据 | 见下方共用验收 |
|
||||
| ⑦ 操作要点 | 见下方派生要点(零绘制,不产生图提示词) |
|
||||
| ⑦ 出图操作要点 | 见下方工艺要点(手绘,不产生图提示词) |
|
||||
|
||||
**共用验收(四件都要过)**
|
||||
|
||||
- ←机检: 逐件显示档高与归档一致(小 96 / 中 144 / 大 188),过滤 = NEAREST,纵横倍率恒等;源高低于档高者按已接受放大项登记(主角 92 / 钩爪 94 / 熊地精 123 / 地精 90 / 巨蜘蛛 81)
|
||||
- ←机检: 源档高逐位等于 48 / 74 / 90 / 94,显示 = 源 ×2(96 / 148 / 180 / 188),过滤 = NEAREST
|
||||
- ←机检: 单张像素域色数 ≤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 独占)
|
||||
@@ -403,39 +401,48 @@ 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,再其余四敌(同管线,无绘制差异)。
|
||||
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 为已接受项。
|
||||
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 上限拦截)。
|
||||
|
||||
**5.4.1 主角(中档 144)**
|
||||
**5.4.1 主角(源 48 / 显示 96)**
|
||||
|
||||
- 形象 = 现役稿即终稿,character-design §5.4 形象锚已兑现于现役素材;朝向沿用现役,不重新裁定
|
||||
- 色 = 以现役稿为准(§5.5 缺口 4 的额度账随色板议题另批处理)
|
||||
- 源 = `hero-ref/edit/normal_2x.png`(内容 88×92);attack/hit 用 `attack_2x.png`/`hit_2x.png`
|
||||
- 形象 = 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 生成顺序口径);重产沿用现役朝向,不重新裁定
|
||||
|
||||
**5.4.2 食人魔酋长 ogre_chief(大档 188)**
|
||||
**5.4.2 食人魔酋长 ogre_chief(源 74 / 显示 148)**
|
||||
|
||||
- 档位 = 大型(三场 Boss 战全归大型);形状仍守精英档剪影——普通怪形 + 恰一处垂直延伸(头顶骨与羽冠),底盘不变
|
||||
- 甲档 = 轻甲(皮条/骨饰点缀、裸身为主,零甲板零符纹)——现役稿已如此
|
||||
- 源 = `ogre_chief/pixel-1.png`(158×189);显示宽 157 超桩盒 150 为已接受项
|
||||
- 档位 = 精英档(形状仍守精英档:普通怪形 + 恰一处垂直延伸 = 头顶骨与羽冠,底盘不变;第一关关底 Boss 战,不升 Boss 形)
|
||||
- 甲档 = 轻甲 → 甲面覆盖率 ≈ 0:皮条 / 骨饰点缀、裸身为主,零甲板零符纹
|
||||
- 主色域 = 暖褐皮、圆肚;**暖褐皮 = `暖石 #B8A582`**(2026-10-28 用户裁:换色合并、零额度;体块主色带 §5.4 ④ L 0.22–0.45 校验 L≈0.387 ✓;character-design §5.5 该行原只有形容词)
|
||||
|
||||
**5.4.3 蛛化卓尔精灵 drider(大档 188)**
|
||||
**5.4.3 蛛化卓尔精灵 drider(源 90 / 显示 180)**
|
||||
|
||||
- 档位 = 大型;紫赤 `#8F2F6A` 色相独占 + 形状独占双通道维持现役稿
|
||||
- 甲档 = 魔法护甲(符纹饰带)——现役稿已如此
|
||||
- 源 = `drider/pixel-1.png`(140×189);皮色 `#A79CB8` 保留在板、本批不消费
|
||||
- 档位 = 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 区间定,血条仍落底)
|
||||
|
||||
**5.4.4 灰矮人石君王 duergar_king(大档 188)**
|
||||
**5.4.4 灰矮人石君王 duergar_king(源 94 / 显示 188)**
|
||||
|
||||
- 档位 = 大型(锐角与不对称);紫赤独占 + 岩石板甲维持现役稿色
|
||||
- 源 = `duergar_king/pixel-1.png`(133×189)
|
||||
- 档位 = 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
|
||||
|
||||
### 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 已程序导出;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 |
|
||||
| 主角战斗小人(待机/攻击/受击三态) | ✅ 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 |
|
||||
| 意图准备动画三档 | ◐ | [art-bible](art-bible.md) §4.6.4 |
|
||||
|
||||
## P2 图标与格面(可与样张并行)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|---|---|---|
|
||||
| `character/` | 角色棋盘帧 + B 区立绘 | character_hero_idle/walk(棋盘 32×32 朝左基准)、character_hero_portrait(B 区立绘 46×48)+ character_enemy_<键> 八敌 |
|
||||
| `fx/` | 特效帧 | shovel_swing_f0/f1/f2(挥铲三帧,握把锚 (18,12)) |
|
||||
| `scene/` | **场景图**(背景层 + 地面层 + 围墙层 + 可点物件层;营地场景图首批) | 营地 10 张:`scene_camp_bg`(背景层,不透明全幅=天空+远山+地面+门洞小路)+ `scene_camp_wall`(围墙层,透明底、门洞留透明缺口)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件,不可点)+ `scene_stage_bg_dungeon` / `scene_stage_bg_underdark` / `scene_stage_bg_deep_underdark`(B 区战斗舞台背景层三关变体,✦ 插画级,顶部空区+远景天然洞壁,不画近景地面,按 board_type 选图)+ `scene_stage_ground`(B 区连续近景地面层,✦ 插画级,泥土/砂砾/碎石+顶缘分隔带,跨关恒定)+ `scene_stage_plaza`(B 区角色脚下小石台,48×12 九宫格件)——B 区战斗舞台背景与连续地面 2026-10-31 已入包;营地 10 张 2026-11-01 随迭代 47 卡④ 入包(`scene_camp_*` 十件在役、`TEX_CAMP_*` 十键消费;用户游戏内终审如有置换,按本行键名同径替换) |
|
||||
| `scene/` | **场景图**(背景层 + 地面层 + 围墙层 + 可点物件层;营地场景图首批) | 营地 10 张:`scene_camp_bg`(背景层,不透明全幅=天空+远山+地面+门洞小路)+ `scene_camp_wall`(围墙层,透明底、门洞留透明缺口)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_tent`(承载件,不可点)+ `scene_stage_bg_dungeon` / `scene_stage_bg_underdark` / `scene_stage_bg_deep_underdark`(B 区战斗舞台背景层三关变体,✦ 插画级,顶部空区+远景天然洞壁,不画近景地面,按 board_type 选图)+ `scene_stage_ground`(B 区连续近景地面层,✦ 插画级,泥土/砂砾/碎石+顶缘分隔带,跨关恒定)+ `scene_stage_plaza`(B 区角色脚下小石台,48×12 九宫格件)——B 区战斗舞台背景与连续地面 2026-10-31 已入包;登记超前:营地场景图批落地前盘上未建 |
|
||||
| `board/` | 砖面/封印印记(三关换皮 + 封印印记件) | terrain_floor_<board_type>_<n>.png(三关地面系)、seal_mark.png |
|
||||
|
||||
## 4. 坐标/朝向等工程侧约定备查
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 营地场景 · 分层生图提示词卡 v1.4
|
||||
# 营地场景 · 分层生图提示词卡 v1.2
|
||||
|
||||
> **性质**:营地场景图(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 为准。
|
||||
@@ -105,8 +105,6 @@ Do not add any of the following: realistic, photorealistic or 3D rendering; pain
|
||||
| 笔记本 | 5 | 100×80 | 200×160 | §3.7 |
|
||||
| 篝火 | 6 | 160×160 | 320×320 | §3.8 |
|
||||
|
||||
**落位实测(2026-11-01 迭代 47 卡④,锁定矩形 contain 呈报备案追认值)**:布告板 130×121/门扇 240×211/木桩 120×180/锻炉 240×193/帐篷 251×170/宝箱 106×90/笔记本 100×66/篝火 160×149——贴图锚定=底中心(帐篷/锻炉/宝箱/篝火/笔记本)+顶锚(门扇悬挂/布告板挂墙);命中矩形按锁定值。换算式登记(源 2× 档→落位尺寸)存 artwork 工作区,git 提交 3443890d 一线可考。
|
||||
|
||||
**3.1 布告板(→ 图鉴)**
|
||||
```
|
||||
A wooden notice board hung on a wall, with two or three blank pinned papers, no writing on them, one corner of a paper slightly curled; wood brown #8A5A34 frame, warm tan stone #C9AF83 papers.
|
||||
@@ -164,8 +162,6 @@ A campfire made of a ring of grey stones around crossed stacked logs with a warm
|
||||
- 目检:密排竖木桩、桩顶削尖、不透缝、无横木;门洞 x1040–1520 **完全透明**(透出 bg 小路)、无门扇
|
||||
- 单张色数 ≤32(量化后)、缩放为整数倍无拉伸痕迹
|
||||
|
||||
> **量化与缩放实况注记(2026-11-01 迭代 47 卡④ 落地)**:围墙层与八件已按营地主色板 **32 色重量化回写**(art-bible §4.1 子板行——违例对 0);落位缩放实况=源 2× 档经后处理换算为锁定矩形 contain(×0.1934–0.4404 非整数倍),「整数倍无拉伸痕迹」判据的机检面以落位实测尺寸与目检兜底执行。
|
||||
|
||||
**八件**
|
||||
- 单件居中、四周留边、整件完整不出画
|
||||
- 底色均匀 `#F2F2F2`、奶油描边闭合(去底脚本要能识别到屏障)
|
||||
@@ -175,10 +171,10 @@ A campfire made of a ring of grey stones around crossed stacked logs with a warm
|
||||
|
||||
---
|
||||
|
||||
## 5. 已裁(2026-11-01 用户裁,随迭代 47 卡④ 开工门销号;不再阻塞坐标表与定稿)
|
||||
## 5. 待裁(不阻塞出图,阻塞坐标表与定稿)
|
||||
|
||||
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),本卡仍不复述色值
|
||||
1. **帐篷尺寸**:现 300×170 待重定,重定后按比例重出
|
||||
2. **门扇高宽**:现 240×220,围墙带只有 43px 高;围墙独立成层后门扇叠在门洞上,两者高宽关系待裁
|
||||
3. **布告板位置**:现 y330 悬空在围墙下方,建议 `(845, 268, 130, 140)` 挂回围墙带
|
||||
4. **描边颜色追认**:本卡按奶油色(去底技术前提);若改参照物的深灰褐,需换去底方案
|
||||
5. **营地环境子板色值**:art-bible §4.1 为数值单点,色值待作者拍板,本卡不复述
|
||||
@@ -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 站桩立绘显示档条,本档指路不复述)**:三档绝对高 96/144/188,逐件归档、源档与缩放规则一律见圣经 §8.2——**数值权威 = 圣经 §8.2**(定值单点纪律,圣经 §8 头注);归档经三体型约束校验(§5.2)确认;派生管线与排版规范同归圣经 §8.2。
|
||||
- **立绘源档高(定值单点 = 圣经 §8.2 立绘源档表,本档指路不复述)**:八敌 36–94 逐键定档 + 主角 48 不变、显示一律 ×2 整数档(NEAREST)——**数值权威一律圣经 §8.2**(定值单点纪律,圣经 §8 头注);定档经三体型约束校验(§5.2)确认;派生管线与排版规范同归圣经 §8.2。
|
||||
- **钩爪恐魔「宽≥高」豁免登记(待样张终审)**:定档源 58 高窄条束状剪影(长臂圆钩),宽高比 <1,与 §5.1 普通怪「近圆墩形宽≥高」冲突——按体量=生物天性(§5.2)登记豁免:以长臂束状轮廓为识别锚,不强行压成墩形;样张(圣经 §1.4)终审复验灰度剪影可辨性。
|
||||
|
||||
## 5.6 意图准备动画三档(规格承载节)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## 文档状态
|
||||
|
||||
- 版本:1.28
|
||||
- 最近更新:2026-11-02(迭代 47 反哺点落文——营地域六件归属行增补〔UiCampModals/CampSceneView/营地四子界面视图族/camp_esc_overlay+MainEscFlow/CampHudBand/CampRefreshBus〕+D1 营地面现状化+ui_registry 行子容器与合同槽换代;沿革查 git)
|
||||
- 版本:1.26
|
||||
- 最近更新:2026-10-30(迭代 42 反哺点落文——ShopSession 行对外暴露格按委托壳拆除后实测直连面重写:完整直连方法清单+set_shop_pool 调用面改述;沿革查 git)
|
||||
- 引擎:Godot 4.7.2-stable(GDScript 单语言;2026-09-07 锁定)
|
||||
- 评审模式:solo(`review-mode.txt`,2026-09-12 用户拍板——TD/LP 门禁按 solo 跳过,用户授权架构产出自主推进、迭代期回调)
|
||||
- 覆盖 GDD:现行全集(根索引=`design/gdd/02-系统清单.md`,文档树构成以根索引为准)
|
||||
@@ -89,7 +89,7 @@
|
||||
| **SlotStore**(`core/slot_store.gd`,D3 存档槽位域件,已建件——迭代 41 存量债批) | 存档三槽位文件域唯一所有者(自 save_manager 收编槽位族):槽位挂载与模板、激活与建删、槽位摘要窥视(peek 族);改名覆盖原语(_rename_overwrite)留 save_manager(IO 原语单点) | 槽位读写原语族+摘要投影(公开 10/类变量 4;SaveManager 与菜单消费方经 `save.slots.` 直连) | save_manager(注入路径与模板配置);FileAccess——LOW |
|
||||
| **MetaStateStore**(`core/meta_state_store.gd`,D3 存档内元进度数据件,已建件——迭代 41 存量债批) | 存档内元进度数据的读写唯一层(自 save_manager 收编 accessor 族:钥石/道具解锁/树节点与叉侧/锻造路线/同调/真视/计数器/摘要——数据读写原语,不含业务规则);save_manager 与 meta_progression 双 God Class 的共同病根=该数据域摊在两文件,本件即其归一 | 写口原语 13+只读投影 6(tree_state/forge_state/attune_state/career_state 四投影+keystone_balance/item_unlocked)=公开 19(add_keystone 私有 `_add_keystone`),零 _data 副本零缓存;MetaProgression 业务层与营地/锻造 UI 消费方经 `save.meta_state.` 直连(meta 的纯转发读方法已退役) | save_manager(序列化接线);FileAccess——LOW |
|
||||
| **RngService**(autoload,D2) | master_seed、各流 scope 计数器(封闭流词表现值以 `RngServiceCore.PURPOSE_TAGS` 为准;增流走 P1 评审) | `set_master_seed()`(唯一调用方 C2);`open_stream(stream)` 有状态门面;`derive_at(stream, scope)` 纯函数;只读计数器 | ConfigDB(无) | 无——纯逻辑 |
|
||||
| **action_economy**(A3) | AP/RAP 账本、last_round_end、轮次相位机 {Idle, Active, Waiting}、结束仲裁;dig_paid 命中施加抽私有单点(_apply_dig_paid_hits——矿脉储能入池面) | 意图与通知布尔面:notify_round_start / request_dig / request_stop / request_refresh(收折后价入参——折价计算归 BattleRefreshFlow 单点)/ notify_settlement_done / notify_combat_end;预授权通道:grant_reserve_ap / finish_round_stop;ap/rap/phase 只读 | ConfigDB(ActionEconomyTable);effect_engine(事件发射 dispatch——基础层内同层引用);B1 回发 next_round_mode/battle_over | 无 |
|
||||
| **action_economy**(A3) | AP/RAP 账本、last_round_end、轮次相位机 {Idle, Active, Waiting}、结束仲裁 | 意图与通知布尔面:notify_round_start / request_dig / request_stop / request_refresh / notify_settlement_done / notify_combat_end;预授权通道:grant_reserve_ap / finish_round_stop;ap/rap/phase 只读 | ConfigDB(ActionEconomyTable);effect_engine(事件发射 dispatch——基础层内同层引用);B1 回发 next_round_mode/battle_over | 无 |
|
||||
| **variation 定义**(A4) | VariationTable schema(现行 6 行:六 region——贫矿脉 global 行 2026-10-16 用户令屏蔽删行,global 型机制代码保留)与 F1 候选池门控谓词 | `variant_pool(level_index, board_type, node_type="")`(门控三维:unlock+board_types+节点身份——global 行仅 boss/elite 入池,缺省跳过零扰动);`global_variant_row(level_index, board_type, node_type)`(全局型单行决议访问器) | ConfigDB(VariationTable) | 无 |
|
||||
| **status_container**(B3) | 状态实例容器(战斗域生命周期、跨轮跨图延续) | 5 API:notify_battle_start / apply_effect / query_active / decay_round / notify_battle_end | ConfigDB(StatusEffectTable) | 无(RefCounted) |
|
||||
| **effect_engine**(内容数据驱动扩展批 iter17 建域) | 零运行时可变状态的系统(见「架构原则」条 8):全同步解释器——事件由宿主在明确代码点位显式调 dispatch 进件,命中序=装载声明序(feats→treasure→树规则)写死保种子复放,不是事件总线/事件循环;事件/事实/效果类型/算子四词表封闭 + 树规则合成订阅六键(posture 每关首轮窗/sense 两组限次 counter 闸·adv_ 前缀走冒险桶跨战斗持久) | rebuild(每战重建订阅表)/`dispatch(ctx, event, payload, facts)`/`validate_trigger_row` + 静态计数口 `counter`·`counter_add`·`counter_spend`·`bucket_of`·`counter_id_legal`(计数器读写只经 EffectContext 引用束,无导出/灌入接口) | ConfigDB(feats/treasure 行集)、EffectContext(四桶引用由宿主持有:round/battle 桶归编排器、level 桶归关卡域、adventure 桶归应用层——RunLedger 已随迭代 39 拆件批退役,桶字典由 adventure_runner 持有) | 无(零随机全确定) |
|
||||
@@ -119,8 +119,7 @@
|
||||
| **battle_orchestrator**(B1) | 段序骨架(七段结构冻结)、轮 facts 缓冲、可回放步骤流、[明细行协议](../../gdd/systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋)(基础行型+扩展行两层 + 字段分层,词表以 owner「明细流水」节为准)、条款③⑤闭合对账、**战斗域全部跨系统调用权**、效果引擎宿主面与树规则挂点施加面(**已拆出 BattleFxHost 并实装(2026-10-28 迭代 40——battle_fx_host.gd/reward_grant.gd 两件在库,效果引擎宿主面归 BattleFxHost 行承载,拆件细则见「战斗编排域拆件定稿」节);2026-10-29 零豁免裁设计闭合:RoundCycle/BattleFacts 两新件随批(宿主 38→15、S13 豁免第三条注销——见下两行与定稿节轴丁)**) | `begin_battle(encounter)`;窗口/刷新意图提交面(扮演 D1);`level_clear()`;步骤流(输出契约) | B2/B3/B4 全调用面(**唯一调用方=B1 域文件集:battle_orchestrator.gd + settlement/ 五段段类 + damage_controller + 拆件后三新件**);A3 request_refresh 代理 + 回发;A2 install/retire + 三事件订阅;A1 generate(attempt 非 scope 事件);D2 SettlementTable + set_reload_gate | 无(零随机、禁 await——切片 88 冒烟实证可回放) |
|
||||
| **BattleFxHost**(`core/battle_fx_host.gd`,B1 域·效果引擎宿主件,2026-10-26 编排域拆件设计定稿——实现批建件) | 效果引擎挂点施加面唯一承载(自 battle_orchestrator 收编 fx 族):事件发射与事实判定(_fx_emit/_fx_fact)、主角 AC 基线与本轮/本战 AC 累积、bonus_delta 四轴累积(战末宿主 drain 汇入账本)、揭示待兑队列与兑付(含 _fx_revealing 再入闸——同步信号回环防二次缓冲二次兑付)、树规则合成订阅的 round/battle 桶计数器持有(桶随编排器生灭语义不变) | 构造=引用束注入(防 _init max-parameters 新键);bind 时注入 emit Callable(行发射经宿主协议面单点)与兑付 Callable(RewardGrant 授予面——揭示队列 flush 回调);只读投影分两类消费方:AC 累积/bonus_delta 供宿主呈现快照与战末 drain,揭示态供宿主 A2 订阅旁路判定;`luck_bias_for(cell_count) -> int` 偏运合计查询口(体内三参形 dispatch——reward_roll 发射与 bias_rewards 求和自 RewardEconomy 收回本件,RewardEconomy 经注入 Callable 消费) | EffectEngine(EffectContext 引用束);RngServiceCore(fx_reveal 封闭流——除该流外零随机);A2 minesweeper(板读与 fx_reveal 调用);A3 action_economy(queue_ap_delta/charge_layers 读);B3 status_container 读;B2 unit_container 读(hp_ratio);DamageController(set_hero_ac_override);ConfigDB;encounter 引用 | 无(零 await——效果引擎全同步解释器) |
|
||||
| **RewardGrant**(`core/reward_grant.gd`,B1 域·奖励授予件,2026-10-26 编排域拆件设计定稿——实现批建件) | 奖励授予唯一兑付管线(自 battle_orchestrator 收编 grant 族):极性字典路由与封印溢价兑付(_grant_routed)、三种行应用(flat/percent/gp)、伤害单据提交面(_submit 族)、清图奖励与身份授予、秘宝使用编排(use_treasure 四步)、战斗域 GP 缓冲 escrow 与授予段暂存伤害闸(_defer_board_damage/_deferred_board) | use_treasure 编排入口(treasure_interact_controller 经组合根现值回调直连);授予入口(结算段类直调);只读投影(escrow 读与 GP 掉落合计——宿主呈现快照与 _roll_gp_drop 消费) | B4 RewardEconomy(polarity_row/grant);B1 域 DamageController(开单);BattleFxHost 引用(treasure_used 事件发射与计数器消耗);A2 minesweeper(校验回调/fx_reveal/remove_mines_in);A3 action_economy(相位闸/grant_ap/queue_ap_delta);B3 status_container(apply_effect/池读);B2 unit_container(hero_unit/heal);ConfigDB;注入 emit Callable、宿主 fatal Callable(battle_result 状态机唯一写点留宿主)、宿主 attune 读投影(_attune_safe_seen)、encounter 账本引用;TreasureItems 经宿主 setter 转发注入(库存注入时序在宿主构造之后) | 无(零随机零 await——授予期零随机口径,roll 值授予前已定) |
|
||||
| **damage_controller**(B1 域·伤害单据管线) | 伤害单据唯一兑现管线(单据九栏目声明→分量掷点/克制修正/伤害下限归一→明细行发射;双方同规);`damage_taken` 唯一发射口(_emit_hero_damage_taken)+唯一消费族(_apply_hero_damage_effects)——敌方攻击/地形/危险格/错误封印直伤四路径统一发射,board_flat_hero 结账前统一发射 | 兑付入口(编排侧开单)+ fx 揭示回调注入位 | B2 单位实例(单据目标传入);B1 注入(EffectContext 引用束与编排回调) | 无 |
|
||||
| **BattleRefreshFlow**(`core/battle_refresh_flow.gd`,B1 域·换图刷新流程件,已建件——迭代 46) | 弃图刷新流程唯一承载(自编排器收编):刷新可用性门(相位/每轮上限/储备门槛)、折后价单点(_effective_refresh_cost——专长行声明序第一命中、floor 取整、can 与执行同源)、换图执行序(扣储备→used+1→旧资源退役→旧板退役→装新板→board_refreshed 成功事实登记) | can_refresh/execute 两公开入口;调用路径呈现层→编排器两公开方法单行转发→本件;A3 request_refresh 收折后价入参 | 编排器(宿主,B1 域内唯一调用方约束同族适用);A3 action_economy(折价扣费与拒绝判据);A2 装板壳(装板与退役);RewardGrant(apply_board_refreshed 成功事实消费);feats 折价行经 ConfigDB;static_scan S7_B1_SET 已增补本件(判据口径不变) | 无(零随机零 await) |
|
||||
| **damage_controller**(B1 域·伤害单据管线) | 伤害单据唯一兑现管线(单据九栏目声明→分量掷点/克制修正/伤害下限归一→明细行发射;双方同规) | 兑付入口(编排侧开单)+ fx 揭示回调注入位 | B2 单位实例(单据目标传入);B1 注入(EffectContext 引用束与编排回调) | 无 |
|
||||
| **BoardInstaller**(`core/board_installer.gd`,B1 域·装板域件,2026-10-29 尾波清偿批设计闭合——实现批建件,细则见「战斗编排域拆件定稿」节轴丙) | 棋盘安装与变异布置域唯一承载(自 battle_orchestrator 收编装板族六函数):生成受理、全局作用域变异决议、资源摇号资格域组装(roll_board 恰一次)、region 变异决议与连通片布置;BoardGenerator 实例随件自建自接线(宿主成员退役)——宿主侧装板后状态复位(封印/完美计数清零、板序推进、board_changed 发射、秘宝叠加层退役)留宿主薄壳,时序逐字保持 | `install(during_round) -> bool` 单入口;构造=引用束注入(rng/db/minesweeper/reward_economy/encounter 只读引用+宿主 fatal Callable——防 _init max-parameters 新键) | A1 board_generator(件内持有);A2 install_board;B4 roll_board/board_skew 写;A4 变异决议行集经 ConfigDB;board_gen/variant_place 两封闭流随件、流名与开流时点零漂移 | 无(零 await——装板全同步) |
|
||||
| **RoundCycle**(`core/round_cycle.gd`,B1 域·轮循环驱动件,已建件——迭代 44) | 轮循环驱动域唯一承载(自宿主收编 7 成员):结算五段段件持有与驱动、重入门闸、开轮摇号、踩雷账 | 三参直构(host/fx_host_ref/reward_grant_ref——五段构造透传不落成员);段驱动入口由宿主接线调用;实测类变量 8/公开方法 3 | B1 宿主;settlement/ 五段段类;A3 相位读 | 无 |
|
||||
| **BattleFacts**(`core/battle_facts.gd`,B1 域·战斗事实件,已建件——迭代 44) | 战斗事实域唯一承载(自宿主收编 14 成员):受理事实(encounter)、轮 facts 缓冲(reveals/seals/path)、封印判定账、事实溯源、同调快照读面;attune 状态两成员随件(行为函数留宿主) | 零参直构;宿主与段类经注入引用读写;实测类变量 15(贴顶)/公开方法 4(reset_round+三 mark 写口) | B1 宿主;A2/A3 只读 | 无 |
|
||||
@@ -132,7 +131,7 @@
|
||||
|
||||
### 战斗域装配现状(迭代 27——2026-10-17 用户裁写入;RunLedger 段已随迭代 39 拆件批退役,四账去向见下「冒险域拆分(现状登记)」节)
|
||||
|
||||
- **结算五段**:B1 编排器的每轮结算序列拆为 `game/core/settlement/` 五个段类——GrantSegment(①授予段,含封印判定与 counter/grant 助手单点、成功刷新事实消费面 apply_board_refreshed 与轮末治疗计数兑付面)→ AuraSegment(②增益/减益段)→ CombatSegment(③战斗段)→ DecaySegment(④衰减段)→ VerdictSegment(⑤判定段,返回裁定,终局早退语义留 B1)。段序骨架冻结于 `SettlementTable.segment_order` 语义,B1 退为段序编排+接线;段类可直接 `new()` 单测(`settle_segments_test` 在役)。
|
||||
- **结算五段**:B1 编排器的每轮结算序列拆为 `game/core/settlement/` 五个段类——GrantSegment(①授予段,含封印判定与 counter/grant 助手单点)→ AuraSegment(②增益/减益段)→ CombatSegment(③战斗段)→ DecaySegment(④衰减段)→ VerdictSegment(⑤判定段,返回裁定,终局早退语义留 B1)。段序骨架冻结于 `SettlementTable.segment_order` 语义,B1 退为段序编排+接线;段类可直接 `new()` 单测(`settle_segments_test` 在役)。
|
||||
|
||||
### 战斗编排域拆件定稿(2026-10-26 用户裁写入——gdstyle 存量债波 1 设计闭合;同日独立审查(grilling+godot-master 双子代理)阻断 8 条去重 6 组全折入本节,v1.18;2026-10-28 迭代 40 实现批合回(合并提交 514457d1),本节为现状登记)
|
||||
|
||||
@@ -163,15 +162,9 @@
|
||||
|
||||
| 模块 | 独占职责 | 对外暴露 | 消费依赖 | 引擎 API |
|
||||
|---|---|---|---|---|
|
||||
| **D1 HUD**(呈现面拆分持续换代:组合根 main.gd + 三屏壳(MenuScreen/CampScreen/AdventureScreen)+ views/ 视图单元(现行承载面见「呈现层装配与会话生命周期」节;早期 9 单元清单与 LevelStripView 已随场景换代退役);营地面=场景根+四子界面功能窗口+Esc 浮窗+顶栏 HUD 栏〔迭代 21 四柜台覆盖层已随迭代 47 卡④⑤ 退役〕) | 全部 Control/CanvasLayer(例外:BoardView 棋盘本体与 BattleStageView 站桩本体 = Control 壳 + SubViewportContainer + SubViewport + Node2D 世界〔BoardWorld/CellUnit;StageWorld/StandeeBody——B 区 slot_row 伴随件留壳层〕,探针契约面不变——棋盘 Node2D 迁移批 Phase A 2026-09-23 / 战斗舞台 Phase B 2026-09-26)、输入→意图翻译(**唯一提交路径 = settle_batch,不直调 A3**)、呈现义务状态记录(A2×6 / B2×4 / B3×4 / B4×5 / B1×3) | 视图单元接口(信号外发、setter 只读注入) | A2 compute_move_path / ActionOutcome;A3 ap/rap;B1 步骤流行协议(数据源);ConfigDB display_name | **MEDIUM**:Control / CanvasLayer / draw_* / InputEvent / RichTextLabel——对照 breaking-changes.md 4.5–4.7 条目(CanvasItem 线条无抗锯齿羽化、设备 ID 常量、RichTextLabel 图片参数两轮变化) |
|
||||
| **D1 HUD**(呈现面拆分持续换代:组合根 main.gd + 三屏壳(MenuScreen/CampScreen/AdventureScreen)+ views/ 视图单元(现行承载面见「呈现层装配与会话生命周期」节;早期 9 单元清单与 LevelStripView 已随场景换代退役);营地覆盖层=四柜台并排〔树淬炼主界面/锻造工坊·秘宝匣钥匙门/图鉴常开——迭代 21 T6 程序占位重排〕) | 全部 Control/CanvasLayer(例外:BoardView 棋盘本体与 BattleStageView 站桩本体 = Control 壳 + SubViewportContainer + SubViewport + Node2D 世界〔BoardWorld/CellUnit;StageWorld/StandeeBody——B 区 slot_row 伴随件留壳层〕,探针契约面不变——棋盘 Node2D 迁移批 Phase A 2026-09-23 / 战斗舞台 Phase B 2026-09-26)、输入→意图翻译(**唯一提交路径 = settle_batch,不直调 A3**)、呈现义务状态记录(A2×6 / B2×4 / B3×4 / B4×5 / B1×3) | 视图单元接口(信号外发、setter 只读注入) | A2 compute_move_path / ActionOutcome;A3 ap/rap;B1 步骤流行协议(数据源);ConfigDB display_name | **MEDIUM**:Control / CanvasLayer / draw_* / InputEvent / RichTextLabel——对照 breaking-changes.md 4.5–4.7 条目(CanvasItem 线条无抗锯齿羽化、设备 ID 常量、RichTextLabel 图片参数两轮变化) |
|
||||
| **组合根拆件**(呈现侧写口子件:curio_interact_controller / camp_shop_presenter / treasure_interact_controller) | 代组合根执行核心写调用(冒险节点后续推进/营地购买与刷新/道具使用——依赖与回调由 main 构造注入) | 各拆件的写入口(仅组合根装配面可达;视图零直连) | 构造注入的核心写引用(推进/商店/道具各域——详见各拆件头注) | 无 |
|
||||
| **ui_registry**(`presentation/ui_registry.gd`,界面注册面,已建件——迭代 44) | 呈现层接口面唯一承载:层序合同(LAYER_CONTRACT)迁驻与合同寻址、三域句柄容器 ui_screens(9 屏壳与覆盖层句柄)/ui_battle(5 战区句柄)/ui_modals(平铺模态句柄+营地域 UiCampModals 子容器〔六句柄、两段式寻址 `modals.camp.*`——迭代 47 卡⑥〕+ModalFlow;层序合同营地槽换代=camp_overlay 退役、换 camp_scene+四子界面+camp_esc_overlay——迭代 47 卡④⑤);组合根经其一级寻址接线,探针挂载假设迁移为 `main.ui.<域容器>.<句柄>`;七个探针别名/转发成员随批消灭 | 三容器属性+合同寻址口(RefCounted 门面,不挂树、零会话期引用);四件(UiRegistry/UiScreens/UiBattle/UiModals)类变量 3/9/5/14,ModalFlow 随 UiModals 容器构造即建;UiCampModals 营地域子容器随卡⑥(ui_modals 类变量 14→14——删六平铺增一子容器引用) | 构造注入:三屏壳、覆盖层、战区句柄、ModalFlow(main_ui_build 交付随之改型) | 无(RefCounted) |
|
||||
| **UiCampModals**(`presentation/ui_camp_modals.gd`,营地域句柄子容器,已建件——迭代 47 卡⑥) | ui_modals 营地域六句柄唯一容器(场景根/四子界面/Esc 浮窗/HUD 栏),句柄寻址两段式 `modals.camp.*`;ui_modals 平铺面收窄 | 子容器属性+六句柄只读投影;main_ui_build 装配交付 | 构造注入(六视图句柄);ui_modals(父容器挂载) | 无(RefCounted) |
|
||||
| **CampSceneView**(`views/camp_scene_view.gd`,营地场景根视图,已建件——迭代 47 卡④) | 营地场景图唯一呈现件:十层贴图铺底(层序按 layout 表 z 序;`camp_scene_layout.tres` fail-fast 校验+七命中区几何红线——camp_scene_layout_test 承载)、七物件命中区命中即发 object_pressed(组合根路由大门/篝火/笔记本/四界面)、反参照纪律(场景根零文字按钮)、登记模态初始隐(boot 态不盖菜单) | 构造注入(贴图经 `UiKit.TEX_CAMP_*`、layout 表经 ConfigDB 表现域容器);object_pressed 信号外发;open/close 经营地域子容器寻址 | camp_screen(宿主挂载);ConfigDB;组合根(路由消费) | Control+TextureRect 树 |
|
||||
| **营地四子界面视图**(`views/camp_sub_view_base.gd`+forge_view/tree_view/treasure_view/codex_view,功能窗口公共壳+四件,已建件——迭代 47 卡④) | 营地五功能窗口的四个承载视图(锻造工坊/天赋树/秘宝匣/图鉴)+公共壳(窗口壳/行板/关闭钮;生涯页挂笔记本路由不经此族);模态栈单口 open/close、五窗互斥单开;presenter 承接面零回表(树购买/锻造三意图经既有写口族) | 各 view open/close+意图信号外发;关闭钮 pressed | camp_shop_presenter/MetaProgression 写口(经组合根);CampRefreshBus(刷新广播消费) | Control |
|
||||
| **camp_esc_overlay+MainEscFlow**(`views/camp_esc_overlay.gd`+`presentation/main_esc_flow.gd`,Esc 浮窗视图+四态路由件,已建件——迭代 47 卡⑤) | Esc 浮窗三钮视图(返回主菜单/设置/退出)+Esc 四态路由静态件(根态唤起/再按关窗/功能窗开着先关最上层窗〔camp.md v2.3 定稿覆盖卡面 Q1 甲〕/设置开态不误关;菜单语境不唤起);**main._unhandled_input=产品码首个键盘输入入口**(契约 9——事件驱动、禁 Input 轮询、不挂 ui_cancel、pressed 非 echo) | flow.open/close 路由口;三钮 pressed 意图信号(经组合根接线) | 组合根(_unhandled_input 前置与意图消费);模态栈(关窗单口);screen_flow(设置/返回动线) | Control+静态件 |
|
||||
| **CampHudBand**(`views/camp_hud_band.gd`,营地顶栏 HUD 件,已建件——迭代 47 卡⑤) | 顶栏 48px 全宽窄条:钥石余额 badge 唯一呈现(单口取现值、显式刷新才变);set_hud_band 预留位落座 | badge 文本投影;refresh 时取现值 | CampRefreshBus(挂册广播);MetaProgression 钥石读 | Control |
|
||||
| **CampRefreshBus**(`views/camp_refresh_bus.gd`,营地刷新单口件,已建件——迭代 47 卡④⑤) | 营地刷新单口唯一承载:进档尾与营地写口尾触发 refresh 广播(八写口尾改路由),现开界面与 HUD 同步取现值 | 挂册/refresh 广播 | 组合根(触发点接线);四子界面与 CampHudBand(消费) | 无(RefCounted 信号件) |
|
||||
| **ui_registry**(`presentation/ui_registry.gd`,界面注册面,已建件——迭代 44) | 呈现层接口面唯一承载:层序合同(LAYER_CONTRACT)迁驻与合同寻址、三域句柄容器 ui_screens(9 屏壳与覆盖层句柄)/ui_battle(5 战区句柄)/ui_modals(13 模态句柄+ModalFlow);组合根经其一级寻址接线,探针挂载假设迁移为 `main.ui.<域容器>.<句柄>`;七个探针别名/转发成员随批消灭 | 三容器属性+合同寻址口(RefCounted 门面,不挂树、零会话期引用);四件(UiRegistry/UiScreens/UiBattle/UiModals)类变量 3/9/5/14,ModalFlow 随 UiModals 容器构造即建 | 构造注入:三屏壳、覆盖层、战区句柄、ModalFlow(main_ui_build 交付随之改型) | 无(RefCounted) |
|
||||
| **board_hover**(`views/board_hover.gd`,悬停预承诺呈现域件,已建件——迭代 45) | 悬停反馈域唯一承载(自 board_view 收编 11 函数族+_hover/_last_click 两成员):三语义光标、目标格边框、BFS 路径预览、chord 双击首遇锚;_r0 出生展开区成员留宿主 | 构造=引用束注入实为 7 项(cells/minesweeper/diggable 引用+格命中换算/光标写入/hover_forbidden/chord_attempted 四 Callable——卡片预估 5 项,11 函数逐字迁移的最小必需面);三入站口留宿主委托;hover_probe 读面留宿主 | board_view(宿主);输入事件经宿主入站口 | 无(RefCounted) |
|
||||
| **cell_base_layer/cell_mark_overlay**(`views/`,单格两绘制域件,已建件——迭代 45) | 单格绘制域拆分(自 board_cell_unit 状态件 `_CellPaintState` 收编,入参与该层绘制命令同件):基座层(底面/描边/立面/投影/接触带/倒角,12 成员)与标识叠加层(标记/字形/徽章/封印/点迹/高亮/退色,13 成员);宿主 _draw 退为六步时序器,类变量 30→10 | 构造注入;绘制命令由宿主 _draw 按层序调用 | board_cell_unit(宿主);gdstyle 键各文件独立计数(12/13 均 ≤15) | 无(RefCounted) |
|
||||
| **standee_vitals/standee_motion**(`views/`,站桩两状态件,已建件——迭代 45) | 站桩状态域拆分(自 `_StandeeState` 真实 25 成员收编):身份命值(hp/atk/状态值族,12 成员)与资产演出(动作/姿态/位移演出族,11 成员);三个演出探针计数器留宿主,宿主类变量 19→10 | 构造注入;st(沿用)/mo 两实例由宿主持有 | standee_body(宿主);battle_stage_view 写入面逐字保持 | 无(RefCounted) |
|
||||
@@ -256,17 +249,18 @@
|
||||
| **文档域** | 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 共享件例外);开发期手动档按本批改动域选 `--code`/`--docs`,合回后 CI 恒跑全量档。代码域跑前先过**素材齐盘预检**——`game/assets/art/**` 与一切 `*.import` 边车都不入 git(2026-09-29 裁定),worktree 冷检出必空盘,空盘态冒烟 V1 成片假红、渲染闸更是一段 golden 都不比。
|
||||
口径三条:**域划分的唯一出处是 `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 都不比。
|
||||
|
||||
### 车道分层执法(2026-10-31 用户裁恢复「一切改动走 worktree」)
|
||||
### 车道分层执法(2026-10-26 用户裁「按路径切车道,取代一切改动走 worktree」)
|
||||
|
||||
一切改动走独立 worktree,主工作区只作合并落点。执法全机械——判据只有 git 图与路径(豁免面 `.zcode/`、`.qoder/` 不入 git,主区直改),不含「改动大小」等语义判断。
|
||||
改动车道与改动域同一判据(文件性质):代码面=worktree 批车道,文档面=主工作区直改车道。执法全机械——判据只有文件路径性质与 git 图,不含「改动大小」等语义判断;「预期大改」由用户开工点名自愿开 worktree,不进规则。
|
||||
|
||||
| 执法层 | 载体 | 职责 | 判据 |
|
||||
|---|---|---|---|
|
||||
| **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 图 |
|
||||
| **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 图 |
|
||||
|
||||
验证收拢为合回后本地 CI 全量(post-merge 触发),commit 点零执法;主工作区防污染由 ZCode 层守卫承担——只约束 agent,用户手工改动不受限。子代理覆盖面(2026-10-26 冷启动评估实测):子代理的工具调用不经过 ZCode 层守卫——ZCode 层实际只覆盖主会话工具调用;残余暴露面=子代理写主工作区与蓄意 `--no-verify`(该旗标整族跳过 git 层钩子),属故意越权而非遗忘型失误,与防遗忘的设计目标正交。
|
||||
取舍依据:失败代价不对称——docs 红是死链/术语/格式,下次任何跑批对基线比对必暴露、秒级修复,故文档面轻执法(commit 点闸、无回执流程);代码面红是运行时行为坏,保留「worktree+批末域闸+回执硬绑定」全套。git 层钩子对一切提交者生效(含用户手工提交,紧急出口 `--no-verify`);ZCode 层只约束 agent。子代理覆盖面(2026-10-26 冷启动评估实测):子代理的工具调用不经过 ZCode 层守卫(代码面 Edit 未被拦、`--no-verify` 探针未被拦)——ZCode 层实际只覆盖主会话工具调用,git 层因此是唯一覆盖一切提交者的执法层;残余暴露面=蓄意 `--no-verify`(pre-commit 族钩子被该旗标整族跳过,git 层无法自防),属故意越权而非遗忘型失误,与防遗忘的设计目标正交。
|
||||
|
||||
### 三条判定硬闸现行状态
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# 流程规范(Dungeon Sweeper)
|
||||
|
||||
> 约束范围:一切批次的流程纪律——车道(一切改动走独立 worktree,主区只作合并落点)、五段流水线、批次工作流、测试执行细则、像素件工作纪律。
|
||||
> 约束范围:一切批次的流程纪律——车道(代码面 worktree 批/其余主区直改)、五段流水线、批次工作流、测试执行细则、像素件工作纪律。
|
||||
> 分工:硬性红线的一句话索引在 AGENTS.md「硬性红线」;本文承载其操作细则;批次开工前通读本文。
|
||||
|
||||
**开工任何批次前必须通读本文。**
|
||||
|
||||
## 1. 分支纪律(worktree 铁律)
|
||||
## 1. 分支纪律(车道铁律)
|
||||
|
||||
主工作区(master 检出)只作合并落点,一切开发/设计改动禁止直接发生在主工作区(2026-10-31 用户裁恢复;2026-10-30 的「主区直改+直提」车道与 commit 点 pre-commit 直改闸随之退役;验证模型不动——合回后本地 CI 仍是唯一强制验证点):
|
||||
一切面主工作区直改+直提(2026-10-30 用户裁本地 CI 化:验证挪到合回后,主区直提不再拦任何面;判据与 §3 第 8 条改动域划分同一):
|
||||
|
||||
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):跑批期间新触发不中断当前轮,轮尾对最新尖端补跑,绝不逐笔各跑一轮。**回执等待纪律(2026-11-02 用户裁)**:合回落地即向用户呈报本批总结,回执等待一律以 run_in_background 后台任务运行 `tools/ci_watch.sh <合并提交>`(认领判据与容错细则=该件头注),禁前台 sleep/轮询循环等 CI;后台任务唤醒会话后按回执续行——绿即删区收口,红或超时即呈报走修复批。闸重跑等一切分钟级跑批等待同此纪律
|
||||
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(子代理不经守卫层)
|
||||
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
|
||||
|
||||
## 2. 五段流水线
|
||||
|
||||
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI」(车道细则见 §1)。两层口径(2026-10-25 用户立):需求卡片=需求单位的实施文档,任何工作量皆可写、皆可编入迭代;工作量限制只属迭代层——迭代估时总量 <5 个工作日=「冻结待凑批」,不可开工。段间不自动流转,每段转进由用户发令;每段一个技能入口(五者均装于项目 `.zcode/skills/`:doc-design/asset-ingest/req-card/iter-plan/iter),登记不立技能(条目格式由流水线闸强制)。设计工作在会话内进行、用户主导、禁无监管自跑。
|
||||
**需求改动**(游戏代码,含随逻辑写/改的测试)按五段流水线走:**登记 → 文档设计/美术设计(两条设计线)→ 写需求卡片 → 迭代编排 → 实现**;工程/文档设计/美术素材三类改动不走写卡与编排段,走「(如需设计讨论)→ 用户发令直改 → 主区直改+commit 点闸」(车道细则见 §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 验证(唯一强制验证点)**——主工作区 `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 条),合并后无手工动作
|
||||
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 条),合并后无手工动作
|
||||
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-merge 钩子复验追绿(修复走新开 `fix-<任务名>` 修复 worktree,process-conventions §1)。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
|
||||
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6);主区小改孤跑验证后直提。全量验证归合回后的本地 CI:git merge 合回 master 触发 post-merge 钩子跑 `tools/run_all_gates.sh` 不带参数档(两域全量,02 全套件——跨卡触面与关联档选测的盲区由全量兜底),结果落 `game/tests/.gates/ci-status.json`;CI 红态下修复提交经 post-commit 钩子自动追绿。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
|
||||
行为探针使用随机化玩家行为(封印/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。
|
||||
|
||||
## 5. 测试函数怎么写——验收清单条目的落码规范
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| `术语表.md` | 全库专用术语的唯一对照表——危险格/安全格/同调位/RAP/GP/真视等定义+退役词对照(2026-10-11 建立,v1.23) |
|
||||
| `待办清单.md`(仓库根,不属本树) | 全项目待办事项的唯一登记处;各系统文档的「待办」节只留指路存根 |
|
||||
| `体验验证清单.md`(仓库根,不属本树) | 需要真人试玩观测的验证项登记处(与待办清单分工:待办清单是"要拍板的事",本篇是"要玩过才知道的事");每条注明结论回流路径 |
|
||||
| `08-Alpha版本标准.md` | Alpha 版本包含哪些东西的清单——里程碑 M3 的完成标准;只列条目不写内容,定义一律指路各权威文档(**已定稿 v1.21**,2026-10-13) |
|
||||
| `08-Alpha版本标准.md` | Alpha 版本包含哪些东西的清单——里程碑 M3 的完成标准;只列条目不写内容,定义一律指路各权威文档(**已定稿 v1.20**,2026-10-13) |
|
||||
|
||||
## systems/ 五个循环
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **一句话定位**:本文定义这个项目的 **Alpha 版本包含哪些东西**——它是里程碑 M3 的完成标准,也是判断"还差什么"的对照表。
|
||||
> **本文只列条目,不写内容**:每样东西是什么、规则怎么走,一律归各自的权威文档,本文只给指路链接。想知道某样东西的定义,请点链接,不要在本文里找解释。
|
||||
> **状态**:**已定稿(v1.23)**——2026-10-13 用户令定稿,历次升版明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **状态**:**已定稿(v1.20)**——2026-10-13 用户令定稿,历次升版明细见 git 提交史。Alpha 的内容范围自此冻结:增删条目一律须经用户裁决。
|
||||
> **成文**:2026-10-13。
|
||||
|
||||
## 本文怎么用
|
||||
@@ -103,8 +103,8 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 主菜单 | —— | 在役(迭代 33 卡②三入口化:进入存档/设置/退出游戏——入口拓扑=进入存档→选档界面→进档/新档→营地覆盖层→〔营地交互重做卡④〕营地场景图与生涯笔记本;2026-10-22 用户裁换代〔反哺 #13〕) |
|
||||
| 开始覆盖层 | —— | 在役 |
|
||||
| 选档界面(3 存档位) | —— | 在役(迭代 33 卡③——空槽新档/有档进档/删档二次确认+摘要三项;2026-10-22 用户裁增行〔反哺 #8〕) |
|
||||
| 营地(天赋树 / 锻造工坊 / 秘宝匣 / 图鉴) | [camp](ux/camp.md) | 在役(迭代 47 卡④⑤ 落地——场景图+七物件点选+Esc 浮窗与顶栏,2026-11-01 合回) |
|
||||
| 营地交互重做(五卡:存档制数据层/主菜单三入口/选档界面/营地场景图/Esc 浮窗与营地顶栏) | [camp](ux/camp.md) | 在役(五卡全部落地——卡①②③ 迭代 33 批合回、卡④⑤ 迭代 47 批合回,卡⑥ 句柄子容器为同批前置基建卡。设计过程件已按吸收即死退役,沿革查 git 提交史。2026-10-21 用户裁:五卡整体列入 Alpha。出口=五卡各自验收标准通过+营地屏用户目检〔登记体验验证清单 ㊺〕) |
|
||||
| 营地(天赋树 / 锻造工坊 / 秘宝匣 / 图鉴) | [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。出口=五卡各自验收标准通过+营地屏用户目检通过) |
|
||||
| 生涯页 | [生涯](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) | 在役(义务维持;当前轮次未清=㉚–㊸ 各条待用户核销——迭代 32 起各批实机目检项登记不阻塞,迭代 42 无玩家可见改动未登记) |
|
||||
| 真人试玩轮次 | [体验验证清单](../../体验验证清单.md) | 在役(义务维持;当前轮次未清:㉚/㉛ 未核销,迭代 34 三条目检项待登记) |
|
||||
|
||||
---
|
||||
|
||||
@@ -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) | 待做(评估已结——不回退高清、维持像素风;终态方案=保留原稿分辨率、入包禁降采样、显示按三档绝对高 96/144/188 缩放、现有形象即终稿;裁决录=待办清单本条+art-bible §8.2;出口=9 件按档落位+用户目检清晰度与同屏体量顺序通过) |
|
||||
| 立绘清晰度回退评估(是否改用高清图) | [角色设计](../art/character-design.md) | 待做(评估已结——2026-10-14 用户裁维持像素风、升级精细手绘像素、不回退高清图,裁决录=[美术审计-cursor会话存档](../../reports/美术审计-cursor会话存档-2026-10-14.md) §2.2;Alpha 范围=主角+三个 Boss 按新口径重产,其余敌种当前可读则保留到 Beta;出口=用户目检八敌体量关系通过且主角与三个 Boss 重产完成) |
|
||||
| 敌人立绘(全敌种) | [角色设计](../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 | 部分在役(规格半已闭合=目标规格清单已落 art-bible 五张规格表〔2026-10-30 合回〕,五项实装定值与样张机检随迭代 43 入库;未结半=出口未达——方向稿用户目检(体验验证清单㊶)先行,样张整体过闸随其后。站桩帧动画的定版前置与主角+三 Boss 重产依赖照旧) |
|
||||
| 战斗一屏样张(界面美术的终稿闸) | [美术圣经](../art/art-bible.md) §1.4 | 待做(Alpha 硬前置:站桩帧动画的定版前置与主角+三 Boss 重产都依赖它;出口=用户目检通过的战斗屏样张,并产出尺寸档/色板/明度档清单) |
|
||||
| UI 氛围层与九宫格切片观感修 | [待办清单](../../待办清单.md) | 部分在役(光柱硬边已修=连续 alpha 渐变;未结半=九宫格切片错位——Alpha 内做最小修复:核对素材尺寸,必要时重出九宫格素材,全套素材重出排 Beta;出口=结算屏与图例屏错位消失,渲染闸+用户目检通过) |
|
||||
| UI 元素细节修(滑轨填充 / 图标垫块 / 留白 / 开发件外泄) | [待办清单](../../待办清单.md) | 在役(迭代 26 · 2026-10-14 入库:①滑轨填充随迭代 25 注销;余②图标垫块明度差降档/③商店+营地布局修/④诊断钮迁调试浮窗 全落地) |
|
||||
|
||||
@@ -190,7 +190,7 @@ Alpha 内以玩法循环本身的内容与表现为主(棋盘、关卡、战
|
||||
| 遗物式构筑协同 | [05-不做清单](05-不做清单.md) |
|
||||
| 战后自动回复 | [05-不做清单](05-不做清单.md) |
|
||||
| 反雪球机制 | [05-不做清单](05-不做清单.md) |
|
||||
| 键盘 / 手柄输入 | [待办清单](../../待办清单.md)(排期归 Beta。例外:营地语境 Esc 浮窗已落地〔迭代 47 卡⑤,用户明示裁决〕;键盘面其余仍排 Beta) |
|
||||
| 键盘 / 手柄输入 | [待办清单](../../待办清单.md)(排期归 Beta) |
|
||||
| 数值统一标定(M4 数值批) | 2026-10-09 里程碑立规:M4 = Beta 阶段、M3 未由用户明令关闭前不排期(用户 2026-10-07 令移出迭代 18;2026-10-16 确认此裁长期有效)——待办清单各条「已编入 Alpha 收官 M4」措辞系旧口径残留 |
|
||||
| 连刷 / 换图资金跨战斗携带 | [05-不做清单](05-不做清单.md) |
|
||||
| 战斗节点地图类型差异化(更多地图主题) | 排 Beta 版本([playnote](../../playnote.md),2026-10-13 裁) |
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
>
|
||||
> **兑付频率(先看这条,它是三条渠道的共同前提)**:奇物**不是打一场战斗给一份**。关内的普通战斗节点打完了什么也不给;一关之内奇物**只兑付一次**,触发点是**关底 Boss 被击败**——而按 [冒险结构](冒险结构.md) 的定义,打掉关底 Boss 恰好就等于"通过这一关",这两件事是同一个时刻,不是一条链上的两步。
|
||||
>
|
||||
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.4。本文件是奇物的唯一定义文档。
|
||||
> 状态:**已实现**(2026-10-04 双层 BD 实施批落地);现行为 v1.2。本文件是奇物的唯一定义文档。
|
||||
>
|
||||
> 数值纪律:本文件只写概念、取值域与指路;每件给多少、效果是什么一律不进正文,事实源是 `data/tables/feats.tres` 与 `data/tables/adventure.tres`。
|
||||
|
||||
## 专长(重设计·基础框架 v1)
|
||||
|
||||
> 本节是专长分类的现行事实源(2026-10-07 用户终审认同写入文档)。件表为设计声明——数值全 provisional 归调参批;命名全为代号,正式命名归用户。**新框架已实装:`feats.tres` 37 行=基础 27+迁入 2+规则豁免 1+换图系 7(换图系七件 2026-11-01 迭代 46 落表),行为与数据行 1:1。**
|
||||
> 本节是专长分类的现行事实源(2026-10-07 用户终审认同写入文档)。件表为设计声明——数值全 provisional 归调参批;命名全为代号,正式命名归用户。**新框架已实装:`feats.tres` 30 行=基础 27+迁入 2+规则豁免 1,行为与数据行 1:1。**
|
||||
|
||||
### 设计原则
|
||||
|
||||
@@ -97,27 +97,27 @@
|
||||
|---|---|---|
|
||||
| 血税 | 本局:踩雷 −2 最大生命;标中 +1 攻击(**均下一战起生效**——战末汇入冒险加算区) | 双向都痛都爽,一句话讲得清;双向同账、本局累积、冒险收束清零。生命上限/攻击系属战界重建属性,即时改会被重建冲掉,故滞后一战兑付(2026-10-12 追认⑨回写;卡面 desc 同步补「下一战起生效」) |
|
||||
|
||||
### 换图系(7 件,已实装——2026-11-01 迭代 46 落表 `feats.tres`,稀有度 14/13/10/0、每件持有上限 1)
|
||||
### 换图系(7 件,设计定稿,待实现)
|
||||
|
||||
换图系把「弃图刷新」从坏图逃生改成主动构筑行为。两件基座分别解决 RAP 的来源与价格;后续专长让成功刷新产生输出、生存或 GP 资源收益。七件不要求一次拿齐,不设强制先拿基座、集件标签或集件奖励。
|
||||
换图系把「弃图刷新」从坏图逃生改成主动构筑行为。两件基座分别解决 RAP 的来源与价格;后续专长让成功刷新产生输出、生存或资源收益。七件不要求一次拿齐,不设强制先拿基座、集件标签或集件奖励。
|
||||
|
||||
| 层 | 代号 | 规则 | 行为职责 |
|
||||
|---|---|---|---|
|
||||
| 基座 | 矿脉储能 | 每次付费挖格被受理后,额外获得 RAP;安全格与危险格同口径 | 持续挖图也能准备刷新,不再必须少挖停手 |
|
||||
| 基座 | 省力周转 | 弃图刷新的 RAP 成本减半 | 同一份储备支持更多刷新 |
|
||||
| 输出 | 崩壁冲击 | 成功刷新后,在本轮战斗段末对全体存活敌人造成一次固定伤害 | 刷新成为直接进攻行为 |
|
||||
| 输出 | 蓄力周转 | 成功刷新攒层,最多 3 层;本轮下一次主角攻击消费全部层数后清零,战斗结束不跨战保留 | 连续换图蓄成一次单点爆发 |
|
||||
| 生存 | 石层缓冲 | 本轮至少成功刷新一次后,主角每次受到伤害固定减少 2 点;不叠加 | 刷新形成当轮全局防御姿态 |
|
||||
| 生存 | 坑道小憩 | 每次成功刷新累计 1 点恢复,最多 3 点;本轮授予段合计回复生命,封顶生命上限 | 放弃当前图换取恢复 |
|
||||
| 资源 | 矿道红利 | 每次成功刷新固定获得 1 GP | 刷新动作自身带少量GP收益 |
|
||||
| 基座 | 掘进储能 | 每次付费挖格后额外获得 RAP;安全格与危险格同口径 | 持续挖图也能准备刷新,不再必须少挖停手 |
|
||||
| 基座 | 折价换图 | 弃图刷新的 RAP 成本减半 | 同一份储备支持更多刷新 |
|
||||
| 输出 | 弃图轰击 | 成功刷新后,在本轮战斗段对全体存活敌人造成一次伤害 | 刷新成为直接进攻行为 |
|
||||
| 输出 | 换图蓄势 | 成功刷新攒层,层数有上限;下一次攻击附加全部层数对应的伤害后清零,战斗结束清空 | 连续换图蓄成一次单点爆发 |
|
||||
| 生存 | 换图庇护 | 成功刷新后,在本轮授予段获得护盾 | 用刷新预防后续伤害 |
|
||||
| 生存 | 弃图休整 | 成功刷新后,在本轮授予段回复生命,封顶生命上限 | 放弃当前图换取恢复 |
|
||||
| 资源 | 弃图回收 | 成功刷新后,按旧图探索进度在本轮授予段回收清图奖励 | 先探索再主动弃图变现 |
|
||||
|
||||
共同规则:
|
||||
|
||||
- **双基座叠加**:矿脉储能与省力周转可以同时持有,产 RAP 与成本减半分别生效;每轮刷新次数上限继续限频,不设互斥规则。
|
||||
- **成功才触发**:后续五件只认成功换图;刷新被拒绝时不产生事实,也不给输出、生存或资源收益。同一轮多次成功刷新按发生顺序逐次登记。
|
||||
- **轮末兑付**:刷新不新增结算段。崩壁冲击走本轮战斗段末结账,石层缓冲在每次主角受伤时消费,坑道小憩与矿道红利走本轮授予段兑付。
|
||||
- **全局受伤面**:石层缓冲覆盖敌方攻击、危险格、错误封印资源格与地形造成的主角伤害;临时生命值不在本流派设计范围内。
|
||||
- **基线不变**:没有这些专长时,RAP 来源、刷新成本、清图奖励与伤害结果保持原规则。数值与稀有度归 `feats.tres`(已落表——稀有度档 14/13/10/0、每件持有上限 1;数值全 provisional 归调参批)。
|
||||
- **双基座叠加**:掘进储能与折价换图可以同时持有,产 RAP 与成本减半分别生效;每轮刷新次数上限继续限频,不设互斥规则。
|
||||
- **成功才触发**:后续五件只认成功换图;刷新被拒绝时不产生效果。同一轮多次成功刷新按发生顺序逐次登记。
|
||||
- **轮末兑付**:刷新不新增结算段。群伤、护盾、回血与回收按[战斗结算](../战斗循环/战斗结算.md)的既有段序兑付,刷新瞬间不直接改这些战斗结果。
|
||||
- **回收看旧图**:弃图回收读取被放弃旧图的探索进度,不把旧图或新图判为清图;具体口径归[地图战斗资源](../战斗循环/地图战斗资源.md)。
|
||||
- **基线不变**:没有这些专长时,RAP 来源、刷新成本、清图奖励与刷新结果保持原规则。数值与稀有度归 `feats.tres`,落表前本节只作设计声明。
|
||||
|
||||
### 迁入登记(秘宝域迁出件;实装批落地后并入件表)
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
11. 凭证拿到即开,不进持有集,玩家无法囤积——选中或买下的同一时刻就开专长三选一。
|
||||
12. 凭证带有计步保底:连续 5 次三选一展示未出现则第 6 次必出;计数跨奖励节点与商店共享,刷新后的重新展示计入次数,限购用满后保底不再触发。
|
||||
13. 被称为流派基座的专长必须能说明拿到前后的操作差异与新行为的持续资源来源;只有同一操作后的泛用数值增长,不得登记为基座。
|
||||
14. 换图系两件基座同时持有时都生效;后续五件只在成功刷新后触发,无专长基线不变;石层缓冲覆盖所有主角受伤路径且同轮不叠加,矿道红利按成功刷新次数固定获得 GP。
|
||||
14. 换图系两件基座同时持有时都生效;后续五件只在成功刷新后触发,无专长基线不变;弃图回收按旧图探索进度结算,同一旧图至多回收一次。
|
||||
|
||||
> 实装批落地前,现役代码按旧 27 件专长目录与旧资源成员在同框架下运行;新成员口径(27 件基础件表 / 凭证件表)自实装批起验。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 地图战斗资源
|
||||
|
||||
> 一句话:格面资源的产出、极性字典与清图奖励封装
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.4
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -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(敌方输出池;同名重复授予只加时长)——幅度见 `status_effects.tres` weaken 行 | sapped(主角输出池)——幅度见 `status_effects.tres` sapped 行 |
|
||||
| weaken 削弱 | weaken(敌方输出池;同名重复授予只刷新剩余次数——2026-09-18 续时定案)——幅度见 `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` |
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
### 清图奖励对象:整图封装,可配置可替换
|
||||
|
||||
清图奖励不属于任何一格的格面资源——它是作用域为整张板的独立奖励对象:把一张图的雷全部清完(全部安全格开完)时结算发的那一份。装板摇号时与格子资源同批判尾摇定身份;清图批兑付恰好一次;完美封印(零误标且至少一次封印标中)追加第二份同身份兑付。清图奖励只在清图批兑付:弃图刷新不触发清图事件,换来的新图不发清图奖励;刷新专长的固定 GP 收益走战斗内缓冲入账,不属清图奖励封装。
|
||||
清图奖励不属于任何一格的格面资源——它是作用域为整张板的独立奖励对象:把一张图的雷全部清完(全部安全格开完)时结算发的那一份。装板摇号时与格子资源同批判尾摇定身份;清图批兑付恰好一次;完美封印(零误标且至少一次封印标中)追加第二份同身份兑付。
|
||||
|
||||
它是可配置可替换的封装:数据面上是清图池里的一行(现行默认槽 clear_default),换行即换内容,机制零改动。按图类型差异化已留结构位——`clear_<图类型>` 命名空间预留,不同地图类型可以配不同的清图奖励;词表现行 = {dungeon, underdark, deep_underdark}(2026-09-19 定稿两值、第三关内容批 2026-09-20 增 deep_underdark,玩家侧「地图类型」),差异化内容启用随内容批。
|
||||
|
||||
@@ -158,8 +158,29 @@
|
||||
| 可配置可替换 | 内容 = 池行值;允许即时类(当场兑付,不入库) |
|
||||
| 图类型差异化是预留插槽 | 现行只有默认槽;结构位已留,启用走棋盘生成词表落定 |
|
||||
|
||||
持有弃图回收专长时,同一清图奖励对象也可按旧图探索进度部分回收;这不改变正常清图与清图奖励的定义,例外口径见下节。
|
||||
|
||||
举证:本节为封装与流程规则,无计算。
|
||||
|
||||
### 弃图回收:按旧图探索进度兑付(设计定稿,待实现)
|
||||
|
||||
弃图回收让玩家不必挖完最后几个安全格,也能把已经推进的探索换成一部分清图奖励。它只读取被放弃的旧图,不把旧图判为清图,也不把新图判为已清图。
|
||||
|
||||
旧图退役前记录三项读数:装板初始已开安全格数、装板初始未开安全格数、退役时已开安全格数。探索进度定义为:
|
||||
|
||||
```
|
||||
探索进度 =(退役时已开安全格数 − 装板初始已开安全格数)÷ 装板初始未开安全格数
|
||||
```
|
||||
|
||||
进度限制在 0~1。玩家付费挖开与连锁展开的安全格都真实推进探索,计入分子;装板时白送的初始格、封锁格与危险格不计入。没有新探索就刷新,进度为 0,不能获得回收奖励。
|
||||
|
||||
回收兑付的是旧图在装板期已经摇定的清图奖励对象,按探索进度取对应份额;具体比例、档位、取整与数值归调参。它只发基础奖励的进度份额:
|
||||
|
||||
- 不触发完美封印加倍,不产生清图事件,不执行正常清图的整板兑付。
|
||||
- 同一旧图只在成功退役时回收一次;新图不继承旧图探索进度。
|
||||
- 刷新被拒绝时旧图不退役,也不回收;成功后的兑付时机归[战斗结算](战斗结算.md)本轮授予段。
|
||||
- 正常清图仍发完整清图奖励对象;弃图回收不得让同等探索投入的收益无条件高于正常清图。
|
||||
|
||||
## 依赖与输出
|
||||
|
||||
本系统被两侧夹住:事实与数值从上游来,调用与呈现往下游去。下表就是删除任何一方时的影响面清单。
|
||||
@@ -239,6 +260,9 @@
|
||||
- 类型封闭与字典完整:表里出现九型之外的类型、或任一型缺正向/负向行,加载拒绝。
|
||||
- 未生效可见:满层增益、满血正向治疗、无存活敌的群伤,各自在结算明细记"奖励未生效"行,零折算、零补偿。
|
||||
- 加载护栏:池空、清图池无正当量、增益引用悬空——任一命中即加载时返回错误并中止;默认内容加载通过。
|
||||
- 弃图回收进度:付费挖开与连锁展开的安全格计入探索,初始格、封锁格与危险格不计;无新探索时进度为 0,不能获得回收奖励。
|
||||
- 弃图回收恰一次:成功退役的旧图按当次探索进度回收其预摇清图奖励对象,新图不继承进度,同一旧图不能重复回收;被拒绝的刷新零兑付。
|
||||
- 回收不是清图:弃图回收不触发完美封印加倍、清图事件或正常清图整板兑付;正常清图仍发完整清图奖励对象。
|
||||
- 表实一致:数据表字段与奖励表字段定义逐字段一致。
|
||||
|
||||
## 待办
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 增益状态
|
||||
|
||||
> 一句话:增益/减益的状态池、叠层与衰减
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
|
||||
## 设计目的
|
||||
|
||||
战斗里有一类效果不在当场一次性用掉,而要管上几次结算:挖开安全格拿到的主角增益、封印标中带来的怪物削弱、踩雷喂给怪物的增强(警觉——blast 型负向)、标错封印招来的负向兑付,都是这类。本文把它们统称"状态":发给主角的有益效果叫增益(失衡与乏力是例外——它们是发给主角的减益),发给敌人的负面效果叫减益;敌方侧的增强同样是状态——几个方向共用同一个状态池。
|
||||
|
||||
增益状态就是这一个状态池:它收下全部状态,用同一套规矩管理——发入、合并(同一种效果重复拿到时按表行策略处理:叠层加一,或把剩余次数累加上本次授予的时长——读 `status_effects.tres` 逐行 stack_policy)、衰减(每次结算后剩余次数减一,减到零效果消失)、清空(战斗结束全部清除)。没有它,几类效果得各写一套计时逻辑,结算明细也无法逐行指回"这效果是我哪步棋带来的"。状态池管机制、不管数值从哪来:某个效果该多大,由各收益系统的表去定,状态池只照章入列、按次衰减。
|
||||
增益状态就是这一个状态池:它收下全部状态,用同一套规矩管理——发入、合并(同一种效果重复拿到时按表行策略处理:叠层加一,或只刷新剩余次数——读 `status_effects.tres` 逐行 stack_policy)、衰减(每次结算后剩余次数减一,减到零效果消失)、清空(战斗结束全部清除)。没有它,几类效果得各写一套计时逻辑,结算明细也无法逐行指回"这效果是我哪步棋带来的"。状态池管机制、不管数值从哪来:某个效果该多大,由各收益系统的表去定,状态池只照章入列、按次衰减。
|
||||
|
||||
## 系统范围
|
||||
|
||||
@@ -76,14 +76,14 @@
|
||||
|
||||
举证:本节为授予路径与登记规则,无计算。
|
||||
|
||||
### 同名状态再拿到:叠层、累加、并存
|
||||
### 同名状态再拿到:叠层、刷新、并存
|
||||
|
||||
同一种状态还没消失又拿到一份,怎么合并由状态表每行的合并策略字段说了算,共三种。合并在授予那一刻由状态池完成;多个状态在结算里按什么先后起作用,是战斗结算的编排——两件事互不干扰。
|
||||
|
||||
| 合并策略 | 重复授予时发生什么 | 说明 |
|
||||
|---|---|---|
|
||||
| 叠加 | 层数加一;剩余结算次数不动——只有首次授予定钟 | 现行内容行多数用叠加(逐行合并策略读 `status_effects.tres`) |
|
||||
| 累加 | 剩余结算次数加上本次授予的时长——技能施放行带覆盖时长时按覆盖值加;层数不变 | 现行内容行六行用累加(削弱行 weaken、增益技行 empower / fortify、主角增益行 attack_up / ac_up);重复拿一次就多攒一段时长,同一轮拿几份攒几段,停手后按剩余次数逐轮耗完;逐行读 `status_effects.tres` |
|
||||
| 刷新 | 剩余结算次数重置为该实例首次授予时的初始值,层数不变 | 刷新类内容行已就位——削弱行 weaken(2026-09-18 续时定案)、增益技行 empower / fortify、**主角增益行 attack_up / ac_up(2026-09-20 用户拍板「只续时不叠层」,全状态表增益/减益两侧合并语义就此统一为:主角可重复获取的增益与减益一律续时、不叠层)**;逐行读 `status_effects.tres` |
|
||||
| 并存 | 新建一份独立实例,两份各算各的 | 同上 |
|
||||
|
||||
并存分支没有满层上限,但状态池规模天然有界:实例数的上界就是受理过的授予数——多受理一份就多一份实例,多少份在授予那一刻就完全确定,不会无限膨胀。
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
### 剩余次数怎么倒数:按结算次数衰减
|
||||
|
||||
每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。现行累加行里主角增益两行持续 1、削弱与敌方增益技行持续 2——累加语义下重复拿一次就按本次授予时长多攒一段,同一轮拿几份攒几段;跨轮持续的状态,寿命由表行初始值表达,给几就活几次结算。
|
||||
每个状态实例自带一个倒数计数——剩余结算次数(本文说的"衰减"就是它逐次递减、归零消失)。它不承诺"活几轮",而是数"经历过几次轮末结算":每轮结算先让状态起作用,随后计数减一,减到零状态即被移除。持续 1 的状态就是"一次性":在授予它的那轮结算里生效一次,轮末消失。MVP 时期六行内容均为一次性(削弱后改续时——见 git 提交史;2026-09-20 起主角增益两行也走续时、duration 表值仍为 1——重复拿等于重新获得一次当轮增益);跨轮持续的状态,寿命由表行初始值表达,给几就活几次结算。
|
||||
|
||||
```
|
||||
一轮结算:查询生效清单 → 逐行兑付(层数 × 行值)→ 轮末衰减(全部实例剩余次数 −1)→ 归零实例移除
|
||||
@@ -140,8 +140,8 @@
|
||||
|
||||
| 例子 | 状态池输出 | 消费侧读法 |
|
||||
|---|---|---|
|
||||
| 暴走叠两层 | +(2 × enrage 行幅度)(读表;越出单行取值域,原样输出) | 压在天花板 +100% 上:敌方输出乘区封顶 ×2(削弱非叠层行——"叠两层压地板 −100% 归零"的读法不成立) |
|
||||
| 主角盾牌加护再拿一份 | 层数恒 1、剩余结算次数累加本次授予的时长 | 不经任何池:掷点判定阈值 = 主角基准(读 hero 表)+ 层数(现行 +1——上限 2 层叠读取法退役,局内可达 AC 上限 11) |
|
||||
| 暴走叠两层 | +(2 × enrage 行幅度)(读表;越出单行取值域,原样输出) | 压在天花板 +100% 上:敌方输出乘区封顶 ×2(削弱已改续时——"叠两层压地板 −100% 归零"的读法随 2026-09-18 结构修复批退役) |
|
||||
| 主角盾牌加护再拿一份 | 层数恒 1、剩余结算次数重置(refresh——2026-09-20 主角增益续时批) | 不经任何池:掷点判定阈值 = 主角基准(读 hero 表)+ 层数(现行 +1——上限 2 层叠读取法退役,局内可达 AC 上限 11) |
|
||||
|
||||
状态池还有一条快照纪律:实例的幅度、初始次数、量纲在授予那一刻从表行拷贝。战斗中配置表即使被重载,已授予实例不受任何影响,只影响后续授予。
|
||||
|
||||
@@ -162,15 +162,15 @@
|
||||
|
||||
| 状态 | 效果一句话 | 发给谁 | 幅度(现行值) | 时限 / 合并 | 来源类别 |
|
||||
|---|---|---|---|---|---|
|
||||
| attack_up 增伤 | 主角攻击提升 | 主角 | 见 `data/tables/status_effects.tres` attack_up 行 | 时限/上限读表 / 累加(层数恒 1) | 安全格 |
|
||||
| ac_up 盾牌加护 | 主角护甲等级提升(掷点折扣判定阈值——AC 轴,不入池) | 主角 | 见 `status_effects.tres` ac_up 行(2026-09-16 换装,原 defense_up 行已退役) | 时限/上限读表 / 累加(层数恒 1) | 安全格 |
|
||||
| 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) | 安全格 |
|
||||
| alert 警觉 | 敌方护甲等级 +1/层 | 敌方 | 见 `status_effects.tres` alert 行 | 1 结算 / 叠加读表 | 踩雷 / 标空封印(blast 型负向) |
|
||||
| weaken 削弱 | 敌方攻击被削弱(同名重复标中只加时长、层数恒 1) | 敌方 | 见 `status_effects.tres` weaken 行(取值域 [60,85],取负生效) | 时限读表 / 累加 | 封印标中 |
|
||||
| weaken 削弱 | 敌方攻击被削弱(同名重复标中只刷新剩余次数、层数恒 1) | 敌方 | 见 `status_effects.tres` weaken 行(取值域 [60,85],取负生效) | 时限读表 / 刷新(2026-09-18 续时定案) | 封印标中 |
|
||||
| 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 | 累加、并存两类合并策略的后续内容行 | 语义已定义、状态池已承载;累加类已有内容(weaken/empower/fortify/attack_up/ac_up),并存仍空,首个并存内容行上线时顺带补验收 |
|
||||
| P1 | 刷新、并存两类合并策略的后续内容行 | 语义已定义、状态池已承载;刷新类已有内容(empower/fortify 2026-09-16 敌人技能池批、weaken 2026-09-18 续时定案),并存仍空,首个并存内容行上线时顺带补验收 |
|
||||
| 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(duration 2)+ empower/fortify(表值 2 = 先兑付后衰减含施放当轮的 1 个有效增益拍)+ attack_up / ac_up(duration 1);累加语义下重复拿一次就按本次授予时长加一段;拉长表值 = 每次授予攒更多段、规划变简单 |
|
||||
| 叠层上限 | 整数 [1,16] | max_stack | 仅叠加策略消费;累加行层数恒 1、表值上限不消费,empower/fortify ×1(冷却闸下不可能自叠加) |
|
||||
| 初始剩余次数 | 整数 [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(冷却闸下不可能自叠加) |
|
||||
|
||||
非调参的冻结项:三类合并策略语义、两种量纲语义、来源类别枚举、"先兑付后衰减"时序、拒绝原子性、零随机——改动走本文修订流程,不走调参。
|
||||
|
||||
联动纪律:增伤与削弱是当量锚的两端;盾牌加护与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)的承伤预期联动(掷点折扣轴——AC 行值变更须复算生存链区间;ac_up 层数恒 1、局内可达 AC 上限 11,复算随调参批);削弱与主角增益行同名重复授予只加时长、层数恒 1,"单轮层数上限受[行动经济](../排雷循环/行动经济.md)行动点约束"的联动不成立。这些联动值禁止独立调参,修订须重跑锚对照并同步登记册。
|
||||
联动纪律:增伤与削弱是当量锚的两端;盾牌加护与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)的承伤预期联动(掷点折扣轴——AC 行值变更须复算生存链区间;2026-09-20 主角增益续时批后 ac_up 层数恒 1、局内可达 AC 上限 11,复算随调参批);削弱改续时后"单轮层数上限受[行动经济](../排雷循环/行动经济.md)行动点约束"的联动随之失效(2026-09-18 续时定案——同名重复标中只刷新剩余次数、层数恒 1;2026-09-20 起主角侧 attack_up / ac_up 同口径)。这些联动值禁止独立调参,修订须重跑锚对照并同步登记册。
|
||||
|
||||
## 验收标准
|
||||
|
||||
@@ -243,7 +243,7 @@ empower 与 fortify 是敌方技能池增益技在状态池侧的数据落点(
|
||||
- 多向同构:主角侧增益与减益、敌方侧增强与削弱共用同一套受理、合并、倒数、清除语义,不存在第二套计时逻辑。
|
||||
- 授予即生效:状态发入当轮结算即兑付第一次,无"待生效"中间态。
|
||||
- 只登记不去重:同一事实被转化两次授予,登记两层——去重责任在战斗结算。
|
||||
- 叠层不减钟:叠加策略行叠加只加层数,剩余次数保持首次授予定下的轨迹不变(累加策略行则相反——层数不变、剩余次数加上本次授予的时长;两策略逐行读表)。
|
||||
- 叠层不减钟:叠加策略行叠加只加层数,剩余次数保持首次授予定下的轨迹不变(刷新策略行则相反——层数不变、只重置剩余次数;两策略逐行读表)。
|
||||
- 满层拒绝原子且留痕:叠满后再授予返回失败、状态池逐字不变;结算明细多一行"奖励未生效"(含来源)。
|
||||
- 先兑付后衰减:每轮结算恰做一次衰减,归零实例当轮移除;停手轮、弃图刷新轮照常衰减。
|
||||
- 合成不设上下限:状态池输出的有效值恒等于层数 × 行值,越出百分比域不封顶不保底;地板在战斗结算侧。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 战斗结算
|
||||
|
||||
> 一句话:轮末结算的编排与逐行兑现
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.5
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.3
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
| 判定行 | 胜 / 负 / 继续各一行;终局判定行同时是胜负画面的数据锚 |
|
||||
| 高光行 | 完美封印成立时一行,只记实际有效兑付 |
|
||||
|
||||
每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调封印标中、清图奖励这类值得高亮的结果)。奖励直伤行的数值字段沿用 `effectiveness` 键名、值为整数点数——键名与量纲是数据导出与分析工具的兼容契约,改动须连动分析工具面。没生效也要留痕:两类"未生效"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。
|
||||
每行还携带公共字段:来源类别、来源格、轮次、显示名、数值,以及可空的高光标记(用于强调封印标中、清图奖励这类值得高亮的结果)。没生效也要留痕:两类"未生效"行如实记录合法例外,呈现时聚合计数,不逐行刷屏。
|
||||
|
||||
任何一行找不到来源即断言失败——宁可返回错误,也不让"合并到看不出来源"的呈现混过数据层。但有些行天然没有棋盘来源,断言为此留了两层豁免,豁免集合是封闭清单,改动同样走本文修订。
|
||||
|
||||
@@ -266,23 +266,23 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值
|
||||
|
||||
挖掘期刷新是玩家在挖掘中途主动换图,经[行动经济](../排雷循环/行动经济.md)扣费:扣费成功后按"旧板退役、生成新板、装板、资源预分配恰一次、变异布置"编排,本轮不打断——没有结算段、没有轮次结束。储备不足被拒是玩家决策的合法结果,不是编排错误。刷新图不发清图奖励,完美封印计数随装板重置。
|
||||
|
||||
### 换图系专长:刷新事实与本轮兑付(已实装——2026-11-01 迭代 46)
|
||||
### 换图系专长:刷新事实与本轮兑付(设计定稿,待实现)
|
||||
|
||||
持有换图系后续专长时,成功刷新除执行上述换图编排外,还产生一次 `board_refreshed` 事实——它是换图系效果兑付的统一事实挂点。事实只在新板装板成功后登记恰一次;刷新被拒绝时不产生事实,任何后续效果都不触发。同一轮多次成功刷新按发生顺序逐次登记,不合并成一次。
|
||||
持有换图系后续专长时,成功刷新除执行上述换图编排外,还产生一次 `board_refreshed` 事实。旧图退役前保存它的探索进度与清图奖励对象身份;新板就位后登记事实。刷新被拒绝时不产生事实,任何后续效果都不触发。同一轮多次成功刷新按发生顺序逐次登记,不合并成一次。
|
||||
|
||||
刷新只登记事实,不当场插入结算。各效果在既有五段与对应消费点兑付:
|
||||
刷新只登记事实,不当场插入结算。各效果在既有五段内兑付:
|
||||
|
||||
| 专长 | 兑付规则 |
|
||||
|---|---|
|
||||
| 崩壁冲击 | 每次刷新事实开一张全体敌人伤害单据,在本轮战斗段末经伤害控制器统一结账;目标按结账时的存活状态解析 |
|
||||
| 蓄力周转 | 每次刷新事实累计一层,最多 3 层;本轮下一次主角攻击消费全部层数对应的攻击加成,随后清零;战斗结束不跨战保留 |
|
||||
| 石层缓冲 | 每次刷新事实把当轮全局减伤状态置为生效;本轮主角每次受到伤害都固定减少 2 点,同一轮多次刷新不继续叠加 |
|
||||
| 坑道小憩 | 每次刷新事实累计 1 点恢复量,最多 3 点;本轮授予段合计回复生命,封顶生命上限,随后清零;满血时记未生效行 |
|
||||
| 矿道红利 | 每次刷新事实固定获得 1 GP,走战斗内 GP 缓冲并在授予段留痕;不按旧图探索进度折算,也不与清图奖励绑定 |
|
||||
| 弃图轰击 | 每次刷新事实开一张全体敌人伤害单据,在本轮战斗段末经伤害控制器统一结账;目标按结账时的存活状态解析 |
|
||||
| 换图蓄势 | 每次刷新事实当场累计一层,受表值上限约束;下一次主角攻击附加全部层数对应的伤害,随后清零;战斗结束清空 |
|
||||
| 换图庇护 | 每次刷新事实在本轮授予段经既有护盾通道授予一次,不新开挖掘期状态入口 |
|
||||
| 弃图休整 | 每次刷新事实在本轮授予段回复生命,按既有治疗规则封顶生命上限;满血时记未生效行 |
|
||||
| 弃图回收 | 每次刷新事实在本轮授予段按旧图探索进度回收其清图奖励对象;进度与奖励口径归[地图战斗资源](地图战斗资源.md) |
|
||||
|
||||
全局受伤事件统一:`damage_taken` 是主角受到伤害的唯一效果事件——敌方攻击、危险格、错误封印资源直伤与地形四条伤害路径统一经同一发射口发出、同一消费族消费,对该事件的全部既有订阅(减免、护甲修正、反击攒点、护盾吸收)对四条路径同等生效。石层缓冲订阅该事件并在现行伤害下限之前消费。临时生命值不在换图系流派设计范围内。
|
||||
弃图轰击若击杀最后敌人,仍由本轮判定段按既有顺序判胜,不新增挖掘期即时胜利出口。换图系效果使用扩展行留痕:来源写明专长与被弃旧图,弃图轰击走伤害单据,护盾、回血与回收各保留自己的兑付记录;它们不指向单一棋盘格,使用明细流水的专长刷新效果来源豁免,不伪造来源格。
|
||||
|
||||
崩壁冲击若击杀最后敌人,仍由本轮判定段按既有顺序判胜,不新增挖掘期即时胜利出口。换图系效果使用扩展行留痕:崩壁冲击走伤害单据,石层缓冲在受伤消费行体现减免,坑道小憩与矿道红利分别走治疗与 GP 入账记录。刷新事实来源不指向单一棋盘格,使用明细流水的专长刷新效果来源豁免,不伪造来源格。
|
||||
弃图回收不是清图:不触发完美封印加倍,不发清图事件,不改变「刷新图不发清图奖励」。它兑的是被放弃旧图按探索进度回收的一部分奖励。
|
||||
|
||||
战斗开始按固定序列开张:关上配置热重载的闸门、初始化状态池、掷骰定敌方实际数量、生成敌人、装板并预分配资源恰一次、变异布置([地形变异](../排雷循环/地形变异.md)——棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 → 战斗资源格设置 → 变异格设置,消费 `variant_place` 封闭流)、发开轮通知并执行开轮摇号(摇定首轮各敌本拍动作)。上一关的大奖励(如血上限提升)经战斗开始入参的覆盖值通道生效,内容归[冒险结构](../冒险循环/冒险结构.md)。
|
||||
|
||||
@@ -379,9 +379,8 @@ 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 配 ×1.5 的突刺落到 2 而不是 1)。
|
||||
普通攻击也是一行技能行,两条域校验锁死它的特殊性:倍率恒 1、冷却恒 0——想配倍率或冷却的就不是普攻,是伤害技。技能伤害存倍率、不存固定值:伤害 = 施放者攻击基线 × 技能倍率——同一行"重击",不同敌种按各自攻击基线缩放 = 基线 A × 技能倍率 M(行值读 enemy.tres / skills.tres),深层强化行自动缩放,这是共享技能表成立的前提;**固定的绝对值伤害不满足该前提**(不随基线缩放、跨关稀释——Brotato 教训),"固定值伤害"的合法形态是**必中附加分量 rider_mult**(×攻击基线的必中小倍率,2026-09-19 实装)。增益技带效果引用与持续回合,百分比天然按施放者同池生效,无缩放问题;妨害技同构,落点由状态行 target_side 路由到主角。
|
||||
|
||||
池构成纪律(加载校验,违规拒绝入表):**每池恰一行普通攻击**(普攻冷却恒 0 → 可摇集合恒非空,"空池不可能"由结构保证,最坏情况怪物平砍等待——那是玩家的反打窗口而非死拍);**每池增益类技能 ≤1**(非 RPG,怪物连出不同增益显得呆);**每池妨害类技能 ≤1**(2026-09-19 新效应词汇批同构纪律);**每池至少一行不带触发条件的特殊技**(普攻不算——保底特殊技集合恒非空的结构保证,2026-10-26 触发条件批);池条目 skill_id 悬空致命;权重 > 0、冷却 ≥ 0。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 行动经济
|
||||
|
||||
> 一句话:每轮行动点怎么花、停手与刷新的代价
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.4
|
||||
> 状态:已定稿(自旧体系迁移);现行为 v1.2
|
||||
|
||||
## 设计目的
|
||||
|
||||
@@ -91,18 +91,18 @@
|
||||
|
||||
停手与刷新合起来构成这本账的基线节拍:满行动点一轮停手,正好攒够一次刷新。行动点修正队列会扰动该节拍的实际兑现(+1 轮可多挖一格),不变式本身不动;对账归曲线调参批。
|
||||
|
||||
### 换图系专长对储备与刷新的改写(已实装——2026-11-01 迭代 46 落表 `feats.tres`)
|
||||
### 换图系专长对储备与刷新的改写(设计定稿,待实现)
|
||||
|
||||
换图系两件基座分别改写 RAP 的来源与刷新价格。它们只给持有专长的玩家增加规则,不改变无专长时的基线账本。
|
||||
|
||||
| 专长 | 规则 | 不变边界 |
|
||||
|---|---|---|
|
||||
| 矿脉储能 | 每次付费挖格被受理后,额外获得表值 RAP;安全格与危险格同口径 | 连锁展开只随首格的一次付费动作结算,不逐格追加;封印、移动、刷新不产 RAP |
|
||||
| 省力周转 | 弃图刷新的 RAP 成本为基线的 50% | 只改价格;AP、轮次、每轮刷新上限与换图流程不变 |
|
||||
| 掘进储能 | 每次付费挖格被受理后,额外获得表值 RAP;安全格与危险格同口径 | 连锁展开只随首格的一次付费动作结算,不逐格追加;封印、移动、刷新不产 RAP |
|
||||
| 折价换图 | 弃图刷新的 RAP 成本为基线的 50% | 只改价格;AP、轮次、每轮刷新上限与换图流程不变 |
|
||||
|
||||
两件基座可以同时持有并叠加:付费挖格照常产 RAP,刷新同时按半价扣费。两条规则作用在不同账户入口,不互相覆盖,也不设互斥。折后价向下取整,受理门槛判断与实际扣费使用同一个折后值,只改价格不改拒绝判据——刷新仍须储备达到折后门槛才受理;储备不足或超过每轮次数上限时拒绝,状态不变。RAP 仍在战斗结束清零,刷新本身不返 RAP。
|
||||
两件基座可以同时持有并叠加:付费挖格照常产 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`,provisional 归调参批) | 每次受理的付费挖格兑付一次;连锁不逐格追加 |
|
||||
| (跨表)省力周转成本率 | 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,行动点进入休眠。
|
||||
- 跨字段校验:任一动作成本配置超过每轮预算时,加载期致命中止。
|
||||
- 零随机:同一动作序列驱动两个独立行动点实例,轮次结束记录与储备轨迹逐项相同。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 生涯
|
||||
|
||||
> **一句话定位**:营地里与三柜台平级的**局外只读界面**——营地管「花」(花钥石买永久强化),生涯管「看」(看自己跨局攒下的一切:累计战绩、历史冒险、图鉴收集)。冒险进行中不给任何数据回看(2026-10-04 用户裁决 P-C7 的延续),生涯是历史数据唯一的家。
|
||||
> **状态**:期一已实装(2026-10-07 迭代 18 卡④生涯界面实装批·期一——跨局汇总页+主菜单入口+终身计数域存档 v6 落地;期二/期三另行立项,本文不预占排期)。入口拓扑已随迭代 33 卡② 三入口化换代、营地里经笔记本接入归迭代 34 卡④(信息架构节 v1.1,2026-10-22 反哺写入)。现行为 v1.4。
|
||||
> **状态**:期一已实装(2026-10-07 迭代 18 卡④生涯界面实装批·期一——跨局汇总页+主菜单入口+终身计数域存档 v6 落地;期二/期三另行立项,本文不预占排期)。入口拓扑已随迭代 33 卡② 三入口化换代、营地里经笔记本接入归迭代 34 卡④(信息架构节 v1.1,2026-10-22 反哺写入)。现行为 v1.3。
|
||||
> **来源**:[待办清单「『生涯』界面——冒险中数据回看的唯一承接处」条](../../../../待办清单.md)(2026-10-04 用户裁决 P-C7——冒险中不给数据回看,历史战报与结算明细的查看去向 = 新「生涯」界面)。
|
||||
|
||||
## 一、信息架构
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
| 界面 | 管什么(现状指证) | 与生涯的边界 |
|
||||
|---|---|---|
|
||||
| 营地([camp UX spec](../../ux/camp.md)) | 跨局成长**操作**面(花钥石买强化 / 装配 / 点树——2026-11-01 迭代 47 卡④ 起为营地场景图+五功能窗口形态);原底栏「最近冒险」条带已随同批撤下(P6 甲提前落地),逐局历史回看唯一归生涯 | 营地不改生涯只读定位;生涯不做任何购买入口,也不放「去买」按钮。营地条带已撤(P6 甲——随迭代 47 卡④ 执行) |
|
||||
| 营地([camp UX spec](../../ux/camp.md)) | 跨局成长**操作**面(花钥石买强化 / 装配 / 点树)+ 底栏「最近冒险」条带——营地底栏把摘要逐局拼行(行模板「完成/放弃/失败 · 通关 N 关 · 钥石 +N · 种子 N」)、无截断上限,数据源与生涯历史回看同属一份最近摘要 | 营地不改生涯只读定位;生涯不做任何购买入口,也不放「去买」按钮。营地条带的去留是本提纲必答项——见 §五 P6 |
|
||||
| 状态面板(迭代 17 卡③,并行批开发中) | **局内**只读查看这一局进行中的钥石与本局冒险进度 | 状态面板看当局当前值,生涯看跨局累计与历史;局内无生涯入口(P4 甲) |
|
||||
| 结算屏([settlement-summary UX spec](../../ux/settlement-summary.md)) | 冒险收束当下的入账明细——冒险收束时由结算屏呈现入账明细与钥石明细(复用终局面账行的呈现形态) | 收束当下的呈现保留不动;生涯承接的是收束之后的任意时刻回看 |
|
||||
| 帮助手册(Beta 批,未实装——教学内容归 Beta 帮助手册) | 讲「怎么玩」 | 生涯讲「我玩过什么」。图鉴条目文案与手册是否共用内容源,接口登记归 Beta 批一并裁 |
|
||||
@@ -82,7 +82,7 @@
|
||||
| 期 | 内容 | 前置 | 建议窗口(候选) |
|
||||
|---|---|---|---|
|
||||
| 期一 | 跨局汇总页 + 主菜单生涯入口落位。总局数/通关局数两个终身计数域随该批做存档 schema 迁移——**✅ 已实装(2026-10-07 迭代 18 卡④)**:汇总段六类九项+营地与系统组生涯钮+存档 v6 两终身计数域〔v5 升 v6 幂等迁移〕;闸读数与代拍板追认单原记于该批出口报告,该件已按「吸收即死」删除,读数以本行与该批提交信息为准 | 提纲评审通过。实装批前置义务 = 对齐迭代 16 UI 批后的现行基线:字号六档 / 语义色 / 按钮最小高 40 点等现行界面基线 / 色板单源 / 页面壳的模态栈纪律——与卡③需求卡片同款红线,本文只登记、不展开 | Alpha 后最近一个 UI 窗口 |
|
||||
| 期二 | 历史冒险回看整段:N 扩容 + 用时埋点 + 旧档摘要迁移 + 逐局列表 + 单局明细(遗留明细界面复活承接)——营地条带处置已随 P6 甲提前落地(2026-11-01 迭代 47 卡④ 撤下),期二无残留条带义务 | 期一。实装批开工前须向用户呈报两件:N 终值读数(20 / 50 / 维持 10 三选一)与「用时」埋点口径(计时起点、暂停是否计入——字段级细节本文不做) | 期一落地后独立批 |
|
||||
| 期二 | 历史冒险回看整段:N 扩容 + 用时埋点 + 旧档摘要迁移 + 逐局列表 + 单局明细(遗留明细界面复活承接)+ 营地条带处置(P6 裁案落地) | 期一。实装批开工前须向用户呈报两件:N 终值读数(20 / 50 / 维持 10 三选一)与「用时」埋点口径(计时起点、暂停是否计入——字段级细节本文不做) | 期一落地后独立批 |
|
||||
| 期三 | 图鉴承接(敌人优先,范围与顺序随 P3 甲);「见过」记账面 schema 义务随该批执行 | 期二或独立评估 | Beta 窗口候选(与教学引导同段评估——图鉴的「认得」轴与教学相关;待办清单教学引导节各条 2026-10-04 起已整体标 Beta 阶段) |
|
||||
|
||||
本地化文案权:生涯界面本体按钮、页签、空态文案归[设置与本地化](../表现与支撑/设置与本地化.md)线(全仓无多语言实装;文案权先例 = 教学文案定稿权登记于待办清单),实装批承接。
|
||||
@@ -91,10 +91,9 @@
|
||||
|
||||
P0-P5 六点已于 2026-10-06 用户全裁(案甲 × 6,备选案与淘汰依据在需求卡片 §8),结论已写入上文对应节,不重复列。本批新发现的待裁点一项:
|
||||
|
||||
### P6 · 营地底栏「最近冒险」条带去留(已裁甲案并落地——2026-11-01 迭代 47 卡④)
|
||||
### P6 · 营地底栏「最近冒险」条带去留(期二必答项)
|
||||
|
||||
**现状与问题**:营地底栏「最近冒险」条带把摘要逐行全部拼进同一个标签(行模板「完成/放弃/失败 · 通关 N 关 · 钥石 +N · 种子 N」,无截断上限),数据源与生涯历史回看同属一份最近摘要。生涯期二把上限扩到 N 后,条带按现行逻辑会把 N 行全拼进底栏——这是本批复核发现的实际耦合点,期二落地时必须给条带一个去处。
|
||||
**裁决与执行**:用户裁甲案(2026-11-01,随迭代 47 卡④ 四柜台拆解执行)——条带已撤下、逐局历史回看唯一归生涯;期二生涯历史回看批的条带处置义务随之清空。
|
||||
|
||||
| 案 | 内容 | 淘汰/保留依据 |
|
||||
|---|---|---|
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Status**: Complete(现行)
|
||||
> **Author**: user + ux-designer
|
||||
> **Last Updated**: 2026-11-01
|
||||
> **Last Updated**: 2026-10-29
|
||||
> **Platform Target**: PC (Steam)——Keyboard/Mouse 主(全部操作纯鼠标可完成)、Gamepad Partial 非 MVP、Touch None(同 technical-preferences)
|
||||
> **上游修订依赖**: 弃图刷新挖掘期受理已随布局决策 4 落稿上游(2026-09-12:行动经济 Active 受理 + 每轮上限 2 次、排雷玩法 Active·仅刷新路径装板、战斗结算的挖掘期刷新编排 + 恒同盘不变、系统索引的刷新规则同步——见 OQ11)
|
||||
> **Template**: UX Spec
|
||||
@@ -141,7 +141,7 @@
|
||||
```
|
||||
|
||||
- N = 棋盘长宽两边的较大者;格间距 2px;垂直预留 24px(上 12 / 下 12);悬崖厚度 8px;结构钳制区间 [26, 46]px。
|
||||
- 格边长可读性以用户验收的现行画面为锚点(2026-11-01 用户裁——现行渲染值即基准,基座档 14×14=28px 达标);全域不设人造的格边长可读阈值。结构钳制 [26, 46] 只管算式结果不越界;格边长接线守卫=生产写入值与上式单点算式的返回值严格相等,最大板型的推定读数仅留痕观察、不作判红依据。
|
||||
- 可读下限 30px——与结构钳制分开:钳制管算式不越界,下限管玩家能否看清。
|
||||
- N 的取值与每关板型的对应关系读[棋盘生成](../systems/排雷循环/棋盘生成.md),现行值的事实源是 `data/tables/board_gen.tres`。
|
||||
- 数字字号 = max(18, ceil(格边长 × 0.5)),像素字体整数倍缩放(canvas_items stretch 保证清晰)。
|
||||
|
||||
@@ -366,7 +366,7 @@
|
||||
- [ ] 两种格态(已开格 / 未开格)灰度截图下一眼可辨(排雷玩法×6②)
|
||||
- [ ] 悬停光标区分移动/挖/封印三语义;可挖域(8 向相邻)常驻指示;非相邻格悬停显禁止反馈(排雷玩法×6③)
|
||||
- [ ] 已引爆危险格呈"可通行可读"(排雷玩法×6④)
|
||||
- [ ] 全部数字对所在格底对比 ≥4.5:1;明度分层规则无违例(art-bible §2/§4.1——格底功能分档合法,格态语义由形与色相承载)
|
||||
- [ ] 全部数字对所在格底对比 ≥4.5:1;明度分层规则无违例(art-bible §2/§4.1,v3.0——状态区分不走明度,格底功能分档合法)
|
||||
|
||||
**交互与反馈**
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 营地场景 UX spec(camp)
|
||||
|
||||
> 状态:v2.4(现行)
|
||||
> 状态:v2.2(现行)
|
||||
> 事实源:本 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)(主菜单语境)
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
**中景带自画面底部往上约 2/3 处起**;近景物件允许压住围墙(围墙不可点,z 序在近景物件之下),**唯一例外=中间门洞必须留空、不被任何物件遮挡**。围墙形态、门扇比例与帐篷尺寸的现行口径归 [art-bible](../../art/art-bible.md) §6.2,本 spec 不再复述。
|
||||
|
||||
坐标草案与分层验收判据见 [camp-scene-prompts](../../art/camp-scene-prompts.md) §2.3/§4(10 张口径现行事实源);2026-10-22 美术方案讨论沿革查 git 提交史。
|
||||
坐标草案与分层验收判据见 [camp-scene-prompts](../../art/camp-scene-prompts.md) §2.3/§4(10 张口径现行事实源);2026-10-22 美术方案讨论留痕见 [提案](../../../reports/提案-营地场景图美术生成方案-2026-10-22.md)。
|
||||
|
||||
## 点选反馈(根态)
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
|
||||
## 顶栏
|
||||
|
||||
钥石余额常驻画面顶部一行小 HUD 栏(高 48px、全宽窄条、**浮层覆盖在场景图上层,不挤压画面**)。刷新入口已实装=营地刷新单口(2026-11-01 迭代 47 卡④⑤:进档与营地内写口尾触发,现开界面与顶栏余额同步取现值)。
|
||||
钥石余额常驻画面顶部一行小 HUD 栏(高 48px、全宽窄条、**浮层覆盖在场景图上层,不挤压画面**)。刷新入口=营地刷新单口(卡⑤同口挂接)。
|
||||
|
||||
## 叠层与开合
|
||||
|
||||
各界面开合走模态栈单口,层序按模态栈契约登记。**承载形态已定稿(2026-11-01 用户裁)**:五个功能界面(锻造工坊/秘宝匣/天赋树/奖励格图鉴/生涯记录)各以**功能窗口**打开——场景根留底可见,窗口之下垫半透明压暗遮罩(交互聚焦用,复用既有模态遮罩件,现值锚黑色 0.6;观感随样张目检校准,属普通设计决策)。**五窗互斥单开**(模态栈深恒 1,不开窗叠窗);各窗自带关闭钮。ESC 优先序:有功能窗口开着 → 先关最上层窗;无窗 → 才进 ESC 浮窗(返回主菜单/设置/退出游戏)。
|
||||
各界面开合走模态栈单口,层序按模态栈契约登记。**承载形态(独立全屏页 vs 单一内页切换)与叠层规则归卡④ §9 代拍板域,本 spec 不预设值**;派生规则:独立全屏页=场景根隐藏/单一内页切换=场景根留底(「缩放虚化」为第三形态,禁批内自选)。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) | 图区组件 + Accessibility 对比条 |
|
||||
| [art-bible](../../art/art-bible.md) | 方块地牢观感(§3.2 圆角方形世界侧)、色板冻结(§4.1)、半拟物(§7.1)、明度分层(§2,v3.0——状态不走明度、空间光照可走) | 图区组件 + Accessibility 对比条 |
|
||||
|
||||
---
|
||||
@@ -30,13 +30,12 @@ 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/camp_scene_layout)
|
||||
var presentation: ConfigDomainPresentation # 表现层布局域一表(battle_layout)
|
||||
|
||||
var is_ready := false
|
||||
var _diagnostics: Array[Dictionary] = [] # {severity, category, message}
|
||||
@@ -150,9 +149,6 @@ 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,7 +1,6 @@
|
||||
## 表现层布局域容器(D2 域容器——config_db 表持有按域收敛):战斗屏与营地场景布局两表类型化持有。
|
||||
## 表现层布局域容器(D2 域容器——config_db 表持有按域收敛):战斗屏布局一表类型化持有。
|
||||
## 零方法零行为——纯持有结构;「经 ConfigDB 单点读取」=经其持有的容器与表对象(架构 :88 定稿注记)。
|
||||
class_name ConfigDomainPresentation
|
||||
extends Resource
|
||||
|
||||
var battle_layout: BattleLayoutTable
|
||||
var camp_scene_layout: CampSceneLayoutTable
|
||||
@@ -1,13 +0,0 @@
|
||||
## 营地场景单层规格(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
|
||||
@@ -1 +0,0 @@
|
||||
uid://bll8d0s1nmarf
|
||||
@@ -1,67 +0,0 @@
|
||||
## 营地场景布局表 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
|
||||
@@ -1 +0,0 @@
|
||||
uid://c6p8wyvcc5upp
|
||||
@@ -1,82 +0,0 @@
|
||||
[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_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 views: Dictionary = {} # {camp_overlay, shop_overlay, toast, start_overlay, logger} # type-hint: ignore(三束异构混载——视图句柄/查询 Callable/编排 Callable)
|
||||
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 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 camp_overlay: CampOverlay
|
||||
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
|
||||
views = ctx.views # 营地域五目标+刷新单口+关层收闭面(卡④ T3——CampOverlay 退役后投影目标改线)
|
||||
camp_overlay = ctx.views.camp_overlay
|
||||
shop_overlay = ctx.views.shop_overlay
|
||||
toast = ctx.views.toast
|
||||
start_overlay = ctx.views.start_overlay
|
||||
@@ -34,54 +34,21 @@ func _init(ctx: CampFlowContext) -> void:
|
||||
_refresh_presentations = ctx.flow.refresh_presentations
|
||||
|
||||
|
||||
# 打开营地场景根(进档落点——卡④ T3 改挂:开层=交互式场景图,四子界面经场景点选进)
|
||||
# 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
|
||||
# 规范 2.3 遮挡纪律;打开时先刷条件解锁——达标未解锁就地入账)
|
||||
func open_camp() -> void:
|
||||
var meta = _meta_get.call()
|
||||
if meta == null:
|
||||
return
|
||||
meta.refresh_treasure_unlocks() # 条件解锁刷新
|
||||
start_overlay.visible = false
|
||||
_open_modal.call(views.camp_scene) # 模态栈单口(场景根开——选档层被栈遮挡挂起)
|
||||
views.refresh_bus.refresh(camp_data()) # 进档动线尾显式刷新一闸(卡⑤ 契约 5 时机②——HUD 读数重读)
|
||||
_open_modal.call(camp_overlay) # 模态栈单口(登记层开合一律走栈)
|
||||
camp_overlay.open_camp(camp_data())
|
||||
_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:
|
||||
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)
|
||||
_close_modal.call(camp_overlay) # 模态栈单口
|
||||
start_overlay.visible = true
|
||||
_refresh_presentations.call() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
|
||||
|
||||
@@ -112,7 +79,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
if not meta.buy_treasure_unlock(attr):
|
||||
toast.show_toast("购买被拒(余额不足或不可购买)", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
return
|
||||
# Y 刷新升级行购买(双入口之一——树上 ks_refresh 同账;
|
||||
@@ -120,7 +87,7 @@ func on_camp_buy(attr: String) -> void:
|
||||
if not meta.buy_attune_refresh():
|
||||
toast.show_toast("购买被拒", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -132,7 +99,7 @@ func on_camp_attune_toggle(item_id: String) -> void:
|
||||
if not meta.toggle_attunement(item_id):
|
||||
toast.show_toast("同调位已满——先卸下再装配", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
|
||||
|
||||
# 天赋树购买(写口唯一归 MetaProgression.tree_buy/tree_buy_fork;Fork 节点购=激活侧)
|
||||
@@ -144,7 +111,7 @@ func on_camp_tree_buy(node_id: String) -> void:
|
||||
if not ok:
|
||||
toast.show_toast("购买被拒——前置/行门槛/余额不足", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -156,7 +123,7 @@ func on_camp_fork_toggle(group: String, side: String) -> void:
|
||||
if not meta.tree_toggle_fork(group, side):
|
||||
toast.show_toast("切换被拒——未激活组请先购买", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -169,7 +136,7 @@ func on_camp_forge_buy(route_id: String) -> void:
|
||||
if not meta.forge_buy_level(route_id):
|
||||
toast.show_toast("锻造被拒——钥石不足或路线不可锻造", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -181,7 +148,7 @@ func on_camp_forge_unlock(route_id: String) -> void:
|
||||
if not meta.forge_buy_unlock(route_id):
|
||||
toast.show_toast("解锁被拒——钥石不足或已解锁", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
@@ -195,7 +162,7 @@ func on_camp_forge_select(route_id: String) -> void:
|
||||
if not ok:
|
||||
toast.show_toast("切换被拒——路线未解锁", "warn")
|
||||
return
|
||||
views.refresh_bus.refresh(camp_data())
|
||||
camp_overlay.refresh_camp(camp_data())
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
|
||||
@@ -123,37 +123,19 @@ func _start_adventure_from_menu() -> void:
|
||||
|
||||
|
||||
# 确认弹窗分派(卡③ 契约 3):按 _confirm_consumer 路由——"delete_slot"=卡③ 删档确认
|
||||
#(确认才删、取消只关窗——裁决 3)。"gate"/"gate_new"=卡④ 大门两分支消费方(有/无断点),
|
||||
# "campfire_leave"=篝火二次确认(卡④ T4 接入——分派机制扩展第三/第四消费方)。
|
||||
#(确认才删、取消只关窗——裁决 3)。"new_adventure" 消费方已随卡② 主菜单确认链摘除退役
|
||||
#(mn6/V5——主菜单零确认残留),分派机制保留(卡④ 到位接入第三消费方不返工)。
|
||||
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 = ""
|
||||
|
||||
|
||||
@@ -518,35 +500,12 @@ func _sync_status_entry() -> void:
|
||||
MainOverlayFlow._sync_status_entry(self)
|
||||
|
||||
|
||||
# ---- 探针缝保缝(click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd / main_overlay_flow.gd)----
|
||||
# ---- 探针缝保缝(click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.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)
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
## 组合根 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()
|
||||
@@ -1 +0,0 @@
|
||||
uid://caa868un7dnt4
|
||||
@@ -10,11 +10,7 @@ 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) # 模态栈单口
|
||||
# 语境分支(卡⑤ 契约 3——谓词=camp_esc_overlay.visible,谁开的设置谁收):
|
||||
# 营地浮窗路径关设置回浮窗不回主菜单(栈语义:浮窗登记在下、由树序遮挡非属性隐藏);
|
||||
# 菜单路径保留开始层恢复硬副作用。
|
||||
if not root.ui.modals.camp.esc_overlay.visible:
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)----
|
||||
@@ -44,43 +40,7 @@ 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) # 模态栈单口
|
||||
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("是否开始新冒险?", "是", "否")
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# 生涯页快照投影 —— 承接体见 HudPresenter.career_data(开层调用点直接改引)
|
||||
@@ -105,7 +65,7 @@ static func _on_save_select_closed(root: Variant) -> void:
|
||||
|
||||
|
||||
|
||||
# 空槽新档链(卡④ T3 换代:落点=营地场景根——CampOverlay 随四柜台拆解退役):卡① 创建=激活=白板落盘,
|
||||
# 空槽新档链(Q3 裁乙暂行落点=现役 CampOverlay;卡④ 重立卡后改挂场景图):卡① 创建=激活=白板落盘,
|
||||
# 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。
|
||||
static func _on_save_select_new(root: Variant, slot: int) -> void:
|
||||
root.save_manager.slots.create_slot(slot)
|
||||
@@ -113,7 +73,7 @@ static func _on_save_select_new(root: Variant, slot: int) -> void:
|
||||
|
||||
|
||||
|
||||
# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→落点=营地场景根(卡④ T3)。
|
||||
# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→暂行落点 CampOverlay。
|
||||
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,34 +92,8 @@ 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)
|
||||
# 营地场景根装配(布局表经 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.camp_overlay = root.ui.screens.camp_screen.camp_overlay
|
||||
# 营地屏壳插位(夹商店层与设置层之间)由 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)
|
||||
@@ -184,18 +158,9 @@ 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_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},
|
||||
{"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},
|
||||
{"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,
|
||||
@@ -203,19 +168,14 @@ 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)
|
||||
# 营地信号接线(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.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)
|
||||
root.ui.screens.menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口
|
||||
root.ui.screens.menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单
|
||||
|
||||
@@ -234,12 +194,7 @@ 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
|
||||
# 营地屏壳(营地场景根的屏壳容器)。
|
||||
# 场景图视图代码构建(CampSceneView 继续代码构建、零 .tscn 化改造);布局表经组合根注入
|
||||
# (main_ui_build 装配段 setup_layers/bind_hotspots——视图禁自载表路径);购买/开合接线归组合根经
|
||||
# CampShopPresenter(信号连接只在组合根)。可独立实例化(F6/headless 挂载)——V2 探针断言载体。
|
||||
# 营地屏壳(营地覆盖层的屏壳容器)。
|
||||
# 营地覆盖层视图原样构建(CampOverlay 类视图继续代码构建、零 .tscn 化改造——契约 2);
|
||||
# 购买/开合接线归组合根经 CampShopPresenter(信号连接只在组合根)。
|
||||
# 可独立实例化(F6/headless 挂载)——V2 探针断言载体。
|
||||
|
||||
var camp_scene: CampSceneView # 营地场景图(交互式营地场景根——main 组合根句柄指向本屏成员)
|
||||
var camp_overlay: CampOverlay # 营地覆盖层(原 build_overlays 营地段迁入——main 组合根句柄指向本屏成员)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE # 屏壳=纯容器(ui_input 实证:全屏 STOP 壳会吃掉下层模态鼠标输入)
|
||||
camp_scene = CampSceneView.new()
|
||||
add_child(camp_scene)
|
||||
camp_overlay = CampOverlay.new()
|
||||
add_child(camp_overlay)
|
||||
@@ -1,12 +0,0 @@
|
||||
## 营地交互域模态句柄子容器:持营地域全部模态句柄(终态六枚=营地场景根+锻造/秘宝/天赋树/图鉴四子界面+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 浮窗(卡⑤——终态六枚落满)
|
||||
@@ -1 +0,0 @@
|
||||
uid://goaqtequmh8s
|
||||
@@ -1,4 +1,4 @@
|
||||
## 模态层域句柄册:十二个平铺模态句柄+营地交互域子容器(camp)+模态栈编排(ModalFlow——登记/开合/输入锁谓词)的注册面域容器。
|
||||
## 模态层域句柄册:十三个模态覆盖层句柄+模态栈编排(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: UiCampModals
|
||||
var camp_overlay: CampOverlay
|
||||
var settings_overlay: SettingsOverlay
|
||||
var status_panel: StatusPanel
|
||||
var career_overlay: CareerOverlay
|
||||
@@ -22,7 +22,6 @@ 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,12 +22,7 @@ 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_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": "camp_overlay", "kind": "modal", "tags": [], "shell": "camp_screen"},
|
||||
{"key": "settings_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "status_panel", "kind": "modal", "tags": []},
|
||||
{"key": "career_overlay", "kind": "modal", "tags": []},
|
||||
@@ -63,12 +58,7 @@ func node(key: String) -> Node:
|
||||
"end_overlay": return modals.end_overlay
|
||||
"shop_overlay": return modals.shop_overlay
|
||||
"camp_screen": return screens.camp_screen
|
||||
"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
|
||||
"camp_overlay": return modals.camp_overlay
|
||||
"settings_overlay": return modals.settings_overlay
|
||||
"status_panel": return modals.status_panel
|
||||
"career_overlay": return modals.career_overlay
|
||||
|
||||
@@ -203,11 +203,15 @@ 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
|
||||
_cell = float(_cell_for(maxi(_board.width, _board.height), avail))
|
||||
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))
|
||||
_hero_sprite.set_cell_size(_cell, float(SEP))
|
||||
var stride := _cell + float(SEP)
|
||||
for c in _cells:
|
||||
@@ -220,17 +224,6 @@ func set_cell_area(avail: Vector2) -> void:
|
||||
_update_world_size()
|
||||
|
||||
|
||||
# 格边长算式唯一落点(迭代 46 卡C T1 自 set_cell_area 抽出——逐位同式:SEP/DEPTH_PX 读本文件
|
||||
# 既有常量、clampi [26,46] 留本函数内;名字带下划线是 S13 max-public-methods 27 顶格的约束,禁改公开名。
|
||||
# 探针(非当前板型 13/14×实板可用面积)与单元测试(四档板型)经本函数求值,复写算式即造第二口径)
|
||||
static func _cell_for(n: int, avail: Vector2) -> int:
|
||||
var sep := SEP # 格间距(_init 同值)
|
||||
var d := DEPTH_PX
|
||||
var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出)
|
||||
var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n)))
|
||||
return clampi(cell, 26, 46)
|
||||
|
||||
|
||||
# 悬崖参数下发(网格像素尺寸/步距/列数/材质锚色——BoardWorld._draw 消费)
|
||||
func _refresh_slab() -> void:
|
||||
if _board == null or _board.width == 0:
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
## 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())
|
||||
@@ -1 +0,0 @@
|
||||
uid://col25elh1utw5
|
||||
@@ -1,27 +0,0 @@
|
||||
## 营地顶栏小 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)))
|
||||
@@ -1 +0,0 @@
|
||||
uid://by6ugjfdicgud
|
||||
@@ -0,0 +1,655 @@
|
||||
## 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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dvg2wmjh87p8a
|
||||
@@ -1,17 +0,0 @@
|
||||
## 营地刷新单口:各写口尾→现开界面刷新+场景根 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) # 现开界面就地重刷(未开的零开销——留底形态下场景根恒开)
|
||||
@@ -1 +0,0 @@
|
||||
uid://cdfmyxf87n6f6
|
||||
@@ -1,309 +0,0 @@
|
||||
## 营地场景图视图(交互式营地):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 = &""
|
||||
@@ -1 +0,0 @@
|
||||
uid://btsrctv5ddx16
|
||||
@@ -1,88 +0,0 @@
|
||||
## 营地子界面模态壳基类(锻造工坊/秘宝匣/天赋树/奖励格图鉴四子界面公共形态):
|
||||
## 实底全屏暖亮档+石纹暗角+顶栏(铭牌+钥石徽)+内容列+底栏返回钮。
|
||||
## 开合可见性归模态栈(登记层开合一律走栈);返回钮只发 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()
|
||||
@@ -1 +0,0 @@
|
||||
uid://dv0gyjyrtoxkh
|
||||
@@ -18,7 +18,6 @@ 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()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
## 奖励格图鉴子界面(自 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)) # 内容挂起——余额徽随刷(占位态无行板)
|
||||
@@ -1 +0,0 @@
|
||||
uid://m1bcfjiceyiq
|
||||
@@ -1,18 +1,14 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 通用确认弹窗:open(text, confirm_label, cancel_label) 双钮外发 confirmed/cancelled;
|
||||
## open_three(text, confirm_label, secondary_label, cancel_label) 三钮增设 secondary_confirmed
|
||||
##(营地大门两分支——卡④ T4;双钮调用方零改动:次钮随 open 隐藏)。
|
||||
## 通用确认弹窗:open(text, confirm_label, cancel_label) 双钮外发 confirmed/cancelled。
|
||||
## 文案与按钮语义装配归调用方——本类零业务触碰。
|
||||
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
|
||||
|
||||
|
||||
@@ -31,9 +27,6 @@ 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)
|
||||
@@ -44,20 +37,12 @@ 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
|
||||
|
||||
@@ -70,9 +55,5 @@ func _emit_confirmed() -> void:
|
||||
confirmed.emit()
|
||||
|
||||
|
||||
func _emit_secondary() -> void:
|
||||
secondary_confirmed.emit()
|
||||
|
||||
|
||||
func _emit_cancelled() -> void:
|
||||
cancelled.emit()
|
||||
@@ -1,159 +0,0 @@
|
||||
## 锻造工坊子界面(自 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}
|
||||
@@ -1 +0,0 @@
|
||||
uid://b77xh0e3txmh5
|
||||
@@ -1,238 +0,0 @@
|
||||
## 秘宝匣子界面(自 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)
|
||||
@@ -1 +0,0 @@
|
||||
uid://dcakccwlacegq
|
||||
@@ -1,105 +0,0 @@
|
||||
## 天赋树子界面(自 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
|
||||
@@ -1 +0,0 @@
|
||||
uid://mjjrdelhi7yg
|
||||
@@ -247,19 +247,6 @@ 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,16 +6,15 @@
|
||||
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:
|
||||
@@ -42,12 +41,8 @@ func _ready() -> void:
|
||||
# 复原本项目基座(探针不该把进程留在末档尺寸)
|
||||
get_tree().root.content_scale_size = Vector2i(1280, 800)
|
||||
if _problems.is_empty():
|
||||
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"])])
|
||||
print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)"
|
||||
% [VIEWPORTS.size(), MIN_CELL])
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in _problems:
|
||||
@@ -96,25 +91,5 @@ 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")
|
||||
_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),
|
||||
}
|
||||
if cell < MIN_CELL:
|
||||
_problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])
|
||||
@@ -27,7 +27,6 @@ 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()
|
||||
@@ -52,7 +51,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.scene != null and main.ui.modals.camp.scene.visible,
|
||||
"营地": main.ui.modals.camp_overlay != null and main.ui.modals.camp_overlay.visible,
|
||||
"冒险": main.run_scope != null and main.run_scope.is_adventure,
|
||||
"终局": main.ui.modals.end_overlay != null and main.ui.modals.end_overlay.visible,
|
||||
}
|
||||
@@ -116,16 +115,14 @@ func _step_menu_to_camp() -> void:
|
||||
_expect_exclusive("②菜单→营地", "营地")
|
||||
|
||||
|
||||
# ---- ③ 营地 → 菜单(回退路径:篝火二次确认动线——卡④ 营地唯一回菜单出口;选档层随关层收闭同收)----
|
||||
# ---- ③ 营地 → 菜单(回退路径:营地关闭发自覆盖层自身信号)----
|
||||
func _step_camp_back_to_menu() -> void:
|
||||
if main.ui.modals.camp.scene == null:
|
||||
_problems.append("③营地→菜单:营地场景根句柄缺失〔句柄子容器批 V1〕")
|
||||
if main.ui.modals.camp_overlay == null:
|
||||
_problems.append("③营地→菜单:营地覆盖层句柄缺失")
|
||||
return
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"campfire")
|
||||
main.ui.modals.camp_overlay.closed.emit()
|
||||
await _frames()
|
||||
if not main.ui.modals.confirm_dialog.visible:
|
||||
_problems.append("③篝火:二次确认弹窗未现(camp.md v2.2 必须先确认)〔营地图批 V7〕")
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(confirmed 先于关层——卡④ T4)
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await _frames()
|
||||
_expect_exclusive("③营地→菜单(回退)", "菜单")
|
||||
|
||||
@@ -232,73 +229,3 @@ 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.scene, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, 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,15 +119,17 @@ 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;截图=可点击场景图形态——卡④ 契约 9 V1 验收载体)
|
||||
# ② 营地(进档开层新路径——卡② mn9;槽模板注入隔离命名空间)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
|
||||
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
await _snap("camp")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"campfire") # 收闭改接篝火动线(营地根态无关闭钮——卡④ T5)
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(关层收闭——选档层随收)
|
||||
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() # 关闭被栈还原的选档层(动线序中间态)
|
||||
await get_tree().process_frame
|
||||
# ③ 关卡画面(开始冒险→停点出图——入口停点语义;函数直调中间态 mn10)
|
||||
main._start_adventure_from_menu()
|
||||
|
||||
+53
-106
@@ -62,131 +62,123 @@ 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 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
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〕")
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
_problems.append("营地层未打开(进档开层新路径)")
|
||||
elif main.ui.modals.camp_overlay._tree_buy_btns.is_empty():
|
||||
_problems.append("树淬炼柜台购买钮缺失(点击路径——四柜台重排后树=主界面)")
|
||||
else:
|
||||
# 树钮句柄用后即取(refresh_camp 随刷重建树行板——旧钮 queue_free,跨刷新句柄必失效)
|
||||
iso_c.meta_state._add_keystone(9999)
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var buy_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
if String(card.node_id) == "sp_hp":
|
||||
buy_btn = card.btn
|
||||
if buy_btn == null:
|
||||
_problems.append("脊柱生命行缺失(点击路径)〔句柄子容器批 V1〕")
|
||||
_problems.append("脊柱生命行缺失(点击路径)")
|
||||
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)〔句柄子容器批 V1〕〔营地图批 V4〕" % [bal_before, iso_c.meta_state.keystone_balance(), price])
|
||||
_problems.append("营地树购买未出账(%d→%d 应 −%d)" % [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)〔句柄子容器批 V1〕〔营地图批 V4〕")
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)")
|
||||
# Fork 双态切换点击路径(V20——购=解锁默认 A,钮切 B;前置=同调位 1,先点亮)
|
||||
var socket_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._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.tree.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var fork_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
if String(card.node_id) == "fork_posture":
|
||||
fork_btn = card.btn
|
||||
if fork_btn == null:
|
||||
_problems.append("Fork 解锁钮缺失(点击路径)〔句柄子容器批 V1〕")
|
||||
_problems.append("Fork 解锁钮缺失(点击路径)")
|
||||
else:
|
||||
fork_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main.ui.modals.camp.tree.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var side_b: Button = null
|
||||
for fk in main.ui.modals.camp.tree._tree_fork_btns:
|
||||
for fk in main.ui.modals.camp_overlay._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 钮)〔句柄子容器批 V1〕")
|
||||
_problems.append("Fork 双态切换钮缺失(解锁后应展开 a/b 钮)")
|
||||
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)〔句柄子容器批 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, "未解锁")
|
||||
_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, "未解锁")
|
||||
if locked_status.is_empty():
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)")
|
||||
for l in locked_status:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)")
|
||||
var form_found := false
|
||||
for l in ws_labels:
|
||||
for l in camp_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)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:同调形态标签未用 STONE_DEEP(wc4 :509)")
|
||||
if not form_found:
|
||||
_problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)")
|
||||
var tier_vals := {}
|
||||
for v in UiKit.TREASURE_TIER_LABELS.values():
|
||||
tier_vals[String(v)] = true
|
||||
var tier_found := false
|
||||
for l in ws_labels:
|
||||
for l in camp_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)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:同调档位标签未用 STONE_DEEP(wc4 :513)")
|
||||
if not tier_found:
|
||||
_problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)")
|
||||
var refresh_inked := false
|
||||
for l in ws_labels:
|
||||
for l in camp_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)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)")
|
||||
if not refresh_inked:
|
||||
_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)
|
||||
_problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)")
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.scene.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("营地关闭后未恢复开始层〔句柄子容器批 V1〕")
|
||||
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("营地关闭后未恢复开始层")
|
||||
# ---- 选档界面行为走查(迭代 33 卡③):sl5 空槽新档链/sl6 有档槽进档链/sl7 删档二次确认双路径
|
||||
# /sl8 消费方分派单口/sl12 动线序不回闪。开层=函数直调(「进入存档」钮归卡② mn3——中间态在案);
|
||||
# 隔离=attach_slot_template 注入命名空间,夹具按槽直写(卡① 注入口)。----
|
||||
@@ -216,7 +208,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.scene.visible, main.ui.screens.start_overlay.visible])
|
||||
samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp_overlay.visible, main.ui.screens.start_overlay.visible])
|
||||
if samples[0][0]:
|
||||
_problems.append("[savesel] sl12 新档后选档层仍可见(落点开层未发生)")
|
||||
elif not samples[0][1]:
|
||||
@@ -225,7 +217,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.scene, [])
|
||||
var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp_overlay, [])
|
||||
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:
|
||||
@@ -243,7 +235,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.scene.visible or main.ui.modals.save_select_overlay.visible:
|
||||
if not main.ui.modals.camp_overlay.visible or main.ui.modals.save_select_overlay.visible:
|
||||
_problems.append("[savesel] sl6 落点未开营地层或选档未隐藏")
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
@@ -676,51 +668,6 @@ 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,11 +45,15 @@ 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 根底档)
|
||||
|
||||
+18
-28
@@ -25,44 +25,34 @@ 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 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
if not main.ui.modals.camp.scene.visible:
|
||||
problems.append("营地场景根未打开(进档开层新路径)〔句柄子容器批 V1〕")
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
problems.append("营地层未打开(进档开层新路径)")
|
||||
elif main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)〔句柄子容器批 V1〕")
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)")
|
||||
else:
|
||||
# 天赋树子界面(场景点选真实路由——木桩命中区进树界面;卡④ 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〕")
|
||||
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("树淬炼柜台购买钮缺失(四柜台重排后树=主界面)")
|
||||
else:
|
||||
var btn_by_id := {}
|
||||
for card2 in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
for card2 in main.ui.modals.camp_overlay._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 不含余额面语义)〔句柄子容器批 V1〕")
|
||||
problems.append("脊柱生命 L1 白板可购钮未启用(无门槛——can_buy 不含余额面语义)")
|
||||
if not btn_by_id.has("att_socket2") or not bool(btn_by_id["att_socket2"].disabled):
|
||||
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)
|
||||
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()
|
||||
await get_tree().process_frame
|
||||
if main.ui.modals.camp.scene.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地关闭后未恢复开始层(开合往返失守)〔句柄子容器批 V1〕")
|
||||
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("营地关闭后未恢复开始层(开合往返失守)")
|
||||
# 换回全新零档(营地段购买态不泄入后续冒险段——确定性;先删后建防陈旧档泄入)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", true, false, true)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支)
|
||||
|
||||
+17
-109
@@ -39,118 +39,36 @@ func _ready() -> void:
|
||||
_settle()
|
||||
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_struct_probe_menu"):
|
||||
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
|
||||
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
_settle()
|
||||
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")
|
||||
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()
|
||||
_settle()
|
||||
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)
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
_settle()
|
||||
|
||||
# ---- 生涯页:入口=帐篷旁笔记本(卡④ T3 改挂——场景点选信号真实路由→组合根 _open_career)→
|
||||
# ---- 生涯页:入口点击链(合成点击真路径=pressed.emit→组合根 _open_career)→
|
||||
# 结构断言族→只读巡检(契约 3)+零动效契约+零跳转契约(提纲 P4 甲)→组合根真实路径关闭。
|
||||
#(登记序/树序契约统一在全模态 _modal_registry_contract——boot 段跑)----
|
||||
# 营地语境:开始层全程隐藏(笔记本入口在营地场景根——关生涯回营地不回开始层,卡④ T3 语境分支)
|
||||
main._camp_pres.open_camp() # 重开营地场景根(笔记本入口语境)
|
||||
_settle()
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"notebook")
|
||||
# 生涯页驱动=函数直调中间态(卡② mn9/mn10——careerbtn 已撤,卡④ 营地笔记本改挂前无 UI 路径)
|
||||
main._open_career()
|
||||
_settle()
|
||||
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 生涯页未打开或开始层未隐藏(笔记本改挂路径断裂)〔营地图批 V5〕")
|
||||
problems.append("[career] 生涯页未打开或开始层未隐藏(函数直调中间态断裂)")
|
||||
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 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()
|
||||
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
|
||||
# ---- 选档界面(迭代 33 卡③):函数直调开层(「进入存档」钮归卡② mn3——点击链在卡② 承接),
|
||||
# sl1 三槽呈现/sl2 摘要三项逐槽读数/sl3 摘要禁项/sl4 操作集二分/sl9 重启链不预选。
|
||||
@@ -476,7 +394,7 @@ func _career_zero_motion_contract(main: Node) -> void:
|
||||
# ⑫模态登记/树序全量合同(V1——career 专属登记序契约推广到全部模态+屏壳;
|
||||
# 判据=main.ui.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。
|
||||
# 两特例按合同数据表达:level_screen 宿住冒险屏壳(树序随宿主=全模态之下,遮挡语义归模态栈
|
||||
# screen_state 登记)、camp_scene 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。
|
||||
# screen_state 登记)、camp_overlay 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。
|
||||
# _career_registry_contract 的断言(career=状态面板后/确认弹窗前、树序同步)由本全量合同覆盖)
|
||||
# ⑭选档界面契约(卡③ sl1/sl2/sl3/sl4/sl9——呈现面全部来自喂入数据,视图零自判)
|
||||
func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
@@ -598,7 +516,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_scene 登记位代言)
|
||||
# ④ 营地同槽特例:camp_screen 屏壳树序夹商店层与设置层之间(成员 camp_overlay 登记位代言)
|
||||
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〕" % [
|
||||
@@ -801,13 +719,3 @@ 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
|
||||
+2
-13
@@ -134,21 +134,11 @@ 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〕 |
|
||||
| 营地七物件命中区点选与分界面可达 | 营地场景视图 | 七物件命中区 → `CampSceneView._gui_input()`(命中发 `object_pressed` 信号) → 组合根开界面 | `ui_click_probe.gd:_ready`(七物件合成点击段)〔营地图批 V2〕 |
|
||||
| 营地大门两分支确认与断点续玩接线 | 营地交互流 | 大门命中区 → ConfirmDialog(三出口)→ `main._on_confirm_accepted()`(新冒险确认)/`main._resume_from_menu()`(断点续玩) | `ui_click_probe.gd:_ready`(大门两分支段/断点确认段 :500-528)〔营地图批 V3〕 |
|
||||
| 四柜台拆解后五界面入口改挂与写口零回归 | 营地五界面 | 五界面意图信号 → `CampShopPresenter` → `MetaProgression.tree_buy()` 写口 | `forge_workbench_test.gd:_run`(承载类同迁断言十二项)〔营地图批 V4〕;`ui_click_probe.gd:_ready`(写口链段) |
|
||||
| 营地篝火回主菜单与笔记本改挂动线 | 营地交互流 | 笔记本命中区 → `CareerOverlay.open()`;篝火命中区 → ConfirmDialog 二次确认 → 主菜单 | `ui_struct_probe.gd:_ready`(career 段)〔营地图批 V5〕;`screen_flow_probe.gd:_ready`(篝火/开合段)〔营地图批 V7〕 |
|
||||
| 营地图鉴占位挂起态与反参照结构纪律 | 营地场景视图 | 布告板命中区 → `CampSceneView._gui_input()`(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`(顶栏位结构段) |
|
||||
|
||||
**旅程段七:数据配置与跨系统契约**
|
||||
|
||||
@@ -163,7 +153,6 @@ 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/` 逐篇对照——状态三档:已覆盖/部分覆盖/未覆盖;部分与未覆盖行的缺口指待办清单条目,覆盖面提升时同批改本表):
|
||||
@@ -176,9 +165,9 @@ tests/
|
||||
| 冒险循环 | 奇物.md | 专长(奇物)授予与凭证保底;奖励三选一候选/刷新付费/呈现 | 已覆盖 | — |
|
||||
| 冒险循环 | 难度曲线与Boss.md | Boss 半血解锁技门控;行集寻址、行序保序与难度乘数 clamp;难度修饰套乘:空表零行为/套乘生效/越域 clamp | 部分覆盖 | 难度曲线整链与 Boss 出场门控无专项断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 地图战斗资源.md | 清图奖励双份与零路径;GP 钱包/escrow/商店收口/共享池;封印与极性路由/不可逆 | 已覆盖 | — |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;同名状态合并策略(叠加/累加/并存)与剩余次数语义;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗结算.md | 伤害公式与掷点乘区;伤害单据编排与回放;轮内流与结算五段全序 | 已覆盖 | — |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定;站桩显示档与素材分辨率绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 敌人系统.md | 技能池数据面与回放独立;Boss 半血解锁技门控;敌人技能池跨表链接校验 | 已覆盖 | — |
|
||||
| 排雷循环 | 地形变异.md | 变异布置门控;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
|
||||
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
|
||||
|
||||
@@ -192,13 +192,11 @@ 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 迁入)
|
||||
{"path": "res://tests/unit/battle_settlement/seal_polarity_test.gd", "expect": 0}, # 封印与极性宿主(末点 AP 封印/封印不可逆四情况/清图奖励双份与弱形式/刷新 provenance 守卫/极性路由与封印溢价/负向字典/格面资源资格——批 B 自 smoke_all 迁入)
|
||||
{"path": "res://tests/unit/battle_settlement/round_flow_skill_pool_test.gd", "expect": 0}, # 轮内流与技能池宿主(全战斗回放/轮内刷新与 V5 快照单源/遭遇区间骰/结算五段全序/技能池数据面·回放独立·EV 锚·死亡打断·校验拒绝/妨害附加/四桶计数——批 B 自 smoke_all 迁入)
|
||||
{"path": "res://tests/unit/battle_settlement/refresh_feats_test.gd", "expect": 0}, # 换图系专长七件宿主(迭代 46 卡A——基座产 RAP 与折价同源/成功事实恰一次/全局受伤四路径恰一次/石层缓冲/崩壁延迟/蓄力层/坑道小憩/矿道红利 GP/七件数据与牌库/无专长基线;原经卡片申报走 02 关联档选跑,卡片退役后补登记由 02 全套件承接)
|
||||
{"path": "res://tests/unit/battle_settlement/variant_placement_test.gd", "expect": 0}, # 变异布置宿主(战内布置段门控与数量公式/全局变异兜底回落与资源面偏斜——批 B 自 smoke_all 迁入)
|
||||
{"path": "res://tests/unit/battle_settlement/skill_unlock_test.gd", "expect": 0}, # Boss 半血解锁技·容器机制(门谓词/闩不可逆/双面过滤/保底不逼锁定/零扰动)
|
||||
{"path": "res://tests/unit/battle_settlement/extended_row_violation_test.gd", "expect": 1, "negative": true}, # 卡④ wr2-np 扩展行必载 effect_id 负路径(行协议运行期违约面首个负面套件——独立进程豁免错误扫描)
|
||||
|
||||
@@ -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_scene != null, "V2 营地屏壳独立实例化(camp_scene 在位)")
|
||||
check(not camp.camp_scene.visible, "V2 营地层默认隐藏(模态纪律)")
|
||||
check(camp.camp_overlay != null, "V2 营地屏壳独立实例化(camp_overlay 在位)")
|
||||
check(not camp.camp_overlay.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〕〔句柄子容器批 V2〕" % chain_detail)
|
||||
check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" % chain_detail)
|
||||
# ③ 探针挂载假设句柄在座(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.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.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.scene, main.ui.modals.settings_overlay,
|
||||
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, 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,29 +51,21 @@ 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()
|
||||
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)
|
||||
for L in [1, 2]:
|
||||
var b = board_generator.generate(L, 0, _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 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)],
|
||||
Kit.check("L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" % [L, b.width, b.height, grid_h, int(avail.y), int(cell)],
|
||||
grid_h <= int(avail.y) - 24)
|
||||
Kit.check("L%d D%d 格边在 26–46 档内(%dpx)〔格边长 V3〕" % [int(t["L"]), int(t["D"]), int(cell)],
|
||||
Kit.check("L%d 格边在 26–46 档内(%dpx)" % [L, 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,14 +228,16 @@ 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 缺口行留任随迁(不删行不补立绘——补齐归敌种内容批)
|
||||
# ② hobgoblin 缺口行留任随迁(契约 9:不删行不补立绘——缺口登记随行)
|
||||
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〕",
|
||||
Kit.check("hobgoblin 行在 (1,battle,3) 留任随迁(缺口行不删不补——补齐归敌种内容批)〔fv12〕〔fv25〕",
|
||||
int(hob.get("level_index", -1)) == 1 and String(hob.get("row_kind", "")) == "battle" \
|
||||
and int(hob.get("depth_band", -1)) == 3)
|
||||
Kit.check("hobgoblin 缺口登记随行(表文件注释在位——缺口不静默)〔fv25〕",
|
||||
FileAccess.get_file_as_string("res://data/tables/enemy.tres").contains("hobgoblin 行为缺口行"))
|
||||
# ③ Boss 行集带键 = 覆盖表(旧判据「各恰一行=图 Boss 节点深度」属单终点模板时代;
|
||||
# 生成图多终点要求每终点深度解析到互异带键 ⇒ 每关 Boss 行必然多于一行——
|
||||
# 迭代 35 覆盖批随动改写为带键清单,「带键互异」的运行时把关在 level_shop_economy_test〔v11〕)
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
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"))
|
||||
@@ -1 +0,0 @@
|
||||
uid://ckyd57l11408
|
||||
@@ -15,7 +15,7 @@ func _initialize() -> void:
|
||||
|
||||
func _row_by_id(ov) -> Dictionary:
|
||||
var out := {}
|
||||
for r in ov._forge_rows:
|
||||
for r in ov._forge._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/forge_view.gd").new()
|
||||
var ov = load("res://presentation/views/camp_overlay_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_workbench.visible and ov._forge_lock_l.visible)
|
||||
not ov._forge._forge_workbench.visible and ov._forge._forge_lock_l.visible)
|
||||
Kit.check("锁定态:两区零行板(占位纪律——零购买路径)〔锻造工坊批 V13〕",
|
||||
ov._forge_zone_weapon.get_child_count() == 0 and ov._forge_zone_armor.get_child_count() == 0)
|
||||
ov._forge._forge_zone_weapon.get_child_count() == 0 and ov._forge._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_workbench.visible)
|
||||
Kit.check("解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕", ov._forge._forge_workbench.visible)
|
||||
Kit.check("两区各三路线(武器锻造区 3 + 护甲锻造区 3)〔锻造工坊批 V13〕",
|
||||
ov._forge_zone_weapon.get_child_count() == 3 and ov._forge_zone_armor.get_child_count() == 3)
|
||||
ov._forge._forge_zone_weapon.get_child_count() == 3 and ov._forge._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,6 +52,7 @@ func _init() -> void:
|
||||
_smoke_diag_button_retired()
|
||||
_status_panel_projection()
|
||||
_career_overlay_projection()
|
||||
_camp_recent_projection()
|
||||
quit(Kit.finish("theme_projection_test"))
|
||||
|
||||
|
||||
@@ -707,6 +708,12 @@ 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)----
|
||||
@@ -789,6 +796,20 @@ func _all_labels(root: Node) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# ---- 营地最近冒险行投影(口径换代 V6:完成关数措辞钉子——open_camp 全量重建路径直调;
|
||||
# 最小档 data=recent 单条 completed 局,其余键缺省走 .get 兜底——与组合根 _camp_data 投影同键面)----
|
||||
func _camp_recent_projection() -> void:
|
||||
print("--- 营地·最近冒险行投影(open_camp 最小档)---")
|
||||
var camp = load("res://presentation/views/camp_overlay_view.gd").new()
|
||||
camp.open_camp({"balance": 0, "recent": [
|
||||
{"result": "completed", "levels_cleared": 2, "keystone_total": 5, "master_seed": 42}]})
|
||||
var recent_text: String = String(camp._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,25 @@
|
||||
# 迭代 46 GDD 反哺点清单(2026-10-31)
|
||||
|
||||
> 接收方:待办清单「反哺点专条」通道——用户会话逐条裁决后由文档批写入 `design/`,写入后本清单条目清掉(吸收即死)。
|
||||
> 依据:迭代 46 实现报告(同目录)与实测评读数。实现批禁写 `design/`,以下为待裁决落文面。
|
||||
|
||||
## 卡A 换图系双基座七件批
|
||||
|
||||
1. **[奇物.md]** 基础件表(或专长实装状态句):七件(矿脉储能/省力周转/崩壁冲击/蓄力周转/石层缓冲/坑道小憩/矿道红利)由设计声明改为**已实装**(feats.tres 37 行、稀有度 14/13/10/0、cap 均 1);「双基座叠加、后续效果本轮五段结算内兑付」已在案零改述。依据=实现报告 §二 V2–V11。
|
||||
2. **[行动经济.md]** 两件基座由「待实现」改为在册:付费挖格 +1 RAP(受理后一次性入池、拒绝轮零入池)、刷新成本折价 50%(floor 取整、can 与执行同源、只改门槛与扣费不改拒绝判据)。依据=V2/V3。
|
||||
3. **[战斗结算.md]** 全局 `damage_taken` 落码状态补记:敌方攻击/地形/危险格/错误封印资源直伤四路径统一经唯一发射口与唯一消费族;**地形路径消费面自三件扩为五件(+shield)——铁壁自此对地形伤害生效;危险格/错误封印直伤路径自此纳入 damage_taken 订阅面**(用户已裁全局语义,实现报告 §六);成功刷新事实 `board_refreshed` 为七件订阅主挂点(装新板后恰一次发射)。依据=V5/V4。
|
||||
4. **[地图战斗资源.md]** 清图奖励语义确认句:矿道红利不绑清图奖励(每次成功刷新固定 +1 GP 走战斗内缓冲,不读旧图探索进度、不触发清图事件;刷新图零清图奖励守卫不变)——若现行正文已含此意则零改(本行核对性反哺)。依据=V10/V12。
|
||||
5. **[architecture.md]** 「模块归属」增补/现状化三行:①**BattleRefreshFlow**(新 B1 域拆件——刷新可用性门/折价扣费/换图执行/成功事实登记,调用路径呈现层→编排器单行转发→流程件→行动经济/装板壳/授予段;static_scan S7_B1_SET 已随批增补该文件,判据口径不变);②**damage_controller** 行现状化(damage_taken 唯一发射口 `_emit_hero_damage_taken`+唯一消费族 `_apply_hero_damage_effects`(原名 _enemy_shield_apply 改名)+board_flat_hero 结账前统一发射);③**action_economy** 行现状化(dig_paid 命中施加抽私有 `_apply_dig_paid_hits` 单点;request_refresh 收折后价入参)+**grant_segment** 行现状化(apply_board_refreshed 成功事实消费+round_end heal value_counter 兑付面)。依据=实现报告 §五/§八.6。
|
||||
|
||||
## 卡B 站桩名字行提档与地台色合并批
|
||||
|
||||
6. **[art-bible.md §4.1]** 换色合并登记①②句的代码指称换代:①「代码 `STAND_STONE_RIM`」②「代码 `STAND_STONE_EDGE`」两源常量已删除(全仓零引用——唯一残留=退役断言测试串,预期例外);②合并目标 `#4C4038` 补代码名 **`PLAZA_EDGE`**(ui_kit 唯一落点)。依据=卡B 对照表 3/4 行+grep 取证(实现报告 §三)。
|
||||
7. **[art-bible.md §8.2]** 高墙瓦片体系条口沿高光行:「Color8(196,172,132)」值随换值换代为 **`#C9AF83`(201,175,131)**(并档掩尘枚 BRICK 单点);程序化回退顶缘线与口沿高光两绘制点已改指。依据=对照表 3 行。
|
||||
|
||||
## 卡C 棋盘格边长口径收口批(停摆——随裁决落文)
|
||||
|
||||
8. **[battle.md §棋盘格尺寸公式]** 算例表(:148-152)与实机偏差实测落点:1280 档实板可用面积 **(1022, 454)**(算例假设 486 可用高)→ 实测 L1 格边 40px、13×13 推定 30px(恰达标)、**14×14 推定 28px(低于可读下限 30)**;1080 档 (1662, 734) → L1=46、13/14 推定=46(钳制顶格)。落文方向随「30px 下限适用域」裁决(读法甲/乙见停机呈报 §三):读法乙需补「下限按视口档分级」条+算例表填实测值或标注「按旧布局口径」。依据=停机呈报 §一/§四。
|
||||
|
||||
## 术语表
|
||||
|
||||
9. 零新增术语——本批新名(BattleRefreshFlow/_cell_for/PLAZA_EDGE/counter_id 三枚)均属代码标识符与数据行键,不入术语表(登记口径=概念规范名)。
|
||||
@@ -0,0 +1,109 @@
|
||||
# 迭代 46 实现报告(2026-10-31)
|
||||
|
||||
> 批次:迭代 46 三卡——卡A 换图系双基座七件批 · 卡B 站桩名字行提档与地台色合并批 · 卡C 棋盘格边长口径收口批。
|
||||
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,自 b5b2c555 检出)。
|
||||
> **批内状态总览:卡A 收口 · 卡B 收口 · 卡C 停摆(L3 #2 预设停机点,呈报见主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`)**。合并阻塞等裁决。
|
||||
|
||||
## 一、各卡状态
|
||||
|
||||
| 卡 | 状态 | 出口证据 |
|
||||
|---|---|---|
|
||||
| 卡A 换图系双基座七件批 | **收口**(T0–T8 全落) | `tools/run_all_gates.sh --code` 7 闸全绿(回执 commit 45b53d68 时点);此后 S9/S7 修正与卡B 提交待迭代级复跑 |
|
||||
| 卡B 站桩名字行提档与地台色合并批 | **收口**(T0–T3 全落) | standee_present_values_test 26 checks 绿;golden 板面三张 0.00% 留痕;gdstyle 零新增键 |
|
||||
| 卡C 棋盘格边长口径收口批 | **停摆**(T0 完成;T1–T3 改动已验未提交;T4 未走) | 卡C §9 L3 #2:实板可用面积代入 14×14 得 28px < 30px(1280 档)——停卡呈报等裁决 |
|
||||
|
||||
## 二、卡A 实际对照表(改动—验收)
|
||||
|
||||
| 需求点 | 改动(文件:行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 结果 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 词表扩签 | effect_engine.gd EVENTS 20→22/MINE_EVENTS 同步换图刷新面/EFFECT_KINDS 32→34;forge_table/effect_words 计数换代 | `effect_words_t1_test.gd:_init`(新增①-a 两断言);`forge_table_test.gd:_test_effect_kinds_closed` | 单元 | validate 与整表加载 | EVENTS=22、EFFECT_KINDS=34、出域仍拒(旧负例全保留) | 绿(20/41 checks) |
|
||||
| V2 矿脉储能 | action_economy.gd dig_paid 命中环累计 grant_reserve_ap(扣费受理后一次性入池·拒绝轮零入池);feats.tres vein_reserve 行 | `refresh_feats_test.gd:test_reserve_on_accepted_dig` | 接口 | `settle_batch(dig)`(AutoPlayer 真实挖掘) | RAP 恰+1(8→9)、AP 按表扣(8→7)、耗尽轮增量==受理挖数 | 绿 |
|
||||
| V3 省力周转 | BattleRefreshFlow `_effective_refresh_cost`(声明序第一命中 floor(base×mult/100),can 与执行同源);request_refresh 收折后价入参;easy_swap 行 | `test_refresh_discount_gate_and_charge`;`test_double_base_stack` | 接口 | `player_request_refresh()` | base 8→扣 4;两次折后扣尽 8→0;门槛拒零受理;双基座同场生效 | 绿 |
|
||||
| V4 成功事实 | battle_refresh_flow execute 成功序(折价扣RAP→used+1→旧资源退役→旧板退役→装新板→apply_board_refreshed);编排器两公开方法单行转发 | `test_board_refresh_fact_only_on_success` | 集成 | `player_request_refresh()` | 非 Active/每轮上限/RAP 不足三拒面零事实零换图(probe_br_counter 计数+board_changed 双观测);成功恰一次 | 绿 |
|
||||
| V5 全局受伤 | damage_controller `_emit_hero_damage_taken` 唯一发射口+`_apply_hero_damage_effects` 唯一消费族(改名自 _enemy_shield_apply,组合次序冻结);敌方攻击/棋盘直伤/地形三路径接入;grant_segment 删本地 _terrain_damage_reduce | `test_all_hero_damage_paths_emit_once`;`test_no_duplicate_damage_taken_event` | 集成 | 敌方攻击、dig danger、seal wrong、terrain 四真实入口 | 每路径承伤单据在案且计数器==行数(逐单恰一次);context 标记闸防地形二次发射 | 绿 |
|
||||
| V6 石层缓冲 | round_swap_guard 计数+damage_taken when≥1 damage_reduce 2(round 桶轮始自清) | `test_stone_mitigation_all_damage` | 集成 | 刷新→危险格(pierce 8)/地形两真实受伤路径 | pierce 8→6(双刷不叠加)、未刷新轮不减(8)、地形 max(下限, effect_value−2) 逐单闭合 | 绿 |
|
||||
| V7 崩壁冲击 | apply_board_refreshed open_ticket blast→reward_grant.defer_ticket(案C 同队列);SOURCE_EXEMPT_CLASSES+fx_board_refresh;wall_blast 行 | `test_blast_defers_to_combat_segment` | 集成 | 刷新→停手 | 刷新时点零伤害行;战斗段末恰一单 ×2 全体存活敌 | 绿 |
|
||||
| V8 蓄力周转 | swap_charge_layers 战斗桶+attack_bonus value_counter+set0(既有消费面零新码) | `test_charge_layers_consumed_same_round` | 接口 | 两次刷新→停手(主角先手攻击) | 双刷 2 层→攻击后 0;白盒预置 3 层时刷新被闸(上限 3);战斗新建 0 | 绿 |
|
||||
| V9 坑道小憩 | round_swap_rest+round_end heal value_counter(grant_segment 新增 `_round_end_heal_value_counter`——先于 set0 行消费层值) | `test_rest_rotation_cap_and_clear` | 集成 | 刷新→停手(轮末授予段) | 回复 min(3,缺血)==3、层清 0、满血发未生效行、自然累计 2 路径 | 绿 |
|
||||
| V10 矿道红利 | board_refreshed grant_gp→escrow+fx_grant_gp 行(apply_board_refreshed 消费);mine_profit 行 | `test_mine_profit_gp` | 集成 | `player_request_refresh()` | escrow 每成功+1(0→1→2)、fx_grant_gp 行在案、拒绝零变化 | 绿 |
|
||||
| V11 数据与池 | feats.tres 七行(rarity 14/13/10/0·cap 1);adventure_feats 30→37/牌库 132→139 | `test_seven_feats_validate_and_pool`;adventure_feats `_smoke_feats` | 单元 | ConfigDB 整表与冒险牌库 | rows 37、relic 37、14/13/10、deck 139、validate 空、禁词 0、七件计数器引用闭合 | 绿(85/138 checks) |
|
||||
| V12 无专长回归 | round_flow/seal 旧基线零改动(刷新体迁移编排器单行转发) | `_smoke_active_refresh`;`_smoke_refresh_provenance_guard`;`test_no_feats_baseline` | 单元+集成 | 现行刷新/清图入口 | RAP8、AP 保留、上限 2、刷新图零清图、挖格零产 RAP、表基线扣费 | 绿(134/70 checks) |
|
||||
|
||||
**窄测实跑记录**:refresh_feats 85 · adventure_feats 138 · round_flow 134 · seal_polarity 70 · damage_controller 44 · effect_words 20 · forge_table 41 · config_db 29——全绿。批末 `--code` 闸 00/01/02/03/05/09/10 全绿(回执 45b53d68)。
|
||||
|
||||
## 三、卡B 实际对照表(改动—验收)
|
||||
|
||||
| # | 需求点 | 改动 | 验收测试 | 结果 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 名字行字号提档 FS_HINT 14 | standee_body.gd 五取值点+name_line_probe 增 name_font_size | 〔名字行 V10〕探针实读==FS_HINT+两绘制点源码锚+FS_CAPTION 计数锚==1 | 绿 |
|
||||
| 2 | 名字行几何随档联动 | _name_baseline 偏移 17→18+注释换代 | V6 换代读数 150±0.51(8px 档)且 < plinth_top_y | 绿 |
|
||||
| 3 | 受光线常量退役改指 BRICK | ui_kit 删 STAND_STONE_RIM;standee_body:386+cell_base_layer:176 改指 | 〔名字行 V11〕常量表零键+BRICK==#C9AF83+两绘制点源码锚;grep 反证(仅剩测试断言串——预期例外) | 绿 |
|
||||
| 4 | 描边常量退役改指新 token | ui_kit 删 STAND_STONE_EDGE+增 PLAZA_EDGE=Color8(76,64,56);standee_body:385 改指 | 〔名字行 V12〕PLAZA_EDGE 逐位+常量表零旧键+描边源码锚 | 绿 |
|
||||
| 5 | 板面 golden 与换值一致 | (预期重锚——实际无需) | render_gate 板面三张 0.00%(改后跑,基线未动) | 绿(见实现决定 #4) |
|
||||
| 6 | 批末收口 | 全批 | 迭代级 --code 闸(待卡C 裁决后统一走) | 待迭代出口 |
|
||||
|
||||
卡B 改前读数(T0):standee_present_values_test 18 checks 绿 · gdstyle 5 键全基线内 · 渲染闸绿(--code 回执 45b53d68 内 05 闸)· 字体度量 ts(14px)=(14,20)。
|
||||
|
||||
## 四、卡C 停摆实况(改动已验未提交)
|
||||
|
||||
- T0 完成:行号重锚(board_view set_cell_area 现 :206-224、layout_probe MIN_CELL :15、冒烟 :53-69);取档对账 resolve_row(1,0)/(2,0)/(3,0)/(3,9)=10/12/13/14 全符;改前读数 board_gen_reward_test 62 绿+layout_probe 两档绿+gdstyle board_view max-public-methods 27 持平。
|
||||
- T1 算式单点:`BoardView._cell_for(n, avail)` 提取(逐位同式)+set_cell_area 改调——等价性由 board_gen_reward_test 既有断言承载(62 checks 绿)+gdstyle 持平。
|
||||
- T2 探针:MIN_CELL 36→30+注释改指 battle.md+BoardViewScript preload+零尺寸前置+13/14 推定断言——**推定断言触发 L3 #2(14×14=28<30)**。
|
||||
- T3 冒烟四档:取样表 [{1,0,10},{2,0,10→12},{3,0,13},{3,9,14}]+板型一致断言+算式单点等价断言——74 checks 绿(单元面全过:钳制 [26,46] 面无越界)。
|
||||
- 三件改动**未提交**(停摆卡不提交),随裁决处置(可复用——行为零变化)。
|
||||
- 停机呈报:主区 `reports/停机呈报-棋盘格边长口径收口批-2026-10-31.md`(分歧读法/建议案/待办回退拟稿)。
|
||||
|
||||
## 五、结构预算(S13 前后)
|
||||
|
||||
| 触面 | 前测 | 后测 | 判定 |
|
||||
|---|---|---|---|
|
||||
| battle_orchestrator.gd | 811 行/类变量 15(卡A 写 38 系笔误) | 803 行(−8 达标)/类变量 15 | 只降不升 ✓ |
|
||||
| reward_grant.gd | 类变量 15 | 15(defer_ticket 为方法零增员) | ✓ |
|
||||
| grant_segment.gd | 类变量 3/公开方法 1 | 3/2(apply_board_refreshed);私有新增 1(_round_end_heal_value_counter)≤4 | ✓ |
|
||||
| damage_controller.gd | 公开方法 9 | 9(私有净增 1:_emit_hero_damage_taken)≤2 | ✓ |
|
||||
| action_economy.gd | 公开方法 15 | 15(命中环抽私有 _apply_dig_paid_hits——S13 分支超限重构,分支本体逐字迁入) | ✓ |
|
||||
| battle_refresh_flow.gd(新件) | — | 公开函数 2(can_refresh/execute)/类变量 0 | ≤20/≤15 ✓ |
|
||||
| gdstyle_gate | 5 键全基线内 | 5 键全基线内零新增零恶化 | ✓ |
|
||||
|
||||
## 六、旧 damage_taken 专长覆盖面变化
|
||||
|
||||
全局统一后,订阅 `damage_taken` 的既有专长(背水 hp_ratio when/石肤 ac_bonus+counter_add/保险丝 counter_add)与换图系石层缓冲的覆盖面变化:
|
||||
|
||||
- **敌方攻击路径**:不变(原即经 _enemy_shield_apply 全组合消费)。
|
||||
- **地形路径**:消费族从本地三件(damage_reduce/ac_bonus/counter_add)扩为全局五件(+shield)——**铁壁(shield)自此对地形伤害生效**(用户已裁全局语义;卡A §十二削弱申报第 2 条在案)。
|
||||
- **危险格/错误封印资源直伤路径(board_flat_hero)**:原零事件,现统一发射——上述专长自此对这些路径生效(同上申报面)。
|
||||
- 无专长基线逐位零漂移(V12 全绿;同一伤害恰一次由 V5 钉死)。
|
||||
|
||||
## 七、实机目检总清单(登记不阻塞)
|
||||
|
||||
1. 〔卡A〕玩家持基座与收益件后,是否主动规划刷新而非只在坏图刷新(行为观感)。
|
||||
2. 〔卡B〕站桩名字行 14px 清晰可读且长名不截字(验收底线句)。
|
||||
3. 〔卡B〕墙格口沿亮线换值后观感无异(#C4AC84→#C9AF83)。
|
||||
4. 〔卡B〕贴图缺位回退描边与顶缘线观感无异(PLAZA_EDGE/BRICK 程序化回退)。
|
||||
5. 〔卡C·停摆批登记〕第三关 14×14 板格面数字在基座档的可辨读性(28px 实况——随裁决)。
|
||||
|
||||
## 八、实现决定记录(超白名单/偏差备案)
|
||||
|
||||
1. **卡A 自洽扫描笔误三处(不改合同)**:「类变量 38」实为 15;gdstyle_baseline.txt:9 引用实无 battle_orchestrator 键(该文件现无 S13 键);稀有度断言实挂 :476 非 :465。T0 按实数重锚记录,合同(只降不升/换代判据)不受影响。
|
||||
2. **卡A T0 工具名**:卡写 `bash tools/gdstyle.sh game`——该文件不存在,实际工具 `python tools/gdstyle_gate.py`(与卡B/C 一致),按实跑。
|
||||
3. **MINE_EVENTS 同步口径**:卡A T1「同步 :19 的 MINE_EVENTS」取「两新事件入集」读法——弃图刷新属扫雷侧动作面,流派铁律订阅集随之认列(零机械消费面差异,mine_covered 生产无强制点)。
|
||||
4. **卡B golden 预期红未现**:卡预期「板面三张红→重锚」,实测三张 0.00%——口沿高光换值 #C4AC84→#C9AF83 每通道差 ≤5,低于逐像素容差(≤16 差/≤2%),golden 判定零变化;重锚步骤无需执行(基线未动、零差留痕达成 V5 判据;禁借重锚夹带红线零触碰)。
|
||||
5. **S9 修正**:四处显式非参数化容器注解改 `:=` 推断(R1)——零行为改动。
|
||||
6. **S7 域集增补**:`static_scan.sh` S7_B1_SET 增补 battle_refresh_flow.gd——换图刷新流程件=卡A C13 拆分边界的 B1 域拆件(承载 retire_mapping/装板编排调用,卡A §八明列新结算族件);判据口径不变(B2/B3/B4 对外 API 仍限 B1 域调用)。**architecture.md「模块归属」需补 BattleRefreshFlow 行——已入反哺点清单**。
|
||||
7. **V11 引用闭合判据域**:全表扫描发现预存行完美保险 when 读 insurance_used 无 counter_add 写入方(消费点落账合法形态——feats.tres 注记在案)——判据域收窄为「换图系七件自身引用闭合」,预存形态不入判据。
|
||||
8. **apply_board_refreshed 访问形态**:BattleRefreshFlow 经 `oc._rounds._grant_seg` 直调(round_cycle 不在卡A §八改动文件清单内,不加转发壳——沿结算族直调惯例)。
|
||||
9. **卡A T8「代拍板追认单」不另立**:iter 技能与 process-conventions §2 定「超白名单记实现报告实现决定记录,不另立追认单」——本批全部决定在上列备案,白名单内决定零追认面。
|
||||
10. **测试断言口径修正(开发期自纠)**:V5②③④ 聚合判据从「两来源行合计」修正为「全 hero 行数」(盲挖途经的其他直伤单据一并入账);V2 辅证改「RAP 增量==受理挖数」(移位耗 AP 不入账);V4 时序修正(停手再入 RAP)——均为测试侧修正,生产码零回退。
|
||||
11. **V9 上限 3 承载形态**:每轮刷新上限 2(表值)使自然路径层值至多 2——「上限 3」判据由白盒计数器预置+真实刷新入口承载(test-conventions §5.4 计数器直改属数据面注入),自然累计 2 另设战场钉死。
|
||||
|
||||
## 九、独立验收审计结论
|
||||
|
||||
- **审计方**:未参与实现的子代理(独立会话上下文),2026-10-31 执行。
|
||||
- **报告**:`reports/iter46-验收审计-2026-10-31.md`(本目录)。
|
||||
- **结论**:**审计通过(零缺漏)**——卡A V1–V12 与卡B 6 行对照表逐行通过;尾缀全可 grep;真实入口零绕过(V2 真挖/V3·V4·V10 经 player_request_refresh/V5 四路径真实战斗驱动);自跑复核 refresh_feats 85/standee 26/adventure_feats 138/round_flow 134/seal_polarity 70/effect_words 20/forge_table 41 全绿+gdstyle 5 键零新增+render_gate 三张 0.00% 实跑;削弱核对全部落在两卡申报面内(round_flow/seal 零进 diff,禁删面原样在案);无变异/撤销类取证。
|
||||
- **审计附加发现处置**:①两枚新件 .uid 边车未随批提交——已补提交(.gitignore 要求);②static_scan S8_FILES 与 S7_B1_SET 未同步增补新件——已同步(新件零 await,扫描面实跑绿);③V10 断言消息在求值前格式化的显示瑕疵(判据本体正确,零处置);④V9 私有读前置守卫附注(审计方判例外不阻塞——判据主面走公共 step_row 信号)。
|
||||
|
||||
## 十、遗留读数义务
|
||||
|
||||
- 卡C 裁决后:探针两档 bv.size/L1 格边/13·14 推定读数已录本报告第四节与停机呈报——重写卡可直接引用,无需重采。
|
||||
- 无 MC/数值验证义务(本批无数值改动,feats.tres 七行为卡内 provisional 值——归调参批)。
|
||||
@@ -0,0 +1,130 @@
|
||||
# 迭代 46 独立验收审计报告
|
||||
|
||||
> 审计方:独立验收审计子代理(未参与本批实现——迭代 loop 内启动,符合 test-conventions §8.7 两入口铁规)。
|
||||
> 日期:2026-10-31。
|
||||
> 工作区:`D:\code\g-plan2-iter46`(分支 iter46,基点 b5b2c555)。Godot:`Godot_v4.7.2-stable_win64_console.exe`。
|
||||
> 审计范围:合并候选=卡A(换图系双基座七件批)+卡B(站桩名字行提档与地台色合并批)已提交 diff=`git diff b5b2c555..c10bbc11`(10 笔提交=9 实现+1 收尾文档;20 文件,+1231/−96)。卡C(棋盘格边长)停摆不计。
|
||||
> 审计方式:不采信实现报告「已全绿」自述——逐行核对对照表两端、自跑最窄套件与闸件、对照 diff 逐处核对削弱申报。本审计只写本报告,零源码/测试/数据文件改动。
|
||||
|
||||
## 一、审计自跑记录(当轮真实执行)
|
||||
|
||||
| 套件/闸 | 结果 | 与报告申报对照 |
|
||||
|---|---|---|
|
||||
| refresh_feats_test | **all green (85 checks)**,exit 0 | 与报告§二 申报 85 一致 |
|
||||
| standee_present_values_test | **all green (26 checks)**,exit 0 | 与报告§三 申报 26 一致 |
|
||||
| adventure_feats_test | **all green (138 checks)**,exit 0 | 与报告§二 申报 138 一致 |
|
||||
| round_flow_skill_pool_test | **all green (134 checks)**,exit 0 | 与报告§二 申报 134 一致 |
|
||||
| seal_polarity_test(附加) | all green (70 checks),exit 0 | 与报告§二 申报 70 一致 |
|
||||
| effect_words_t1_test(附加,V1) | all green (20 checks),exit 0 | 与报告§二 申报 20 一致 |
|
||||
| forge_table_test(附加,V1) | all green (41 checks),exit 0 | 与报告§二 申报 41 一致 |
|
||||
| gdstyle_gate | 5 键全基线内、基线外新违规 0、恶化 0 | 与报告§五「零新增零恶化」一致 |
|
||||
| static_scan.sh | exit 0——S7 ok(B2/B3/B4 符号收敛 B1 文件集)/S8 ok/S13 ok | S7 新域集生效见§六① |
|
||||
| render_gate.sh(窗口化实跑) | 板面三张+menu_boot 全 **changed=0.00%**,全绿 | 与报告§八.4 一致,见§三行 5 |
|
||||
|
||||
## 二、卡A 对照表逐行核对(V1–V12)
|
||||
|
||||
| 需求点 | 测试存在 | 尾缀可 grep | 真实入口无绕过 | 判定 |
|
||||
|---|---|---|---|---|
|
||||
| V1 词表扩签 | `effect_words_t1_test.gd:_init` 新增①-a 两断言+`forge_table_test.gd:_test_effect_kinds_closed` 22/34 换代 | 〔换图系批 V1〕×2+〔锻造工坊批 V11〕×2 | ConfigDB 整表加载+词表常量直读 | **通过**(实跑 20/41 checks 绿;diff 核对 effect_words 纯增量 5 行,旧出域负例零删) |
|
||||
| V2 矿脉储能 | `test_reserve_on_accepted_dig` | 〔换图系批 V2〕×3 | 经 `_auto_dig`→`settle_batch([move]+[dig])` 真实付费挖格;辅证 `_drain_round_rap_accounting` 仍走 settle_batch 逐挖计数 | **通过**(RAP 8→9、AP 按表扣、耗尽轮增量==受理挖数三面全钉) |
|
||||
| V3 省力周转 | `test_refresh_discount_gate_and_charge`+`test_double_base_stack` | 〔换图系批 V3〕×6 | 经 `orch.player_request_refresh()` 唯一入口;can 与执行同源(`_effective_refresh_cost` 单点,diff 核实编排器两公开方法单行转发) | **通过**(折后 −4×2、门槛拒 can=false 且执行拒、双基座同场生效) |
|
||||
| V4 成功事实 | `test_board_refresh_fact_only_on_success` | 〔换图系批 V4〕×6 | 经 `player_request_refresh()`;非 Active 拒=decay 行内同步探测(round_flow_skill_pool_test:161 先例同款),上限拒/RAP 不足拒均在 Active 相位真入口 | **通过**(三拒面 probe_br_counter==0+board_changed 计数冻结双观测;成功恰一次×2+新轮×2) |
|
||||
| V5 全局受伤 | `test_all_hero_damage_paths_emit_once`+`test_no_duplicate_damage_taken_event` | 〔换图系批 V5〕×8 | **四路径全真实战斗驱动、零直调 `_emit`**:①敌方攻击=boss 战零挖掘连停三轮(enemy_action 行在案);②地形=AutoPlayer 盲挖 flat_damage 变异格+stop;③危险格=真挖直接伤型雷格;④错误封印=真 `seal_place`+stop | **通过**(每路径「承伤行数==计数器」恰一次+无重复消费两样本;实跑读数 行2=计2/行9=计9/行2=计2/行2=计2) |
|
||||
| V6 石层缓冲 | `test_stone_mitigation_all_damage` | 〔换图系批 V6〕×3 | 刷新经 `player_request_refresh`;危险格/地形经真挖+stop 结算 | **通过**(pierce 8→6 双刷不叠=叠加态 4 反证钉死;未刷轮不减=round 桶轮始清空;地形 max(下限, effect_value−2) 逐单闭合) |
|
||||
| V7 崩壁冲击 | `test_blast_defers_to_combat_segment` | 〔换图系批 V7〕×2 | 刷新+停手(轮末授予段→战斗段末) | **通过**(刷新时点零伤害行=授予段不结账;停手后恰一单 value 2 全体存活敌) |
|
||||
| V8 蓄力周转 | `test_charge_layers_consumed_same_round` | 〔换图系批 V8〕×2 | 双刷+停手(主角先手攻击)真实消费 | **通过**(白盒预置 3 层上限闸=实现决定 #11 申报面;双刷 2 层攻击后清 0;战斗新建 0) |
|
||||
| V9 坑道小憩 | `test_rest_rotation_cap_and_clear` | 〔换图系批 V9〕×3 | 刷新经真入口;轮末恢复在授予段(stop 触发) | **通过(附未注例外,见§五)**(回复 min(3,缺血)==3 且层清 0;满血未生效行+零 heal 行;自然累计 2 路径) |
|
||||
| V10 矿道红利 | `test_mine_profit_gp` | 〔换图系批 V10〕×4 | 经 `player_request_refresh()`;拒绝=RAP 不足真拒 | **通过**(escrow 每次+1、fx_grant_gp 行恰 1、拒绝零变化;消息文案时点瑕疵见§七.3,不涉判据) |
|
||||
| V11 数据与池 | `test_seven_feats_validate_and_pool`+adventure_feats `_smoke_feats` | 〔换图系批 V11〕×5+〔专长批 V2〕换代句 | ConfigDB 整表+冒险牌库真实启动(seed 71) | **通过**(validate 空/七行稀有度 cap 对表/desc 禁词/七件计数器引用闭合/三计数器在册;rows 37、14/13/10/0、deck 139;feats.tres 七行 trigger 结构逐行对照卡§十.T6 零出入) |
|
||||
| V12 无专长回归 | `test_no_feats_baseline`+round_flow `_smoke_active_refresh`+seal `_smoke_refresh_provenance_guard` | 〔换图系批 V12〕×4 | 挖格/刷新/停手全真入口 | **通过**(挖格零 RAP、表基线扣 8、AP 保留、第 3 次拒;round_flow/seal 两文件**零进 diff**——RAP8/上限2/AP保留/刷新图零清图/五段序/回放断言原样在案,134/70 checks 实跑绿) |
|
||||
|
||||
## 三、卡B 对照表逐行核对(6 行)
|
||||
|
||||
| # | 需求点 | 判定 |
|
||||
|---|---|---|
|
||||
| 1 | 名字行字号提档 FS_HINT 14 | **通过**——diff 核对五取值点(`:334-336` 两测量+`:467` 测量+`:474` 护甲绘制+`:476` 名字绘制)全改 `UiKit.FS_HINT`;`:114` 消散提示零触碰(FS_CAPTION 计数锚==1 断言在案且实跑绿);`name_line_probe` 增 `name_font_size` 键(探针实读==FS_HINT 绿) |
|
||||
| 2 | 名字行几何随档联动 | **通过**——`_name_baseline` 偏移 17→18+注释换代(diff 核实);V6 读数 150±0.51 与「8px 档」句换代=卡B§8 申报第 1 条面内;`baseline_y < plinth_top_y` 文字不压地台判据零改动且绿 |
|
||||
| 3 | 受光线常量退役改指 BRICK | **通过**——ui_kit 真删 `STAND_STONE_RIM` 无别名占位;`standee_body` 顶缘线与 `cell_base_layer:176` 口沿高光两绘制点改指 `BRICK`;V11=常量表零键+BRICK 逐位 c9af83+两处源码锚全绿;grep 全仓退役常量仅剩测试断言串(与报告§三「预期例外」申报一致) |
|
||||
| 4 | 描边常量退役改指新 token | **通过**——ui_kit 删 `STAND_STONE_EDGE`+原位增 `PLAZA_EDGE := Color8(76, 64, 56)`;`standee_body` 回退描边改指;V12=PLAZA_EDGE 逐位 4c4038+源码锚全绿 |
|
||||
| 5 | 板面 golden 与换值一致 | **通过**——审计方自跑 `bash tools/render_gate.sh`(窗口化):board_L1/L2/L3 与 menu_boot 全 0.00%;golden 五文件 git 零改动=基线未动、重锚未执行(与实现决定 #4 备案一致——#C4AC84→#C9AF83 灰度差约 2,低于容差 ≤16,0.00% 为必然);level_map.png 不在 render_gate 对比域、git 零改动 |
|
||||
| 6 | 批末收口 | **通过(遗留义务明示)**——迭代级 `--code` 闸未跑(卡B 行 6 申报「待迭代出口」);批内证据=commit 点 docs 域回执(fde1795d)全绿含 08c 流水线闸+审计方补跑 gdstyle/static_scan/render_gate/五套件全绿。合回后本地 CI 全量域闸为唯一强制验证点(process-conventions §1),本行遗留义务随合回 CI 承接 |
|
||||
|
||||
## 四、抽查断言原样输出行(当轮实跑摘录)
|
||||
|
||||
```
|
||||
[refresh_feats_test] all green (85 checks)
|
||||
[PASS] V5① 敌方攻击路径:承伤单据在案且逐单恰一次(行 2=计数 2)〔换图系批 V5〕
|
||||
[PASS] V5② 地形路径:地形单据在案且全单据逐张恰一次(行 9=计数 9——盲挖途经单据一并计入)〔换图系批 V5〕
|
||||
[PASS] V5③ 危险格路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
|
||||
[PASS] V5④ 错误封印路径:直伤单据在案且全单据逐张恰一次(行 2=计数 2)〔换图系批 V5〕
|
||||
[PASS] V2 受理付费挖格 RAP 恰 +1(8→9)〔换图系批 V2〕
|
||||
[PASS] V3 折后单次扣费(8→4,−4)〔换图系批 V3〕
|
||||
[PASS] V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕
|
||||
[PASS] V6 S1 危险格减伤恰 2 且多刷不叠加(行值 [6]∈[6]——叠加态应为 4)〔换图系批 V6〕
|
||||
[PASS] V9 R1 轮末回复=min(3,缺血102) 且层清 0(heal 行值 [3])〔换图系批 V9〕
|
||||
[PASS] V10 第 1 次成功 escrow +1(0→0)〔换图系批 V10〕
|
||||
[PASS] V12 刷新受理并按表基线扣(−8)〔换图系批 V12〕
|
||||
[standee_present_values] all green (26 checks)
|
||||
[PASS] 基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕
|
||||
[PASS] 名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕
|
||||
[PASS] 全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕
|
||||
[PASS] 退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕
|
||||
[PASS] PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕
|
||||
[adventure_feats_test] all green (138 checks)
|
||||
[PASS] 专长带入池:37 件四级直映射(普通 14/优秀 13/稀有 10/传说 0)——牌库 102+37=139〔专长批 V2+迭代 46 换图系七件换代〕
|
||||
[round_flow_skill_pool_test] all green (134 checks)
|
||||
[seal_polarity_test] all green (70 checks)
|
||||
[effect_words_t1] all green (20 checks)
|
||||
[forge_table_test] all green (41 checks)
|
||||
```
|
||||
|
||||
```
|
||||
[render_gate] [golden] board_L1_dungeon.png — changed=0.00%(容差 ≤16 差/≤2%)OK
|
||||
[render_gate] [golden] board_L2_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
|
||||
[render_gate] [golden] board_L3_deep_underdark.png — changed=0.00%(容差 ≤16 差/≤2%)OK
|
||||
[render_gate] [golden] menu_boot.png — changed=0.00%(容差 ≤16 差/≤2%)OK
|
||||
[render_gate] 全绿
|
||||
```
|
||||
|
||||
注:V10 消息「escrow +1(0→0)」的两个读数均在 `player_request_refresh()` 求值前格式化——显示瑕疵;断言本体绑刷新后 `escrow == escrow0 + 1`,判据面正确(绿即真值)。
|
||||
|
||||
## 五、内部字段直查受控例外核对(test-conventions §80/§8.7 第 4 条)
|
||||
|
||||
| 直查行 | 性质 | 判定 |
|
||||
|---|---|---|
|
||||
| `orch.fx_host().counters_ctx`(refresh_feats_test 5 处:读计数器×3+白盒预置×2) | `counters_ctx` 是 BattleFxHost **公开成员**(无下划线),读经公共 API `EffectEngine.counter` | 非私有直查;存量套件 adventure_feats_test 同款先例 |
|
||||
| `EffectEngineScript.counter_add(...)` 白盒预置(V9/V8 上限 3 闸) | 计数器直改=数据面注入 | **已申报**——实现决定 #11(test-conventions §5.4 引用)+对照表 V8/V9 行「白盒预置」在案 |
|
||||
| `orch1._enemy_system.hero_unit()`(refresh_feats_test:518,V9 缺血读数) | **私有面直查 1 处** | **例外(手续缺注,不阻塞)**——对照表 V9 行与实现决定 #11 均未注该行理由。依据:①判据主面(heal 行/未生效行)经公共 `step_row` 信号在案,私有读只作前置量(`missing >= 3` 守卫),行为并非钉在私有面上;②存量套件 roll_multiplier_test.gd:51 同款先例;③公开替代面存在(`presentation_snapshot()` 含 hero.hp)。建议实现报告 V9 行补注一句(随合回流程,本审计不代改) |
|
||||
| 生产码 `oc._damage._emit_hero_damage_taken`/`oc._rounds._grant_seg` 直调 | 结算族内部协作 | 实现决定 #8 备案(结算族直调惯例)——非测试面,不在本节判域 |
|
||||
|
||||
## 六、附加核对(实现决定复核三项+卡C 脏面)
|
||||
|
||||
1. **S7_B1_SET 增补(实现决定 #6)——判据口径未变,通过**:static_scan.sh diff 仅 1 行清单+2 行注释;B2/B3/B4 三族符号交替式(14/5/9 符号)逐字未动;豁免语义(自家文件、注释/字符串排除、s7-b1-exempt)未动;`battle_refresh_flow.gd` 调 `oc._reward_economy.retire_mapping()` 命中 B4 符号,入 B1 域集与 C13 拆分边界自洽。审计实跑 static_scan:S7 ok(50 文件扫描面)。
|
||||
2. **S9 修正(f498d2ec)——零行为变化,通过**:4 处显式非参数化容器注解(`damage_controller.gd` 3 处 `: Dictionary`/`: Array`、`grant_segment.gd` 1 处)改 `:=` 推断,静态注解改写,无逻辑分支变化。
|
||||
3. **卡C 停摆脏面核对——申报一致,另见§七.1**:`git status` 修改面恰为申报的三件(`board_view.gd`/`layout_probe.gd`/`board_gen_reward_test.gd`),均未提交、不在审计 diff 内;另有 2 个未跟踪 `.uid` 文件(§七.1)。
|
||||
4. **golden 申报制核对**:golden 五文件(三张板面+level_map+menu_boot)git 零改动=重锚申报制零触发(实现决定 #4「无需重锚」与实跑 0.00% 互证);不存在借重锚夹带。
|
||||
|
||||
## 七、测试削弱核对清单(diff 被改/被删旧断言逐处 → 申报面)
|
||||
|
||||
| 位置 | 改动 | 申报依据 | 判定 |
|
||||
|---|---|---|---|
|
||||
| adventure_feats_test :464/:476 | 行数 30→37、稀有度 12/9/9→14/13/10、牌库 132→139 | 卡A§十二第 1 条(计数换代申报) | **通过**——零放宽:validate/禁词/退役钉面零删(diff 核对) |
|
||||
| forge_table_test :202 附近 | EFFECT_KINDS 32→34、EVENTS 20→22 | 卡A§十二第 1 条 | **通过** |
|
||||
| effect_words_t1_test :17-21 | 纯增量 5 行(两事件两效果在册) | 增量非削弱 | **通过**(旧负例全保留,diff 核对) |
|
||||
| standee_present_values_test V6 | 读数 149→150+「9px 档→8px 档」句 | 卡B§8 申报第 1 条 | **通过**(文字不压地台判据零改动) |
|
||||
| standee_present_values_test V10–V12 | 纯增量 8 断言+文件头注释换代 | 卡B§8 申报第 2 条 | **通过**(V5/V7/V8/V9 判据零改动,diff 核对) |
|
||||
| run_all.gd :191 | 套件注释 V5–V9→V5–V12 | 非行为改动(卡B§6 预告) | **通过** |
|
||||
| 禁删面(卡A§十二):RAP8 基线/每轮上限 2/AP 保留/刷新图零清图/伤害下限/五段序/同种子回放 | round_flow/seal 两文件零进 diff;伤害下限在 `_resolve_board_flat_hero` 保位(`maxi(下限, dealt)` 减后过下限=卡C4 同款) | 卡A§十二禁删令 | **通过**(134/70 checks 实跑绿承载) |
|
||||
| 生产面删除项 | `_terrain_damage_reduce` 删/编排器旧刷新体删/`_enemy_shield_apply` 改名/ui_kit 两常量删 | 卡A T2/T3 步骤明列+卡B§1/§3 | **通过**(全部卡面内,无未申报删改) |
|
||||
| 变异/撤销类取证 | 全 diff 无回退 hunk/注入破坏证明 | 卡A§十二+卡B§3 | **通过**(临时专长经 ConfigDB 表行追加=卡§十二「正常数据配置路径」申报一致) |
|
||||
|
||||
## 八、附加发现(不属验收对照面、不阻塞合并——处置建议随合回流程)
|
||||
|
||||
1. **批内新件两枚 `.uid` 未随批提交**:`game/core/settlement/battle_refresh_flow.gd.uid` 与 `game/tests/unit/battle_settlement/refresh_feats_test.gd.uid` 未跟踪(mtime 07:49,早于 T3 提交 07:52——非审计跑测所生成)。`.gitignore` 第 6 行明文「Godot 4 的 .uid 文件必须提交,不要忽略」,全仓其余 `.gd` 均有 `.uid` 入库伴件。已核零 UID 引用(grep 两 uid 串零命中),风险低;建议合回前补提交(本审计受只读约束不代提交)。
|
||||
2. **S8_FILES 未与 S7_B1_SET 同步增补**:await 零命中扫描集仍为 10 文件,新 B1 拆件不在其内。实查新件零 await(grep 证),当日无漏扫;但两清单同源 B1 域集出现分叉,建议 architecture「模块归属」BattleRefreshFlow 行落文(反哺点 #5)时同批补 S8_FILES。
|
||||
3. **V10 消息文案时点**(§四注):纯文案瑕疵,判据不受影响。
|
||||
4. **V9 私有读未注理由**(§五):建议补注。
|
||||
|
||||
## 九、结论
|
||||
|
||||
**审计通过(零缺漏)**——卡A 对照表 V1–V12 与卡B 对照表 6 行逐行通过(例外 1 处=V9 私有读手续缺注,附依据不阻塞);四套件+三附加套件+gdstyle+static_scan+render_gate 当轮自跑全绿;测试削弱全部在两卡申报面内,无未申报删改,无变异/撤销类取证;结构预算申报(803 行/类变量 15/各件增员)经实数核对全部相符。§八附加发现四条均不属验收对照面,随合回流程处置。
|
||||
@@ -0,0 +1,21 @@
|
||||
# grilling 复核——站桩名字行提档与地台色合并批需求卡片(2026-10-31)
|
||||
|
||||
> 接收方:reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md(v1.0→v1.1)。复核方=独立子代理对抗审查(未参与写卡;Godot 4.7.2 console 版实测断言写法)。本件属中间文档,随批退役。
|
||||
|
||||
## Findings 与处置(五条全落 v1.1)
|
||||
|
||||
| # | 级别 | 发现 | 处置(落卡位置) |
|
||||
|---|---|---|---|
|
||||
| 1 | 阻断 | V11 断言 `UiKit.get_script().get_script_constant_map()` 实测 parse error(类引用直调非静态函数不合法);机制本身可靠(实测枚举 186 键含目标常量) | T2 断言载体改 `load("res://presentation/views/ui_kit.gd").get_script_constant_map()`,依据随句注明(§7.3) |
|
||||
| 2 | 阻断 | 五取值点中三处宽度测量点零断言——漏改测量点时牌宽按 12px 计、文字溢素底牌,全断言仍绿 | V10 增第三断言=计数锚 `body_src.count("UiKit.FS_CAPTION") == 1`(改后仅剩 :114 消散提示一处),三测量点强制换代(§7.2/§8 行 1) |
|
||||
| 3 | 阻断 | 绘制点改指只有 grep 反证(旧名消失)无正证(落成错值也绿,golden 重锚还会冻结错值) | V11/V12 补三处绘制点源码锚正证:顶缘线 `ped.size.x, 2.0), UiKit.BRICK, true`、口沿 `FACADE_HIGHLIGHT_PX), UiKit.BRICK, true`、描边 `UiKit.PLAZA_EDGE`(§7.3/§8 行 3/4);grep 降为反证补充 |
|
||||
| 4 | 建议 | §8 首句「6 行对应完成定义第 1–6 条」对应关系不成立(第 5 条无行) | 首句改「第 1–4、6 条;第 5 条经 T3 核对步承载」(§8 首句) |
|
||||
| 5 | 建议 | `run_all.gd:191` 套件注释「V5–V9」增 V10–V12 后成死注,但该文件不在结构图 | §6 文件结构图补 run_all.gd 行(注释换代,非行为改动)+T2 增注释换代步(§7.3) |
|
||||
|
||||
## 无发现审点
|
||||
|
||||
步骤扫描(每步一动作一可核查结果)/名字一致性(PLAZA_EDGE、name_font_size、V10–V12 逐字一致且与盘面无冲突;V10 锚串与顺位徽标行无碰撞)/落位与复用(`Color8(76` 全仓零命中、`#4C4038` 仅探针注释非 token——新常量论证成立)/预算与契约(probe 增字典键非成员,gdstyle 三触面零基线键,S13 零新增预期成立;golden 重锚工具缺省只动板面三张,与禁触清单兼容)/估时(0.5 天与触面相称)。
|
||||
|
||||
## 结论
|
||||
|
||||
三阻断两建议全部落卡后 **PASS**——卡片 v1.1 成立,可交迭代编排段。
|
||||
@@ -1,50 +0,0 @@
|
||||
# 双审复核 · 立绘显示档批(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**(观察项):另立工程批。
|
||||
@@ -1,145 +1,331 @@
|
||||
# 提案:弃图刷新系专长流派(双基座七件)——预案 v0.5(2026-10-31)
|
||||
# 提案:弃图刷新系专长流派(双基座七件)——预案 v0.4(2026-10-30)
|
||||
|
||||
> **性质**:换图系设计提案与 GDD 修改清单。双基座七件结构已经用户终审;本文保留为后续实现与验收的参考件,不随 GDD 落文退役。
|
||||
> **性质**:待办清单条目「封锁-蓄力」combo 设计素材的设计提案与 GDD 修改清单。双基座七件结构已经用户确认,修改清单已经用户审核并落入四份 GDD;本文按用户要求保留,供后续写需求卡片参考。代码与数值表尚未实现。
|
||||
>
|
||||
> **核心裁定**:流派先由基座专长改变资源来源与刷新价格,再由后续专长让成功刷新直接形成输出、生存与 GP 资源收益。所有效果都改变玩家的刷新决策,不把旧图探索进度折算成清图奖励。
|
||||
> **需求来源**:用户 2026-10-30 明确设计方向——流派先有基座专长,从资源来源、动作成本或结算规则上改变基础行为;后续专长围绕新行为提供输出、生存与资源收益。只有刷新后的数值增长,不足以形成流派。
|
||||
>
|
||||
> **结论行**:矿脉储能让付费挖格产 RAP,省力周转把刷新成本减半;两件基座允许叠加。崩壁冲击与蓄力周转把刷新变成输出,石层缓冲把刷新变成当轮全局受伤减免,坑道小憩把多次刷新累计成当轮恢复,矿道红利每次成功刷新固定给 1 GP。
|
||||
> **结构裁定**:用户指定设计 **2 件基座+2 件输出+2 件生存+1 件资源**,共 7 件;两件基座允许同时持有并叠加;弃图回收按旧图探索进度发清图奖励,不采用固定发一份。本提案结构待裁点已清零。
|
||||
>
|
||||
> **结论行**:「掘进储能」让挖格产 RAP,解决刷新资源从哪里来;「折价换图」让刷新 RAP 成本减半,解决同样资源能刷几次。两件基座叠加后形成完整换图引擎。后续五件让刷新分别产生群体直伤、下次攻击爆发、护盾、回血与按探索进度回收的清图奖励。拿到任一基座后,刷新不再只是坏图逃生;配上后续专长,玩家会为了输出、生存或资源主动规划换图。
|
||||
|
||||
---
|
||||
|
||||
## 0. 七件总表
|
||||
|
||||
| 层 | 显示名 | 效果 | 行为职责 |
|
||||
> 名称均为代号;数值与稀有度均为 provisional,不在本预案定稿。
|
||||
|
||||
| 层 | 代号 | 效果候选 | 它改变的决策 |
|
||||
|---|---|---|---|
|
||||
| 基座 | 矿脉储能 | 每次受理的付费挖格额外获得 RAP | 持续挖图也能为刷新蓄能 |
|
||||
| 基座 | 省力周转 | 弃图刷新的 RAP 成本减半 | 同一份 RAP 支撑更多刷新 |
|
||||
| 输出 | 崩壁冲击 | 成功刷新后,本轮战斗段末对全体存活敌造成固定伤害 | 刷新成为进攻动作 |
|
||||
| 输出 | 蓄力周转 | 每次成功刷新叠层,上限 3;本轮下一次主角攻击消费全部层数 | 连续刷新汇成单点爆发 |
|
||||
| 生存 | 石层缓冲 | 本轮只要成功刷新过,主角每次受到伤害固定减少 2 点;不叠加 | 刷新成为当轮防御姿态 |
|
||||
| 生存 | 坑道小憩 | 每次成功刷新累计 1 点恢复,上限 3;本轮授予段合计回血 | 用刷新换取恢复空间 |
|
||||
| 资源 | 矿道红利 | 每次成功刷新固定获得 1 GP | 刷新动作自身带少量金币收益 |
|
||||
| **基座** | **掘进储能** | 每次付费挖格后额外获得 RAP | 想刷新不再必须少挖停手;持续挖图也能蓄能 |
|
||||
| **基座** | **折价换图** | 弃图刷新的 RAP 成本减半 | 同样的储备支持更多刷新;部分挖格+部分停手也能维持换图 |
|
||||
| **输出** | **弃图轰击** | 成功刷新后,对全体存活敌人造成伤害 | 刷新本身成为进攻动作 |
|
||||
| **输出** | **换图蓄势** | 每次刷新攒层;下一次攻击附加全部层数后清零 | 连续换图可以蓄成一次单点爆发 |
|
||||
| **生存** | **换图庇护** | 成功刷新后获得护盾 | 刷新可以主动用来抵挡后续伤害 |
|
||||
| **生存** | **弃图休整** | 成功刷新后回复生命 | 玩家可以放弃当前图,换取一次恢复机会 |
|
||||
| **资源** | **弃图回收** | 成功刷新后,按旧图探索进度回收清图奖励 | 不必挖完最后几格;可以先投入,再主动弃图变现 |
|
||||
|
||||
**结构关系**:基座负责让主动刷新可持续;输出、生存、资源五件负责让主动刷新有明确目的。后续专长不需要设置「必须先拿基座」的硬前置,它们的联动由实际刷新频率自然形成。
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计原则
|
||||
## 1. 通用设计思想:先改变行为,再给新行为收益
|
||||
|
||||
1. **基座先改行为**:必须改变资源来源、动作成本、动作结算或动作关系,使偶尔止损的刷新成为可持续打法。
|
||||
2. **后续收益围绕新行为**:输出、生存、资源件只在成功刷新后兑现,不做放进任何构筑都成立的泛用增益。
|
||||
3. **双基座可叠加**:一件改 RAP 收入,一件改 RAP 支出;每轮刷新上限继续负责限频。
|
||||
4. **无专长基线不变**:七件只给持有者增加规则;全局经济、清图奖励与刷新基础语义不改。
|
||||
5. **临时生命值不做**:现有体系没有独立血量护盾,本流派不使用该概念;石层缓冲直接减少最终承伤。
|
||||
这一节同时作为后续修订 `奇物.md`「专长·设计原则」的建议文本。现在只记录在提案中,尚未写入 GDD。
|
||||
|
||||
### 1.1 基座专长的定义
|
||||
|
||||
**基座专长=让某个原本偶尔使用、主要用于止损的动作,变成可以持续使用的主要打法的专长。**
|
||||
|
||||
它至少改变下列一项:
|
||||
|
||||
1. **资源来源**:原来主要靠停手获得 RAP,现在挖格也能获得 RAP。
|
||||
2. **动作成本或频率**:原来一整轮停手才能刷新一次,现在同样储备可以支持更多刷新。
|
||||
3. **动作结算**:原来危险格只伤害玩家,现在也能重伤敌人,使主动踩雷成为进攻手段。
|
||||
4. **动作关系**:原来挖图与弃图争夺资源,现在挖图为弃图蓄能,两个动作组成循环。
|
||||
|
||||
成本减半虽然形式上仍是数值变化,但如果它让刷新从偶尔可用变成持续可用,也满足基座要求。**判断标准不是改了多少规则文本,而是玩家的实际操作是否改变。**
|
||||
|
||||
### 1.2 基座与后续专长的分工
|
||||
|
||||
| 层 | 职责 | 不合格形态 |
|
||||
|---|---|---|
|
||||
| **基座专长** | 建立新的资源循环或动作规则,让核心动作能持续使用 | 只在原动作后加少量攻击、护甲或资源,动作使用时机不变 |
|
||||
| **后续专长** | 让核心动作产生输出、生存或资源收益,扩展新打法 | 不依赖新行为、放进任何构筑都同样有效的泛用数值增长 |
|
||||
|
||||
后续专长仍可采用「当 X 时 Y」的触发器句式,也可以各自独立生效;不另设集件标签。它们与基座的联系来自实际玩法:没有基座时,刷新少、收益兑现慢;有基座后,同一效果成为主循环的一部分。
|
||||
|
||||
### 1.3 行为改变检验
|
||||
|
||||
新流派或新基座专长过准入时,必须回答三个问题:
|
||||
|
||||
1. **拿之前怎么玩,拿之后怎么玩?** 如果最优操作与决策时机基本不变,只是结算数字变大,就不是基座。
|
||||
2. **新行为为什么能持续?** 必须写清资源从哪里来、多久能做一次、什么限制防止无限循环。
|
||||
3. **后续专长为什么属于这里?** 它应当兑现或强化基座建立的行为,而不是替代基座。
|
||||
|
||||
以自爆流为例:没有相关专长时,玩家应躲开危险格;拿到能减少自身雷伤、同时让危险格重伤敌人的基座后,玩家会主动寻找危险格。后续专长再提供踩雷后的回复、护甲或追加伤害。**「本来不该点」变成「现在值得主动点」,才是行为改变。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 行为循环
|
||||
## 2. 两件基座:分别改资源来源与动作价格
|
||||
|
||||
无专长时:
|
||||
### 2.1 掘进储能
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[正常挖图] --> B[遇到坏图]
|
||||
B --> C[停手攒 RAP]
|
||||
C --> D[必要时刷新]
|
||||
D --> A
|
||||
**效果候选**:每次付费挖格后,额外获得一定 RAP。安全格与危险格都按付费挖格计,不另判玩家意图。
|
||||
|
||||
**基础循环**:
|
||||
|
||||
```
|
||||
持续挖格并获得格面收益 ──同时积攒 RAP──▶ 主动弃图刷新
|
||||
▲ │
|
||||
└──────── 新图继续挖格 ◀───────────────┘
|
||||
```
|
||||
|
||||
拿到两件基座后:
|
||||
**行为改变**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[付费挖格] -->|矿脉储能| B[RAP 增长]
|
||||
B -->|省力周转半价| C[成功刷新]
|
||||
C --> D[崩壁冲击]
|
||||
C --> E[蓄力周转]
|
||||
C --> F[石层缓冲]
|
||||
C --> G[坑道小憩]
|
||||
C --> H[矿道红利]
|
||||
C --> I[新图继续挖格]
|
||||
- 玩家可以一边挖图拿收益,一边准备下一次刷新,不再必须停手。
|
||||
- 每轮 AP 有限,因此新增 RAP 也有限;它扩大刷新机会,不提供无限刷新。
|
||||
- 单拿时,坏图更容易被换掉;配上任意后续专长得益后,主动刷新才有完整目的。
|
||||
|
||||
### 2.2 折价换图
|
||||
|
||||
**效果候选**:弃图刷新的 RAP 成本减半。
|
||||
|
||||
**基础循环**:
|
||||
|
||||
```
|
||||
部分挖格/部分停手留 AP → 同一份 RAP 支持更多次刷新 → 更频繁兑现后续专长
|
||||
```
|
||||
|
||||
玩家会同时比较:
|
||||
**行为改变**:
|
||||
|
||||
- 还要不要继续挖旧图,还是现在刷新兑现崩壁冲击或矿道红利;
|
||||
- 本轮承伤压力大时,是否用石层缓冲换一次全局减伤;
|
||||
- 生命偏低时,是否通过多次刷新累计坑道小憩的恢复;
|
||||
- 是否蓄满 3 层蓄力周转,把多次刷新集中成一次攻击;
|
||||
- 刷新次数与 RAP 价格如何影响后续图推进。
|
||||
- 玩家不再需要为一次刷新攒下完整一轮的 AP;挖图与留 AP 的比例出现更多选择。
|
||||
- 与掘进储能不同,它保留停手攒 RAP 的旧来源,但降低主动换图所需的投资。
|
||||
- 单拿时,刷新从昂贵逃生变成常用工具;配上后续专长后,刷新次数直接转化为输出、生存或资源。
|
||||
|
||||
### 2.3 两件基座的组合
|
||||
|
||||
两件基座作用在不同位置:一个改 RAP 来源,一个改 RAP 价格。**用户已裁定允许同时持有并叠加**,组合后形成完整基座:
|
||||
|
||||
```
|
||||
挖格产 RAP + 刷新半价 → 每轮更容易达到刷新次数上限 → 后续五件兑现收益
|
||||
```
|
||||
|
||||
叠加后的硬边界仍是每轮刷新次数上限、有限 AP、旧图进度损失与战斗结束 RAP 清零。若实测过强,优先调产能量与半价数值,不增加「两件基座互斥」的特殊规则。
|
||||
|
||||
---
|
||||
|
||||
## 3. 七件效果结构
|
||||
## 3. 两件输出:即时群伤与延迟爆发
|
||||
|
||||
### 3.1 矿脉储能
|
||||
### 3.1 弃图轰击
|
||||
|
||||
每次付费挖格被受理后,额外获得 1 RAP。连锁展开不逐格追加;封印、移动、刷新不产 RAP。
|
||||
**效果候选**:每次弃图刷新成功后,对全体存活敌人造成伤害。
|
||||
|
||||
### 3.2 省力周转
|
||||
**玩法职责**:提供即时、稳定的群体输出。玩家会在「继续挖旧图」与「现在刷新打一轮伤害」之间做选择。
|
||||
|
||||
刷新 RAP 成本固定为基线的 50%。它只改持有者的价格,不改全局基值,也不返 RAP。
|
||||
**与其他件的关系**:
|
||||
|
||||
### 3.3 崩壁冲击
|
||||
- 掘进储能支持持续挖、持续刷、持续轰击。
|
||||
- 折价换图让同一份 RAP 兑现更多次轰击。
|
||||
- 与换图蓄势组合时,一次刷新同时提供当场群伤与下次攻击的蓄力层。
|
||||
|
||||
每次成功刷新记录一次当轮事实。本轮战斗段末,所有存活敌人各受 2 点固定伤害;刷新瞬间不当场插入战斗结算。
|
||||
**边界**:只有成功刷新才触发;刷新被拒绝不触发;每轮刷新上限同时限制伤害次数。
|
||||
|
||||
### 3.4 蓄力周转
|
||||
### 3.2 换图蓄势
|
||||
|
||||
每次成功刷新叠 1 层,最多 3 层。本轮下一次主角攻击附加全部层数对应的攻击加成,消费后清零;战斗结束不跨战保留。
|
||||
**效果候选**:每次成功刷新获得 1 层蓄势,层数有上限;下一次攻击附加全部层数对应的伤害,然后清零。
|
||||
|
||||
### 3.5 石层缓冲
|
||||
**玩法职责**:提供延迟、集中的单点爆发。与弃图轰击的即时群伤不同,它鼓励连续换图后寻找合适的攻击窗口。
|
||||
|
||||
每次成功刷新把当轮减伤状态置为生效。本轮主角每次受到伤害,在现行伤害下限之前固定减少 2 点;同一轮多次刷新不继续叠加。覆盖敌方攻击、危险格、错误封印资源格与地形造成的主角伤害。
|
||||
**与其他件的关系**:
|
||||
|
||||
### 3.6 坑道小憩
|
||||
- 双基座组合更容易连续刷新,为一次重攻蓄满层数。
|
||||
- 与现有攻击强化专长可以组合,但不另设专属加成。
|
||||
- 层数只在当前战斗内保留,战斗结束清零,避免跨战囤积。
|
||||
|
||||
每次成功刷新累计 1 点恢复量,最多累计 3 点。本轮授予段一次性合计回复生命,不超过生命上限,随后清零。
|
||||
|
||||
### 3.7 矿道红利
|
||||
|
||||
每次成功刷新固定获得 1 GP。不按旧图探索进度折算,也不与清图奖励绑定。每轮刷新上限限制触发次数。
|
||||
**边界**:层数必须有上限;一次攻击消费全部层数,不能同一批层数重复附加到多次攻击。
|
||||
|
||||
---
|
||||
|
||||
## 4. 与现行结算时序的衔接
|
||||
## 4. 两件生存:当场护盾与直接回血
|
||||
|
||||
- 刷新只结束挖掘动作的资源支出,不结束当前轮次,也不新增第六个结算段。
|
||||
- 成功刷新后产生 `board_refreshed` 事实;刷新被拒绝时不产生事实。
|
||||
- 崩壁冲击只先开伤害单,沿用现行案 C,在战斗段末结账。
|
||||
- 石层缓冲订阅全局 `damage_taken`;该事件统一覆盖所有主角受伤路径。
|
||||
- 坑道小憩和矿道红利用现有 GP/治疗兑付通道留痕。
|
||||
- 刷新图仍不发清图奖励;正常清图支配性不变。
|
||||
### 4.1 换图庇护
|
||||
|
||||
**效果候选**:每次成功刷新后获得护盾,按现有护盾规则抵挡伤害与消耗。
|
||||
|
||||
**玩法职责**:提供面向未来伤害的保护。玩家可以预判敌方压力,主动弃图换护盾,而不是只在棋盘不好时刷新。
|
||||
|
||||
**与弃图休整的区别**:护盾适合抵挡即将发生的伤害;休整用于修复已经失去的生命。一件偏预防,一件偏补救。
|
||||
|
||||
### 4.2 弃图休整
|
||||
|
||||
**效果候选**:每次成功刷新后回复一定生命。
|
||||
|
||||
**玩法职责**:把旧图与 RAP 转成恢复机会。生命偏低时,玩家可以放弃当前图的后续收益,换取继续冒险的空间。
|
||||
|
||||
**边界**:
|
||||
|
||||
- 回复不能超过生命上限,不转化为额外生命。
|
||||
- 刷新成本与每轮次数上限不变,不能无代价连续回血。
|
||||
- 强度对账时须同时计算放弃旧图进度的损失,不能只看单次回血量。
|
||||
|
||||
两件生存专长可以同时持有,让刷新兼具保护与恢复;但它们不替代输出件,玩家仍需通过其他攻击手段结束战斗。
|
||||
|
||||
---
|
||||
|
||||
## 5. 防退化边界
|
||||
## 5. 一件资源:把旧图投入换成清图奖励
|
||||
|
||||
- RAP 仍来自有限 AP,不能无限刷新。
|
||||
- 刷新本身不返 RAP。
|
||||
- 每轮刷新上限继续生效。
|
||||
- 石层缓冲是固定减伤,不堆层、不产生零伤害。
|
||||
- 坑道小憩恢复上限 3,不能靠无限刷新稳定续命。
|
||||
- 矿道红利每次仅 1 GP,量级明显低于正常战斗掉落,不足以替代清图与探索收益。
|
||||
- 不引入临时生命值,不改变棋盘信息规则。
|
||||
### 5.1 弃图回收
|
||||
|
||||
**已定效果**:成功刷新后,按旧图本次探索进度回收清图奖励;探索越多,回收越完整。具体档位、比例与奖励内容归后续调参。
|
||||
|
||||
**玩法职责**:建立与正常清图不同的资源兑现路线:
|
||||
|
||||
```
|
||||
先挖图投入 AP、积累探索进度 → 不再挖最后几格 → 主动刷新 → 按进度回收清图奖励
|
||||
```
|
||||
|
||||
**行为改变**:旧图不再只有「清完」与「全部放弃」两个结果。玩家可以比较继续清图的时间与风险,选择提前弃图变现。
|
||||
|
||||
**为什么不固定发一份**:空图刷新也拿完整收益,会让挖图投入失去意义,并把资源流退化成机械地点刷新。按进度回收要求玩家先探索、再决定何时兑现,保留先投入再弃图的循环,也保留接近清图时的取舍。用户已据此裁定采用按进度回收。
|
||||
|
||||
**边界**:同一旧图只能在成功放弃时结算一次;新图没有继承旧图的可回收进度;刷新被拒绝时不发奖励。
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终裁定记录
|
||||
## 6. 七件如何组成不同打法
|
||||
|
||||
- D1:双基座允许叠加。
|
||||
- D2:资源件改为每次成功刷新固定 1 GP,不采用探索进度回收清图奖励。
|
||||
- D3:生存件改为全局固定减伤,不使用护盾或临时生命。
|
||||
- D4:恢复量按当轮成功刷新次数累计,上限 3。
|
||||
- D5:所有主角伤害统一经过 `damage_taken`,允许现有订阅该事件的专长覆盖面按字面扩展。
|
||||
- D6:七件显示名采用矿脉储能/省力周转/崩壁冲击/蓄力周转/石层缓冲/坑道小憩/矿道红利。
|
||||
- D7:其余数值与稀有度先按需求卡片 provisional 投值,出口人闸追认。
|
||||
| 构筑方向 | 组合 | 主要操作 |
|
||||
|---|---|---|
|
||||
| **持续轰击** | 掘进储能+弃图轰击 | 边挖边蓄 RAP,够价就刷新,刷新持续削全体敌人 |
|
||||
| **蓄力爆发** | 折价换图+换图蓄势 | 用较低成本连续换图,把多次刷新汇成一次重攻 |
|
||||
| **双基座输出** | 两基座+两件输出 | 高频刷新同时提供群伤与攻击层数,形成完整输出流 |
|
||||
| **换图续命** | 任一基座+换图庇护+弃图休整 | 在生命与敌方压力下主动换图,用护盾与回血延长战斗 |
|
||||
| **弃图采收** | 掘进储能+弃图回收 | 挖到一定进度后主动放弃,连续从新图获取探索收益与回收奖励 |
|
||||
| **七件完整形态** | 全部七件 | 挖格蓄能、低价刷新,每次换图同时兑现输出、生存与资源;仍受每轮次数上限约束 |
|
||||
|
||||
**最小成立单位**是任一基座+任一符合当前需要的后续专长。七件不要求一次拿齐,也不设集件奖励。
|
||||
|
||||
---
|
||||
|
||||
## 7. 后续参考
|
||||
## 7. 防退化与共同边界
|
||||
|
||||
本文保留供需求卡片、实现验收与后续调参比对。换图系现行权威规则仍以四份 GDD为准:
|
||||
| 检查 | 约束 |
|
||||
|---|---|
|
||||
| **是否无限自供能** | 新 RAP 来自有限 AP 的付费挖格;刷新本身不产 RAP;每轮刷新上限与战斗结束清零不变。 |
|
||||
| **无专长基线是否改变** | 不改变。两件基座是专长提供的新规则,不直接修改无专长时的全局经济。 |
|
||||
| **双基座是否失控** | 叠加后仍受每轮次数上限;数值对账以每轮实际可达到的刷新次数为准。 |
|
||||
| **输出是否重复消费** | 轰击每次成功刷新结算一次;蓄势层数在一次攻击后全部清零。 |
|
||||
| **生存是否无代价** | 护盾与回血都要求成功刷新;玩家同时付出 RAP、旧图进度与刷新名额。 |
|
||||
| **正常清图是否被删除** | 不删除。弃图回收不应让同等投入的收益无条件高于正常清图。 |
|
||||
| **空图是否拿满资源** | 推荐案不允许;回收按旧图探索进度计算。 |
|
||||
| **信息红线** | 七件均不增加揭示效果,不碰危险格信息规则。 |
|
||||
|
||||
- `design/gdd/systems/冒险循环/奇物.md`
|
||||
- `design/gdd/systems/排雷循环/行动经济.md`
|
||||
- `design/gdd/systems/战斗循环/战斗结算.md`
|
||||
- `design/gdd/systems/战斗循环/地图战斗资源.md`
|
||||
若实装后玩家仍然只在坏图时刷新,说明基座或后续收益没有真正成立;不能只继续增加数值来掩饰行为没有改变。
|
||||
|
||||
---
|
||||
|
||||
## 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. 自爆流现行条款不在本批改动面内;若以后要按同一基座思想重构,另行登记与裁决。
|
||||
|
||||
本提案保留为写卡参考;换图系七件尚未实装,未跑任何游戏测试。
|
||||
@@ -0,0 +1,522 @@
|
||||
# 提案:营地场景图美术生成方案(渠道 · 分层 · 工作流 · 样张验收判据)
|
||||
|
||||
> 性质:设计讨论段过程稿(2026-10-22 美术方案讨论产出)。**只讨论生成方案,不出图、不写需求卡片、不改 `game/`、不编迭代**——美术线是用户自立批(铁律 2),方向稿与「过/不过」的目检结论由作者出。
|
||||
> **现行口径指针(2026-10-30 作者裁定)**:分层张数与围墙归属已有新裁定——营地场景图 **10 张** = 背景层 `scene_camp_bg`(z0,不透明全幅:天空+远山+整片地面+门洞小路)+ 围墙层 `scene_camp_wall`(z1,透明底:横贯密排木桩墙+两根门柱,门洞留透明缺口)+ 八个物件层(布告板 z2/门扇 z3/木桩·锻炉·帐篷 z4/宝箱·笔记本 z5/篝火 z6)。**围墙已从背景层拆出、独立成层**;门洞小路画在背景层、由围墙门洞透明缺口透出。**现行坐标、提示词、验收与消费名的单一事实源 = `design/art/camp-scene-prompts.md` §2/§4、`art-bible.md` §6.2、`asset-path-map.md` §3、`art-assets-checklist.md` §3.5、`camp.md`**;本稿 §2.2 层表、§2.5 坐标草案、§4 消费常量表、§5 验收、§12 分层元素表所记的「8 张/背景层含围墙/门柱门洞画在背景层」为 2026-10-22 讨论当时口径,其推理留痕保留不改,执行以上列五份现行文档为准。
|
||||
> 输入事实源:`reports/需求卡片-营地场景图新建批-2026-10-18.md`(卡④ v2.3,已退回美术设计段)、`reports/需求澄清-营地交互重做-2026-10-18.md`(目标体验原话/纵深稿/10 条交互裁决)、`design/art/art-bible.md` §6.2/§7.5/§4.1/§4.2/§8.3、`design/art/asset-path-map.md`、`design/art/art-prompts.md` §0/§4bis、`design/art/remote-gen-prompts.md`。
|
||||
> 待作者带的输入:①参照物(作者原话「后续找一下」)②渠道一句话裁定 ③每轮方向稿「过/不过」的目检结论。
|
||||
|
||||
---
|
||||
|
||||
## 0. 四问速览(只读这一段也能定)
|
||||
|
||||
| 问 | 答 |
|
||||
|---|---|
|
||||
| **① 现状差在哪** | 营地场景图**一张都不存在**(现役是四柜台覆盖层,程序化占位)。卡④ 已因美术轨凭据缺(P1–P5 未销号)退回美术设计段,卡⑤ 排在它后面,营地五卡是 Alpha 例外项——**没有图,卡④⑤ 一行代码都开不了**。 |
|
||||
| **② 建议做什么** | 走**项目既有远程生图管线(主角/八敌同条路)出图 + 既有后处理管线收口**,交付形态定为 **「1 张背景层 + 7 张物件层 + 1 份坐标表」**(不是一张整图叠七个矩形热区)。出图顺序:先 2–3 张低分辨率**方向稿**让作者选划方向,判完方向才分层精制。 |
|
||||
| **③ 为什么选它** | 七物件要**悬停/按下/禁用/已解锁四态**:整图 + 矩形热区做不出「贴合物件形状的高亮」和「单个物件灰掉」,要补就得给每个物件抠一张透明遮罩——那其实还是分层,只是遮罩不公开成素材。渠道 **B(插画级新产,✦ 渠道作者自跑远程生图管线)** 的**返工成本最低**(换提示词=换一张,八敌已量产验证);♻ CC0 凑不齐七件同套素材,且多来源像素密度不齐。**不是三选一**:A/B 是「产出什么」,远程生图管线是「怎么产出」。 |
|
||||
| **④ 怎么验证** | 机检三件:色板(`tools/palette_check.py --baseline`)、像素件(`tools/standee_asset_check.py`)、几何(七命中矩形两两不重叠 + 中心点落在物件不透明像素上,实现批写成探针断言)。风格一致、构图好看、七件一眼找得到——这三项机检判不了,**归作者目检**。 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 议题 1 · 生成渠道怎么选
|
||||
|
||||
### 1.1 三条路摆开
|
||||
|
||||
> **口径更正(2026-10-22,Agent 自纠)**:草案曾把 B 误标为「人工/外包」——**错**。项目原文(卡④ `:282`)定义 **B = 插画级新产 = 按纵深稿从零新画一张**;执行方仍是**作者自跑远程生图管线**(`art-assets-checklist.md:14` 图例:✦ = 用户自跑,产出「角色、插画级素材」)。
|
||||
> 因此真实结构是:**A vs B =「产出什么」的二选一**(改现有 vs 从零画),**远程生图管线是「怎么产出」的执行通道**,不是与之并列的第三条路。下表保留三行以便对照,但裁定实质是 A / B 二选一(详见 1.3)。
|
||||
|
||||
| 案 | 怎么干活(逐天的真实动作) | 构图控制力 | 成本/周期 | 贴合已定色域的代价 | 返工风险 |
|
||||
|---|---|---|---|---|---|
|
||||
| **A · CC0 素材改色复用**(♻ 渠道) | 去 Kenney / OpenGameArt / itch.io 免费包找现成像素场景件(营火、砧台、宝箱、床、木牌、栅栏门等);选中后过「复用四道闸」(`art-assets-checklist.md:22`):① 色板量化到 §4.1 的 48 色 token、不保留原包杂色 ② 吸色检查无 HSV 300°–345° ③ 去色相仍可辨、不与语义件混 ④ 授权干净(CC0 优先,CC-BY 须署名,授权不明不用);过闸后按 v3 坐标拼上画面 | **最弱**——要凑齐八个指定物件的同一套素材几乎不可能,落点是东拼西凑 | 找素材耗时不可控(看似零绘成本,实际耗在筛选与授权核对) | **高**——多来源拼的图,像素密度、描边粗细、光照方向各不相同,改色+统一描边的工作量接近重画 | **中偏高**:发现风格不齐或构图不贴纵深稿,只能换源重找,前功尽弃 |
|
||||
| **B · 插画级新产**(✦ 渠道 · 卡④ 原案) | 以已接受的布局稿 + 纵深稿作参考锚,开远程生图对话 → 出构图候选 → 选一张 → 分层出图 → 抠底 / 降采样 / 色板量化 / 描边统一 → 合成校验 → 样张呈裁。**全链路=本提案 §3 的 S0–S7**,执行人始终是作者自己 | **最强**——七件位置与纵深完全由布局稿+提示词控制 | 重出一张很便宜(换提示词=换一张);拖周期的是反复横评,不是绘制本身 | **低**——提示词锁主色(烛光黄 `#F6C667` / 木色 `#8A5A34`)+后处理色板量化收口 | **最低**——候选多、迭代便宜 |
|
||||
| **C · 方向稿先行的混合时序**(B 的低风险写法) | 与 B 同一条管线,只是分两段:**先出 2–3 张低分辨率方向稿**给作者选划 → 判完方向才分层精制。**C 不是与 B 并列的第三条路,它就是 B 的执行时序** | 同 B | 同 B(因先用便宜稿定方向,总额外轮次更少) | 同 B | 最低 |
|
||||
|
||||
### 1.2 我的推荐:选 B(插画级新产),用 C 的时序去走(不替作者拍板,只摊代价)
|
||||
|
||||
> 原推荐写作「C 为主 + 混合收口」,口径更正后重新表述:**产出方式的裁定是 A / B 二选一,推荐 B;C 只是 B 的执行时序(先出可被否决的便宜方向稿),不构成第三选项。**
|
||||
|
||||
**推荐 B**,三条理由按权重排:
|
||||
|
||||
1. **它和本项目的具体缺口对得上**:缺口是「七个可点击物件的独立透明件 + 一张背景」,不是「一张漂亮插画」。远程生图一次出一张、想要几张出几张,天然适配分层交付;♻ 渠道卡在「找不齐一套八件的同一套 CC0 素材」。
|
||||
2. **返工风险最低是硬道理**:重出一张很便宜(换提示词=换一张),所以「先用过渡规格跑通交互、画质后升」这个口径才成立。
|
||||
3. **后处理管线已齐**:抠底→最近邻降采样→色板量化→描边,这条链在八敌批跑通过(art-prompts §0 五步第 4 步);换渠道等于这条链作废重搭。
|
||||
|
||||
**混合怎么混**(= C 时序,B 的内部工序):AI 出构图候选 → 作者选划方向 → 按定稿构图出「背景 1 张 + 物件拼表 1–2 张」→ 脚本后处理(抠底/降采样/量化/描边统一)→ 按坐标表合成校验 → 样张呈裁。**人工只出现在两处:作者选方向、作者判样张**。
|
||||
|
||||
**⚠ 选 B 时要连带处理的一件事(卡④ Q6 甲原文冲突,须作者确认)**:Q6 甲的裁定原文是「**Q1 裁 A 时**用过渡尺寸档=现役 1280 工程档+烛光黄 `#F6C667`/木色 `#8A5A34`」;**若 Q1 裁 B,按该裁定原文「过渡口径不成立」**——须走乙案「等样张批规格清单产出才开 T1」,或由作者**另行裁定一次过渡规格**。而本次讨论的输入条件里把「过渡规格=1280 工程档」列为已定硬约束,两者口径冲突,**属呈报项,不自行取一侧**:请作者确认过渡规格是否无条件适用。
|
||||
|
||||
**一句话裁定**:`A` 还是 `B`(推荐 **B · 插画级新产**,并按 C 的「方向稿先行」时序执行)。裁完即可进 §3 的 S1(S0 已销号)。
|
||||
**同时请顺便确认**:① 过渡规格是否无条件适用(见上方 ⚠ Q6 甲冲突);② 参照物是否阻塞 S1(等参照 vs 先用布局稿当锚)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 议题 2 · 要不要分层、怎么分(核心)
|
||||
|
||||
### 2.1 先判「一张整图 + 七个矩形热区」够不够
|
||||
|
||||
结论:**不够**,四态里有三态做不出来。
|
||||
|
||||
| 四态 | 整图 + 矩形热区能做到吗 | 说明 |
|
||||
|---|---|---|
|
||||
| 悬停高亮 | ❌ 只能整体提亮整图,或画一个**矩形框**——矩形框正是作者点名拒绝的「各种框框」形态;若要贴合物件形状提亮,必须有一张**该物件的透明遮罩** | 「要遮罩」就等于分层,只是遮罩不公开成素材,反而更难维护 |
|
||||
| 按下 | ❌ 同上(位移/压暗也要遮罩,否则整图一起动) | |
|
||||
| 禁用 / 未解锁 | ❌ 灰掉单个物件 → 同样要遮罩 | |
|
||||
| 已解锁 | ✅ 不需要额外素材 | |
|
||||
|
||||
**不分层还会发生什么**(逐条摊开):
|
||||
|
||||
1. **改构图 = 整图重出**:七件的相对位置被一张图锁死。分层后调位置只改坐标表(背景空档由背景层自己承担)。
|
||||
2. **物件没法复用**:图鉴提示里想放宝箱小图标、顶栏想放钥石徽 → 得另出一套小件。分层后同一张 PNG 直接缩着用。
|
||||
3. **命中区与剪影对不齐**:矩形热区必然误吞(点大门旁边的空地也进冒险),只能逐个手工调矩形,且物件形状越不规则误差越大。
|
||||
4. **遮挡关系写死在像素里**:现在篝火压在近景是画出来的,将来要在它后面加东西、或者把火焰做成动效,都得整图重出。
|
||||
5. **四态演示无法提前给作者看**:分层后可以在出图阶段就合成「常态/悬停/按下/禁用」四联图给作者判;整图方案要等代码写完才看得见反馈。
|
||||
|
||||
### 2.2 结论:分层,共 8 张 + 1 份坐标表
|
||||
|
||||
**交付形态 = 1 张背景层 + 7 张物件层 + 1 份坐标表**(8 张 PNG)。
|
||||
|
||||
| 层 | 文件名(草案) | 内容 | z 序 | 是否透明底 |
|
||||
|---|---|---|---|---|
|
||||
| L0 | `scene_camp_bg.png` | 远景天空 + 地面 + 中景围栏(**含门柱与门洞**,门洞内画通往远处的路)+ 远景植被/山;**不含七个可点物件的本体** | 0 | 否(全幅不透明) |
|
||||
| L1 | `scene_camp_board.png` | 布告板/悬赏板(挂在中景围栏上)→ 图鉴 | 1 | 是 |
|
||||
| L2 | `scene_camp_gate.png` | 大门门扇(叠在 L0 门洞上)→ 冒险 | 2 | 是 |
|
||||
| L3 | `scene_camp_dummy.png` | 木桩 → 天赋树 | 3 | 是 |
|
||||
| L4 | `scene_camp_forge.png` | 锻炉·铁砧 → 锻造工坊 | 3 | 是 |
|
||||
| L5 | `scene_camp_tent.png` | 帐篷 →(承载笔记本,本体不可点) | 3 | 是 |
|
||||
| L6 | `scene_camp_chest.png` | 宝箱 → 秘宝匣 | 4 | 是 |
|
||||
| L7 | `scene_camp_notebook.png` | 帐篷旁笔记本 → 生涯记录 | 4 | 是 |
|
||||
| L8 | `scene_camp_campfire.png` | 篝火 → 离开营地/回主菜单 | 5(最前) | 是 |
|
||||
|
||||
> **七个可点物件 = L1/L2/L3/L4/L6/L7/L8 七张**(帐篷 L5 是承载件,不可点;笔记本挂它旁边)。
|
||||
> **四态不落素材**(2026-10-22 作者裁后改写):**悬停 = 给轮廓加亮边(描边发光)+ 浮动**,**不是整体提亮**——亮边由物件层 alpha 外扩 1px 派生,零手绘;按下 = 位移 1px(平移不是缩放,不触甲类整数倍缩放规则);**禁用/未解锁 = 变暗并调成灰度**(见 §9 ⑨,**作者特许破除 §2 明度禁令**,须登反哺点);已解锁 = 常态。
|
||||
> **若作者要「描边发光」式高亮**:不需要新出图——由物件层的透明通道自动外扩 1px 派生一张描边图(脚本生成,零生成成本)。这条正好回答「不分层就做不了描边」。
|
||||
|
||||
### 2.3 篝火与大门要不要区别对待
|
||||
|
||||
**要,两件各有各的特殊处,但都不是「不分离」的理由**:
|
||||
|
||||
- **篝火**(近景居中、可能遮挡):它是**最该分离**的一件——① 它是 z 序最前的近景件,分离后遮挡由代码 z 序管,背景不会被它烧进去;② 它绑「离开营地」这个高代价动作,命中区要单独定(建议与躯干等宽、不含火焰上部飘动区,防误点);③ 火焰是将来唯一可能带动效的件,分离后加帧只动它一张。
|
||||
- 过渡规格先出**静态一张**;若作者要火焰动效,后补 2–4 帧(同方向稿续写锁定形态),属后续画质档,本次非目标。
|
||||
- **大门**(中景、与围栏连体):**背景层画全门柱与门洞**,大门物件层只画**门扇**叠在门洞上。这样分层边界天然(门柱内框 = 对位锚),且不会出现「背景缺个洞」的空洞问题。
|
||||
- **已裁(2026-10-22):门扇=半开**,门洞内**画一条通向远处的小路**(暗示「出去即冒险」)。两扇门同属一张 `scene_camp_gate.png`,接缝一起落在同一命中矩形内。
|
||||
|
||||
### 2.4 分了层,AI 能不能保证七个物件与背景风格一致
|
||||
|
||||
**单靠「同一对话续写」不行**——项目实证:同对话续写 = 对上一张图的**编辑模式**(会被当成增量修图),变体必须各开新对话。所以续写恰恰是风格漂移的来源,不是解法。
|
||||
|
||||
**靠三道,按硬度排序**:
|
||||
|
||||
1. **硬办法 · 拼表制(首选)**:**一张图里画完全部物件**,出完按网格裁开。同一张图内不可能出现两种像素密度、两种描边——风格一致性由「同源」保证,不靠模型自觉。这是本项目瓦片批已验证的制式(2×2 拼表一张出全族)。
|
||||
- 适配到本件:七件尺寸差异大,用**不等分网格 + 每件一个标注框**的排版(提示词写明「七个独立物件,各自占据一个矩形区域,区域之间留统一浅灰间隔,物件的任何像素不得越出自己的区域,不得画任何边框/标签/文字」)。
|
||||
- 兜底:若拼表连续失败(模型坚持画框/串位),退回**一张一跑**,改靠第 2、3 道收口。
|
||||
2. **软办法 · 风格参考图**:出图时贴**已定稿的项目素材**(主角立绘/已定档敌人/瓦片纹理任择)并带既有句式「只是风格参考,只对齐像素密度、平涂、亮暖白描边与暖色限制调色板,不许抄服装道具配件」。目标是把**像素密度与描边**锚到既有世界观上。
|
||||
3. **兜底 · 后处理统一**:抠底 → 最近邻降采样到统一像素密度 → 色板量化到同一套板 → 描边统一(缺描边的件由脚本按 alpha 外扩补一圈亮暖白描边)。三道做完,风格差异被压到只剩造型差异(造型差异是好事,七个物件本来就该长得不一样)。
|
||||
|
||||
### 2.5 分层的代价:位置谁定、对位怎么保证
|
||||
|
||||
| 代价项 | 谁定 | 怎么保证 |
|
||||
|---|---|---|
|
||||
| **七件摆哪儿** | **作者定方向**(纵深稿已给:中景围栏+大门、近景中间篝火、左锻炉铁砧、右床铺与宝箱),Agent 出**灰块布局稿**(1280×800,七个编号矩形 + z 序标注)供作者调 | 布局稿 = 唯一坐标事实源,后续所有出图都贴它作参考锚 |
|
||||
| **对位精度** | 出图后由**脚本按坐标表合成** | 合成图与方向稿并排,逐件锚点误差 **≤2px**;物件层边缘**无 1px 半透明毛边**(硬边/无抗锯齿纪律) |
|
||||
| **坐标表格式** | 实现批定(建议走代码常量单点,与 `UiKit.TEX_*` 同处;或 `.tres` 落 `data/tables/`,按数值单一事实源口径) | 表必须含四列:**物件键 / 贴图锚点 (x,y) / 命中矩形 (x,y,w,h) / z 序** |
|
||||
| **背景空档** | 背景层自己承担 | 背景层画全(含门洞、地面、围栏完整),物件层只是叠上去,移走任何一件背景都不露洞 |
|
||||
|
||||
**坐标草案 v3(2026-10-22 · 作者已接受)**
|
||||
|
||||
口径(用户 2026-10-22 亲自定):**只有最左、最右两件往边上推;不靠边的那些不许贴边**——它们按「离画面中心的距离」成比例往外挪一点点(最外挪 ~80,第二圈 ~40,中轴 0)。**物件尺寸一律不变**,拉开的是它们之间的**间隔**。
|
||||
|
||||
做法:**以中心线 x=640 为原点等比外扩,k=1.20**,只改 x、不动 y 与尺寸。v2「所有物件一律贴边」的做法已被否决作废(把非边缘件也拽到边上,无故且无意义)。
|
||||
|
||||
| 物件 | 层 | v1 基线 x | **v3 命中矩形 (x, y, w, h)** | 外移量 | z 序 | 可点 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 锻炉·铁砧(最左) | L4 | 120 | **`(40, 420, 240, 240)`** | −80 | 3 | 是 → 锻造工坊 |
|
||||
| 木桩 | L3 | 380 | **`(340, 520, 120, 180)`** | −40 | 3 | 是 → 天赋树 |
|
||||
| 篝火 | L8 | 560 | **`(560, 560, 160, 160)`** | 0 | 5 | 是 → 离开营地 |
|
||||
| 大门(门扇) | L2 | 520 | **`(520, 250, 240, 220)`** | 0 | 2 | 是 → 冒险 |
|
||||
| 布告板 | L1 | 800 | **`(845, 330, 130, 140)`** | +45 | 1 | 是 → 图鉴 |
|
||||
| 笔记本 | L7 | 770 | **`(818, 690, 100, 80)`** | +48 | 4 | 是 → 生涯记录 |
|
||||
| 帐篷 | L5 | 880 | **`(958, 520, 300, 170)`**(待重定) | +78 | 3 | **否**(承载件) |
|
||||
| 宝箱 | L6 | 1000 | **`(1085, 700, 130, 90)`** | +85 | 4 | 是 → 秘宝匣 |
|
||||
|
||||
配套约束(2026-10-22 更新):顶栏 HUD 保留带 = **`y ≤ 48`**(§9 ⑤⑦:一行量、浮层覆盖),八矩形全部落在其下方;中景围栏横贯约 `y ≈ 500`(画在背景层);**八个命中矩形两两不相交**(已逐对校验;y 方向分离的有:大门/篝火、布告板/帐篷、帐篷/宝箱);间隙最小的两处 = 笔记本右缘↔帐篷左缘 40px、锻炉右缘↔木桩左缘 60px。
|
||||
|
||||
**⚠ 唯一例外件(2026-10-22 作者裁)**:**篝火的「贴图矩形 ≠ 命中矩形」**——整张 `scene_camp_campfire.png` 仍画火堆+火焰,**命中矩形只取下半(火堆),上半=火焰只作特效展示、不可点**(防误点「离开营地」)。这是八个件里唯一两栏不一致的,**实现批坐标表必须用两列表达**(贴图矩形 / 命中矩形),其余七件两栏相同。具体分界数值**等成图后按实际画面切**(现在定死无意义)。火焰动效本次不做(作者令),过渡档=静态一张;将来要动效只需给这一层补帧(分层的收益)。
|
||||
|
||||
### 2.6 分层后,探针与断言怎么验「物件在正确的位置上」
|
||||
|
||||
分三层验,**前两层是机检主力,第三层是兜底**:
|
||||
|
||||
1. **几何断言(零渲染,最便宜)**——七件命中矩形两两不相交;每个矩形不越出 1280×800;不侵入顶栏 HUD 保留带(`y ≤ 48`,§9 ⑤⑦);最小命中尺寸 ≥ 64×64(小件笔记本允许 ≥48×48,并允许命中区大于贴图本身,外扩补齐);**篝火用命中矩形(火堆)判定,不用贴图矩形**。
|
||||
2. **alpha 采样断言(这一条是「位置正确」的可机检近似)**——取每个命中矩形的中心点,断言该点在**对应物件层**上是不透明像素、在**其它六个物件层**上是透明像素。物件贴图摆错位置、坐标表抄错行、z 序写反,这条都会红。
|
||||
3. **可达性断言(功能主力)**——七件逐一合成点击,断言进入对应界面(契约 2,卡④ V2);这一条验的是「点得到」,前两条验的是「图摆对了」。
|
||||
|
||||
**变异验证**(新增断言必须验证过它会红):把某个物件的坐标表改错一行 → 第 2 条断言红;把某物件的命中区删掉 → 第 3 条红。注入 → 见红 → 精确还原 → 记录。
|
||||
|
||||
### 2.7 一句话结论
|
||||
|
||||
**分层,8 张(1 背景 + 7 可点物件 + 床铺承载件共 8 张图),配一份四列坐标表;四态全部走程序化(提亮/位移/幽灵化),零四态素材;风格一致靠拼表制硬压 + 风格参考图 + 后处理统一三道。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 议题 3 · 生成工作流(从空白到样张过审)
|
||||
|
||||
> 「谁做」列:作者 = 用户本人;Agent = 我(脚本/文档);管线 = 远程对话式生图(作者自跑)。
|
||||
|
||||
| 步 | 做什么 | 谁做 | 产出物 | 完成判据 |
|
||||
|---|---|---|---|---|
|
||||
| **S0 布局稿** ✅**已销号 2026-10-22** | 按纵深稿把七件摆到 1280×800 上 | Agent 出稿 → **作者已裁** | 坐标表=本文 **§2.5 坐标草案 v3** | v3 经作者目检通过;改动须显式提出 |
|
||||
| **S1 方向稿**(下一步) | 开 **3 个独立对话**,贴项目自家定稿素材作风格锚,出**整图构图候选**(背景+七件全画,不分层) | 作者出图 | `artwork/2026-10-23-营地场景/direction/v1.png v2.png v3.png` | 每张登记**模型名 + 分辨率 + seed + 对话/日期**(缺登记不入遴选);分辨率取平台**最大 16:10**(不小于 1024×640);提示词=**§13** |
|
||||
| **S2 选划方向** | 作者目检三张,选一张并点名要改什么 | **作者** | 选划记录(选了哪张 / 改什么 / 淘汰另两张的理由) | 作者一句裁定 |
|
||||
| **S3 分层出图** | 按定稿构图出:① 背景 1 张(1280×800 幅面)② 物件拼表 1–2 张(七件同图,浅灰 `#F2F2F2` 均匀底,便于抠图);每件 2–3 版候选 | 作者出图 | `.../layers/bg_v1-3.png`、`.../layers/props_v1-3.png` | 同 S1 登记义务;拼表图物件的任何像素不越出自己的区域、无边框无文字;**提示词待 S2 后另起** |
|
||||
| **S4 后处理** | 抠底转透明 → 最近邻降采样到统一像素密度 → 色板量化到既有板 → 描边统一 → 按布局尺寸裁成 8 张 | Agent(脚本管线) | `.../layers/out/*.png`(8 张透明 PNG)+ 机检读数 | `palette_check.py <目录> --baseline <基线目录>` 无新增色红灯;`standee_asset_check.py` 单张 32 色、无非整数缩放痕迹、描边亮度达标 |
|
||||
| **S5 合成校验** | 按坐标表把 8 张合到 1280×800,与 S2 选中的方向稿并排;另出**四态演示图**(常态/悬停/按下/禁用四联) | Agent | `.../composite_check.png` + `.../states_preview.png` | 逐件锚点误差 ≤2px;七命中矩形两两不重叠(脚本先跑一遍);四态四联一眼可辨 |
|
||||
| **S6 样张过审** | 作者目检合成图与四态演示 | **作者** | 一句「过/不过 + 改什么」 | 过 = 进 S7;不过 = 回 S3(改提示词重出)或回 S1(方向错了) |
|
||||
| **S7 落盘与挂链** | 终稿拷入 `game/assets/art/scene/`;代码侧先加 `UiKit.TEX_CAMP_*` 常量再接线(终稿文件名 = 代码消费名) | 实现批(**本次不做**) | 8 张终稿 + 常量 + 坐标表落码 | 反哺点中的**文档部分已于 2026-10-23 执行完毕**(见 §11:art-bible §6.2/§7.5/§2.8/§2/§4.1 改版、camp.md 整体改版、asset-path-map §3 增 `scene/`);剩余为代码侧常量与落码 |
|
||||
|
||||
**出图分辨率纪律(像素硬规则)**:源档按**终稿的整数倍**出(建议 2×:背景 2560×1600 → 降采样到 1280×800;物件件按布局尺寸 ×2 出),降采样走最近邻(放大缩小不做平滑插值,保硬像素边)。**禁止非整数倍降采样**——甲类(语义件/环境结构件)严格整数倍。
|
||||
|
||||
**参考物到位后的处置**:作者找到参照物 → 与纵深稿对表;结构性冲突(比如参照的营地根本没有围栏/大门)→ **呈报订正构图,不在批内私改**(卡④ L3 条)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 命名与消费链(先定消费名,防断链)
|
||||
|
||||
命名按 art-bible §8.3:`<域>_<对象键>_<状态|变体>.png`,全小写 snake_case。新增语义子目录 `scene/`(现有仅 board/character/fx/ui)。
|
||||
|
||||
| 消费常量(草案,实现批落 `UiKit`) | 资源路径 | 可点 |
|
||||
|---|---|---|
|
||||
| `TEX_CAMP_BG` | `res://assets/art/scene/scene_camp_bg.png` | 否 |
|
||||
| `TEX_CAMP_GATE` | `res://assets/art/scene/scene_camp_gate.png` | 是 → 冒险 |
|
||||
| `TEX_CAMP_FORGE` | `res://assets/art/scene/scene_camp_forge.png` | 是 → 锻造工坊 |
|
||||
| `TEX_CAMP_CHEST` | `res://assets/art/scene/scene_camp_chest.png` | 是 → 秘宝匣 |
|
||||
| `TEX_CAMP_NOTEBOOK` | `res://assets/art/scene/scene_camp_notebook.png` | 是 → 生涯记录 |
|
||||
| `TEX_CAMP_DUMMY` | `res://assets/art/scene/scene_camp_dummy.png` | 是 → 天赋树 |
|
||||
| `TEX_CAMP_BOARD` | `res://assets/art/scene/scene_camp_board.png` | 是 → 图鉴(占位态) |
|
||||
| `TEX_CAMP_CAMPFIRE` | `res://assets/art/scene/scene_camp_campfire.png` | 是 → 离开营地 |
|
||||
| `TEX_CAMP_BED` | `res://assets/art/scene/scene_camp_bed.png` | 否(承载件) |
|
||||
|
||||
装载一律 `UiKit.load_tex`(素材不入 git,缺位要有回退),**禁 preload**(asset-path-map §2)。
|
||||
|
||||
> **状态(2026-10-23)**:本节命名与 `game/assets/art/scene/` 子目录**已随文档批写入 `asset-path-map` §3**(commit `47907625`),本节与该文件互为一致;常量 `TEX_CAMP_*` 仍待实现批落码。
|
||||
|
||||
---
|
||||
|
||||
## 5. 样张验收判据(QA 回头照这张表判)
|
||||
|
||||
| # | 判据 | 怎么判 |
|
||||
|---|---|---|
|
||||
| **Y1 色数** | ①**单张 PNG ≤32 色**(像素域硬闸,不变);②色板按 **§9⑥ 乙口径**:语义/UI token 48 不动,**每屏一套环境子板 ≤32 色、允许跨屏不共享** | `palette_check.py <目录> --baseline <基线目录>` + `standee_asset_check.py`;超了走「换色合并」,**禁加色绕行** |
|
||||
| **Y2 色域** | 吸色全扫 **无 HSV 300°–345°**(禁紫/洋红);主色域 = 烛光黄 `#F6C667` × 木色 `#8A5A34` 暖色相,中等饱和(饱和 ≤0.55) | 脚本吸色统计;建议暖色相像素占比判据由作者定一档 |
|
||||
| **Y3 新色双门** | 新增色仍过两门:① 不占语义色段(红只说危险/金只说收益/蓝只说己方)② 与既有任一色 RGB 三通道差 ≥8/255。**注**:按乙口径,新增的**环境色**归所属屏的环境子板,**不再挤占 48 token 额度** | `palette_check.py` |
|
||||
| **Y4 明度档** | 三档带成立:信息面层(顶栏 HUD/提示名牌)> 世界中层(七物件)> 世界底层(地面/远景),层间灰度可分;**明度序在渲染合成空间(伽马)排档**——按 CIE L* 线性排会把序排反(本项目实测教训) | 截图转灰度抽测 + 作者目检 |
|
||||
| **Y5 像素纪律** | 纹理过滤 Nearest;像素网格对齐、**硬边、无渐变、无抗锯齿**(放大 4× 目检无中间过渡色);无 1px 半透明毛边 | `standee_asset_check.py` + 放大目检 |
|
||||
| **Y6 物件可辨性** | 转灰度后七件剪影**两两可辨**(不混);每件在 1280×800 上最小外接矩形 ≥64×64(笔记本可 ≥40×40,命中区外扩补到 ≥48×48);七件均带亮暖白描边(§5.1 轮廓通道,禁黑描边) | 灰度图目检 + 脚本量外接矩形 |
|
||||
| **Y7 可点区域** | 七命中矩形**两两不相交**;不越出画面;**不侵入顶栏保留带(y ≤ 48)**;最小 64×64;中心点落在对应物件的不透明像素上且**不落在其它六件上**;**篝火按「命中矩形(火堆)」而非贴图矩形判定** | 脚本几何断言(实现批落探针) |
|
||||
| **Y8 对位** | 合成图与 S2 选中方向稿逐件锚点误差 **≤2px**;移走任一物件背景不露洞 | 并排对比 + 脚本差分 |
|
||||
| **Y9 风格一致** | 八张图 1× 像素密度一致(放大后色块大小相同)、描边同色同粗、色域同源 | 八件并排目检(机检不判气质) |
|
||||
| **Y10 文字与结构** | 顶栏/HUD/提示名牌文字对直接底 **≥4.5:1**(大字 ≥3:1);围栏/门柱/石板等结构**必须是画出来的结构件**,禁以渐变/滤镜伪造(§8.5) | 对比度实测 + 放大目检 |
|
||||
|
||||
**不机检、归作者目检的三项**:构图气质(纵深稿味道对不对)、七件一眼找得到(可辨性终审)、四态反馈手感。
|
||||
|
||||
---
|
||||
|
||||
## 6. 待裁清单状态(2026-10-22 更新)
|
||||
|
||||
**已销号**:
|
||||
|
||||
1. ✅ **渠道** = B 插画级新产(§1.2)|卡④ P1 销号
|
||||
2. ✅ **规格口径** = 沿用 `art-bible` §8.1/§8.2 既有裁决,营地不单开例外(§1.2 ⚠ 条)
|
||||
3. ✅ **参照物** = 讨论阶段不需要,S1 开工前由我提醒(§9 ⑧)
|
||||
4. ✅ **门扇形态** = 半开(§9 ①)
|
||||
5. ✅ **篝火命中区** = 只含火堆不含火焰;火焰动效本次不谈(§9 ②)
|
||||
6. ✅ **顶栏** = 一行量、高 48px、浮层覆盖、素材仍 1280×800(§9 ⑤⑦)
|
||||
7. ✅ **S0 布局坐标** = v3 已目检接受(§2.5)——后续改动须显式提出,会连带改对位/命中区断言
|
||||
8. ✅ **色板口径** = 乙 · 分层(§9 ⑥)
|
||||
|
||||
**仍待裁 / 待补**:
|
||||
|
||||
1. ~~顶栏形态~~ ✅ 已定=全宽窄条 48px(§9 ⑦)
|
||||
2. ~~名牌文案七条~~ ✅ 已定(§9 ③)
|
||||
3. ~~门洞远景画不画小路~~ ✅ 已定=画(§9 ①)
|
||||
4. ~~色板加不加新色~~ ✅ 已定=允许加;额度不足时呈报作者压缩(§9 ⑩)
|
||||
5. ~~禁用态给不给~~ ✅ 已定=给,仅锻炉与宝箱,表现=变暗+灰度(§9 ⑨)
|
||||
6. **篝火「命中矩形」的具体数值** —— 规则已定(只取下半=火堆,上半=火焰只展示),**数值归实现批按成图裁切定**
|
||||
7. ✅ **P5 落文的文档部分已于 2026-10-23 执行完毕**(§11,commit `47907625`):camp.md 整份重写、art-bible §2/§2.8/§4.1/§6.2/§7.5/§7.6 改版。**剩余=代码侧**(`UiKit.TEX_CAMP_*` 常量与落码)
|
||||
8. ✅ ~~**§8 提示词草稿**~~ → **已由 §13 取代**:旧稿被删(与已定裁定冲突),**新提示词 v2 已按全部定稿裁定重新起草**。
|
||||
9. **下一步=S1 出方向稿**(作者跑生图)→ S2 作者选划 → 反馈改词 → 再出。当前等作者的预览图。
|
||||
|
||||
---
|
||||
|
||||
## 9. 讨论裁定记录(2026-10-22)
|
||||
|
||||
> **性质**:讨论段决定,尚未落实任何素材与代码。**作者 2026-10-22 令=先写进本文档、暂不提交**,待本轮议题全部结束后统一处置(会话内容不留存,只认文件)。
|
||||
> 已并入上文的:渠道=B(§1)、规格=沿用既有口径(§1.2)、坐标=v3(§2.5)、篝火贴图/命中两分(§2.5 末)。
|
||||
|
||||
| # | 裁定 | 连带影响 / 待办 |
|
||||
|---|---|---|
|
||||
| ① | **门扇=半开** | 与既有结构一致(背景画门柱+门洞、门扇叠在洞上)。**已定(2026-10-22)**:门洞=营地大门的门洞(半开门扇透出处),**画一条通向远处的小路**,暗示「出去即冒险」 |
|
||||
| ② | **篝火命中区只含火堆、不含火焰** | 篝火=唯一「贴图矩形 ≠ 命中矩形」的件,坐标表须两列表表达;**火焰动效本次不谈**(作者令),过渡档=静态一张 |
|
||||
| ③ | **悬停反馈=轮廓亮边 + 浮动 + 名牌**(2026-10-22 作者更正:**不是整体提亮**,是给轮廓加亮边) | 亮边=按物件层 alpha 外扩 1px 派生(零手绘、零新素材);**浮动⚠必须整像素吸附**(每帧 `round()`,否则 Nearest 下边缘抖/糊,须写成验收项),幅度 **±2px、周期约 0.5s**(作者认可);**名牌**=物件**下方 8px 水平居中**(作者认可),字号走 §7.4 tooltip 档 14px、对比 ≥4.5:1、不压物件本体;**文案七条**:开始冒险/锻造工坊/秘宝匣/生涯记录/天赋树/奖励格图鉴/离开营地(取自现役界面名) |
|
||||
| ⑨ | **禁用态=变暗 + 调灰度**(2026-10-22 作者裁) | ⚠ **作者特许破除 `art-bible` §2 明度禁令**(原「不动明度」、§6.2「亮起只走饱和/色相」)与 §7.6 按钮禁用态(α0.45 幽灵化)——**本项须登记为反哺点,由文档批改条款,不私下改规矩**。适用范围:**只有锻炉(锻造工坊)与宝箱(秘宝匣)两件**会进入禁用态(依据 `camp_overlay_view.gd:111`「🔒 锻造工坊未开放」、`:25-31` 两柜台锁定态行);**其余五件恒可点**;布告板(图鉴)是内容冻结未激活,**不是锁定**,不做禁用态 |
|
||||
| ⑩ | **色板:允许新增颜色**(2026-10-22 作者裁) | 过渡档**不再一律压回既有色板**;按 §9⑥ 乙口径加**环境色**(归每屏环境子板)。**额度不够时呈报作者**,由作者决定如何压缩既有色 |
|
||||
| ④ | **篝火「离开营地」必须二次确认** | ⚠ **改判**:推翻卡④ §9-4 既有代拍板(原「默认直回」),须登记为反哺点;实现复用**删档二次确认**的既有形态(澄清⑤裁决 3),零新控件 |
|
||||
|
||||
| ⑤ | **顶部信息只保留「一行的量」;顶栏高度=48px**(2026-10-22 作者裁,重要) | 内容 = 钥石图标 32px(64 源 ×0.5 显示档,asset-path-map §2)+ 余额文字一行(FS_BODY 16);**不加第二行、不放大按钮**。48 = 32 + 上下各 8,8/16 整数倍、图标在条内居中(备选 44=与现役 AP 条同高,未采用)。作废的 64/96/80/72 见本节末留痕 |
|
||||
| ⑥ | **色板=乙 · 分层口径**(2026-10-22 作者裁) | 语义/UI token 48 不动;**新增「每屏环境子板」:每屏一套 ≤32 色、允许跨屏不共享**;单张 PNG ≤32 色这条不变。落实需走文档批改 `art-bible` §4.1 措辞(反哺点) |
|
||||
| ⑦ | **顶栏=浮层覆盖在画面上层,形态=全宽窄条,高 48px,素材仍 1280×800,不挤压、不缩放、不变形**(2026-10-22 作者裁) | v3 坐标零影响(最高物件大门 y=250)。禁区判定:全宽 `y ≤ 48` |
|
||||
| ⑧ | **参照物=讨论阶段不需要**(2026-10-22 作者裁) | 由 Agent 在「开始生成美术素材(S1)」**之前**提醒作者;不是本讨论阶段的阻塞项 |
|
||||
|
||||
**待办**:~~§8 提示词草稿~~ **已整段删除**(2026-10-22 作者令:草稿与已定裁定冲突,后续专门讨论生成时重新起草)。
|
||||
|
||||
**已作废的推导(留痕)**:曾按「占屏高百分比」比照 SNES《超级马里奥世界》(40/224=17.9%)与 Deltarune 幻想主机(16/144=11.1%),推出顶栏 96px——**作者否决**:取样太古老、像素尺度不同、无参考价值。改用近代像素 Roguelike/幸存者 like 后作废该结论(详见上表 ⑤)。
|
||||
|
||||
## 11. 既有条款修订清单(2026-10-22 核定,**2026-10-23 已执行**)
|
||||
|
||||
> **执行结果**:worktree `D:/code/g-plan2-camp-art-doc`(分支 `camp-art-doc-20261023`)→ commit `569db112` → **`git merge --no-ff` 合回 master = `47907625`**;**文档域闸 `tools/run_all_gates.sh --docs` 全绿**(闸况与基线一致、无新增红;过程中修掉 1 处死链:`HUD框架` 指路 camp.md 应为 `../../ux/camp.md`)。主工作区 status CLEAN。
|
||||
>
|
||||
> 本轮讨论的裁定与 `design/` 既有条款冲突者共 **10 项**(另加 GDD 侧「柜台」用词 9 处,以及补扫出的 `元进度.md` 5 处、`HUD框架.md` 2 处)。作者令:**逐条定改法 → 随本会话文档一并修改并提交**(走 worktree,不碰主区)。下表为**改法记录**,条文已按此改完。
|
||||
|
||||
| # | 条款 | 改法(作者 2026-10-22 裁) | 状态 |
|
||||
|---|---|---|---|
|
||||
| 1 | `art-bible` §2 头注明度禁令 | **缩窄范围**:删去「状态表达」这个禁止项,只保留「情绪表达」;**不设例外条款** | ✅ 措辞已定,见下 |
|
||||
| 2 | `art-bible` §2.8 菜单与元进度 | **删旧写新**:「营地三柜台一字排开」整句换成七物件可点场景图 | ✅ 措辞已定,见下 |
|
||||
| 3 | `art-bible` §4.1 色板 | **新增一款**(原文不动):色板分层=语义/UI token 48 + 每屏环境子板 ≤32 | ✅ 措辞已定,见下 |
|
||||
| 4 | `art-bible` §6.2 营地场景 | **整条重写**:七物件场景图 + 分层 + 四态 + 顶栏 | ✅ 措辞已定,见下 |
|
||||
| 5 | `art-bible` §7.5 营地界面 | **整条重写** | ✅ 措辞已定,见下 |
|
||||
| 6 | `art-bible` §7.6(一)禁用态 | **加一句注**:本条管 UI 按钮,场景物件另按 §6.2 | ✅ 措辞已定,见下 |
|
||||
| 7 | `camp.md` | **整份重写**(场景图 + 七物件点选 + 顶栏 + 叠层) | ✅ 措辞已定,见下 |
|
||||
| 8 | `asset-path-map` §3 | **加一行**:新增 `scene/` 子目录 | ✅ 措辞已定,见下 |
|
||||
| 9 | `art-assets-checklist` §3.5(含 :175) | **整条重写**为营地场景图 | ✅ 措辞已定,见下 |
|
||||
| 10 | `art-status-checklist`:32 | 条目名改「营地场景图」,依据改指 §6.2 | ✅ 措辞已定,见下 |
|
||||
| 附 | GDD 侧「柜台」用词 9 处 | **直接更正为新的营地说法**(作者令:不必逐条裁) | ✅ 措辞已定,见下 |
|
||||
|
||||
### 第 1 条 · `art-bible` §2 头注
|
||||
|
||||
**现行**:「`禁令保留`:明度变化仍禁止用于**状态表达与情绪表达**——状态区分只允许**色相、饱和、运动、元素**四手段;roguelike 失败重开,世界无光暗记忆;Darkest Dungeon 式压暗仍在 §9.2 反面清单。」
|
||||
|
||||
**改为**:「`禁令保留(2026-10-22 缩窄至情绪域)`:明度变化禁止用于**情绪表达**——画面不得靠压暗/渐晕阴沉下来;roguelike 失败重开,世界无光暗记忆;Darkest Dungeon 式压暗仍在 §9.2 反面清单。**状态表达不受本禁令约束**:状态可用明度、色相、饱和、运动、元素任一手段承载。」
|
||||
|
||||
(同段其余不动:明度三档带分层、空间纵深/光照氛围/形体塑造允许域、§4.2 类型微光明度梯度一句,全部原样保留。)
|
||||
|
||||
**影响核查**:现有冒烟断言里编码「禁用态 α0.45」的只有 UI 按钮(`smoke_all.gd:6526/6551/6563`,归 §7.6),**没有**任何断言编码「明度禁用于状态」→ 本条改动不牵动现役断言。
|
||||
|
||||
### 第 2 条 · `art-bible` §2.8 菜单与元进度(目标措辞)
|
||||
|
||||
**现行(最后一行)**:`承载元素`:营地三柜台(属性锻造/秘宝匣/奖励格图鉴)在烛光下一字排开——场景构成、柜台亮光与钥石呈现位连同系统语义边界一并见 §6.2
|
||||
|
||||
**改为**:`承载元素`:**一张可点击的营地场景图**——远景天空与远山,中景围栏与半开的营地大门,近景中央的篝火,左侧锻炉铁砧,右侧床铺与宝箱、床边的笔记本,另有木桩与挂在围栏上的布告板;点画面里的物件进入对应界面(冒险/锻造工坊/秘宝匣/生涯记录/天赋树/图鉴/离开营地)。钥石余额常驻画面顶部的一行小 HUD 栏。场景构成、七物件绑定与呈现位见 §6.2
|
||||
|
||||
(本条其余四行——主情绪「可靠的营地感」、色彩性格「全作最暖烛光黄与木色」、氛围形容词、能量 3/10——**全部保留不动**。)
|
||||
|
||||
### 第 3 条 · `art-bible` §4.1 色板(新增一款,原文不动)
|
||||
|
||||
在 §4.1「色板增量纪律」之后**新增**一款(原文全部保留):
|
||||
|
||||
> **色板分层(2026-10-22 作者裁)**:色板分两层管理——① **语义/UI token 板**:全作共享,硬上限 **48 色**,管 UI 语义色、墨色、材质色,跨屏恒定;② **场景环境子板**:**每屏一套,≤32 色,允许跨屏不共享**(环境色=天空、地面、植被、石材、火焰等随场景走的颜色)。新增环境色仍须过两道门(§4.2 语义检查 + 与既有任一色 RGB 三通道差 ≥8/255),但**不占用 48 token 额度**;不过门照旧走「换色合并」,**禁加色绕行**。单张 PNG 的像素域色数上限 **≤32 色**另由 `standee_asset_check` 执法(像素域口径,与本表 token 口径互不干扰)。额度不足时呈报作者,由作者决定压缩或合并既有色。
|
||||
|
||||
### 第 4 条 · `art-bible` §6.2 营地场景(整条重写)
|
||||
|
||||
**现行**:三柜台一字排开 + 柜台=石板圆角容器 + 「亮起只走饱和/色相,不动明度」+ 事实源边界。
|
||||
|
||||
**改为**:
|
||||
|
||||
> ### 6.2 营地场景
|
||||
> - **情绪与色域**:承载 §2.8——全作最暖:烛光黄 `#F6C667` × 木色 `#8A5A34`(§7.2),中等饱和,无棋盘式高对比。
|
||||
> - **场景构成**:一张可点击的营地场景图(1280×800,与工程档同幅面)。远景=天空与远山;中景=木围栏与**半开的营地大门**(门洞内一条通向远处的小路);近景=中央篝火、左侧锻炉铁砧、右侧床铺与宝箱、床边的笔记本,另有木桩与挂在围栏上的布告板。七个可点物件的功能绑定归 `camp.md`,本节只定画法。
|
||||
> - **交付形态**:**背景 1 层 + 七个物件各 1 层**(床铺为承载件、不可点),共 8 张透明 PNG + 一份坐标表(贴图矩形/命中矩形/z 序)。**篝火例外**:命中矩形只取下半(火堆),上半(火焰)只作特效展示。
|
||||
> - **交互四态(零新素材,全部程序化)**:悬停=**轮廓亮边 + 浮动**(浮动须整像素吸附);按下=位移 1px;**禁用/未解锁=降明度 + 去饱和**(§2 已缩窄至情绪域,状态表达不受限),**仅锻炉(锻造工坊)与宝箱(秘宝匣)两件会有此态**;已解锁=常态。
|
||||
> - **钥石余额**:常驻画面顶部的一行小 HUD 栏(高 48px,**浮层覆盖,不挤压画面**)。
|
||||
> - **事实源边界**:解锁条件/价格/存档写入归元进度 GDD 与 `MetaProgression`;素材命名与路径归 `asset-path-map`;清单登记归 `art-assets-checklist` §3.5。
|
||||
|
||||
### 第 5 条 · `art-bible` §7.5 营地界面(整条重写)
|
||||
|
||||
> - 营地界面=**一张可点击的场景图 + 七物件点选**:点画面里的物件进入对应界面(冒险/锻造工坊/秘宝匣/生涯记录/天赋树/图鉴/离开营地)。场景图的画法、分层与四态见 §6.2;色域走 §7.2 最暖一档;钥石余额常驻顶部一行小 HUD 栏。界面视觉终稿以表现层样张过审为前置(§1.4 整屏样张闸)。
|
||||
|
||||
### 第 6 条 · `art-bible` §7.6(一)按钮禁用态(加一句注,原文不动)
|
||||
|
||||
在「禁用 = 本族底 + α0.45 幽灵化(甲案)」条目末**追加**:
|
||||
|
||||
> (注:本条管 **UI 按钮**;营地场景图里的**物件**禁用态另按 §6.2——降明度 + 去饱和,不走 α 幽灵化。)
|
||||
|
||||
### GDD 侧「柜台」用词清理(一并改,共 9 处)
|
||||
|
||||
| 文件:行 | 现行 | 改为 |
|
||||
|---|---|---|
|
||||
| `术语表.md:43` | 秘宝匣|营地**柜台之一**:… | 秘宝匣|营地**功能界面之一**(场景图里点**宝箱**进入):… |
|
||||
| `术语表.md:56` | 锻造工坊|营地**四柜台之一**:… | 锻造工坊|营地**功能界面之一**(场景图里点**锻炉·铁砧**进入):…(顺带消除 43/56 条"三柜台 vs 四柜台"自相矛盾) |
|
||||
| `02-系统清单.md:82` | camp.md|营地覆盖层 UX spec:**三柜台呈现**、钥石余额… | camp.md|营地 UX spec:**可点击场景图与七物件点选**、钥石余额、各功能界面入口… |
|
||||
| `08-Alpha版本标准.md:106` | 营地(天赋树/锻造工坊/秘宝匣/图鉴)…锻造**柜台**占位面板注已解除;**柜台贴图**占位… | 营地(**场景图** + 天赋树/锻造工坊/秘宝匣/图鉴/生涯)…**场景图与七物件点选**随营地交互重做列入 Alpha… |
|
||||
| `08-Alpha版本标准.md:200` | …**营地柜台贴图**已随营地交互重做列入 Alpha | …**营地场景图**已随营地交互重做列入 Alpha |
|
||||
| `05-不做清单.md:22` | …首批已落地(营地**三柜台**/钥石/跨局注入) | …首批已落地(**营地**/钥石/跨局注入) |
|
||||
| `01-核心循环.md:156` | …`unlock_requires` 字段 + **工坊柜台** + 里程碑分档发槽 | …`unlock_requires` 字段 + **锻造工坊界面** + 里程碑分档发槽 |
|
||||
| `03-内容规模.md:59` | 扩**柜台**或改定价结构走… | 扩**锻造路线**或改定价结构走… |
|
||||
| `ux/level-clear.md:64` | …**营地柜台全量** | …**营地界面全量**(禁入语义不变) |
|
||||
|
||||
(`architecture/code-architecture.json:122` 与 `.html:5273-5284` 的「商品卡·三柜台」是**脚本生成的架构图节点副标签**,随实现批代码变更重生成,不在本次文档批改。)
|
||||
|
||||
### 第 7 条 · `camp.md`(整份重写)
|
||||
|
||||
**现行**:通篇是「四柜台覆盖层 + 石纹全屏底 + 三柜台列 + 底栏关闭钮 + 秘宝匣柜台呈现细则」。整体作废,替换为:
|
||||
|
||||
```markdown
|
||||
# 营地场景 UX spec(camp)
|
||||
|
||||
> 状态:v2.0(2026-10-22 营地交互重做美术设计段;取代旧「四柜台覆盖层」形态)
|
||||
> 事实源:本 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)(主菜单语境)
|
||||
|
||||
## 定位
|
||||
营地=存档内的局外枢纽(存档语境可达,冒险进行中不可达)。形态=**一张可点击的营地场景图**:画面里有篝火、锻炉、床铺等物件,点物件进对应界面。**不是按钮列表式 UI。**
|
||||
|
||||
## 场景构成与七物件绑定
|
||||
| 物件 | 进入 | 备注 |
|
||||
|---|---|---|
|
||||
| 大门(半开,门洞内一条通向远处的小路) | 冒险 | 点大门永远先出确认:有未完冒险=三出口(继续上一段/开始新冒险/取消);无=是否开始新冒险 |
|
||||
| 锻炉·铁砧 | 锻造工坊 | **未解锁=禁用态**(降明度+去饱和) |
|
||||
| 宝箱 | 秘宝匣 | **未解锁=禁用态**(同上) |
|
||||
| 床铺旁笔记本 | 生涯记录 | 生涯页返回钮文案**禁含「营地」字样**(`ui_struct_probe.gd` nojump 禁词表) |
|
||||
| 木桩 | 天赋树 | |
|
||||
| 布告板 | 奖励格图鉴 | 内容冻结未激活,点进看占位——**不是禁用态** |
|
||||
| 篝火 | 离开营地回主菜单 | **必须二次确认**;命中区只含火堆、不含火焰 |
|
||||
| 床铺 | — | 承载件,**不可点** |
|
||||
|
||||
纵深=远景天空与远山/中景围栏与大门/近景篝火与左右物件(作者纵深稿)。
|
||||
|
||||
## 点选反馈(根态)
|
||||
悬停=**轮廓亮边 + 浮动**(浮动须整像素吸附)+物件**名牌**(物件下方 8px 居中,FS_HINT 14,对比 ≥4.5:1);按下=位移 1px;禁用=降明度+去饱和;已解锁=常态。**四态零新素材**(全部程序化)。
|
||||
命中区:七件两两不相交、不越出画面、**不侵入顶栏禁区(y ≤ 48)**。
|
||||
|
||||
## 顶栏
|
||||
钥石余额常驻画面顶部一行小 HUD 栏(高 48px、全宽窄条、**浮层覆盖在场景图上层,不挤压画面**)。刷新入口=营地刷新单口(卡⑤同口挂接)。
|
||||
|
||||
## 叠层与开合
|
||||
各界面开合走模态栈单口,层序入 `LAYER_CONTRACT`。**承载形态(独立全屏页 vs 单一内页切换)与叠层规则归卡④ §9 代拍板域,本 spec 不预设值**;派生规则:独立全屏页=场景根隐藏/单一内页切换=场景根留底(「缩放虚化」为第三形态,禁批内自选)。ESC 浮窗(返回主菜单/设置/退出游戏)归卡⑤。
|
||||
|
||||
## 口径
|
||||
- 呈现数据源=MetaProgression 只读投影经组合根喂入;本层不持有系统资源、不算价。
|
||||
- 「最近冒险」条带已撤下(卡④ Q4=甲),逐局历史回看归生涯。
|
||||
- 旧「秘宝匣柜台呈现细则」随形态作废;秘宝匣界面内部呈现(解锁面+同调装配段)保留原条款,迁本文件附录暂管,待该界面立独立 spec 后迁出。
|
||||
```
|
||||
|
||||
### 第 8 条 · `asset-path-map` §3(加一行)
|
||||
|
||||
| 子目录 | 内容 | 现役 |
|
||||
|---|---|---|
|
||||
| `scene/` | **场景图**(背景层 + 可点物件层;2026-10-22 新增,营地场景图首批) | `scene_camp_bg`/`_gate`/`_forge`/`_chest`/`_notebook`/`_dummy`/`_board`/`_campfire`/`_bed`(九张,床铺=承载件不可点) |
|
||||
|
||||
### 第 9 条 · `art-assets-checklist` §3.5(整条重写,含 :175 子句)
|
||||
|
||||
> ### 3.5 营地场景图(2026-10-22 重写;原「营地与元进度件·三柜台」条目作废)
|
||||
> - **要什么**:营地场景图 9 张——`scene_camp_bg`(背景)+ `scene_camp_gate / forge / chest / notebook / dummy / board / campfire`(七个可点物件)+ `scene_camp_bed`(承载件)。钥石顶栏图标沿用现役 `icon_keystone`。
|
||||
> - **来源/规格**:**✦ 插画级**(作者自跑远程生图管线);烛光黄 `#F6C667` + 木色 `#8A5A34`(art-bible §6.2);2× 源档出图、最近邻降采样到 1280×800;后处理=抠底/降采样/色板量化/描边统一。**每张出图须登记模型名+分辨率+seed+对话/日期**。
|
||||
> - **排期**:营地交互重做**美术自立批**(不占迭代位;UI 贴图重构批已搁置)。终稿落 `game/assets/art/scene/`,消费常量 `UiKit.TEX_CAMP_*`。
|
||||
|
||||
(`:175` 原句「营地件(3.5)归 UI 贴图重构批」→ 改为「营地场景图(3.5)归营地交互重做美术自立批」。)
|
||||
|
||||
### 第 10 条 · `art-status-checklist`:32
|
||||
|
||||
`| 营地与元进度件 | ◐ | art-bible §4.4 |` → `| 营地场景图 | ◐ | art-bible §6.2 |`
|
||||
|
||||
---
|
||||
|
||||
## 12. 营地场景 · 分层元素描述表(2026-10-23)
|
||||
|
||||
> 用途:跳过外部参照物后的**替代输入**——先把"脑里的画面"落成文字,既是给作者确认,也是写提示词的直接依据。远景/中景/近景三层齐全。
|
||||
|
||||
**时间**:**白天(晴朗)**——推导依据:① 美术基调「明亮轻快」;② §2 明度禁令缩窄后**情绪/氛围仍禁压暗**,夜间必然压暗即违规;③ 篝火作营地炊火,白天点着成立,不冲突「全作最暖」。
|
||||
**天空元素**:**不画太阳**(§3.4 焦点预算:首焦唯一,背景禁构成完整凸形、禁抢焦);允许 **1–2 朵极简扁平云**,低对比,只为天空留层次。
|
||||
|
||||
### A. 背景层(1 张,1280×800,不透明全幅)
|
||||
|
||||
**层次分带**:远景=画面上部;**中景自底部往上约 2/3 处起**(≈ y 267);再往下全部是近景。
|
||||
**近景覆盖中景(2026-10-23 作者口径)**:中景围墙**大部分只是背景板**(与远景同为填空),**允许近景物件放大后压住、遮住围墙**;**唯一例外=中间门洞必须留空可见、不被任何物件遮挡**(门是可点件)。
|
||||
|
||||
| 层次 | 元素 | 形态描述 | 画面位置 |
|
||||
|---|---|---|---|
|
||||
| 远景 | 天空 | 平涂,**冷调灰白、微带蓝灰**,禁米黄/奶黄倾向;**顶部约 48px 保持空旷**(顶栏覆盖);无太阳,可有 1–2 朵极简扁平云 | y 0–200 |
|
||||
| 远景 | 远山 | 2–3 层低矮起伏**剪影**,越远越淡(明度走**世界底层档**);**不画远树**——树形并入山轮廓 | y 180–260 |
|
||||
| **中景** | **木围墙** | 横贯全宽的**密排竖木桩墙**:木桩一根挨一根无缝、**桩顶削尖**、完全挡住外面;无横木、不透缝;**中段留缺口**给大门 | y ≈ 267–310;缺口 x 520–760 |
|
||||
| **中景** | **大门(门柱 + 门洞)** | 缺口两侧各一根粗木柱,柱顶略高出围栏;门洞内透出远景,一条向远处收缩的土路(暗示"出去即冒险") | 门柱 x ≈ 500–520 / 760–780;门洞 y 267–310(原 300–500 随分带作废)|
|
||||
| 近景 | 地面 | **干实裸土**;硬边色块,无渐变;**近景内不画任何草** | y 310–800 |
|
||||
|
||||
### B. 物件层(8 张,透明 PNG)
|
||||
|
||||
| 层次 | 物件 | 形态描述(识别特征) | 位置/大小(v3 坐标) | 可点 |
|
||||
|---|---|---|---|---|
|
||||
| **中景** | 大门**门扇** | **两扇半开**的木门,向外敞开一条缝;厚木板 + 横撑 + 深色铁件(门柱与门洞在背景层) | `(520,250,240,220)` | 是 → 冒险 |
|
||||
| **中景** | 布告板 | 挂**在围栏上**的木板,钉 2–3 张**空白**纸,纸角微翘 | `(845,330,130,140)` | 是 → 图鉴 |
|
||||
| 近景 | 锻炉·铁砧 | 石砌炉体 + 短烟囱,炉口透橙光;炉前一具深色铁砧(矮墩 + 平顶 + 一角外伸);**两件纵向紧邻、可互相遮挡,读起来是一个物件**(2026-10-24 作者口径);**主体落在近景地面**,顶部略高于中景分带线 | `(40,420,240,240)` | 是 → 锻造工坊(**可能禁用态**) |
|
||||
| 近景 | 木桩 | 单根木柱立在地面,中段缠草绳/布垫,顶端略圆 | `(340,520,120,180)` | 是 → 天赋树 |
|
||||
| 近景 | 篝火 | 灰石围成一圈,中间交叉架柴;上方橙黄火焰(**上半=火焰只展示,不可点**) | `(560,560,160,160)` | 是(**仅下半火堆**)→ 离开营地 |
|
||||
| 近景 | 帐篷 | 帆布帐篷 + 矮木架 + 一侧掀起的门帘(2026-10-24 作者裁:床铺换代);**体量大于原床铺,尺寸待重定** | `(958,520,300,170)`(待重定) | **否**(承载件) |
|
||||
| 近景 | 宝箱 | 闭合的木箱,深色铁箍包边,正面一个小锁扣 | `(1085,700,130,90)` | 是 → 秘宝匣(**可能禁用态**) |
|
||||
| 近景 | 笔记本 | 摊开的小册子,平放在靠床头的位置,露出纸页 | `(818,690,100,80)` | 是 → 生涯记录 |
|
||||
|
||||
> **中景只有两样东西:木围栏(背景层)+ 营地大门(门柱门洞在背景层,门扇是物件层)**,另加挂在围栏上的布告板(物件层)。其余全部属于近景。
|
||||
>
|
||||
> **待裁(2026-10-23)**:中景带按 2/3 分带收窄到约 43px 后,门扇 `(520,250,240,220)`(220px 高)远高于围墙带,门与墙的高宽关系需作者重定(门洞须留空、不可被近景物件遮挡)。
|
||||
|
||||
### C. 全画面通用约束
|
||||
|
||||
- **视角**:正侧平视(orthographic),所有物件同一侧角,**无透视变形**
|
||||
- **光照**:均匀,无长投影;每个物件正下方**只一个小的深色接地影**(不是情绪压暗)
|
||||
- **色域骨架**:**指路**——营地色彩与氛围的唯一依据 = `reports/提案-营地色彩与氛围脚本-2026-10-23.md`(本节**不复述色值**,避免双处漂移);物件本色沿用 §4.1 既有三色:烛光黄 `#F6C667`/木色 `#8A5A34`/掩尘 `#C9AF83`
|
||||
- **禁用**:无文字/字母/数字/水印;无边框;无角色与生物;无紫或洋红;无渐变、无抗锯齿;无暗角渐晕
|
||||
|
||||
---
|
||||
|
||||
## 13. S1 方向稿 · 生图提示词 v7(2026-10-24 定稿,取代 v6)
|
||||
|
||||
> 依据=§12 分层元素描述表(已确认)。**整段可贴,不需拼装、不需读其他文件。**
|
||||
|
||||
```
|
||||
Pixel art style. True pixel art game scene drawn with chunky square pixels, strict pixel grid alignment, hard edges, no anti-aliasing, no gradients, flat colors only, a limited palette of about 24 colors, retro 16-bit look, Aseprite style, bright cheerful daytime fantasy, all ages, flat side view, orthographic, no perspective distortion, every object drawn from the same side angle. Wide horizontal composition, 16:10 aspect ratio, showing a small adventurer camp on open dry bare ground in clear daylight, dry and settled and open in feeling. COLOR SCRIPT — cool recedes, warm advances: the large areas of the picture, meaning the sky, the distant hills and the ground, are COOL, pale, desaturated and low in contrast so that they sit back; warm color appears only on the small foreground objects, meaning the fire, the woodwork and the stone; the campfire flame is the single most saturated thing in the picture; the picture must never look golden, amber or yellow overall. ATMOSPHERIC DEPTH: every plane that is farther away is lighter, closer to the value of the sky, cooler, lower in contrast and simpler in detail, while the nearest plane is the darkest, the warmest and the highest in contrast. SKY (top of frame): the lightest thing in the picture, flat pale cool off-white with a faint blue-grey tint, no yellow or cream tone, no sun anywhere; the very top strip of the frame stays calm and empty with nothing in it, and it is only a shallow strip about one sixteenth of the frame height; at most one or two extremely simple flat low-contrast clouds. FAR DISTANCE: two or three layers of low rolling hill silhouettes in pale cool blue-grey, each farther layer lighter, cooler and lower in contrast than the one in front, fading towards the value of the sky, no trees, no individual foliage; the foot of the hills is cut off and hidden behind the wall, so there is no visible horizon line and no distant ground band, no distant grass or earth strip below the hills — between the hills and the wall there is nothing but the wall. MIDDLE GROUND: the base line where the logs meet the ground sits at two thirds of the frame height measured from the bottom edge, so the lower two thirds of the frame is foreground ground and only the upper third holds the hills, the sky and the clouds; at that height runs a wooden palisade wall across the whole width of the frame, built from closely spaced vertical logs standing side by side with no gaps between them, every log cut to a sharp point at the top, a solid barrier that completely blocks the view of the outside — no horizontal rails, no see-through gaps, no ranch-style post-and-rail fence; the wall is painted in a muted, greyed wood brown that is cooler and less saturated than the foreground woodwork, so that it sits back; in the middle of the wall one wide gap holds two thick gate posts slightly taller than the wall, and between them an opening showing a dirt path running away into the distance; in that opening hang two wooden gate leaves, half open, tilted outward, each made of thick planks with a cross brace and dark iron fittings; the gate opening stays completely clear, with nothing drawn in front of it. FOREGROUND GROUND: the darkest area of the picture, dry packed bare earth in a neutral desaturated grey-brown, never golden, amber or warm yellow, hard flat color blocks, hard edges, no gradient, no grass, no grass tufts, no weeds and no plants of any kind anywhere in the foreground. Seven separate objects stand on this ground, and each one is drawn LARGE — the foreground objects are big enough to overlap and cover long stretches of the palisade wall behind them, because the wall is only a backdrop and most of it is meant to be hidden, except the gate opening in the middle which must stay fully visible and unobstructed; the seven objects never overlap, touch or hide one another, except that the anvil may overlap the forge because the two are one single object: on the left side, one single forge object made of two tightly joined parts standing close together with only a small vertical offset: a stone forge with a short chimney and a warm orange glow in its mouth, and directly in front of it a dark iron anvil on a low block, the anvil standing close against the foot of the forge so the two read as one object rather than two separate things, the anvil allowed to overlap and be partly hidden behind the forge; this forge-and-anvil object is the largest object on the left, its left edge close to the left border; to its right and slightly lower, a single wooden training post wrapped with a straw rope pad around its middle; at the bottom center, a campfire made of a ring of grey stones around crossed stacked logs with a warm orange-yellow flame above them; on the right side, a canvas tent with a low wooden frame and a fabric flap tied back to one side, the largest object on the right, its right edge close to the right border; below and slightly in front of the tent, a small closed wooden trLine truncated
|
||||
```
|
||||
|
||||
### 出图设置(平台侧,不在提示词里)
|
||||
|
||||
| 项 | 设什么 |
|
||||
|---|---|
|
||||
| 张数 | **3 张,各开一个独立对话**(同对话续写=修图,不是变体) |
|
||||
| 分辨率 | 平台能选的**最大 16:10**(理想 1536×960 或 1280×800;不小于 1024×640) |
|
||||
| seed | 可取则取 |
|
||||
| 参考图 | 贴**项目自己的定稿素材**(主角立绘/瓦片纹理)作风格锚 —— 见下方追加句 |
|
||||
| 登记 | 每张记 **模型名 + 分辨率 + seed + 对话/日期**,随图存 `artwork/2026-10-23-营地场景/direction/`;缺登记不入遴选 |
|
||||
|
||||
### 贴参考图时的追加句
|
||||
|
||||
```
|
||||
Style reference: the attached image is this game's own finished pixel art — match only its rendering style, the same pixel density, flat shading, thin warm cream outline and warm limited palette. It is a style reference only: do not copy its character, gear or props, and do not put any character into the scene.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 本轮确立的协作纪律(2026-10-22~23,作者逐条立)
|
||||
|
||||
> 这些是**本轮会话里立下的工作方式**,此前只存在于会话与记忆中,现固化于此,供后续批与后续会话照做。
|
||||
|
||||
1. **会话不留存**:每次新会话只读得到文件。**一切决定必须落文档**,不能"说过了就算"。
|
||||
2. **无证据的推荐值不采纳**:给任何推荐值前先自检是否有项目文档/代码出处。**证据在会话里举给作者判断即可,不写进文档**(作者原话:「证据是用来干什么的?是给我判断用的…你写到文档里面给谁看」)。
|
||||
3. **布局口径(作者原话)**:说「两边太空、往两边移」=**只把最左、最右那两个组件推向边缘**;**不靠边的绝不允许贴边**,它们按离中心的距离成比例外移(最外移 80、第二圈 40、中轴 0),**组件尺寸不变**,拉开的是间隔。
|
||||
4. **作者说了算**:改既有规矩时,Agent 的义务=**指出该裁定破了哪条既有规矩 + 登记反哺点**,由文档批改条款;**不反对、也不私下改规矩**。
|
||||
5. **焦点纪律**:本次会话的目标=营地场景图「**美术素材怎么生成**」。凡**不影响出图、不影响分层、不影响提示词**的实现侧事项(如五界面承载形态、叠层规则、屏壳改造),**在文档里引用该域、不预设值**,绝不端到作者面前当"待定项"。
|
||||
6. **能推导的直接定**:能从已定设计推出来的,**直接定值、报作者同意**;只有上下文里确实没定过的才问。不要把所有未决事项一股脑端上去。
|
||||
7. **陈旧概念一律删净**:重新设计就是要替换掉旧的;凡与新设计冲突的旧说法(如「三柜台/柜台」),**全部删干净、用新设计取代**,不逐条问"删还是改"。
|
||||
8. **方向稿先行**(卡④ Q8=甲):2~3 张低成本方向稿供选划,判完方向才精制。**外部参照物非必需**——风格锚用**项目自己的定稿素材**(比任何外部图都准),构图用纵深稿 + 坐标表。
|
||||
|
||||
---
|
||||
|
||||
## 7. 本次做了什么 / 没做什么(边界留痕 · 2026-10-23 更新)
|
||||
|
||||
**做了**:
|
||||
- 出图提示词 v2(§13)与分层元素描述表(§12)
|
||||
- **文档批已执行并合回 master**:15 个文件,commit `47907625`;文档域闸 `--docs` 全绿(§11)
|
||||
- 待办清单登记「营地五界面窗口化批」(优先级=绝对)
|
||||
- 参照物(首轮 6 张)已判定不合格并整目录删除
|
||||
|
||||
**没做**:
|
||||
- 未出图、未跑生图管线(等作者跑 S1)
|
||||
- 未改 `game/` 任何文件、未写需求卡片、未编迭代号
|
||||
- 未做代码侧:`UiKit.TEX_CAMP_*` 常量与落码(归实现批)
|
||||
- 旧条「每购得一件永久强化,对应柜台亮起一格暖光」随形态失效,**已随 §6.2 重写一并删除**(不再是候选事项)
|
||||
@@ -0,0 +1,209 @@
|
||||
# 需求卡片:Esc 浮窗与营地顶栏批(营地顶部小 HUD 栏 · Esc 三钮浮窗)· 营地交互重做卡⑤
|
||||
|
||||
- 版本:**v1.2**(2026-10-18 grilling 轮次裁定回写——Q1 裁定甲(只营地根态响应,代拍板留痕)、Q2 裁定甲(再按 Esc 关浮窗,推荐案)、Q3 裁定甲(确认 Esc 只在营地语境,推荐案,用户的从严收窄升格为已确认);契约 2 随 Q3 升格;卡④回设计段→本卡排期随④重新立卡后,卡片保留有效)
|
||||
- 来源:[需求澄清-营地交互重做-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 回设计段,本卡排期随④之后,卡片本身有效)
|
||||
|
||||
## 0. 前置依赖与并行性
|
||||
|
||||
- 前置卡④(营地场景图):Esc 浮窗的「返回主菜单」与顶栏 HUD 都落在营地场景屏上;**卡④ 2026-10-18 已回设计段**(美术设计定稿未过设计前置闸)——本卡不可先于卡④重新立卡落地,排期随④之后;本卡自身零美术依赖(零新素材、裁决 9/10 已给全),设计前置检查通过,卡片有效。
|
||||
- **跨卡接口(v1.1 补全)**:①场景屏顶栏位(卡④ T2 预留);②**营地刷新单口**(卡④ T3 定义:各写口尾→现开界面刷新+场景根 HUD 刷新一闸完成)——本卡 HUD 刷新挂该单口,**非已退役的 `refresh_camp`**;③子界面返回钮承诺(卡④ T3——Q1 甲案前提);④卡④终局落点不变量(T0 互证)。
|
||||
- 卡②的退出游戏钮、现役设置面——本批只复用不重做(契约 3)。
|
||||
- 与卡④同批或随后(两卡触碰同一新屏)。**探针入口路径还依赖卡②③链**(v1.1 补明): restructuring 后营地只能经「菜单三钮→进入存档→选档→进档」到达——本卡 V1/V3 探针段隐含依赖该全链在位;若本卡先于卡②③合入,探针段挂起或走中间态断言(仿卡② §9 第 4 条先例)。
|
||||
- **camp 截图/golden 联动(v1.1 补)**:HUD 栏落营地场景屏会改 camp 截图内容——若本批晚于卡④单跑,卡④ 刚重制的 camp 断言基线必再漂移;若同批,卡④ T5 迁移时点须晚于本卡 T2 HUD 落位(排序约束)。本卡 T2 含「HUD 落位后核漂移热区→限 HUD 区显式更新+呈报」条。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标(澄清⑤裁决 9/10 照录)**:
|
||||
- 裁决 9「**Esc 键**唤起浮窗:返回主菜单 / 设置 / 退出游戏 三项」(用户原话「ESC键可以唤起一个浮窗,里面有返回主菜单,设置,退出游戏三项」);
|
||||
- 裁决 10「钥石余额:场景**顶部小 HUD 栏**」(用户原话「先按顶部小HUD栏设计」——provisional 措辞留痕)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
1. Esc 浮窗(全仓现无任何键盘快捷键处理——从零新建);
|
||||
2. 营地场景顶部小 HUD 栏(钥石余额常驻——现营地钥石徽在 CampOverlay 顶栏,随卡④拆解后由新 HUD 栏承接)。
|
||||
|
||||
**完成定义**:
|
||||
1. 营地语境按 Esc 唤起浮窗,浮窗有且仅有返回主菜单/设置/退出游戏三项;浮窗的关闭/取消路径按 Q2 裁定落地;
|
||||
2. 「返回主菜单」回主菜单(营地场景屏隐藏+选档层同收+主菜单可见);「设置」开现役设置面(语境分支见契约 3);「退出游戏」接现役退出路径;
|
||||
3. 营地场景顶部小 HUD 栏在位,钥石余额读数=存档单口现值,刷新时机=购买后(挂卡④营地刷新单口)/进档或回营地进入时重读/夹具直写后探针显式触发(v1.1 清单化);
|
||||
4. 浮窗零动效直开直关(生涯页同款契约);新层入 `LAYER_CONTRACT`;
|
||||
5. 批末重闸按改动域选档全绿;三份 `reports/` 产出物写齐(T4)。
|
||||
|
||||
## 2. 现状事实(2026-10-18 实查;开工日 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` |
|
||||
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 浮窗三项且仅三项**:返回主菜单/设置/退出游戏(裁决 9)。禁加项(禁「继续冒险」类——营地无冒险可继续)、禁减项。浮窗的关闭路径=Q2 裁定项(再按 Esc 关/关闭钮/无关闭三选一),不属「加项」争议。
|
||||
|
||||
🔴 **契约 2 · Esc 范围=营地语境(Q3 已确认,2026-10-18)**:浮窗只在营地场景图语境唤起(含其子界面开态——行为按 Q1 裁定的甲案:只根态响应)。**禁扩展到战斗/地图/其他屏**(用户 2026-10-18 确认「只在营地语境」;08:192 键盘面仍排 Beta,本批只落 Esc 一项)。
|
||||
|
||||
🔴 **契约 3 · 设置与退出本体零重做、接线允许语境分支(v1.1 放宽)**:设置面本体与退出行为零改(F2/F3);**开合接线允许按语境分支**——现役 `_on_settings_closed` 带「回开始层」硬副作用,营地语境须分支为「关设置回浮窗不回主菜单」;正解=浮窗登记于设置层之下,`_open_modal(settings_overlay)` 自动隐藏浮窗、关闭自动恢复(既有栈语义);`_quit_game` 无语义副作用,原样复用。
|
||||
|
||||
🔴 **契约 4 · 返回主菜单动线**:营地场景屏隐藏+**选档层同收**(卡④ T3 口径)+开始层恢复;营地语境⇔闲时会话(无冒险会话——卡④ 契约 6「冒险进行中不开营地」+现役终局回菜单并 `_begin_session(false)` 收束推得,v1.1 前提成文),故动线本身不含会话收束;**回主菜单后菜单=卡②三钮、断点在档不在菜单露面**(禁实现者自作主张加「继续冒险」钮——那正是契约 1 在浮窗侧禁的);营地每笔消费即时写盘(存档序列化.md:121),回主菜单不写档也无损。
|
||||
|
||||
🔴 **契约 5 · 顶栏 HUD 单口读数+刷新挂卡④单口(v1.1 订正)**:钥石余额只经 MetaProgression/SaveManager 访问器单口读(禁第二账本、禁视图自算);**刷新挂卡④定义的营地刷新单口**(非已退役 `refresh_camp`——F9);刷新时机清单=购买后/进档或回营地进入时重读/夹具直写后探针显式触发(T2 落定)。
|
||||
|
||||
🔴 **契约 6 · 零动效**:浮窗直开直关,子树零 AnimationPlayer、源码禁动效令牌(生涯页同款契约,F7);减动效设置项不新增(在役项沿用)。
|
||||
|
||||
🔴 **契约 7 · 层序纪律+遮挡结论(v1.1 落结论)**:浮窗入 `LAYER_CONTRACT`(改层序只改合同表);禁散落 `move_child`。**与调试角标遮挡的结论(v1.1 补,既有纪律已定论、无需用户裁)**:debug_badge 是末位 float 类不注册 ModalStack,ModalStack.open 只隐藏登记层⇒角标**恒压浮窗且保持可点**(树序末位=最顶绘制序,全仓无 `debug_badge.disabled` 赋值)——合同内照此表达。另写明一条栈语义防误触 L3:浮窗开会隐藏登记序在其上的已开层(如调试浮窗体 debug_overlay),关闭恢复——属既有栈语义非新决策。
|
||||
|
||||
🔴 **契约 8 · provisional 口径留痕**:裁决 10「先按顶部小HUD栏设计」的「先」字留痕——HUD 栏形态按最小集落地(只承载钥石余额),扩项=新裁点。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:Esc 输入接线+浮窗视图(三项钮+Q2 关闭路径)+层序登记;营地场景屏顶栏小 HUD 栏(钥石余额);断言批(探针合成键盘事件+零动效契约+HUD 读数一致性);反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- 营地场景图本体 → 卡④(本批只在其顶栏位落 HUD+挂刷新单口);
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①;
|
||||
- 其余键盘快捷键(任意键关闭浮窗/手柄/战斗内 Esc) → 用户未裁,禁自扩(契约 2);
|
||||
- 设置面/退出路径本体改动 → 无(契约 3);
|
||||
- HUD 栏扩项(冒险天数/种子等) → 契约 8,扩项另裁。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
> **v1.3 口径**:本批禁写 `design/` 整目录与根规则文件,唯一可写目录 = `reports/`;下表一律执行为列进反哺点清单。
|
||||
|
||||
| 文档 | 关联点 | 同步义务 |
|
||||
|---|---|---|
|
||||
| `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/*` | 实现与断言本体 | 实施本体(可写) |
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
> 估时按 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)。
|
||||
|
||||
### 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 关/关闭钮/无关闭,按裁定)。
|
||||
|
||||
### 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)。
|
||||
|
||||
### T3 断言批(≈0.5–1d)
|
||||
|
||||
- 探针(**宿主选 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 补);注入→最窄闸见红→精确还原→记录。
|
||||
|
||||
### T4 产出物落位卡(≈0.5d,末卡)
|
||||
|
||||
三份 `reports/` 写齐。
|
||||
|
||||
**估时合计 ≈3–4.5d**(v1.1 订正:分项和=3.0–4.5d,原「2.5–3.5d」算术少算)。切分预案:T1/T2 后可切(浮窗先落、HUD 后落)——切分价值低,建议与卡④同批单跑。
|
||||
|
||||
## 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–V8 机检自动化(能力验证经隔离存档实例直写钥石/开态——v1.1 订正:不经 DebugOverlay 界面);V9 归用户目检(生命周期末步统一进行,不阻塞迭代,澄清⑥口径)。
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码与断言面 → `--code`)全绿才合并;MC 零跑(零数值零行为)。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救)**:断言写法、探针合成键事件写法、命名。
|
||||
- **L2(记录继续)**:Esc 接线方式选型(§9 域内)、浮窗壳尺寸/位次(UiKit 现行件域内)、HUD 栏位微调。
|
||||
- **L3(停机呈报)**:卡④ 场景屏未到位又需合并;Q1/Q2/Q3 未裁即开工;发现需扩展到非营地语境才能成立(触契约 2=用户裁面);Esc 键盘合成 spike 失败且无替代验收手段(真实路径纪律不让直呼处理函数代偿);浮窗开会隐藏其上登记层的栈语义被误判为缺陷(契约 7 已写明,勿误停机)。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
1. Esc 输入接线方式(`_unhandled_input` vs InputMap 新 action vs 专用控制器)——约束:不吞他处输入、探针可合成(与 T0 spike 结论兼容;InputMap action+轮Polling 路线禁——与合成探针不兼容)。
|
||||
2. 浮窗模态壳尺寸与位次(UiKit `modal_shell` 现行件域内)。
|
||||
3. HUD 栏在顶栏的左右位次与间距(卡④场景屏顶栏域内)。
|
||||
4. 「返回主菜单」复用卡④篝火同一处理函数(建议复用——动线同口;实现期定)。
|
||||
5. 浮窗视觉面是否入 shot_checker 取样(T3 定:入=补截图段;不入=显式登记归目检并写明理由)。
|
||||
6. 提交切分与 worktree 分支命名。
|
||||
|
||||
## 10. 待裁点
|
||||
|
||||
### Q1 · Esc 在子界面开态下的行为(必裁,未裁不开 T1)
|
||||
|
||||
**背景**:营地语境含「场景根态」与「子界面开态」(锻造/秘宝/生涯/天赋树/图鉴开着时)。澄清⑤裁决 9 未覆盖此分支。两案并列,**无推荐(用户令:不代裁)**:
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| 甲 | 只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗(**前提=各子界面自带返回钮闭环——待卡④ T3 承诺,v1.1 括注**) | 语义最简;子界面里按 Esc 无反馈 |
|
||||
| 乙 | 营地语境任意态响应 Esc:子界面开着时先关子界面回根态,再按才唤浮窗(单义化) | 多一层输入编排;与既有「子界面返回钮」形成双关闭路径 |
|
||||
|
||||
连带:判据形态随案(T1 已列);V1 按案分拆断言;契约 2 措辞裁后回写。裁决方式:随卡片评审定案;裁定落 T0 执行+实现报告留痕。
|
||||
|
||||
**裁定(2026-10-18)**:**甲**——只营地根态响应 Esc;子界面开着时 Esc 不唤浮窗。代拍板留痕(用户令「事项按推荐的来」,本项无推荐案,取默认甲=语义最简;前提=卡④ T3 承诺各子界面自带返回钮闭环,卡④重新立卡时该承诺已在内;追认单可 veto)。T1 判据=营地场景屏 visible 且无营地子模态开态。
|
||||
|
||||
### Q2 · 浮窗的关闭/取消路径(必裁,未裁 T1 视图定不了稿——v1.1 新增)
|
||||
|
||||
**背景**:`UiKit.modal_shell`(ui_kit.gd:456-475)只产 dim+center+panel+VBox、**无内置关闭件**;契约 1 禁加项⇒浮窗只有三钮,无一「关闭」。而 §4 不做清单把「任意键关闭浮窗」划为用户未裁禁自扩——**Esc 自身再按的行为恰好落在被 punt 的缝隙里**:Q1 两案都不覆盖「浮窗已开时按 Esc」。若不裁且无关闭钮,玩家误开浮窗后离开营地只有「回主菜单/开设置/退游戏」三条路——体感陷阱,且与卡④子界面(生涯有返回钮)的可用性水准倒挂。
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| 甲(推荐) | 再按 Esc 关浮窗(同键唤起/关闭单义切换) | 与 Q1 裁甲并存时 Esc 语义三态分枝(关浮窗/唤浮窗/子界面无反馈),探针断言须分拆(V1 已按案分拆) |
|
||||
| 乙 | 浮窗加关闭钮 | 触契约 1「禁加项」——须重新解释「三项」仅指功能项 |
|
||||
| 丙 | 维持无关闭(三钮即全部出口,「返回主菜单」充当默认离开) | 误开浮窗后成本高;与子界面可用性水准倒挂 |
|
||||
|
||||
裁决方式:随卡片评审定案;裁定落 T1 视图与接线+V 表。
|
||||
|
||||
**裁定(2026-10-18)**:**甲**——再按 Esc 关浮窗(同键唤起/关闭单义切换)。推荐案,用户令按推荐执行。T1 接线含「浮窗已开时再按 Esc=关浮窗」;V1 断言按「根态唤起/再按关闭」两态分拆。
|
||||
|
||||
### Q3 · Esc 范围确认(顺带确认项,低成本高保险——v1.1 新增)
|
||||
|
||||
**背景**:契约 2「Esc 只在营地语境」是卡的从严收窄(澄清文档只裁「Esc 唤浮窗三项」、未裁范围——S1)。收窄方向保守且已自标「用户未裁」,但若用户本意是全局 Esc 菜单,范围锁死即返工。
|
||||
|
||||
| 案 | 内容 | 代价 |
|
||||
|---|---|---|
|
||||
| 甲(推荐) | 确认「Esc 浮窗只在营地语境」——契约 2 现文成立 | 无(一句话确认) |
|
||||
| 乙 | 用户本意为全局(或更宽范围) | 本卡范围重开=契约 2/T1/V1 大改,且触 08:192 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)零改动。
|
||||
@@ -0,0 +1,431 @@
|
||||
# 需求卡片:换图系双基座七件批
|
||||
|
||||
> 日期:2026-10-31
|
||||
> 性质:需求改动实现批卡片;零待裁点,开工门=U0。
|
||||
> 来源:待办清单「「封锁-蓄力」combo 设计素材」条。唯一事实源:[奇物](../design/gdd/systems/冒险循环/奇物.md) v1.3+[行动经济](../design/gdd/systems/排雷循环/行动经济.md) v1.3+[战斗结算](../design/gdd/systems/战斗循环/战斗结算.md) v1.4+[地图战斗资源](../design/gdd/systems/战斗循环/地图战斗资源.md) v1.3;参考设计过程件:[提案 v0.5](提案-弃图刷新系专长流派-2026-10-30.md)。
|
||||
> 估时:T0–T8 ≈ **7.75~8.5 个工作日**;切分预案见第十二节。
|
||||
|
||||
## 一、设计事实源
|
||||
|
||||
本卡实现下列已闭合设计,不重新裁断机制:
|
||||
|
||||
| 来源 | 本卡采用的事实 |
|
||||
|---|---|
|
||||
| [奇物 v1.3](../design/gdd/systems/冒险循环/奇物.md) | 基座先行原则、七件名称与职责、双基座叠加 |
|
||||
| [行动经济 v1.3](../design/gdd/systems/排雷循环/行动经济.md) | 付费挖格产 RAP、刷新成本减半、两件基座叠加 |
|
||||
| [战斗结算 v1.4](../design/gdd/systems/战斗循环/战斗结算.md) | 成功刷新事实、当轮结算兑付、全局 `damage_taken` |
|
||||
| [地图战斗资源 v1.3](../design/gdd/systems/战斗循环/地图战斗资源.md) | 现行清图奖励语义不变;旧探索进度回收方案已注销 |
|
||||
| [提案 v0.5](提案-弃图刷新系专长流派-2026-10-30.md) | 七件最终结构与行为验收面;保留作实现参考,不替代 GDD |
|
||||
|
||||
## 二、目标与完成定义
|
||||
|
||||
交付七件专长:
|
||||
|
||||
| 层 | feat_id | 显示名 | 稀有度/cap | provisional 数值 |
|
||||
|---|---|---|---|---|
|
||||
| 基座 | `vein_reserve` | 矿脉储能 | excellent/1 | 每次受理的付费挖格 +1 RAP |
|
||||
| 基座 | `easy_swap` | 省力周转 | excellent/1 | 刷新成本为基线 50% |
|
||||
| 输出 | `wall_blast` | 崩壁冲击 | excellent/1 | 全体存活敌各受 2 点 |
|
||||
| 输出 | `charge_rotation` | 蓄力周转 | excellent/1 | 每刷一次 +1 层,上限 3;下一次主角攻击消费全部层 |
|
||||
| 生存 | `stone_mitigation` | 石层缓冲 | common/1 | 当轮有成功刷新时,每次主角受伤固定 −2 |
|
||||
| 生存 | `tunnel_rest` | 坑道小憩 | common/1 | 每刷一次累计 1 点恢复,上限 3;本轮授予段合计回血 |
|
||||
| 资源 | `mine_profit` | 矿道红利 | rare/1 | 每次成功刷新固定 +1 GP |
|
||||
|
||||
完成定义:
|
||||
|
||||
1. `feats.tres` 30→37 行;稀有度 普通14/优秀13/稀有10/传说0;牌库 132→139。
|
||||
2. 两件基座可叠加;刷新次数与 RAP 清零限制保持现行。
|
||||
3. 所有主角伤害路径统一经过 `damage_taken`,每种伤害单据恰发射一次。
|
||||
4. 刷新效果只在成功刷新后产生;RAP 不足、相位非法、每轮上限均零触发。
|
||||
5. 崩壁冲击沿用案 C,在战斗段末结账;石层缓冲、坑道小憩、矿道红利均在本轮既有通道兑付。
|
||||
6. 无七件专长时现行行为逐位不漂移;持有旧 `damage_taken` 专长时按全局受伤语义生效。
|
||||
7. 新测试套件与计数回归全部通过,旧断言只允许因语义扩展而改,不得放宽阈值。
|
||||
|
||||
## 三、现状事实
|
||||
|
||||
1. `dig_paid` 在 AP 扣费前发射,消费环已有 counter/grant AP 通道,无 RAP 通道:[action_economy.gd:101](../game/core/action_economy.gd#L101)。
|
||||
2. `grant_reserve_ap()` 已存在,直接增加 RAP:[action_economy.gd:177](../game/core/action_economy.gd#L177)。
|
||||
3. `request_refresh()` 固定读全局基线成本:[action_economy.gd:199](../game/core/action_economy.gd#L199)。
|
||||
4. 刷新成功序=扣 RAP→计数→旧资源退役→旧板退役→装新板:[battle_orchestrator.gd:610](../game/core/battle_orchestrator.gd#L610)。
|
||||
5. 当前事件词表20种,效果词表32种:[effect_engine.gd:12](../game/core/effect_engine.gd#L12)。
|
||||
6. `damage_taken` 现只在敌方攻击路径发射:[damage_controller.gd:472](../game/core/damage_controller.gd#L472)。
|
||||
7. 地形伤害在开单前单独发射并只消费 `damage_reduce/ac_bonus/counter_add`,不消费 shield:[grant_segment.gd:174](../game/core/settlement/grant_segment.gd#L174)。
|
||||
8. 棋盘主角直伤在控制器终段只做伤害下限后扣血,不发射事件:[damage_controller.gd:569](../game/core/damage_controller.gd#L569)。
|
||||
9. 危险格与错误封印的负向资源直伤最终进入同一 `board_flat_hero` 单据:[reward_grant.gd:320](../game/core/reward_grant.gd#L320)、[reward_grant.gd:369](../game/core/reward_grant.gd#L369)。
|
||||
10. 授予段伤害单已有案 C 暂存队列,战斗段末统一补提交:[grant_segment.gd:24](../game/core/settlement/grant_segment.gd#L24)、[combat_segment.gd:114](../game/core/settlement/combat_segment.gd#L114)。
|
||||
11. `grant_gp` 现消费点写 `reward_grant.escrow` 并发专长 GP 行:[grant_segment.gd:265](../game/core/settlement/grant_segment.gd#L265)。
|
||||
12. `attack_bonus` 已支持 `value_counter`:[damage_controller.gd:360](../game/core/damage_controller.gd#L360)。
|
||||
13. GP 袋正常格面行值为3,普通战斗掉落均值3:[reward.tres:184](../game/data/tables/reward.tres#L184)、[shop.tres:7](../game/data/tables/shop.tres#L7)。
|
||||
14. 现行专长表30行、牌库132;稀有度12/9/9/0:[adventure_feats_test.gd:465](../game/tests/unit/core/adventure_feats_test.gd#L465)。
|
||||
15. 刷新真实入口回归已覆盖 RAP、AP保留、上限与刷新图零清图:[round_flow_skill_pool_test.gd:145](../game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd#L145)。
|
||||
16. BattleOrchestrator 当前811行,类变量与文件长度均有 S13 登记值:[battle_orchestrator.gd:600](../game/core/battle_orchestrator.gd#L600)、[gdstyle_baseline.txt:9](../tools/gdstyle_baseline.txt#L9)。
|
||||
17. `BattleFacts` 成员冻结15/15,不得新增刷新队列:[battle_facts.gd:10](../game/core/battle_facts.gd#L10)。
|
||||
18. `RewardGrant` 类变量封闭15/15,不得新增 GP/回收队列:[reward_grant.gd:13](../game/core/reward_grant.gd#L13)。
|
||||
|
||||
## 四、设计契约
|
||||
|
||||
- 🔴 **C1 无专长基线零扰动**:挖格不产 RAP、刷新成本8、伤害事件旧结果、GP与清图路径均保持现行。
|
||||
- 🔴 **C2 成功刷新才登记**:RAP不足、非Active、每轮第3次均不产生 `board_refreshed`;成本折扣只改变门槛与扣费,不改变拒绝判据。
|
||||
- 🔴 **C3 全局受伤事件恰一次**:敌方攻击、危险格、错误封印资源格、地形伤害都经统一 `damage_taken`;同一伤害单据不得重复发射。
|
||||
- 🔴 **C4 减伤固定不堆**:石层缓冲只看当轮是否至少成功刷新一次;同一轮多刷不提高减伤,减后仍走现行全局伤害下限。
|
||||
- 🔴 **C5 不引入临时生命**:本批不新增HP护盾池、承伤库存或生命上限变化。
|
||||
- 🔴 **C6 五段时序不变**:崩壁冲击走现有延迟伤害队列;坑道小憩在授予段;GP走现有战斗内缓冲。
|
||||
- 🔴 **C7 矿道红利不绑清图奖励**:每次成功刷新固定1 GP;不读取旧图探索进度、不触发清图事件。
|
||||
- 🔴 **C8 数值单源**:RAP产量、50%折扣、群伤、恢复、减伤、GP均从 `feats.tres` 读取;代码零玩法数值字面量。
|
||||
- 🔴 **C9 词表封闭**:只新增 `refresh_request`、`board_refreshed` 两事件与 `grant_reserve_ap`、`apply_discount` 两效果。
|
||||
- 🔴 **C10 结构预算**:BattleOrchestrator文件长度与类变量只降不升;新增流程件独立承载刷新生命周期。
|
||||
- 🔴 **C11 反自动刷新**:仍只允许玩家入口发起,RAP满不自动刷新。
|
||||
- 🔴 **C12 信息红线**:不揭示格、不改数字、不改棋盘生成分布。
|
||||
- 🔴 **C13 拆分边界**:`BattleRefreshFlow` 只负责刷新可用性、扣费、换图与成功事实登记;七件效果的算法留在效果引擎、授予段与伤害控制器。
|
||||
- 🔴 **C14 禁凑计数袋**:不把所有分支堆进一个无名 helper;中央受伤消费件只处理 `damage_taken` 命中组合。
|
||||
|
||||
## 五、行为结构
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[付费挖格受理] -->|矿脉储能| B[RAP增加]
|
||||
C[玩家请求刷新] -->|省力周转| D[折后成本]
|
||||
D -->|成功| E[board_refreshed]
|
||||
E --> F[崩壁冲击:延迟群伤单]
|
||||
E --> G[蓄力周转:当轮攻击层数]
|
||||
E --> H[石层缓冲:当轮全局减伤]
|
||||
E --> I[坑道小憩:当轮累计恢复]
|
||||
E --> J[矿道红利:1 GP]
|
||||
K[敌方攻击/危险格/错误封印/地形] --> L[damage_taken 恰一次]
|
||||
H --> L
|
||||
```
|
||||
|
||||
## 六、范围边界
|
||||
|
||||
做:
|
||||
|
||||
1. 扩两事件、两效果;统一全局 `damage_taken` 发射与消费。
|
||||
2. 实现两件基座、三件输出/两件生存/一件资源的数据行与消费点。
|
||||
3. 拆 `BattleRefreshFlow`,保持编排器入口与唯一通道。
|
||||
4. 新增七件专项测试,同步词表、专长池与刷新回归。
|
||||
5. 能力清单入表。
|
||||
|
||||
不做:
|
||||
|
||||
1. 不做探索进度回收、清图奖励折算或旧图快照。
|
||||
2. 不做临时生命值或独立护盾池。
|
||||
3. 不新增 UI、素材、渲染基线、MC档位。
|
||||
4. 不重构自爆流、不改 `增益状态`、不新增数据表。
|
||||
5. 不抬高刷新成本,不做全板开,不返 RAP。
|
||||
|
||||
## 七、关联文档同步面
|
||||
|
||||
| 文档 | 关联点 | 同步义务 |
|
||||
|---|---|---|
|
||||
| [奇物](../design/gdd/systems/冒险循环/奇物.md) | 七件由设计声明改为实装;行数、稀有度与验收口径 | 实现批禁写;落 `reports/iter<N>-反哺点-<日期>.md`,用户裁决后文档批执行 |
|
||||
| [行动经济](../design/gdd/systems/排雷循环/行动经济.md) | 两件基座由待实现改为在册 | 同上 |
|
||||
| [战斗结算](../design/gdd/systems/战斗循环/战斗结算.md) | 全局受伤事件与刷新事实落码状态 | 同上 |
|
||||
| [地图战斗资源](../design/gdd/systems/战斗循环/地图战斗资源.md) | 确认清图奖励不受矿道红利改变 | 同上 |
|
||||
| [架构文档](../design/engineering/architecture/architecture.md) | 新增事件、效果、BattleRefreshFlow与API边界 | 同上 |
|
||||
| [测试手册](../game/tests/README.md) | 换图系能力行 | 本写卡段已入表;实现批只核对名实一致 |
|
||||
|
||||
## 八、文件结构与职责
|
||||
|
||||
| 文件 | 职责 | 改动类型 | 验收行 |
|
||||
|---|---|---|---|
|
||||
| [effect_engine.gd](../game/core/effect_engine.gd) | 封闭词表扩两事件、两效果 | 行为改动 | V1 |
|
||||
| [action_economy.gd](../game/core/action_economy.gd) | dig_paid产RAP、折价成本受理 | 行为改动 | V2/V3 |
|
||||
| [battle_refresh_flow.gd](../game/core/settlement/battle_refresh_flow.gd) | 新建:刷新门、扣费、换图、成功事实 | 行为改动 | V4/V10 |
|
||||
| [battle_orchestrator.gd](../game/core/battle_orchestrator.gd) | 刷新公开口单行转发;来源豁免 | 行为改动 | V4 |
|
||||
| [damage_controller.gd](../game/core/damage_controller.gd) | 全局受伤消费、攻击层消费、延迟群伤目标解析 | 行为改动 | V5/V6/V7 |
|
||||
| [grant_segment.gd](../game/core/settlement/grant_segment.gd) | 刷新命中施加、GP、当轮恢复、地形路径接入统一消费 | 行为改动 | V5/V6/V7/V9 |
|
||||
| [reward_grant.gd](../game/core/reward_grant.gd) | 群伤单延迟入队 | 行为改动 | V6 |
|
||||
| [feats.tres](../game/data/tables/feats.tres) | 七行专长与数值 | 行为改动 | V2/V3/V6/V7/V8/V9/V11 |
|
||||
| [run_all.gd](../game/tests/run_all.gd) | 新测试登记 | 非行为改动 | V12 |
|
||||
| [refresh_feats_test.gd](../game/tests/unit/battle_settlement/refresh_feats_test.gd) | 新建七件与全局受伤链路专项 | 非行为改动 | V2–V10 |
|
||||
| [adventure_feats_test.gd](../game/tests/unit/core/adventure_feats_test.gd) | 专长行数、稀有度、牌库 | 非行为改动 | V11 |
|
||||
| [forge_table_test.gd](../game/tests/unit/data_config/forge_table_test.gd) | 事件/效果闭集计数 | 非行为改动 | V1 |
|
||||
| [effect_words_t1_test.gd](../game/tests/unit/core/effect_words_t1_test.gd) | 新词在册与出域拒绝 | 非行为改动 | V1 |
|
||||
| [round_flow_skill_pool_test.gd](../game/tests/unit/battle_settlement/round_flow_skill_pool_test.gd) | 无专长基线回归 | 非行为改动 | V12 |
|
||||
| [README.md](../game/tests/README.md) | 能力清单 | 非行为改动 | V11 |
|
||||
|
||||
## 九、落位与可复用性预检
|
||||
|
||||
### 9.1 新增件
|
||||
|
||||
| 新件 | 复用结论 | 唯一落点 | 调用路径 |
|
||||
|---|---|---|---|
|
||||
| `BattleRefreshFlow` | 已查 `can_refresh`、`player_request_refresh`、`request_refresh`、商店代理、autoplay;没有同时负责成本查询与成功事实登记的现成件。不能复用 GrantSegment,因为刷新发生在授予段外;不能塞 BattleOrchestrator,因其文件长度贴顶 | `game/core/settlement/battle_refresh_flow.gd` | 呈现层→编排器转发→流程件→行动经济/装板壳/授予段 |
|
||||
| `refresh_feats_test.gd` | 现有刷新测试只覆盖无专长基线;专长宿主没有经真实 `player_request_refresh` 驱动七件的夹具 | `game/tests/unit/battle_settlement/refresh_feats_test.gd` | `settle_batch`/`player_request_refresh`/轮末结算 |
|
||||
| `_apply_hero_damage_effects` | 复用现有 `_enemy_shield_apply` 的组合算法,不新造事件消费件 | DamageController私有族 | 所有 `damage_taken` 发射点唯一消费 |
|
||||
|
||||
### 9.2 实现规范预算
|
||||
|
||||
| 触面 | 实测预算 | 结论 |
|
||||
|---|---|---|
|
||||
| `battle_refresh_flow.gd` | 公开函数2/类变量0 | ≤20/≤15,通过 |
|
||||
| `battle_orchestrator.gd` | 类变量38不变;当前811行,删除旧刷新体并改两行转发,目标下降至少8行 | S13只降不升 |
|
||||
| `action_economy.gd` | 公共方法15→15,仅扩参与局部状态 | ≤20,通过 |
|
||||
| `grant_segment.gd` | 类变量3→3;公开方法1→2;新增私有施加函数≤4 | ≤20/≤15,通过 |
|
||||
| `damage_controller.gd` | 公共方法不新增;现有 `_enemy_shield_apply` 改名为 `_apply_hero_damage_effects`,私有函数净增≤2 | S13逐键实测 |
|
||||
| `reward_grant.gd` | 类变量15→15;新增一个公开延迟入口 | 禁增员,通过 |
|
||||
| `feats.tres` | 同一专长表30→37 | 无第二数据源 |
|
||||
|
||||
## 十、任务卡
|
||||
|
||||
### T0 开工基线与预算实测(0.5d)
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 读 `game/tests/.gates/ci-status.json`;CI红未修即停卡。
|
||||
- [ ] 复核第三节 file:line;任一事实漂移先停卡核对。
|
||||
- [ ] 跑开发期局部读数:`bash tools/gdstyle.sh game`。
|
||||
- [ ] 记录 BattleOrchestrator行数/类变量、RewardGrant类变量、DamageController与GrantSegment方法数。
|
||||
|
||||
产出:所有结构预算基线;本卡无事实漂移。
|
||||
|
||||
### T1 词表扩签与计数(0.5d)
|
||||
|
||||
接口块:
|
||||
|
||||
- EVENTS 新增:`refresh_request`、`board_refreshed`
|
||||
- EFFECT_KINDS 新增:`grant_reserve_ap`、`apply_discount`
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 在 [effect_engine.gd:12](../game/core/effect_engine.gd#L12) 追加两事件,并同步 [effect_engine.gd:19](../game/core/effect_engine.gd#L19) 的 MINE_EVENTS。
|
||||
- [ ] 在 [effect_engine.gd:24](../game/core/effect_engine.gd#L24) 追加两效果;不新增fact/op/DO_KEYS。
|
||||
- [ ] 更新 [forge_table_test.gd:202](../game/tests/unit/data_config/forge_table_test.gd#L202):EVENTS20→22、EFFECT_KINDS32→34。
|
||||
- [ ] 更新 [effect_words_t1_test.gd:14](../game/tests/unit/core/effect_words_t1_test.gd#L14):两事件与两效果在册;旧出域负例全部保留。
|
||||
- [ ] 孤立验证:`effect_words_t1_test.gd`、`forge_table_test.gd`。
|
||||
|
||||
验收:词表封闭性无洞,旧词行为不变。
|
||||
|
||||
### T2 全局 `damage_taken` 统一(1.75d)
|
||||
|
||||
接口块:
|
||||
|
||||
```gdscript
|
||||
func _emit_hero_damage_taken(amount: int, damage_kind: String, source: String, cell: Vector2i, ri: int) -> Dictionary
|
||||
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary
|
||||
```
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 把 [damage_controller.gd:507](../game/core/damage_controller.gd#L507) 的敌方受伤消费族改名为 `_apply_hero_damage_effects`,保留 shield、damage_reduce、ac_bonus、counter_add 的组合次序。
|
||||
- [ ] 新增 `_emit_hero_damage_taken()`:payload必含 `kind/amount/source/cell/round/hp_ratio`,dispatch后调用唯一消费族,返回减后金额与命中束。
|
||||
- [ ] 敌方攻击改走该方法,保持现有“掷点后、交付前”消费时点。
|
||||
- [ ] `_resolve_board_flat_hero()` 在伤害下限前调用该方法;若单据 context 已标 `hero_damage_consumed=true` 则跳过,避免重复。
|
||||
- [ ] 地形伤害改为调用该方法并把 `hero_damage_consumed=true` 写入单据 context,删除本地重复消费:[grant_segment.gd:174](../game/core/settlement/grant_segment.gd#L174)。
|
||||
- [ ] 危险格与错误封印的负向主角直伤经 `board_flat_hero` 自动进入该方法,不新增第二发射点。
|
||||
- [ ] 写专项函数:
|
||||
- `test_all_hero_damage_paths_emit_once()`:注入临时 `counter_add damage_taken` 专长,从敌方攻击、危险格、错误封印、地形四种真实入口进入,每种单据恰好计数一次。
|
||||
- `test_no_duplicate_damage_taken_event()`:同一伤害不得出现两次消费。
|
||||
- [ ] 窄测:`refresh_feats|damage_controller|seal_polarity|round_flow` 红件最小复跑。
|
||||
|
||||
验收:`damage_taken` 字面语义覆盖所有主角承伤;无专长仍逐位保持原伤害。
|
||||
|
||||
### T3 两件基座(1d)
|
||||
|
||||
接口块:
|
||||
|
||||
```gdscript
|
||||
func request_refresh(cost_override: int = -1) -> bool
|
||||
static func _effective_refresh_cost(oc: BattleOrchestrator) -> int
|
||||
```
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 在 [action_economy.gd:101](../game/core/action_economy.gd#L101) 的 `dig_paid` 命中环累计 `grant_reserve_ap`,只在扣费受理后一次性调用 `grant_reserve_ap()`;连锁不二次调用。
|
||||
- [ ] `refresh_request` 命中只认 `kind=="apply_discount" && mode=="refresh"`;取声明序第一个命中,按 `floor(base*cost_mult/100)` 计算。
|
||||
- [ ] 新建 `BattleRefreshFlow`:公开 `can_refresh(oc)`、`execute(oc)`;execute只在ActionEconomy扣费成功后继续。
|
||||
- [ ] 编排器两个公开方法改为单行转发,删除旧刷新体。
|
||||
- [ ] 写:
|
||||
- `test_reserve_on_accepted_dig()`:真实挖格入口,RAP+1,AP按表扣。
|
||||
- `test_refresh_discount_gate_and_charge()`:真实刷新入口成本4,can_refresh同源判断。
|
||||
- `test_double_base_stack()`:同时持两件时挖格产RAP、刷新扣4。
|
||||
- `test_no_feats_baseline()`:旧成本与RAP来源逐值不变。
|
||||
- [ ] 窄测 `--only=refresh_feats|round_flow`。
|
||||
|
||||
验收:两件基座改变行为但不破坏现行经济闸。
|
||||
|
||||
### T4 成功刷新事实与命中施加(1d)
|
||||
|
||||
接口块:
|
||||
|
||||
```gdscript
|
||||
static func execute(oc: BattleOrchestrator) -> bool
|
||||
func apply_board_refreshed(ri: int) -> void
|
||||
func defer_ticket(t: Dictionary) -> void
|
||||
```
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] `execute()` 成功序钉死:折价扣RAP→refresh_used+1→旧资源退役→旧板退役→装新板→GrantSegment `apply_board_refreshed(ri)`。
|
||||
- [ ] install失败时返回false且零效果;成功事实只在新板就位后发射。
|
||||
- [ ] `apply_board_refreshed()` dispatch一次 `board_refreshed`,payload `{round:ri}`,命中消费:
|
||||
- `counter_add` → 既有单点落账。
|
||||
- `grant_gp` → `reward_grant.escrow += value` 并发 `fx_grant_gp` 行。
|
||||
- `open_ticket && channel=="blast"` → `DamageController.KIND_BOARD_BLAST`、`TARGET_ALL_ALIVE`、`source_class="fx_board_refresh"`,调用新增 `defer_ticket()`。
|
||||
- [ ] 在 [battle_orchestrator.gd:35](../game/core/battle_orchestrator.gd#L35) 的来源豁免追加 `"fx_board_refresh"`。
|
||||
- [ ] 写 `test_board_refresh_fact_only_on_success()`、`test_mine_profit_gp()`。
|
||||
- [ ] 窄测 `--only=refresh_feats|round_flow`。
|
||||
|
||||
验收:拒绝刷新零事实;GP固定入账且每轮上限同时限制次数。
|
||||
|
||||
### T5 五件后续效果(1.75d)
|
||||
|
||||
接口块:
|
||||
|
||||
```gdscript
|
||||
func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary
|
||||
func _round_end_apply(ri: int) -> void
|
||||
```
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 崩壁冲击已在T4延迟开单;确认授予段不结账、战斗段末按存活目标结账,最后敌死亡仍由判定段胜。
|
||||
- [ ] 石层缓冲用 `round_swap_guard`:成功刷新时+1;`damage_taken` 条件≥1时 `damage_reduce value=2`,不清层、不随伤害段变化。round桶轮始自然清空。
|
||||
- [ ] 坑道小憩用 `round_swap_rest`:成功刷新累计至上限3;`round_end` 命中 `heal value_counter=round_swap_rest`,调用 `units.heal()`封顶,满血发未生效行;随后同事件行 `counter_add set=0`。
|
||||
- [ ] 蓄力周转用战斗桶 `swap_charge_layers`:成功刷新至上限3;`attack_commit` 消费全部层数,随后清零;战斗新建自然清空。
|
||||
- [ ] 写:
|
||||
- `test_stone_mitigation_all_damage()`
|
||||
- `test_rest_rotation_cap_and_clear()`
|
||||
- `test_charge_layers_consumed_same_round()`
|
||||
- `test_blast_defers_to_combat_segment()`
|
||||
- [ ] 窄测 `--only=refresh_feats|damage_controller|seal_polarity`。
|
||||
|
||||
验收:五件只在成功刷新链生效;固定减伤不堆、恢复累计、GP定额。
|
||||
|
||||
### T6 七件数据行与牌库计数(1d)
|
||||
|
||||
接口块:`feats.tres` 行键沿用 `feat_id/display_name/rarity/cap/desc/triggers`,不扩schema。
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 追加七行,trigger结构固定:
|
||||
|
||||
| feat_id | triggers |
|
||||
|---|---|
|
||||
| `vein_reserve` | `dig_paid → grant_reserve_ap value=1` |
|
||||
| `easy_swap` | `refresh_request → apply_discount mode=refresh cost_mult=50` |
|
||||
| `wall_blast` | `board_refreshed → open_ticket channel=blast value=2` |
|
||||
| `charge_rotation` | `board_refreshed when counter<3 → counter_add swap_charge_layers1`;`attack_commit when≥1 → attack_bonus value_counter=swap_charge_layers`;`attack_commit when≥1 → set0` |
|
||||
| `stone_mitigation` | `board_refreshed → counter_add round_swap_guard1`;`damage_taken when≥1 → damage_reduce value=2` |
|
||||
| `tunnel_rest` | `board_refreshed when<3 → counter_add round_swap_rest1`;`round_end when≥1 → heal value_counter=round_swap_rest`;`round_end when≥1 → set0` |
|
||||
| `mine_profit` | `board_refreshed → grant_gp value=1` |
|
||||
|
||||
- [ ] desc分别写:挖格储能、刷新半价、刷后群伤、刷后蓄力、当轮减伤、当轮恢复、刷后GP;禁“雷/旗”字面。
|
||||
- [ ] 行数30→37;稀有度12/9/9/0→14/13/10/0;relic令牌30→37;牌库132→139。
|
||||
- [ ] 同步 [adventure_feats_test.gd:465](../game/tests/unit/core/adventure_feats_test.gd#L465)、[:476](../game/tests/unit/core/adventure_feats_test.gd#L476),旧30行断言只按实数换代,不降低 validate/禁词检查。
|
||||
- [ ] 写 `test_seven_feats_validate_and_pool()`。
|
||||
|
||||
验收:七件全部数据驱动,跨表计数器引用闭合。
|
||||
|
||||
### T7 回归与能力名实核对(0.75d)
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 核对 [README.md](../game/tests/README.md) 五类换图能力行,文件名与测试函数逐字一致。
|
||||
- [ ] `round_flow_skill_pool_test.gd:_smoke_active_refresh()` 无专长基线断言不删除、不放宽。
|
||||
- [ ] `seal_polarity_test.gd:_smoke_refresh_provenance_guard()` 继续证明刷新图零清图奖励。
|
||||
- [ ] 实跑关联窄测:
|
||||
`--only=refresh_feats|adventure_feats|round_flow|seal_polarity|damage_controller|effect_words|forge_table|config_db`
|
||||
- [ ] 出红只按红件最小复跑,波内不跑全量。
|
||||
|
||||
验收:新增与旧专长并存;无UI/素材改动,不新增golden。
|
||||
|
||||
### T8 批末出口与产出物(1d)
|
||||
|
||||
步骤:
|
||||
|
||||
- [ ] 里程碑出口跑 `bash tools/run_all_gates.sh --code`。
|
||||
- [ ] 回执必须 `all_green=true`、`dirty_game=false`。
|
||||
- [ ] 独立验收审计报告 `reports/iter<N>-验收审计-<日期>.md` 未过禁合并。
|
||||
- [ ] 轻闸全绿、合并前写齐:
|
||||
1. `reports/iter<N>-实现报告-<日期>.md`
|
||||
2. `reports/iter<N>-代拍板追认单-<日期>.md`
|
||||
3. `reports/iter<N>-反哺点-<日期>.md`
|
||||
- [ ] 实现报告列七卡状态、V1–V12读数、S13前后数、旧damage_taken专长覆盖面变化、玩家主动刷新目检项。
|
||||
- [ ] 反哺点逐条覆盖第七节六个目标。
|
||||
|
||||
完成定义:三份产出物齐全+代码域闸全绿+验收审计通过。
|
||||
|
||||
## 十一、验证闸:改动—验收对照表
|
||||
|
||||
| 需求点 | 改动(文件/行为) | 验收测试 | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| V1 词表扩签 | effect_engine;forge/effect_words | `effect_words_t1_test.gd:_init`;`forge_table_test.gd:_test_effect_kinds_closed` | 单元 | validate与整表加载 | EVENTS=22、EFFECT_KINDS=34、出域仍拒 | 新增:换图系刷新链路与效果词表接线 |
|
||||
| V2 矿脉储能 | action_economy dig_paid消费 | `refresh_feats_test.gd:test_reserve_on_accepted_dig` | 接口 | `settle_batch(dig)` | RAP恰+1、AP扣值与板资源不变 | 新增:换图基座:挖格产 RAP 与刷新折扣 |
|
||||
| V3 省力周转 | BattleRefreshFlow成本门 | `test_refresh_discount_gate_and_charge`;`test_double_base_stack` | 接口 | `player_request_refresh()` | base8→扣4;can与执行同源;两件都生效 | 新增:换图基座:挖格产 RAP 与刷新折扣 |
|
||||
| V4 成功事实 | BattleRefreshFlow install尾点 | `test_board_refresh_fact_only_on_success` | 集成 | `player_request_refresh()` | RAP不足/第3次上限/非Active均board_changed与效果零变化 | 新增:换图系刷新链路与效果词表接线 |
|
||||
| V5 全局受伤 | damage_controller统一发射消费 | `test_all_hero_damage_paths_emit_once`;`test_no_duplicate_damage_taken_event` | 集成 | 敌方攻击、dig danger、seal wrong、terrain触发 | 每路径一次事件、无重复扣血或重复counter | 新增:换图生存:全局固定减伤与轮末恢复 |
|
||||
| V6 石层缓冲 | round_swap_guard+damage_reduce | `test_stone_mitigation_all_damage` | 集成 | 刷新→四种真实受伤路径 | 每种伤害最终金额减2;多刷不增加;下限1保持 | 新增:换图生存:全局固定减伤与轮末恢复 |
|
||||
| V7 崩壁冲击 | open_ticket延迟队列 | `test_blast_defers_to_combat_segment` | 集成 | 刷新→停手 | 授予段无伤害行、战斗段末全体存活敌各受伤 | 新增:换图输出:刷新群伤与蓄力攻击 |
|
||||
| V8 蓄力周转 | counter+attack_bonus value_counter | `test_charge_layers_consumed_same_round` | 接口 | 两次刷新→本轮主角攻击 | 攻击加2层、随后0;战斗新建0;上限3 | 新增:换图输出:刷新群伤与蓄力攻击 |
|
||||
| V9 坑道小憩 | round_end heal消费 | `test_rest_rotation_cap_and_clear` | 集成 | 三次刷新→停手 | 回复3、上限3、层清0、hp≤max;满血未生效 | 新增:换图生存:全局固定减伤与轮末恢复 |
|
||||
| V10 矿道红利 | board_refreshed grant_gp | `test_mine_profit_gp` | 集成 | `player_request_refresh()` | escrow每次成功刷新+1;拒绝零变化;fx_grant_gp行在案 | 新增:换图资源:刷新固定 GP 收益 |
|
||||
| V11 数据与池 | feats七行;adventure计数 | `test_seven_feats_validate_and_pool`;adventure_feats | 单元 | ConfigDB整表与冒险牌库 | rows37、relic37、14/13/10、deck139、validate空、禁词0 | 既有:专长(奇物)授予与凭证保底 |
|
||||
| V12 无专长回归 | round_flow/seal旧基线 | `_smoke_active_refresh`;`_smoke_refresh_provenance_guard` | 单元+集成 | 现行刷新/清图入口 | RAP8、AP保留、上限2、刷新图零清图、五段与回放不降 | 既有:停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) |
|
||||
|
||||
## 十二、测试方式与削弱申报
|
||||
|
||||
自动验证:
|
||||
|
||||
- V1–V12均从真实公共入口进入;计数器、事件行、escrow、hp与伤害行都是可观测提交面。
|
||||
- 不造变异或回退hunk证明;临时专长夹具通过现行ConfigDB注入,属正常数据配置路径。
|
||||
|
||||
真人目检:
|
||||
|
||||
- 登记一项:玩家持基座与收益件后,是否主动规划刷新而非只在坏图刷新。写入实现报告,不阻塞合回。
|
||||
|
||||
测试削弱申报:
|
||||
|
||||
- 允许把行数、稀有度、事件/效果计数从旧实数改到新实数。
|
||||
- 允许旧 `damage_taken` 行为测试增加危险格/错误封印路径,以体现用户已裁的全局语义;原敌方攻击与地形断言不得删除或放宽。
|
||||
- 禁止删除RAP8基线、每轮上限2、AP保留、刷新图零清图、伤害下限、五段序、同种子回放等断言。
|
||||
|
||||
## 十三、估时与切分预案
|
||||
|
||||
| T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | 合计 |
|
||||
|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 0.5 | 0.5 | 1.75 | 1 | 1 | 1.75 | 1 | 0.75 | 1 | **8d** |
|
||||
|
||||
浮动±0.5d。
|
||||
|
||||
切分预案:段一=T0–T4+T6(基座、全局受伤事件、刷新事实、数据接线,约4.75d);段二=T5+T7–T8(五件收益兑现与回归出口,约3.5d)。两段各自独立可测、独立提交。
|
||||
|
||||
## 十四、异常分级
|
||||
|
||||
L1(自救一次):编译错误、签名拼写、manifest登记遗漏。
|
||||
|
||||
L2(记录继续;同红三次升L3):既有无关文档域红;局部测试文案漂移。
|
||||
|
||||
L3(停卡写报告):
|
||||
|
||||
- CI红未修。
|
||||
- 同一伤害出现两次 `damage_taken`,或某路径完全不出现。
|
||||
- 石层缓冲使伤害归零、突破下限或改变治疗语义。
|
||||
- 刷新生效出现清图奖励/探索进度耦合。
|
||||
- BattleOrchestrator或RewardGrant结构预算被迫上升。
|
||||
- 机检键与本卡契约互斥;禁为凑绿保留无效成员。
|
||||
- 任何需求点找不到真实入口与特有可观测提交面。
|
||||
|
||||
## 十五、代拍板白名单
|
||||
|
||||
允许实现期自拍:
|
||||
|
||||
1. 私有函数换行、局部变量名、内部注释位置。
|
||||
2. 测试断言消息措辞与helper顺序。
|
||||
3. 错误扫描暴露的纯格式修复。
|
||||
4. 不改变语义的前提下复用现有 `units.heal()`、counter API与GP行格式。
|
||||
|
||||
超清单必停卡呈报:
|
||||
|
||||
- 数值、稀有度、cap、counter_id、事件/效果名、来源类别、文件职责、验收入口。
|
||||
- 任何把清图奖励、探索进度、临时生命重新引入本批的改法。
|
||||
|
||||
## 十六、零待裁点
|
||||
|
||||
七件名称、收益对象、全局受伤范围、叠加规则、时序、数值与稀有度均由最终确认单锁定。本卡零待裁点,开工门=U0。
|
||||
|
||||
## 十七、开工门
|
||||
|
||||
1. 由 iter-plan 编入迭代后,`/iter` 才可开工;无迭代外执行通道。
|
||||
2. 开工前四份GDD凭据在待办清单闭合、CI绿、T0实测无漂移。
|
||||
3. 本卡预计8个工作日,符合迭代硬线。
|
||||
@@ -0,0 +1,183 @@
|
||||
# 需求卡片——棋盘格边长口径收口批(探针门槛纠偏+格边长算式单点+冒烟扩四档板型)
|
||||
|
||||
状态行:v1.0 定稿——零待裁点(2026-10-30 用户裁:①探针不扩到 L3 推关;②最紧档 14×14 的可读断言落单元四档+探针实板推定两处;③算式单点提取用私有静态名,依据见 §2 第 5 条)
|
||||
|
||||
接收方:待办清单条目「棋盘格边长三方取值不一致——待扩板定档处置(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`。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
这批做什么:把「棋盘格边长」在代码侧残留的两处旧口径收干净。文档侧已于 2026-10-26 归一(口径只剩 battle.md 一节),代码侧还差两件——探针的可读门槛仍是 12×12 时代的 36px(比 battle.md 的可读下限 30px 严 6px,属用旧值当新标准),格边长冒烟只跑 L1/L2(13×13 与 14×14 两档板型无断言)。本批顺带把格边长算式收成单点(现在它内联在 `set_cell_area` 里,任何「在别的板型/别的面积上求值」的需求都只能复写算式=造第二口径)。
|
||||
|
||||
**行为零变化**:算式逐位不变,`set_cell_area` 的副作用顺序(英雄脚锚重置 → `_refresh_slab` → `_update_world_size`)零改动,玩家可见画面零变化。
|
||||
|
||||
完成定义(全部满足才算完):
|
||||
1. 代码侧可读门槛=30px,与 battle.md「可读下限 30px」一致;旧值 36 在 `game/` 下零残留。
|
||||
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)。
|
||||
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`(写卡段已同批入表),与在飞批次无文件交叠需编排段复核。
|
||||
|
||||
## 2. 现状事实
|
||||
|
||||
全部行号为 **2026-10-30 实查**——本批编入后须由 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` 为准。
|
||||
|
||||
## 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。
|
||||
- 🔴 **行为零变化**:`set_cell_area` 的副作用顺序与数值逐位不变;`rebuild`/`resized` 两条调用路径不改;画面像素零变化。
|
||||
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8「测试削弱申报」,未申报的删改由独立验收审计判红。
|
||||
- 🔴 **禁变异类测试与撤销类取证**:不得靠回退 hunk 或注入破坏证明断言有效。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
做:算式提取为 `_cell_for` 并改调;探针门槛 36→30 与注释改指 battle.md;探针新增「实板可用面积下 13/14 板格边长推定」断言与 board_view 零尺寸前置;格边长冒烟扩到四档板型并加算式单点等价断言;`game/tests/README.md` 能力行入表(写卡段同批,见 §5)。
|
||||
|
||||
不做(明示去向):
|
||||
1. **探针扩到 L3 推关**——2026-10-30 用户裁不做:成本是探针要真打穿两关(main 无跳关入口),且一旦红因是推关而非布局会污染该闸;最大板可读面改由 §7.3 的推定断言承担。
|
||||
2. **battle.md 算例表改数**——实现批禁写 `design/`;实测与算例的偏差走 §5 反哺点通道。
|
||||
3. **四档板型数值调整**——`board_gen.tres` 板尺寸由设计侧定,本批只读不写。
|
||||
4. **B 区高度 252 vs 270**——另一条在册待办(迭代 43 批后待裁三点),本批不碰。
|
||||
5. **`set_cell_area` 副产物顺序调整**——零触碰。
|
||||
6. **MC/数值验证**——无数值改动零跑。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| 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 归一后它们只写规则与指针,不存算例与定值档) | 无(本行占位——按规约查不出也要写明) |
|
||||
|
||||
## 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框架行补名 | 修改 | 否(机械面文档,写卡段同批完成) |
|
||||
|
||||
不触碰:`board_zone_view.gd`、`ui_assembler.gd`、`data/tables/board_gen.tres`、全部 `core/` 与 `data/` 代码、其余探针与测试套件。
|
||||
|
||||
## 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`。
|
||||
- [ ] 四档取档复核:对 `db.combat.board_gen.resolve_row(L, D)` 逐组取 (1,0)(2,0)(3,0)(3,9),实测返回 `board_width` 依次为 10/12/13/14——不符=L3 停卡退回写卡段。
|
||||
- [ ] 改前基线读数(孤立验证):`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_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 逐字消费)。
|
||||
|
||||
### 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 对拍基准)。
|
||||
|
||||
### 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)` 不改(本卡把「真实可用面积」的可读面交给探针,样本只承担算式不溢出面)。
|
||||
- [ ] 孤立验证:单件跑 `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 扫描面。
|
||||
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):
|
||||
- `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`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
|
||||
- 取用:T1–T3 全部产出。产出:代码域闸回执+三份 reports/ 产出物。
|
||||
|
||||
## 8. 验证闸(改动—验收对照表)
|
||||
|
||||
本批三处改动全部落在守卫面(探针门槛/测试覆盖/算式落点),游戏行为零变化,故无新玩法验收;用户目检一项登记不阻塞(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. **探针门槛 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` 换成别的入口。
|
||||
|
||||
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面是否绑在被测改动特有的可观测面(`cell_size`/`_cell` 取值与 `_cell_for` 返回)、核对削弱申报、确认全文无变异/撤销类取证;未过不得合并。
|
||||
|
||||
## 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。这是产品面问题(不是断言错),停卡呈报,禁调低下限、禁换样本凑绿。
|
||||
3. **四档取档对不上**——`resolve_row(1,0)/(2,0)/(3,0)/(3,9)` 实测尺寸与 §2 第 6 条不符(表已改而本卡未跟上)。
|
||||
4. **算式等价性破**——T1 后既有四套断言出现改前不存在的红,说明提取改变了算式语义。
|
||||
|
||||
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
|
||||
|
||||
1. `_cell_for` 内部局部变量名与注释措辞(算式、常量、clamp 数值不变)。
|
||||
2. 四档取样表用字典数组还是三元组数组(取值与断言不变)。
|
||||
3. 探针推定断言与零尺寸前置的失败消息文案、PASS 消息措辞(判据不变)。
|
||||
4. 尾缀〔格边长 V1〕–〔格边长 V4〕的字样编排(含义不变)。
|
||||
5. `BoardViewScript` 常量名(preload 载体命名)。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。** 2026-10-30 用户三项裁定已定案:①探针不扩到 L3(推关成本与红因混淆,最大板改走推定断言);②最紧档 14×14 的断言落两处(单元四档+探针实板推定);③算式提取用 `_cell_for` 私有名(§2 第 5 条 S13 实测依据)。编批由迭代编排段定。
|
||||
|
||||
## 12. 落位与可复用性预检
|
||||
|
||||
**复用面**:本批零新增文件、零新增类。`_cell_for` 抽自 `board_view.set_cell_area` 既有内联算式——不能只留在原处内联,是因为探针要在「非当前板型(13/14)× 真实可用面积」上求值、单元测试要在四档板型上求值,内联形态无法复用,复写算式就是造第二口径(正是本卡要消灭的缺陷形态)。探针与测试的既有工具件(`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` 同名或同义函数,无第二落点。
|
||||
|
||||
**分层与交互面**:`_cell_for` 是无状态纯函数——零成员读写、零信号、零 RNG、零 UI 依赖、零 `load()`;调用路径=`set_cell_area`(生产)/探针 `_check_one`(经脚本常量静态调用)/单元测试(同前)。preservation:不新增跨层依赖,Core 零 UI 红线零触碰(本批不触 `core/`)。
|
||||
|
||||
## 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` 局部变量反而减少)。达成手段:算式整体搬出,函数体只留一行调用。
|
||||
|
||||
**指标例外申报**:无。
|
||||
|
||||
## 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,151 +0,0 @@
|
||||
# 需求卡片:状态合并累加制——同名状态重复授予改加时长
|
||||
|
||||
> 日期: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 均为该裁决的实现面,无新增设计分叉。)
|
||||
@@ -1,244 +0,0 @@
|
||||
# 需求卡片 · 立绘显示档批(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 停机/回设计段裁档,另立新条。
|
||||
@@ -0,0 +1,188 @@
|
||||
# 需求卡片——站桩名字行提档与地台色合并批(名字行字号 12→14 + 受光线/描边两常量退役改指)
|
||||
|
||||
状态行:v1.1 定稿——零待裁点(grilling 复核过:reports/复核-grilling-站桩名字行提档与地台色合并批-2026-10-31.md,三阻断两建议全落;两点裁决已于 2026-10-31 闭合落文 art-bible v3.29,见 §11)
|
||||
|
||||
接收方:待办清单条目「迭代 43 批后待裁两点——名字行字号档位/换色常量退役」(状态=文档设计已闭合,本卡写完推进「已写卡」)
|
||||
|
||||
来源:**设计事实源**=`design/art/art-bible.md` v3.29——§7.4 小字号豁免条款「站桩名字行同理提档」句(名字行提档 `FS_HINT` 14 归六档、不增列②装饰 caption 在册面、几何随档联动)+§4.1 换色合并登记三处①②(`#C4AC84`→并入掩尘 `#C9AF83`;`#5A4C3A`→并入 `#4C4038`)。验收底线(2026-10-31 用户裁)=名字行清晰可读、长名不截字。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
这批做什么:把 art-bible v3.29 两处新裁决落进代码——①站桩名字行(敌人名+护甲字形+全部宽度测量与基线)从 `FS_CAPTION`(12px)提到 `FS_HINT`(14px),名字行几何随档联动;②`STAND_STONE_RIM`/`STAND_STONE_EDGE` 两枚旧色常量删除,两个绘制点改指合并目标色(站桩石板顶缘受光线与棋盘墙格口沿高光共用枚→掩尘 `BRICK`;石板描边→新常量承载 `#4C4038`),使贴图缺位时的程序化回退绘制与已落合并值的贴图一致。顺带了结迭代 43 移交小件①(能力清单补行——写卡段随本卡同批入表)。
|
||||
|
||||
完成定义(全部满足才算完):
|
||||
1. 名字行五处取值点全取 `UiKit.FS_HINT`,基线偏移按 14px 档重算(0.35h 规则→牌顶+18),素底牌几何单点 `_name_plate_rect`/`_name_baseline` 仍是唯一算式落点。
|
||||
2. `STAND_STONE_RIM`/`STAND_STONE_EDGE` 在 `ui_kit.gd` 删除且全仓零引用;`UiKit` 脚本常量表断言两枚不在册。
|
||||
3. 两枚绘制点改指:`cell_base_layer.gd` 口沿高光与 `standee_body.gd` 顶缘受光线→`UiKit.BRICK`;`standee_body.gd` 回退描边→新常量 `UiKit.PLAZA_EDGE`(=Color8(76,64,56))。
|
||||
4. 渲染 golden 三张板面基线与口沿高光换值一致(改后跑 render_gate,红则重锚三张、重锚后 0.00% 留痕;level_map/menu_boot 不触不重锚)。
|
||||
5. 能力清单补行落表(`game/tests/README.md` 能力行+覆盖域表战斗表现.md 行,本卡成文同批完成)。
|
||||
6. 代码域闸全绿;产出物三份落 `reports/`,独立验收审计未过不合并。
|
||||
|
||||
## 2. 现状事实
|
||||
|
||||
全部行号为 **2026-10-31 实查**——编入后 T0 按 T0 时点 master 重测重锚。
|
||||
|
||||
1. 名字行字号取值点五处,全在 `game/presentation/views/standee_body.gd`:`:334-335` 护甲字形宽测量(贴图缺位时)、`:336` 名字宽测量(`_name_plate_rect` 内)、`:467` 护甲字形宽测量(绘制侧)、`:474` 护甲字形绘制、`:476` 名字绘制——全取 `UiKit.FS_CAPTION`。
|
||||
2. 同文件另有 `FS_CAPTION` 合法在役一处:`:114` 站桩消散提示(§7.4 ②在册面「站桩消散提示」)——本批不动,禁误改。
|
||||
3. 名字行几何单点:`_name_plate_rect`(`:331-338`,牌高 26 固定=护甲图标 24px+上下各 1px;牌宽=行宽+8 水平居中;牌底沿=地台顶)+`_name_baseline`(`:342-343`,返回 `plate.position + Vector2(4.0, 17.0)`;注释 `:341` 「12px 字形基线=中心+4」)。基线偏移遵循本文件既有 0.35h 规则(立绘字形 `:434` 用 `ts.y * 0.35`;12×0.35≈4.2→4,与 `+4` 吻合)→14×0.35≈4.9→**5**,新基线=牌顶+13+5=**牌顶+18**。
|
||||
4. 名字行探针 `name_line_probe()`(`standee_body.gd:63`)返回 Dictionary:`baseline_y`/`plinth_top_y`/`plate`/`plate_color`/`armor_icon`/`armor_tex_bound`——消费面=`ui_struct_probe` 与 `standee_present_values_test`。
|
||||
5. 地台程序化回退(`standee_body.gd:380-383`):`TEX_STAGE_PLAZA` 缺位时三段绘制——板身 `UiKit.STAND_STONE`、描边 `UiKit.STAND_STONE_EDGE`(`:382`)、顶缘受光线 2px `UiKit.STAND_STONE_RIM`(`:383`)。贴图在位时不走此路径(九宫格拉伸域 `:388-393`,贴图已落合并值 `#C9AF83`/`#4C4038`)。
|
||||
6. 口沿高光:`game/presentation/views/cell_base_layer.gd:176` `canvas.draw_rect(..., UiKit.STAND_STONE_RIM, true)`(墙格南立面受光亮线,线厚常量 `FACADE_HIGHLIGHT_PX := 2.0` 于 `:9`;与地台顶缘线共用同枚常量,行内注释「同值单点」)。设计出处=art-bible §8.2 高墙瓦片体系条(南临空墙格=立面口沿高光 2px `Color8(196,172,132)`)。
|
||||
7. `ui_kit.gd`:`:188` `STAND_STONE_EDGE := Color8(90, 76, 58)`、`:189` `STAND_STONE_RIM := Color8(196, 172, 132)`;合并目标①=`BRICK := Color8(201, 175, 131)`(`:165` 掩尘,在役);合并目标②=`#4C4038`=Color8(76,64,56)——**全仓无既有 token**(2026-10-31 grep `Color8(76` 零命中),需新常量承载。
|
||||
8. 全仓 `STAND_STONE_RIM`/`STAND_STONE_EDGE` 引用面=定义 1 处+消费 2 文件 3 行(`:382`/`:383`/`cell_base_layer:176`)——tests/probe/sim 零引用(grep 实查),删除无隐性断裂。
|
||||
9. 既有断言面 `game/tests/unit/presentation/standee_present_values_test.gd`:V6 硬编码几何读数 `baseline_y=149.0`/`plinth_top_y=158.0`(±0.51);V7 源码锚 `contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT")`(contains 语义——名字行改 `FS_HINT` 后仍绿,零破坏);V5 先例=「绘制点取某常量」用源码锚承载。
|
||||
10. golden 触面:`game/tests/golden/` 板面三张(board_L1_dungeon/L2_underdark/L3_deep_underdark)渲染含墙格口沿高光像素——换值后逐像素比对必红;载体=`tools/render_gate.sh`(闸 05,窗口化)+重锚工具 `tools/golden_baseline_refresh.sh`。level_map/menu_boot 与站桩名字行均不在 golden 像素域。
|
||||
11. 能力清单:`game/tests/README.md` 无 `standee_present_values_test` 代表用例行(迭代 43 移交小件①在册待补,登记规格=能力「站桩呈现定值与素材绑定」/真实入口 `UiKit.load_tex()`/`StandeeBody.name_line_probe()`/代表用例 `standee_present_values_test.gd:_init`)。本会话即写卡会话,随本卡同批补行(§5)。
|
||||
12. gdstyle:`standee_body.gd` 无基线登记键(迭代 44 收尾 refresh 已清偿);本批零新增成员/函数,S13 预期零新增键。
|
||||
|
||||
## 3. 设计契约(违反即返工,标 🔴 触碰=L3 停机呈报)
|
||||
|
||||
- 🔴 **两个 12px 位不得混**:`FS_HINT` 只进名字行五取值点;`:114` 站桩消散提示的 `FS_CAPTION` 是 §7.4 ②在册面合法位,禁顺手改。
|
||||
- 🔴 **合并目标逐位**:受光线绘制点→`UiKit.BRICK`(`#C9AF83`),描边绘制点→`UiKit.PLAZA_EDGE`(=Color8(76,64,56));禁改 `BRICK` 值、禁引入任何近似替代值。
|
||||
- 🔴 **`STAGE_BG` 零触碰**:§4.1 ③已裁随舞台背景插画批处置——本批不改 `STAGE_BG` 值、不改 `battle_stage_view`、不动 `theme_projection_test.gd:669` 与 `ui_contrast_probe.gd:30`。
|
||||
- 🔴 **几何单点**:字号与基线联动只落在 `_name_plate_rect`/`_name_baseline` 两个既有单点内;探针、测试、任何调用方不得复写算式。
|
||||
- 🔴 **常量退役=真删**:`STAND_STONE_RIM`/`STAND_STONE_EDGE` 删除后禁留别名、转发常量或空注释占位(禁为机检保留无效成员)。
|
||||
- 🔴 **golden 重锚申报制**:三张板面基线重锚仅限口沿高光换值所致,重锚前后读数留实现报告;禁借重锚夹带其他像素变化。
|
||||
- 🔴 **测试削弱申报制**:允许改动的旧断言全集见 §8 申报节,未申报的删改由独立验收审计判红。
|
||||
- 🔴 **禁变异类测试与撤销类取证**:不得靠回退 hunk 或注入破坏证明断言有效。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
做:名字行五取值点提档+基线重算+探针增 `name_font_size` 键;`ui_kit.gd` 删两常量+增 `PLAZA_EDGE`;三处绘制点改指;golden 三张板面重锚;既有 V6 读数换代;能力清单补行(写卡段,本卡成文同批)。
|
||||
|
||||
不做(明示去向):
|
||||
1. **`STAGE_BG` 改值**——随「战斗区背景图替换」批(art-bible §4.1 ③改注在案)。
|
||||
2. **`:114` 消散提示与②在册面其他位**——合法 caption 档,零触碰。
|
||||
3. **口沿高光独立语义 token 化**——拆枚是命名扩张,值同源且 art-bible 已裁「统一为同一枚」,禁另立。
|
||||
4. **battle.md/art-bible 直改**——实现批禁写 `design/`;§4.1 源常量名退役后的指称换代走 §5 反哺点通道。
|
||||
5. **长名截字的主动钳制逻辑**——现行无钳制,本批不新增行为;若目检发现截字=产品面问题走 L3 停卡呈报。
|
||||
6. **MC/数值验证**——无数值改动零跑。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|
||||
|---|---|---|
|
||||
| design/art/art-bible.md(设计权威——实现批禁直改) | §4.1 换色合并登记①②句中「代码 `STAND_STONE_RIM`/`STAND_STONE_EDGE`」指称随退役失指;②合并目标 `#4C4038` 需补代码名 `PLAZA_EDGE`;§8.2 口沿高光行「`Color8(196,172,132)`」值随换值换代 | 反哺点通道:逐条列进 `reports/iter<N>-反哺点-<日期>.md`(改哪一节、改成什么、依据=本卡与实测读数),用户裁决后归文档批落文 |
|
||||
| design/art/art-bible.md §7.4 | 无(提档句 v3.29 已落文,实现后即为合规态) | 无(本行占位——按规约查不出也要写明) |
|
||||
| game/tests/README.md(机械面——归写卡段维护) | 能力行补行「站桩呈现定值与素材绑定」(迭代 43 移交小件①)+覆盖域表战斗表现.md 行补名 | 写卡段于本卡成文同批入表(已完成),实现批零增删零改行;批末核对表上名字与实况一致 |
|
||||
| design/gdd/ux/battle.md | 无——`:155` 名字行陈述(纯名字+护甲类型图标)不涉字号与色值 | 无(本行占位) |
|
||||
|
||||
## 6. 卡前文件结构图
|
||||
|
||||
| 文件 | 职责 | 新增/复用 | 行为改动? |
|
||||
|---|---|---|---|
|
||||
| game/presentation/views/ui_kit.gd | 删 `STAND_STONE_EDGE`/`STAND_STONE_RIM` 两常量;原位新增 `PLAZA_EDGE := Color8(76, 64, 56)`(注释=art-bible §4.1 ②合并目标·小石台描边回退用色) | 修改 | 是(色值换——对照表行 3/4) |
|
||||
| game/presentation/views/standee_body.gd | 名字行五取值点 `FS_CAPTION`→`FS_HINT`;`_name_baseline` 偏移 17.0→18.0+注释换代;回退描边 `:382`→`PLAZA_EDGE`、顶缘线 `:383`→`BRICK`;`name_line_probe` 增 `name_font_size` 键 | 修改 | 是(行 1/2/3/4) |
|
||||
| game/presentation/views/cell_base_layer.gd | 口沿高光 `:176`→`UiKit.BRICK`+行内注释换代 | 修改 | 是(行 3) |
|
||||
| game/tests/unit/presentation/standee_present_values_test.gd | 增 V10–V12 断言(提档/退役/新 token);V6 基线读数 149→150(申报) | 修改 | 是(测试面——行 1/2/3/4) |
|
||||
| game/tests/README.md | 能力行补行+覆盖域表战斗表现.md 行补名 | 修改 | 否(机械面文档,写卡段同批完成) |
|
||||
| game/tests/run_all.gd | 套件登记注释「V5–V9」→「V5–V12」换代 | 修改 | 否(注释换代,非行为改动) |
|
||||
|
||||
不触碰:`battle_stage_view.gd`、`board_view.gd`、`theme_projection_test.gd`、`ui_contrast_probe.gd`、`data/tables/` 全部、`core/` 全部。
|
||||
|
||||
## 7. 任务卡 T0..T3
|
||||
|
||||
### 7.1 T0——读数重锚与改前基线(估时 0.1 个工作日)
|
||||
|
||||
- [ ] 重锚 §2 全部行号(standee_body/cell_base_layer/ui_kit/test 五文件);复核实查结论第 7 条(`#4C4038` 无既有 token)与第 8 条(引用面仅三行)——不符=L3 停卡退回写卡段。
|
||||
- [ ] 改前读数(孤立验证):单件跑 `standee_present_values_test`(绿)+`python tools/gdstyle_gate.py`(三触面文件零新增键、既有键持平)+窗口化跑 `tools/render_gate.sh`(板面三张绿=改前基线确认,读数进实现报告 T0 节)。
|
||||
- [ ] 字体度量校验:`f.get_string_size("名", HORIZONTAL_ALIGNMENT_LEFT, -1, 14)` 实测 Fusion Pixel 14px 高度,核对 0.35h 推导(≈5);若实测推导出不同偏移,按实测定值并记实现报告「实现决定记录」。
|
||||
- 取用:本卡 §2。产出:重锚行号+三份改前读数+基线偏移定值(T1/T2 对拍基准)。
|
||||
|
||||
### 7.2 T1——名字行提档(估时 0.15 个工作日)
|
||||
|
||||
- [ ] `standee_body.gd` 五取值点(`:334-335`/`:336`/`:467`/`:474`/`:476`)`UiKit.FS_CAPTION`→`UiKit.FS_HINT`;`:114` 消散提示零触碰。
|
||||
- [ ] `_name_baseline` 返回 `plate.position + Vector2(4.0, 18.0)`;`:341` 注释改「14px 字形基线=中心+5(0.35h 规则,T0 实测校验)」。
|
||||
- [ ] `name_line_probe()`(`:63`)返回字典增键 `"name_font_size": UiKit.FS_HINT`(值出自几何单点所用字号常量)。
|
||||
- [ ] `standee_present_values_test.gd` 增三断言(尾缀〔名字行 V10〕):①probe 面——`int(nl.name_font_size) == UiKit.FS_HINT`(几何单点实读);②源码锚——`body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM")` 且 `body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col")`(名字/护甲两绘制点,沿 V7 先例);③计数锚——`body_src.count("UiKit.FS_CAPTION") == 1`(改后全文件仅剩 `:114` 消散提示一处——三处宽度测量点随此锚强制换代)。
|
||||
- [ ] V6 基线读数 149.0→150.0、判据句「地台顶上方 9px 档」→「8px 档」(§8 申报第 1 条)。
|
||||
- [ ] 孤立验证:单件跑 `standee_present_values_test`(V5–V10 全绿)+`python tools/gdstyle_gate.py`(standee_body 键只降不升)。
|
||||
- 取用:T0 重锚行号与偏移定值。产出:`name_font_size` 探针出口(T3 对照表行 1 判据面)+绿的单件日志。
|
||||
|
||||
### 7.3 T2——地台色合并(估时 0.15 个工作日)
|
||||
|
||||
- [ ] `ui_kit.gd` 删 `:188`/`:189` 两常量;原位新增 `const PLAZA_EDGE := Color8(76, 64, 56)`,注释「小石台描边(art-bible §4.1 换色合并②合并目标——贴图已落此值,缺位回退同值)」。
|
||||
- [ ] `standee_body.gd:382` `STAND_STONE_EDGE`→`PLAZA_EDGE`;`:383` `STAND_STONE_RIM`→`BRICK`;`:365` 附近回退注释「STAND_STONE 族」句随改。
|
||||
- [ ] `cell_base_layer.gd:176` `STAND_STONE_RIM`→`BRICK`,行内注释「同值单点」句改「合并归掩尘枚(art-bible §4.1 ①)」。
|
||||
- [ ] `standee_present_values_test.gd` 增两断言(尾缀〔名字行 V11〕〔名字行 V12〕;常量面载体=`load("res://presentation/views/ui_kit.gd").get_script_constant_map()`——经 Script 对象取常量表,`UiKit` 类引用直调 `get_script()` 不合法〔grilling 实测 parse error〕):V11=常量表无 `STAND_STONE_RIM`/`STAND_STONE_EDGE` 两键、`BRICK` 值=Color8(201,175,131)、源码锚 `body_src.contains("ped.size.x, 2.0), UiKit.BRICK, true")`(顶缘受光线)与 `cell_src.contains("FACADE_HIGHLIGHT_PX), UiKit.BRICK, true")`(口沿高光——`cell_src`=同法读 `res://presentation/views/cell_base_layer.gd`);V12=`PLAZA_EDGE` 值=Color8(76,64,56)(§4.1 ②合并目标逐位)+源码锚 `body_src.contains("UiKit.PLAZA_EDGE")`(回退描边)。
|
||||
- [ ] `standee_present_values_test.gd` 文件头注释「V5–V9 断言面」句与 `game/tests/run_all.gd` 套件注释同步换代「V5–V12」(非行为改动)。
|
||||
- [ ] 批内静态取证:`grep -rn "STAND_STONE_RIM\|STAND_STONE_EDGE" game/ tools/ --include="*.gd" --include="*.py" --include="*.sh"` 零命中,输出原文落实现报告(绘制点改指的观测面局限申报见 §8——色值进 draw 命令无运行期读出面,由「常量不在册断言+grep 零引用+PLAZA_EDGE 值断言」三件合成承载)。
|
||||
- [ ] golden 改后比对:窗口化跑 `tools/render_gate.sh`——板面三张红(口沿高光换值所致)=预期证据,读数落实现报告。
|
||||
- [ ] golden 重锚:跑 `tools/golden_baseline_refresh.sh` 重锚板面三张基线;复跑 `tools/render_gate.sh` 三张 0.00% 留痕;`level_map`/`menu_boot` 禁触(§3 重锚申报制)。
|
||||
- [ ] 孤立验证:单件跑 `standee_present_values_test`(V5–V12 全绿)+`python tools/gdstyle_gate.py`(零新增键)。
|
||||
- 取用:T1 产出与 T0 读数。产出:退役常量断言面+重锚后 golden 三张+grep 取证原文。
|
||||
|
||||
### 7.4 T3——批末收口与产出物落位(估时 0.1 个工作日)
|
||||
|
||||
- [ ] 批末代码域闸:`tools/run_all_gates.sh --code` 全绿(本批改动含 `game/` 下非 `.md` 行为载体);里程碑步出口跑一次全套;波内修复只做出红最小单元的最窄确认。
|
||||
- [ ] 能力行核对:`game/tests/README.md` 能力行「站桩呈现定值与素材绑定」与覆盖域表战斗表现.md 行名字与实况一致(本卡成文时已落,实现批禁改)。
|
||||
- [ ] **产出物落位卡**(轻闸全绿后、合并前写齐,方算收口):
|
||||
- `reports/iter<N>-实现报告-<日期>.md`——各卡状态+T0 三份改前读数与改后对拍+grep 取证原文+golden 重锚前后读数+实机目检总清单(登记不阻塞三项:名字行清晰可读且长名不截字/墙格口沿亮线观感无异/贴图缺位回退描边与顶缘线观感无异)+实现决定记录。
|
||||
- `reports/iter<N>-反哺点-<日期>.md`——§5 同步面 art-bible 两行逐条有落点。
|
||||
- `reports/iter<N>-验收审计-<日期>.md`——独立验收审计(未参与实现的会话或子代理执行,未过不得合并)。
|
||||
- 取用:T1–T2 全部产出。产出:代码域闸回执+三份 reports/ 产出物。
|
||||
|
||||
## 8. 验证闸(改动—验收对照表)
|
||||
|
||||
本批两处行为改动(名字行字号与几何、两枚色值换)+一节常量面退役;无新玩法验收。对照表 6 行对应 §1 完成定义第 1–4、6 条;第 5 条(能力行落表)经 T3 核对步承载(本卡成文时已落表)。能力行=**新增:站桩呈现定值与素材绑定**(本卡成文同批入表,见 §5)。
|
||||
|
||||
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | 名字行字号提档 FS_HINT 14 | `standee_body.gd` 五取值点+`name_line_probe` 增 `name_font_size` | 「名字行绘制与几何取 FS_HINT」`standee_present_values_test.gd:_init`(〔名字行 V10〕) | 单元 | `godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd` | probe 几何单点实读 `name_font_size==UiKit.FS_HINT`+两绘制点源码锚+`FS_CAPTION` 计数锚==1(三处测量点强制换代,沿 V7 先例) | 新增:站桩呈现定值与素材绑定 |
|
||||
| 2 | 名字行几何随档联动 | `_name_baseline` 偏移 17→18;V6 读数换代 | V6 既有断言更新值(〔样张批 V6〕,申报第 1 条) | 单元 | 同上 | `baseline_y=150±0.51`(牌顶+18)且 `< plinth_top_y`(文字不压地台判据不变) | 新增:站桩呈现定值与素材绑定 |
|
||||
| 3 | 受光线常量退役改指 BRICK | `ui_kit.gd` 删 `STAND_STONE_RIM`;`standee_body.gd:383`+`cell_base_layer.gd:176` 改指 | 「退役常量不在册+掩尘值逐位」同套件(〔名字行 V11〕) | 单元 | 同上 | 常量表无该键+`BRICK==Color8(201,175,131)`+顶缘线/口沿高光两处源码锚(正证改指目标色);grep 零引用作反证补充(观测面局限申报:色值进 draw 命令无运行期读出面,正证由源码锚承载) | 新增:站桩呈现定值与素材绑定 |
|
||||
| 4 | 描边常量退役改指新 token | `ui_kit.gd` 删 `STAND_STONE_EDGE`+增 `PLAZA_EDGE`;`standee_body.gd:382` 改指 | 「描边合并目标逐位」同套件(〔名字行 V12〕) | 单元 | 同上 | `PLAZA_EDGE==Color8(76,64,56)`(§4.1 ②目标逐位)+常量表无旧键+描边行源码锚;grep 零引用反证补充 | 新增:站桩呈现定值与素材绑定 |
|
||||
| 5 | 板面 golden 与口沿高光换值一致 | golden 三张板面基线重锚 | `tools/render_gate.sh` 三张 0.00% | golden | 窗口化 `tools/render_gate.sh` | 重锚后逐像素零差留痕;level_map/menu_boot 零触 | 新增:站桩呈现定值与素材绑定 |
|
||||
| 6 | 批末收口 | 全批 | 代码域闸 `tools/run_all_gates.sh --code` | 全量闸 | `tools/run_all_gates.sh --code` | 所选域全绿回执(`game/tests/.gates/receipt.json`) | 新增:站桩呈现定值与素材绑定 |
|
||||
|
||||
**测试削弱申报**(实现期允许的旧断言改动全集,超出即审计判红):
|
||||
1. V6 基线读数 `149.0→150.0` 与判据句「9px 档→8px 档」——规格换代(字号 12→14,0.35h 规则推导),非放宽;文字不压地台判据(`baseline_y < plinth_top_y`)零改动。
|
||||
2. 新增断言=纯增量;V5/V7/V8/V9 判据零改动(V7 源码锚 contains 语义,名字行改档后自动覆盖,零破坏)。
|
||||
3. golden 三张板面重锚=有意色值换代的基线换代(§3 申报制),重锚前后读数留实现报告。
|
||||
4. 显式禁止:删任何旧断言、改 `BRICK` 值、把探针/测试的字号读取换成别的入口。
|
||||
|
||||
**独立验收审计**:入口与报告=`reports/iter<N>-验收审计-<日期>.md`;审计方=未参与实现的会话或子代理,逐行核对对照表两端、抽查判据绑定面(`name_font_size`/常量表/`PLAZA_EDGE` 值)、核对削弱申报与 golden 重锚申报、确认全文无变异/撤销类取证;未过不得合并。
|
||||
|
||||
## 9. 异常分级
|
||||
|
||||
- **L1 自救**:单件跑红→定位该件最小单元修后重跑;`--only` 选跑零命中→修正选词重跑。
|
||||
- **L2 记录继续**:同一红连续 3 次同因→在实现报告「实现决定记录」记因后继续最窄确认。
|
||||
- **L3 停机写报告等用户**:
|
||||
1. **机检预算互斥=上游缺陷停卡退回写卡段**——`gdstyle_gate` 出 NEW 键或既有键恶化;本批零新增成员/函数,任何键恶化都不许改基线换绿,禁开豁免。
|
||||
2. **实机可读面本身不达标**——目检发现名字行截字、叠压血条或 14px 观感劣化;产品面问题停卡呈报,禁缩字号、禁加钳制逻辑凑绿(钳制=行为扩张,归新裁决)。
|
||||
3. **改前实查与 §2 不符**——引用面超第 8 条清单、`#4C4038` 已有 token、或 V6 读数与实装几何不匹配且非字号所致。
|
||||
4. **golden 重锚夹带**——重锚后 diff 域超出口沿高光像素带(含名字行字号变化渗入板面基线的可能——板面不含站桩,预期零渗入;若渗入=停卡呈报)。
|
||||
|
||||
## 10. 代拍板白名单(逐条列,超清单记实现报告「实现决定记录」)
|
||||
|
||||
1. 注释与断言消息文案措辞(判据、数值不变)。
|
||||
2. `name_line_probe` 新键名(`name_font_size`)与其在返回字典中的位置。
|
||||
3. 尾缀〔名字行 V10〕–〔名字行 V12〕字样编排(含义不变)。
|
||||
4. T0 字体度量实测偏移与 0.35h 推导不一致时的定值(按实测,记录依据)。
|
||||
5. `PLAZA_EDGE` 常量在 `ui_kit.gd` 的落位行与注释句。
|
||||
|
||||
## 11. 待裁点
|
||||
|
||||
**零待裁点,开工门=U0。** 两点裁决已于 2026-10-31 用户裁闭合并落文 art-bible v3.29(§7.4 名字行提档句、§4.1 换色合并登记句换代;舞台底 `STAGE_BG` 对已裁随背景插画批,不在本卡)。编批由迭代编排段定。
|
||||
|
||||
## 12. 落位与可复用性预检
|
||||
|
||||
**复用面**:本批零新增文件、零新增函数/类。既有复用——字号档位取 `UiKit.FS_*` 既有单点(§7.4 六档);受光线合并目标复用既有 `BRICK` token;断言承载复用 `standee_present_values_test` 既有套件与源码锚先例(V5/V7);golden 重锚复用既有工具 `golden_baseline_refresh.sh`;能力行补行沿迭代 43 移交小件登记规格,不另立能力名。
|
||||
|
||||
**唯一面**:新增常量 `PLAZA_EDGE` 唯一落点=`ui_kit.gd`(色板 token 单点——`#4C4038` 全仓无既有 token,2026-10-31 grep 证实;禁在 `standee_body` 内联字面值);`name_font_size` 探针键唯一落点=`name_line_probe`(探针共用面单点);名字行几何唯一算式落点=`_name_plate_rect`/`_name_baseline`(本批不改此结构)。2026-10-31 实查无同名/同义标识符冲突(`grep PLAZA` 仅 `TEX_STAGE_PLAZA` 纹理路径族)。
|
||||
|
||||
**分层与交互面**:全部改动在 presentation 层与单元测试层——零跨层新依赖、零 Core 触碰、零 ConfigDB/RNG 触碰(无随机、无表值变化);探针消费方向不变(`ui_struct_probe`/测试→`name_line_probe` 只读)。
|
||||
|
||||
## 13. 禁为机检文本保留无效成员
|
||||
|
||||
本批含常量退役——`STAND_STONE_RIM`/`STAND_STONE_EDGE` 删除后禁留别名常量、转发成员或注释占位凑「零引用」判据;机检键与卡片契约互斥时实现段停卡呈报,禁自造第三条路(code-conventions §1.1 同款)。
|
||||
|
||||
## 14. 实现规范预算
|
||||
|
||||
**新件三查**:本批零新增文件——三查不适用(写「无新增件」)。
|
||||
|
||||
**存量触面一查**:`standee_body.gd`(S13 扫描面内,无基线登记键)——零新增成员/函数,`name_line_probe` 返回字典增键非成员,预期 S13 零新增、既有键持平;`ui_kit.gd` 常量净 −1(删 2 增 1);`cell_base_layer.gd` 零成员变化(引用换值)。达成手段:全部改动为常量引用替换与既有函数体内取值/偏移改写。
|
||||
|
||||
**指标例外申报**:无。
|
||||
|
||||
**估时合计**:T0 0.1+T1 0.15+T2 0.15+T3 0.1=**0.5 个工作日**(区间 0.4–0.6;切分预案=若 T0 字体度量或 golden 重锚节外生枝超软盒,T2 拆出独立卡,T1 先行不阻塞)。
|
||||
@@ -0,0 +1,308 @@
|
||||
# 需求卡片:营地场景图新建批(交互式营地 · 七物件绑定 · 四柜台拆解)· 营地交互重做卡④
|
||||
|
||||
- 版本:**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
|
||||
- 归属:呈现层+美术线(`game/presentation/` + 场景素材)——营地交互重做五卡之四,**最大件**
|
||||
- 排期:未编入(2026-10-18 用户令:需求卡片不预占迭代号,批次归属按工作量另裁)
|
||||
- 开工门:**前置条件销号后进代码实现**(设计前置闸纪律不变:设计定稿前禁 `/iter` 开本卡代码段——美术素材线按铁律 2 走自立批并行推进,不占迭代位);前置解决清单见下方
|
||||
|
||||
## 🔧 状态:已退回美术设计段 · 非有效卡(2026-10-21 用户令;美术设计闭合后重立卡,再谈编迭代)
|
||||
|
||||
**背景**:本卡是新内容+新美术件需求,按 2026-10-10 用户立规(req-card 设计前置检查),设计定稿前不进代码实现。v1.x 曾用「待裁点」覆盖设计缺项(渠道/规格/设计稿/参照物),违立规;v2.0 一度按「回设计段」移出序列;v2.1 一度把「批内解决」执行成「Agent 代跑设计稿」——**v2.2 用户令终订:本卡是批内成员,前置条件在批内登记、逐项销号;但由谁解决、何时解决由用户定,美术/设计线是用户自立批(铁律 2),Agent 不代跑不代产**。设计前置闸纪律不变(设计定稿前禁 `/iter` 开本卡代码段),执行方式是「批内销号」而非剔除,也非 Agent 代劳。
|
||||
|
||||
**前置条件销号清单**(五项重启条件转为批内登记项):
|
||||
|
||||
| # | 前置条件 | 归属 | 状态(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:美术素材走自立批、不占迭代位;先产图后改代码)**:P1–P4 销号(设计定稿)→ 场景图美术产出(自立批,素材线并行推进,产物归档 artwork/→game/assets/art/)→ P5 文档落文 → 本卡代码段 `/iter`(与 ⑤ 同波)。工程面 ①②③ 代码在美术线推进期间并行开发、先行交付(③ 进档落点按 Q3 裁定乙暂行接现役 CampOverlay,本卡落地时改挂)——**四卡同在批内,分波收口,不是剔除**。
|
||||
|
||||
**归属纪律(2026-10-18 用户令订正,v2.2)**:本卡只负责**登记前置缺口与已有裁断**;前置条件由谁来解决、何时解决、设计稿/素材由谁产出,**由用户定**——美术/设计线是用户自立批(铁律 2),Agent 不代跑、不代产、不自行开工设计稿。本卡曾越权起草点选反馈设计稿 v0.1(2026-10-18),已按用户令撤回删除;Q8(出稿呈裁)仅是「若用户发令出稿时的产出方式」预裁,非开工授权。
|
||||
|
||||
**已裁事项(2026-10-18 grilling 轮次)**:
|
||||
|
||||
- Q4=甲:最近冒险条带随四柜台拆解撤下,生涯.md P6 同批注销(代拍板留痕);
|
||||
- Q5=甲:ConfirmDialog 扩第三钮(继续上一段冒险/开始新冒险/取消,cancelled 恒=无变更退回营地);
|
||||
- Q6=甲:Q1 裁 A 时过渡尺寸档=现役 1280 工程档+烛光黄 #F6C667/木色 #8A5A34 既有规格,终档归样张批冻结后置换(反哺点登记);
|
||||
- Q8=甲:设计稿产出方式=批内出稿呈裁(方向稿先行纪律照旧——2~3 张低成本方向稿供选划,判完方向才精制);
|
||||
- Q3 设计稿只覆盖根态点选反馈;进界面后的叠层规则随承载形态裁定派生(独立全屏页=场景根隐藏/单一内页切换=场景根留底)。
|
||||
|
||||
**仍待用户裁**:Q1 美术渠道 A/B(我的推荐:**A · CC0 改色**——06:23 已有配色规格、成本低、方向稿不受「从零构图」拖累;B 依赖规格清单输入且周期长。你说过不替你选,但我把推荐摊开了,等你一句 A 或 B,方向稿当天就能出);Q2 参照物(你后续找)。
|
||||
|
||||
## 0. 前置依赖与并行性(批内视角,v2.1)
|
||||
|
||||
> 本卡在批内,前置条件解决中(见卡头销号清单)——本节描述批内并行关系,非排期承诺。
|
||||
|
||||
- 卡③(选档界面)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 反哺点已注)。
|
||||
|
||||
## 1. 目标与完成定义
|
||||
|
||||
**目标(澄清①一句提炼照录)**:营地首页从「文字按钮菜单 + 列表式覆盖层」改为「**一张可点击的营地场景图**」——点场景中的具体物件进入对应功能界面。用户原话照录:「我是希望有一个营地地图的游戏画面;或者是一张营地的图片,跟 UI 的交互是点击图片中的某一处地方,然后再进入界面。不是完全普通的那种 UI 设计,不是各种框框的……我点击某些具体的东西之后,就可以进入对应的操作界面。」(称呼订正:「它就是一个营地的画面;并不是一张地图。」)
|
||||
|
||||
**七物件绑定(澄清④结构树照录,逐一落地)**:
|
||||
|
||||
| 物件 | 绑定目标 | 现状定性 |
|
||||
| ------- | -------------------------- | ------------ |
|
||||
| 营地大门 | 冒险(选路屏/战斗屏已实现,不动;断点弹窗保留移位) | 入口新建+动线迁徒 |
|
||||
| 锻炉·铁砧 | 锻造工坊 | 界面已实现 · 入口改挂 |
|
||||
| 宝箱 | 秘宝匣 | 界面已实现 · 入口改挂 |
|
||||
| 床铺旁笔记本 | 生涯记录 | 界面已实现 · 入口改挂 |
|
||||
| 木桩 | 树淬炼天赋树 | 界面已实现 · 入口改挂 |
|
||||
| 布告板/悬赏板 | 奖励格图鉴 | 界面已实现 · 入口改挂 |
|
||||
| 篝火 | 离开营地/回主菜单 | 新建小动线 |
|
||||
|
||||
**纵深稿(用户原话照录)**:「远景是天空;中景是一个围栏,还有营地大门;近景中间是一个篝火;左边是锻炉铁砧;右边是我们的床铺,宝箱等。」
|
||||
|
||||
**大门两分支(裁决 5 照录)**:有未完冒险 → 弹「继续上一段冒险 / 开始新冒险」;无未完冒险 → 弹「是否开始新冒险:是/否」。**点大门永远先出确认,不直接进冒险**(「断点续玩也是开始冒险的两个分支之一……它算是开始冒险的一个子节点」)。**「有断点」分支的三出口弹窗形态待裁(Q5)**——现役 ConfirmDialog 双钮表达不了两个肯定性动作。
|
||||
|
||||
**钥石余额(裁决 10 照录)**:场景**顶部小 HUD 栏**(用户原话「先按顶部小HUD栏设计」——provisional 措辞留痕;承载落卡⑤)。
|
||||
|
||||
**相对现状,新增了什么**:
|
||||
|
||||
1. 交互式营地场景图(场景素材+场景屏+七物件命中区与点选交互)——从零新建;
|
||||
2. 现四柜台覆盖层(CampOverlay)拆解:锻造工坊/秘宝匣/天赋树/奖励格图鉴四界面抽为独立可开界面,入口改挂场景物件;生涯页入口自主菜单改挂笔记本;
|
||||
3. 大门动线(开始冒险/继续上局两分支+断点确认弹窗从主菜单移位);
|
||||
4. 篝火回主菜单小动线;场景屏顶栏预留位(卡⑤ HUD);
|
||||
5. camp 截图断言段与六探针营地段的迁移(**现无 camp 逐像素 golden 基线**——v1.1 订正,见契约 10)。
|
||||
|
||||
**完成定义**:
|
||||
|
||||
1. 营地首页=可点击场景图;七物件绑定逐一在役,点各物件进对应界面;
|
||||
2. 四柜台拆解完毕:CampOverlay 覆盖层形态退役,五界面(锻造/秘宝/生涯/天赋树/图鉴)经新入口可达且**功能与写口链路零回归**(presenter 改造=承载面迁移的一部分——见契约 4);
|
||||
3. 大门两分支确认弹窗在役;点大门不直接进冒险;
|
||||
4. 篝火可回主菜单;场景屏顶栏预留位就绪(卡⑤联验);
|
||||
5. camp 截图=场景图形态(迭代内验收=截图对比,澄清⑥);探针断言迁移完毕;
|
||||
6. 美术约束(48 色板/禁入带/语义色/WCAG)入闸或登记缺口;**用户目检项登记 07**(生命周期末步,不阻塞);
|
||||
7. 批末重闸按改动域选档全绿;三份 `reports/` 产出物写齐(T6)。
|
||||
|
||||
## 2. 现状事实(2026-10-18 实查;开工日 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` |
|
||||
|
||||
## 3. 设计契约(红线,违反 = L3 停机呈报)
|
||||
|
||||
🔴 **契约 1 · 交互式场景图形态**:营地首页=一张可点击的营地场景图,点物件进界面(澄清①)。**禁退回文字按钮列表+面板覆盖层形态**(澄清②反参照:「完全普通的那种 UI 设计」「各种框框」——用户判现状「交互流程完全错误」)。
|
||||
|
||||
🔴 **契约 2 · 七物件绑定逐一落地**:大门/锻炉铁砧/宝箱/笔记本/木桩/布告板/篝火七个绑定(§1 表)缺一不可;禁合并、禁以「其他入口」替代(如顶栏文字钮)。
|
||||
|
||||
🔴 **契约 3 · 大门两分支**(裁决 5):有未完冒险→「继续上一段冒险/开始新冒险」二选一弹窗(**三出口形态=Q5 待裁**);无→「是否开始新冒险:是/否」弹窗。**点大门永远先出确认,不直接进冒险**;弹窗类=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`(与其余六口不一致)——**保持原样,禁「顺手统一」防行为漂移**。
|
||||
|
||||
🔴 **契约 5 · 图鉴内容保持挂起**:只改入口(布告板),不激活图鉴内容(F11:2026-10-05 用户令冻结,重启须用户发令);布告板点开的图鉴=现行占位态(T3 定义占位壳)。
|
||||
|
||||
|
||||
🔴 **契约 6 · 冒险进行中不开营地**(澄清③6):营地是存档语境(局外)枢纽,现状口径延续;局内只读面=状态面板(在役,本批零改动——其「营地三柜台进度」文案若因拆解失义,列反哺点归文档批订正)。
|
||||
|
||||
🔴 **契约 7 · 美术硬约束**:48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1(F15);场景素材全局纹理过滤=Nearest(project.godot 现行默认,像素纪律)。
|
||||
|
||||
🔴 **契约 8 · 像素规格不硬定**:尺寸档/色板/明度档只写指针(战斗一屏样张批冻结的规格清单,F16);卡内禁自定像素规格数值;规格清单未产出时的输入缺口=Q6 裁定(禁批内自补、禁 T0 现场发挥)。
|
||||
|
||||
🔴 **契约 9 · 迭代内验收=截图对比+终稿落盘口径(v1.1 补)**(澄清⑥):camp 截图须为场景图形态;用户实机目检=生命周期最后一步、统一进行、**不阻塞迭代**——目检项登记 07 清单即完成义务。**T1 终稿落 `game/assets/art/` 的「验收」口径=迭代内验收**(截图对比+shot_checker 机检+方向稿用户选划)——营地屏整屏样张按既定排队行延后(§0),不构成对 art-bible §7.5「前置」条的违背(§5 反哺点注)。
|
||||
|
||||
🔴 **契约 10 · 探针断言迁移契约(v1.1 订正,属仓库既有测试纪律的卡内成文,非用户裁决)**:F8 全量位点逐点显式迁移并留痕;**现无 camp 逐像素 golden 基线**(F17:golden 现役仅棋盘三张+menu_boot)——本批不建 camp golden(「把不存在的东西列进迁移清单」禁),camp 截图验收=shot_probe 出图+shot_checker 语义断言段;若要新建 camp golden 须另裁登记(L3)。
|
||||
|
||||
## 4. 范围边界
|
||||
|
||||
**做**:场景素材产出(渠道按 Q1 裁定);场景屏+七物件命中区与点选交互(反馈细节按 Q3 设计稿);四柜台拆解与五界面入口改挂(含 presenter 承接面改造);大门动线两分支+断点弹窗移位;篝火回主菜单;场景屏顶栏预留位;断言/探针迁移;反哺点清单。
|
||||
|
||||
**不做**(明示去向):
|
||||
- **不做贴图重构批**(澄清③1):柜台场景贴图/钥石顶栏图标/永久强化条目图标的达标化置换——搁置未废,待交互重做完形后重新评估立项(F12);
|
||||
- 不激活图鉴内容(契约 5);
|
||||
- 不改五界面功能与写口链路(契约 4);
|
||||
- 不改冒险屏/选路屏/战斗屏本体(F7 已实现不动);
|
||||
- 不做整屏样张闸、不替代样张批、不新建 camp golden(§0/契约 10);
|
||||
- 局内状态面板零改动(契约 6);
|
||||
- ESC 浮窗与顶栏 HUD 实现 → 卡⑤(本卡只留顶栏位+定义刷新单口);
|
||||
- 选档界面/主菜单/数据层 → 卡③/卡②/卡①。
|
||||
|
||||
## 5. 关联文档同步面
|
||||
|
||||
> **v1.3 口径**:本批禁写 `design/` 整目录与根规则文件,唯一可写目录 = `reports/`;下表一律执行为列进反哺点清单。美术素材产物按素材线纪律落 `artwork/`(在途)→ `game/assets/art/`(终稿),不随 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/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/*` | 实现/断言/终稿素材本体 | 实施本体(可写) |
|
||||
|
||||
## 6. 任务卡
|
||||
|
||||
> 估时按 Claude 工作日口径;每卡出口自跑轻闸;开工日 T0 复核全部行号。**估时主变量=美术渠道(Q1 待裁)、点选反馈设计稿往返(Q3)与规格输入(Q6)**。
|
||||
|
||||
### T0 事实坐实与设计输入齐备检查(≈0.5d)
|
||||
|
||||
- 复核 §2 全部行号;四柜台界面清单与探针面清单(F8)逐点过;
|
||||
- 设计输入齐备检查:Q1(美术渠道)/Q2(参照物)/Q3(点选反馈细节)/Q4(条带去留)/Q5(三出口弹窗)/Q6(规格缺口)状态逐项核——**未裁项对应的任务段禁开工**(缺输入=呈报,禁批内自补);
|
||||
- 规格清单依赖核检:战斗一屏样张批(06 待设计)产出时点 vs 本批排期——按 Q6 裁定执行(禁 T0 现场发挥);
|
||||
- 与卡③对齐 ConfirmDialog 消费方编排机制(T4 互指)、与卡⑤对齐顶栏位与刷新单口(§0 跨卡接口)。
|
||||
|
||||
### T1 场景美术产出(≈2–4d,渠道 A/B 待裁 Q1)
|
||||
|
||||
- 按纵深稿构图(远景天空/中景围栏+大门/近景篝火居中/左锻炉铁砧/右床铺+宝箱)出场景图;七物件可辨性=第一合格线(玩家一眼找到点哪);
|
||||
- 美术硬约束入产线(契约 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 张低成本方向稿供用户选划,判完方向才精制(样张卡「方向稿先行」同型纪律;选划记录三段表进开工单)。⚠ 本条为**本卡自我从严的执行纪律**,非用户新裁决——用户若要以单路线直接精制,从其裁。
|
||||
|
||||
### T2 场景视图与点选交互(≈1.5–2d)
|
||||
|
||||
- 场景屏承载(camp_screen 屏壳改造 vs 新屏壳——§9 拍板):场景图铺底+七物件命中区;
|
||||
- 点选交互:命中区判定+反馈形态按 Q3 设计稿(悬停高亮/提示文案/可点区域判定三项);
|
||||
- **场景屏顶栏预留位(v1.1 注明)**:卡⑤ HUD 落位;**刷新入口=本卡 T3 定义的营地刷新单口**(卡⑤同口挂接);
|
||||
- **叠层规则显式派生(v1.1 补)**:随 §9 第 2 条承载形态裁定派生并写入 V2 断言——独立全屏页=场景根隐藏(生涯先例);单一内页切换=场景根留底;「缩放虚化」是两选项之外的第三形态(涉新演出面+素材),禁批内自选(若设计稿/用户想要,升格裁点);
|
||||
- 键盘焦点/手柄不涉(08:191-192 排 Beta;ESC 属卡⑤用户明示裁决面)。
|
||||
|
||||
### 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 裁定执行。
|
||||
|
||||
### 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 明示,函数随卡②删,防两卡互以为对方在处理)。
|
||||
|
||||
### T5 断言、探针与 golden 迁移批(≈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 补);注入→最窄闸见红→精确还原→记录。
|
||||
|
||||
### T6 产出物落位卡(≈0.5d,末卡)
|
||||
|
||||
三份 `reports/` 写齐(实现报告含方向稿选划记录三段表、探针迁移留痕表、变异记录表、目检总清单/代拍板追认单/反哺点清单)。
|
||||
|
||||
**估时合计 ≈9–12d**(v1.1 上调:presenter 承接面+三钮弹窗+坏槽态联动+探针深员面增量)。**天然切分点=T1 出口**(场景素材归档齐)——美术线与代码线可分批交付(用户批次归属裁);若单批跑超迭代盒,T3 末(四柜台拆解完)为次切分点。
|
||||
|
||||
## 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–V10、V12 机检自动化(能力验证经调试接口直改断点/钥石/解锁状态,沿用 DebugOverlay/DS_DEBUG 门控);V11 归用户目检(生命周期末步统一进行,不阻塞迭代)。
|
||||
|
||||
**闸口径**:轻闸每卡出口自跑;批末重闸按改动域选档(本批碰 `game/` 代码/断言/终稿素材 → `--code`)全绿才合并;MC 零跑(零数值零行为——纯呈现+承载迁移)。
|
||||
|
||||
## 8. 异常分级
|
||||
|
||||
- **L1(自救)**:断言写法、探针路径、命中区微调、命名。
|
||||
- **L2(记录继续)**:场景屏承载形态选择(§9 域内)、点选反馈参数(Q3 设计稿域内)、四界面承载形态(§9 域内)、方向稿张数在 2–3 内自选、图鉴占位文案称谓订正。
|
||||
- **L3(停机呈报)**:Q1–Q6 未裁即需开工对应任务段;规格清单(样张批)晚于本批且 Q6 未裁;golden 漂移越出预期面(若涉及既有棋盘/menu 基线);发现五界面任一功能/写口链路被意外触碰(契约 4 破);参照物到手后与纵深稿结构性冲突(呈报,禁批内自行改构图);美术渠道 A/B 执行中撞墙(如 CC0 改色达不到七物件可辨性);提议新建 camp golden(契约 10——须另裁)。
|
||||
|
||||
## 9. 代拍板白名单(超清单必呈报)
|
||||
|
||||
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 分支命名。
|
||||
|
||||
## 10. 裁点与设计议程(v2.1:本卡在批内,以下为前置解决议程)
|
||||
|
||||
> Q4/Q5/Q6/Q8 已裁(裁定见卡头);Q1/Q2 仍待用户。本节约等于批内设计段的工作清单。
|
||||
|
||||
### Q1 · 营地场景图的美术渠道(**仍待用户裁——批内第一议程,阻塞 P2 规格分支与方向稿**)
|
||||
|
||||
**背景**:澄清⑧2——「美术渠道 A/B 未裁:CC0 改色 vs 插画级新产——原贴图批的渠道问题对营地场景图同样成立,本讨论未裁决」。
|
||||
|
||||
**我的推荐: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)零改动。
|
||||
@@ -0,0 +1,94 @@
|
||||
# 营地交互重做 · 需求澄清结论
|
||||
|
||||
> 2026-10-17~18 需求讨论澄清会话产出。讨论性质:**只澄清「我要什么」,不出设计稿、不钉像素规格**。
|
||||
> 本文供主项目会话建需求卡片使用;讨论会话不直接写卡。
|
||||
> 缘起:原议题「UI 贴图重构批」讨论中发现病根在交互层——用户裁「贴图重构批搁置,先重做交互方式」,故本文替代原《贴图重构批·需求澄清结论》。
|
||||
> 版本:**v1.0**(2026-10-18 澄清会话定稿;2026-10-18 文档批入库时补标版本行,供 06 条目「文档设计已闭合」凭据引用)
|
||||
|
||||
---
|
||||
|
||||
## ① 目标体验(用户原话 + 一句提炼)
|
||||
|
||||
**用户原话(2026-10-17,照录不美化)**:
|
||||
|
||||
> 「我是希望有一个营地地图的游戏画面;或者是一张营地的图片,跟 UI 的交互是点击图片中的某一处地方,然后再进入界面。不是完全普通的那种 UI 设计,不是各种框框的;比如一张图片里面有篝火,有锻造的工坊,还有一些床铺之类的东西。我点击某些具体的东西之后,就可以进入对应的操作界面。」
|
||||
|
||||
**称呼订正(用户原话)**:「营地地图,这个称呼不太对,它就是一个营地的画面;并不是一张地图。」
|
||||
**性质注记(用户原话)**:「可以备注好原话,写到文档里面去,但它不算是一个最终的设计。」方向确认:「没错的,就是要把它改成一个交互式的场景图。」
|
||||
|
||||
**一句提炼**:营地首页从「文字按钮菜单 + 列表式覆盖层」改为「一张可点击的营地场景图」——点场景中的具体物件进入对应功能界面。
|
||||
|
||||
## ② 参照与反参照
|
||||
|
||||
- **参照物**:目前没有。用户原话「目前还没有一个参照,我可以后续找一下」——**遗留待补**。
|
||||
- **反参照(用户明示拒绝的方向)**:「完全普通的那种 UI 设计」「各种框框」——即文字按钮列表 + 面板覆盖层的传统菜单形态。现状主菜单/营地覆盖层正是此形态,被判「交互流程完全错误」(用户原话)。
|
||||
|
||||
## ③ 非目标清单
|
||||
|
||||
1. **不做贴图重构批**(本讨论前已搁置):柜台场景贴图、钥石顶栏图标、永久强化条目图标的达标化置换,等交互重做定形后再议。
|
||||
2. **篝火不绑休息/恢复**:用户原话「我们是在一段冒险前,不存在休息恢复之类的概念」。
|
||||
3. **档槽不显示「有无未完冒险」**:摘要只要三项(见⑤)。
|
||||
4. **美术定档不翻案**:明亮轻快像素风、48 色板、HSV 300°–345° 禁入带、语义色纪律、WCAG ≥4.5:1——全局硬约束继续有效,不在本讨论范围。
|
||||
5. **像素级规格不在本讨论硬定**:尺寸档/色板/明度档归「战斗一屏样张批」冻结的规格清单(排期关系已定)。
|
||||
6. **冒险进行中不开营地**:营地是存档语境(局外)枢纽,现状口径延续。
|
||||
|
||||
## ④ 目标结构(节点树 + 现状差距三分类)
|
||||
|
||||
```
|
||||
主菜单(需改动:6 钮 → 3 钮)
|
||||
├─ 进入存档(完全没有 · 新建)
|
||||
│ └─ 选档界面(完全没有 · 新建):3 存档位,新档/进档/删档
|
||||
│ └─ 营地场景图 · 交互式(完全没有 · 新建;现四柜台覆盖层拆解)
|
||||
│ ├─ 营地大门 → 冒险〔选路屏/战斗屏已实现,不动;断点弹窗保留移位〕
|
||||
│ ├─ 锻炉·铁砧 → 锻造工坊(界面已实现 · 入口改挂)
|
||||
│ ├─ 宝箱 → 秘宝匣(界面已实现 · 入口改挂)
|
||||
│ ├─ 床铺旁笔记本 → 生涯记录(界面已实现 · 入口改挂)
|
||||
│ ├─ 木桩 → 树淬炼天赋树(界面已实现 · 入口改挂)
|
||||
│ ├─ 布告板/悬赏板 → 奖励格图鉴(界面已实现 · 入口改挂)
|
||||
│ └─ 篝火 → 离开营地/回主菜单(新建小动线)
|
||||
├─ 设置(已实现 · 不动)
|
||||
└─ 退出游戏(已实现 · 不动)
|
||||
```
|
||||
|
||||
**场景纵深稿(用户原话)**:「远景是天空;中景是一个围栏,还有营地大门;近景中间是一个篝火;左边是锻炉铁砧;右边是我们的床铺,宝箱等。」
|
||||
|
||||
**现状文档依据核查**:入口拓扑有 spec(`design/gdd/ux/camp.md`、`跨局循环/生涯.md`),但形态无设计——`生涯.md` 明示「不画布局稿」,现四柜台并排为迭代 21 T6 程序占位(代码注释原文「视觉终稿归 HUD 营地视觉批」)。**「从哪进」有文档,「进去后什么形态」从未被设计过。**
|
||||
|
||||
**病根诊断(用户原话)**:「主菜单界面应该只有 3 个入口:①进入一个存档 ②游戏的设置 ③退出游戏。开始冒险,和营地相关的都应该是一个存档内的东西;现在似乎没有存档这个概念;我感觉是之前做垂直切片的时候,进行扩充时没有做好这方面的考虑。」
|
||||
事实补钉:存档在数据层存在(`save_manager.gd`,已到存档 v7),但是**隐式单档**——交互层无存档概念、无档槽、无选档界面。
|
||||
|
||||
## ⑤ 关键交互裁决(全部为用户拍板,照录)
|
||||
|
||||
| # | 裁决 | 用户原话/依据 |
|
||||
|---|---|---|
|
||||
| 1 | 主菜单只留 3 入口:进入存档 / 设置 / 退出游戏 | 见④病根诊断引文 |
|
||||
| 2 | 多存档制,**3 个存档位** | 「多存档可以让它有三个存档位」 |
|
||||
| 3 | 删档:**要**,且必须**二次确认** | 「需要有删档,也确实是需要有二次确认的」 |
|
||||
| 4 | 档槽摘要=**钥石余额、通关次数、冒险次数(总局数)**三项,不含未完冒险进度 | 「摘要信息直接是钥匙余额和通关次数,冒险次数……」「不需要显示有无未完成的冒险;只要我提到的那三个就够了」 |
|
||||
| 5 | 大门点击:**有未完冒险** → 弹「继续上一段冒险 / 开始新冒险」;**无未完冒险** → 弹「是否开始新冒险:是/否」。点大门永远先出确认,不直接进冒险 | 「断点续玩也是开始冒险的两个分支之一……它算是开始冒险的一个子节点」+ 两分支原文 |
|
||||
| 6 | 天赋树 → **木桩** | 「天赋树是自身的加强,可以搞一个木桩这样的东西」 |
|
||||
| 7 | 图鉴 → **布告板/悬赏板** | 「布告板/悬赏板;就这个吧,挺不错的」 |
|
||||
| 8 | 篝火 → **离开营地/回主菜单** | 用户明示选② |
|
||||
| 9 | **ESC 键**唤起浮窗:返回主菜单 / 设置 / 退出游戏 三项 | 「ESC键可以唤起一个浮窗,里面有返回主菜单,设置,退出游戏三项」 |
|
||||
| 10 | 钥石余额:场景**顶部小 HUD 栏** | 「先按顶部小HUD栏设计」 |
|
||||
|
||||
## ⑥ 验收方式(用户原话 + 提炼)
|
||||
|
||||
> 「迭代过程中,只要有截图对比;后续我目检是统一来进行的;我目检是作为我们整个开发生命周期中最后一个步骤……这是不会阻塞迭代过程的。」
|
||||
|
||||
- **迭代内验收=截图对比**(不阻塞合入);
|
||||
- **用户实机目检=整个开发生命周期最后一步,统一进行,不阻塞迭代**;目检后用户开启新一轮需求循环。
|
||||
|
||||
## ⑦ 切批原则(用户原话)
|
||||
|
||||
> 「可以拆成多个需求卡片,但不一定需要放到多个迭代批次中;迭代批次是根据工作量大小来的,而需求卡片是根据它的工作内容来进行划分的。」
|
||||
|
||||
**工作内容维度的天然分卡线索**(供建卡参考,不预占):①存档制数据层改造(隐式单档 → 3 档槽,save_manager + 存档格式);②主菜单三入口化;③选档界面;④营地场景图(含美术产出 + 物件点选 + 入口改挂);⑤ESC 浮窗 + 顶部 HUD 栏。
|
||||
|
||||
## ⑧ 遗留与待裁
|
||||
|
||||
1. **参照物**:用户后续找(②),找到后补充进卡。
|
||||
2. **美术渠道 A/B 未裁**:CC0 改色 vs 插画级新产——原贴图批的渠道问题对营地场景图同样成立,本讨论未裁决,归建卡时或样张批规格语境下裁。
|
||||
3. **物件点选反馈细节**(悬停高亮/提示文案/可点区域判定)未谈,归设计稿阶段。
|
||||
4. **现营地覆盖层四柜台的拆解方式**(各界面保留、入口改挂)是方向级结论,具体拆解归实现卡。
|
||||
5. **贴图重构批**:搁置状态,未废——待交互重做完形后重新评估立项。
|
||||
+3
-5
@@ -5,8 +5,8 @@
|
||||
## 当前位(只回答「现在该做什么」;三行内随批次换代,禁止在此扩写成状态快照)
|
||||
|
||||
- **阶段**:M3——Alpha 版本的完整实现(2026-10-09 立规:未明令宣布完成前停留 M3,迭代只排 M3 需求,M4 不排期;根目录 stage.txt 为旧阶段开关仅存档)。
|
||||
- **迭代位**:迭代 1–47 全部已完成——迭代 47(营地模态句柄子容器批卡⑥+营地场景图新建批卡④+Esc浮窗与营地顶栏批卡⑤)2026-11-01 合回(合并提交 3443890d,独立验收审计处置后通过),批后收尾闭合(2026-11-02):反哺点十二条处置闭合(architecture v1.28 营地域六件归属行增补+D1 营地面与 ui_registry 行现状化+层序合同槽换代与产品码首个键盘输入入口注记/camp.md v2.4 顶栏刷新单口实装回填/art-bible v3.35 围墙细条实装落盘值与营地场景环境子板 32 色行/camp-scene-prompts v1.4 八物件落位实测表与量化缩放实况注记/生涯.md v1.4 P6 甲落地条带撤下/08-Alpha v1.23 键盘行 Esc 例外注记与营地两行在役换代/asset-path-map 与美术清单 §3.5 营地十件实装入库确认;反哺 #10 shot_checker 色数判据互斥已随 82b2cbea/8e294de9 用户裁决落地〔阈值 40、camp 特判删除、色数收敛登记待办新条〕——零落文面)、目检五项登记体验验证清单 ㊺、产出物退役九件(三需求卡片+反哺点+实现报告+验收审计+需求澄清+美术生成方案提案+卡④⑤双审复核)、待办清单营地交互重做条整条删除(五卡全落地,状态机出口)+UI 贴图重构条与图鉴条落地注记+palette_check 双门机检化与 CAMP_HEADER_BAND 令牌退役评估两条新登记(未决先迁);提案-营地色彩与氛围脚本按 art-bible §6.2/camp-scene-prompts 依据行活引用保留。迭代 46 收尾记录见 git 提交 73fc11eb,迭代 45 见 457e0739。
|
||||
- **下一步**:迭代 48(状态合并累加制+立绘显示档+战斗区背景替换三卡,≈6.1 个工作日)已编排待 `/iter` 发令开工。待编池:冒险前资源配比预设(冻结备选)、迭代 30 原卡②③(2026-10-17 已剔出迭代 30 规划、退回待设计,待美术/设计定稿后重新立卡)。
|
||||
- **迭代位**:迭代 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 规划、退回待设计,待美术/设计定稿后重新立卡)。
|
||||
|
||||
## 里程碑时间线
|
||||
|
||||
@@ -79,9 +79,7 @@
|
||||
| 迭代 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 | 已完成(2026-11-01) | 已退役(git 历史可考) | 2026-11-01 | ≈8.625 个工作日 | 已退役(git 历史可考) |
|
||||
| 迭代 47 | 已完成(2026-11-01) | 已退役(git 历史可考) | 2026-11-01 | ≈15 个工作日〔用户特批 2026-11-01 超 10 个工作日〕 | 已退役(git 历史可考) |
|
||||
| 迭代 48 | 待开工 | reports/需求卡片-状态合并累加制批-2026-11-01.md<br>reports/需求卡片-立绘显示档批-2026-11-01.md<br>reports/需求卡片-战斗区背景替换-B区舞台插画与地面层-2026-10-31.md | 2026-11-01 | ≈6.1 个工作日 | — |
|
||||
| 迭代 46 | 待开工 | reports/需求卡片-换图系双基座七件批-2026-10-31.md<br>reports/需求卡片-站桩名字行提档与地台色合并批-2026-10-31.md<br>reports/需求卡片-棋盘格边长口径收口批-2026-10-30.md | 2026-10-31 | ≈9.125 个工作日 | — |
|
||||
|
||||
- 迭代 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,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 回执等待件——合回后等 CI 回执的后台轮询器(2026-11-02 用户裁;规则面=process-conventions §1 第 3 条「回执等待纪律」)。
|
||||
# 用法: tools/ci_watch.sh <合并提交hash> [超时秒,默认 900]
|
||||
# 调用面: 合回落地先向用户呈报本批总结,随即以 run_in_background 后台任务运行本件;命令结束时
|
||||
# harness 唤醒会话——绿→删区收口;红→呈报红闸名单走修复批;超时→呈报「回执未落」(CI 可能崩溃)。
|
||||
# 「本次回执已落」认领判据(三条全满足,防认错轮):
|
||||
# ① 回执可整读——CI 写回执非原子,半截/读空静默重试;
|
||||
# ② 回执 commit 为传入提交或其后代——另一会话合回推进尖端触发补跑覆盖时按后代认领(coalesce 语义见 ci_runner.sh);
|
||||
# ③ 回执 timestamp 晚于合并提交时刻——排除合并前盘上的旧回执(旧尖端是合并提交的祖先,只查②会误领旧回执)。
|
||||
# 退出码: 0=绿 1=红 2=超时/回执未落 3=参数错误
|
||||
set -u
|
||||
# 主工作区定位:CI 只在主工作区跑(post-merge 钩子限主区),从任务 worktree 内调用也要读主区回执——
|
||||
# 经 git 公共目录(.git)的上级定位主仓根,主区调用与 worktree 调用同一读数
|
||||
COMMON="$(git -C "$(dirname "$0")" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" \
|
||||
|| { echo "不在 git 仓库内"; exit 3; }
|
||||
REPO="$(dirname "$COMMON")"
|
||||
STATUS="$REPO/game/tests/.gates/ci-status.json"
|
||||
BASE="${1:-}"
|
||||
TIMEOUT="${2:-900}"
|
||||
INTERVAL=30
|
||||
[ -n "$BASE" ] || { echo "用法: ci_watch.sh <合并提交hash> [超时秒]"; exit 3; }
|
||||
BASE="$(git -C "$REPO" rev-parse --verify "${BASE}^{commit}")" || { echo "提交不存在: $BASE"; exit 3; }
|
||||
BASE_CT="$(git -C "$REPO" log -1 --format=%ct "$BASE")"
|
||||
_deadline=$(( $(date +%s) + TIMEOUT ))
|
||||
while :; do
|
||||
_read="$(python -I -c "
|
||||
import json, sys, datetime
|
||||
try:
|
||||
d = json.load(open(sys.argv[1], encoding='utf-8'))
|
||||
ts = int(datetime.datetime.fromisoformat(d['timestamp']).timestamp())
|
||||
print(d.get('commit', ''), d.get('verdict', ''), ts)
|
||||
except Exception:
|
||||
pass
|
||||
" "$STATUS")"
|
||||
_com=""; _verdict=""; _ts=""
|
||||
if [ -n "$_read" ]; then
|
||||
set -- $_read
|
||||
[ $# -eq 3 ] && { _com="$1"; _verdict="$2"; _ts="$3"; }
|
||||
fi
|
||||
if [ -n "$_com" ] && [ -n "$_verdict" ] && [ -n "$_ts" ] \
|
||||
&& [ "$_ts" -gt "$BASE_CT" ] \
|
||||
&& git -C "$REPO" merge-base --is-ancestor "$BASE" "$_com" 2>/dev/null; then
|
||||
python -I -c "
|
||||
import json, sys
|
||||
d = json.load(open(sys.argv[1], encoding='utf-8'))
|
||||
print('CI回执:', d.get('verdict'), '| commit', d.get('commit', '')[:8], '| 耗时', d.get('duration_s'), 's')
|
||||
if d.get('note'): print('note:', d['note'])
|
||||
for g in d.get('red_gates', []): print('红闸:', g)
|
||||
for l in d.get('key_lines', []): print('key:', l)
|
||||
" "$STATUS"
|
||||
[ "$_verdict" = "green" ] && exit 0
|
||||
exit 1
|
||||
fi
|
||||
_now=$(date +%s); _left=$((_deadline - _now))
|
||||
[ "$_left" -le 0 ] && { echo "超时(${TIMEOUT}s)回执未落——CI 可能崩溃或未触发,查 game/tests/.gates/ 的 ci.lock 与 ci.log"; exit 2; }
|
||||
sleep $(( _left < INTERVAL ? _left : INTERVAL ))
|
||||
done
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user