二七王:修复出牌操作条/亮牌历史布局越界,补主牌统计条改名遗漏

- I1:Layers.js 群组 206 改名遗漏一处注释(亮牌信息条 → 主牌统计条),全仓复查确认无第二处
- I2:出牌操作条补「提示」项后 anchor:center 导致已有两项挤偏,改用 anchor:left 并反推
  anchorX=446,令「出牌」按钮回到参考图实测 x≈556(求解器验证:446/556/748)
- I3:HIST_FAN/MING_FAN 的 maxWidth 反推算错(旧注释「488」有误,实际总宽恒等于
  maxWidth=500),导致 RIGHT 座位整排超出画布 5px;收窄为 490 使三座位求解结果落在
  0-1280 内,订正注释与清单 §6.9c 对应文字
- M3:同步修正计划文档里遗留的旧名「亮牌信息条/亮牌条」
- M6:test_constants.js 补 items[].key 的三命名空间解析守卫(此前完全无守卫)
- M5/M7:订正 ACC_ROW_TEXT_GRID 与扣底按钮宽度的估值依据说明
- M1/M2:清单 §6.9 子节重新排序为 6.9a→6.9b→6.9c 并统一标题层级,修正脚注星号误渲染

client/tests/run.js 与 server/games/erqiwang/test/run.js 均全绿,精灵总数(380)/
布局节点数(83) 两个钉死断言未变。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 09:06:01 +08:00
co-authored by Claude Opus 5
parent 8f36b61620
commit 2b1dd0d947
7 changed files with 87 additions and 37 deletions
+24 -22
View File
@@ -1688,7 +1688,7 @@ CARD_SIZE: {
| 主牌统计条(节点 `ZHU_STAT_BG`) | `point` | `x:35 y:400 w:210 h:35` |
| 底栏条 | `point` | `x:0 y:648 w:1280 h:72` |
| 底栏 · 局数文字 | `point` | `x:635 y:672 w:65 h:26`,`textStyle{fontSize:20, align:center}` |
| 底栏 · 功能钮组(明牌/已出牌/上一轮/**扣底**/底牌) | `line` + `items` | `direction:horizontal, anchorX:1250, anchorY:672, itemHeight:30, spacing:15, anchor:right`,`items:[74,87,93,**78***,78]`(逐项不等宽,见 §6.1)。**\*`扣底` 的 78 是估值**(2026-08-27 补项,本表原只列 4 项)——同为两字按钮的 `底牌` 实测 78、`明牌` 74,故取 78,待设计稿复核 |
| 底栏 · 功能钮组(明牌/已出牌/上一轮/**扣底**/底牌) | `line` + `items` | `direction:horizontal, anchorX:1250, anchorY:672, itemHeight:30, spacing:15, anchor:right`,`items:[74,87,93,78†,78]`(逐项不等宽,见 §6.1)。†`扣底` 的 78 是估值(2026-08-27 补项,本表原只列 4 项)——参考图(等待叫分.png)底栏当时也只画出 `底牌` 一项;依据取自 `底牌` 实测的 78,不能笼统说「两字按钮 = 78」:同为两字的 `明牌` 实测只有 74,两者不同,待设计稿复核 |
| 底栏 · 庄标 | `point` | `x:338 y:668 w:32 h:34` |
| 底栏 · 提示按钮组(踩/有分/没分) | `line` | `direction:horizontal, anchorX:335, anchorY:672, itemWidth:52, itemHeight:30, spacing:26, anchor:left` |
| 右侧语音钮(平台) | `point` | `x:1210 y:310 w:56 h:56` |
@@ -1754,7 +1754,7 @@ CARD_SIZE: {
| 选主 · 张数 | `attach` | `target:对应花色按钮, hAlign:center, vAlign:bottom, offsetY:-6, h:NUM_STYLE.MAIN_SUIT_COUNT.charHeight(32)`;`w` 运行时注入(`9张`=2 字符、`26张`=3 字符,宽度随之变)。一个花色一个精灵 |
| 选主 · 对数角标文字 | `attach` | `target:对应花色按钮, corner:topRight, offsetX:-36, offsetY:2`(按钮帧内已含角标底,本节点只定位叠加文字) |
| 埋牌 · 操作条 | `line` + `items` | `direction:horizontal anchorX:640 anchorY:165 itemHeight:63 spacing:45 anchor:center`,`items:[180,60,180]`(提示 / 倒计时 / 埋牌) |
| 出牌 · 操作条 | `line` + `items` | `direction:horizontal anchorX:640 anchorY:345 itemHeight:55 spacing:35 anchor:center`,`items:[75,157,**180***]`(倒计时 / 出牌 / **提示**)。**\*`提示` 的 180 是估值**(2026-08-27 补项,本表原漏了 §1.7 明确要求的 `提示` 按钮)——与埋牌条的 `提示` 同资源 `BTN_TIP`(§3.3 实测 180×63),埋牌条 `items` 首项亦取 180,故沿用;顺序照 §1.7 行文。⚠ 本条 `itemHeight:55` 对齐的是 `BTN_PLAY`(157×55),而 `BTN_TIP` 原生高 63,高度差待设计稿裁定 |
| 出牌 · 操作条 | `line` + `items` | `direction:horizontal anchorX:446 anchorY:345 itemHeight:55 spacing:35 anchor:left`,`items:[75,157,180‡]`(倒计时 / 出牌 / **提示**)。‡`提示` 的 180 是估值(2026-08-27 补项,本表原漏了 §1.7 明确要求的 `提示` 按钮)——与埋牌条的 `提示` 同资源 `BTN_TIP`(§3.3 实测 180×63),埋牌条 `items` 首项亦取 180,故沿用;顺序照 §1.7 行文。⚠ 本条 `itemHeight:55` 对齐的是 `BTN_PLAY`(157×55),而 `BTN_TIP` 原生高 63,高度差待设计稿裁定。**2026-08-27 再订正**:补「提示」项后若沿用 `anchor:center`,整排重新居中会把已有的倒计时/出牌两项相对原先(仅 2 项时)左移 107.5px,导致「出牌」按钮偏离参考图 `出牌.png` 的实测位置(556–711)。改为 `anchor:left, anchorX:446`(= 参考图出牌按钮实测起点 556 − 倒计时宽 75 − 间距 35),求解结果:倒计时 x=446、出牌 x=556(与实测吻合)、提示 x=748 |
| 捡分飘字 | `point` | `x:570 y:180 w:170 h:55`;另配 `floatRise:-60`、`floatDuration:800`(动画参数也进配置) |
| 状态提示条 · 等待类 | `point` | `x:500 y:373 w:320 h:47`(§2.8) |
| 状态提示条 · 即时反馈类 | `point` | `x:462 y:172 w:363 h:50`;另配 `toastDuration:1500` |
@@ -1807,30 +1807,12 @@ CARD_SIZE: {
| 玩家栏**行** | `ACC_ROW` | `line` | `direction:vertical, anchorX:0, anchorY:14, itemWidth:1100, **itemHeight:132(行高)**, spacing:0, anchor:top`(相对容器;行数 = `ctx.count`) |
| 行内 · 名次徽章 | `ACC_TPL_RANK` | `attach` | `target:ACC_ROW, hAlign:left, vAlign:middle, offsetX:28, w:44 h:44` |
| 行内 · 头像 | `ACC_TPL_AVATAR` | `attach` | `target:ACC_ROW, hAlign:left, vAlign:middle, offsetX:92, w:80 h:80` |
| 行内 · 文字栅格(6 条文字的槽位) | `ACC_ROW_TEXT_GRID` | `grid` | `anchorX:192, cols:4, rows:2, itemWidth:176, itemHeight:34, spacingX:0, spacingY:10, anchor:left, fillOrder:row`;`anchorY` 运行时注入(随所在行浮动)。列序:昵称/`ID:` · 基础分/总得分 · 冲关分 · 傍王分(第 2 行仅前两列有文字) |
| 行内 · 文字栅格(6 条文字的槽位) | `ACC_ROW_TEXT_GRID` | `grid` | `anchorX:192, cols:4, rows:2, itemWidth:176, itemHeight:34, spacingX:0, spacingY:10, anchor:left, fillOrder:row`;`anchorY` 运行时注入(随所在行浮动)。列序:昵称/`ID:` · 基础分/总得分 · 冲关分 · 傍王分(第 2 行仅前两列有文字)。**估值精度**:本节点是等距列模型(相对容器 192/368/544/720),参考图目视实测列 x 约 272/455/634/800(列距 183/179/166,并不等距),偏差约 10px,比本节头部通用的「±5px」更大,待设计稿复核 |
| 行内 · 右侧总分大字 | `ACC_TPL_SCORE_NUM` | `attach` | `target:ACC_ROW, hAlign:right, vAlign:middle, offsetX:-25, h:NUM_STYLE.RESULT_WIN.charHeight(62)`;`w` 运行时注入 |
| 行内 · 分隔线 | `ACC_TPL_DIVIDER` | `attach` | `target:ACC_ROW, hAlign:center, vAlign:bottom, offset(0,0), w:1060 h:2`(`vAlign:bottom` 的语义是"目标底边之下",见 §6.0) |
| 面板**外**底部 4 按钮 | `ACC_FUNC_BUTTONS` | `line` | `direction:horizontal, anchorX:345, anchorY:640, itemWidth:190, itemHeight:60, spacing:25, anchor:left`(参考图里这排不居中于屏幕、整体偏右,故用 `anchor:left` 定起点) |
#### 6.9c 出牌历史 / 亮牌 · 明牌配置(§5.7b,2026-08-27 补齐)
> 本表同样原先**整块缺失**(`HIST_*` / `MING_*` 一个节点都没有)。
> 两者与冲关牌型叠加层是**同一种版式**(半透黑遮罩 + 每家一排重叠大牌,参考图同为 `uiref/冲关牌型显示.png`),故照 §6.7 的 `CHONGGUAN_FAN` 同构给出,三家锚点沿用冲关牌的 `LEFT 260,120 / RIGHT 1035,120 / SELF 640,400`。
> **与冲关牌的唯一差别是张数上限**:出牌历史摊平后可达 28 张以上,`maxWidth:420` 装不下,故放宽到 **500**、`spacingMin` 收到 **-96**(每张露 14px,28 张总宽 `110+27×14=488 ≤ 500`)。这两个数是**估值,待设计稿复核**。
> 牌用精灵复制:三个容器精灵摆在原点、尺寸铺满屏,故 `fan` 求出的屏幕坐标可直接当容器内坐标用。
| 部件 | 节点名 | kind | 参数 |
| --- | --- | --- | --- |
| 出牌历史 · 遮罩 | `HIST_MASK` | `point` | `x:0 y:0 w:1280 h:720, opacity:0.72`(【亮牌】用途下**不注册点击**,见 §1.6) |
| 出牌历史 · 三个牌容器 | `HIST_BOX` | `point` | `x:0 y:0 w:1280 h:720`(同摆原点、铺满屏) |
| 出牌历史 · 每家一排牌 | `HIST_FAN` | `fan` | `size:L, anchor:center, maxWidth:500, spacingMax:-40, spacingMin:-96`;`bySeat{ LEFT:{260,120}, RIGHT:{1035,120}, SELF:{640,400} }` |
| 出牌历史 · 座位标签 | `HIST_LABEL` | `point` | `bySeat{ LEFT:{200,318}, RIGHT:{975,318}, SELF:{580,598} }`;`w/h` 运行时注入 |
| 明牌 · 遮罩 | `MING_MASK` | `point` | 同 `HIST_MASK` |
| 明牌 · 两个牌容器 | `MING_BOX` | `point` | 同 `HIST_BOX` |
| 明牌 · 每家一排牌 | `MING_FAN` | `fan` | 同 `HIST_FAN`,但 **`bySeat` 只有 LEFT / RIGHT**(明牌只看他家,没有自己那排;精灵 `MING_BOX_A` 落 LEFT 位、`MING_BOX_B` 落 RIGHT 位) |
| 明牌 · 座位标签 | `MING_LABEL` | `point` | `bySeat{ LEFT:{200,318}, RIGHT:{975,318} }`;`w/h` 运行时注入 |
### 6.9b 建房规则选项配置(§1.1)
#### 6.9b 建房规则选项配置(§1.1)
布局是**两层嵌套**:类别竖排,类别内的选项组横排。
@@ -1857,6 +1839,26 @@ ROOM_CAT_TITLE_STYLE: { fontSize: 18, color: '#7FBFB0', bold: true, align: 'righ
不要在代码里写死 `'#E8873A'`。
#### 6.9c 出牌历史 / 亮牌 · 明牌配置(§5.7b,2026-08-27 补齐)
> 本表同样原先**整块缺失**(`HIST_*` / `MING_*` 一个节点都没有)。
> 两者与冲关牌型叠加层是**同一种版式**(半透黑遮罩 + 每家一排重叠大牌,参考图同为 `uiref/冲关牌型显示.png`),故照 §6.7 的 `CHONGGUAN_FAN` 同构给出,三家锚点沿用冲关牌的 `LEFT 260,120 / RIGHT 1035,120 / SELF 640,400`。
> **与冲关牌的唯一差别是张数上限**:出牌历史摊平后可达 28 张以上,`maxWidth:420` 装不下。
> **2026-08-27 订正**:原估值 `maxWidth:500` 的推导算错了——`fan` 的间距公式是 `spacing = max(spacingMin, min(spacingMax, (maxWidth-itemWidth)/(count-1) - itemWidth))`,代入 `maxWidth 500 / itemWidth 110 / count 28` 得 `(500-110)/27-110 ≈ -95.56`,**没有**触到 `spacingMin:-96` 的下限;而 `fan` 的总宽恒等于 `maxWidth`(只要 spacing 未被下限截断),并不是旧注释算的「488」。`RIGHT` 座位锚点 `anchorX:1035` 配合总宽 500、`anchor:center`,整排右边缘落在 `1035+250=1285`,**超出 1280 设计画布 5px**(`LEFT`/`SELF` 因锚点更靠左未暴露)。现收窄为 `maxWidth:490`:总宽仍未触及下限(`spacingMin:-96` 因此在 28 张时始终是**死参数**,留作日后张数/尺寸变化时的兜底),`RIGHT` 右边缘 `1035+245=1280`,恰好落在画布内;每张仍露约 14px(`itemWidth+spacing≈110-95.93≈14.07`),视觉上与旧估值接近。`maxWidth:490` 与 `spacingMin:-96` 仍是**估值,待设计稿复核**。
> **已知限制**:每张仅露约 14px、视觉偏窄,待设计稿复核是否改为分两排显示,或改用 `CARD_SIZE.M`——这是估值阶段接受的限制,留给后续复核。
> 牌用精灵复制:三个容器精灵摆在原点、尺寸铺满屏,故 `fan` 求出的屏幕坐标可直接当容器内坐标用。
| 部件 | 节点名 | kind | 参数 |
| --- | --- | --- | --- |
| 出牌历史 · 遮罩 | `HIST_MASK` | `point` | `x:0 y:0 w:1280 h:720, opacity:0.72`(【亮牌】用途下**不注册点击**,见 §1.6) |
| 出牌历史 · 三个牌容器 | `HIST_BOX` | `point` | `x:0 y:0 w:1280 h:720`(同摆原点、铺满屏) |
| 出牌历史 · 每家一排牌 | `HIST_FAN` | `fan` | `size:L, anchor:center, maxWidth:490, spacingMax:-40, spacingMin:-96`;`bySeat{ LEFT:{260,120}, RIGHT:{1035,120}, SELF:{640,400} }`。求解验证(count=28):`LEFT` 首尾 x ≈ `15 → 505`,`RIGHT` 首尾 x ≈ `790 → 1280`,`SELF` 首尾 x ≈ `395 → 885`,均落在 0–1280 内 |
| 出牌历史 · 座位标签 | `HIST_LABEL` | `point` | `bySeat{ LEFT:{200,318}, RIGHT:{975,318}, SELF:{580,598} }`;`w/h` 运行时注入 |
| 明牌 · 遮罩 | `MING_MASK` | `point` | 同 `HIST_MASK` |
| 明牌 · 两个牌容器 | `MING_BOX` | `point` | 同 `HIST_BOX` |
| 明牌 · 每家一排牌 | `MING_FAN` | `fan` | 同 `HIST_FAN`,但 **`bySeat` 只有 LEFT / RIGHT**(明牌只看他家,没有自己那排;精灵 `MING_BOX_A` 落 LEFT 位、`MING_BOX_B` 落 RIGHT 位) |
| 明牌 · 座位标签 | `MING_LABEL` | `point` | `bySeat{ LEFT:{200,318}, RIGHT:{975,318} }`;`w/h` 运行时注入 |
### 6.10 配置文件组织
按 client 02 §2 的三件套拆分,布局部分建议再按界面拆文件,用保护性声明合并: