二七王:UI 清单落实 A/B/C 组决策,新增服务端待补项

规格确认(T-4/6/7/8/10/11/12/15/16/18/19/22/23/26 全部落地):
- 叫分按钮配色按【档位位置】固定三档(浅黄/金黄/橙)+ 灰=已叫过,
  按钮底与角标底各 4 帧。特别标注:参考图角标 1子/2子/4子 是 mock,
  颜色不随子数变(反证:常规算子下 50~5 档同为 6 子却是两种颜色)。
- 选主大数字=该花色张数、角标=对数;手牌标记 3 帧(拖/正2正7/其他主牌)。
- 底栏功能按钮定为 5 个,新增「扣底」;并显著标注术语陷阱——底栏「底牌」
  按钮看的是【暗牌】、「扣底」按钮看的才是【底牌】,而协议里两批牌共用
  bottomcards 字段名,要求前端用 anCards/diCards 区分。
- 结算底牌文案的 x 后面是扣底倍数;牌型标签只做「毙」;准备标识用平台的;
  数字一律用图片多帧(参考图数字带描边渐变)。
- 发牌动画:从右往左铺开。算奖叠加层点遮罩关闭、并列显示奖数。
- 自己发的提示本地回显,作为悲观 UI 的受控例外并写明成立理由
  (提示不含对局状态、服务端不校验真实性)。
- T-5 手牌排序定为对齐服务端 order_cards 主牌序,选主后整体重排。

新增 §7.5 服务端待补项:
- S-1 抓分倍数字段:协议现无,出牌阶段拿不到任何倍数;服务端 get_upgrade
  现成可用,建议补 curmultiple,语义待最终确认。
- S-2 下发应选中的牌:get_followcard 已返回 mustcard,仅用于内部校验、
  未下发;建议只发给 nextseat 一家(整表下发会泄露他家手牌结构)。
- S-3 ⚠️ 底栏「底牌」按钮要求所有人可看暗牌,与 design §4「非 70 分时
  暗牌只有庄家可见」及协议下发面直接冲突;给出三个方案,确认前前端按
  最保守的 (a) 限定可见范围实现。

