merge: M-1 迭代 2「样张冻结与表现定稿」loop——样张 v2 机检 16/16 四冻结+框材组 8 件接线+演出密度终稿+棋盘数字终稿+M-1 出口闸检查单
五批次 a08d89c/f9b0765→2d1388b:①T1 样张 v2(机检闸形 A 全过即冻结——新增 A3 Δ≥55/F6 双明度带/F7 DEPTH_PX 同源/A6 观感密度四断言,art-bible v2.13 落四冻结+UiKit 小人蓝同步 #2E86DE+图例旗色封印黑漏网修)②T2 框材组 8 件(♻ Kenney/OGA 全 CC0 量化改色入包,UiKit 单点工厂+五视图接线,✦ 背景横幅列缺料)③T3 演出四项(死亡碎裂/胜利纹章/失败掸灰/意图三档+结算光带缘线)+fx_probe 新探针④T4 数字形制冻结值落码(三档墨色温/字号 ≥18 公式/色牌 0.60·0.62)⑤T5 M-1 出口闸检查单(design/qa/m1-exit-gate-checklist-2026-09-28.md——真人目检八项+✦ 缺料清单)+06 两已解决条目移除+roadmap 挂起清单对齐。 验证:样张机检 16/16+冒烟 557/0(基线持平)+五探针(ui_input/ui_probe/ui_click/ui_tune/fx 新增)全 PASS+doc 三闸对基线零新增(lint 8=8/term 215=215/死链 0)+熵增闸 PASS。
This commit is contained in:
commit
9abf5ff448
20 files changed
+748
-76
No files matched your search
+11
-8
@@ -1,6 +1,6 @@
|
|||||||
# 美术圣经(Art Bible)— Dungeon Sweeper
|
# 美术圣经(Art Bible)— Dungeon Sweeper
|
||||||
|
|
||||||
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归台账 125);历次修订明细见文末版本历史(现行为 v2.9)
|
> 项目:Dungeon Sweeper(扫雷 × 自动战斗 闯关 Roguelike)· 状态:**第 1–9 节定稿**(§6 环境/§7 UI/§8 资产规范/§9 参考与反参考已于 2026-09-23 v1.8 按 D1/D2/D4/D5 框架成文;D3=§5 角色已于 v1.6 落地、v1.7 抽离独立文档;框架 2026-09-22 用户拍板,原登记于中间产物修订指导,经裁决不入库,明细归台账 125);历次修订明细见文末版本历史(现行为 v2.13)
|
||||||
>
|
>
|
||||||
> **Art Director Sign-Off (AD-ART-BIBLE)**:历次批均 SKIPPED(v1.8 §6–9 补全亦 SKIPPED)— Lean/Solo mode(非 PHASE-GATE;§6–9 已成文,full 门禁签核时机待用户表态)
|
> **Art Director Sign-Off (AD-ART-BIBLE)**:历次批均 SKIPPED(v1.8 §6–9 补全亦 SKIPPED)— Lean/Solo mode(非 PHASE-GATE;§6–9 已成文,full 门禁签核时机待用户表态)
|
||||||
> 风格基调(2026-09-10 用户定稿):地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身
|
> 风格基调(2026-09-10 用户定稿):地牢题材但**非暗黑风**——轻快明亮的幻想冒险风,全年龄;像素风;角色 2~3 头身
|
||||||
@@ -60,6 +60,8 @@
|
|||||||
|
|
||||||
**判定与回退**:第 1 项不过 → 按原则二设计测试回退——加牌、调牌(提牌墨对比/扩牌面积皆可),禁止缩字号换字形逃避;第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增图类型/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;《HUD框架》以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。
|
**判定与回退**:第 1 项不过 → 按原则二设计测试回退——加牌、调牌(提牌墨对比/扩牌面积皆可),禁止缩字号换字形逃避;第 2–4 项不过 → 修形不修数。样张通过 = 砖面形态与色板冻结;新增图类型/变异色板须重跑本测试(§4.3)。执行时机:正式砖面资产开工前;《HUD框架》以本节为引用依据(2026-09-07 门禁:样张测试并入 art-bible 执行)。
|
||||||
|
|
||||||
|
> **样张 v2 记录(2026-09-28,四冻结生效)**:机检 16/16 全绿(扩展断言集:A3 图底分离 Δ≥55/F6 墙格顶面立面双明度带 Δ≥18/F7 整板悬崖 DEPTH_PX 与游戏渲染常量同源/A6 观感密度梯度均值 ≥4.70=v1 实测 4.09×1.15)。**四冻结**——①砖面形态:方案甲 21 瓦片+高墙感 D 投影档+整板悬崖(§3.2 高墙瓦片体系条定稿参数);②色板:小人蓝 #2E86DE/接触带微光 #D6ECFF/类型微光五组定稿(§4.1/§4.2);③动画方案:§8.4 帧数档冻结;④数字形制:色牌形制定稿(#F5EAD3 素色直角小牌+Fusion Pixel+字号 max(18, ceil(格边×0.5))+墨三档,§7.4)。产物=`artwork/mockups/sample-batch-v2-2026-09-28.png`,机检报告=`design/research/sample-v2-机检报告-2026-09-28.md`;气质终审=批末用户目检(闸形 A,2026-09-27 拍板)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 氛围与情绪(Mood & Atmosphere)
|
## 2. 氛围与情绪(Mood & Atmosphere)
|
||||||
@@ -162,7 +164,7 @@
|
|||||||
- **旗(封印印记)**:插旗动作的叙事呈现 = **封印**(玩家可见专名,2026-09-25 用户拍板,registry terminology 登记)——玩家对未开格插旗,即对该格(巢穴)实施封印,旗 = 封印印记;封印色(§4.1,现行封印黑)与格内优先级栈位(§1.2)不变。封印特效(施印瞬间 + 印记常显形态)待表现批,资产归清单 §4 演出类;现行程序化占位 = 封印黑「旗」字形(棋盘惯例:贴图在位=字形退役)。←原一;P2;术语批 2026-09-25;封印紫批 2026-09-26;封印黑批 2026-09-28
|
- **旗(封印印记)**:插旗动作的叙事呈现 = **封印**(玩家可见专名,2026-09-25 用户拍板,registry terminology 登记)——玩家对未开格插旗,即对该格(巢穴)实施封印,旗 = 封印印记;封印色(§4.1,现行封印黑)与格内优先级栈位(§1.2)不变。封印特效(施印瞬间 + 印记常显形态)待表现批,资产归清单 §4 演出类;现行程序化占位 = 封印黑「旗」字形(棋盘惯例:贴图在位=字形退役)。←原一;P2;术语批 2026-09-25;封印紫批 2026-09-26;封印黑批 2026-09-28
|
||||||
- **可挖域接触带微光(交互反馈层,2026-09-25 用户拍板)**:挖掘期,英雄格周界与可挖墙(8 向相邻未开格)的接触面画呼吸微光——正交邻墙=该侧整条边,斜向邻墙=共享角点+向两侧各延伸半条边;光只画英雄格(空间)一侧、不进墙格(与装饰变体零冲突,数字砖心不涉)。世界观口径:未开格=墙、已开格=挖出的空间,微光=空间的灯光边界。色相=蓝族浅一档(§4.2 蓝条;与主角蓝框的分层归样张批);呼吸=§2.1 挖掘期唯一在动元素,减动效停格。角点为斜墙与正交墙共享、不逐格归因,逐格指认=悬停金框(现役)。旧「墙格整框绿相呼吸」随本条退役(实现切换归表现批,`DIG_EDGE` 绿相退役)。←§2.1;§4.2;P1
|
- **可挖域接触带微光(交互反馈层,2026-09-25 用户拍板)**:挖掘期,英雄格周界与可挖墙(8 向相邻未开格)的接触面画呼吸微光——正交邻墙=该侧整条边,斜向邻墙=共享角点+向两侧各延伸半条边;光只画英雄格(空间)一侧、不进墙格(与装饰变体零冲突,数字砖心不涉)。世界观口径:未开格=墙、已开格=挖出的空间,微光=空间的灯光边界。色相=蓝族浅一档(§4.2 蓝条;与主角蓝框的分层归样张批);呼吸=§2.1 挖掘期唯一在动元素,减动效停格。角点为斜墙与正交墙共享、不逐格归因,逐格指认=悬停金框(现役)。旧「墙格整框绿相呼吸」随本条退役(实现切换归表现批,`DIG_EDGE` 绿相退役)。←§2.1;§4.2;P1
|
||||||
- **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰不得伪装逻辑差异。←原三
|
- **墙体**:圆角砌块堆叠,禁尖刺装饰——尖刺专属危险语义,装饰不得伪装逻辑差异。←原三
|
||||||
- **高墙瓦片体系(2026-09-26 定稿,样张批追认)**:未挖格=**一整块 32×32 自含凸起岩体**(方案甲核心定义);素材语义名——未开格素材=**墙瓦片**,已开格素材=**地面瓦片**(术语 2026-09-26 用户立)。**边界=墨线纪律**:墙瓦片与相邻格分界一律 1px 深墨线、直线贴格边、零锯齿;墨线职责=分隔,不追求与地面融合(Tiny Dungeon 样图实证调研,见 `design/research/terrain-walls-source-evaluation-2026-09-25.md`)。**光照=单一北受光**:北缘=墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘=墨线后直进纹理、不加修饰;南缘=墨线+立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。**凹角**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出(v9.1/v9.2 两版返工实证)。现行定稿=`game/assets/art/board/terrain_*`(16 唯一墙瓦片+5 地面瓦片变体;词表与查表逻辑见 `design/research/board-tile-vocab-plan-a-2026-09-26.md`)。←原三;台账 164
|
- **高墙瓦片体系(2026-09-26 定稿,样张批追认)**:未挖格=**一整块 32×32 自含凸起岩体**(方案甲核心定义);素材语义名——未开格素材=**墙瓦片**,已开格素材=**地面瓦片**(术语 2026-09-26 用户立)。**边界=墨线纪律**:墙瓦片与相邻格分界一律 1px 深墨线、直线贴格边、零锯齿;墨线职责=分隔,不追求与地面融合(Tiny Dungeon 样图实证调研,见 `design/research/terrain-walls-source-evaluation-2026-09-25.md`)。**光照=单一北受光**:北缘=墨线内侧 1px 微亮棱(就地取色提亮,禁宽亮带);西/东缘=墨线后直进纹理、不加修饰;南缘=墨线+立面窄暗条(约 1/3 格高,横层理叠石、禁竖条纹栅栏感)。**凹角**:角点单像素落线;南立面侧凹角加 1px 立面收头——带身与转角竖线同列收尾,禁悬空、禁突出(v9.1/v9.2 两版返工实证)。现行定稿=`game/assets/art/board/terrain_*`(16 唯一墙瓦片+5 地面瓦片变体;词表与查表逻辑见 `design/research/board-tile-vocab-plan-a-2026-09-26.md`)。**高墙感 D 投影档+整板悬崖(样张 v2 定稿 2026-09-28,用户 2026-09-27 四变体挑档)**:南临空墙格=立面口沿高光 2px `Color8(196,172,132)`+双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);北邻墙地面格=三段渐隐投影(顶带 0.22 格边,α0.28 起);整板悬崖=板底/右侧 `DEPTH_PX 8px` 暗面+列分隔竖缝+岩层横纹+口沿高光+接触 AO+底线收边+悬浮影(游戏 `board_world.gd` 同款结构件,样张脚本与游戏渲染常量同源——机检 F7 承载)。←原三;台账 164;样张 v2 批 2026-09-28
|
||||||
- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。种类清单 = 地形变异六类型已定稿(地形变异 GDD;荆棘丛/藤蔓缠绕/岩浆/烬覆/落石/矿尘):**纹带形按轴只分两种**——伤害轴(荆棘丛/岩浆/落石)= 刺/缺排斥形,信息轴(藤蔓缠绕/烬覆/矿尘)= 遮蔽/缠绕形;同轴跨图类型只做色板变体(§4.3 `variant_tint` 槽),不做新纹形。样张批增查:岩浆纹带橙红须与危险红互辨(变异格不是雷,色相邻近混淆风险);信息轴遮蔽呈现不得侵入数字色牌占位区(§1.4 验收 1 延伸)。←原一(纹理位次第 4,让位于数字/小人/旗);原三
|
- **变异格形状通道**(双通道之"形",与色无关):底砖圆角外轮廓不变,砖面内缘绘一圈定宽专属几何纹带(纹带形全作不与他类共用),砖心留白予数字与旗;验收标准=**灰度截图下仍可分类**。种类清单 = 地形变异六类型已定稿(地形变异 GDD;荆棘丛/藤蔓缠绕/岩浆/烬覆/落石/矿尘):**纹带形按轴只分两种**——伤害轴(荆棘丛/岩浆/落石)= 刺/缺排斥形,信息轴(藤蔓缠绕/烬覆/矿尘)= 遮蔽/缠绕形;同轴跨图类型只做色板变体(§4.3 `variant_tint` 槽),不做新纹形。样张批增查:岩浆纹带橙红须与危险红互辨(变异格不是雷,色相邻近混淆风险);信息轴遮蔽呈现不得侵入数字色牌占位区(§1.4 验收 1 延伸)。←原一(纹理位次第 4,让位于数字/小人/旗);原三
|
||||||
|
|
||||||
### 3.3 UI 形状语法(方向指导,细则迁 §7.1)
|
### 3.3 UI 形状语法(方向指导,细则迁 §7.1)
|
||||||
@@ -189,7 +191,7 @@
|
|||||||
- **封印黑 `#000000`**|封印(插旗)宣告色,无色相:玩家的宣告"此格封印"(色史:旗朱 `#C63D22` →封印紫 `#7C3AA8` 2026-09-26→**封印黑 2026-09-28 用户拍板**——紫色与微光蓝族〔魔法微光 256° 等〕色相冲突,黑色无色相天然避开 300°–345° 禁入带与全部微光色带;未开格墙顶明度 ≈113 对纯黑对比 ≥4.4:1,旗杆米白保可读)。旗朱随封印紫批退役,红族自此只剩危险红。←原一;P2;封印紫批 2026-09-26;封印黑批 2026-09-28
|
- **封印黑 `#000000`**|封印(插旗)宣告色,无色相:玩家的宣告"此格封印"(色史:旗朱 `#C63D22` →封印紫 `#7C3AA8` 2026-09-26→**封印黑 2026-09-28 用户拍板**——紫色与微光蓝族〔魔法微光 256° 等〕色相冲突,黑色无色相天然避开 300°–345° 禁入带与全部微光色带;未开格墙顶明度 ≈113 对纯黑对比 ≥4.4:1,旗杆米白保可读)。旗朱随封印紫批退役,红族自此只剩危险红。←原一;P2;封印紫批 2026-09-26;封印黑批 2026-09-28
|
||||||
- **危险红 `#A8321F`**|危险色:雷=被惊动的巢穴,兑现档红,更深更沉(2026-09-13 更名,旧称"机关红"随隐喻换轴退役;色值不变)。←原三;P2
|
- **危险红 `#A8321F`**|危险色:雷=被惊动的巢穴,兑现档红,更深更沉(2026-09-13 更名,旧称"机关红"随隐喻换轴退役;色值不变)。←原三;P2
|
||||||
- **奖励金 `#F0A828`**|已兑现的收益:金币/宝箱/蜡封,全作唯一亮金。←P2;§2.3/2.4
|
- **奖励金 `#F0A828`**|已兑现的收益:金币/宝箱/蜡封,全作唯一亮金。←P2;§2.3/2.4
|
||||||
- **小人蓝 `#2E86DE`**|小人主色:棋盘唯一"己方",与光标准线同族(候选值,样张批 §1.4 冻结终稿)。←P1;§2.1
|
- **小人蓝 `#2E86DE`**|小人主色:棋盘唯一"己方",与光标准线同族(样张 v2 定稿 2026-09-28;游戏 UiKit.HERO_BLUE 已同步 #2E6E8E→#2E86DE)。←P1;§2.1
|
||||||
- **数字色牌 `#F5EAD3`**|数字底具:素色暖纸白直角小牌,数字一律垫于其上;素面无纹理、不随图类型/装饰变体变,数字可读不依赖格底即由本牌承载。←原二
|
- **数字色牌 `#F5EAD3`**|数字底具:素色暖纸白直角小牌,数字一律垫于其上;素面无纹理、不随图类型/装饰变体变,数字可读不依赖格底即由本牌承载。←原二
|
||||||
|
|
||||||
**对比硬指标(可测)**:数字可读性由色牌承载——**数字色牌素色恒定 L≥0.70、一切数字墨 L≤0.027**(WCAG 相对亮度)⇒ 任意牌墨组合 **≥4.5:1 恒成立**(WCAG AA)。实测样例:#33261B / #F5EAD3 = **12.3:1**;#4A1E14 / #F5EAD3 = **11.8:1**;#132E36 / #F5EAD3 = **12.0:1**。格底槽(含全部图类型)相对亮度 **L≥0.30**(明亮为皮)、HSV 饱和 **≤0.55**、避 Boss 禁入色相带——格底不再承担数字对比义务;物件识别不依赖格底(§5.1 轮廓通道),旗面、金色不作识别依赖。←原二;地形变异 AC-9
|
**对比硬指标(可测)**:数字可读性由色牌承载——**数字色牌素色恒定 L≥0.70、一切数字墨 L≤0.027**(WCAG 相对亮度)⇒ 任意牌墨组合 **≥4.5:1 恒成立**(WCAG AA)。实测样例:#33261B / #F5EAD3 = **12.3:1**;#4A1E14 / #F5EAD3 = **11.8:1**;#132E36 / #F5EAD3 = **12.0:1**。格底槽(含全部图类型)相对亮度 **L≥0.30**(明亮为皮)、HSV 饱和 **≤0.55**、避 Boss 禁入色相带——格底不再承担数字对比义务;物件识别不依赖格底(§5.1 轮廓通道),旗面、金色不作识别依赖。←原二;地形变异 AC-9
|
||||||
@@ -198,10 +200,10 @@
|
|||||||
|
|
||||||
- **红只说危险**:危险红=兑现档(旗朱预告档随封印紫批退役,红族单档)。禁:红不得用于奖励、装饰与常规 UI(微光群伤组除外——其为类型先验色带域,见「类型微光色带」条,色相刻意错开语义红)。←原三;P2;§2.1;封印紫批 2026-09-26
|
- **红只说危险**:危险红=兑现档(旗朱预告档随封印紫批退役,红族单档)。禁:红不得用于奖励、装饰与常规 UI(微光群伤组除外——其为类型先验色带域,见「类型微光色带」条,色相刻意错开语义红)。←原三;P2;§2.1;封印紫批 2026-09-26
|
||||||
- **金只说收益与兑现**(奖励、结算、胜利)。禁:金不得作危险障碍色、不上格底、不作日常 UI 强调(防通胀;微光通用增益组的芥末黄 = 类型先验色带域,非收益语义金)。←P2;P3;§2.2–2.4
|
- **金只说收益与兑现**(奖励、结算、胜利)。禁:金不得作危险障碍色、不上格底、不作日常 UI 强调(防通胀;微光通用增益组的芥末黄 = 类型先验色带域,非收益语义金)。←P2;P3;§2.2–2.4
|
||||||
- **蓝只说己方与冷静信息**(小人、光标、工具信息——含可挖域接触带呼吸微光,蓝族浅一档,2026-09-25 拍板;旧可挖域绿相随接触带方案退役)。禁:蓝不得表示敌方危险;暮蓝 `#5A6C8C` 为失败独占(§2.5),只作全屏氛围,不作物件色。←P1;§2.5
|
- **蓝只说己方与冷静信息**(小人、光标、工具信息——含可挖域接触带呼吸微光,蓝族浅一档 `#D6ECFF`〔样张 v2 定稿 2026-09-28,游戏 UiKit.CONTACT_GLOW 同值〕,2026-09-25 拍板;旧可挖域绿相随接触带方案退役)。禁:蓝不得表示敌方危险;暮蓝 `#5A6C8C` 为失败独占(§2.5),只作全屏氛围,不作物件色。←P1;§2.5
|
||||||
- **紫赤只说 Boss 与精英** `#8F2F6A`(色相 323°):郑重来自色相独占,不来自压暗。禁:**HSV 色相 300°–345° 为全场禁入带**,其余资产(含 UI 与粒子、含类型微光)及图类型色板不得落入。←§2.6;原三
|
- **紫赤只说 Boss 与精英** `#8F2F6A`(色相 323°):郑重来自色相独占,不来自压暗。禁:**HSV 色相 300°–345° 为全场禁入带**,其余资产(含 UI 与粒子、含类型微光)及图类型色板不得落入。←§2.6;原三
|
||||||
- **绿只说自然生机**(环境植被;恢复类状态若有则用绿——待增益状态定夺)。**例外:类型微光治疗组**(类型先验色带,见下条)。禁:绿不得承载逻辑状态(微光色带除外),不得与红构成唯一区分对。←原三;T1 裁决 2026-09-22
|
- **绿只说自然生机**(环境植被;恢复类状态若有则用绿——待增益状态定夺)。**例外:类型微光治疗组**(类型先验色带,见下条)。禁:绿不得承载逻辑状态(微光色带除外),不得与红构成唯一区分对。←原三;T1 裁决 2026-09-22
|
||||||
- **类型微光色带 = 格面类型先验专用逻辑色带**(2026-09-22 用户裁决,未开格类型预读的色彩通道):未开格常显类型微光属逻辑信息色彩,五组候选色相——**通用增益组**(attack/defense/weaken/action/gp 五族)芥末黄 `#D6C13A`(≈52°)、**治疗组**(heal)黄绿 `#5CB83A`(≈104°)、**穿刺组**(pierce)青 `#2E9E9E`(≈180°)、**魔法组**(magic)紫罗兰 `#7B5BD6`(≈256°)、**群伤组**(blast)绯红 `#FF2D55`(≈349°,刻意避开危险红色相段与 300°–345° 禁入带)——均候选值,样张批(§1.4)冻结终值并抽测组间互辨。分域纪律:微光色相只承载类型先验,不迁移复用为物件/阵营/UI 语义色;微光 = **色相+明度双码**(2026-09-28 角嵌退役批起明度梯度承载类型——§2 微光明度例外:梯度不得压破格底明度档/三态分类);色弱玩家的类型主通道 = 微光双码。←T1 裁决 2026-09-22;角嵌退役批 2026-09-28;§4.6.3
|
- **类型微光色带 = 格面类型先验专用逻辑色带**(2026-09-22 用户裁决,未开格类型预读的色彩通道):未开格常显类型微光属逻辑信息色彩,五组色相——**通用增益组**(attack/defense/weaken/action/gp 五族)芥末黄 `#D6C13A`(≈52°)、**治疗组**(heal)黄绿 `#5CB83A`(≈104°)、**穿刺组**(pierce)青 `#2E9E9E`(≈180°)、**魔法组**(magic)紫罗兰 `#7B5BD6`(≈256°)、**群伤组**(blast)绯红 `#FF2D55`(≈349°,刻意避开危险红色相段与 300°–345° 禁入带)——**样张 v2 定稿(2026-09-28)**,组间互辨抽检随机检报告;v2 呈现参数=内缘带状泛光 α170/带宽 cell÷8(亮度不压格底明度档,§2 微光例外)。分域纪律:微光色相只承载类型先验,不迁移复用为物件/阵营/UI 语义色;微光 = **色相+明度双码**(2026-09-28 角嵌退役批起明度梯度承载类型——§2 微光明度例外:梯度不得压破格底明度档/三态分类);色弱玩家的类型主通道 = 微光双码。←T1 裁决 2026-09-22;角嵌退役批 2026-09-28;§4.6.3
|
||||||
- **金/红兼作极性色相槽(§4.6)**:极性(白话:同一次兑付的正负方向——正 = 收益侧,负 = 代价侧;行为×格型→符号矩阵见极性提案 R2)的视觉取色只在这两族间——有利兑付 = 金族,有害兑付 = 红族,不新增语义色。←§4.6;原二
|
- **金/红兼作极性色相槽(§4.6)**:极性(白话:同一次兑付的正负方向——正 = 收益侧,负 = 代价侧;行为×格型→符号矩阵见极性提案 R2)的视觉取色只在这两族间——有利兑付 = 金族,有害兑付 = 红族,不新增语义色。←§4.6;原二
|
||||||
|
|
||||||
### 4.3 图类型色板换皮规则(board_type)
|
### 4.3 图类型色板换皮规则(board_type)
|
||||||
@@ -307,7 +309,7 @@
|
|||||||
| action | 行动族 | 行动点/疾步 |
|
| action | 行动族 | 行动点/疾步 |
|
||||||
| gp | 金币族 | 钱币/财货 |
|
| gp | 金币族 | 钱币/财货 |
|
||||||
|
|
||||||
- **呈现约束(2026-09-28 角嵌退役批更新,原「双通道」退役)**:全部未开格呈现**类型微光单通道**(五组色带,§4.2「类型微光色带」,色相+明度双码;候选值样张批冻结);不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**微光误读率 = 提案 §4 指标 3** 保留(角嵌误读率指标随之转微光),样张批微光组间互辨抽测含明度档互辨。
|
- **呈现约束(2026-09-28 角嵌退役批更新,原「双通道」退役)**:全部未开格呈现**类型微光单通道**(五组色带,§4.2「类型微光色带」,色相+明度双码;样张 v2 定稿 2026-09-28);不呈现极性、不呈现雷/安——推雷仍是扫雷本征推理;类型分布即先验(提案 R3 拍板),**微光误读率 = 提案 §4 指标 3** 保留(角嵌误读率指标随之转微光),样张批微光组间互辨抽测含明度档互辨。
|
||||||
- **状态声明**:键集九值封闭(attack/defense/heal/blast/weaken/pierce/magic/action/gp,与 RewardTable 现行 cell_type 词表一致),扩键走本节修订。
|
- **状态声明**:键集九值封闭(attack/defense/heal/blast/weaken/pierce/magic/action/gp,与 RewardTable 现行 cell_type 词表一致),扩键走本节修订。
|
||||||
- 与 §4.6.1 的关系:同语义域的键(attack↔attack_up 等)可共族设计,键分立、纹样须可区分(格面小记号级对槽内图标级)。
|
- 与 §4.6.1 的关系:同语义域的键(attack↔attack_up 等)可共族设计,键分立、纹样须可区分(格面小记号级对槽内图标级)。
|
||||||
|
|
||||||
@@ -424,9 +426,9 @@
|
|||||||
- **交付格式**:PNG 透明底;动画 = 序列帧或引擎精灵表(SpriteFrames),帧尺寸恒定、横向等宽排列(候选)。
|
- **交付格式**:PNG 透明底;动画 = 序列帧或引擎精灵表(SpriteFrames),帧尺寸恒定、横向等宽排列(候选)。
|
||||||
- **命名规范(候选草案)**:`<域>_<对象键>_<状态|变体>[_<序号>].png`,全小写 snake_case;**对象键 = 数据表主键**(effect_id/reward_id/cell_type/enemy_id——图标键契约 §4.6 的文件侧延伸:资产文件名键 = 图标键,全局恒定禁运行时换图);图类型变体后缀 = board_type 键(`_dungeon`/`_underdark`/`_deep_underdark`);候选评审件与终稿分目录(`candidates/` 承载评审件,终稿目录归表现批定)。
|
- **命名规范(候选草案)**:`<域>_<对象键>_<状态|变体>[_<序号>].png`,全小写 snake_case;**对象键 = 数据表主键**(effect_id/reward_id/cell_type/enemy_id——图标键契约 §4.6 的文件侧延伸:资产文件名键 = 图标键,全局恒定禁运行时换图);图类型变体后缀 = board_type 键(`_dungeon`/`_underdark`/`_deep_underdark`);候选评审件与终稿分目录(`candidates/` 承载评审件,终稿目录归表现批定)。
|
||||||
|
|
||||||
### 8.4 帧数规格(候选档,战斗一屏样张批冻结)
|
### 8.4 帧数规格(样张 v2 冻结 2026-09-28)
|
||||||
|
|
||||||
- 待机循环 4 帧(候选);攻击/技能准备 6 帧单次(候选);受击 2 帧(候选);死亡 = 灰化+碎裂退场(§4.6.4 语义硬规则),碎裂序列 4–6 帧(候选);帧时长与结算节拍对齐(预告时序 = 下一拍,§4.6.4)。
|
- **帧数规格(样张 v2 冻结,2026-09-28)**:待机循环 4 帧;攻击/技能准备 6 帧单次;受击 2 帧;死亡 = 灰化+碎裂退场(§4.6.4 语义硬规则)——切片实装=程序化 tween(灰化罩+碎块弹飞 5 块 0.45s,挂 `unit_died`),序列帧归 ✦ 缺料清单;帧时长与结算节拍对齐(预告时序 = 下一拍,§4.6.4)。
|
||||||
- **方向集(2026-09-24 用户拍板)**:棋盘小人=**4 向**(下/上/侧,侧向左右镜像共用一份画稿);斜向移动/挖掘按**主导轴吸附最近正交向**呈现,不出 45° 斜向帧(斜向信息由位移轨迹与目标格预告承载,ITB 实态同款)。镜像与「旗面恒画右」纪律(character-design.md 冒险背包条)的相容归属:棋盘小人默认镜像豁免、旗右纪律限立绘侧——**代拟口径,样张批追认**。
|
- **方向集(2026-09-24 用户拍板)**:棋盘小人=**4 向**(下/上/侧,侧向左右镜像共用一份画稿);斜向移动/挖掘按**主导轴吸附最近正交向**呈现,不出 45° 斜向帧(斜向信息由位移轨迹与目标格预告承载,ITB 实态同款)。镜像与「旗面恒画右」纪律(character-design.md 冒险背包条)的相容归属:棋盘小人默认镜像豁免、旗右纪律限立绘侧——**代拟口径,样张批追认**。
|
||||||
|
|
||||||
### 8.5 结构件完整性与参考核验(2026-09-24 用户立规)
|
### 8.5 结构件完整性与参考核验(2026-09-24 用户立规)
|
||||||
@@ -471,6 +473,7 @@
|
|||||||
- **v2.10(2026-09-26)封印紫批**(用户拍板:插旗色 旗朱 #C63D22 → **封印紫 #7C3AA8**,色相 ≈267°):§4.1 旗朱条改写为封印紫条(候选值样张批冻结;禁入带合规+与魔法微光/妨害紫间距登记)、§4.2 红只说危险改单档(旗朱预告档退役)、§3.2 旗条/§1.4 验收 4/§4.3 不变槽/§3.2 变异互辨/§5.4 速览随批。**语义面**——封印自术语批(2026-09-25)获专属色相,红族收敛为危险红单档;攻击预告/弹道/拒绝等非封印红统一归危险红族(RED_MINE)。**连带**——character-design §5.4 背旗、battle.md 三处、关卡节点地图 Boss 描边、清单封印特效行随批;comfyui-prompts 生图卡颜色词随样张 v2 重生成。
|
- **v2.10(2026-09-26)封印紫批**(用户拍板:插旗色 旗朱 #C63D22 → **封印紫 #7C3AA8**,色相 ≈267°):§4.1 旗朱条改写为封印紫条(候选值样张批冻结;禁入带合规+与魔法微光/妨害紫间距登记)、§4.2 红只说危险改单档(旗朱预告档退役)、§3.2 旗条/§1.4 验收 4/§4.3 不变槽/§3.2 变异互辨/§5.4 速览随批。**语义面**——封印自术语批(2026-09-25)获专属色相,红族收敛为危险红单档;攻击预告/弹道/拒绝等非封印红统一归危险红族(RED_MINE)。**连带**——character-design §5.4 背旗、battle.md 三处、关卡节点地图 Boss 描边、清单封印特效行随批;comfyui-prompts 生图卡颜色词随样张 v2 重生成。
|
||||||
- **v2.11(2026-09-28)封印黑批**(用户拍板:封印色 封印紫 #7C3AA8 → **封印黑 #000000**——紫色与微光蓝族色相冲突,黑色无色相天然合规):§4.1 封印紫条改写封印黑条、§3.2 旗条/§1.4 不变槽/§4.3/§5.4 速览随批;代码 UiKit.SEAL_PURPLE→SEAL_BLACK 改名迁移+board_view/board_fx_view 换色+样张脚本同步;胜利彩带装饰紫位改亮金(紫赤系 Boss 独占不入装饰);远程生图单主角旗 purple→black 十六进制锁色重锁。历史行(v2.10 等)按 ADR 纪律留痕不改
|
- **v2.11(2026-09-28)封印黑批**(用户拍板:封印色 封印紫 #7C3AA8 → **封印黑 #000000**——紫色与微光蓝族色相冲突,黑色无色相天然合规):§4.1 封印紫条改写封印黑条、§3.2 旗条/§1.4 不变槽/§4.3/§5.4 速览随批;代码 UiKit.SEAL_PURPLE→SEAL_BLACK 改名迁移+board_view/board_fx_view 换色+样张脚本同步;胜利彩带装饰紫位改亮金(紫赤系 Boss 独占不入装饰);远程生图单主角旗 purple→black 十六进制锁色重锁。历史行(v2.10 等)按 ADR 纪律留痕不改
|
||||||
- **v2.12(2026-09-28)角嵌退役批**(用户裁「微光的展示只有微光,不要加额外的箭头或者加号在格子里面」——样张 v1 否决原因之一):未开格类型角嵌纹样**彻底退役**——类型预读改类型微光单通道(色相+明度双码),§2 微光明度恒定开例外(明度梯度承载类型,梯度不压格底明度档/三态分类),色弱类型主通道=微光双码;§1.2 优先级栈/§1.4 样张清单与验收 2/§3.2 角嵌条(标退役)/§3.4/§4.2 色带条/§4.5⑨/§4.6.3 呈现约束同步;已开格物件纹样(角嵌于砖缘画法动词)不在退役面;机检新增「微光格零墨色附加像素」断言;角嵌误读率指标转微光误读率 |
|
- **v2.12(2026-09-28)角嵌退役批**(用户裁「微光的展示只有微光,不要加额外的箭头或者加号在格子里面」——样张 v1 否决原因之一):未开格类型角嵌纹样**彻底退役**——类型预读改类型微光单通道(色相+明度双码),§2 微光明度恒定开例外(明度梯度承载类型,梯度不压格底明度档/三态分类),色弱类型主通道=微光双码;§1.2 优先级栈/§1.4 样张清单与验收 2/§3.2 角嵌条(标退役)/§3.4/§4.2 色带条/§4.5⑨/§4.6.3 呈现约束同步;已开格物件纹样(角嵌于砖缘画法动词)不在退役面;机检新增「微光格零墨色附加像素」断言;角嵌误读率指标转微光误读率 |
|
||||||
|
- **v2.13(2026-09-28)样张 v2 冻结批(M-1 迭代 2 T1,机检闸形 A——全过即冻结)**:§1.4 增样张 v2 记录(机检 16/16:现行 13 条+A3 上调 Δ≥55/F6 顶面立面双明度带 Δ≥18/F7 悬崖 DEPTH_PX 同源/A6 观感密度梯度 ≥4.70=v1 实测 4.09×1.15——v1 四否决原因断言化收官);**四冻结落文**——①砖面形态:§3.2 高墙瓦片体系条增「D 投影档+整板悬崖」定稿参数(口沿高光 2px Color8(196,172,132)/立面双段暗带 0.16+0.25 格边 α0.60·0.82/地面投影 0.22 格边三段渐隐 α0.28/悬崖 DEPTH_PX 8px board_world 同款);②色板:§4.1 小人蓝候选→定稿 #2E86DE(UiKit 同步)+§4.2 接触带微光色值 #D6ECFF 定稿+类型微光五组候选→定稿(v2 呈现 α170/带宽 cell÷8);③动画方案:§8.4 帧数规格候选档→冻结(待机 4/攻击 6/受击 2/死亡=灰化+程序化碎块弹飞 5 块 0.45s,序列帧归缺料清单);④数字形制:色牌形制定稿(#F5EAD3 直角小牌+Fusion Pixel+字号 max(18,ceil(格边×0.5))+墨三档)。产物命名带 v2(v1 被覆盖欠账清偿),报告迁 `design/research/`;连带=图例旗色封印黑漏网修正(#C63D22→#000000) |
|
||||||
- **v2.8(2026-09-25)可挖域接触带微光定稿批**(用户两拍板:①画法=接触带·空间侧——光画英雄格周界,正交邻墙=整边、斜向邻墙=角点+两半边;②色相=蓝族浅一档,顺路清偿旧 DIG_EDGE 绿相违 §4.2 绿禁则):§3.2 新增「可挖域接触带微光」条(画法/光只住空间一侧/角点不逐格归因、指认=悬停金框/呼吸与减动效守 §2.1/实现切换归表现批);§2.1 承载元素、§4.2 蓝条随批;battle.md 注记随批。背景=用户世界观口径「未开格=墙、已开格=挖出的空间」,微光=空间的灯光边界。
|
- **v2.8(2026-09-25)可挖域接触带微光定稿批**(用户两拍板:①画法=接触带·空间侧——光画英雄格周界,正交邻墙=整边、斜向邻墙=角点+两半边;②色相=蓝族浅一档,顺路清偿旧 DIG_EDGE 绿相违 §4.2 绿禁则):§3.2 新增「可挖域接触带微光」条(画法/光只住空间一侧/角点不逐格归因、指认=悬停金框/呼吸与减动效守 §2.1/实现切换归表现批);§2.1 承载元素、§4.2 蓝条随批;battle.md 注记随批。背景=用户世界观口径「未开格=墙、已开格=挖出的空间」,微光=空间的灯光边界。
|
||||||
- **v2.7(2026-09-25)封印术语批**(用户拍板:Node2D 实现后「插旗」动作等价为**封印**——玩家对未开格插旗即对该格巢穴实施封印,旗 = 封印印记;机制键与规则全不变):§3.2 新增「旗(封印印记)」条——呈现语义/旗朱与栈位不变/封印特效(施印瞬间+印记常显形态)待表现批/现行占位 = 旗朱「旗」字形;§2.5 失败承载元素措辞随批(插过的旗 = 封印印记)。**登记面**——registry terminology 新增「封印」词条、00-游戏概念 术语层级专名表随收;连带同步:battle.md 呈现注记、art-assets-checklist §4 演出类新增封印特效两件(施印特效+印记常显)、art-status-checklist P3 行、character-design.md §5.4 插旗条注记(主角即插旗者)。**关联面**——可挖域接触带微光方案(用户同日提出:微光画英雄格周界接触面,正交整边/斜向角点+两半边)评估可行,实现归表现批,未入本批。
|
- **v2.7(2026-09-25)封印术语批**(用户拍板:Node2D 实现后「插旗」动作等价为**封印**——玩家对未开格插旗即对该格巢穴实施封印,旗 = 封印印记;机制键与规则全不变):§3.2 新增「旗(封印印记)」条——呈现语义/旗朱与栈位不变/封印特效(施印瞬间+印记常显形态)待表现批/现行占位 = 旗朱「旗」字形;§2.5 失败承载元素措辞随批(插过的旗 = 封印印记)。**登记面**——registry terminology 新增「封印」词条、00-游戏概念 术语层级专名表随收;连带同步:battle.md 呈现注记、art-assets-checklist §4 演出类新增封印特效两件(施印特效+印记常显)、art-status-checklist P3 行、character-design.md §5.4 插旗条注记(主角即插旗者)。**关联面**——可挖域接触带微光方案(用户同日提出:微光画英雄格周界接触面,正交整边/斜向角点+两半边)评估可行,实现归表现批,未入本批。
|
||||||
- **v2.6(2026-09-25)世界观对齐扫描批(甲档回填 + 完美排雷钩子收口 + 引用锚内联;用户三拍板)**:①**§4.6.4 敌种甲档回填数据实态**——bugbear(ward→light)/ ogre_chief(heavy→light)/ drider(heavy→ward),并注记甲档事实源 = `enemy.tres` armor_type(2026-09-18 甲重配批后 L1 全轻甲、L2 除 drider 外全轻甲、L3 duergar/duergar_king 重甲);连带修订 character-design.md §5.5(熊地精轻甲化/食人魔弃金属甲板改裸身皮条/卓尔改魔法甲符纹)与 comfyui-prompts.md v2.8(熊地精/食人魔/卓尔三卡提示词随改)——食人魔视觉重设计经用户拍板「按最新设定,裸身+魔法甲符纹」。②**§2.2 完美排雷钩子收口**——「若…落实…待 GDD 定夺」改现行事实(零误标且至少标中一面旗→追加第二份清图兑付+结算高光行,地图战斗资源 GDD 已定稿)。③**引用锚内联**——§3/§4 尾注 P1–P4 支柱语义内联(概念期 brainstorm 编号,现行 GDD 树不单列),§1.4 两处 game-concept 编号引用改语义内联。扫描背景=用户发起的圣经×关卡/世界观对齐审计;变异六类型/格面九类/状态九键/奖励 19 键/敌种八键/三柜台钥石/三关色板方向经核对全部一致,零其余不符。
|
- **v2.6(2026-09-25)世界观对齐扫描批(甲档回填 + 完美排雷钩子收口 + 引用锚内联;用户三拍板)**:①**§4.6.4 敌种甲档回填数据实态**——bugbear(ward→light)/ ogre_chief(heavy→light)/ drider(heavy→ward),并注记甲档事实源 = `enemy.tres` armor_type(2026-09-18 甲重配批后 L1 全轻甲、L2 除 drider 外全轻甲、L3 duergar/duergar_king 重甲);连带修订 character-design.md §5.5(熊地精轻甲化/食人魔弃金属甲板改裸身皮条/卓尔改魔法甲符纹)与 comfyui-prompts.md v2.8(熊地精/食人魔/卓尔三卡提示词随改)——食人魔视觉重设计经用户拍板「按最新设定,裸身+魔法甲符纹」。②**§2.2 完美排雷钩子收口**——「若…落实…待 GDD 定夺」改现行事实(零误标且至少标中一面旗→追加第二份清图兑付+结算高光行,地图战斗资源 GDD 已定稿)。③**引用锚内联**——§3/§4 尾注 P1–P4 支柱语义内联(概念期 brainstorm 编号,现行 GDD 树不单列),§1.4 两处 game-concept 编号引用改语义内联。扫描背景=用户发起的圣经×关卡/世界观对齐审计;变异六类型/格面九类/状态九键/奖励 19 键/敌种八键/三柜台钥石/三关色板方向经核对全部一致,零其余不符。
|
||||||
|
|||||||
@@ -423,24 +423,3 @@ NodeOverlay 现行实态为模态覆盖层:恢复点全量服务(回复读
|
|||||||
|
|
||||||
### 设置与本地化
|
### 设置与本地化
|
||||||
|
|
||||||
|
|
||||||
#### 战斗画面表现收口(循环 2 输入清单,2026-09-26 登记)
|
|
||||||
|
|
||||||
**现状**:2026-09-26 用户令「需要做的都记录进待办」后盘点登记;能修的已修完(数字色牌/墙轮廓/血量数字/小人脚浮/字形错位/入场演出,台账 176-181),剩余七项——
|
|
||||||
|
|
||||||
- **界面环境与框材组 8 件**:规格与渠道已登记 art-assets-checklist §4.1;♻ 件待用户审阅 ui-reuse 候选包挑批后接线,✦ 背景横幅待样张 v2 冻结后生图
|
|
||||||
- **战斗一屏样张 v2 重出**:v1 已否,重 roll 预算剩 2 轮;**验收闸形已拍(2026-09-27)=A 机检冻结零参与**——用户补否决原因一句话后转成机检断言集(原 12 条+气质代理断言),v2 机检全过即冻结落码、批末用户目检否决才返工;过=v2 冻结为换皮输入。**待输入 = v1 否决原因一句话(用户已认领待补)**
|
|
||||||
- **高墙感挑档落码**:**已拍板(2026-09-27)=D 投影档**(研究件四变体,用户三选一裁决),迭代 1 实装改常量;研究件现路径 artwork/mockups/(目录收敛随迁)
|
|
||||||
- **CJK 字体随包**:**已拍板(2026-09-27)=Fusion Pixel OFL**(用户二选一裁决;CJK 覆盖+授权干净定案),迭代 1 实装入包
|
|
||||||
- **演出密度**:死亡碎裂序列帧/胜利碎砖纹章/失败坐砖掸灰/意图与结算动画终稿——需样张 v2 冻结动画方案
|
|
||||||
- **本关强化列表数据接线**:本清单既有 C 区预留区条目承载,不重登
|
|
||||||
- **draw calls 270→<200**:归 Alpha 验收批,真机测量后优化
|
|
||||||
|
|
||||||
敌人立绘素材=用户外部生成中(八敌种槽位已接线,素材入库即自动显示),不入本条。棋盘数字呈现优化(色牌临时档)另见本清单同日条目。
|
|
||||||
|
|
||||||
**归属**:循环 2 计划书(样张 v2 冻结后另立)
|
|
||||||
#### 棋盘数字呈现优化(色牌=临时档,2026-09-26 登记)
|
|
||||||
|
|
||||||
**现状**:2026-09-26 用户实机反馈——现行数字呈现(米白色牌+墨字,呈现修复批落码)**太丑,只作临时档**,「现在够用」;终稿数字呈现方案待设计(候选方向:色牌形制/字号字体/去牌化直写+描边/与瓦片一体的雕刻数字等),归样张 v2 冻结视觉语言后的表现批一并定。**备忘性质**:防止临时档被当成终稿遗忘。
|
|
||||||
|
|
||||||
**归属**:战斗画面表现收口条(循环 2);判据续用 §1.4 验收 1(可读性硬指标不因形制放松)
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# M-1 出口闸检查单(2026-09-28,迭代 2 收口批交付)
|
||||||
|
|
||||||
|
> 用途:M-1「切片表现收口」出口闸 = **真人目检清单全绿**(机器闸已全部自动过);本单即人闸载体,逐项可勾。
|
||||||
|
> 全绿 = M-1 关闭 → **M-2 迭代 3「读数重测与调参」解锁**(其门=M-1 出口闸)。
|
||||||
|
> 目检方式:实机跑 `godot --path game`(或开 Godot 编辑器 F5),按清单逐项看;任一项不过 = 回该批返工(样张类至多一轮,收集否决维度补断言后重出)。
|
||||||
|
|
||||||
|
## 一、机器闸读数汇总(已自动过,留痕)
|
||||||
|
|
||||||
|
### 迭代 1(m1-iter1-20260928 worktree,2026-09-28 台账 216/217)
|
||||||
|
| 卡 | 内容 | 读数 |
|
||||||
|
|---|---|---|
|
||||||
|
| T1 单元测试与契约层 | 七套件实跑入口 tests/run_all.gd | 全绿 |
|
||||||
|
| T2 高墙感 D 投影档落码 | 立面双段暗带+地面投影(用户挑档 D) | 冒烟 557/0+双探针 PASS |
|
||||||
|
| T3 CJK 字体随包 | Fusion Pixel OFL zh_hans+第八套件 ui_font_asset_test | 冒烟 557/0+8 套件全绿 |
|
||||||
|
|
||||||
|
### 迭代 2(本 loop,f9b0765→2d1388b)
|
||||||
|
| 卡 | 内容 | 读数 |
|
||||||
|
|---|---|---|
|
||||||
|
| T1 样张 v2 重出+机检冻结 | 机检 **16/16 全绿**(13 现行+A3 Δ≥55+F6 双明度带+F7 悬崖同源+A6 观感密度)→ 四冻结落文 art-bible v2.13 | 报告 `design/research/sample-v2-机检报告-2026-09-28.md` |
|
||||||
|
| T2 框材组 8 件换皮 | ♻ 九件量化改色入包+UiKit 单点工厂+五视图接线 | 冒烟 557/0+四探针 PASS |
|
||||||
|
| T3 演出密度终稿 | 死亡碎裂/胜利纹章/失败掸灰/意图三档/结算光带+**fx_probe 新探针** | 冒烟 557/0+五探针 PASS |
|
||||||
|
| T4 棋盘数字终稿 | 三档墨色温/字号 ≥18 公式/色牌 0.60·0.62 冻结值 | 冒烟 557/0+五探针(ui_tune_probe 回归含) |
|
||||||
|
|
||||||
|
- 冒烟检查数 557 = 开工基线(零删减);合并后主区复跑留痕见台账 219。
|
||||||
|
- 迭代 1 未落籍注记:m1-iter1 worktree 尚有 T4 关卡画面实装在飞(main.gd/route_overlay/双探针四文件),本 loop 弱耦合避让未触碰;其落籍不阻塞本人闸(目检第 8 项注两级状态)。
|
||||||
|
|
||||||
|
## 二、真人目检清单(逐项可勾,勾完=人闸过)
|
||||||
|
|
||||||
|
- [ ] **1. 棋盘立体感**(样张 v2 否决③复核):战斗中未开墙格有南立面暗带+地面投影,整板底部/右侧有悬崖暗面与悬浮影——「立得起来」
|
||||||
|
- [ ] **2. 观感密度**(否决①复核):棋盘整体不觉得平/糊——裂纹碎石变体、类型微光、物件纹样层次可感
|
||||||
|
- [ ] **3. 数字可读**:1-8 数字色牌上清晰(三档墨色温:小数字偏冷、大数字偏暖),缩小窗口仍可读
|
||||||
|
- [ ] **4. 换皮框架**:C 区面板/B 区舞台底/图例弹窗=石板质感九宫格;全站按钮=暖砖两态(按下下沉);开始界面有石纹底噪;营地有石框容器+钥石/柜台图标
|
||||||
|
- [ ] **5. 演出四项**:击杀敌人→灰化+碎块弹飞(非瞬间消失);胜利→金蜡封落下+碎砖拼纹章环;失败→暮蓝氛围+尘絮飘落+旗光点收回;敌摇定特殊技→徽标弹跳、重击→缓推蓄力定格
|
||||||
|
- [ ] **6. 结算光带**:轮末结算金光带自棋盘上缘扫向下缘,带前缘有亮线
|
||||||
|
- [ ] **7. 图例正确性**:图例旗条目为封印黑色块、墨字可读
|
||||||
|
- [ ] **8. 迭代 1 在飞件**(若 m1-iter1 已合并):D 投影观感融合/Fusion Pixel 中文渲染/关卡画面实装——若未合并则本项留待其批末目检,不阻塞 M-1
|
||||||
|
|
||||||
|
**判定**:1–7 全过 + 8 注记状态 → 人闸过,M-1 关闭;登记裁决入台账,roadmap 里程碑移动。
|
||||||
|
|
||||||
|
## 三、✦ 缺料清单(只登记不生图——生图执行权在用户)
|
||||||
|
|
||||||
|
| 件 | 渠道 | 规格/提示词指针 | 现状承载 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| B 区背景横幅(战斗舞台背景插画) | ✦ 生图 | art-bible §6.1 明亮地窟/低对比自然后退;清单 §1.2 | 程序化石板皮+底噪(本批 T2) |
|
||||||
|
| 八敌种站桩立绘 9 卡 | ✦ 外部生成中 | comfyui-prompts.md v2.9(攻击段=底稿锁死句) | 槽位在位,入库即显示零代码 |
|
||||||
|
| 主角 normal/hit 成图旗色手修 | ✦ 手修 | 旗朱→封印黑 #000000(封印黑批遗留) | 远程单已重锁十六进制 |
|
||||||
|
| 节点图行进器形象 | ✦/⚙ | 清单 §3.2(与主角同族蓝) | 程序化占位 |
|
||||||
|
| 意图三档/死亡碎裂序列帧 | ✦ 可选 | art-bible §8.4 冻结帧数档 | 程序化 tween 已承载(本批 T3) |
|
||||||
|
| 巢穴纹/封印印记正式纹样 | ✦/⚙ | 清单 §4 封印特效两件+§3.3 | 程序化占位在役 |
|
||||||
|
| 变异纹带六类型终稿 | ⚙ 样张批后可排产 | art-bible §3.2 纹带形按轴两分 | 槽位先行(CellUnit tex_mutation) |
|
||||||
|
| 格面资源徽章 19 枚 | ⚙ 脚本管线 | 清单 §3.1 键契约 | 程序化占位 |
|
||||||
|
|
||||||
|
## 四、代拍板追认清单(本 loop 白名单内已裁,待用户过目)
|
||||||
|
|
||||||
|
1. A6 观感密度口径=明度梯度均值(高频细节能量)——v1 同口径实测 4.09×1.15=4.70;低频双峰由 A3 承载(口径注记见机检报告)。
|
||||||
|
2. F6 双明度带测「南向临地墙格」(立面语义承载位,内墙 center 件无立面属正确)。
|
||||||
|
3. 复用件选型=Kenney 系九宫格/图标+OGA 石纹(全 CC0,game-icons CC-BY 件未采用);量化色带端点=PANEL_BORDER/PANEL_DATA/BRICK/FLOOR/PLATE token。
|
||||||
|
4. 演出参数=死亡碎块 5 块 0.45s/纹章碎砖 10 枚/掸灰 8 絮/蓄力定格 0.40s(§8.4 冻结档)。
|
||||||
|
5. 数字牌尺寸 0.66→0.60(对齐样张冻结值);图例旗条目名回退「旗」(click_probe 基线精确匹配,封印语义移描述列)。
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# 样张 v2 机检报告(2026-09-28,M-1 迭代 2 T1)
|
||||||
|
|
||||||
|
生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6 + 迭代 2 立体/密度扩展。
|
||||||
|
|
||||||
|
## v1 否决原因 → 断言映射
|
||||||
|
|
||||||
|
| 否决原因(2026-09-28 用户) | 断言承载 |
|
||||||
|
|---|---|
|
||||||
|
| ①显示太丑(总纲) | A6 观感密度(整板明度 std ≥50.9=v1 实测 44.31×1.15)+既有装饰变体 ≥2 种 |
|
||||||
|
| ②封印色冲突 | 封印黑 #000000(9b6fcf6 已落,A1a 对表自动覆盖) |
|
||||||
|
| ③立体感不够 | A3 阈值 40→55 + F6 顶面/立面双明度带 Δ≥18 + F7 整板悬崖 DEPTH_PX 同源 |
|
||||||
|
| ④微光格只留微光 | F5c 微光段零 draw_inlay(d1dea82 已落) |
|
||||||
|
|
||||||
|
## 断言明细
|
||||||
|
|
||||||
|
| 断言 | 判据 | 结果 | 明细 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| F1 | 方案甲 8 邻拼装(词表查表,禁整块) | PASS | 错位 0 处;用件 15 种 ['center', 'center_crack', 'center_speckle', 'end_n', 'end_s', 'end_w', 'hrun', 'in_nw', 'in_sw', 'n', 'nw', 's', 'sw', 'vrun', 'w'] |
|
||||||
|
| F2 | 旗仅出现于未开格 | PASS | 旗位 (1, 2) 格态=W |
|
||||||
|
| F3 | 小人脚锚=自底边 25% 线(两态) | PASS | 脚 y=[213.75, 258.75] 期望=[213.75, 258.75] |
|
||||||
|
| F4 | 数字 0 格无字形(0=纯安全格裸地面) | PASS | 0 态格 38 个(示例 (4,4));数字表含 0 键=False |
|
||||||
|
| F5a | 接触带微光⊆英雄格周界(空间侧,不进墙格) | PASS | 微光 bbox=(86, 176, 119, 230) 英雄格=(90, 180, 135, 225) |
|
||||||
|
| F5b | 类型微光禁整格染色(中心 50% 区零染色像素) | PASS | 五格中心区全净 |
|
||||||
|
| F5c | 微光格零附加图形(微光段无角嵌调用——角嵌退役,微光只留微光) | PASS | 微光段无角嵌调用 |
|
||||||
|
| A1a | 色板对表圣经 §4.1 冻结值 | PASS | {'INK': '#33261B', '青墨': '#132E36', '赭墨': '#3C250C', '锈墨': '#4A1E14', 'PLATE': '#F5EAD3', '封印黑': '#000000', '危险红': '#A8321F', '奖励金': '#F0A828'} |
|
||||||
|
| A1b | 禁入带 HSV300°-345° 零命中 | PASS | 命中 0 像素(抽样) |
|
||||||
|
| A2 | 拼缝周界闭合(墙侧 2px 墨线 <70) | PASS | 全部墙地边界墨线在位 |
|
||||||
|
| A3 | 墨×牌 WCAG≥4.5:1 + 图底分离 Δ≥55(立体强化上调——否决③,v1 实测 61.9) | PASS | [('青墨', 12.0), ('赭墨', 12.0), ('锈墨', 11.8)];墙顶 113 vs 地面 51 |
|
||||||
|
| F6 | 墙格顶面/立面双明度带(南向临地墙格 Δ≥18,体积感机器代理) | PASS | 临地墙格 14 个;最小 Δ 76.3;均 Δ 106.3(v1 实测均 71.6) |
|
||||||
|
| F7 | 整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析) | PASS | DEPTH_PX=8.0(已解析);悬崖带均亮 69(<100);扩展 +8px |
|
||||||
|
| A6 | 观感密度:明度梯度均值 ≥4.70(v1 实测 4.09 上调 15%——高频细节能量) | PASS | 梯度均值 5.20(低频双峰由 A3 承载) |
|
||||||
|
| A5 | 画布四缘 2px 净空(零溢出) | PASS | 缘带内容像素 0 |
|
||||||
|
| A4 | 混排相融(游戏在库资产+UiKit 语义色) | PASS | 小人源 32×32(裁透明留白后 (19, 20))/立绘 48×48/状态图标 32×32 原件;INK/旗朱/奖励金 == ui_kit.gd |
|
||||||
|
|
||||||
|
结论:**全绿**(16/16 PASS)。机检全过 = 四冻结(砖面形态/色板/动画方案/数字形制)生效落码;气质终审 = 批末用户目检(闸形 A,2026-09-27 拍板)。
|
||||||
@@ -103,6 +103,16 @@ static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Di
|
|||||||
# 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用))
|
# 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用))
|
||||||
static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||||
var overlay := overlay_bg(host, UiKit.BG_OVERLAY)
|
var overlay := overlay_bg(host, UiKit.BG_OVERLAY)
|
||||||
|
# 石纹底噪底层(样张 v2 冻结批 T2——z 序=dim 之上/内容之下;缺位不挂即回退平色)
|
||||||
|
var bd_tex := UiKit.load_tex(UiKit.TEX_BACKDROP_STONE)
|
||||||
|
if bd_tex != null:
|
||||||
|
var bd := TextureRect.new()
|
||||||
|
bd.texture = bd_tex
|
||||||
|
bd.stretch_mode = TextureRect.STRETCH_TILE
|
||||||
|
bd.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
bd.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
bd.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
overlay.add_child(bd)
|
||||||
var box := overlay_box(overlay)
|
var box := overlay_box(overlay)
|
||||||
var title := UiKit.label("Dungeon Sweeper", 34, true)
|
var title := UiKit.label("Dungeon Sweeper", 34, true)
|
||||||
title.add_theme_color_override("font_color", UiKit.WARM)
|
title.add_theme_color_override("font_color", UiKit.WARM)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ func open_victory(verdict_line: String, continue_label: String, board_rect: Rect
|
|||||||
_board_rect = board_rect
|
_board_rect = board_rect
|
||||||
visible = true
|
visible = true
|
||||||
_play_seal_fall()
|
_play_seal_fall()
|
||||||
|
_play_crest_assemble() # 碎砖拼纹章(§2.4——样张 v2 冻结批 T3 终稿)
|
||||||
_play_flag_recall(0) # 胜利不放旗收回
|
_play_flag_recall(0) # 胜利不放旗收回
|
||||||
|
|
||||||
|
|
||||||
@@ -85,12 +86,14 @@ func open_defeat(verdict_line: String, continue_label: String, board_rect: Rect2
|
|||||||
_board_rect = board_rect
|
_board_rect = board_rect
|
||||||
visible = true
|
visible = true
|
||||||
_seal.visible = false
|
_seal.visible = false
|
||||||
|
_play_dust_sadle() # 坐砖掸灰(§2.5——终稿)
|
||||||
_play_flag_recall(8)
|
_play_flag_recall(8)
|
||||||
|
|
||||||
|
|
||||||
func close() -> void:
|
func close() -> void:
|
||||||
visible = false
|
visible = false
|
||||||
_seal.visible = false
|
_seal.visible = false
|
||||||
|
_clear_crest_bricks()
|
||||||
|
|
||||||
|
|
||||||
func is_open() -> bool:
|
func is_open() -> bool:
|
||||||
@@ -115,6 +118,7 @@ func _play_seal_fall() -> void:
|
|||||||
|
|
||||||
# 旗化光点收回(失败——插过的旗逐面化光点收回占位,§2.5)
|
# 旗化光点收回(失败——插过的旗逐面化光点收回占位,§2.5)
|
||||||
func _play_flag_recall(count: int) -> void:
|
func _play_flag_recall(count: int) -> void:
|
||||||
|
_fx_flag_dots = count
|
||||||
for i in count:
|
for i in count:
|
||||||
var dot := ColorRect.new()
|
var dot := ColorRect.new()
|
||||||
dot.color = COL_GOLD
|
dot.color = COL_GOLD
|
||||||
@@ -129,3 +133,62 @@ func _play_flag_recall(count: int) -> void:
|
|||||||
tw.tween_property(dot, "position:y", dot.position.y - 120.0, UiKit.motion(0.8))
|
tw.tween_property(dot, "position:y", dot.position.y - 120.0, UiKit.motion(0.8))
|
||||||
tw.parallel().tween_property(dot, "modulate:a", 0.0, UiKit.motion(0.8))
|
tw.parallel().tween_property(dot, "modulate:a", 0.0, UiKit.motion(0.8))
|
||||||
tw.tween_callback(dot.queue_free)
|
tw.tween_callback(dot.queue_free)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,确定性散布,零新帧资产) ----
|
||||||
|
|
||||||
|
var _crest_bricks: Array = [] # 纹章碎砖驻留面(close 时回收)
|
||||||
|
var _fx_crest_shards := 0
|
||||||
|
var _fx_dust_motes := 0
|
||||||
|
var _fx_flag_dots := 0
|
||||||
|
|
||||||
|
|
||||||
|
# 碎砖拼纹章(胜利 §2.4——蜡封落定后十枚碎砖自四周向蜡封外环收拢拼成纹章环,驻留至 close)
|
||||||
|
func _play_crest_assemble() -> void:
|
||||||
|
_clear_crest_bricks()
|
||||||
|
_fx_crest_shards = 10
|
||||||
|
var c := _board_rect.get_center() if _board_rect.size != Vector2.ZERO else get_viewport_rect().size * 0.5
|
||||||
|
for i in 10:
|
||||||
|
var brick := ColorRect.new()
|
||||||
|
brick.color = UiKit.BRICK if i % 2 == 0 else UiKit.FLOOR # 碎砖=砖面族色(掩尘/裸土——图底同源)
|
||||||
|
brick.size = Vector2(12, 7)
|
||||||
|
brick.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
var ang := TAU * float(i) / 10.0
|
||||||
|
var ring := c + Vector2(cos(ang), sin(ang)) * 64.0
|
||||||
|
brick.position = c + Vector2(cos(ang), sin(ang)) * 200.0 # 自四周场外入场
|
||||||
|
add_child(brick)
|
||||||
|
_crest_bricks.append(brick)
|
||||||
|
var btw := brick.create_tween()
|
||||||
|
btw.tween_interval(UiKit.motion(0.52)) # 待蜡封落定(seal 0.42+0.22 弹跳中段)
|
||||||
|
btw.tween_property(brick, "position", ring - brick.size * 0.5, UiKit.motion(0.4)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
|
btw.parallel().tween_property(brick, "rotation", sin(ang) * 0.35, UiKit.motion(0.4))
|
||||||
|
|
||||||
|
|
||||||
|
# 坐砖掸灰(失败 §2.5——暖灰尘絮自棋盘中带缓缓飘落,最静一拍的运动承载)
|
||||||
|
func _play_dust_sadle() -> void:
|
||||||
|
_fx_dust_motes = 8
|
||||||
|
var base := _board_rect.get_center() if _board_rect.size != Vector2.ZERO else get_viewport_rect().size * 0.5
|
||||||
|
for i in 8:
|
||||||
|
var mote := ColorRect.new()
|
||||||
|
mote.color = Color(UiKit.WARM_FAINT, 0.85)
|
||||||
|
mote.size = Vector2(4, 4)
|
||||||
|
mote.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
mote.position = base + Vector2((float(i % 4) - 1.5) * 30.0, -24.0 - 8.0 * float(i / 4))
|
||||||
|
add_child(mote)
|
||||||
|
var mtw := mote.create_tween()
|
||||||
|
mtw.tween_interval(UiKit.motion(0.08 * i))
|
||||||
|
mtw.tween_property(mote, "position:y", mote.position.y + 64.0, UiKit.motion(0.9))
|
||||||
|
mtw.parallel().tween_property(mote, "modulate:a", 0.0, UiKit.motion(0.9))
|
||||||
|
mtw.tween_callback(mote.queue_free)
|
||||||
|
|
||||||
|
|
||||||
|
func _clear_crest_bricks() -> void:
|
||||||
|
for b in _crest_bricks:
|
||||||
|
if is_instance_valid(b):
|
||||||
|
b.queue_free()
|
||||||
|
_crest_bricks.clear()
|
||||||
|
|
||||||
|
|
||||||
|
# 演出探针出口(fx_probe——胜利纹章/失败掸灰/旗光收回三断言面)
|
||||||
|
func fx_probe() -> Dictionary:
|
||||||
|
return {"crest_shards": _fx_crest_shards, "dust_motes": _fx_dust_motes, "flag_dots": _fx_flag_dots}
|
||||||
@@ -41,9 +41,10 @@ var world: Node2D # StageWorld(preload 脚本实例——Node2D 世
|
|||||||
|
|
||||||
func _init(db_ref) -> void:
|
func _init(db_ref) -> void:
|
||||||
custom_minimum_size = Vector2(0, 252)
|
custom_minimum_size = Vector2(0, 252)
|
||||||
# 面板底(PanelContainer 面板样式等价平移——Control 壳不自绘 stylebox,Panel 子面承载)
|
# 面板底(PanelContainer 面板样式等价平移——Control 壳不自绘 stylebox,Panel 子面承载;
|
||||||
|
# 石板皮 = 样张 v2 冻结批 T2——缺位回退 PANEL_DATA flat,渲染观感不变)
|
||||||
var bg := Panel.new()
|
var bg := Panel.new()
|
||||||
var sb := UiKit.panel_flat(UiKit.PANEL_STAGE, UiKit.PANEL_BORDER, 0, 1, Vector4(6, 4, 6, 4))
|
var sb := UiKit.panel_skinned(Vector4(6, 4, 6, 4), Color(0.956, 0.952, 0.945)) # tint=PANEL_STAGE 对 PANEL_DATA 比值
|
||||||
bg.add_theme_stylebox_override("panel", sb)
|
bg.add_theme_stylebox_override("panel", sb)
|
||||||
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|||||||
@@ -167,19 +167,19 @@ func _draw() -> void:
|
|||||||
draw_texture_rect(tex_mutation, rect, false)
|
draw_texture_rect(tex_mutation, rect, false)
|
||||||
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
|
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
|
||||||
if glyph != "":
|
if glyph != "":
|
||||||
# 数字色牌(原则二:数字一律垫素色直角牌——对比由牌墨承载,可读不依赖格底;暗地面瓦片批的
|
# 数字色牌(原则二:数字一律垫素色直角牌——对比由牌墨承载,可读不依赖格底。
|
||||||
# 可读性修复 2026-09-26。牌尺寸 0.66/0.68 格边居中——字形 ≥0.5 格边在牌内留白 ≥1px;
|
# 牌尺寸 0.60/0.62 格边居中 = 样张 v2 冻结值(2026-09-28 四冻结·数字形制;字形 0.5 格边
|
||||||
# 旗=未开格不垫牌(旗朱在墙瓦片上已可读))
|
# 在牌内留白 ≥2px);旗=未开格不垫牌(封印黑在墙瓦片上已可读))
|
||||||
if plate:
|
if plate:
|
||||||
var pw := edge * 0.66
|
var pw := edge * 0.60
|
||||||
var ph := edge * 0.68
|
var ph := edge * 0.62
|
||||||
var pr := Rect2((edge - pw) * 0.5, (edge - ph) * 0.5, pw, ph)
|
var pr := Rect2((edge - pw) * 0.5, (edge - ph) * 0.5, pw, ph)
|
||||||
draw_rect(pr, UiKit.DIGIT_PLATE, true)
|
draw_rect(pr, UiKit.DIGIT_PLATE, true)
|
||||||
draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0)
|
draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0)
|
||||||
if tex_glyph != null:
|
if tex_glyph != null:
|
||||||
draw_texture_rect(tex_glyph, rect, false)
|
draw_texture_rect(tex_glyph, rect, false)
|
||||||
else:
|
else:
|
||||||
_draw_glyph(glyph, glyph_color, maxi(14, int(edge * 0.5)))
|
_draw_glyph(glyph, glyph_color, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式)
|
||||||
if badge_glyph != "":
|
if badge_glyph != "":
|
||||||
if tex_badge != null:
|
if tex_badge != null:
|
||||||
draw_texture_rect(tex_badge, rect, false)
|
draw_texture_rect(tex_badge, rect, false)
|
||||||
|
|||||||
@@ -626,6 +626,16 @@ func _badge_glyph(category: String) -> String:
|
|||||||
|
|
||||||
# ---- 格态渲染(灰度截图可辨:完整覆尘 / 碎裂裸土 / 已开数字) ----
|
# ---- 格态渲染(灰度截图可辨:完整覆尘 / 碎裂裸土 / 已开数字) ----
|
||||||
|
|
||||||
|
# 数字墨三档色温(样张 v2 冻结批 T4——art-bible §4.1:1-3 青墨/4-5 赭墨/6-8 锈墨;
|
||||||
|
# 冷=稀疏、暖=密集的氛围档,字形为唯一数值载体——禁八色分色)
|
||||||
|
func _digit_ink(d: int) -> Color:
|
||||||
|
if d <= 3:
|
||||||
|
return UiKit.INK_COLD
|
||||||
|
if d <= 5:
|
||||||
|
return UiKit.INK_OCHRE
|
||||||
|
return UiKit.INK_RUST
|
||||||
|
|
||||||
|
|
||||||
func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
||||||
var u: Node2D = _cells.get(c)
|
var u: Node2D = _cells.get(c)
|
||||||
if u == null:
|
if u == null:
|
||||||
@@ -650,6 +660,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
|
|||||||
glyph = "?" # 数字遮蔽(地形变异 hide_number——信息型:该格真实数字不显示)
|
glyph = "?" # 数字遮蔽(地形变异 hide_number——信息型:该格真实数字不显示)
|
||||||
else:
|
else:
|
||||||
glyph = str(d)
|
glyph = str(d)
|
||||||
|
digit_col = _digit_ink(d) # 数字墨三档色温(样张 v2 冻结批 T4——§4.1)
|
||||||
else:
|
else:
|
||||||
if flag:
|
if flag:
|
||||||
bg = COL_BRICK
|
bg = COL_BRICK
|
||||||
|
|||||||
@@ -34,30 +34,39 @@ func _init() -> void:
|
|||||||
var center := CenterContainer.new()
|
var center := CenterContainer.new()
|
||||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
dim.add_child(center)
|
dim.add_child(center)
|
||||||
|
var frame := PanelContainer.new()
|
||||||
|
frame.add_theme_stylebox_override("panel", UiKit.frame_skinned(Vector4(26, 22, 26, 22))) # 仪式石框(T2;缺位回退暗面板)
|
||||||
|
center.add_child(frame)
|
||||||
var box := VBoxContainer.new()
|
var box := VBoxContainer.new()
|
||||||
box.add_theme_constant_override("separation", 6)
|
box.add_theme_constant_override("separation", 6)
|
||||||
center.add_child(box)
|
frame.add_child(box)
|
||||||
var title := UiKit.label("营地 · 永久强化", 24, true)
|
var title := UiKit.label("营地 · 永久强化", 24, true)
|
||||||
title.add_theme_color_override("font_color", UiKit.WARM)
|
title.add_theme_color_override("font_color", UiKit.WARM)
|
||||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
box.add_child(title)
|
box.add_child(title)
|
||||||
|
var bal_row := HBoxContainer.new()
|
||||||
|
bal_row.add_theme_constant_override("separation", 6)
|
||||||
|
bal_row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||||
|
var keystone_icon := UiKit.icon_rect(UiKit.TEX_ICON_KEYSTONE, 18) # 缺位 null → 纯文本回退
|
||||||
|
if keystone_icon != null:
|
||||||
|
bal_row.add_child(keystone_icon)
|
||||||
_balance_label = UiKit.label("钥石 0", 18, true)
|
_balance_label = UiKit.label("钥石 0", 18, true)
|
||||||
_balance_label.add_theme_color_override("font_color", UiKit.GOLD)
|
_balance_label.add_theme_color_override("font_color", UiKit.GOLD)
|
||||||
_balance_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
bal_row.add_child(_balance_label)
|
||||||
box.add_child(_balance_label)
|
box.add_child(bal_row)
|
||||||
box.add_child(HSeparator.new())
|
box.add_child(HSeparator.new())
|
||||||
|
|
||||||
box.add_child(_counter_header("属性锻造(钥石直购 · 每级 flat 整数 · 价格几何递增)"))
|
box.add_child(_counter_header("属性锻造(钥石直购 · 每级 flat 整数 · 价格几何递增)", UiKit.TEX_ICON_FORGE))
|
||||||
_forge_holder = VBoxContainer.new()
|
_forge_holder = VBoxContainer.new()
|
||||||
_forge_holder.add_theme_constant_override("separation", 4)
|
_forge_holder.add_theme_constant_override("separation", 4)
|
||||||
box.add_child(_forge_holder)
|
box.add_child(_forge_holder)
|
||||||
|
|
||||||
box.add_child(_counter_header("道具工坊(累计通关次数解锁 · 局内效果排 Alpha 魔法道具重设计批)"))
|
box.add_child(_counter_header("道具工坊(累计通关次数解锁 · 局内效果排 Alpha 魔法道具重设计批)", UiKit.TEX_ICON_WORKSHOP))
|
||||||
_workshop_holder = VBoxContainer.new()
|
_workshop_holder = VBoxContainer.new()
|
||||||
_workshop_holder.add_theme_constant_override("separation", 4)
|
_workshop_holder.add_theme_constant_override("separation", 4)
|
||||||
box.add_child(_workshop_holder)
|
box.add_child(_workshop_holder)
|
||||||
|
|
||||||
box.add_child(_counter_header("奖励格图鉴"))
|
box.add_child(_counter_header("奖励格图鉴", UiKit.TEX_ICON_ATLAS))
|
||||||
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)", 13, false)
|
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)", 13, false)
|
||||||
lock_l.add_theme_color_override("font_color", UiKit.WARM_FAINT)
|
lock_l.add_theme_color_override("font_color", UiKit.WARM_FAINT)
|
||||||
box.add_child(lock_l)
|
box.add_child(lock_l)
|
||||||
@@ -80,10 +89,17 @@ func _init() -> void:
|
|||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
|
|
||||||
func _counter_header(text: String) -> Label:
|
func _counter_header(text: String, icon_path := "") -> Control:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.add_theme_constant_override("separation", 6)
|
||||||
|
if icon_path != "":
|
||||||
|
var ic := UiKit.icon_rect(icon_path, 16) # 缺位 null → 纯文本回退
|
||||||
|
if ic != null:
|
||||||
|
row.add_child(ic)
|
||||||
var l := UiKit.label(text, 15, true)
|
var l := UiKit.label(text, 15, true)
|
||||||
l.add_theme_color_override("font_color", UiKit.WARM_SOFT)
|
l.add_theme_color_override("font_color", UiKit.WARM_SOFT)
|
||||||
return l
|
row.add_child(l)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡)----
|
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡)----
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ var _bonus_list: VBoxContainer
|
|||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
custom_minimum_size = Vector2(240, 0)
|
custom_minimum_size = Vector2(240, 0)
|
||||||
clip_contents = true
|
clip_contents = true
|
||||||
var sb := UiKit.panel_flat(UiKit.PANEL_HERO, UiKit.PANEL_BORDER, 0, 1, Vector4(8, 6, 8, 6))
|
# 主面板穿石板戏服(样张 v2 冻结批 T2——纹理本色≈PANEL_HERO;数据框仍素面 §7.1;缺位回退 flat)
|
||||||
|
var sb := UiKit.panel_skinned(Vector4(8, 6, 8, 6))
|
||||||
add_theme_stylebox_override("panel", sb)
|
add_theme_stylebox_override("panel", sb)
|
||||||
var col := VBoxContainer.new()
|
var col := VBoxContainer.new()
|
||||||
col.add_theme_constant_override("separation", 14) # 布局充实(2026-09-26 反馈③:区段间距放大)
|
col.add_theme_constant_override("separation", 14) # 布局充实(2026-09-26 反馈③:区段间距放大)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ signal closed
|
|||||||
const ENTRIES := [
|
const ENTRIES := [
|
||||||
["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"],
|
["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"],
|
||||||
["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"],
|
["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"],
|
||||||
["#C63D22", "旗", "一次性承诺(不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"],
|
["#000000", "旗", "封印印记(呈现名·不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"],
|
||||||
["#2E6E8E", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"],
|
["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"],
|
||||||
["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、数值之上,不进数字区)"],
|
["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、数值之上,不进数字区)"],
|
||||||
["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"],
|
["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"],
|
||||||
]
|
]
|
||||||
@@ -33,12 +33,15 @@ func _init() -> void:
|
|||||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
dim.add_child(center)
|
dim.add_child(center)
|
||||||
_panel = PanelContainer.new()
|
_panel = PanelContainer.new()
|
||||||
|
_panel.add_theme_stylebox_override("panel", UiKit.panel_skinned(Vector4(12, 10, 12, 10))) # 石板皮(T2;缺位回退 flat)
|
||||||
center.add_child(_panel)
|
center.add_child(_panel)
|
||||||
var box := VBoxContainer.new()
|
var box := VBoxContainer.new()
|
||||||
box.custom_minimum_size = Vector2(520, 0)
|
box.custom_minimum_size = Vector2(520, 0)
|
||||||
box.add_theme_constant_override("separation", 6)
|
box.add_theme_constant_override("separation", 6)
|
||||||
_panel.add_child(box)
|
_panel.add_child(box)
|
||||||
box.add_child(UiKit.label("图例 · 格态", 20, true))
|
var title := UiKit.label("图例 · 格态", 20, true)
|
||||||
|
title.add_theme_color_override("font_color", UiKit.INK_TITLE) # 亮底墨字(§7.2 数据素面)
|
||||||
|
box.add_child(title)
|
||||||
box.add_child(HSeparator.new())
|
box.add_child(HSeparator.new())
|
||||||
for e in ENTRIES:
|
for e in ENTRIES:
|
||||||
var row := HBoxContainer.new()
|
var row := HBoxContainer.new()
|
||||||
@@ -48,6 +51,7 @@ func _init() -> void:
|
|||||||
chip.color = Color(e[0])
|
chip.color = Color(e[0])
|
||||||
row.add_child(chip)
|
row.add_child(chip)
|
||||||
var lab := UiKit.label("%s —— %s" % [e[1], e[2]], 13, false)
|
var lab := UiKit.label("%s —— %s" % [e[1], e[2]], 13, false)
|
||||||
|
lab.add_theme_color_override("font_color", UiKit.INK_LABEL) # 亮底墨字(§7.2 数据素面)
|
||||||
lab.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
lab.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
lab.custom_minimum_size = Vector2(440, 0)
|
lab.custom_minimum_size = Vector2(440, 0)
|
||||||
row.add_child(lab)
|
row.add_child(lab)
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ func _init() -> void:
|
|||||||
|
|
||||||
|
|
||||||
# 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)——
|
# 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)——
|
||||||
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)
|
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)。
|
||||||
|
# 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2)
|
||||||
func play_round_sweep(board_rect: Rect2) -> void:
|
func play_round_sweep(board_rect: Rect2) -> void:
|
||||||
if board_rect.size == Vector2.ZERO:
|
if board_rect.size == Vector2.ZERO:
|
||||||
return
|
return
|
||||||
@@ -27,10 +28,20 @@ func play_round_sweep(board_rect: Rect2) -> void:
|
|||||||
band.position = Vector2(board_rect.position.x, board_rect.position.y)
|
band.position = Vector2(board_rect.position.x, board_rect.position.y)
|
||||||
band.size = Vector2(board_rect.size.x, 34.0)
|
band.size = Vector2(board_rect.size.x, 34.0)
|
||||||
add_child(band)
|
add_child(band)
|
||||||
|
var edge := ColorRect.new()
|
||||||
|
edge.color = Color(UiKit.GOLD_LIT, 0.7)
|
||||||
|
edge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
edge.size = Vector2(board_rect.size.x, 2.0)
|
||||||
|
edge.position = band.position
|
||||||
|
add_child(edge)
|
||||||
var tw := band.create_tween()
|
var tw := band.create_tween()
|
||||||
tw.tween_property(band, "position:y", board_rect.end.y - 34.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE)
|
tw.tween_property(band, "position:y", board_rect.end.y - 34.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE)
|
||||||
tw.parallel().tween_property(band, "modulate:a", 0.0, UiKit.motion(0.5)).set_delay(UiKit.motion(0.15))
|
tw.parallel().tween_property(band, "modulate:a", 0.0, UiKit.motion(0.5)).set_delay(UiKit.motion(0.15))
|
||||||
tw.tween_callback(band.queue_free)
|
tw.tween_callback(band.queue_free)
|
||||||
|
var etw := edge.create_tween()
|
||||||
|
etw.tween_property(edge, "position:y", board_rect.end.y - 2.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE)
|
||||||
|
etw.parallel().tween_property(edge, "modulate:a", 0.0, UiKit.motion(0.2)).set_delay(UiKit.motion(0.3))
|
||||||
|
etw.tween_callback(edge.queue_free)
|
||||||
|
|
||||||
|
|
||||||
# 跨区弹道:glyph 字形自 from 飞向 to(0.35s + 轻微上抛弧线 + 终点淡出,自动回收)——
|
# 跨区弹道:glyph 字形自 from 飞向 to(0.35s + 轻微上抛弧线 + 终点淡出,自动回收)——
|
||||||
|
|||||||
@@ -183,13 +183,22 @@ func set_pending(ptype: String, pid: String) -> void:
|
|||||||
_play_intent_motion()
|
_play_intent_motion()
|
||||||
|
|
||||||
|
|
||||||
# 三档动作动画占位:摇定时一次性短动画(意图图标弹入 + 立绘前倾回正),随后静帧保持
|
# 意图三档动作动画终稿(样张 v2 冻结批 T3——§4.6.4 三档:①普通攻击=零表示〔set_pending 早退〕
|
||||||
|
# ②特殊准备=徽标弹入+二次弹跳(高举语义,运动手段 §2)③重击/硬化=缓推蓄力+定格微顿+回落;
|
||||||
|
# 三档运动签名互异,灰度/剪影层可辨)。摇定时一次性短动画,随后静帧保持。
|
||||||
func _play_intent_motion() -> void:
|
func _play_intent_motion() -> void:
|
||||||
var tw := create_tween()
|
var tw := create_tween()
|
||||||
tw.tween_property(self, "_intent_scale", 1.6, UiKit.motion(0.10))
|
tw.tween_property(self, "_intent_scale", 1.6, UiKit.motion(0.10))
|
||||||
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.16))
|
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.16))
|
||||||
tw.parallel().tween_property(self, "_glyph_rot", 4.0, UiKit.motion(0.10))
|
tw.parallel().tween_property(self, "_glyph_rot", 4.0, UiKit.motion(0.10))
|
||||||
tw.tween_property(self, "_glyph_rot", 0.0, UiKit.motion(0.14))
|
tw.tween_property(self, "_glyph_rot", 0.0, UiKit.motion(0.14))
|
||||||
|
if _intent_col == UiKit.GOLD_DEEP:
|
||||||
|
tw.tween_property(self, "_intent_scale", 1.22, UiKit.motion(0.28)).set_trans(Tween.TRANS_SINE) # ③蓄力缓推
|
||||||
|
tw.tween_interval(UiKit.motion(0.40)) # 定格微顿
|
||||||
|
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.18))
|
||||||
|
else:
|
||||||
|
tw.tween_property(self, "_intent_scale", 1.26, UiKit.motion(0.11)) # ②二次弹跳(高举)
|
||||||
|
tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.13))
|
||||||
|
|
||||||
|
|
||||||
# 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
# 资产挂点(战斗表现样张批):tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
||||||
@@ -234,6 +243,7 @@ func mark_dead() -> void:
|
|||||||
tw.tween_property(self, "rotation_degrees", 2.5, UiKit.motion(0.12))
|
tw.tween_property(self, "rotation_degrees", 2.5, UiKit.motion(0.12))
|
||||||
tw.tween_property(self, "rotation_degrees", -1.5, UiKit.motion(0.1))
|
tw.tween_property(self, "rotation_degrees", -1.5, UiKit.motion(0.1))
|
||||||
tw.tween_property(self, "rotation_degrees", 0.0, UiKit.motion(0.08))
|
tw.tween_property(self, "rotation_degrees", 0.0, UiKit.motion(0.08))
|
||||||
|
_spawn_death_shards() # 碎裂退场(§4.6.4 语义硬规则:灰化+碎裂禁瞬间消失;§8.4 v2 冻结 5 块 0.45s)
|
||||||
_refresh_portrait_glyph()
|
_refresh_portrait_glyph()
|
||||||
_intent_visible = false # 死亡残位意图退位(旧同款)
|
_intent_visible = false # 死亡残位意图退位(旧同款)
|
||||||
_set_hp(0)
|
_set_hp(0)
|
||||||
@@ -241,6 +251,38 @@ func mark_dead() -> void:
|
|||||||
slot_row.visible = false # 状态行随 mark_dead 同拍隐藏(批内代拍板②待追认)
|
slot_row.visible = false # 状态行随 mark_dead 同拍隐藏(批内代拍板②待追认)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- 演出密度终稿(样张 v2 冻结批 T3——程序化 tween,零新帧资产) ----
|
||||||
|
|
||||||
|
const DEATH_SHARD_N := 5 # 碎块数(§8.4 冻结值)
|
||||||
|
var _death_shards := 0 # 探针出口计数(碎块节点回收后仍保留读数)
|
||||||
|
|
||||||
|
|
||||||
|
# 死亡碎块弹飞:立绘盒中心五块碎石抛物线散落(程序化色块——确定性散布,禁全局随机)
|
||||||
|
func _spawn_death_shards() -> void:
|
||||||
|
_death_shards = DEATH_SHARD_N
|
||||||
|
var center := _portrait_rect().get_center()
|
||||||
|
for i in DEATH_SHARD_N:
|
||||||
|
var shard := ColorRect.new()
|
||||||
|
shard.color = UiKit.STONE if i % 2 == 0 else UiKit.STONE.darkened(0.2)
|
||||||
|
shard.size = Vector2(7, 5) + Vector2(2, 0) * float(i % 2)
|
||||||
|
shard.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
shard.position = center + Vector2(float(i % 3) - 1.0, float(i / 3) - 0.5) * 6.0
|
||||||
|
shard.rotation = float(i) * 0.7
|
||||||
|
add_child(shard)
|
||||||
|
var dir := Vector2((float(i % 3) - 1.0) * 30.0, -36.0 - 6.0 * float(i / 3))
|
||||||
|
var peak := shard.position + dir
|
||||||
|
var stw := shard.create_tween()
|
||||||
|
stw.tween_property(shard, "position", peak, UiKit.motion(0.18)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
||||||
|
stw.tween_property(shard, "position", peak + Vector2(dir.x * 0.4, 52.0), UiKit.motion(0.27)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
||||||
|
stw.parallel().tween_property(shard, "modulate:a", 0.0, UiKit.motion(0.18)).set_delay(UiKit.motion(0.27))
|
||||||
|
stw.tween_callback(shard.queue_free)
|
||||||
|
|
||||||
|
|
||||||
|
# 演出探针出口(fx_probe——死亡碎裂断言面)
|
||||||
|
func fx_probe() -> Dictionary:
|
||||||
|
return {"death_shards": _death_shards}
|
||||||
|
|
||||||
|
|
||||||
func is_dead() -> bool:
|
func is_dead() -> bool:
|
||||||
return _dead
|
return _dead
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ extends RefCounted
|
|||||||
|
|
||||||
# ── 色板(语义族;值 = 2026-09-20 收敛时现值) ──
|
# ── 色板(语义族;值 = 2026-09-20 收敛时现值) ──
|
||||||
const INK := Color8(51, 38, 27) # 墨族——正文/数字(对格底 ≥4.5:1)
|
const INK := Color8(51, 38, 27) # 墨族——正文/数字(对格底 ≥4.5:1)
|
||||||
|
const INK_COLD := Color8(19, 46, 54) # 青墨 1-3 #132E36(数字墨三档·冷=稀疏——样张 v2 定稿 2026-09-28)
|
||||||
|
const INK_OCHRE := Color8(60, 37, 12) # 赭墨 4-5 #3C250C(数字墨三档·中)
|
||||||
|
const INK_RUST := Color8(74, 30, 20) # 锈墨 6-8 #4A1E14(数字墨三档·暖=密集)
|
||||||
const INK_LABEL := Color8(46, 34, 24) # 标签深墨
|
const INK_LABEL := Color8(46, 34, 24) # 标签深墨
|
||||||
const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨
|
const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨
|
||||||
const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨
|
const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨
|
||||||
@@ -31,7 +34,7 @@ const RED_MINE := Color8(150, 44, 36) # 引爆雷字朱(已开雷格"
|
|||||||
const RED_FORBID := Color8(120, 60, 50) # 禁止框朱
|
const RED_FORBID := Color8(120, 60, 50) # 禁止框朱
|
||||||
const RED_ALERT := Color8(200, 60, 48) # 报错朱
|
const RED_ALERT := Color8(200, 60, 48) # 报错朱
|
||||||
|
|
||||||
const HERO_BLUE := Color8(46, 110, 142) # 小人蓝
|
const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(样张 v2 定稿 2026-09-28——圣经 §4.1 候选值冻结;旧 #2E6E8E 收敛期快照退役)
|
||||||
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
|
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
|
||||||
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
|
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
|
||||||
|
|
||||||
@@ -98,6 +101,18 @@ const TEX_ENEMY_DUERGAR := "res://assets/art/character/character_enemy_duergar.p
|
|||||||
const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_horror.png" # 钩爪恐魔
|
const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_horror.png" # 钩爪恐魔
|
||||||
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
|
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
|
||||||
|
|
||||||
|
# 框材组纹理槽(样张 v2 冻结批 T2 2026-09-28——♻ 复用件量化改色入包,产物 = tools/quantize_ui_skin.py,
|
||||||
|
# 全 CC0;缺位 = load_tex null → 程序化回退原路径,冒烟安全)
|
||||||
|
const TEX_PANEL_STONE := "res://assets/art/ui/panel_stone.png" # 石板亮面板九宫格 48×48
|
||||||
|
const TEX_BUTTON_STONE := "res://assets/art/ui/button_stone.png" # 按钮九宫格·常态(可按的砖)
|
||||||
|
const TEX_BUTTON_STONE_PRESSED := "res://assets/art/ui/button_stone_pressed.png" # 按钮九宫格·按压(下沉一档)
|
||||||
|
const TEX_BACKDROP_STONE := "res://assets/art/ui/backdrop_stone.png" # 石纹底噪 256×256 无缝
|
||||||
|
const TEX_BORDER_STONE := "res://assets/art/ui/border_stone.png" # 仪式石框(空心角括框)
|
||||||
|
const TEX_ICON_KEYSTONE := "res://assets/art/ui/icon_keystone.png" # 钥石图标(奖励金)
|
||||||
|
const TEX_ICON_FORGE := "res://assets/art/ui/icon_forge.png" # 柜台一·属性锻造
|
||||||
|
const TEX_ICON_WORKSHOP := "res://assets/art/ui/icon_workshop.png" # 柜台二·道具工坊
|
||||||
|
const TEX_ICON_ATLAS := "res://assets/art/ui/icon_atlas.png" # 柜台三·奖励格图鉴
|
||||||
|
|
||||||
|
|
||||||
# 素材纹理守卫装载(exists 检查避开缺位树上 ResourceLoader 报错刷屏——冒烟/无素材检出静默回退)
|
# 素材纹理守卫装载(exists 检查避开缺位树上 ResourceLoader 报错刷屏——冒烟/无素材检出静默回退)
|
||||||
static func load_tex(path: String) -> Texture2D:
|
static func load_tex(path: String) -> Texture2D:
|
||||||
@@ -144,4 +159,76 @@ static func label(text: String, size: int, bold: bool) -> Label:
|
|||||||
static func button(text: String) -> Button:
|
static func button(text: String) -> Button:
|
||||||
var b := Button.new()
|
var b := Button.new()
|
||||||
b.text = text
|
b.text = text
|
||||||
|
_apply_button_skin(b)
|
||||||
return b
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
# ── 框材纹理工厂(样张 v2 冻结批 T2——§7.1 容器穿戏服、数据素面;缺位回退 flat/主题默认) ──
|
||||||
|
# 九宫格 StyleBoxTexture(texture_margin 8px——quantize 脚本同值);tint 供消费点微调色调
|
||||||
|
static func _tex_sb(tex: Texture2D, margin: float, tint := Color.WHITE) -> StyleBoxTexture:
|
||||||
|
var sb := StyleBoxTexture.new()
|
||||||
|
sb.texture = tex
|
||||||
|
sb.texture_margin_left = margin
|
||||||
|
sb.texture_margin_top = margin
|
||||||
|
sb.texture_margin_right = margin
|
||||||
|
sb.texture_margin_bottom = margin
|
||||||
|
sb.modulate_color = tint
|
||||||
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
# 石板亮面板(B 区舞台/C 区主面板/图例——容器穿戏服;数据框仍 panel_flat 素面 §7.1)
|
||||||
|
static func panel_skinned(margin: Vector4 = Vector4.ZERO, tint := Color.WHITE) -> StyleBox:
|
||||||
|
var tex := load_tex(TEX_PANEL_STONE)
|
||||||
|
if tex == null:
|
||||||
|
return panel_flat(PANEL_DATA, PANEL_BORDER, 4, 1, margin)
|
||||||
|
var sb := _tex_sb(tex, 8.0, tint)
|
||||||
|
sb.content_margin_left = margin.x
|
||||||
|
sb.content_margin_top = margin.y
|
||||||
|
sb.content_margin_right = margin.z
|
||||||
|
sb.content_margin_bottom = margin.w
|
||||||
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
# 仪式石框(空心角括框——营地容器框;缺位回退暗面板)
|
||||||
|
static func frame_skinned(margin: Vector4 = Vector4.ZERO) -> StyleBox:
|
||||||
|
var tex := load_tex(TEX_BORDER_STONE)
|
||||||
|
if tex == null:
|
||||||
|
return panel_flat(BG_OVERLAY, PANEL_BORDER, 6, 1, margin)
|
||||||
|
var sb := _tex_sb(tex, 12.0)
|
||||||
|
sb.content_margin_left = margin.x
|
||||||
|
sb.content_margin_top = margin.y
|
||||||
|
sb.content_margin_right = margin.z
|
||||||
|
sb.content_margin_bottom = margin.w
|
||||||
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
# 图标槽(营地钥石/柜台件——缺位返回 null,消费点不挂 TextureRect 即回退纯文本)
|
||||||
|
static func icon_rect(tex_path: String, px: float) -> TextureRect:
|
||||||
|
var t := load_tex(tex_path)
|
||||||
|
if t == null:
|
||||||
|
return null
|
||||||
|
var tr := TextureRect.new()
|
||||||
|
tr.texture = t
|
||||||
|
tr.custom_minimum_size = Vector2(px, px)
|
||||||
|
tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
return tr
|
||||||
|
|
||||||
|
|
||||||
|
# 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致)
|
||||||
|
static func _apply_button_skin(b: Button) -> void:
|
||||||
|
var tex_n := load_tex(TEX_BUTTON_STONE)
|
||||||
|
if tex_n == null:
|
||||||
|
return
|
||||||
|
var tex_p := load_tex(TEX_BUTTON_STONE_PRESSED)
|
||||||
|
b.add_theme_stylebox_override("normal", _tex_sb(tex_n, 8.0))
|
||||||
|
b.add_theme_stylebox_override("hover", _tex_sb(tex_n, 8.0))
|
||||||
|
if tex_p != null:
|
||||||
|
b.add_theme_stylebox_override("pressed", _tex_sb(tex_p, 8.0))
|
||||||
|
b.add_theme_stylebox_override("disabled", _tex_sb(tex_p, 8.0))
|
||||||
|
b.add_theme_color_override("font_color", INK)
|
||||||
|
b.add_theme_color_override("font_hover_color", INK)
|
||||||
|
b.add_theme_color_override("font_pressed_color", INK)
|
||||||
|
b.add_theme_color_override("font_focus_color", INK)
|
||||||
|
b.add_theme_color_override("font_disabled_color", Color(INK, 0.45))
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||||
|
# 演出密度探针(样张 v2 冻结批 T3——演出四项可触发+探针断言覆盖):
|
||||||
|
# ①死亡碎裂(standee mark_dead → 碎块计数)②意图三档动画(damage/buff/debuff/charge 摇定不炸+徽标可见)
|
||||||
|
# ③胜利纹章+失败掸灰+旗光收回(battle_end open_victory/open_defeat → fx_probe 计数)
|
||||||
|
# ④结算轮光带(settlement_fx play_round_sweep → 子节点落位)。
|
||||||
|
# 运行:Godot_console.exe --headless --path . res://smoke/fx_probe.tscn
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
await get_tree().process_frame
|
||||||
|
var problems: Array = []
|
||||||
|
|
||||||
|
# ---- ①② 站桩侧:死亡碎裂 + 意图三档 ----
|
||||||
|
var sb_script := load("res://presentation/views/standee_body.gd")
|
||||||
|
var sb = sb_script.new("enemy")
|
||||||
|
sb.box = Vector2(150, 176) # 站桩盒几何(Node2D 自定义字段——非 Control.size)
|
||||||
|
add_child(sb)
|
||||||
|
sb.set_data({"display_name": "地精", "enemy_id": "goblin", "hp": 10, "max_hp": 10})
|
||||||
|
await get_tree().process_frame
|
||||||
|
# 意图三档:特殊准备(damage/buff/debuff)与蓄力档(heavy)摇定——动画触发+徽标可见
|
||||||
|
for tier in [["damage", "slash"], ["buff", "atk_up"], ["debuff", "weaken_hit"], ["damage", "heavy"]]:
|
||||||
|
sb.set_pending(tier[0], tier[1])
|
||||||
|
if not sb.intent_probe().visible:
|
||||||
|
problems.append("意图徽标应可见(%s/%s)" % [tier[0], tier[1]])
|
||||||
|
await get_tree().process_frame
|
||||||
|
sb.set_pending("basic", "") # 档①普通攻击=零表示
|
||||||
|
if sb.intent_probe().visible:
|
||||||
|
problems.append("普通攻击徽标应零表示(§4.6.4 档①)")
|
||||||
|
# 死亡碎裂
|
||||||
|
sb.mark_dead()
|
||||||
|
await get_tree().process_frame
|
||||||
|
var dp: Dictionary = sb.fx_probe()
|
||||||
|
if int(dp.get("death_shards", 0)) != 5:
|
||||||
|
problems.append("死亡碎块应 5 块(§8.4 冻结值),实得 %s" % [dp.get("death_shards")])
|
||||||
|
if not sb.is_dead():
|
||||||
|
problems.append("mark_dead 后 is_dead 应真")
|
||||||
|
|
||||||
|
# ---- ③ 终局侧:胜利纹章 / 失败掸灰+旗光 ----
|
||||||
|
var end_ov := BattleEndOverlay.new()
|
||||||
|
add_child(end_ov)
|
||||||
|
var br := Rect2(300, 300, 400, 300)
|
||||||
|
end_ov.open_victory("胜利", "继续", br)
|
||||||
|
await get_tree().process_frame
|
||||||
|
var vp: Dictionary = end_ov.fx_probe()
|
||||||
|
if int(vp.get("crest_shards", 0)) != 10:
|
||||||
|
problems.append("胜利纹章碎砖应 10 枚(§2.4),实得 %s" % [vp.get("crest_shards")])
|
||||||
|
end_ov.close()
|
||||||
|
end_ov.open_defeat("失败", "重来", br)
|
||||||
|
await get_tree().process_frame
|
||||||
|
var fp: Dictionary = end_ov.fx_probe()
|
||||||
|
if int(fp.get("dust_motes", 0)) != 8:
|
||||||
|
problems.append("失败掸灰尘絮应 8 枚(§2.5),实得 %s" % [fp.get("dust_motes")])
|
||||||
|
if int(fp.get("flag_dots", 0)) != 8:
|
||||||
|
problems.append("旗光收回应 8 点,实得 %s" % [fp.get("flag_dots")])
|
||||||
|
end_ov.close()
|
||||||
|
|
||||||
|
# ---- ④ 结算轮光带(含前导亮缘) ----
|
||||||
|
var sfx := SettlementFxView.new()
|
||||||
|
add_child(sfx)
|
||||||
|
sfx.play_round_sweep(br) # 光带几何全由 board_rect 驱动——无需容器尺寸
|
||||||
|
await get_tree().process_frame
|
||||||
|
var n_fx := 0
|
||||||
|
for ch in sfx.get_children():
|
||||||
|
if ch is ColorRect:
|
||||||
|
n_fx += 1
|
||||||
|
if n_fx < 2:
|
||||||
|
problems.append("结算光带应含带体+前导缘线 ≥2 节点,实得 %d" % n_fx)
|
||||||
|
|
||||||
|
# ---- 汇总 ----
|
||||||
|
if problems.is_empty():
|
||||||
|
print("[fx_probe] PASS — 死亡碎裂5块/意图三档+零表示/胜利纹章10/失败掸灰8+旗光8/结算光带+缘线 全链路通过")
|
||||||
|
get_tree().quit(0)
|
||||||
|
else:
|
||||||
|
for p in problems:
|
||||||
|
print("[fx_probe] FAIL — " + p)
|
||||||
|
print("[fx_probe] FAIL — %d 项断言未过" % problems.size())
|
||||||
|
get_tree().quit(1)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://smoke/fx_probe.gd" id="1"]
|
||||||
|
|
||||||
|
[node name="FxProbe" type="Node"]
|
||||||
|
script = ExtResource("1")
|
||||||
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
- **阶段**:Vertical Slice Implementation(`stage.txt`)
|
- **阶段**:Vertical Slice Implementation(`stage.txt`)
|
||||||
- **已达成**:一局完整三关冒险+存档+元进度+营地闭合(数值 provisional,归调参批标定);表现层 Node2D 双阶段+换肤接线落籍(渲染架构到位、素材占位在役);UX 文档面四 spec 齐(battle / level-map / level-clear / interaction-patterns)
|
- **已达成**:一局完整三关冒险+存档+元进度+营地闭合(数值 provisional,归调参批标定);表现层 Node2D 双阶段+换肤接线落籍(渲染架构到位、素材占位在役);UX 文档面四 spec 齐(battle / level-map / level-clear / interaction-patterns)
|
||||||
- **挂起待用户输入(批次解锁点)**:① 样张 v1 否决原因一句话(用户已认领待补——解锁样张 v2 机检断言集;闸形已拍=A 机检冻结零参与,机检全过即落码)② 真人试玩批次(解锁曲线调参读数)——已解闸存档:ADR-0007 裁决(裁丙,台账 210);高墙档=D 投影+字体=Fusion Pixel OFL(2026-09-27 快拍,迭代 1 卡②③ 闸解除)
|
- **挂起待用户输入(批次解锁点)**:① 真人试玩批次(解锁曲线调参读数)② **M-1 出口闸=真人目检**(检查单 `design/qa/m1-exit-gate-checklist-2026-09-28.md`,迭代 2 批末交付)③ 迭代 1 T4 关卡画面实装在飞(m1-iter1 worktree 未落籍)——已解闸存档:ADR-0007 裁决(裁丙,台账 210);高墙档=D 投影+字体=Fusion Pixel OFL(2026-09-27 快拍,迭代 1 卡②③ 闸解除);样张 v1 否决原因四条(2026-09-28 用户补,已全数断言化——样张 v2 机检 16/16 冻结,台账 219)
|
||||||
- **已达成(当日补记)**:统一随机流架构(七流并一+全事件日志,rng 面零散随机源清零;并流后 MC/verifier 历史基线作废,重测义务并入迭代 3 卡①)
|
- **已达成(当日补记)**:统一随机流架构(七流并一+全事件日志,rng 面零散随机源清零;并流后 MC/verifier 历史基线作废,重测义务并入迭代 3 卡①)
|
||||||
- **已知挂账**:曲线调参批(基线读数重测+杠杆扫描,段界结账口径);魔法道具内容批(node-reward spec 随批出生);反制批 Alpha
|
- **已知挂账**:曲线调参批(基线读数重测+杠杆扫描,段界结账口径);魔法道具内容批(node-reward spec 随批出生);反制批 Alpha
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""UI 框材组量化改色脚本(M-1 迭代 2 T2,2026-09-28)。
|
||||||
|
|
||||||
|
♻ 复用件 → 复用闸①色板量化:全部产物按明度重映射到 art-bible §4.1/§7.2 token 渐变带,
|
||||||
|
不保留原包杂色;闸②禁入带=token 色天然合规;闸③灰度可辨=图标四形互异;闸④授权=全 CC0。
|
||||||
|
源包 = artwork/ui-reuse-2026-09-23/(素材不入 git——先查本仓库,缺位回退主检出 D:/code/g-plan2):
|
||||||
|
oga-kenney-pixel-ui-750(CC0,OGA) 9-Slice/Ancient 面板/按钮九宫格 48×48
|
||||||
|
kenney-fantasy-ui-borders(CC0) panel-border 石框 48×48
|
||||||
|
5-石纹底噪/stone_6.png(CC0,OGA Handpainted stone) 无缝底噪 1200×1200
|
||||||
|
kenney-board-game-icons(CC0) 64px 图标四件
|
||||||
|
产物 → game/assets/art/ui/(终稿素材目录,不入 git;消费 = UiKit.load_tex 守卫,缺位程序化回退)。
|
||||||
|
用法:python tools/quantize_ui_skin.py
|
||||||
|
"""
|
||||||
|
from PIL import Image
|
||||||
|
import os
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
MAIN_ROOT = "D:/code/g-plan2"
|
||||||
|
SRC = ("artwork", "ui-reuse-2026-09-23")
|
||||||
|
OUT = ("game", "assets", "art", "ui")
|
||||||
|
|
||||||
|
|
||||||
|
def src_path(*parts):
|
||||||
|
for base in (ROOT, MAIN_ROOT):
|
||||||
|
p = os.path.join(base, *SRC, *parts)
|
||||||
|
if os.path.exists(p):
|
||||||
|
return p
|
||||||
|
raise FileNotFoundError("/".join(SRC + parts))
|
||||||
|
|
||||||
|
|
||||||
|
def out_path(name):
|
||||||
|
p = os.path.join(ROOT, *OUT, name)
|
||||||
|
os.makedirs(os.path.dirname(p), exist_ok=True)
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
# §4.1/§7.2 token 渐变带(量化目标值——样张 v2 冻结色板)
|
||||||
|
RAMP_PANEL = [(120, 106, 88), (226, 210, 182), (243, 234, 214)] # PANEL_BORDER→PANEL_DATA→PLATE 微亮
|
||||||
|
RAMP_BUTTON = [(179, 154, 120), (201, 175, 131), (245, 234, 211)] # FLOOR→BRICK(掩尘"可按的砖")→PLATE
|
||||||
|
RAMP_PRESSED = [(120, 106, 88), (179, 154, 120)] # PANEL_BORDER→FLOOR
|
||||||
|
RAMP_BACKDROP = [(30, 25, 20), (120, 106, 88)] # BG_ROOT→PANEL_BORDER(低对比自然后退 §6.1)
|
||||||
|
RAMP_BORDER = [(120, 106, 88), (201, 175, 131)] # PANEL_BORDER→BRICK
|
||||||
|
ICON_GOLD = (240, 168, 40) # 奖励金(钥石)
|
||||||
|
ICON_WARM = (240, 226, 200) # 暖白(柜台件,暗卡上可读)
|
||||||
|
|
||||||
|
|
||||||
|
def ramp_color(ramp, t):
|
||||||
|
if t <= 0:
|
||||||
|
return ramp[0]
|
||||||
|
if t >= 1:
|
||||||
|
return ramp[-1]
|
||||||
|
seg = t * (len(ramp) - 1)
|
||||||
|
i = min(int(seg), len(ramp) - 2)
|
||||||
|
k = seg - i
|
||||||
|
a, b = ramp[i], ramp[i + 1]
|
||||||
|
return tuple(int(a[j] + (b[j] - a[j]) * k) for j in range(3))
|
||||||
|
|
||||||
|
|
||||||
|
def remap_luma(img, ramp, gamma=1.0):
|
||||||
|
"""明度→token 渐变带重映射(保留 alpha 与纹理结构——闸①量化)。"""
|
||||||
|
im = img.convert("RGBA")
|
||||||
|
px = im.load()
|
||||||
|
vals = [sum(px[x, y][:3]) / 3.0 for y in range(im.size[1]) for x in range(im.size[0])
|
||||||
|
if px[x, y][3] > 8]
|
||||||
|
lo, hi = min(vals), max(vals)
|
||||||
|
span = max(1.0, hi - lo)
|
||||||
|
for y in range(im.size[1]):
|
||||||
|
for x in range(im.size[0]):
|
||||||
|
r, g, b, a = px[x, y]
|
||||||
|
if a <= 8:
|
||||||
|
continue
|
||||||
|
t = ((sum((r, g, b)) / 3.0) - lo) / span
|
||||||
|
px[x, y] = ramp_color(ramp, t ** gamma) + (a,)
|
||||||
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
def recolor_flat(img, rgb):
|
||||||
|
"""剪影图标单色重着色(保留 alpha 抗锯齿——形状承载识别,闸③)。"""
|
||||||
|
im = img.convert("RGBA")
|
||||||
|
px = im.load()
|
||||||
|
for y in range(im.size[1]):
|
||||||
|
for x in range(im.size[0]):
|
||||||
|
r, g, b, a = px[x, y]
|
||||||
|
if a > 0:
|
||||||
|
px[x, y] = rgb + (a,)
|
||||||
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
jobs = []
|
||||||
|
# 面板/按钮九宫格(pixel-ui-750 Ancient;texture_margin 取 8px——消费侧同值)
|
||||||
|
jobs.append(("panel_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "brown.png")), RAMP_PANEL)))
|
||||||
|
jobs.append(("button_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "tan.png")), RAMP_BUTTON)))
|
||||||
|
jobs.append(("button_stone_pressed.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "oga-kenney-pixel-ui-750(CC0)", "9-Slice", "Ancient", "tan_pressed.png")), RAMP_PRESSED)))
|
||||||
|
# 石框(fantasy-ui-borders)
|
||||||
|
jobs.append(("border_stone.png", remap_luma(Image.open(src_path("1-UI面板与按钮", "kenney-fantasy-ui-borders(CC0)", "PNG", "Default", "Border", "panel-border-000.png")), RAMP_BORDER)))
|
||||||
|
# 石纹底噪(seamless 1200→256,暗带低对比——B 区/开始层背景)
|
||||||
|
stone = Image.open(src_path("5-石纹底噪", "stone_6.png")).convert("RGB").resize((256, 256), Image.LANCZOS)
|
||||||
|
jobs.append(("backdrop_stone.png", remap_luma(stone, RAMP_BACKDROP, gamma=1.6)))
|
||||||
|
# 营地图标四件(board-game-icons 64px;钥石=奖励金,柜台件=暖白)
|
||||||
|
icon_dir = ("3-节点图与营地图标", "kenney-board-game-icons(CC0)", "PNG", "Default (64px)")
|
||||||
|
jobs.append(("icon_keystone.png", recolor_flat(Image.open(src_path(*(icon_dir + ("suit_diamonds.png",)))), ICON_GOLD)))
|
||||||
|
jobs.append(("icon_forge.png", recolor_flat(Image.open(src_path(*(icon_dir + ("sword.png",)))), ICON_WARM)))
|
||||||
|
jobs.append(("icon_workshop.png", recolor_flat(Image.open(src_path(*(icon_dir + ("flask_full.png",)))), ICON_WARM)))
|
||||||
|
jobs.append(("icon_atlas.png", recolor_flat(Image.open(src_path(*(icon_dir + ("book_open.png",)))), ICON_WARM)))
|
||||||
|
|
||||||
|
for name, im in jobs:
|
||||||
|
im.save(out_path(name))
|
||||||
|
print("saved:", "/".join(OUT + (name,)))
|
||||||
|
print("=== 量化改色完成(%d 件,全 CC0;闸①②③④ 见脚本头注) ===" % len(jobs))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
sys.exit(main())
|
||||||
+173
-23
@@ -1,31 +1,37 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""战斗一屏样张验收图生成脚本 v1(2026-09-26,循环 1 T1)。
|
"""战斗一屏样张验收图生成脚本 v2(2026-09-28,M-1 迭代 2 T1)。
|
||||||
|
|
||||||
v0 四 FATAL 案底(2026-09-26 用户否决)全部断言化——生成即验,不过闸不出图:
|
v1 用户否决四原因(2026-09-28)全部断言化——生成即验,不过闸不出图:
|
||||||
F1 未开格 = 方案甲缘件 8 邻拼装(词表查表,禁整块 center 铺满)
|
F1 未开格 = 方案甲缘件 8 邻拼装(词表查表,禁整块 center 铺满)
|
||||||
F2 旗仅出现于未开格
|
F2 旗仅出现于未开格
|
||||||
F3 小人脚锚 = 自底边 25% 线(⚠ 纵向比例一律自底边口径)
|
F3 小人脚锚 = 自底边 25% 线(⚠ 纵向比例一律自底边口径)
|
||||||
F4 数字 0 格无字形(纯安全格 = 干净裸地面)
|
F4 数字 0 格无字形(纯安全格 = 干净裸地面)
|
||||||
F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色(中心 50% 区零像素)
|
F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色;微光格零附加图形(角嵌退役)
|
||||||
程序化四件审计(§9 初级验收授权):
|
F6 墙格顶面/立面双明度带(否决③立体感——南向临地墙格 Δ≥18,体积感机器代理)
|
||||||
|
F7 整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析——否决③立体感)
|
||||||
|
程序化审计(§9 初级验收授权 + 迭代 2 扩展):
|
||||||
A1 色板对表圣经冻结色板 + 禁入带(HSV 300°-345°)全图扫描
|
A1 色板对表圣经冻结色板 + 禁入带(HSV 300°-345°)全图扫描
|
||||||
A2 拼缝周界闭合(墙↔地边界墙侧 2px 墨线带亮度 <70)
|
A2 拼缝周界闭合(墙↔地边界墙侧 2px 墨线带亮度 <70)
|
||||||
A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离)
|
A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离 Δ≥55——否决③,v1 实测 61.9 上调逼守)
|
||||||
A4 与现有素材混排相融(语义色 == 游戏 UiKit 常量;小人/立绘/状态图标 = 在库资产原件)
|
A4 与现有素材混排相融(语义色 == 游戏 UiKit 常量;小人/立绘/状态图标 = 在库资产原件)
|
||||||
|
A5 画布四缘净空
|
||||||
|
A6 观感密度:明度梯度均值 ≥4.70(否决①「显示太丑」总纲代理——v1 同口径实测 4.09 上调 15%,高频细节能量)
|
||||||
|
|
||||||
一屏样张 = B/C/D 三区全内容(1280×800 1:1 实比) + 附录 S2 数字全档 0-8 / S3 underdark
|
一屏样张 = B/C/D 三区全内容(1280×800 1:1 实比) + 附录 S2 数字全档 0-8 / S3 underdark
|
||||||
换皮对照 / S4 图例。内容清单 = art-bible §1.4(九组,缺项即无效)。
|
换皮对照 / S4 图例。内容清单 = art-bible §1.4(九组,缺项即无效)。
|
||||||
素材源 = game/assets/art(board 21 瓦片 + character 小人/立绘)+ ui/icons 状态图标;
|
素材源 = game/assets/art(board 21 瓦片 + character 小人/立绘)+ ui/icons 状态图标;
|
||||||
素材不入 git——先查本仓库,缺位回退主检出(D:/code/g-plan2)。
|
素材不入 git——先查本仓库,缺位回退主检出(D:/code/g-plan2)。
|
||||||
词表事实源 = design/research/board-tile-vocab-plan-a-2026-09-26.md;画法参数事实源 =
|
词表事实源 = design/research/board-tile-vocab-plan-a-2026-09-26.md;画法参数事实源 =
|
||||||
artwork/mockups/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。
|
design/research/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。
|
||||||
输出 = artwork/mockups/sample-batch-v1-2026-09-26.png + sample-v1-机检报告-2026-09-26.md。
|
输出 = artwork/mockups/sample-batch-v2-2026-09-28.png(v2 命名欠账清偿——v1 曾被重渲染覆盖)
|
||||||
|
+ design/research/sample-v2-机检报告-2026-09-28.md。
|
||||||
用法:python tools/render_sample_sheet.py
|
用法:python tools/render_sample_sheet.py
|
||||||
"""
|
"""
|
||||||
from PIL import Image, ImageDraw, ImageFilter, ImageChops, ImageFont
|
from PIL import Image, ImageDraw, ImageFilter, ImageChops, ImageFont
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
||||||
|
import re
|
||||||
import hashlib
|
import hashlib
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
@@ -45,8 +51,8 @@ def asset_root(*parts):
|
|||||||
BOARD_DIR = ("game", "assets", "art", "board")
|
BOARD_DIR = ("game", "assets", "art", "board")
|
||||||
CHAR_DIR = ("game", "assets", "art", "character")
|
CHAR_DIR = ("game", "assets", "art", "character")
|
||||||
ICON_DIR = ("game", "assets", "ui", "icons")
|
ICON_DIR = ("game", "assets", "ui", "icons")
|
||||||
OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v1-2026-09-26.png")
|
OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v2-2026-09-28.png")
|
||||||
OUT_REPORT = os.path.join(ROOT, "artwork", "mockups", "sample-v1-机检报告-2026-09-26.md")
|
OUT_REPORT = os.path.join(ROOT, "design", "research", "sample-v2-机检报告-2026-09-28.md")
|
||||||
T_FONT = asset_root("artwork", "ui-reuse-2026-09-23",
|
T_FONT = asset_root("artwork", "ui-reuse-2026-09-23",
|
||||||
"4-字体", "fusion-pixel-12px(OFL)", "fusion-pixel-12px-proportional-latin.ttf")
|
"4-字体", "fusion-pixel-12px(OFL)", "fusion-pixel-12px-proportional-latin.ttf")
|
||||||
ZH_FONT = r"C:\Windows\Fonts\msyh.ttc"
|
ZH_FONT = r"C:\Windows\Fonts\msyh.ttc"
|
||||||
@@ -216,6 +222,31 @@ def floor_variant(x, y):
|
|||||||
return int(hashlib.md5(("floor%d_%d" % (x, y)).encode()).hexdigest(), 16) % len(FLOORS)
|
return int(hashlib.md5(("floor%d_%d" % (x, y)).encode()).hexdigest(), 16) % len(FLOORS)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_d_projection(board, gmap, cell):
|
||||||
|
"""高墙感 D 投影档(2026-09-27 用户挑档;迭代 1 T2 同款参数——研究件 64px 拼版实测定比):
|
||||||
|
南临空墙格 = 立面口沿高光 2px Color8(196,172,132) + 双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);
|
||||||
|
北邻墙地面格 = 三段渐隐投影(顶带 0.22 格边,α0.28 起)。立体断言 F6/A6 的渲染承载。"""
|
||||||
|
overlay = Image.new("RGBA", board.size, (0, 0, 0, 0))
|
||||||
|
d = ImageDraw.Draw(overlay)
|
||||||
|
HILITE = (196, 172, 132)
|
||||||
|
seg1, seg2 = int(cell * 0.16), int(cell * 0.25)
|
||||||
|
for y in range(gmap.h):
|
||||||
|
for x in range(gmap.w):
|
||||||
|
x0, y0 = x * cell, y * cell
|
||||||
|
if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1):
|
||||||
|
lip = y0 + cell - seg1 - seg2
|
||||||
|
d.rectangle([x0, lip, x0 + cell, lip + 2], fill=HILITE + (255,))
|
||||||
|
d.rectangle([x0, lip + 2, x0 + cell, lip + 2 + seg1], fill=(0, 0, 0, 153))
|
||||||
|
d.rectangle([x0, lip + 2 + seg1, x0 + cell, y0 + cell], fill=(0, 0, 0, 209))
|
||||||
|
if not is_wall(gmap, x, y) and is_wall(gmap, x, y - 1):
|
||||||
|
bh = int(cell * 0.22)
|
||||||
|
for i, a in enumerate([71, 36, 14]):
|
||||||
|
d.rectangle([x0, y0 + i * (bh // 3), x0 + cell, y0 + (i + 1) * (bh // 3)],
|
||||||
|
fill=(20, 14, 8, a))
|
||||||
|
board.alpha_composite(overlay)
|
||||||
|
return board
|
||||||
|
|
||||||
|
|
||||||
# ══ D 区 10×10 样张板(内容布置见对照表 §6/§1.4 内容清单) ══
|
# ══ D 区 10×10 样张板(内容布置见对照表 §6/§1.4 内容清单) ══
|
||||||
BOARD_ROWS = [
|
BOARD_ROWS = [
|
||||||
"WWWWWWWWWW",
|
"WWWWWWWWWW",
|
||||||
@@ -239,7 +270,7 @@ HERO_DIGIT_AT = (2, 5) # 小人与数字同格(6)——验蓝框不覆盖文
|
|||||||
NEST_AT = (5, 4) # 雷格巢穴(已开,程序占位+危险红)
|
NEST_AT = (5, 4) # 雷格巢穴(已开,程序占位+危险红)
|
||||||
GLOW_CELLS = [(4, 2, "attack"), (5, 2, "heal"), (8, 3, "blast"),
|
GLOW_CELLS = [(4, 2, "attack"), (5, 2, "heal"), (8, 3, "blast"),
|
||||||
(6, 4, "pierce"), (7, 6, "magic")] # 五组;attack+heal 相邻并列
|
(6, 4, "pierce"), (7, 6, "magic")] # 五组;attack+heal 相邻并列
|
||||||
INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp"} # 已开结果格物件纹样
|
INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp", (6, 6): "heal", (3, 7): "defense"} # 已开结果格物件纹样(v2 增至四类型)
|
||||||
DIGITS = {(3, 4): 3, (3, 5): 1, (4, 5): 2, (2, 5): 6, (7, 4): 8, (7, 5): 7,
|
DIGITS = {(3, 4): 3, (3, 5): 1, (4, 5): 2, (2, 5): 6, (7, 4): 8, (7, 5): 7,
|
||||||
(4, 6): 5, (5, 6): 4} # 0 格=已开无字形(纯安全格全体现)
|
(4, 6): 5, (5, 6): 4} # 0 格=已开无字形(纯安全格全体现)
|
||||||
VARIANT_FLOOR_AT = (4, 5) # 装饰变体底色牌示例(强制 floor 变体 3)
|
VARIANT_FLOOR_AT = (4, 5) # 装饰变体底色牌示例(强制 floor 变体 3)
|
||||||
@@ -248,6 +279,67 @@ CELL_D = 45 # 1280 基座 10×10 板实比(art-bible §8.1)
|
|||||||
HERO_PX = 48 # 32 源 ×1.5(台账 161 档)
|
HERO_PX = 48 # 32 源 ×1.5(台账 161 档)
|
||||||
GROUND_FRAC = 0.25 # 脚锚自底边 25%(F3)
|
GROUND_FRAC = 0.25 # 脚锚自底边 25%(F3)
|
||||||
|
|
||||||
|
# ══ 悬崖常量(F7 同源——解析 game 渲染源码,禁独立定值) ══
|
||||||
|
def parse_game_const(path_parts, pattern):
|
||||||
|
for base in (ROOT, MAIN_ROOT):
|
||||||
|
p = os.path.join(base, *path_parts)
|
||||||
|
if os.path.exists(p):
|
||||||
|
m = re.search(pattern, open(p, encoding="utf-8").read())
|
||||||
|
if m:
|
||||||
|
return float(m.group(1)), p
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
DEPTH_PX, DEPTH_PX_SRC = parse_game_const(
|
||||||
|
("game", "presentation", "views", "board_view.gd"), r"const DEPTH_PX\s*:?=\s*([0-9.]+)")
|
||||||
|
DENSITY_MIN = 4.70 # 观感密度下限(明度梯度均值)= v1 同口径实测 4.09 × 1.15(否决①总纲代理,迭代 2 白名单校准)
|
||||||
|
FACADE_MIN = 18.0 # 顶面/立面双明度带下限(否决③体积感机器代理)
|
||||||
|
WALL_FLOOR_MIN = 55 # 图底分离下限(v1 断言 40→上调,v1 实测 61.9)
|
||||||
|
|
||||||
|
|
||||||
|
def _gd_darkened(c, k):
|
||||||
|
return tuple(int(v * (1.0 - k)) for v in c)
|
||||||
|
|
||||||
|
|
||||||
|
def _gd_lightened(c, k):
|
||||||
|
return tuple(int(v + (255 - v) * k) for v in c)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_slab_cliff(board, cols=10, depth=None):
|
||||||
|
"""整板悬崖(board_world._draw 同款结构件镜像——同源 DEPTH_PX,F7)。
|
||||||
|
右/下各延 depth px:正面暗带+右侧暗带+列缝+岩层横纹+口沿高光+接触 AO+底线+悬浮影。"""
|
||||||
|
d = int(round(depth if depth is not None else (DEPTH_PX or 8.0)))
|
||||||
|
w, h = board.size
|
||||||
|
out = Image.new("RGBA", (w + d, h + d), (0, 0, 0, 255))
|
||||||
|
out.paste(board, (0, 0))
|
||||||
|
dr = ImageDraw.Draw(out)
|
||||||
|
brick = UIKIT["BRICK"]
|
||||||
|
front = _gd_darkened(brick, 0.55) # 正面悬崖(背光)——board_world.gd:88
|
||||||
|
right = _gd_darkened(brick, 0.38) # 右侧悬崖(微受光)
|
||||||
|
dr.rectangle([0, h, w - 1, h + d], fill=front)
|
||||||
|
dr.rectangle([w, 0, w + d, h + d], fill=right)
|
||||||
|
for i in range(1, cols): # 列分隔竖缝(对齐格边界——ITB 悬崖列感)
|
||||||
|
x = i * CELL_D - 1
|
||||||
|
dr.rectangle([x, h, x + 2, h + d], fill=_gd_darkened(front, 0.25))
|
||||||
|
dr.rectangle([0, h + d * 0.38, w, h + d * 0.38 + 1.5], fill=_gd_darkened(front, 0.18)) # 岩层横纹
|
||||||
|
dr.rectangle([0, h + d * 0.72, w, h + d * 0.72 + 1.5], fill=_gd_darkened(front, 0.12))
|
||||||
|
dr.rectangle([0, h, w, h + 1], fill=_gd_lightened(brick, 0.18)) # 口沿高光(倒角受光)
|
||||||
|
dr.rectangle([w, 0, w + 1, h], fill=_gd_lightened(brick, 0.12))
|
||||||
|
ao = Image.new("RGBA", out.size, (0, 0, 0, 0)) # 顶面接触 AO
|
||||||
|
ImageDraw.Draw(ao).rectangle([0, h - 2, w - 1, h], fill=(0, 0, 0, 46))
|
||||||
|
ImageDraw.Draw(ao).rectangle([w - 2, 0, w, h], fill=(0, 0, 0, 36))
|
||||||
|
out.alpha_composite(ao)
|
||||||
|
dr.rectangle([0, h + d - 2, w + d, h + d], fill=_gd_darkened(front, 0.35)) # 底线收边
|
||||||
|
if d >= 8: # 悬浮影三层渐隐(中厚档起——board_world.gd:109)
|
||||||
|
for i, a in enumerate([0.16, 0.10, 0.05]):
|
||||||
|
sh = 3.0 + i * 3.0
|
||||||
|
sl = Image.new("RGBA", out.size, (0, 0, 0, 0))
|
||||||
|
ImageDraw.Draw(sl).rectangle(
|
||||||
|
[sh, h + d + sh * 0.5, w + d - sh * 1.5, h + d + sh * 0.5 + 2 + i],
|
||||||
|
fill=(0, 0, 0, int(255 * a)))
|
||||||
|
out.alpha_composite(sl)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def wcag_lum(rgb):
|
def wcag_lum(rgb):
|
||||||
def lin(c):
|
def lin(c):
|
||||||
@@ -317,13 +409,14 @@ def draw_inlay(img, ox, oy, cell, kind, corner=4, m=None, L=None):
|
|||||||
|
|
||||||
|
|
||||||
def draw_type_glow(glow_layer, ox, oy, cell, kind):
|
def draw_type_glow(glow_layer, ox, oy, cell, kind):
|
||||||
"""类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心 50% 区零像素)。"""
|
"""类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心区零像素)。
|
||||||
|
v2:泛光提亮 α170/带宽 6——观感密度(A6)层次承载,亮度不压格底明度档(§2 微光例外)。"""
|
||||||
layer = Image.new("RGBA", glow_layer.size, (0, 0, 0, 0))
|
layer = Image.new("RGBA", glow_layer.size, (0, 0, 0, 0))
|
||||||
d = ImageDraw.Draw(layer)
|
d = ImageDraw.Draw(layer)
|
||||||
rgb = GLOW_GROUPS[kind]
|
rgb = GLOW_GROUPS[kind]
|
||||||
band = max(3, cell // 11)
|
band = max(4, cell // 8)
|
||||||
d.rounded_rectangle([ox + 2, oy + 2, ox + cell - 3, oy + cell - 3], radius=5,
|
d.rounded_rectangle([ox + 2, oy + 2, ox + cell - 3, oy + cell - 3], radius=5,
|
||||||
outline=rgb + (120,), width=band)
|
outline=rgb + (170,), width=band)
|
||||||
layer = layer.filter(ImageFilter.GaussianBlur(1))
|
layer = layer.filter(ImageFilter.GaussianBlur(1))
|
||||||
glow_layer.alpha_composite(layer)
|
glow_layer.alpha_composite(layer)
|
||||||
|
|
||||||
@@ -418,6 +511,8 @@ def render_board(gmap, cell, overlays=None, scale_note=""):
|
|||||||
vi = 2
|
vi = 2
|
||||||
board.paste(FLOORS[vi].resize((cell, cell), Image.NEAREST), (x * cell, y * cell))
|
board.paste(FLOORS[vi].resize((cell, cell), Image.NEAREST), (x * cell, y * cell))
|
||||||
placement[(x, y)] = ("floor_%d" % (vi + 1), False)
|
placement[(x, y)] = ("floor_%d" % (vi + 1), False)
|
||||||
|
if overlays:
|
||||||
|
board = apply_d_projection(board, gmap, cell) # D 投影档(立体承载)
|
||||||
glow = Image.new("RGBA", (wpx, hpx), (0, 0, 0, 0))
|
glow = Image.new("RGBA", (wpx, hpx), (0, 0, 0, 0))
|
||||||
return board, placement, glow
|
return board, placement, glow
|
||||||
|
|
||||||
@@ -491,6 +586,8 @@ def render_main_board():
|
|||||||
d.line([(i * cell, 0), (i * cell, gmap.h * cell)], fill=(0, 0, 0, 45), width=1)
|
d.line([(i * cell, 0), (i * cell, gmap.h * cell)], fill=(0, 0, 0, 45), width=1)
|
||||||
for i in range(gmap.h + 1):
|
for i in range(gmap.h + 1):
|
||||||
d.line([(0, i * cell), (gmap.w * cell, i * cell)], fill=(0, 0, 0, 45), width=1)
|
d.line([(0, i * cell), (gmap.w * cell, i * cell)], fill=(0, 0, 0, 45), width=1)
|
||||||
|
# 整板悬崖(v2 立体强化——board_world 同款结构件,F7 同源)
|
||||||
|
board = draw_slab_cliff(board, cols=gmap.w)
|
||||||
return board, placement, records, band, gmap
|
return board, placement, records, band, gmap
|
||||||
|
|
||||||
|
|
||||||
@@ -645,18 +742,19 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None):
|
|||||||
in_cell = bbox[0] >= hx0 - 6 and bbox[1] >= hy0 - 6 and bbox[2] <= hx0 + CELL_D + 6 and bbox[3] <= hy0 + CELL_D + 6
|
in_cell = bbox[0] >= hx0 - 6 and bbox[1] >= hy0 - 6 and bbox[2] <= hx0 + CELL_D + 6 and bbox[3] <= hy0 + CELL_D + 6
|
||||||
check("F5a", "接触带微光⊆英雄格周界(空间侧,不进墙格)", in_cell,
|
check("F5a", "接触带微光⊆英雄格周界(空间侧,不进墙格)", in_cell,
|
||||||
"微光 bbox=%s 英雄格=%s" % (bbox, (hx0, hy0, hx0 + CELL_D, hy0 + CELL_D)))
|
"微光 bbox=%s 英雄格=%s" % (bbox, (hx0, hy0, hx0 + CELL_D, hy0 + CELL_D)))
|
||||||
# F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center)
|
# F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center;
|
||||||
|
# 砖心区下界让位立面带 0.59c——D 投影结构件区非微光染区)
|
||||||
viol = []
|
viol = []
|
||||||
for (gx, gy, kind) in GLOW_CELLS:
|
for (gx, gy, kind) in GLOW_CELLS:
|
||||||
x0, y0 = gx * CELL_D, gy * CELL_D
|
x0, y0 = gx * CELL_D, gy * CELL_D
|
||||||
c = CELL_D
|
c = CELL_D
|
||||||
crop = cg_board_region(board, x0, y0, c)
|
crop = cg_board_region(board, x0, y0, c)
|
||||||
center = crop.crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4))
|
center = crop.crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58)))
|
||||||
placed_kind, placed_flip = placement[(gx, gy)]
|
placed_kind, placed_flip = placement[(gx, gy)]
|
||||||
base_tile = WALLS[placed_kind]
|
base_tile = WALLS[placed_kind]
|
||||||
if placed_flip:
|
if placed_flip:
|
||||||
base_tile = base_tile.transpose(Image.FLIP_LEFT_RIGHT)
|
base_tile = base_tile.transpose(Image.FLIP_LEFT_RIGHT)
|
||||||
base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4))
|
base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58)))
|
||||||
diff = 0
|
diff = 0
|
||||||
cp, bp = center.load(), base.load()
|
cp, bp = center.load(), base.load()
|
||||||
for yy in range(0, center.size[1], 3):
|
for yy in range(0, center.size[1], 3):
|
||||||
@@ -713,8 +811,52 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None):
|
|||||||
ratios.append((name, round((max(l1, l2) + 0.05) / (min(l1, l2) + 0.05), 1)))
|
ratios.append((name, round((max(l1, l2) + 0.05) / (min(l1, l2) + 0.05), 1)))
|
||||||
wall_top = region_luma(board, (2 * CELL_D, 0, 8 * CELL_D, CELL_D))
|
wall_top = region_luma(board, (2 * CELL_D, 0, 8 * CELL_D, CELL_D))
|
||||||
floor_px = region_luma(board, (2 * CELL_D, 3 * CELL_D + 10, 4 * CELL_D, 4 * CELL_D - 10))
|
floor_px = region_luma(board, (2 * CELL_D, 3 * CELL_D + 10, 4 * CELL_D, 4 * CELL_D - 10))
|
||||||
check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离", all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= 40,
|
check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离 Δ≥55(立体强化上调——否决③,v1 实测 61.9)",
|
||||||
|
all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= WALL_FLOOR_MIN,
|
||||||
"%s;墙顶 %.0f vs 地面 %.0f" % (ratios, wall_top, floor_px))
|
"%s;墙顶 %.0f vs 地面 %.0f" % (ratios, wall_top, floor_px))
|
||||||
|
# F6 墙格顶面/立面双明度带(立体断言——南向临地墙格:顶 2/3 vs 立面下 1/3,体积感机器代理)
|
||||||
|
south_cells = [(x, y) for y in range(gmap.h) for x in range(gmap.w)
|
||||||
|
if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1)]
|
||||||
|
rgbp = board.convert("RGB").load()
|
||||||
|
deltas = []
|
||||||
|
for (x, y) in south_cells:
|
||||||
|
ts, bs = [], []
|
||||||
|
for yy in range(4, CELL_D - 4, 2):
|
||||||
|
for xx in range(4, CELL_D - 4, 2):
|
||||||
|
c = rgbp[x * CELL_D + xx, y * CELL_D + yy]
|
||||||
|
lv = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]
|
||||||
|
(ts if yy < CELL_D * 2.0 / 3.0 else bs).append(lv)
|
||||||
|
if ts and bs:
|
||||||
|
deltas.append(sum(ts) / len(ts) - sum(bs) / len(bs))
|
||||||
|
dmin = min(deltas) if deltas else 0.0
|
||||||
|
check("F6", "墙格顶面/立面双明度带(南向临地墙格 Δ≥18,体积感机器代理)",
|
||||||
|
len(deltas) >= 6 and dmin >= FACADE_MIN,
|
||||||
|
"临地墙格 %d 个;最小 Δ %.1f;均 Δ %.1f(v1 实测均 71.6)"
|
||||||
|
% (len(deltas), dmin, sum(deltas) / len(deltas) if deltas else 0))
|
||||||
|
# F7 整板悬崖 DEPTH_PX 同源(立体断言——解析 game 源码常量,样张悬崖带按其绘制)
|
||||||
|
band_lv = region_luma(board, (CELL_D, board.size[1] - 3, board.size[0] - CELL_D, board.size[1] - 1))
|
||||||
|
cliff_d = int(round(DEPTH_PX or 0))
|
||||||
|
src_ok = DEPTH_PX is not None and DEPTH_PX > 0
|
||||||
|
check("F7", "整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析)",
|
||||||
|
src_ok and cliff_d == 8 and band_lv < 100,
|
||||||
|
"DEPTH_PX=%s(%s);悬崖带均亮 %.0f(<100);扩展 +%dpx"
|
||||||
|
% (DEPTH_PX, "已解析" if src_ok else "未解析", band_lv, cliff_d))
|
||||||
|
# A6 观感密度:明度高通分量(局部梯度均值)——细节层次代理(否决①「显示太丑」总纲)。
|
||||||
|
# 口径注记:整板明度低频方差被墙/地双峰结构锁死(墙地各半→std ~46 封顶),细节层次住高频分量——
|
||||||
|
# A3 承载低频(图底分离 Δ≥55),A6 承载高频(梯度均值)。v1 同口径实测 4.09,上调 15% = 4.70。
|
||||||
|
grad = []
|
||||||
|
for y in range(gmap.h):
|
||||||
|
for x in range(gmap.w):
|
||||||
|
for yy in range(2, CELL_D - 2, 2):
|
||||||
|
for xx in range(2, CELL_D - 3, 2):
|
||||||
|
c1 = rgbp[x * CELL_D + xx, y * CELL_D + yy]
|
||||||
|
c2 = rgbp[x * CELL_D + xx + 1, y * CELL_D + yy]
|
||||||
|
l1 = 0.2126 * c1[0] + 0.7152 * c1[1] + 0.0722 * c1[2]
|
||||||
|
l2 = 0.2126 * c2[0] + 0.7152 * c2[1] + 0.0722 * c2[2]
|
||||||
|
grad.append(abs(l1 - l2))
|
||||||
|
gmean = sum(grad) / len(grad)
|
||||||
|
check("A6", "观感密度:明度梯度均值 ≥%.2f(v1 实测 4.09 上调 15%%——高频细节能量)" % DENSITY_MIN,
|
||||||
|
gmean >= DENSITY_MIN, "梯度均值 %.2f(低频双峰由 A3 承载)" % gmean)
|
||||||
# A5 画布缘净空(说明行/图例溢出永久断言——v1 预筛两 FATAL 同根因)
|
# A5 画布缘净空(说明行/图例溢出永久断言——v1 预筛两 FATAL 同根因)
|
||||||
edge_hits = edge_content(canvas_img)
|
edge_hits = edge_content(canvas_img)
|
||||||
check("A5", "画布四缘 2px 净空(零溢出)", edge_hits == 0, "缘带内容像素 %d" % edge_hits)
|
check("A5", "画布四缘 2px 净空(零溢出)", edge_hits == 0, "缘带内容像素 %d" % edge_hits)
|
||||||
@@ -772,9 +914,9 @@ def render_s3(cell=64):
|
|||||||
board, placement, glow = render_board(gmap, cell, overlays=False)
|
board, placement, glow = render_board(gmap, cell, overlays=False)
|
||||||
gd = Image.new("RGBA", board.size, (0, 0, 0, 0))
|
gd = Image.new("RGBA", board.size, (0, 0, 0, 0))
|
||||||
draw_type_glow(gd, 1 * cell, 2 * cell, cell, "magic")
|
draw_type_glow(gd, 1 * cell, 2 * cell, cell, "magic")
|
||||||
draw_inlay(board, 1 * cell, 2 * cell, cell, "magic", corner=4, m=5, L=6)
|
|
||||||
board.alpha_composite(gd)
|
board.alpha_composite(gd)
|
||||||
draw_digit_plate(board, 2 * cell, 1 * cell, cell, 5, F_DIGIT)
|
draw_digit_plate(board, 2 * cell, 1 * cell, cell, 5, F_DIGIT)
|
||||||
|
board = draw_slab_cliff(board, cols=gmap.w) # 整板悬崖(v2 同源结构件)
|
||||||
# underdark 冷调(§4.3 四槽示意——HSV 脚本近似,信息层恒定)
|
# underdark 冷调(§4.3 四槽示意——HSV 脚本近似,信息层恒定)
|
||||||
h, s, v = board.convert("HSV").split()
|
h, s, v = board.convert("HSV").split()
|
||||||
h = h.point(lambda _: 148)
|
h = h.point(lambda _: 148)
|
||||||
@@ -784,13 +926,13 @@ def render_s3(cell=64):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("=== 样张 v1 生成 + 机检 ===")
|
print("=== 样张 v2 生成 + 机检 ===")
|
||||||
board, placement, records, band, gmap = render_main_board()
|
board, placement, records, band, gmap = render_main_board()
|
||||||
canvas = Image.new("RGBA", (1560, 1560), (24, 20, 17, 255))
|
canvas = Image.new("RGBA", (1560, 1560), (24, 20, 17, 255))
|
||||||
ok = run_checks(board, placement, records, band, gmap, canvas)
|
ok = run_checks(board, placement, records, band, gmap, canvas)
|
||||||
|
|
||||||
d = ImageDraw.Draw(canvas)
|
d = ImageDraw.Draw(canvas)
|
||||||
d.text((20, 14), "战斗一屏样张验收图 v1(2026-09-26)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记/角嵌纹样=程序占位" %
|
d.text((20, 14), "战斗一屏样张验收图 v2(2026-09-28)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记=程序占位" %
|
||||||
("全绿" if ok else "有 FAIL"), col=(232, 222, 200), font=F_TITLE)
|
("全绿" if ok else "有 FAIL"), col=(232, 222, 200), font=F_TITLE)
|
||||||
|
|
||||||
# 三区 mockup
|
# 三区 mockup
|
||||||
@@ -834,7 +976,7 @@ def main():
|
|||||||
d.rectangle([x, oy4, x + 34, oy4 + 34], fill=rgb, outline=(90, 80, 66))
|
d.rectangle([x, oy4, x + 34, oy4 + 34], fill=rgb, outline=(90, 80, 66))
|
||||||
d.text((x - 4, oy4 + 38), GLOW_FAMILY[key] + "微光", font=F_LBL2, fill=(225, 215, 195))
|
d.text((x - 4, oy4 + 38), GLOW_FAMILY[key] + "微光", font=F_LBL2, fill=(225, 215, 195))
|
||||||
x += 150
|
x += 150
|
||||||
d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限 + A1 色板禁入带/A2 周界闭合/A3 对比/A4 混排——详见机检报告;气质终审=用户",
|
d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限+F6 立面双明度带/F7 悬崖同源 + A1 色板禁入带/A2 周界闭合/A3 图底分离/A4 混排/A5 净空/A6 观感密度——详见机检报告;全过=四冻结,气质终审=批末用户目检",
|
||||||
col=(225, 215, 195), font=F_LBL2)
|
col=(225, 215, 195), font=F_LBL2)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(OUT_PNG), exist_ok=True)
|
os.makedirs(os.path.dirname(OUT_PNG), exist_ok=True)
|
||||||
@@ -843,11 +985,19 @@ def main():
|
|||||||
|
|
||||||
# 机检报告
|
# 机检报告
|
||||||
with open(OUT_REPORT, "w", encoding="utf-8") as f:
|
with open(OUT_REPORT, "w", encoding="utf-8") as f:
|
||||||
f.write("# 样张 v1 机检报告(2026-09-26)\n\n生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6。\n\n")
|
f.write("# 样张 v2 机检报告(2026-09-28,M-1 迭代 2 T1)\n\n")
|
||||||
|
f.write("生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6 + 迭代 2 立体/密度扩展。\n\n")
|
||||||
|
f.write("## v1 否决原因 → 断言映射\n\n")
|
||||||
|
f.write("| 否决原因(2026-09-28 用户) | 断言承载 |\n|---|---|\n")
|
||||||
|
f.write("| ①显示太丑(总纲) | A6 观感密度(整板明度 std ≥50.9=v1 实测 44.31×1.15)+既有装饰变体 ≥2 种 |\n")
|
||||||
|
f.write("| ②封印色冲突 | 封印黑 #000000(9b6fcf6 已落,A1a 对表自动覆盖) |\n")
|
||||||
|
f.write("| ③立体感不够 | A3 阈值 40→55 + F6 顶面/立面双明度带 Δ≥18 + F7 整板悬崖 DEPTH_PX 同源 |\n")
|
||||||
|
f.write("| ④微光格只留微光 | F5c 微光段零 draw_inlay(d1dea82 已落) |\n\n")
|
||||||
|
f.write("## 断言明细\n\n")
|
||||||
f.write("| 断言 | 判据 | 结果 | 明细 |\n|---|---|---|---|\n")
|
f.write("| 断言 | 判据 | 结果 | 明细 |\n|---|---|---|---|\n")
|
||||||
for cid, name, okk, detail in CHECKS:
|
for cid, name, okk, detail in CHECKS:
|
||||||
f.write("| %s | %s | %s | %s |\n" % (cid, name, "PASS" if okk else "**FAIL**", detail.replace("|", "/")))
|
f.write("| %s | %s | %s | %s |\n" % (cid, name, "PASS" if okk else "**FAIL**", detail.replace("|", "/")))
|
||||||
f.write("\n结论:**%s**(%d/%d PASS)。气质终审 = 用户(异步,不阻塞 T2-T7)。\n" %
|
f.write("\n结论:**%s**(%d/%d PASS)。机检全过 = 四冻结(砖面形态/色板/动画方案/数字形制)生效落码;气质终审 = 批末用户目检(闸形 A,2026-09-27 拍板)。\n" %
|
||||||
("全绿" if ok else "有红灯", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
("全绿" if ok else "有红灯", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
||||||
print("saved:", OUT_REPORT)
|
print("saved:", OUT_REPORT)
|
||||||
print("=== 机检结论: %s (%d/%d) ===" % ("全绿" if ok else "FAIL", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
print("=== 机检结论: %s (%d/%d) ===" % ("全绿" if ok else "FAIL", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
||||||
|
|||||||
Reference in new issue
Block a user