diff --git a/active.md b/active.md index 0af249eb..be3d88ed 100644 --- a/active.md +++ b/active.md @@ -16,8 +16,8 @@ - **工程路线图**:根目录 `roadmap.md`(里程碑时间线的权威;实时状态看下方快照节) ## 当前状态快照(收口批先更新本节,再追加状态记录;≤10 行) -- **更新于**:2026-10-17(**迭代 30 卡① 未开格类型徽章批已合并**——代码实现批走 worktree `g-plan2-iter30-glow`/分支 `iter30-glow-20261017`,五提交合并 `7ffd1dc0`:五组微光实装(shader 主路径+ui_kit 色板单点)+断言三件(冒烟 1127→1151、单测 33→34 套件、探针 case⑰)+渲染双帧取证+`reports/` 三份产出物;群伤组色值经契约 5 停机呈报用户裁「换更暗档 #A61D37」(冻结绯红 V=0.65 档、色相零移动);代码域重闸零新增红(09 由基线红转绿)。此前同日已合并迭代 29 批 `fff4f120`、闸按域分跑批 `60c25ca3`、GDD 反哺写入批 `bd6ab32e`,M4 数值批已废归 06 待裁)。**同日文档批**(`iter30-fb-20261017`):卡①代拍板七项追认通过(用户令「追认没有问题」)、反哺点七条全部回写(06/HUD框架/08/art-bible/roadmap/07 六文件+两份 reports 批注),`--docs` 域闸零新增红。 -- **最新闸态**:重闸唯一入口 `tools/run_all_gates.sh` 按改动域分跑(代码域 01 冒烟/02 单元/03-04 探针/05 渲染/09 静态扫描/10 熵增/11 对拍,文档域 06 gdd_lint/07 doc_link/08 doc_term/08b active.md 体量/10 熵增;**域划分只在该脚本定义一处,文档禁止另列闸清单**)。文档域末次回执 commit=`3f9ec7c5`,五闸全绿零存量红。代码域末次实测读数(2026-10-17 迭代 30 卡①合并态,回执 commit=`7ffd1dc0`):冒烟 1151/0、单元 34 套件(type_glow_test 51 检查)、探针十八断言、渲染 golden 棋盘×3(0.13/0.11/0.10%)与菜单(0.14%)均在容差内、static_scan 相对基线「改善」(基线红→现绿);**11 号闸 parity 是条件闸件,本批判未触面(diff 未触数值/存档/RNG/结算面)SKIP**,迭代 29 批判为触面、须 `PARITY_APPROVED=1` 才发起,MC 未跑。 +- **更新于**:2026-10-17(**在途线头收编批已合并**——`art-standee-20261006` 立绘定档批合入+`doc-adr-fixes-20261014` 判废删除,两 worktree 与分支已清(用户令):立绘机检三件工具与 2026-10-07 三处文档修正(溢出甲案×2/参考核验废路径更正)+06 新登「战斗区背景图替换」条落 master,13 件未跟踪立绘抢救至 `artwork/archive/standee-tier-20261007/`(素材不入 git);立绘批引擎实装与人闸两段仍未跑、roadmap 迭代位归属仍待裁。此前同日已合并迭代 30 卡① `7ffd1dc0`、迭代 29 批 `fff4f120`、闸按域分跑批 `60c25ca3`、GDD 反哺写入批 `bd6ab32e`,M4 数值批已废归 06 待裁)。**同日文档批**(`iter30-fb-20261017`):卡①代拍板七项追认通过(用户令「追认没有问题」)、反哺点七条全部回写(06/HUD框架/08/art-bible/roadmap/07 六文件+两份 reports 批注),`--docs` 域闸零新增红。 +- **最新闸态**:重闸唯一入口 `tools/run_all_gates.sh` 按改动域分跑(代码域 01 冒烟/02 单元/03-04 探针/05 渲染/09 静态扫描/10 熵增/11 对拍,文档域 06 gdd_lint/07 doc_link/08 doc_term/08b active.md 体量/10 熵增;**域划分只在该脚本定义一处,文档禁止另列闸清单**)。文档域末次回执 commit=分支尖(2026-10-17 线头收编批,见合并说明),零新增红。代码域末次实测读数(2026-10-17 迭代 30 卡①合并态,回执 commit=`7ffd1dc0`):冒烟 1151/0、单元 34 套件(type_glow_test 51 检查)、探针十八断言、渲染 golden 棋盘×3(0.13/0.11/0.10%)与菜单(0.14%)均在容差内、static_scan 相对基线「改善」(基线红→现绿);**11 号闸 parity 是条件闸件,本批判未触面(diff 未触数值/存档/RNG/结算面)SKIP**,迭代 29 批判为触面、须 `PARITY_APPROVED=1` 才发起,MC 未跑。 - **迭代 28 交付**:修好启动时菜单被冒险层盖住的装配顺序,层序收敛为唯一装配合同(散落的两处 `move_child` 删除);boot 链路、九阶段生命周期、启动态菜单 golden 三组验收补全;本批新立硬规则「验收函数无变异红记录不算验收成立」,三处规约落文。 - **迭代 27 交付**:三屏壳场景与局态物化、结算五段拆分、RunLedger 单类持四账、模态编排类、类型闸、死代码退役,编排器由 1725 行降到 1325 行。 - **规约体系现状**:规则文件按领域拆为 7 份、单文件上限 150 行,CLAUDE.md 只留元信息、命令、目录、规范导航与一行一条红线;动手前按规范导航表查对应细则。2026-10-17 增两条硬边界:迭代批(`/iter` 发射的代码实现)对 `design/` 只读、禁止在其中新增/修改/删除任何文件,产出物只准落 `reports/`,批末只跑代码域闸;非迭代批(工具/规则/文档改动)不占 roadmap 迭代号,按改动域跑闸。 @@ -29,10 +29,10 @@ ## 未决面(进行中/待人闸/待裁点指针;现行事实源登记处=`design/gdd/06-待办清单.md`) - 待用户裁决(06 在册):美术审计两项 P0(art-bible 死样张指涉、主角像素管线重产);去毒残留词是否清扫(「魔法道具」约 30 处、「罕见」约 18 处);喂雷流 R5 甲重启条件回落文;「通关」与「通过第 N 关」全库口径统一(约 273 处);**贫矿脉屏蔽的设计语义面反哺五处**(地形变异.md §全局作用域型/HUD 框架三层预告/教学引导手册行/难度曲线与Boss 两行/art-bible §4.6.3 括注——均描述已撤回功能,待裁是否改写,机械面已同步)。 - 待登记 06:需求卡片入库纪律一条(502 批遗留,2026-10-17 在 06 清单内查无该条,属悬空待办)。 -- 待用户追认:仅剩 req-card 技能的 WorkBuddy 与 ZCode 两份副本是否取齐一项。迭代 27 代拍板十五项、迭代 28 十三项(含四条白名单外)、迭代 29 十二项、迭代 30 卡① 七项已于 2026-10-17 用户令全部同意(iter30 追认令原文「追认没有问题」,追认结果行已加在该批追认单),四份实现报告(2026-10-17 文档批已迁位 `reports/`)的追认单节已就地标注追认结果;迭代 29 第四节「经你裁决的四项」不在追认范围(当时已问过已答)。 +- 待用户追认:仅剩 req-card 技能的 WorkBuddy 与 ZCode 两份副本是否取齐一项。迭代 27 代拍板十五项、迭代 28 十三项(含四条白名单外)、迭代 29 十二项、迭代 30 卡① 七项已于 2026-10-17 用户令全部同意(iter30 追认令原文「追认没有问题」)——iter27/28/29 三份实现报告(2026-10-17 文档批已迁位 `reports/`)的追认单节已就地标注追认结果,iter30 卡① 追认单(独立文件)文末已加追认结果行;迭代 29 第四节「经你裁决的四项」不在追认范围(当时已问过已答)。 - 待发令:**迭代 30 UI 呈现三卡批·卡②战斗一屏样张批与卡③二三关棋盘观感批**(卡①已合并 `7ffd1dc0`;②③卡片=`design/research/需求卡片-战斗一屏样张批-2026-10-16.md`/`design/research/需求卡片-二三关棋盘观感批-2026-10-16.md`,v1.2 零待裁残留,②不适用卡①的 U1 预授权并留分类结论注,③有 Q2 判据自动裁/目标带批内自定/L3 分类预授权;两批合计 ≈7.5–10.5d,逐个 `/iter` 发令);迭代 27/28 预留追加卡;黑话清扫第二波(GDD 正文流程动词族约三千处);规则文件编写 skill(按规则文件审计与目录划分批的工作流提炼,见提交 `81c0ee2b`)。 - 待批准发起:**11 号闸 parity 回放确定性对拍**(迭代 29 把它收编为重闸里的条件闸件,每次跑批只做触面判定、不自动跑;迭代 29 批已判触面并挂 NEED-APPROVAL,批准方式=带 `PARITY_APPROVED=1` 发起,MC 须用户批准才跑)。 -- 在途分支未合并 master(读现状时勿当已入库):`art-standee-20261006`(worktree `g-plan2-standee-tier`,领先 5 笔——artwork 独立仓 dry-run 报告+立绘定档)、`doc-adr-fixes-20261014`(worktree `g-plan2-doc-adr-fixes`,领先 5 笔,**方向已废**——ADR 四节重建/修订注记与用户「ADR 不需要保存」裁定相反,分支与 worktree 去留待裁)。已合并的 worktree/分支清理(2026-10-17 用户令执行):`g-plan2-iter29-testlayers`、`g-plan2-menusub-20261016` 两工作区与分支 `adr-purge-20261015`、`adr-leak-fix-20261016` 已删;`ci-removal-20261015` 已按 2026-10-16 既裁重做(只删 `.github/workflows/tests.yml`、撤 ADR 两条)合并 `db032fb5` 后工作区与分支同删;**在盘待删**:`g-plan2-carddoc-20261017`(分支 `carddoc-20261017` 已合并 `480d8118`)、`g-plan2-iter30-glow`(分支 `iter30-glow-20261017` 已合并 `7ffd1dc0`)两工作区,删除待令。 +- 在途分支未合并 master(读现状时勿当已入库):**无**。线头清理(2026-10-17 用户令):`art-standee-20261006` 已合并入 master(立绘定档批机检三件+文档修正,见状态记录 511)、`doc-adr-fixes-20261014` 判废删除(ADR 体系已废、内容 git 历史可考),两 worktree 与分支同删。已合并的 worktree/分支清理(2026-10-17 用户令执行):`g-plan2-iter29-testlayers`、`g-plan2-menusub-20261016` 两工作区与分支 `adr-purge-20261015`、`adr-leak-fix-20261016` 已删;`ci-removal-20261015` 已按 2026-10-16 既裁重做(只删 `.github/workflows/tests.yml`、撤 ADR 两条)合并 `db032fb5` 后工作区与分支同删;**在盘待删**:`g-plan2-carddoc-20261017`(分支 `carddoc-20261017` 已合并 `480d8118`)、`g-plan2-iter30-glow`(分支 `iter30-glow-20261017` 已合并 `7ffd1dc0`)、`g-plan2-iter30-fb`(分支 `iter30-fb-20261017` 已提交 `34f3cdb3`,合回 master 后删)三工作区,删除待令。 - 在册观察(06 登记,非阻塞):迭代 27 卡片义务补登两项——长函数余量 17 个中的装配/校验函数、lambda 连接防回归。 ## 状态记录(滚动窗口:仅当前迭代;退役规则见 doc-conventions §10) @@ -41,7 +41,7 @@ - **499(2026-10-15,active.md 收口机制批)**:用户裁定收口检查的触发时机不能挂在"一轮答话结束"上——旧的 Stop 钩子判的是「active.md 内容有没有变」,追加新条目就算变,结构上永远抓不到"没删旧条目";且它比对的还是已废弃路径 `production/session-state/active.md`。实测三份 harness 副本现已全部失效:Qoder 侧快照零份、ZCode 侧没接 Stop 事件、Claude 侧配置为空。改法分两层:机械层 = `tools/audit_active_state.py` 判五条膨胀签名(状态记录条目数超 30、快照行数超 10、条目编号重复、快照整行自我重复、指向 `design/qa`|`design/research` 的死指针),并入 `tools/run_all_gates.sh` 为 08b,由既有合回闸回执强制,不另立第二份闸清单;语义层 = `active-closing` 技能,规定用户问「任务是否结束」时先跑闸、按 §10 三判据逐条核对并删除、报删除清单,然后才回答。变异验证:对换代前的 34 条版本报 5 项红(连 472/477/481/494 撞号与 6 处报告死指针一并抓出),对退役后的 3 条版本报绿。钩子副本收敛为单一事实源:`.qoder/settings.json` 改指 `.zcode/hooks/worktree-guard.sh`,该脚本加 `QODER_PROJECT_DIR` 兼容并把 `.qoder/` 纳入豁免面(gitignored 配置走 worktree 带不进去),`.qoder/hooks/` 三份副本删除。脚本层守卫五例实测(stdin 直喂):主区改 tracked 文档拦、改 active.md 放行、改 .qoder 放行、主区 commit 拦、无全量回执的 merge 拦。**待核**:hooks 属各 harness 私有配置、不随仓库传播——ZCode 侧确认在跑,Qoder 侧本会话活体探针未被执行(`.claude/settings.json` 仍为空),故拦截力目前只在 ZCode 生效,成因待诊断。全量闸由用户自跑,本批按令合回 master(分支 `active-gate-20261015`,合并见提交史)。 -- **500(2026-10-15,决策文档体系废除 + architecture.md 确立架构唯一事实源,文档批)**:用户裁「ADR 不需要保存,任何决策记录在 git commit log 里记好,不需要工作日志文档存在」——七份 `adr-0001~0007` 全删(内容留 git 历史),architecture.md 删「ADR 审计」「ADR 立项清单」两节、文档状态立「事实源地位」条(唯一事实源),未决表关闭 QQ-01/02/03、立项清单在途事项转登记 QQ-11~14,补 A1 生成姿态与 autoload 四件(ConfigDB/RngService/SaveManager/MetaProgression)事实;CLAUDE.md 新增「架构事实唯一源」红线并恢复 RNG 单点红线;doc-conventions §5/§11 落架构唯一事实源条文;registry 删 18 处 adr 字段;game/ 约 60 处代码注释与 GDD/qa/research/roadmap/tests README 引用全清,豁免标记 `ADR-0007-exempt`→`rng-seed-exempt` 五处同步(main.gd/static_scan.sh/architecture.md/06/iter27);主区 4 个未跟踪今日产物(测试审计报告/两张需求卡片/自造短语清单)就地清除。走 worktree `adr-purge-20261015` 合并 4ed48b86;死链校验 1206 链接 0 死链、gdd_lint 0 不通过;按用户令未跑测试闸(本会话合回闸未触发、receipt 未生成)。代码中 `adr`/`adr5` 等为 AdventureRunner 既有局部变量名(smoke/单测 5 文件约 60 处),非文档引用、未动。**遗留呈报待用户裁**:`ci-removal-20261015` worktree 的 ADR 两条改动已被本批取代(仅 CI 清除部分对迭代 29 有效)、`doc-adr-fixes-20261014` 分支方向已废,两个 worktree/分支的去留待裁(2026-10-17 核:`ci-removal-20261015` 已合并 master、worktree 仍在盘;`doc-adr-fixes-20261014` 仍未合并,两条的现状与清理待令都记在未决面)。 +- **500(2026-10-15,决策文档体系废除 + architecture.md 确立架构唯一事实源,文档批)**:用户裁「ADR 不需要保存,任何决策记录在 git commit log 里记好,不需要工作日志文档存在」——七份 `adr-0001~0007` 全删(内容留 git 历史),architecture.md 删「ADR 审计」「ADR 立项清单」两节、文档状态立「事实源地位」条(唯一事实源),未决表关闭 QQ-01/02/03、立项清单在途事项转登记 QQ-11~14,补 A1 生成姿态与 autoload 四件(ConfigDB/RngService/SaveManager/MetaProgression)事实;CLAUDE.md 新增「架构事实唯一源」红线并恢复 RNG 单点红线;doc-conventions §5/§11 落架构唯一事实源条文;registry 删 18 处 adr 字段;game/ 约 60 处代码注释与 GDD/qa/research/roadmap/tests README 引用全清,豁免标记 `ADR-0007-exempt`→`rng-seed-exempt` 五处同步(main.gd/static_scan.sh/architecture.md/06/iter27);主区 4 个未跟踪今日产物(测试审计报告/两张需求卡片/自造短语清单)就地清除。走 worktree `adr-purge-20261015` 合并 4ed48b86;死链校验 1206 链接 0 死链、gdd_lint 0 不通过;按用户令未跑测试闸(本会话合回闸未触发、receipt 未生成)。代码中 `adr`/`adr5` 等为 AdventureRunner 既有局部变量名(smoke/单测 5 文件约 60 处),非文档引用、未动。**遗留呈报待用户裁**:`ci-removal-20261015` worktree 的 ADR 两条改动已被本批取代(仅 CI 清除部分对迭代 29 有效)、`doc-adr-fixes-20261014` 分支方向已废,两个 worktree/分支的去留待裁(2026-10-17 核:`ci-removal-20261015` 已合并 master、worktree 仍在盘;`doc-adr-fixes-20261014` 仍未合并,两条的现状与清理待令都记在未决面。同日再核:两线头已随状态记录 511 收编清偿——ci-removal 合并删净,doc-adr-fixes 判废删除)。 - **501(2026-10-15~16,简明技术中文用语规范批总收口)**:用户令对 design/gdd 全 45 文件做三层语言规范并融合词表——词面约 250 处按术语表「用语规范」A 表替换(不得→禁止/收口→完成/随批→顺带等 12 行,五子代理分四波执行+逐处语境裁决)、术语表 v1.12 新立「用语规范」节(STC 词表为权威+豁免三类+注销词义限定注)、词条五组(总线/抽屉、机能件/机制件、装板/摇号、坍塌显形、增益归一)+死链全库清零(3 处:config_fatal_probe 去向双证落 v0.4 行注、武器锻造漏改 2 处)+doc_term_audit 吸收 A 表 10 词与随批/台账补扫通道。**本批两教训**:①合回闸与「文档批零测试」铁规冲突时助手机械服从、擅跑全量测试两轮(含渲染闸),用户暴怒——正解=改闸的判断条件,现守卫已加「纯文档合入豁免」(merge diff 不触 game/ 直接放行,代码合入仍全量闸);②坑册新增:守卫按命令文本解析分支名(cd 前缀/环境变量前缀连坐误拦)、.import 边车被全局忽略致 worktree 冷检出探针 texture=null 假红。合并 af321c03(九提交,纯文档合入未跑测试,同日 4ed48b86 前例)。 - **502(2026-10-16,迭代 29 编入批——roadmap 门牌登记)**:用户令「把需求卡片写进 roadmap 的迭代 29」——此前需求卡片只存在于中间文档,roadmap 仍写「下一前沿=迭代 29+ M4 数值批」,致别的会话启动迭代 29 时跑去做 M4(该卡同日已被用户删除=死引用)。本批三改:①roadmap 迭代 29 行改写为「测试分层与门禁语义修正批」(需求卡片=`design/research/需求卡片-测试分层与门禁语义修正批-2026-10-15.md`,v1.4,T0–T8≈5.5–7d,开工门 U0=Q3–Q7 裁点+卡片 v2.0 重核+`/iter` 发令),新增「迭代 30+ 位空」行记录 M4 废除(禁止再以该名义立批/规划/引用已删卡;四项欠账归 06 在册待裁),迭代 23+ 原始泊位行加作废注仅作排期史保留;②需求卡片升 v1.5(排期行与 §5 同步行更新;登记 v2.0 重核遗留=残留 CI 措辞四处+事实基线换代〔冒烟 1086→1125/0、门禁五闸→run_all_gates.sh 编排、architecture.md 测试域节已成文、Q3 的 CI timeout 前提消失〕;订正 v1.3 版本条目尾部重复段);③本文件快照与未决面三行同步换代。文档三闸全绿(gdd_lint 0 不通过/死链 0/疑似现行 0)。提交 21c83f5。**遗留待用户裁**:06 待办清单登记一条(卡片入库纪律);`ci-removal-20261015` 分支重做(撤 ADR 两条、只保留删 .github/、rebase 现行 master)。(2026-10-17 核:该分支已按现状合并、重做令失效;「06 登记一条」至今在 06 清单内查无,已改记在未决面的待登记项。) @@ -56,4 +56,6 @@ - **510(2026-10-17,迭代 30 卡① 未开格类型徽章批总收口)**:用户「完成迭代30的开发」——三卡串行只做卡①(代码实现批,worktree `g-plan2-iter30-glow`,分支 `iter30-glow-20261017`,合回前 merge master `2aa7e8fd` 后重闸,快进合并 `7ffd1dc0`)。**实现**:Q1 丙案主路径 shader 距离场(`cell_type_glow.gdshader`+`cell_type_glow_layer.gd` 承载件,与 `_desat_mat` 共存实测未撞墙、a 案程序绘制兜底未启用);五组微光色板与九型归组单点 `ui_kit.gd`(TG_GLOW_*+`glow_group_for_cell_type`,呈现参数 α170/带宽 cell÷8);board_view 作用集=`_diggable` ∩ 身份可查 ∩ 灯光区 − r0,相位门控与接触带同源,Q4 丙案默认开。**唯一停机呈报**:群伤组初版 #FF1A31 与冻结绯红 #FF2D55 的 ΔH 5.40°(触 art-bible §4.2 既裁冻结面)→ 用户裁「换更暗档 #A61D37」(冻结值 V=0.65 档、色相零移动);五组现全数「只动明度不动色相」(ΔH ≤0.13°)。**断言三件**:冒烟八相 24 项(1127→1151,含契约 1 同板同序列 on/off 双实例 12 步 digest 对照)/单测 type_glow_test 五段 51 检查(33→34 套件)/ui_input_probe case⑰ 活板面(十八断言)。**渲染取证**:board_shot 双帧(motion_scale=0.5 停格隔离)三关 glow=2/4/4、带外零变化、生效格实绘非空;判据源探针(夹具面五组齐现)相邻组 CR 最弱对 1.232;off 帧对 2026-09-30 代 golden 三基线漂移 0.13/0.11/0.10%(≤2%,基线未重制)。**变异验证 M1–M5 五组反例实证**(归组错映/清单外静默/相位切换不重算/α=0 不绘/派生路径误触玩法 API,红输出与还原实录见实现报告 §四)。**验收**:`--code` 重闸零新增红(09 由基线红转绿判「改善」;11 号 parity 判未触面 SKIP),回执 commit=`7ffd1dc0`=分支尖。**坑档两条入注释**:探针裸视图必须接生产同源身份解析(否则作用集恒空、PASS 假绿——实跑两次 glow=0);`Callable.bind()` 动态派发对类型化参数做 Object 硬校验,回调传参改成员现值+直接方法引用。**产出物**:`reports/iter30-实现报告-2026-10-17.md`(含实机目检总清单 V10 人闸五项,登记不阻塞)/`iter30-代拍板追认单-2026-10-17.md`(§9 白名单 7 项待追认)/`iter30-反哺点-2026-10-17.md`(§5 七条待用户裁决归文档批)。**遗留**:反哺点七条待裁(06 条目状态+措辞、HUD框架 :140/:269、08 表现表、art-bible 明度档修订条目、roadmap 批内状态、Q3 两带分工+06 新待办、07 目检人闸条);实机目检人闸待用户过目(「很丑」终审位);群伤暗档 provisional 待样张批转正;阔视野带 desc「微光范围 8→12 格」与卡 §4「不扩微光」冲突属数据文本批(treasure_items.tres:47,实现报告 §六登记,不占反哺点条数);`g-plan2-iter30-glow` 工作区在盘待删(同 carddoc)。 -- **511(2026-10-17,迭代 30 卡① 文档批——代拍板七项追认+反哺点七条回写)**:用户令「追认没有问题;反哺点可以都进行回写」两项一波走完(文档批,不占迭代号,worktree `g-plan2-iter30-fb`,分支 `iter30-fb-20261017`,基于 master `0a164958`)。**追认**:卡①代拍板白名单 7 项全部同意,追认结果行加在 `reports/iter30-代拍板追认单-2026-10-17.md` 文末(用户令原文「追认没有问题」)。**反哺点七条全部回写**:①06-待办清单——条目状态待办→已完成(入库 7ffd1dc0、冒烟 1151/0、单测 34 套、探针 case⑰、07 ㉘ 人闸五项)+色相措辞修正为「样张 v2 冻结既裁值」+2026-10-05 实查读数刷新(bonus_pick_overlay.gd 已改名 curio_pick_overlay.gd:97、TG_* 五族在役 ui_kit.gd:49-53)+新增「卡顶色带与格面微光两带统一」待办条(Q3 甲案);②HUD框架——实装状态行转「已实装(迭代 30 卡① 2026-10-17 入库,合并 `7ffd1dc0`)」、验收行转正式验收(去「待实施批落地后验收」括注)、版本历史 v0.19、口径史注记后补两带分工注记;③08-Alpha版本标准——「五 · 表现」表新增「未开格类型微光(五组色相+明度档)」行(在役;明度档 provisional 值,样张批复核转正)+版本历史 v1.9+文头状态行 v1.8→v1.9;④art-bible——五组明度档 provisional 值(芥末黄 #F7DF43/黄绿 #68D042/青 #30A6A6/紫罗兰 #7B5BD6/绯红 **#A61D37**=冻结 #FF2D55 的 V=0.65 档;五组 ΔH ≤0.13° 色相零移动)+生成口径(渲染合成空间伽马阶梯、相邻组模型 CR 1.245/1.234/1.239/1.270、判据线 1.20、实测最弱对 1.232、群伤暗档停机呈报留痕)落版本历史 v3.15;§4.2/§4.6.3 权威面零改动、§2.1 未追加(Q5 甲案静态);头注「现行为」v3.11→v3.15 漏更订正;⑤roadmap——迭代进度行、近期主战场行与迭代 30 表行状态列同步卡①已入库(②③待 `/iter` 发令);⑥Q3 甲案两带分工——注记落 HUD框架 微光节、06 新待办条(见①);⑦07-体验验证清单——新增 ㉘ 节五项人闸(五组一眼可分/不遮数字〔环不外溢〕/出圈灭入圈复亮/整屏不脏/群伤暗档 #A61D37 观感〔provisional,读数归样张批〕)+版本历史 v1.5。**反哺点清单批注**:`reports/iter30-反哺点-2026-10-17.md` 加「回写结果」节逐条标已回写。**验收**:文档域闸 `tools/run_all_gates.sh --docs` 零新增红(回执 commit=分支尖,见合并说明)。**遗留**:卡②③待 `/iter` 发令(未决面在册);实机目检人闸五项(07 ㉘)待用户过目;群伤暗档 provisional 待样张批转正;`g-plan2-iter30-fb` 工作区待合回后清理。 +- **511(2026-10-17,在途线头收编批——立绘定档分支合入+两条老线头清理)**:用户令「art-standee-20261006 合入;完成之后把两个工作区删除」。①**`art-standee-20261006` 合入**(worktree `g-plan2-standee-tier`,2026-10-06/07 立绘定档与站桩重排实装批,领先 5 笔、落后曾达 478 笔;先 merge master 解一处冲突——art-bible 定档行取 master 用语规范版、排版行取分支「允许向上溢出盒顶 N px」甲案版)。落 master 内容:10-07 三处文档修正(art-bible §8.2 站桩排版③大体档溢出甲案与光柱脱钩/§8.3 参考核验废路径 `production/mockups/`→`artwork/mockups/`/battle.md 敌方站桩行同款甲案措辞——三处 master 实查均仍为旧措辞即从未落库)+06 新登「战斗区背景图替换——B 区舞台插画+临时光柱退役」条(2026-10-07 用户点名登记)+机检三件工具(`tools/standee_asset_check.py` 逐键档高升级版、`tools/palette_check.py`、`tools/path_check.py`;依赖 `STANDEE_TIER_H` master 已有)+四份过程文档归档(实装批任务书/交接说明/artwork 独立仓 dry-run 清单/立绘读数 txt)。**该批止于 B 段工具闸,C 段引擎实装、D 段人闸未跑,roadmap 迭代位归属仍待用户裁**。②**13 件未跟踪立绘先抢救再删区**:`_tmp_standee_new/`(八敌+主角三态+九人对照总图+读数,149K,素材不入 git)已拷至主区 `artwork/archive/standee-tier-20261007/`(gitignore 豁免面零状态噪音);该批 AI 生成像素立绘在 2026-10-14「精细手绘像素」方向裁定下的去留归用户后裁。③**`doc-adr-fixes-20261014` 判废删除、不合入**(worktree `g-plan2-doc-adr-fixes`):五笔提交改的 10 文件全数失效——7 份 `adr-*.md` 已被废除批删除(modify/delete 冲突面)、architecture.md v1.4 被 v1.8 超越、02 清单与 registry 实查零 ADR 字样;分支内 ADR 审查报告已被分支自身第三笔删除,git 历史 `302b5b2c` 可考(Qoder 会话「报告仍在分支」系误报,已核)。两 worktree 与分支(`-d`/`-D`)均删,merge 说明载 --docs 闸回执。 + +- **512(2026-10-17,迭代 30 卡① 文档批——代拍板七项追认+反哺点七条回写)**:用户令「追认没有问题;反哺点可以都进行回写」两项一波走完(文档批,不占迭代号,worktree `g-plan2-iter30-fb`,分支 `iter30-fb-20261017`,基于 master `0a164958`、合回前 merge master 解 active.md 一处冲突——本批状态记录原编 511 与 master 线头收编批 511 撞号,按 iter29 先例改 512)。**追认**:卡①代拍板白名单 7 项全部同意,追认结果行加在 `reports/iter30-代拍板追认单-2026-10-17.md` 文末(用户令原文「追认没有问题」)。**反哺点七条全部回写**:①06-待办清单——条目状态待办→已完成(入库 7ffd1dc0、冒烟 1151/0、单测 34 套、探针 case⑰、07 ㉘ 人闸五项)+色相措辞修正为「样张 v2 冻结既裁值」+2026-10-05 实查读数刷新(bonus_pick_overlay.gd 已改名 curio_pick_overlay.gd:97、TG_* 五族在役 ui_kit.gd:49-53)+新增「卡顶色带与格面微光两带统一」待办条(Q3 甲案);②HUD框架——实装状态行转「已实装(迭代 30 卡① 2026-10-17 入库,合并 `7ffd1dc0`)」、验收行转正式验收(去「待实施批落地后验收」括注)、版本历史 v0.19、口径史注记后补两带分工注记;③08-Alpha版本标准——「五 · 表现」表新增「未开格类型微光(五组色相+明度档)」行(在役;明度档 provisional 值,样张批复核转正)+版本历史 v1.9+文头状态行 v1.8→v1.9;④art-bible——五组明度档 provisional 值(芥末黄 #F7DF43/黄绿 #68D042/青 #30A6A6/紫罗兰 #7B5BD6/绯红 **#A61D37**=冻结 #FF2D55 的 V=0.65 档;五组 ΔH ≤0.13° 色相零移动)+生成口径(渲染合成空间伽马阶梯、相邻组模型 CR 1.245/1.234/1.239/1.270、判据线 1.20、实测最弱对 1.232、群伤暗档停机呈报留痕)落版本历史 v3.15;§4.2/§4.6.3 权威面零改动、§2.1 未追加(Q5 甲案静态);头注「现行为」v3.11→v3.15 漏更订正;⑤roadmap——迭代进度行、近期主战场行与迭代 30 表行状态列同步卡①已入库(②③待 `/iter` 发令);⑥Q3 甲案两带分工——注记落 HUD框架 微光节、06 新待办条(见①);⑦07-体验验证清单——新增 ㉘ 节五项人闸(五组一眼可分/不遮数字〔环不外溢〕/出圈灭入圈复亮/整屏不脏/群伤暗档 #A61D37 观感〔provisional,读数归样张批〕)+版本历史 v1.5。**反哺点清单批注**:`reports/iter30-反哺点-2026-10-17.md` 加「回写结果」节逐条标已回写。**验收**:文档域闸 `tools/run_all_gates.sh --docs` 零新增红(回执 commit=`34f3cdb3`)。**遗留**:卡②③待 `/iter` 发令(未决面在册);实机目检人闸五项(07 ㉘)待用户过目;群伤暗档 provisional 待样张批转正;`g-plan2-iter30-fb` 工作区待合回后清理。 diff --git a/design/art/art-bible.md b/design/art/art-bible.md index 995bf029..956cc435 100644 --- a/design/art/art-bible.md +++ b/design/art/art-bible.md @@ -493,7 +493,7 @@ - **主角战斗状态图(含 B 区站桩侧向)= 48×72**(同上暂定;v1.9 待决注记「主角立绘档空缺」由此完成); - **棋盘小人 = 32×32 二头身派生版(暂定,允许更小)**——独立资产,自人设定稿压缩派生(人物设计档 §5.4),不吞数字位、占位 ≤ 0.75× 格边长;36px 板 × 32px 源的占比摩擦(v1.9 待决注记①)沿「独立小源档」路线随格边长终值在样张批定终值; - **站桩立绘源档 = 八敌逐键定档(2026-10-06 定档批,用户确认定档总图;原三档 48/64/96 候选与 v3.0 档位上调方向候选一并完成)**:主角 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。定档依据 = 生物体型三约束(用户拍板:钩爪恐魔>灰矮人、灰矮人<熊地精、食人魔>熊地精——体量=物种体型,非强度线性映射,人物设计档 §5.2 顺带修订);可读性实测门槛 = 源高 ≥32(24 糊/32 可读/48 清楚/64 细节完整),全阶梯 36–94 达标;派生管线 = pixel-1.png 紧裁 → 等比缩至档高 → dom_block_resize 块内众数降采样(tools/pixelize_candidates.py `STANDEE_TIER_H`,`SIZES` 降格为管线上游中间分辨率表);存在感问题(v3.0 病根 6)经方案二整屏对比(B 区 270/立绘显示上限 188)解决。 - - **站桩立绘排版 = 方案二(2026-10-06 用户拍板)**:① 区高 B 区 270(≈1/3)/C 区 530(≈2/3),**扫雷盘 438×438 原尺寸不缩、格子不变**(区高事实源 battle.md 区表;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:drider/duergar_king)头部探出盒顶压进光柱区,血条仍落底;意图徽标悬浮头顶上方(探顶档压胸);④ 原「血条悬浮头顶+立绘盒内居中」排式退役——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞(2026-10-06 用户点名否决)。 + - **站桩立绘排版 = 方案二(2026-10-06 用户拍板)**:① 区高 B 区 270(≈1/3)/C 区 530(≈2/3),**扫雷盘 438×438 原尺寸不缩、格子不变**(区高事实源 battle.md 区表;压缩由 C 区面板空档吸收);② 站桩内部重排——立绘双脚落石台上缘(弃盒内居中)、**血条+状态/名字行落底排**(立绘→血条→状态 顺序,全场血条对齐一线;血条嵌石板位、台缘线留作地面线、落影保留接地感),状态空时该行退化为名字行;③ 大体档(显示 >150px 级:drider/duergar_king)**允许向上溢出盒顶 N px**(2026-10-07 用户裁甲案;N 由实装批 C0 按实测尺寸算定,**与背景/光柱无关**——现行溢出区恰为临时光柱氛围层〔§6.1 方向稿四柱,x 基准 110/900/1055/1210@1280,`battle_stage_view.gd:55-63`〕,正式舞台背景插画 `stage/stage_bg.png` 到位后该层退役/吸收进背景绘制,**溢出判定不随之变更**;横向不得压到顶部中央轮次·阶段指示,溢出上限由 C0 用真图复算 x 区间定),血条仍落底(落底排的第二个收益=头部探顶不带出血条);意图徽标悬浮头顶上方(探顶档压胸);④ 原「血条悬浮头顶+立绘盒内居中」排式退役——盒顶固定 y 与盒内居中在高盒下拉开血条-立绘空洞(2026-10-06 用户点名否决)。 ### 8.3 导出格式与命名规范 @@ -512,7 +512,7 @@ > **白话**:格子读感里的立体结构——顶面、缘件、角件、周界轮廓——必须是素材里**画出来的结构件**;素材缺件就停下报缺,拿调色/渐变/滤镜顶替不是变通,是违规。 - **结构件完整性**:格面立体感(高墙顶面/坡缘/角件/周界轮廓)只能来自**素材本体绘制的结构件拼装**(样例=Necesse 式缘件:专属底色+手绘细节+暗轮廓线的独立贴图);禁止以调色、渐变、明暗滤镜、跨格投影等生成手段伪造素材中不存在的结构。样稿暴露缺件时,正确动作=停下呈报缺口与「换素材/自产缺件」选项,由用户裁决后执行。 -- **参考核验**:宣称「参考 X 游戏」前,必须取得并实际查看 X 的截图证据(像素级放大核验其实现方式),证据存 `production/mockups/` 或 `design/art/references/`;仅凭文字资料、记忆或宏观印象作出「X 是这样做」的断言无效。 +- **参考核验**:宣称「参考 X 游戏」前,必须取得并实际查看 X 的截图证据(像素级放大核验其实现方式),证据存 `artwork/mockups/` 或 `design/art/references/`;仅凭文字资料、记忆或宏观印象作出「X 是这样做」的断言无效。(2026-10-07 路径核对器 B3 首跑发现更正——原 `production/mockups/` 系废路径,v3.9 批已更正asset-path-map 等四处,本条漏网) - **图底分离**:已开地面的明度须低于未开格墙体,由素材选型承载(选更暗的地板砖),不靠渲染压暗实现。 - **生产分工(2026-09-26 用户拍板)**:格面地形类素材=**材质归模型、结构归程序**——AI 生图模型只出无缝填充纹理(无边框/无亮边/均匀光照/方形);一切结构件(缘件/轮廓/立面/角件/收头)一律程序化绘制或手修,因为 autotile 任意拼接的边界像素约束超出 2D 生图模型能力(实证:2026-09-25 生图 5×5 瓦片表每枚砖自带啃咬边框与宽亮带,提示词无法根除)。**镜像规则**:东西对称结构件只许水平翻转(flip_h)复用、**禁旋转**——北受光下东西侧缘光照对称,镜像无损真;旋转会把北受光转到东边。 diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index a4ca7e1f..0c8cc246 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -542,6 +542,14 @@ **状态**:挂账(随整屏样张闸前整备批启动;分章推进,每章一个章节,格式已定) +#### 战斗区背景图替换——B 区舞台插画 + 临时光柱退役(2026-10-07 用户点名登记) + +**现状**:B 区舞台现行 = 程序化平色底(`UiKit.STAGE_BG`,`battle_stage_view.gd:47`)+ **顶光柱氛围层**(`:55-63` 四柱,x 基准 110/900/1055/1210@1280,60 段叠层矩形自顶向下渐宽窄渐隐〔顶 α26→0〕,层序 = bg < 光柱 < 世界)——二者均系 §6.1 方向稿的**临时占位**,用户口径「光柱只是临时方案,换正式战斗背景后效果更好」。正式件已立资产契约:`stage/stage_bg.png`(源 1280×270,B 区全高含明细带底;三关换挡走 §4.3 `wall_ambient` tint 槽,不按板出三张),属审计 P1-2 查得的「B 区舞台背景插画 0 张」缺口。**光柱在背景到位后顺带退役或吸收进背景绘制**(不再作为独立程序层)。**与立绘探顶解耦**:大体档「允许向上溢出盒顶 N px」的判定(2026-10-07 用户裁甲案)**不依赖光柱是否存在**,换背景零返工。 + +**归属**:[art-bible](../art/art-bible.md) §6.1(资产契约)+ §8.3 归档纪律 · 资产管线批;出图工单归 [art-assets-checklist](../art/art-assets-checklist.md) / 实装批任务书 + +**状态**:待办(**开工 = 用户点名**;**前置建议** = 立绘定档与站桩重排实装批的 **C0 盒高账定案后再出图**,避免 B 区高度/构图二次返工;交付后 art-status-checklist 顺带更新 ✅) + ### 教学引导 > **节头注记(2026-10-04 用户拍板)**:教学引导定稿批整体改排 **Beta 阶段**——当前 Alpha 版不做教学相关工作,本节五条全部随之改排(2026-09-19 的 Alpha 排期拍板被本拍板取代)。同日五问讨论已形成初裁与设计提案,提案已归档 `archive/` 目录(磁盘留存、不入 git,Beta 批立项底稿,引用以其「归档状态与初裁记录」节为准)。初裁摘要:①教学形态=丙1 混合(首次冒险编排式首遇提示〔挂 first_script 锚、不锁操作〕+其余全程情景触发);②「因果教学顺序」问题**注销**——只教扫雷操作,结算读法不设教学(用户裁:结算只是表达、展现、演出,无策略内容;展示层每行带来源标注已自带归因;教学引导.md 旧前提〔设计目的句/P0 候选归因提示/验收第 2 条〕随 Beta 批文档定稿清除);③图例面板按循环段落分组、无结算段;④不做「再教一次」——改走独立「帮助」手册面(全部内容放一个文档);⑤教学文案内容权归设置与本地化(两文档负责表对齐随 Beta 批)。**未裁三疑点**(Beta 立项时先裁):增益叠层节奏条目去留/帮助入口位置(设置面为推荐)/图例面板与帮助手册的内容分工。 diff --git a/design/gdd/ux/battle.md b/design/gdd/ux/battle.md index 1aa0d14f..81d9cd84 100644 --- a/design/gdd/ux/battle.md +++ b/design/gdd/ux/battle.md @@ -141,7 +141,7 @@ | 区 | 组件 | 类型 | 内容 | 交互 | 现有 demo 组件 | |---|---|---|---|---|---| | B | 主角站桩立绘 | 站桩(无框立绘+落影) | HP 条落底盘(2026-10-06 方案二拍板:站桩排式 = **立绘→血条→状态**,全场血条对齐一线;血条嵌石板位、台缘线留作地面线;原「悬浮头顶」退役——高盒下血条-立绘空洞,用户点名否决)、状态效果小图标一排挂血条正下方(形状+色相双通道、层数角标;空态不占位退化为空行)——**HP 与状态效果的唯一展示面(2026-09-26 信息归属拍板,原 C 区状态槽职责〔悬停 tooltip 来源+语义+剩余结算次数〕并入本组件)**;攻击值等规划数据仍归 C 区数据框(2026-09-20 全站桩拍板,英雄卡退役) | 悬停状态小图标看 tooltip | HUDView 拆分改造 | -| B | 敌人站桩立绘 ×1–3 | 站桩(无框立绘+落影) | 血条+顺位徽标落底盘(同主角排式,2026-10-06 方案二;**大体档〔显示 >150px 级:drider/duergar_king〕头部探出盒顶压进光柱区,血条仍落底**)、状态小图标挂血条正下方(敌方 side-wide 增益各立绘同值呈现——蓄力/硬化/削弱/暴走/警觉;伏击层数通道随 2026-09-20 八型批退役——数据源 = 战斗结算流水经增益状态投影,敌人系统×4④;2026-09-16 敌人技能池批改型:行为环点拆除〔proposal §2.4〕,意图占位 = 本拍摇定结果的动画占位——普通攻击零表示 / 特殊技类型级提示,**意图徽标悬浮头顶上方,探顶档压胸**,敌方增益〔蓄力/硬化〕入状态图标行);死亡灰化加碎裂退场直接演在立绘上(2026-09-20 全站桩拍板,敌人卡退役);血条下名字行 = 纯名字 + 护甲类型图标(形状+色相双通道,与状态图标同行共存,状态空时为纯名字;原「底座下」随石板退位改落底排),AC 数值不展示——2026-09-26 终裁(用户口径:HP 信息已足够,攻防数值不直接写上;护甲类型图标保留克制对策可读性——类型可读、数值隐藏) | 只读 | 新建 | +| B | 敌人站桩立绘 ×1–3 | 站桩(无框立绘+落影) | 血条+顺位徽标落底盘(同主角排式,2026-10-06 方案二;**大体档〔显示 >150px 级:drider/duergar_king〕允许向上溢出盒顶 N px(N 由实装批 C0 实算,与背景/光柱无关——现行溢出区为临时光柱氛围层,正式舞台背景插画到位后该层退役,判定不变),血条仍落底**)、状态小图标挂血条正下方(敌方 side-wide 增益各立绘同值呈现——蓄力/硬化/削弱/暴走/警觉;伏击层数通道随 2026-09-20 八型批退役——数据源 = 战斗结算流水经增益状态投影,敌人系统×4④;2026-09-16 敌人技能池批改型:行为环点拆除〔proposal §2.4〕,意图占位 = 本拍摇定结果的动画占位——普通攻击零表示 / 特殊技类型级提示,**意图徽标悬浮头顶上方,探顶档压胸**,敌方增益〔蓄力/硬化〕入状态图标行);死亡灰化加碎裂退场直接演在立绘上(2026-09-20 全站桩拍板,敌人卡退役);血条下名字行 = 纯名字 + 护甲类型图标(形状+色相双通道,与状态图标同行共存,状态空时为纯名字;原「底座下」随石板退位改落底排),AC 数值不展示——2026-09-26 终裁(用户口径:HP 信息已足够,攻防数值不直接写上;护甲类型图标保留克制对策可读性——类型可读、数值隐藏) | 只读 | 新建 | | B | 轮次·阶段指示 | 标签 | "第 N 轮 · 挖掘中/结算中"(终局定格「战斗结束」;Boss 变体附 Boss 名) | 只读 | 新建 | | B | 结算明细带 | 滚动列表 | 结算期逐行流水([战斗结算](../systems/战斗循环/战斗结算.md#明细流水每一行都能指回你刚才的哪一步棋) 9 类行协议);挖掘期收起为上轮摘要 1 行 | 点击展开明细回看 overlay(2026-10-04 P-C7 退役——带保留单行摘要,回看归「生涯」) | battle_log_view.gd 改造 | | B | ~~结算明细回看~~(已退役) | —— | 2026-10-04 P-C7 裁:冒险中回看退役归「生涯」——settlement_review_overlay.gd 为未接线遗留件(06 登记) | —— | —— | diff --git a/design/research/_A0-A1_立绘读数-20261007.txt b/design/research/_A0-A1_立绘读数-20261007.txt new file mode 100644 index 00000000..3e62b804 --- /dev/null +++ b/design/research/_A0-A1_立绘读数-20261007.txt @@ -0,0 +1,20 @@ +=== ./_tmp_standee_new === + character_enemy_bugbear.png 51x62 colors=32 opaque=2145 + character_enemy_drider.png 67x90 colors=33 opaque=3513 + character_enemy_duergar.png 34x46 colors=33 opaque=1014 + character_enemy_duergar_king.png 66x94 colors=33 opaque=4385 + character_enemy_giant_spider.png 48x38 colors=33 opaque=1389 + character_enemy_goblin.png 36x36 colors=31 opaque=828 + character_enemy_hook_horror.png 46x58 colors=33 opaque=1538 + character_enemy_ogre_chief.png 62x74 colors=33 opaque=2886 + character_hero_portrait.png 46x48 colors=32 opaque=1228 + + +[口径澄清 · 2026-10-07] +上表 colors 列由 _tmp_baseline.py 统计,含全透明色 (0,0,0,0),故显示 33。 +剔除透明色后的真实唯一色数(_tmp_palette.py 实测,uniq_rgb): + bugbear 31 / drider 32 / duergar 32 / duergar_king 32 / giant_spider 32 + goblin 30 / hook_horror 32 / ogre_chief 32 / hero 31 +=> C2「色数 ≤32」全档通过,无新增色值。 +opaque 面积与 pixelize_candidates.py 注释里的实测校准值逐键吻合 +(828/1014/1389/1538/2145/2886/3513/4385),可证档位口径与讨论批定案一致。 diff --git a/design/research/dry-run清单-artwork独立git仓-20261007.md b/design/research/dry-run清单-artwork独立git仓-20261007.md new file mode 100644 index 00000000..bac562e1 --- /dev/null +++ b/design/research/dry-run清单-artwork独立git仓-20261007.md @@ -0,0 +1,88 @@ +# dry-run 清单:artwork/ 独立 git 仓库 · 入库/排除建议 + +> **性质**:**dry-run 只读报告**——本轮**未建仓、未 `git init`、未动任何文件**、 +> 未加任何 .gitignore。产物 = 下面这张待你逐项裁定的清单。 +> **待决口径**:artwork/ 是否独立成仓(**不用 submodule**,会污染主仓历史)。 +> 现状:`.gitignore:50` 忽略整个 `artwork/`,素材零版本历史。 + +--- + +## 0. 体量实测(2026-10-07) + +全盘`artwork/` ≈ **131 MB / 3402 文件**(2673 png · 703 svg · 25 ttf · 17 txt · 12 html · 9 jpg · 8 url · 6 ogg · 4 xml · 3 py · 3 md · 2 pxo)。 + +| 子目录 | 体积 | 性质初判 | +|---|---:|---| +| `ui-reuse-2026-09-23/` | 49 MB | **草稿**(最大,且已被P2-1 登记待整理) | +| `ui-redesign-2026-10-04/` | 22 MB | 设计迭代稿 | +| `ui-redesign-2026-09-29/` | 18 MB | 设计迭代稿 | +| `mockups/` | 13 MB | **样张/评审产物**(asset-path-map 登记在册) | +| `ui-redesign-2026-10-04-optA/` | 7 MB | 设计迭代稿 | +| `_audit-2026-10-06/` | 4 MB | 审计留底 | +| `remote-gen-2026-09-26/` | 3 MB | **八敌生图源 + 立绘源**(本批命脉) | +| `qa-t17fix-baseline/` | 3 MB | QA 基线截图 | +| `qa-screens-1920-20260930/` | 3 MB | QA 截图 | +| `qa-reduced-test/` | 3 MB | QA 测试 | +| `qa-screens-20260930/` | 2 MB | QA 截图 | +| `qa-golden-20260930/` | 2 MB | **golden基准**(有回归价值) | +| `qa-t17fix-after/` | 1 MB | QA 截图 | +| `legacy/` | 1 MB | **本批新增**:旧 48 档立绘归档(11 件) | + +--- + +## 1. 建议**入库**(红绿线:丢了就再也回不来) + +| 目录 | 理由 | 体积 | +|---|---|---:| +| `remote-gen-2026-09-26/` | **最高优先**。八敌 `pixel-1.png` = 立绘派生的**唯一管线源**;丢了 A 段产图能力彻底归零(AI 原图 `v1.jpg/png` 也只剩这里)。交接文档与项目记忆都点名它是「命脉」 | 3 MB | +| `legacy/standee-48-legacy-20261007/` | **本批新增**。旧 48 档立绘,素材无 git 历史 ⇒ 这份拷贝是**唯一回滚路径**。不入仓 = 违反本批自己的归档纪律 | <1 MB | +| `mockups/` | 已定稿样张(level-map 等),asset-path-map §1 在册引用 | 13 MB | +| `qa-golden-20260930/` | golden 回归基准,对齐后有比对价值 | 2 MB | + +> 小计≈ **19 MB**。**这是「不可再生资产」的集合,也是我建议入仓的核心。** + +## 2. 建议**排除**(可再生/ 已过气/ 有更好的载体) + +| 目录 | 排除理由 | +|---|---| +| `ui-reuse-2026-09-23/` | 49 MB 草稿。已被 P2-1 批登记「3349 草稿 dry-run 整理」——**先整理再定**,别把 49 MB 草稿直接塞进新仓(交接 §8③ 原话) | +| `ui-redesign-2026-09-29/` | 已被 10-04 两轮迭代取代 | +| `ui-redesign-2026-10-04/` + `-optA/` | 同上,optA 是被opt 掉的分支 | +| `qa-screens-*/` `qa-t17fix-*/` `qa-reduced-test/` | 一次性 QA 截图,共约 9 MB,可再生 | +| `_audit-2026-10-06/` | 一次性审计留底 | + +## 3. 建议**先裁定再定**(需你逐项拍板) + +| 项 | 问题 | +|---|---| +| `*.url`(8 个) | 快捷方式/书签类文件?入仓无意义,建议排除 | +| `*.ttf`(25 个) | 字体是第三方资产,**版权与体积双风险**,且多半能从系统/引擎取到。倾向排除 | +| `*.py`(3 个) | 生成脚本属**代码**,应入主仓(受版本管理),不该留在 artwork 里当过程稿。建议迁出 | +| `*.svg`(703 个) | 大头在 ui-reuse 草稿里;若按第2 条排除该目录,svg 自然一并排除 | + +--- + +## 4. 若你拍板「建仓」,建议的落地形态(**本轮未执行**) + +```bash +# 就地独立仓,不用 submodule(理由见交接 §8③) +cd D:/code/g-plan2/artwork +git init +# .gitignore 只排第2 节(草稿/QA),不排源与定稿 +``` + +要点: +- **只入第1 节红绿线资产**(≈19 MB),第2 节列.gitignore; +- `git add` 前**先出 dry-run 清单经你确认**(你一贯纪律:批量文件操作先dry-run); +- 独立仓的 `.gitignore` 需**与主仓 `.gitignore:50` 解耦**(主仓那条继续有效,两仓互不干扰); +- 立绘源/pixel-1 只增不改⇒ 可先只做一次 initial commit 拿住命脉资产,草稿等 P2-1 整理完再补第二commit。 + +--- + +## 5. 待你拍板的三件事 + +1. **入仓范围**:只入第 1 节(≈19 MB,命脉资产)?还是连设计稿一起? +2. **`ui-reuse-2026-09-23/` 49 MB**:等 P2-1 整理完再入,还是本轮就排除? +3. **ttf / py / url**(第 3 节):是否按建议排除/py 迁出主仓? + +> 拍板后我再动手建仓——**绝不自行`git init`**。 \ No newline at end of file diff --git a/design/research/handoff-立绘定档与站桩重排批-2026-10-07.md b/design/research/handoff-立绘定档与站桩重排批-2026-10-07.md new file mode 100644 index 00000000..43231283 --- /dev/null +++ b/design/research/handoff-立绘定档与站桩重排批-2026-10-07.md @@ -0,0 +1,105 @@ +# 交接说明:立绘定档与站桩重排实装批(美术线自立批) + +> **用途**:本批由 2026-10-07 会话推进到「立项完成 + P1–P3 全裁 + U0 可发令」状态后交接。接手 agent 请先读本文件,再读 `任务书-立绘定档与站桩重排实装批-2026-10-07.md`(本目录)。 +> **交接时点**:2026-10-07 17:35 · 主仓 `master` 已推进到 `3e9360d`(台账到 395)。 + +--- + +## 1. 这批在做什么(一句话) + +把八敌站桩立绘从「全敌共用 48 档」换成**按生物体型逐键定档(源高 36–94、显示 ×2 整数 = 72–188)**,并把 B 区站桩排版换成**方案二**(演出区占 1/3 屏、立绘落台、血条+状态/名字行落底排全场一线)。它修的是 playnote 里的两条实机批评:**像素粒度过低看不清 / 怪物大小比例不合理**。 + +## 2. 批次归属口径(用户 2026-10-07 明确,务必遵守) + +- **迭代(roadmap 迭代位)只装「代码实现」类批次。** +- **美术素材生成 + 文档修改不进迭代**,走自立批 / 自由批次(不排队、不占 roadmap 迭代序列)。 +- **推进顺序 = 先产图、后改代码**(素材尺寸是实测值,容器依实测定)。 +- 实现类改动一律走 worktree;主工作区只读(豁免仅 `active.md` 与 `.zcode/`)。 + +## 3. 已完成(全部在分支 `art-standee-20261006`) + +| 提交 | 内容 | +|---|---| +| `0aa85dd` | 讨论批 9 文件主提交(art-bible v3.7/v3.11、character-design v1.8/v1.9、battle.md 区表与组件、`pixelize_candidates.py` 新增 `STANDEE_TIER_H`、06 四条、asset-path-map、art-status-checklist、两份生图单) | +| `5953af3` | 分支内并入 master 80 提交,解 `06-待办清单` 冲突一处 | +| `c688670` | active.md 快照 + 台账(后并入 master 时改登 **386**,因 385 被并行会话占用) | +| → `4359445` | **主区 `merge --no-ff`**(以上三件已进 master;台账 386 在案) | +| `1e42e6e` | **立项任务书 v0.1**(四段 A/B/C/D + T0 十项考证 + 三笔待算账 + P1–P3 全列备选案) | +| `9556539` | **P1–P3 全裁归档** + 光柱区表述解耦 + 06 新登「战斗区背景图替换」 | + +## 4. 当前磁盘状态(重要) + +- **worktree**:`D:/code/g-plan2-standee-tier`,分支 `art-standee-20261006`,**工作区干净**(无未提交改动)。 +- **未并入 master**:`1e42e6e` 与 `9556539` 两个提交**尚未合并**。master 此时已推进 53 个提交到 `3e9360d`。 +- **合并姿势(接手者做,别直动主区)**: + 1. 在 worktree 内 `git merge master`(**先**在分支里解冲突); + 2. 预期冲突点 = `active.md`(并行会话台账已到 395,**本批的 386 会撞号 → 按先例改登下一个空号**,先例见台账 374/383)、可能 `06-待办清单.md`; + 3. 解完再在主区 `git merge --no-ff art-standee-20261006`; + 4. **不要**动主区在飞的脏件(`.gitignore` / `.zcode/config.json` / `playnote.md` / `project.godot`)。 + +## 5. 待办(按优先级) + +### P0 · A 段产图(U0 已具备,可直接开工) + +| 卡 | 动作 | +|---|---| +| A0 | 记录八敌现役 48 档尺寸/色数基线 | +| A1 | 按 `STANDEE_TIER_H` 重产八敌(**命令见 §7**) | +| A2 | 出「九人对照总图」(八敌 + 主角,真实相对体量) | +| A3 | 拷回主区 `game/assets/art/character/` + 同步 `.import` + **旧 48 档归档** | + +### P1 · B 段工具闸(建议提到 A3 之前跑通,理由见 §6) +B1 `standee_asset_check` 硬闸升级(逐键档高 / 色数 ≤32 / 非整数倍痕迹 / 描边亮度)· B2 色板检查器 · B3 路径核对器。 + +### P2 · C 段引擎实装 +C0 **盒高账**(见下) → C1 区高 252→270 / 548→530 → C2 底锚落台 → C3 血条落底 → C4 探顶溢出 + 意图徽标 → C5 探针断言族。 + +### P3 · D 段样张人闸 +整屏样张 + 目检清单 + 追认单 + 出口报告(人闸归用户)。 + +## 6. 关键事实速查(别再重查) + +- `ui_assembler.gd:22` B 区 **252**;`:26` C/D **548**(文档已改 270/530,**代码未动**)。 +- `standee_body.gd:29` `box = Vector2(150,176)`;`:231-234` 立绘盒净高 = 176−44−36 = **96px**;`:237` scale 2.0 NEAREST;`:238` **盒内居中**(方案二要改底锚);`:27` `STANDEE_FOOT_MARGIN=12`;`:475` 意图徽标 y=86;`:194-197` `_intent_scale` 峰值 **1.6**。 +- `battle_stage_view.gd:44` 舞台 `custom_minimum_size=(0,252)`;`:55-63` 顶光柱氛围层(四柱 x 110/900/1055/1210@1280,层序 bg < 光柱 < 世界);`:66-72` `_svp_container` FULL_RECT + `stretch=true` + **无 clip** ⇒ 立绘画到盒顶之上照样渲染。 +- `ui_struct_probe.gd:235-256` ⑥standee 断言族:内容矩形 ⊆ **252px** 舞台(内缩 8px)+ 底缘 ≥8px(改 270 须随改)。 +- **C0 三笔待算账**:① 188px 立绘 vs 96px 盒净高 vs 270 区高 vs 24/84 明细带;② `BattleLogView` 24/84 与 176 盒如何共存(252−176=76 装不下 84);③ 探顶溢出上限 **N**(受顶部中央轮次指示横向约束,暂按 ≤32px 量级,需真图复算)。 + +### 已裁决(P1–P3 全裁甲案,2026-10-07) +- **P1 探顶 = 甲**:盒按最高档抬,**允许向上溢出盒顶 N px,不裁切**。判定**与光柱/背景解耦**(光柱是临时氛围层,正式 `stage/stage_bg.png` 到位后退役,判定不变)。乙(真 clip)淘汰:会切掉石君王王冠;丙(降倍率 ×1.5)淘汰:非整数倍。 +- **P2 结算期 84px 明细带 = 甲**:改**覆盖层**,不挤压站桩盒。乙(压缩盒)淘汰:站桩跳动违反减动效。 +- **P3 棋盘小人 = 甲**:不随本批,保持 32×32。 + +### ⚠ 两个必知的坑 +1. **`.gitignore:50` 忽略整个 `artwork/`** ⇒ **worktree 里没有 `artwork/`**。产图脚本在 worktree 直接跑会**静默产出 0 张**。必须传主区绝对路径。 +2. **素材不入 git** ⇒ 无版本历史、不能 git 回滚 ⇒ **替换前必须归档旧档**,且 **B 段机检是唯一防线**(不是可选项)。 + +## 7. A 段执行命令(已验证可直接跑) + +```bash +# 解释器(Pillow 12.3.0 已装) +PY=D:/dev/python/envs/gamedev/Scripts/python.exe + +# A1 重产八敌 + 主角(源用主区绝对路径,输出先落 worktree 临时目录) +cd D:/code/g-plan2-standee-tier +$PY tools/pixelize_candidates.py \ + D:/code/g-plan2/artwork/remote-gen-2026-09-26 \ + --standee48 --standee-out ./_tmp_standee_new +``` + +源在盘状态(已核实,2026-10-07):八敌 `pixel-1.png` 齐全;`hero-ref/edit/{normal,attack,hit}_1x.png` 齐全。 +输出名 = `character_enemy_<键>.png` / `character_hero_portrait.png` / `character_hero_{attack,hit}.png`。 +档高表 `STANDEE_TIER_H`:goblin 36 / giant_spider 38 / duergar 46 / hook_horror 58 / bugbear 62 / ogre_chief 74 / drider 90 / **duergar_king 94**;主角 48。 + +## 8. 三项未决(等用户拍板,接手者先别自行决定) + +1. **旧 48 档归档位置**:建议 `archive/art/standee-48-legacy-20261007/`(用户若另有素材管理体系则按其路径)。 +2. **B 段是否提前到 A3 之前**(我的建议是提前:素材无 git 保护,机检应在入包前跑通)。 +3. **`artwork/` 独立 git 子仓库**:用户提议(不想素材影响文档/程序实现)。已给方案 = 就地 `git init` 成独立仓库(**不用 submodule**,会污染主仓历史),只入定稿/源(124M 里立绘源仅 2.7M;`ui-reuse-2026-09-23/` 49M 草稿先经 P2-1 dry-run 整理再定)。**待办第一步 = 出 dry-run 入库/排除清单,不动任何文件、不建仓库。** + +## 9. 关联登记 + +- 任务书:`design/research/任务书-立绘定档与站桩重排实装批-2026-10-07.md` +- 06 新登五条:站桩帧动画(Alpha 定版闸前)/ 主角描边素材侧复核 / 3349 草稿 dry-run 整理 / 口号型条款机检化 / **战斗区背景图替换(B 区舞台插画 + 临时光柱退役)** +- art-status-checklist 已对账至 ✅4 / ◐8 / ⬜3(共 15 条) +- 台账:active.md **386**(合并时若撞号按先例改登) diff --git a/design/research/任务书-立绘定档与站桩重排实装批-2026-10-07.md b/design/research/任务书-立绘定档与站桩重排实装批-2026-10-07.md new file mode 100644 index 00000000..fdf33b5d --- /dev/null +++ b/design/research/任务书-立绘定档与站桩重排实装批-2026-10-07.md @@ -0,0 +1,185 @@ +# 任务书:立绘定档与站桩重排实装批(美术线自立批)· v0.1 草案 + +> **批次归属口径(2026-10-07 用户明确)**:**迭代只装「代码实现」类批次**;**美术素材生成 + 文档修改不进迭代**,走自立批 / 自由批次(更灵活、不排队、不占 roadmap 迭代序列)。 +> 故本任务书**不登记进 roadmap 迭代序列表**,只落 `design/research/` + `art-status-checklist` + `06-待办清单` + `active.md`。 +> **推进顺序(用户裁定)= 先产图、后改代码**:素材尺寸是实测值,容器(立绘盒 / 区高 / 血条位)依实测定,避免二次返工。 +> **上游**:讨论批已落籍 master `4359445`(art-bible v3.7/v3.11、character-design v1.8/v1.9、battle.md 区表、`tools/pixelize_candidates.py` 的 `STANDEE_TIER_H`)。本文只负责「把文档承诺变成盘上事实」。 +> **起草**:2026-10-07;T0 时点 master = `4359445`(已并入并行会话推进)。 + +--- + +## 1. 目标与完成定义 + +### 1.1 一句话目标 + +把八敌站桩立绘从「全敌共用 48 档」换成**按生物体型逐键定档(源高 36–94、显示 ×2 整数 = 72–188)**,并把 B 区站桩排版换成**方案二**(演出区占 1/3 屏、立绘落台、血条+状态/名字行落底排全场一线),最终过**整屏样张人闸**。 + +### 1.2 完成定义(出口判定清单;按新立的条款验收标注纪律分三档) + +| # | 判定项 | 验收方式 | +|---|---|---| +| C1 | 八敌源档高逐键 == `STANDEE_TIER_H`(36/38/46/58/62/74/90/94),主角 48 | ←机检:脚本读 PNG 尺寸逐键断言 | +| C2 | 每张新档色数 ≤32、无新增非整数倍缩放痕迹、描边亮度达暖白族档(主角例外档单列) | ←机检:`standee_asset_check` 升级后跑 | +| C3 | 新档拷贝进 `game/assets/art/character/character_enemy_<键>.png` 且 `.import` 同步;原 48 档归档 | ←机检:文件存在 + md5 对拍 | +| C4 | B 区 252→270、C/D 548→530;**扫雷盘 438×438 与格边长零改动** | ←机检:`ui_struct_probe` 断言值随改 + 棋盘格尺寸断言零漂移 | +| C5 | 站桩内容矩形 ⊆ 舞台矩形(内缩 8px)、底缘距舞台底 ≥8px | ←机检:⑥standee 断言族随批扩展 | +| C6 | 血条 + 状态/名字行落底排、全场血条对齐一线 | ←机检:新增断言(血条 y 逐站桩相等,容差 1px) | +| C7 | 大体档(drider 90 / duergar_king 94)头部探出盒顶进入光柱区、不裁切内容主体 | ←目检:样张看头部是否自然压进光柱、血条仍落底 | +| C8 | 意图徽标不溢出盒顶(弹入峰值 ×1.6 后仍在界内) | ←机检:探针按动画峰值矩形断言 | +| C9 | 八敌同屏一眼分得开(地精最小 72px ↔ 石君王 188px) | ←目检:整屏样张 | +| D1 | 数字/名行/徽标对比度 ≥4.5:1(承迭代 16 `ui_contrast_probe`) | ←机检:既有探针零新增红 | +| D2 | 玩家可感知「演出区变大了、怪物大小有差别了」 | ←方向性:人闸目检 | + +--- + +## 2. 现状事实(T0 代码级考证 · 2026-10-07 实查) + +| 事实 | 位置 | 值 | +|---|---|---| +| B 区高度 | `ui_assembler.gd:22` / `battle_stage_view.gd:44` | **252**(`custom_minimum_size=(0,252)`) | +| C/D 两列 | `ui_assembler.gd:26` | **548** | +| 站桩盒宽 | `battle_stage_view.gd:26` / `standee_body.gd:29` | `box = Vector2(150, 176)` | +| **立绘盒净高** | `standee_body.gd:231-234` | `176 − 44 − 36 = ` **96px** | +| 立绘缩放 | `standee_body.gd:237` | 固定 `scale = (2.0, 2.0)`、NEAREST | +| 立绘定位 | `standee_body.gd:238` | **盒内居中**(方案二要改底锚落台) | +| 脚底余量 | `standee_body.gd:27` | `STANDEE_FOOT_MARGIN = 12`;名行基线 `box.y-1-12` | +| 意图徽标 | `standee_body.gd:475` + `:194-197` | 绘制 y=86;`_intent_scale` 峰值 **1.6**(另有 1.26/1.22 变体) | +| 站桩结构断言 | `ui_struct_probe.gd:235-256` | 内容矩形 ⊆ **252px** 舞台(内缩 8px)+ 底缘 ≥8px | +| 现役素材 | `game/assets/art/character/character_enemy_<键>.png` | 八张,**48 档** | +| 重产源 | `artwork/remote-gen-2026-09-26/<键>/pixel-1.png` | 八敌齐全(含 `hero-ref`) | + +### 2.1 待算清的三笔账(T0 必答,答案决定任务卡能否成立) + +1. **盒高账(硬约束)**:新档最大显示高 = 94×2 = **188px**,而立绘盒净高只有 96px。B 区抬到 270 后,可用 = 270 − 顶部轮次指示行 − 底部结算明细带(24 摘要 / 84 结算期)− 血条状态名行(约 30)。**188 + 30 是否塞得下、84 结算期态要不要改覆盖层**——这笔账没算清之前不动代码。 +2. **84px 明细带账**:`BattleLogView` 的 24/84 两态与 176 的盒如何共存(现 252 − 176 = 76,装不下 84)——需实查是压缩盒还是覆盖层。 +3. **探顶账(P1 已裁甲,只剩量值待算)**:已考证 `_svp_container` 为 FULL_RECT + 1:1 + **无 clip** → 溢出即可渲染,**无需 clip 逻辑**。C0 只需算清 **N = 溢出上限**:受顶部中央轮次·阶段指示的横向遮挡约束(大体档在右侧敌位,须用真图复算 x 区间),暂按 ≤32px 量级,并复算最高档 94×2=188 与盒净高的差值。 + +--- + +## 3. 范围边界(🔴 不可越界) + +- **不动玩法与数值**:零核心/数据层改动,MC 零跑(纯呈现批,diff=0 实证,同迭代 16 口径)。 +- **不动棋盘**:扫雷盘 438×438、格边长、迷雾口径零改动(压缩全部由面板空档吸收)。 +- **不动主角档**:主角维持 48(定档已锁),本批只重产八敌。 +- **不动棋盘小人**:32×32 派生不随本批(重产归后续「棋盘小人重出批」)。 +- **不新增色值**:新档色数 ≤32 且色板不增色(§4.1 增量纪律双层门)。 +- **非本批**:站桩帧动画(Alpha 定版闸前)、3349 草稿整理、口号条款机检化——各自有闸,不并入。 + +--- + +## 4. 设计契约(🔴 触碰 = L3 停机) + +1. **整数倍缩放**:显示一律 ×2 NEAREST,禁非整数倍(圣经 §8.2)。 +2. **高定宽随**:只定高度整数倍档、宽度随源图宽高比,禁统一方框强拉(v3.6 教训)。 +3. **定值单点**:档位数值权威 = 圣经 §8.2 / `STANDEE_TIER_H`,代码不得硬编码第二份数字。 +4. **素材不入 git**:产物拷回 `game/assets/art/` 并同步 `.import`;过程稿归档(`artwork/` + 择机对象存储),**worktree 产图必须拷回主区,否则分支一清即丢**。 +5. **派生管线锁死**:`pixel-1` 紧裁 → 等比缩至档高 → `dom_block_resize`;**禁走 `pixelfixer recon`、禁重跑 `remove_bg`**(recon 会把同色大块抹成一坨,2026-10-06 实测)。 +6. **零新素材外的零改动**:本批不新增除八敌立绘与舞台排版以外的任何素材。 + +--- + +## 5. 任务卡(分批执行;每批独立闸) + +### 批 A · 素材重产(先做,产出实测尺寸) + +| 卡 | 内容 | 产出 | 闸 | +|---|---|---|---| +| A0 | 基线:记录八敌现役 48 档尺寸/色数;确认八份 `pixel-1` 源在盘 | 基线表 | — | +| A1 | 按 `STANDEE_TIER_H` 跑 `build_standee48()` 重产八敌 | 八张新档 + 尺寸/色数读数 | C1/C2 | +| A2 | 九人同图对照总图(八敌 + 主角,真实相对体量) | 对照总图 | 目检 | +| A3 | 拷回主区 `game/assets/art/character/` + `.import` 同步 + 旧档归档 | 盘上落位 | C3 | + +> A 批不碰任何 `.gd`。若 A2 目检发现某档偏糊 → **只改 `STANDEE_TIER_H` 重跑**(本批最便宜的返工点,这正是「先产图」的意义)。 + +### 批 B · 工具闸(把纪律变成代码) + +| 卡 | 内容 | 闸 | +|---|---|---| +| B1 | `standee_asset_check.py` 硬闸升级:按 §8.2 **逐键校验档高**(现「宽 1–75」虚设区间退役)+ 色数 ≤32 + 非整数倍痕迹 + 描边亮度 | 负向验证(喂 48 档应红) | +| B2 | 色板检查器(48 上限 + 近色 ≥8/255) | 跑现役素材零误报 | +| B3 | 路径核对器(悬空/退役命名即报) | 首跑读数 | + +### 批 C · 引擎实装(拿到实测尺寸后才动) + +| 卡 | 内容 | 依赖 | +|---|---|---| +| C0 | **T0 盒高账实算**(§2.1 三笔账)+ 定探顶方案 | 批 A 实测尺寸 | +| C1 | `ui_assembler` B 252→270、C/D 548→530 | C0 | +| C2 | `standee_body` 立绘盒抬高 + 居中改底锚落台 | C0 | +| C3 | 血条 + 状态/名字行落底排、全场血条对齐一线 | C2 | +| C4 | 大体档**允许向上溢出盒顶 N px**(不裁切;N 由 C0 实算,与光柱无关)+ 意图徽标改位(探顶档压胸) | C2 | +| C5 | 探针断言族:`ui_struct_probe` 252→270、⑥standee 扩展(血条一线 / 名行不压盒 / 探顶不出血条 / 意图峰值不溢出) | C1-C4 | + +### 批 D · 样张人闸(唯一必经人审) + +| 卡 | 内容 | +|---|---| +| D1 | 整屏样张一屏(方案二排版真图) | +| D2 | 人闸材料包(目检清单 + 代拍板追认单)+ 出口报告 | + +--- + +## 6. 验证闸 + +- **轻闸(每卡)**:`run_smoke` 全量 + `probe_gate`(含 click)+ `ui_struct_probe` + `ui_contrast_probe`。 +- **重闸(批末)**:轻闸 + `run_all` 全套 + doc 三闸(对基线零新增)+ 熵增闸 + **MC 零跑**(零数值改动,同迭代 16 口径)+ 素材机检(B 批三件)。 +- **人闸**:D 批整屏样张目检,用户亲口确认才算通过。 + +--- + +## 7. 裁决归档(P1–P3 已全裁 · 2026-10-07 用户拍板) + +**P1 · 大体档探顶怎么落 ✅ 裁甲** +- **甲(采纳)**:盒按最高档抬,**允许向上溢出盒顶 N px**,不裁切。 + - **口径修正(同批定)**:溢出判定**不得绑定光柱**——表述改为「允许向上溢出盒顶 N px,N 由 C0 实算,与背景/光柱无关」。现行溢出区恰为临时光柱氛围层(`battle_stage_view.gd:55-63`),正式舞台背景插画到位后该层退役/吸收,**判定不变、换背景零返工**。 + - **技术面(已考证)**:`_svp_container` 为 FULL_RECT + `stretch=true` 1:1 + **无 clip**,世界坐标系铺满舞台高度 → 甲案实现即「不裁切」,无需任何 clip 逻辑;光柱为世界层之下的兄弟 Control,立绘自然盖住它。 + - **约束**:横向不得压到顶部中央轮次·阶段指示;溢出上限由 C0 用真图复算 x 区间定(暂按 ≤32px 量级)。 +- 乙:真 `clip` 裁掉头部——**淘汰:会切掉石君王王冠,违反 §5.1 剪影完整**。 +- 丙:大体档降倍率(×1.5)——**淘汰:非整数倍违反契约 1。** + +**P2 · 结算期 84px 明细带与 188px 立绘共存 ✅ 裁甲** +- **甲(采纳)**:明细带结算期态改**覆盖层**,不挤压站桩盒。 +- 乙:结算期态压缩立绘盒——**淘汰:会造成站桩跳动(减动效纪律)**。 +- 丙:84 → 改小——需用户另裁数值,越权本批。 + +**P3 · 棋盘小人是否顺带重出 ✅ 裁甲** +- **甲(采纳)**:**不随本批**,保持 32×32,重出另行立项(依赖 = 本批派生管线先验证)。 +- 乙:顺带重出——本批体积超软时间盒,且混料不利于归因。 + +> **P1–P3 已全裁 ⇒ U0 开工令已具备**(用户 2026-10-07 逐项确认)。A 段可发令。 + +--- + +## 8. 批次工作流十三条对照(合规自证) + +| 条 | 本批落实 | +|---|---| +| 1 先检索后提问 | 已查在册记忆(pixelfixer 坑、档位语义)+ 正主文档(§8.2) | +| 2 任务书全列备选案 | §7 三裁点各列全案含淘汰依据 | +| 3 用户发开工令才动手 | **待 U0**(P1-P3 裁决后) | +| 4 独立 worktree + 拷导入缓存 | 复用 `D:/code/g-plan2-standee-tier`;新 worktree 先拷 `.godot/` 与字体 `.import` | +| 5 改码前定基线三查 | 冒烟 / 单测套数 / 探针读数(A0 记录;涉呈现无模拟锚) | +| 6 涉数值先采改前读数 | **不适用**(零数值改动 = 零 MC) | +| 7 新功能必配新测试 | C5 探针断言族 + B 批三件脚本机检 | +| 8 验收改动先行声明 | §1.2 完成定义表 + §6 闸序列(含 252→270 随改) | +| 9 批末四闸全绿才合并 | §6(素材批以「素材机检 + 探针 + 冒烟」替代 MC 对拍) | +| 10 合并后主区全量复跑 | 批末在主区复跑全套 | +| 11 会话收口落记 | active.md 快照 + 台账 | +| 12 实机目检归用户 | D 批人闸 | +| 13 —— | 批 A/B/C/D 每段独立提交,段末合一次 | + +--- + +## 9. 异常分级 + +L1 自救(一次重试)→ L2 记录继续(连续 3 次同红灯升级)→ L3 停机写报告等用户(触碰 §4 契约任一)。 + +--- + +## 10. 风险与遗留 + +1. **盒高账可能推翻方案二的 270**:若 188 + 血条 + 明细带算下来 > 270,需回到用户重裁(要么再抬 B 区、要么把最高档从 94 降到 ~84)。这是本批最大的单点风险,故排在 C0。 +2. **worktree 素材丢失风险**:A3 拷贝回主区是硬步骤(素材不入 git),漏做即丢。 +3. **并行会话冲突**:主区有在飞会话(iter17-enemy / uialign),合并采取「分支内先并入 master、再 ff 主区」,不直动主区脏件。 +4. **遗留**:站桩帧动画、3349 草稿整理、口号条款机检化、另两个「0 张」缺口(19 徽章 / 6 纹带)各自有闸,不并入本批。 +5. **关联新登记(2026-10-07 用户点名)**:**战斗区背景图替换**(B 区舞台插画 `stage/stage_bg.png` + 临时光柱退役)已作为独立待办登记进 `06-待办清单`。**建议开工顺序 = 本批 C0 盒高账定案后再出背景图**,避免 B 区高度/构图二次返工;本批 P1 甲案的溢出判定已与光柱解耦,二者互不阻塞。 diff --git a/tools/palette_check.py b/tools/palette_check.py new file mode 100644 index 00000000..ecbb5cb0 --- /dev/null +++ b/tools/palette_check.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""B2 色板检查器(圣经 §8.3①挂点实现 · 立绘定档批 2026-10-07)。 + +⚠⚠ 口径纪律(2026-10-07 实跑后订正,**勿改回**)——两处极易写错: + +1. **48 色上限不是「每张 PNG 的色数」**。§4.1 的「全作色板硬上限 48 色」指 + **设计色板 token**(UI 语义色/墨色/材质色那套),不是像素画的独立色数。 + 像素画单张另有**32 色冻结**口径,由 standee_asset_check 管(像素域)。 + 初版把 48 套到每张 PNG 上⇒ 12 张全红(误报率 100%),已作废。 + 本器改为:单张色数**只报读数不判红**;48 只在 --tokens 模式下校验色板表。 + +2. **近色 ≥8/255 是「新色入板双门」,不是存量两两互查**。§4.1 原文: + 「**新色**入板须同时过两道门……②与既有任一色的 RGB 三通道差 ≥8/255」。 + 它管的是**新增色 vs 既有色板**。对存量色两两互查会把 AI 生成的明暗渐变 + 全部打成近色(实测 drider 22 组 / 巨蜘蛛 82 组)⇒ 噪声淹没真问题。 + 本器改为:近色**只对新增色集**判(新增色 vs 基线色板)。 + +另:必须排除全透明像素 (0,0,0,0)(同standee_asset_check 的坑)。 + +用法: + # 主用法:扫目录,新增色相对 --baseline 判定 + python tools/palette_check.py <目录> --baseline <基线目录> + # 无基线:只出读数(单张色数/ 近色对计数),不判红 + python tools/palette_check.py <目录> +退出码: 0 通过 / 1 有红灯(仅新增色违规或超单张上限时) +""" +import os +import sys + +from PIL import Image + +# §4.1 硬上限(**设计色板 token 口径**,非像素画单张色数) +TOKEN_MAX = 48 +NEAR_TOL = 8 # §4.1 新色双门:与既有任一色三通道差 ≥8/255 +# 像素画单张色数(像素域32 色冻结)——由 standee_asset_check 执法, +# 本器只作读数提示,避免两处口径打架 +PIXEL_COLOR_SOFT_MAX = 32 + + +def palette_of(path): + """返回 (不透明像素唯一色集合, 像素数)。排除全透明。""" + im = Image.open(path).convert("RGBA") + px = im.load() + w, h = im.size + cols = set() + n = 0 + for y in range(h): + for x in range(w): + c = px[x, y] + if c[3] == 0: + continue + cols.add(c[:3]) + n += 1 + return cols, n + + +def nearest_to(col, pool, tol): + """返回 col 在 pool 中最近的色及其差值(差 < tol 算近色)。""" + best, bd = None, 999 + for c in pool: + d = max(abs(col[k] - c[k]) for k in range(3)) + if d < bd: + best, bd = c, d + return best, bd + + +def hexs(c): + return "#%02X%02X%02X" % c + + +def scan(d, baseline_cols, report_only): + files = [] + for dirpath, _, names in os.walk(d): + for fn in sorted(names): + if fn.lower().endswith(".png"): + files.append(os.path.join(dirpath, fn)) + files.sort() + print("=== %s(%d 张 PNG)===" % (d, len(files))) + n_fail = 0 + all_new = set() + for p in files: + cols, npx = palette_of(p) + rel = os.path.relpath(p, d) + notes = [] + msgs = [] + + over = len(cols) > PIXEL_COLOR_SOFT_MAX + notes.append("色%d%s" % (len(cols), "(>32 像素域冻结)" if over else "")) + + new = set() + if baseline_cols is not None: + new = cols - baseline_cols + if new: + # §4.1 新色双门②:新增色须与既有任一色差 ≥8/255 + bad = [] + for c in sorted(new): + near, d3 = nearest_to(c, baseline_cols, 999) + if d3 < NEAR_TOL: + bad.append((c, near, d3)) + all_new |= new + notes.append("新增%d" % len(new)) + for c, near, d3 in bad[:5]: + msgs.append("新增色 %s 与既有 %s 三通道差 %d < %d" + "(§4.1 新色双门②,须走换色合并)" + % (hexs(c), hexs(near), d3, NEAR_TOL)) + if len(bad) > 5: + msgs.append("…… 另有 %d 个新增色同样不过门" % (len(bad) - 5)) + else: + notes.append("新增0") + + line = "FAIL " if msgs else ("WARN " if (report_only and over) else "PASS ") + print("%s %-46s %s" % (line, rel, " ".join(notes))) + for m in msgs: + print(" └─ %s" % m) + n_fail += 1 if msgs else 0 + print("小计:%d 张,%d 张有红灯\n" % (len(files), n_fail)) + return n_fail, len(files), all_new + + +def main(): + args = sys.argv[1:] + baseline = args[args.index("--baseline") + 1] if "--baseline" in args else None + dirs = [a for a in args if not a.startswith("--") + and os.path.isdir(a) and a != baseline] + + if not dirs: + print(__doc__) + sys.exit(2) + + baseline_cols = None + if baseline: + baseline_cols = set() + for dirpath, _, names in os.walk(baseline): + for fn in names: + if fn.lower().endswith(".png"): + cols, _ = palette_of(os.path.join(dirpath, fn)) + baseline_cols |= cols + print("基线色板:%s,唯一色 %d" % (baseline, len(baseline_cols))) + + total_fail = total_files = 0 + total_new = set() + report_only = baseline_cols is None + for d in dirs: + f, n, new = scan(d, baseline_cols, report_only) + total_fail += f + total_files += n + total_new |= new + print("色板机检完成:%d 张 PNG,%d 张有红灯,累计新增色 %d" + % (total_files, total_fail, len(total_new))) + if report_only: + print("提示:未给 --baseline ⇒ 本次为只读模式(不判红)。" + "『色数>32』标 WARN 属像素域提示,最终执法在 standee_asset_check。") + sys.exit(1 if total_fail else 0) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/path_check.py b/tools/path_check.py new file mode 100644 index 00000000..68a33e2e --- /dev/null +++ b/tools/path_check.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""B3 路径核对器(圣经 §8.3② 挂点实现 · 立绘定档批 2026-10-07)。 + +职责:扫描 asset-path-map.md 声明的全部路径 + 代码侧素材常量, + 报「悬空(声明存在但盘上无)/ 退役未清(仍引用已退役命名)」。 + +为什么需要(2026-10-06 实证三处悬空):素材不入 git,路径错了不会报错, +只会在实机静默回退(UiKit.load_tex 的 exists 守卫)⇒ 肉眼难发现。 + +三层校验: + L1 目录级 asset-path-map §1 声明的路径存在性(candidates 按需创建豁免) + L2 文件级 §3 game/assets/art 语义子目录现役清单逐件存在性 + L3 代码级 grep 代码里的 res://assets/art 引用,核对盘上是否存在 + + 检出已知退役命名(board_floor/board_wall/pillar_dungeon_*) + +用法: + python tools/path_check.py [--repo <仓库根>] [--code <代码根>] +退出码: 0 通过 / 1 有红灯 +""" +import os +import re +import sys + +# asset-path-map §1 目录分工总表(逐条对应文档,勿复写第二份事实源) +DECLARED_DIRS = [ + # (路径, 角色, 按需创建豁免) + ("design/art/candidates", "设计侧源档(档案库,游戏禁引用)", True), + ("game/assets/art", "运行时素材唯一落地处", False), + ("game/assets/ui/icons", "UI 图标(唯一入 git 素材目录)", False), + ("artwork/mockups", "混合样张/评审产物", False), +] + +# asset-path-map §3 game/assets/art 语义子目录 +DECLARED_SUBDIRS = ["character", "fx", "board", "ui"] + +# §3 现役清单(文件名按 §8.3 域键式) +ACTIVE_FILES = [ + "character/character_hero_idle.png", + "character/character_hero_walk.png", + "character/character_hero_portrait.png", + "fx/shovel_swing_f0.png", + "fx/shovel_swing_f1.png", + "fx/shovel_swing_f2.png", + "board/flag_seal.png", +] +# 立绘八敌按数据表键生成(键集=STANDEE_TIER_H,见 standee_asset_check) +ENEMY_KEYS = ("bugbear", "drider", "duergar", "duergar_king", "giant_spider", + "goblin", "hook_horror", "ogre_chief") +ACTIVE_FILES += ["character/character_enemy_%s.png" % k for k in ENEMY_KEYS] + +# asset-path-map v3.9 已退役命名(检出即红) +RETIRED_PATTERNS = [ + (re.compile(r"board_floor"), "board_floor 命名已退役(→terrain_floor__)"), + (re.compile(r"board_wall"), "board_wall 命名已退役"), + (re.compile(r"pillar_dungeon"), "pillar_dungeon_* 命名已退役"), + (re.compile(r"production/mockups"), "production/mockups 为废路径(→artwork/mockups)"), +] + +# 路径末尾要允许 printf 转换符(%s/%d/%0Nd)——否则正则会在 '%' 处截断, +# 拿到 "res://assets/art/board/terrain_" 这种半截串,既不像模板也不像真实 +# 路径,两边都判错。初版即栽在此处(2 处假悬空),现让 %s/%d 一并吃掉。 +RES_RE = re.compile(r"res://assets/art/[A-Za-z0-9_/\.\-%]+") +# ⚠ 路径模板(printf 式)不是可解析的真实路径——常量里存的是 +# "res://assets/art/board/terrain_%s_%d.png" 这类格式串,盘上当然没有 +# 字面同名文件。初版未识别⇒ 假悬空。识别到 % 转换符即跳过(模板有独立的 +# 运行时守卫与 test 覆盖,不在本器执法面)。 +FMT_MARKERS = ("%s", "%d", "%0", "{") + + +def is_path_template(ref): + return any(m in ref for m in FMT_MARKERS) + + +def main(): + argv = sys.argv[1:] + repo = "." + code = "game" + if "--repo" in argv: + repo = argv[argv.index("--repo") + 1] + if "--code" in argv: + code = argv[argv.index("--code") + 1] + + problems = [] + notes = [] + + # ⚠ worktree 素材环境判定(素材不入 git ⇒ worktree 素材靠人工拷贝同步, + # 常见三种态,须区分否则满屏假红): + # full —— 主区形态,art 下素材齐全,全量校验 + # partial —— worktree 常态:character/ 被拷进来了(跑测试三件套惯例), + # fx/board/ui 没拷 ⇒ 缺的那些是「未同步」不是「悬空」 + # empty —— art 下 PNG 总数为 0,素材整体未同步 + # 只有 full 态才把缺失判红灯;partial/empty 判 SKIP 并提示同步。 + artdir = os.path.join(repo, "game", "assets", "art") + present = [s for s in DECLARED_SUBDIRS + if os.path.isdir(os.path.join(artdir, s))] + png_total = sum(1 for dp, _, ns in os.walk(artdir) + for n in ns if n.lower().endswith(".png")) + if len(present) == len(DECLARED_SUBDIRS): + env = "full" + elif png_total == 0: + env = "empty" + else: + env = "partial" + synced = env != "full" + if synced: + notes.append("素材环境 = **%s**(子目录就位 %d/%d,PNG 共 %d)—— " + "worktree 素材靠人工拷贝同步,缺项属未同步非悬空" + % ({"partial": "部分同步", "empty": "空"}[env], + len(present), len(DECLARED_SUBDIRS), png_total)) + notes.append("缺失子目录:%s(如需完整校验请对**主区**跑本器)" + % ("、".join(s for s in DECLARED_SUBDIRS + if s not in present) or "无")) + else: + notes.append("素材环境 = 完整(主区形态),全量校验") + + print("=== L1 目录级(asset-path-map §1)===") + for rel, role, exempt in DECLARED_DIRS: + p = os.path.join(repo, rel) + ok = os.path.isdir(p) + if ok: + print("OK %-30s %s" % (rel, role)) + elif exempt: + print("SKIP %-30s 按需创建豁免(文档已注非作废)" % rel) + elif synced and rel.startswith(("game/", "artwork/")): + print("SKIP %-30s 素材未同步豁免 —— %s" % (rel, role)) + else: + print("FAIL %-30s 悬空:声明存在但盘上无 —— %s" % (rel, role)) + problems.append("目录悬空: " + rel) + + print("\n=== L2 文件级(asset-path-map §3 现役清单)===") + for rel in ACTIVE_FILES: + p = os.path.join(repo, "game", "assets", "art", rel) + sub = rel.split("/")[0] + if os.path.isfile(p): + print("OK %s" % rel) + elif synced: + print("SKIP %-44s 素材未同步豁免" % rel) + else: + print("FAIL %-44s 悬空(现役清单声明但盘上无)" % rel) + problems.append("文件悬空: " + rel) + for sub in DECLARED_SUBDIRS: + p = os.path.join(artdir, sub) + if not os.path.isdir(p): + if synced: + print("SKIP 子目录 %-40s 素材未同步豁免" % sub) + else: + print("FAIL 子目录 %-40s 悬空" % sub) + problems.append("子目录悬空: " + sub) + + print("\n=== L3 代码级 res:// 引用核对 ===") + # ⚠ 口径:Godot 的 res:// 根 = 项目目录 game/(project.godot 所在处), + # 不是仓库根。所以 res://assets/art/x.png → /game/assets/art/x.png。 + # 2026-10-07 初版误按仓库根解析 ⇒ 全部引用假悬空,已修。 + refs = {} + for dirpath, dirnames, names in os.walk(os.path.join(repo, code)): + dirnames[:] = [d for d in dirnames + if d not in (".git", ".godot", "import", "node_modules")] + for fn in names: + if not fn.endswith((".gd", ".tscn", ".tres")): + continue + fp = os.path.join(dirpath, fn) + try: + txt = open(fp, "r", encoding="utf-8", errors="ignore").read() + except OSError: + continue + for m in RES_RE.findall(txt): + refs.setdefault(m, []).append(os.path.relpath(fp, repo)) + if not refs: + print("(代码侧无 res://assets/art 硬引用——符合 §2 第4步禁 preload 纪律)") + templates = [] + for ref, where in sorted(refs.items()): + if is_path_template(ref): + templates.append((ref, where)) + continue + p = os.path.join(repo, "game", *ref[len("res://"):].split("/")) + if os.path.exists(p): + print("OK %-52s ← %d 处" % (ref, len(where))) + elif synced: + print("SKIP %-52s 无素材环境豁免 ← %s" % (ref, where[0])) + else: + print("FAIL %-52s 悬空 ← %s" % (ref, ", ".join(where[:3]))) + problems.append("代码引用悬空: " + ref) + if templates: + print("SKIP 路径模板 %d 条(printf 格式串,非字面路径;运行时由守卫兜底)" + % len(templates)) + for ref, where in templates: + print(" · %-46s ← %s" % (ref, where[0])) + + print("\n=== 退役命名扫描 ===") + # ⚠ 口径纪律:退役命名**只在代码与素材清单里是违规**;文档的版本历史/ + # 台账在「叙述这些名字已退役」时提到它们属正常,不是违规。 + # 故扫描面只含:代码(.gd/.tscn) + 素材清单(design/art/*.md), + # 且排除版本历史/台账类文件(active.md / *checklist* / *memory* / .zcode)。 + # 2026-10-07 初版全仓扫 .md ⇒ active.md/记忆文件里的历史记述全被误判,已修。 + hits = 0 + scan_exts = (".gd", ".tscn") + skip_names = ("active.md",) + for dirpath, dirnames, names in os.walk(repo): + dirnames[:] = [d for d in dirnames + if d not in (".git", ".godot", "archive", "node_modules", + ".workbuddy", ".zcode")] + for fn in names: + fp = os.path.join(dirpath, fn) + rel = os.path.relpath(fp, repo) + if any(s in rel for s in (".workbuddy", ".zcode")): + continue + if fn in skip_names: + continue + is_code = fn.endswith(scan_exts) + is_manifest = (fn.endswith(".md") + and os.sep + "art" + os.sep in fp + and "版本历史" not in fn) + if not (is_code or is_manifest): + continue + try: + txt = open(fp, "r", encoding="utf-8", errors="ignore").read() + except OSError: + continue + # 叙述性豁免:命中行若同时含「退役/已废/更正/改判」等记述词, + # 视为「在讲这段历史」而非「仍在用这个名字」 + for rx, why in RETIRED_PATTERNS: + for line in txt.splitlines(): + if rx.search(line) and not any( + w in line for w in ("退役", "已废", "更正", "改判", + "原写", "原名", "作废")): + print("FAIL %-52s %s" % (rel, why)) + print(" └─ %s" % line.strip()[:90]) + problems.append("退役命名: " + rel) + hits += 1 + break + if not hits: + print("OK 无退役命名残留(代码 + 素材清单;叙述性历史记述已豁免)") + + for nt in notes: + print("\n注:%s" % nt) + print("\n路径机检完成:%d 处问题" % len(problems)) + return 1 if problems else 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/tools/standee_asset_check.py b/tools/standee_asset_check.py index 8297245e..5d7385ed 100644 --- a/tools/standee_asset_check.py +++ b/tools/standee_asset_check.py @@ -1,79 +1,203 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""站桩槽位素材机检(M-3 迭代 6 T1——48px 档契约断言;重闸素材面闸)。 +"""站桩素材机检(B1 硬闸升级 · 立绘定档批 2026-10-07)。 -四断言(契约=计划书 C4/圣经 §8.2): - 尺寸 高恒 48(槽位源档 ×2 整数放大=盒高 96);宽 1..75(盒宽 150 半值)。 - 锚点 底行有不透明像素(底中锚——纵向比例自底边口径,脚贴画布底缘)。 - 色数 ≤32(fixer 批 32 色冻结;NEAREST 采样不产生新色)。 - 无插值 alpha 二值(仅 0/255——双线性/混合会在边缘产生中间 α)。 +升级依据=art-bible §8.3③ 挂点规格(审计 §07-2/3): + - 逐键校验档高(**原「宽 1..75」虚设区间退役**——宽度改为「源宽×2 ≤ 站桩盒宽 150」 + 的物理约束,档高改为逐键断言 STANDEE_TIER_H); + - 像素域色数 ≤32(**排除全透明像素**,见下方口径注); + - 无非整数倍缩放痕迹(alpha 二值 + 无中间 α); + - 描边亮度达标(亮描边暖白族,character-design §10硬规则「禁黑描边」)。 -用法: python tools/standee_asset_check.py [角色素材目录] - 默认 game/assets/art/character/;必需集=八敌+主角 portrait/attack/hit。 +⚠ 色数口径坑(2026-10-07 实踩,勿改回naive 统计): +naive `len(set(所有像素))` 会把全透明 (0,0,0,0) 计成一个色 ⇒ 报33 假红灯。 +正确口径 = 只统计 alpha>0 像素的唯一 RGB。机检必须如此,否则天天误报。 + +档高表= tools/pixelize_candidates.py 的 STANDEE_TIER_H(定值单点纪律 §8:本表 +不在检查器里复写一份,改由 import 读,避免两处数字漂移)。 + +用法: + python tools/standee_asset_check.py [角色素材目录] + 默认 game/assets/art/character/ + --json 输出机器可读格式 +退出码: 0 全过 / 1 有红灯 """ +import json import os import sys -from collections import Counter + from PIL import Image -REQUIRED = ["character_enemy_%s.png" % k for k in - ("bugbear", "drider", "duergar", "duergar_king", "giant_spider", - "goblin", "hook_horror", "ogre_chief")] + \ - ["character_hero_portrait.png", "character_hero_attack.png", "character_hero_hit.png"] -SLOT_H = 48 -W_MAX = 75 -COLOR_MAX = 32 +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +try: + from pixelize_candidates import STANDEE_TIER_H, STANDEE_SLOT_H +except ImportError: # pragma: no cover + STANDEE_TIER_H = { + "goblin": 36, "giant_spider": 38, + "duergar": 46, "hook_horror": 58, + "bugbear": 62, "ogre_chief": 74, + "drider": 90, "duergar_king": 94, + } + STANDEE_SLOT_H = 48 + +ENEMY_KEYS = ("bugbear", "drider", "duergar", "duergar_king", "giant_spider", + "goblin", "hook_horror", "ogre_chief") +REQUIRED = ["character_enemy_%s.png" % k for k in ENEMY_KEYS] + \ + ["character_hero_portrait.png", "character_hero_attack.png", + "character_hero_hit.png"] + +# ── 契约常量(全部有出处,禁复写第二份) ────────────────────────────── +COLOR_MAX = 32 # 圣经 §4.1/fixer 批 32 色冻结;机检口径=alpha>0 唯一 RGB +DISPLAY_SCALE = 2 # standee_body.gd:237 _portrait_sprite.scale = (2,2) +BOX_W = 150 # standee_body.gd:29 var box := Vector2(150, 176) +SRC_W_MAX = BOX_W // DISPLAY_SCALE # =75,源宽上限(×2 后须塞进盒宽) +ALPHA_BIN = {0, 255} # 二值 alpha 域 +# 描边亮度阈值(2026-10-07 _tmp_probe_outline.py 实测标定): +# 敌人暖白边占比实测 54.1%–84.3%,主角 portrait/hit 实测 0.4%(无暖白描边,见下)。 +# 敌人闸取 40%(低于实测下限 54.1% 留余量,高于噪声);主角**豁免**—— +# character-design §10要求亮描边,但现役三态主角实为暗边,属已知缺口, +# 不在本批(美术素材批 A 范围)修,机检只报读数不判红,避免闸永久红。 +OUTLINE_WARM_L_MIN = 150 # 暖白 = 亮度 ≥150 且低饱和 +OUTLINE_WARM_S_MAX = 0.35 +OUTLINE_MIN_ENEMY = 40.0 # 敌人:暖白描边像素占轮廓像素 ≥40% +HERO_OUTLINE_EXEMPT = ("portrait", "attack", "hit") -def check(path): +def _lum(c): + return 0.299 * c[0] + 0.587 * c[1] + 0.114 * c[2] + + +def _sat(c): + mx, mn = max(c[:3]), min(c[:3]) + return 0.0 if mx == 0 else (mx - mn) / float(mx) + + +def expected_tier_h(fn): + """按文件名返回应有档高。""" + if fn.startswith("character_enemy_") and fn.endswith(".png"): + key = fn[len("character_enemy_"):-len(".png")] + return STANDEE_TIER_H.get(key) + return STANDEE_SLOT_H # 主角三态同档 + + +def check(path, fn): + """返回 (problems, info)。info 含机检读数。""" problems = [] im = Image.open(path).convert("RGBA") w, h = im.size - if h != SLOT_H: - problems.append("高应 48,实得 %d" % h) - if not (1 <= w <= W_MAX): - problems.append("宽应 1..%d,实得 %d" % (W_MAX, w)) px = im.load() + + # 断言①逐键档高(原「高恒 48」已随定档批退役) + tier = expected_tier_h(fn) + if tier is not None and h != tier: + problems.append("档高应 %d(§8.2 逐键定档),实得 %d" % (tier, h)) + + # 断言② 宽度物理约束:源宽 ×2 须塞进站桩盒宽 150(替代旧虚设区间 1..75) + if w > SRC_W_MAX: + problems.append("源宽 %d ×2=%d 超站桩盒宽 %d(§8.2 高定宽随但不得溢出盒)" + % (w, w * DISPLAY_SCALE, BOX_W)) + if w < 1: + problems.append("源宽 %d 非法" % w) + colors = set() alphas = set() + edges = [] bottom_opaque = 0 for y in range(h): for x in range(w): c = px[x, y] alphas.add(c[3]) - if c[3] > 0: - colors.add(c[:3]) - if y == h - 1: - bottom_opaque += 1 + if c[3] == 0: + continue + colors.add(c[:3]) # 口径:只收不透明像素的 RGB + if y == h - 1: + bottom_opaque += 1 + nb = ((x > 0 and px[x - 1, y][3] == 0) or + (x < w - 1 and px[x + 1, y][3] == 0) or + (y > 0 and px[x, y - 1][3] == 0) or + (y < h - 1 and px[x, y + 1][3] == 0)) + if nb: + edges.append(c) + + # 断言③ 底中锚(脚贴画布底缘) if bottom_opaque < 1: problems.append("底行无不透明像素(底中锚失守——脚未贴画布底缘)") + + # 断言④ 色数 ≤32(alpha>0 口径) if len(colors) > COLOR_MAX: - problems.append("色数 %d > %d" % (len(colors), COLOR_MAX)) - if not alphas <= {0, 255}: - problems.append("alpha 非二值(插值痕迹:%s)" % sorted(alphas - {0, 255})[:5]) - return problems, (w, h, len(colors)) + problems.append("色数 %d > %d(口径=alpha>0 唯一 RGB)" % (len(colors), COLOR_MAX)) + + # 断言⑤ 无非整数倍缩放痕迹:alpha 二值(双线性/混合会留中间 α) + if not alphas <= ALPHA_BIN: + problems.append("alpha 非二值(插值/非整数倍缩放痕迹:%s)" + % sorted(alphas - ALPHA_BIN)[:5]) + + # 断言⑥ 描边亮度(亮描边暖白族,禁黑描边) + warm_pct = 0.0 + dark_pct = 0.0 + if edges: + warm = sum(1 for c in edges + if _lum(c) >= OUTLINE_WARM_L_MIN and _sat(c) <= OUTLINE_WARM_S_MAX) + dark = sum(1 for c in edges if _lum(c) < 60) + warm_pct = warm * 100.0 / len(edges) + dark_pct = dark * 100.0 / len(edges) + is_hero = any(t in fn for t in HERO_OUTLINE_EXEMPT) + if not is_hero and warm_pct < OUTLINE_MIN_ENEMY: + problems.append("暖白描边占比 %.1f%% < %d%%(亮描边纪律,character-design §10)" + % (warm_pct, OUTLINE_MIN_ENEMY)) + + info = { + "size": [w, h], + "tier_h": tier, + "colors": len(colors), + "bottom_opaque": bottom_opaque, + "edge_px": len(edges), + "outline_warm_pct": round(warm_pct, 1), + "outline_dark_pct": round(dark_pct, 1), + "outline_exempt": any(t in fn for t in HERO_OUTLINE_EXEMPT), + } + return problems, info def main(): - root = sys.argv[1] if len(sys.argv) > 1 else os.path.join("game", "assets", "art", "character") - missing = [f for f in REQUIRED if not os.path.isfile(os.path.join(root, f))] - n_fail = 0 + argv = [a for a in sys.argv[1:] if not a.startswith("--")] + as_json = "--json" in sys.argv + root = argv[0] if argv else os.path.join("game", "assets", "art", "character") + missing, results, n_fail = [], [], 0 for f in REQUIRED: p = os.path.join(root, f) if not os.path.isfile(p): - print("MISS %-36s 缺位" % f) + missing.append(f) n_fail += 1 continue - problems, info = check(p) - if problems: - n_fail += 1 - for pr in problems: - print("FAIL %-36s %s" % (f, pr)) + problems, info = check(p, f) + results.append({"file": f, "problems": problems, "info": info}) + n_fail += 1 if problems else 0 + + if as_json: + print(json.dumps({"root": root, "missing": missing, + "results": results, "fail": n_fail}, + ensure_ascii=False, indent=2)) + sys.exit(1 if n_fail else 0) + + for f in missing: + print("MISS %-36s 缺位" % f) + for r in results: + i = r["info"] + if r["problems"]: + for pr in r["problems"]: + print("FAIL %-36s %s" % (r["file"], pr)) else: - print("PASS %-36s %dx%d 色数%d" % (f, info[0], info[1], info[2])) + print("PASS %-36s %dx%d 档高%d 色数%d 暖白边%.1f%%%s" + % (r["file"], i["size"][0], i["size"][1], i["tier_h"], + i["colors"], i["outline_warm_pct"], + "(豁免)" if i["outline_exempt"] else "")) print("机检完成:%d 件必需,%d 件未过" % (len(REQUIRED), n_fail)) + if not as_json: + print("提示:主角三态暖白描边占比实测≈0.4%(暗边),属已知缺口," + "本闸豁免不判红——如需修另立主角描边素材批。") sys.exit(1 if n_fail else 0) if __name__ == "__main__": - main() + main() \ No newline at end of file