新增 D-13(扣底查看面板)、验收清单增至 15 条。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-25 13:25:33 +08:00
co-authored by Claude Opus 5
parent 2c3464b6f0
commit 5dda571250
2 changed files with 214 additions and 63 deletions
+214 -63
View File
@@ -6,7 +6,7 @@
>
> **权威源**:玩法规则以 `server/games/erqiwang/docs/design/design.md` 为准;下发字段以 `server/games/erqiwang/docs/protocol/packet_protocol.md` 为准;前端规范以 `docs/client/development-guide/` 为准。本文只做「规则/协议 → 界面资源」的映射,**不重新定义规则**。
>
> **参考图**:`./uiref/*.png`,共 12 张,设计分辨率 1280×720。
> **参考图**:`./uiref/*.png`,共 13 张,设计分辨率 1280×720。
---
@@ -32,6 +32,7 @@
- [6.10 配置文件组织](#610-配置文件组织)
- [6.11 与平台布局配置的衔接](#611-与平台布局配置的衔接)
- [7. 待补充清单](#7-待补充清单)
- [7.5 服务端待补项](#75-服务端待补项需改服务端才能实现的前端需求)
---
@@ -41,7 +42,7 @@
| 项 | 值 | 来源 |
| --- | --- | --- |
| 设计分辨率 | **1280 × 720** | `client/output/gameabc_Project.json` `ScreenWidth/ScreenHeight`;11 张牌桌参考图均为 1280×720;建房参考图为局部截图,不含整屏尺寸 |
| 设计分辨率 | **1280 × 720** | `client/output/gameabc_Project.json` `ScreenWidth/ScreenHeight`;11 张牌桌参考图均为 1280×720;叫分按钮面板与建房参考图为局部截图 |
| 适配模式 | `ScreenFitMode: 1` | 同上 |
| 帧率 | 30 fps | 同上 |
| 玩家数 | **3 人** | design §2(三家各摸 28 张) |
@@ -413,7 +414,7 @@ EQW_RoomOptions = {
| 三家玩家位状态文字清空,`主N/对N` 角标隐藏 | §2.2 玩家位附加标记 |
| 倒计时显示在 `seat` 对应的位置 | §2.4 倒计时 |
- **发牌动画**【待确认 T-4】:是否需要逐张飞入动画。若做,须遵守 client 02 §3「数据优先、表现延后」——先 `setHandCards()` 写数据,再播动画;动画回调里只刷界面、不写数据。
- **发牌动画(需要做)**:**从右往左铺开**。须遵守 client 02 §3「数据优先、表现延后」——先 `setHandCards()` 把 28 张写进 `this.data`,再播铺开动画;动画的开始/结束/出错回调里**只刷界面、不写核心数据**。动画缺失或卡住时,数据与界面仍须正确。动画时长、每张延迟进配置(§6.7)。
- **排序**:手牌排序规则(主牌在左还是在右、副牌花色顺序)由前端自行决定,**但选主前后主牌集合会变**,选主后必须重排。见 §7 T-5。
### 1.3 叫分坐庄 `jiaofen` 【图:等待叫分.png / 自己叫分.png / 自己已经叫分轮到下家叫分.png】
@@ -431,11 +432,31 @@ EQW_RoomOptions = {
**叫分面板规格**(群组 220,号段 1600–1649):
- 14 档:`70 65 60 55 50 45 40 35 30 25 20 15 10 5`,两行 × 7(行1 = 70…40,行2 = 35…5)。
- 每档 3 个精灵:按钮底(多帧,见下)+ 分数文字 + **子数角标文字**。
- 每档 4 个精灵:按钮底 + 分数文字 + 角标底 + **子数角标文字**。
- 角标显示该档的 `multiple`,**随房间「爬坡」开关取值**(协议 §3):
- 常规算子:65→2、60→3、55→4、**50 及以下统一 →6**、70→2
- 爬坡:65→2、60→3、55→4、50→6、45→7、40→8、35→9、30→10、25→11、20→12、15→13、10→14、5→15、70→2
- 按钮底帧:**帧1 = 可选(亮黄)、帧2 = 不可选(灰)、帧3 = 高亮/当前**【待确认 T-6:参考图第二行为橙色,含义待定】。
**按钮配色:按档位位置固定,不随状态或数据变** 【图:叫分按钮面板.png】
| 位置 | 档位 | 按钮底 | 角标底 |
| --- | --- | --- | --- |
| 1–4 | 70 / 65 / 60 / 55 | 浅黄 | 蓝 |
| 5–7 | 50 / 45 / 40 | 金黄 | 绿 |
| 8–14 | 35 / 30 / 25 / 20 / 15 / 10 / 5 | 橙 | 深橙 |
> ⚠️ 参考图里角标写的 `1子`/`2子`/`4子` 是 **mock 数值**,恰好与三组配色一一对应,**不要据此以为「颜色随子数变」**——颜色按档位位置写死,角标数值才随爬坡开关变。叫分越低(承诺越苛刻、子数越高)颜色越热。
>
> 反证:常规算子下 50/45/40 与 35…5 的 `multiple` 同为 6 子,但参考图里它们是两种颜色。
**灰色 = 已经叫过的分数**(不可再叫)。故按钮底与角标底各 **4 帧**:
| 帧 | 按钮底 | 角标底 |
| --- | --- | --- |
| 1 | 浅黄(高分档) | 蓝 |
| 2 | 金黄(中分档) | 绿 |
| 3 | 橙(低分档) | 深橙 |
| 4 | **灰(已叫过 / 不可选)** | 灰 |
- 可选性规则(design §4.2,**由服务端权威 `currcall` 决定,前端只据其置灰、不自行推导规则**):
- 暂定庄家首叫:5–70 全部可选,**无 `不叫`**;
- 其后:只有**严格低于 `currcall`** 的档位可选,`不叫` 可选。
@@ -466,7 +487,7 @@ EQW_RoomOptions = {
- 标题斜角条:`亮主` 金色字 + 倒计时数字。
- **4 个花色按钮**,每个含 4 个精灵:按钮底 + 花色大图标 + **中央大数字** + **右上角蓝色角标**。
- 中央大数字 = 该花色在庄家 36 张手牌中的**总张数**【待确认 T-7:参考图为 6/8/6/7,与角标 `2对` 并存,推定为张数】
- 中央大数字 = 该花色在庄家 36 张手牌中的**总张数**
- 右上角标 = 该花色的**对子数**(design §4/§11 明确要求)
- **两个数字都由前端据 `MyCards`/`cards` 自行统计**,协议 §「ChooseMain」明确「服务端不额外下发」。
- **投降按钮**:仅 `touxiang === 1`(即叫分 70)时显示,与 4 个花色按钮并列。
@@ -486,7 +507,12 @@ EQW_RoomOptions = {
- 手牌从**单排 36 张**改为**双排**(上排 17 + 下排 19,实测估值)。
- 操作条(群组 222):`提示` 按钮 + 倒计时 + **`埋牌 (N/8)`** 按钮,N 为已选张数,`N < 8` 时按钮置灰不可提交。
- 选中的牌**上浮**表示待埋。
- 参考图中牌面上有 `拖` 红色圆标、⭐橙星、🔵蓝星等标记 —— 推定为「提示」功能标出的牌型分组【待确认 T-8:标记种类与含义未定义于 design/协议】。
- 牌面上的三种标记(资源 `MARK_CARD_GROUP` 3 帧):
- **`拖` 红色圆标** = 该牌属于一组**拖拉机**
- **橙色五角星** = **正 2 / 正 7**(选定花色的 2 和 7,design §3 里大小仅次于双王)
- **蓝色五角星** = 除正2正7外的**其他主牌**(双王、副2、副7、主花色普通牌)
三种标记互斥、每张牌至多一个;由前端据 `flower`(主牌花色)在本地标注,**不需要服务端下发**。
**收到 `maipai` 推送后**(字段 `cards`、`bottomcards`、`seat`、`countdown`、`liangpai`):
- 庄家手牌回到单排 28 张;
@@ -508,7 +534,13 @@ EQW_RoomOptions = {
这是最复杂的阶段。
**出牌操作条**(群组 223):倒计时 + `出牌` 按钮(选中牌数为 0 时置灰)。参考图另有 `提示` 按钮(埋牌图里出现,出牌阶段是否保留待确认 T-10)。
**出牌操作条**(群组 223):倒计时 + `出牌` 按钮(选中牌数为 0 时置灰)+ **`提示` 按钮(出牌阶段必须有)**。
**自动选中**:某些情况下(如跟牌时存在**必出牌**、或整手只有唯一合法出法),服务端会在下发包里带上**应当选中的牌**,前端收到后**自动把这些牌选中**(上浮),玩家确认后点 `出牌` 即可。
> ⚠️ **该字段目前协议里没有,需服务端补**——见 §7.5 **S-2**。好消息是服务端已经算过了:`class.arith.js:737` `get_followcard()` 返回 `{ mustcard, cancard, cantype }`,其中 `mustcard` 就是「必出的牌列表」,现在只用于内部校验(`class.arith.js:1055`)、未下发。补一个下发字段即可,无需新增算法。
>
> 这条**不违反**「请求包只带意图」红线——方向是**服务端 → 前端**的建议选中,前端仍只回传 `cards`;且合法性最终仍由服务端校验。
**自己出牌**:点手牌选中(上浮)→ 点 `出牌` → 发 `chupai { seat, cards }`。
- `cards` 必须是**非空、元素为 0–107 整数、互不重复**的数组(协议 §0.2),否则服务端按 `PARAM` 拒绝。
@@ -541,9 +573,12 @@ EQW_RoomOptions = {
**闲家提示 `tishi`** 【图:自己是闲家时,底下的踩有分没分按钮.png】(design §11):
- 闲家在出牌阶段可向**对家**(另一闲家)发三种提示:`1 = 踩`(我能大过庄家)、`2 = 没分`、`3 = 有分`。
- 发送方**收不到成功回执**(协议 §13.7),只有失败才回包 → **前端不得在点击时乐观显示自己发过提示**。
- 发送方**收不到成功回执**(协议 §13.7),只有失败才回包。
- 接收方(对家)收到 `tishi { seat, tip }` → 在发送者的玩家位弹出气泡。
- 庄家无对家,不提供此功能。
- **自己发的提示需要本地回显**:服务端只把提示转发给对家、不回执给发送者,所以**发送者自己的气泡由前端在点击时本地弹出**。
> 这是「悲观 UI」红线的一处**受控例外**,成立的理由是:提示**不含任何对局状态**,服务端明确「不校验提示真实性」(协议 §13.7、design §11),它既不改阶段、不改控制权、不改分数,纯属玩家间的主观喊话。因此本地回显不会造成与服务端状态不一致。仍须遵守:失败回包(`RULE`/`STEP`/`PARAM`)到达时要把本地回显撤掉。
**发起入口**:底栏三个按钮 `踩` `有分` `没分`,**位置就是庄标的位置**——自己是闲家时没有 `庄` 印章,这块区域正好空出来给这三个按钮。
@@ -601,7 +636,8 @@ EQW_RoomOptions = {
**扣底表现**(`bottom` 分组,design §6.3):
- `cards`(8 张底牌)**恒有** → 底牌区(群组 215,资源 `CARD_FACE_S`)翻开展示,design §11 明确「牌局结束需要亮出底牌」;
- `multiple` / `grade1` / `grade2` **仅闲家扣底时才有** → 展示 `底牌 20 × 4` 之类的翻倍算式。
- 参考图底牌面板下方文字为 `叫分2子 底牌 20x0`,即 `叫分{multiple}子 底牌 {grade1}x{multiple}`【待确认 T-12:`x0` 说明该局闲家未扣底,此时的文案规则待定】。
- 文案格式:`叫分{aset.multiple}子 底牌 {bottom.grade1}x{bottom.multiple}`——**`x` 后面是扣底倍数**(design §6.3:单张 ×1、主对 ×2、N 连对 ×2N)。参考图 `底牌 20x0` 即底牌有 20 分、扣底倍数 0(本局闲家未扣底)。
- 闲家未扣底时 `bottom` 只有 `cards`,`multiple`/`grade1`/`grade2` 都不下发 → 倍数位显示 **0**。
**三家结算数字**(`aset.seatlist`,每家两行):
@@ -643,7 +679,9 @@ EQW_RoomOptions = {
**算奖牌型叠加层** 【图:算奖牌型显示.png】:
点 `算奖牌型` 后,在结算面板之上盖一层**半透黑遮罩**,三家各展示自己的 `aset.seatlist[i].cards`,资源用 `CARD_FACE_L`、重叠排列。`下一局` 按钮在遮罩层上**继续可见可点**,`算奖牌型` 按钮本身被遮住。再点一次或点遮罩空白处关闭【待确认 T-22:关闭方式】。
点 `算奖牌型` 后,在结算面板之上盖一层**半透黑遮罩**,三家各展示自己的 `aset.seatlist[i].cards`,资源用 `CARD_FACE_L`、重叠排列。`下一局` 按钮在遮罩层上**继续可见可点**,`算奖牌型` 按钮本身被遮住。
**关闭方式:整块全屏半透明黑遮罩本身就是关闭热区,点击遮罩即关闭。** 不另设关闭按钮。因此 `AWARD_MASK` 必须注册点击事件,且 `下一局` 的层级要高于它、点它不被遮罩吃掉。
- 三家牌位:左上家、右上家在各自头像内侧;自己在中央偏下。
- 布局配置见 §6.7。
@@ -660,7 +698,7 @@ EQW_RoomOptions = {
| `upgrade` | **判定倍率(带符号)**:`3` 大光 / `2` 小光 / `1` 过庄 / `-N` 升 N 级(倒庄)/ `-99` 投降 / `0` 解散 → 需要一组判定文字或图标:`大光` `小光` `过庄` `升N级` `投降`【待设计 D-8】 |
| `bangwang` / `climb` | 本局规则开关,用于结算面板上标注 |
| **`cards`** | **算奖牌型叠加层**的牌(见上) |
| `chongguan` / `wang` / `naward` | 常规算奖奖数 / 王数 / 总奖数 —— 是否在叠加层上并列显示数字【待确认 T-23】 |
| `chongguan` / `wang` / `naward` | 常规算奖奖数 / 王数 / **总奖数**。叠加层**需要并列显示奖数**——以 `naward`(该家总奖数 N,8.4 节的支付基数)为主;`chongguan` / `wang` 作为明细可选展示 |
**投降结算**:只有 `aset`,无 `chupai`、无 `bottom` → 结算面板必须容忍**底牌区缺失**,不能因为读不到 `bottom.cards` 就崩或显示空框。
@@ -713,7 +751,7 @@ EQW_RoomOptions = {
### 1.12 准备 `zhunbei`
小局结算后各家点准备进入下一局。收 `zhunbei { seat }` → 该家玩家位显示「已准备」标记【待设计:参考图未出现,可能复用平台准备标识,见 T-15】。
小局结算后各家点 `下一局` 进入下一局(§1.8),发 `zhunbei { seat }`。收 `zhunbei { seat }` → 该家玩家位显示「已准备」标记——**用平台的准备标识**(§0.2),子游戏不另做资源。
---
@@ -792,12 +830,32 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| `庄` 印章 | x≈338–370 | **自己是庄**时显示 |
| `踩` / `有分` / `没分` 按钮 | x≈335–573(占用同一区域) | **自己是闲家 + 出牌阶段**时显示,与 `庄` 印章互斥(§1.7) |
| 局数 `1/4 局` | 居中 x≈635–700 | 常驻,`asetidx / asetcount` |
| `明牌` 按钮 | x≈876–950, y≈672–702 | **仅可查牌模式 + 出牌阶段 + 已报无主** |
| `已出牌` 按钮 | x≈965–1052 | **仅可查牌模式 + 出牌阶段** |
| `上一轮` 按钮 | x≈1067–1160 | 同上 |
| `底牌` 按钮 | x≈1172–1250 | 常驻【待确认 T-18:庄家看自己埋的底牌?闲家点了看什么?】 |
**右侧功能按钮共 5 个**,按情况显隐(不是全部常驻):
- 精灵:群组 205,号段 1040–1069。
| 按钮 | 看什么 | 显示条件 |
| --- | --- | --- |
| `上一轮` | **上一轮**所有人出的牌 | 可查牌 + 出牌阶段 + 已打过至少一轮 |
| `出牌历史` | 所有人**出过的全部牌** | 可查牌 + 出牌阶段 |
| `明牌` | 另两家手中全部主牌的具体牌面 | 可查牌 + 出牌阶段 + **已报无主** |
| **`扣底`** | **庄家查看自己埋的 8 张底牌** | **仅庄家** + 已埋牌之后 |
| **`底牌`** | **发牌时桌面留下的 8 张暗牌** | 见下方可见性问题 S-3 |
> ### ⚠️ 术语陷阱:底栏「底牌」按钮看的不是「底牌」
>
> design 术语表:**暗牌** = 发牌时留在桌面的 8 张;**底牌** = 庄家埋牌时重新扣下的 8 张。两者是**不同批的牌,不通用、不互换**。
>
> 底栏按钮的命名恰好**与术语相反**:
>
> | 按钮 | 实际展示 | design 术语 | 协议字段 |
> | --- | --- | --- | --- |
> | `扣底` | 庄家埋的 8 张 | **底牌** | `maipai.bottomcards` / `PushCards.bottomcards`(仅庄家)/ 结算 `bottom.cards` |
> | `底牌` | 发牌留桌的 8 张 | **暗牌** | `shangzhuang.bottomcards`(庄家恒有,闲家仅 70 分时有)/ `ChooseMain`·`BuryCards.bottomcards`(仅庄家) |
>
> 更容易踩的是:协议里这**两批牌用了同一个字段名 `bottomcards`**,只靠「在哪个包/哪个阶段」区分语义。
>
> **实现要求**:前端内部一律用 `anCards`(暗牌)/ `diCards`(底牌)两个不同变量名,**不要跟着按钮文案命名**;取值时按所在包判断语义。已列入验收清单(§7.3)。
- 精灵:群组 205,号段 1040–1069(5 个按钮 × 2 精灵 = 10)。
### 2.7 手牌区 【图:全部】
@@ -848,7 +906,7 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| 536 | `BTN_BURY` | `埋牌 (N/8)` | 2 | 帧1=可提交(金黄) 帧2=置灰(未满 8 张) | 180 × 63 |
| 537 | `BTN_PLAY` | `出牌` | 2 | 帧1=可出 帧2=置灰 | 157 × 55 |
| 538 | `BTN_BOTTOM_FUNC` | 底栏功能钮底(明牌/已出牌/上一轮/底牌共用) | 1 | 深色圆角 | 88 × 30 |
| 539 | `BTN_READY` | 准备【T-15】 | 1 | 待设计 | 待定 |
| ~~539~~ | ~~`BTN_READY`~~ | **不需要**:准备标识用平台的(§0.2) | —— | —— |
| 540 | `BTN_TISHI` | 底栏 `踩` / `有分` / `没分` 按钮底(三者共用) | 1 | 深色圆角,同 `BTN_BOTTOM_FUNC` 风格但更窄 | 52 × 30 |
| 541 | `BTN_NEXT_ASET` | 结算 `下一局` | 1 | 金黄渐变 | 180 × 65 |
| 542 | `BTN_AWARD_CARDS` | 结算 `算奖牌型` | 1 | 蓝色渐变 | 183 × 63 |
@@ -862,8 +920,8 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| 573 | `MARK_JOKER_BIG` | 大王 `大` 圆标 | 2 | 帧1=大王 帧2=小王 | 34 × 34 |
| 574 | `BADGE_ZHU_PAIR` | `主N` / `对N` 角标底 | 2 | 帧1=黄底(主) 帧2=白底(对) | 44 × 20 |
| 575 | `BADGE_MULTIPLE` | 子数/倍数角标底 | 2 | 帧1=蓝底 帧2=橙底 | 34 × 18 |
| 576 | `LABEL_CARDTYPE` | 牌型标签 | ≥6 | 帧1=单张 帧2=对子 帧3=拖拉机 帧4=甩牌 帧5=毙 帧6=垫【T-11】 | 76 × 24 |
| 577 | `MARK_CARD_GROUP` | 手牌牌型分组标记(`拖`圆标 / ⭐ / 🔵)【T-8】 | ≥3 | 待定 | 26 × 26 |
| 576 | `LABEL_CARDTYPE` | 牌型标签 | **1** | **只做「毙」一种**(前端只展示这一个标签)。数据层仍保留 毙 / 垫 / 混合出牌 三种标识以备扩展,但当前不出图、不显示 | 76 × 24 |
| 577 | `MARK_CARD_GROUP` | 手牌牌型标记 | **3** | **帧1=`拖`(拖拉机)帧2=橙色五角星(正2 / 正7)帧3=蓝色五角星(除正2正7外的其他主牌)** | 26 × 26 |
| 578 | `MARK_SELECTED` | 手牌选中标记 | 1 | 待定(也可只用上浮,不出图) | 待定 |
### 3.5 面板底图
@@ -889,7 +947,7 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| 633 | `NUM_RESULT_LOSE` | 结算得分数字(输,蓝) | 12 | 同上 | 46 × 62 |
| 634 | `TXT_JUDGE` | 判定结果文字 | ≥5 | 帧1=大光 帧2=小光 帧3=过庄 帧4=升级 帧5=投降【待设计】 | 待定 |
> 若改用文字精灵(`SpriteManager.setText`)而非图片数字,631–634 可省。见 T-19。
> **数字一律用图片多帧精灵**(不用 `SpriteManager.setText`)——参考图里的数字都带描边与渐变,文字精灵做不出这种效果。故 631–634 全部需要出图。
### 3.7 浮层与气泡
@@ -1022,7 +1080,7 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| | 1651 | `MAIN_TITLE_TEXT` | 文字 | `亮主` | —— |
| | 1652–1655 | `MAIN_SUIT_BTN_1..4` | 图片 ×4 | 花色按钮底 | `BTN_SUIT_CHOOSE` |
| | 1656–1659 | `MAIN_SUIT_ICON_1..4` | 图片 ×4 | 花色图标 | `SUIT_ICON_L` 帧1–4 |
| | 1660–1663 | `MAIN_SUIT_COUNT_1..4` | 文字 ×4 | 该花色**张数**【T-7】 | —— |
| | 1660–1663 | `MAIN_SUIT_COUNT_1..4` | 文字 ×4 | 该花色在庄家手中的**张数** | —— |
| | 1664–1667 | `MAIN_SUIT_PAIR_BG_1..4` | 图片 ×4 | 对数角标底 | `BADGE_MULTIPLE` 帧1 |
| | 1668–1671 | `MAIN_SUIT_PAIR_TEXT_1..4` | 文字 ×4 | `N对` | —— |
| | 1672 | `MAIN_BTN_SURRENDER` | 图片 | `投降` | `BTN_SURRENDER` |
@@ -1033,7 +1091,8 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| | 1703 | `BURY_BTN_SUBMIT_TEXT` | 文字 | `埋牌 (N/8)` | —— |
| **223** 出牌操作条 | 1710 | `PLAY_BTN_SUBMIT` | 图片 | `出牌` | `BTN_PLAY` 帧1可出/帧2灰 |
| | 1711 | `PLAY_BTN_SUBMIT_TEXT` | 文字 | —— | —— |
| | 1712 | `PLAY_BTN_TIP`【T-10】 | 图片 | `提示` | `BTN_TIP` |
| | 1712 | `PLAY_BTN_TIP` | 图片 | `提示`(出牌阶段必备) | `BTN_TIP` |
| | 1713 | `PLAY_BTN_TIP_TEXT` | 文字 | 按钮文案 | —— |
| **224** 倒计时 | 1150 | `CD_LEFT` | 文字 | 左上家位倒计时 | —— |
| | 1151 | `CD_RIGHT` | 文字 | 右上家位倒计时 | —— |
| | 1152 | `CD_SELF` | 文字 | 自己操作区倒计时 | —— |
@@ -1055,7 +1114,7 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| 精灵 ID | 键名 | 类型 | 用途 | 资源 / 帧 |
| --- | --- | --- | --- | --- |
| 1800 | `RESULT_BOTTOM_PANEL` | 图片 | 底牌区面板底 | `PANEL_BOTTOM_CARDS` |
| 1801 | `RESULT_BOTTOM_TEXT` | 文字 | `叫分N子 底牌 GxM`【T-12】 | —— |
| 1801 | `RESULT_BOTTOM_TEXT` | 文字 | `叫分{multiple}子 底牌 {grade1}x{bottom.multiple}` | —— |
| 1802–1804 | `RESULT_GLOW_LEFT/RIGHT/SELF` | 图片 ×3 | 三家分数光晕 | `GLOW_RESULT` 帧1赢/帧2输 |
| 1805–1807 | `RESULT_SCORE_LEFT/RIGHT/SELF` | 文字 ×3 | 三家总分(`aset.seatlist[i].grade`) | —— |
| 1808–1810 | `RESULT_JF_LEFT/RIGHT/SELF` | 文字 ×3 | 捡分子数得分 `grade_jf`【T-13】 | —— |
@@ -1081,7 +1140,7 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| 1822 | `AWARD_BOX_RIGHT` | **容器** | 右上家算奖牌容器 | —— |
| 1823 | `AWARD_BOX_SELF` | **容器** | 自己算奖牌容器 | —— |
| 1824 | `AWARD_CARD_TPL` | **模板** | 算奖牌模板(编辑器里放好、默认隐藏,只作样式来源) | `CARD_FACE_L` |
| 1825–1827 | `AWARD_COUNT_LEFT/RIGHT/SELF` | 文字 ×3 | 奖数 `naward` 等【T-23】 | —— |
| 1825–1827 | `AWARD_COUNT_LEFT/RIGHT/SELF` | 文字 ×3 | **奖数**,与该家的算奖牌**并列显示**(取 `aset.seatlist[i].naward`;是否另列 `chongguan`/`wang` 明细见下) | —— |
**生成方式**:
@@ -1462,56 +1521,76 @@ codes/config/
## 7. 待补充清单
### 7.1 待出设计稿的界面
> **状态汇总**(2026-08-25):待确认规格 29 项**已答 27 项**,剩 T-9(亮牌条文案)与 T-20(音效,已明确后续补充)。
> 新增 **§7.5 服务端待补项**——3 条需要改服务端才能实现的前端需求。
### 7.1 待出设计稿的界面(后续统一补充)
| 编号 | 界面 | 所属 | 说明 |
| --- | --- | --- | --- |
| D-1▲ | 建房规则选项区 | §1.1 | **已有样式范式**(他游戏参考图)+ 内容已定(4 行);仍缺**二七王自己的定稿**与在平台建房界面里的确切尺寸/位置 |
| D-1▲ | 建房规则选项区 | §1.1 | 样式范式与内容已定(3 类别 / 4 选项组),缺二七王定稿与在 Layer 27 内的确切尺寸位置 |
| D-2 | 70 分暗牌公示(3 秒) | §1.4 | 全场可见的暗牌展示 + 倒计时 |
| D-3 | 甩错提示 | §1.7 | 整套收回、只出最小一张的表现 |
| D-4 | 报无主提示 | §1.7 | 玩家主牌出空时的全场提示 |
| ~~D-5~~ | ~~踩 / 没分 / 有分 气泡与发起入口~~ | §1.7 | **已定**:入口 = 底栏三按钮(占庄标区,闲家时显示);气泡 = 3×2 六帧、按头像相对位置选朝向 |
| ~~D-5~~ | ~~踩 / 没分 / 有分 气泡与入口~~ | §1.7 | **已定**:入口 = 底栏三按钮(占庄标区);气泡 3×2 六帧、按头像相对位置选朝向 |
| D-6 | 明牌面板 | §1.7 | 另两家未出主牌的具体牌面 |
| D-7 | 出牌历史面板 | §1.7 | 按轮次 × 3 家 |
| D-7 | 出牌历史面板 | §1.7 | 按轮次 × 3 家;另需 `上一轮` 的单轮视图 |
| D-8 | 判定结果文字(大光/小光/过庄/升N级/投降) | §1.8 | 结算时的判定展示 |
| D-9 | 大局总结算 | §1.9 | 三家 × N 局成绩表 |
| D-10 | 解散结算 | §1.10 | **投票界面平台全包**(Layer 420),只剩结算面板,可直接复用 D-9 |
| D-11 | 准备标识 | §1.12 | 或复用平台准备标识;**`下一局` 按钮已有图**(§1.8),准备状态标识仍缺 |
| ~~D-12~~ | ~~左上家已出牌区~~ | §2.3 | **已定**:与右上家左右镜像,无需单独设计稿 |
| D-10 | 解散结算 | §1.10 | 投票界面平台全包(Layer 420),只剩结算面板,可复用 D-9 |
| ~~D-11~~ | ~~准备标识~~ | §1.12 | **已定**:用平台准备标识,子游戏不做 |
| ~~D-12~~ | ~~左上家已出牌区~~ | §2.3 | **已定**:与右上家左右镜像 |
| **D-13** | **`扣底` 按钮的查看面板** | §2.6 | 新增:庄家查看自己埋的 8 张底牌;可与 `底牌`(看暗牌)复用同一个「8 张牌」展示面板,仅数据源不同 |
### 7.2 待确认的规格问题
**仍未决(2 项)**
| 编号 | 问题 | 影响 |
| --- | --- | --- |
| ~~T-1~~ | ~~逆时针方向:下家是 `(seat+1)%3` 还是 `(seat+2)%3`~~ | **已定论**,见 §0.1:`class.paiju.js:264` `get_nextseat = (seat+1)%3`,下家显示在右上 |
| ~~T-2~~ | ~~解散投票界面是平台提供还是子游戏自建?~~ | **已定论**:**平台全包**(`Layer 420 CheckFree_Layer` + `GameUI.openCheck` + `Desk.*_free_room` 一族),子游戏只处理解散结算包 |
| ~~T-3~~ | ~~平台创建房间界面在哪个图层?~~ | **已定论**:**Layer 27 `CreateRoom_Layer`**(`Layer 28 SeniorOptions_Layer` 是代开房/抽水,与玩法规则无关) |
| **T-4** | 是否需要发牌动画? | 动画配置与工期 |
| **T-5** | 手牌排序规则(主牌左/右、副牌花色顺序);选主后重排的动画表现 | 布局算法 |
| **T-6** | 叫分档位按钮第 3 帧(参考图第二行橙色)的含义 | `BTN_CALL_SCORE` 帧数 |
| **T-7** | 选主按钮中央大数字是该花色**张数**还是别的?(design 只要求显示对子数) | `MAIN_SUIT_COUNT_*` 的取值 |
| **T-8** | 埋牌图中牌面上的 `拖` 圆标 / ⭐ / 🔵 是什么?「提示」功能的分组标记? | `MARK_CARD_GROUP` 帧数与含义 |
| **T-9** | 亮牌条文案 `主牌对子: 1对` 与 design §8.2 的四类信息(主/王/7/2)对应关系 | 文案模板 |
| **T-10** | 出牌阶段是否保留 `提示` 按钮?提示什么? | 精灵 1712 是否需要 |
| **T-11** | 牌型标签种类:除单张/对子/拖拉机/甩牌外,是否还要标「毙」「垫」「混合出牌」? | `LABEL_CARDTYPE` 帧数 |
| **T-12** | 结算底牌文案 `叫分2子 底牌 20x0` 的完整规则(未扣底时怎么显示) | 文案模板 |
| ~~T-13~~ | ~~结算面板两个小字标签的正式文案~~ | **已定论**:`牌局分`(`grade_jf`) / `算奖分`(`grade_aw`),见更新后的 `小局结算.png` |
| ~~T-14~~ | ~~结算面板是否展示算奖明细~~ | **已定论**:走独立的**算奖牌型叠加层**(`算奖牌型` 按钮打开),见 `算奖牌型显示.png` |
| **T-15** | 准备状态用平台标识还是自建? | D-11 |
| **T-16** | 顶部 `抓分` 列角标:早期图为 `1子`、三张新图均为 `1倍`。**推定为 `1倍`**,但语义待定:是 `aset.upgrade` 判定倍率,还是 `bottom.multiple` 扣底倍数? | 精灵 1008 的数据源 |
| ~~T-17~~ | ~~倒计时归零后显示 `0` 还是隐藏?~~ | **已定**:**停在 `0` 继续显示**。协议 §0.3 明确归零不触发任何动作、牌局原地等待;隐藏会被误读为「已超时/已跳过」 |
| **T-18** | 底栏 `底牌` 按钮:庄家点了看自己埋的 8 张?闲家点了看什么?未埋牌时呢? | 交互定义 |
| **T-19** | 数字用图片精灵(多帧)还是文字精灵(`setText`)? | 是否需要资源 631–634 |
| **T-20** | **整套音效**:清单、是否需要语音包、是否分男女声 | §4 全节 |
| ~~T-21~~ | ~~手牌 `fan` 的 `spacingMin`/`spacingMax` 取值~~ | **已按参考图算定**:单排 `spacingMax:0, spacingMin:-78`(36 张时露 32px,与参考图实测 33px 吻合);埋牌双排 `spacingMin:-65`。见 §6.7 |
| **T-22** | 算奖牌型叠加层的关闭方式(再点按钮 / 点遮罩 / 独立关闭钮) | §1.8 交互 |
| **T-23** | 算奖叠加层是否并列显示奖数(`chongguan` / `wang` / `naward`) | 精灵 1825–1827 是否需要 |
| ~~T-24~~ | ~~算奖牌 `cards` 超过 8 张时怎么排~~ | **已定论**:改用**精灵复制**(§5.6a),张数不设上限,`fan` 自适应压缩 |
| ~~T-25~~ | ~~`line` 是否支持「逐项不等宽」~~ | **已定**:支持。`line` 增加可选 `items:[{width},…]`,给出时逐项取宽、不给时用统一 `itemWidth`。见 §6.1 |
| **T-26** | 自己发的提示是否需要本地回显气泡(协议不回执,默认看不到) | §6.6 SELF 气泡 |
| ~~T-27~~ | ~~埋牌双排分行策略~~ | **已定**:上排 `floor(n/2)`、下排 `ceil(n/2)`(36 张即 18/18)。参考图的 17/19 无规则依据,按 mock 处理 |
| ~~T-28~~ | ~~两份布局配置的坐标一致性~~ | **已定**:`EQW_Layout` 为权威,`01_SubGame_modify.js` 配置区手工同步并加注释;一致性列入验收清单(§7.3 第 10 条) |
| **T-29** | 建房界面的整体位置与可用尺寸(依赖平台创建房间界面,与 T-3 同源) | §6.9b 的 anchor 起点 |
| **T-9** | 亮牌条文案 `主牌对子: 1对` 与 design §8.2 四类信息(主牌总数/对子数/拖拉机数、王数、7 数、2 数)的对应关系与组合展示格式 | 文案模板;`liangpai` 各字段怎么拼成一行 |
| **T-20** | **整套音效**:清单、是否需要语音包、是否分男女声、倒计时提示音从第几秒起 | §4 全节;**已明确「等待后续补充」** |
**已定论(27 项)**
| 编号 | 结论 |
| --- | --- |
| ~~T-1~~ | `class.paiju.js:264` `get_nextseat = (seat+1)%3`,下家显示在右上(§0.1) |
| ~~T-2~~ | 解散投票**平台全包**(Layer 420 + `GameUI.openCheck` + `Desk.*_free_room`),子游戏只处理结算包 |
| ~~T-3~~ | 建房界面 = **Layer 27 `CreateRoom_Layer`**(Layer 28 是代开房/抽水,无关) |
| ~~T-4~~ | **需要发牌动画**:从右往左铺开(§1.2) |
| ~~T-5~~ | 见下方「T-5 手牌排序」小节 |
| ~~T-6~~ | 叫分按钮**按档位位置固定三档配色**(浅黄/金黄/橙)+ 灰=已叫过;按钮底与角标底各 4 帧(§1.3) |
| ~~T-7~~ | 选主按钮中央大数字 = 该花色**张数**,右上角标 = 该花色**对数**(§1.5) |
| ~~T-8~~ | 手牌标记 3 帧:`拖`=拖拉机 / 橙星=**正2正7** / 蓝星=**其他主牌**;前端据 `flower` 本地标注(§1.6) |
| ~~T-10~~ | 出牌阶段**必有** `提示` 按钮;另需服务端下发「应选中的牌」以实现自动选中 → **S-2** |
| ~~T-11~~ | 牌型标签**只做「毙」一种**;数据层保留 毙/垫/混合出牌 三种标识备扩展,当前不出图 |
| ~~T-12~~ | `底牌 20x0` 的 `x` 后面是**扣底倍数**(`bottom.multiple`);未扣底显示 0(§1.8) |
| ~~T-13~~ | 结算两个小字 = `牌局分`(`grade_jf`) / `算奖分`(`grade_aw`) |
| ~~T-14~~ | 算奖明细走独立的**算奖牌型叠加层** |
| ~~T-15~~ | 准备标识**用平台的** |
| ~~T-16~~ | `抓分` 角标 = **当前抓分的倍数**;协议现无此字段 → **S-1** |
| ~~T-17~~ | 倒计时归零**停在 `0`** 不隐藏 |
| ~~T-18~~ | 底栏 5 按钮:`上一轮`/`出牌历史`/`明牌`/`扣底`(庄家看底牌)/`底牌`(**看暗牌**);**术语陷阱见 §2.6**;暗牌可见性 → **S-3** |
| ~~T-19~~ | 数字**一律用图片多帧精灵**(参考图数字带描边渐变,文字精灵做不出),资源 631–634 全部要出 |
| ~~T-21~~ | 手牌单排 `spacingMin:-78`、双排 `-65`(§6.7) |
| ~~T-22~~ | 算奖叠加层:**全屏半透明遮罩本身即关闭热区,点击遮罩关闭**,不另设关闭钮 |
| ~~T-23~~ | 叠加层**需要并列显示奖数**(以 `naward` 为主) |
| ~~T-24~~ | 算奖牌用**精灵复制**,张数不设上限 |
| ~~T-25~~ | `line` 支持可选 `items` 逐项不等宽 |
| ~~T-26~~ | 自己发的提示**需要本地回显**(受控例外,理由见 §1.7) |
| ~~T-27~~ | 埋牌双排 `floor(n/2)` / `ceil(n/2)` |
| ~~T-28~~ | `EQW_Layout` 为权威,配置区手工同步 + 验收检查 |
| ~~T-29~~ | 建房整体位置尺寸随 Layer 27 定,编辑器建好后回填 |
#### T-5 手牌排序(已定)
前端排序**对齐服务端主牌序**(design §3),不另造一套——服务端 `class.arith.js` 的 `order_cards(mainflower, cards)` 即权威顺序(从大到小),下发的 `pushlist` 等字段也已按此排好。
- **选主前**(叫分阶段):`flower` 未定,只有固定主牌(双王 + 全部 2、7)是主牌,其余按花色分组。
- **选主后**:`flower` 确定,正 2 / 正 7 升格、主花色普通牌并入主牌段,**必须整体重排**。重排动画暂不做(先直接重画),后续可加。
- 主牌段落在**左侧**——排序从大到小、主牌最大,自然靠左。
### 7.3 验收检查清单
@@ -1531,6 +1610,9 @@ codes/config/
| 10 | `EQW_Layout` 与 `Game_Modify.PLAYER_INFO_LAYOUT` 的头像框坐标一致 | §6.11 |
| 11 | 丢弃 `this.data`、仅凭最近一次服务端快照重画,界面完全一致 | 前端 05 §6.2 |
| 12 | 所有布局参数都在配置文件里,业务代码无裸值 | §6 |
| 13 | **暗牌 / 底牌未写反**:`底牌` 按钮显示暗牌、`扣底` 按钮显示底牌;内部变量用 `anCards`/`diCards` 区分 | §2.6 术语陷阱 |
| 14 | 叫分按钮配色按**档位位置**取(非按子数),已叫过的档位置灰 | §1.3 |
| 15 | 自己发的提示本地回显后,收到失败回包时能撤掉 | §1.7 |
### 7.4 开发前置条件
@@ -1548,6 +1630,75 @@ codes/config/
- **布局配置骨架与布局求解器**(§6)——`kind` 五型的参数结构与求解逻辑是设计决定、不依赖具体数值,可先写好并用占位坐标跑通,等设计稿到位只改配置值
- `shared/` 同步机制
### 7.5 服务端待补项(需改服务端才能实现的前端需求)
以下 3 条来自本轮确认的需求,**当前协议/服务端不支持**,需在服务端补齐后前端才能实现。三条都要同步更新 `packet_protocol.md`。
#### S-1 · 「当前抓分的倍数」字段(对应 T-16)
**需求**:顶部信息条 `抓分` 列的角标要显示「当前抓分的倍数」(参考图 `45 [1倍]`)。
**现状**:服务端**没有任何**下发此值的字段。`chupai1/2/3` 只带 `grade`(本轮闲家得分),倍数类字段(`aset.upgrade`、`bottom.multiple`)**都要到结算包才有**——出牌过程中前端拿不到。
**建议实现**:服务端已有 `class.arith.js` 的 `get_upgrade(call, grade, q)`(返回带符号判定倍率:3 大光 / 2 小光 / 1 过庄 / −N 升 N 级),出牌过程中随时可算。在 `chupai1/2/3`(以及重连包 `PushCards`)里增加一个字段,例如:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `curmultiple` | 整数 | 按当前累计捡分实时算出的判定倍率绝对值,供顶部 `抓分` 角标显示 |
**需先确认语义**(本条尚未完全定死):角标要显示的到底是
(a)**当前捡分对应的判定倍率**(大光×3 / 小光×2 / 过庄×1 / 升N级×N)的实时预览——即「若现在结束是几倍」;还是
(b)别的口径。
按字面「当前抓分的倍数」以及 `get_upgrade` 现成可用,(a) 最可能。**确认 (a) 之后即可动工**。
> 前端**不得自算**这个值——判定倍率属服务端权威裁定(红线:服务器权威、前端只显示不自算)。
#### S-2 · 下发「应当选中的牌」(对应 T-10)
**需求**:某些情况下(跟牌存在必出牌、或唯一合法出法)服务端下发应选中的牌,前端收到后自动选中。
**现状**:`chupai1/2/3` 下发包里没有此字段。
**好消息——算法已存在,只差下发**:`class.arith.js:737` `get_followcard(mainflower, inhandcards, startcount, startflower, startcardtype)` 已经返回
```
{ mustcard: 必出的牌列表, cancard: 可出的牌列表, cantype: 可出牌需要的牌型 }
```
目前只在 `class.arith.js:1055` 用于内部合法性校验,**从未下发**。建议在 `chupai1/2/3` 里**按座位差异化**下发给「下一个出牌者」:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `mustcard` | 数组 | 该座位本轮的必出牌 id 列表;**只发给 `nextseat` 对应的那一家**,其他两家不带 |
**下发面注意**(红线:发全 ≠ 发多):`mustcard` 是**该玩家自己手牌**的子集,只发给他本人不泄露任何信息;**绝不可整表下发三家的 `mustcard`**——那等于把另两家的手牌结构透露出去。
**不违反「请求包只带意图」**:方向是服务端 → 前端的建议,前端仍只回传 `cards`,合法性最终由服务端校验。
#### S-3 · 底栏「底牌」按钮的暗牌可见性(对应 T-18)⚠️ 与 design 冲突
**需求**:底栏 `底牌` 按钮让**所有人**查看发牌时的 8 张**暗牌**。
**冲突**:design §4 明确——
> **非 70 分坐庄**:桌面 8 张暗牌**只有庄家可见**,庄家查看后摸入手牌。
> **70 分坐庄**:摸暗牌之前先向所有玩家亮出 3 秒。
协议也与之一致:`shangzhuang.bottomcards` **庄家恒有、闲家仅 70 分坐庄时才有**;重连包 `ChooseMain` / `BuryCards` / `PushCards` 的 `bottomcards` 全部标注「**只有庄家有此属性**」。
**因此闲家在非 70 分局根本收不到暗牌数据,「所有人可查看」在当前协议下无法实现。** 且这属于服务端红线「**下发即泄露**——包到了客户端就能被抓包看到,『前端拿到但不渲染』不算防护」,不能靠前端自觉。
**需要在三种方案里选一个**:
| 方案 | 含义 | 影响 |
| --- | --- | --- |
| **(a) 限定可见范围**(推荐) | `底牌` 按钮只在**能合法看到暗牌**时可用:庄家全程可用;闲家**仅 70 分坐庄局**(已公示过)可用,其余局按钮置灰或不显示 | 协议无需改,只改前端显隐条件;不破坏 design |
| **(b) 局末开放** | 牌局结束后向所有人下发暗牌,`底牌` 按钮在结算阶段才可用 | 需在 `jiesuan` 包补暗牌字段;不影响对局中的信息对称 |
| **(c) 改规则** | 暗牌对所有人全程可见 | **与 design §4 直接冲突**,需先改 design;且削弱 70 分公示这条规则的意义 |
**在此确认前,前端按 (a) 实现**(最保守、不需要改服务端、不违反任何红线),并在代码里留注释指向本条。
---
## 附:本文与其他文档的关系
@@ -1557,6 +1708,6 @@ codes/config/
| `server/games/erqiwang/docs/design/design.md` | **玩法规则权威源**。本文引用其条款,不重新定义规则 |
| `server/games/erqiwang/docs/protocol/packet_protocol.md` | **协议字段权威源**。本文的「渲染什么」全部溯源到具体字段 |
| `docs/client/development-guide/` | **前端规范权威源**。ID 范围、组件范式、红线均以其为准 |
| `docs_dev/uiref/*.png` | 参考图,12 张(11 张牌桌态 + 1 张建房**样式范式**,后者内容属他游戏、不可照抄) |
| `docs_dev/uiref/*.png` | 参考图,13 张(11 张牌桌态 + 叫分按钮面板 + 1 张建房**样式范式**,末者内容属他游戏、不可照抄) |
| `client/js/gameabc-framework/ui/AlignmentUtils.js` | **布局求解的框架能力**。§6 的配置参数名与其入参一一对应,配置可原样喂入 |
| `client/js/gameabc-framework/templates/*.template.js` | 常量文件写法蓝本。本清单落地为常量文件时按其格式 |