二七王:D-1..D-9 定稿落地,新增 S-6/S-7 两个服务端缺口

据新参考图(大局结算.png、强甩失败提示.png)与本轮确认,设计稿只剩 D-8:
- D-1 建房:子游戏只做「类别标签 + 选项」内容区,弹窗背景/标题/关闭/确认
  由平台提供,§5.8 相应不再列弹窗外壳。
- D-2 底牌 3 秒展示:复用庄家翻开底牌那套 UI,不要倒计时;非 70 分只有
  庄家可见、70 分全场可见——同一套界面只差可见范围,故砍掉原先规划的
  独立公示面板(资源 655 与精灵 1757/1758 作废)。
- D-3 甩错:桌面中央深色半透条「强甩失败」,不做收回动画。
- D-4 报无主:不做全场提示(资源 652、精灵 1753 作废),只让他家
  主N/对N 角标出现。
- D-6 明牌 / D-7 出牌历史:版式都照算奖牌型显示.png(遮罩 + 每家一排牌),
  与算奖叠加层共用同一套渲染。出牌历史【不按轮次】,按玩家聚合并用主牌序
  排序——前端摊平 pushlist 即可,无需改服务端。
- D-9 大局结算:玩家栏用精灵复制(新增 §5.7a)。

同时更正了一处我此前的理解错误:左侧那条「主牌对子: 1对」是【自己的主牌
统计、有手牌时常显】,不是 design §8.2 的庄家亮牌 liangpai。§2.5 已重写为
「自己的主牌统计 vs 他家的报无主角标」两套并列,并指出 liangpai 目前没有
界面位置(T-30)。

新增两个服务端缺口:
- S-6(阻塞 D-9):大局结算要显示「基础分/算奖分/傍王分」,而 account 只有
  累积得分与每局得分。傍王分尤其拆不出来——naward = 常规算奖 + 傍王王数,
  两者求和后才代入 grade_aw 公式,事后无法反推各自占比。建议把 N 拆成两个
  分量分别代入同一公式,相加应等于现 grade_aw(可作回归断言)。
- S-7(有泄露口子):明牌按钮条件收紧为「自己是无主玩家」,与 design §9.3
  的「全体三人」及服务端 have_baofu() 校验都不一致。前端先按严格规则显隐,
  但服务端校验未改前,有主牌的玩家伪造请求仍能拿到明牌数据。

另:修复文末附录丢失——三次提交前的一个切片脚本把它挤掉了,现已按当前
状态恢复并补充 SpriteCopyUtils 一行。新增 T-30/T-31、验收清单增至 17 条。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-25 21:32:38 +08:00
co-authored by Claude Opus 5
parent 4aab71e6a3
commit be06d3bf58
3 changed files with 234 additions and 55 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+234 -55
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`,共 13 张,设计分辨率 1280×720。
> **参考图**:`./uiref/*.png`,共 15 张,设计分辨率 1280×720。
---
@@ -381,6 +381,10 @@ var CARD_BACK_FRAME = 55; // 牌背(二七王固定用牌背
| 房主扣卡 | 房主 2 张 | 房主 4 张 |
| AA 每人扣卡 | 每人 1 张 | 每人 2 张 |
#### 职责边界(D-1 已定)
**子游戏只管「类别标签 + 选项」这一块内容区**。弹窗背景、标题、关闭按钮、确认按钮**都由平台提供**——所以 §5.8 只列内容区的精灵,不要重复建弹窗外壳。
#### 实现要点
- 交互:点击平台约定的 25 号精灵 → 拼 `roomtype` 串 → `Net.Send_create_room({agentid, playerid, gameid, roomtype})`。
@@ -501,7 +505,15 @@ EQW_RoomOptions = {
| 常规坐庄 | 无 `ancard3s` | 庄家收到 `bottomcards`(8 张)与 `cards`(36 张)→ 底牌翻正面短暂展示后并入手牌;闲家**收不到** `bottomcards`,底牌区直接收起 |
| **70 分坐庄** | `ancard3s === 1` | **全场三家都收到 `bottomcards`** → 8 张底牌翻正面**向所有玩家亮 3 秒**(design §4/§7.1),3 秒后庄家摸入手牌、闲家侧收起 |
- **70 分亮 3 秒** 【待设计】:需要一个覆盖提示(如「70 分坐庄 · 底牌公示」标题 + 3 秒倒计时),图层 105 群组 230。
- **底牌 3 秒展示(D-2 已定)**:**复用「庄家自己翻开底牌」那套 UI**(见 `叫分确定庄.png`——中央 8 张牌由背面翻正面),**不要倒计时**。
两种情形用的是**同一套界面**,唯一差别是**谁能看到**:
| 情形 | 谁能看到这 3 秒 |
| --- | --- |
| 非 70 分坐庄 | **只有庄家自己**(闲家侧那 8 张保持背面/直接收起) |
| **70 分坐庄** | **全场三人都能看到** |
因此**不需要单独的「公示」面板**,只是同一套翻牌表现的可见范围不同——精灵复用 §5.3 群组 211(底牌区),无需新增。
- 上庄后统一更新:顶部信息条 `叫分` 列 = `grade`、角标 = `multiple`;`banker` 对应玩家位显示 **`庄` 印章**(含底栏自己位)。
- 三处 `庄` 印章是三个独立精灵(左上 / 右上 / 底栏),按 `banker` 显隐其一。
- **重连不重放**:协议明确 70 分的 3 秒亮牌是上庄时的一次性事件,`deskinfo` 重连**不重放**——前端不得在重连时补播。
@@ -587,7 +599,7 @@ EQW_RoomOptions = {
| `seat` / `cards` | 在该家已出牌区摆牌(资源 `CARD_FACE_M`) |
| `cardtype` | 牌型标签(`>100` 单张 / `>200` 对子 / `>300` 拖拉机) |
| `shuai` | **甩牌**分量构成 `{tractors:[], pairs, singles}`——**甩牌的真实结构只能读它,不得据 `cardtype` 反推**(协议 §11 明确警告) |
| `shuaicuo` | **甩错**标志(= 1):整套甩牌收回,本轮只强制打出最小一张(design §5.4.5);需要一个「甩错」提示表现【待设计】 |
| `shuaicuo` | **甩错**标志(= 1):整套甩牌收回,本轮只强制打出最小一张(design §5.4.5)。表现见下方「甩错提示」 |
| `flower` / `count` | 本轮首出花色与张数,供本地跟牌提示 |
| `nextseat` / `countdown` | 倒计时移到下一家 |
| `seatlist` | **仅可查牌模式**:三家牌况,用于 §2.2 的 `主N` / `对N` 角标 |
@@ -632,22 +644,36 @@ EQW_RoomOptions = {
**箭头朝向按头像相对气泡的位置选**:头像在气泡**左**侧 → 用**箭头向左**的帧;头像在气泡**右**侧 → 用**箭头向右**的帧。三个座位的取用见 §6.5。
**报无主**(design §5.4.2 / §9):
- 任一玩家主牌出空 → 服务端 `baozhu = 1` 并整表刷新 `seatlist`;
- **仅可查牌模式**下,为**全体三人**显示另外两家的主牌数量与对子数(§2.2 的 `主N` / `对N` 角标);
- 需要一个「报无主」提示表现【待设计】。
**甩错提示(D-3 已定)** 【图:强甩失败.png】:
- 收到 `shuaicuo === 1` 时,在桌面中央弹一条**深色半透条**:**`强甩失败`**(白字,x≈462 y≈172 w≈363 h≈50)。
- **不做收回动画**——甩出的牌不演"飞回手里",直接按服务端下发的 `cards`(那张被强制打出的最小主牌单张)落牌即可。
- 提示条自动淡出,时长进配置。
**报无主(D-4 已定)**:
- **不做全场提示**。报无主本身没有独立的提示表现。
- 唯一的界面变化是:**他家的 `主N` / `对N` 角标出现**(服务端 `baozhu=1` 并整表刷新 `seatlist`,仅可查牌模式)。详见 §2.5。
- 自己的主牌统计条**本来就一直显示**,不因报无主而变化。
**明牌**(design §9.3):
- 仅可查牌模式 + 出牌阶段 + 已报无主时,底栏 `明牌` 按钮可用;
- 点击 → 发 `mingpai { seat }` → 收 `mingpai { seat, others: [{seat, zhucards}] }` → 弹出面板展示另两家全部未出主牌的**具体牌面**;
- **按钮显示条件(D-6 已定)**:**勾选了可查牌模式 + 自己是「无主玩家」**(自己的主牌已出空)时才显示 `明牌` 按钮。
> ⚠️ 这比 design §9 更严格,且与服务端当前校验不一致,见 §7.5 **S-7**。design 原文是「一旦有玩家的主牌全部打空……界面上会出现一个『明牌』按钮」,读作**全体三人**都出现;服务端 `mingpai` 的受理条件也是 `have_baofu()`(任一玩家报无主)。按新规则则应改成「**请求者本人**无主」。
- 点击 → 发 `mingpai { seat }` → 收 `mingpai { seat, others: [{seat, zhucards}] }` → 弹出面板展示另两家全部未出主牌的**具体牌面**。
- **再点一次取消是纯前端开关**,不再请求服务端(协议 §13.6)。
- 面板:图层 305,群组 243【待设计】。
- **展示形式(D-6 已定)**:**照 `算奖牌型显示.png` 那一套**——半透黑遮罩 + 两家各一排牌(`CARD_FACE_L`、`fan` 重叠排列),点遮罩关闭。与算奖牌型叠加层是同一种版式,可复用同一套渲染逻辑,仅数据源与座位数不同(明牌是**另两家**,算奖是**三家**)。
- 面板:图层 305,群组 243;牌**用精灵复制**(张数不定,同 §5.6a 的判据)。
**出牌历史**(design §9.1):
- 仅可查牌模式;底栏 `已出牌` / `上一轮` 按钮;
- 数据来自重连包 `pushlist`(按轮次 × 3 家),**出牌过程中前端需自行累积**(`chupai1/2/3` 逐包记录);
- 面板:图层 304,群组 242【待设计】。
- **不可查牌模式下这两个按钮必须整体不出现**(design §9)。
- 仅可查牌模式;底栏 `出牌历史` / `上一轮` 两个按钮(§2.6)。
- 数据来自重连包 `pushlist`(按轮次 × 3 家),**出牌过程中前端需自行累积**(`chupai1/2/3` 逐包记录)。
- **展示形式(D-7 已定)**:**不按轮次分组**。把每家出过的牌全部**摊平聚合到该玩家名下**,再**按手牌那套主牌序排序**(同 §7.2 T-5 的 `order_cards` 顺序),三家各显示一排——版式**照 `算奖牌型显示.png`**(遮罩 + 三家各一排牌)。
> 前端自行摊平 + 排序即可,**不需要改服务端**:`pushlist` 已含全部轮次数据,且各轮内部已按本局主牌花色排好。
- `上一轮` 按钮则只取**最近一轮**的三家出牌(`pushlist` 末尾一项),版式同上。
- 面板:图层 304,群组 242;牌**用精灵复制**(张数不定)。
- **不可查牌模式下这两个按钮整体不出现**(design §9)。
**失败回包**(协议 §0.2):任何请求被拒都会回同名 rpc 的 `{ success: false, errcode }`。前端一律 `if (!data.success)` 判断,据 `errcode` 提示:
@@ -735,11 +761,26 @@ EQW_RoomOptions = {
**投降结算**:只有 `aset`,无 `chupai`、无 `bottom` → 结算面板必须容忍**底牌区缺失**,不能因为读不到 `bottom.cards` 就崩或显示空框。
### 1.9 大局总结算 `account` 【待设计】
### 1.9 大局总结算 `account` 【图:大局结算.png】
- 仅当打到最后一局(`idx >= asetcount`)时,`jiesuan` 包才带 `account` 分组。
- 结构:`[[累积得分, [每局得分...]], ...]` 三家。
- 界面:图层 303,群组 241 —— 需要一张三家 × N 局的成绩表【待设计】。
- 界面:图层 303,群组 241。**三条玩家栏用精灵复制生成**(见 §5.7a)。
**版式**(弹窗面板,圆角):
| 区 | 内容 |
| --- | --- |
| 标题栏(青绿底) | `二七王-3人 房号{roomcode} 共{asetcount}局 {开战时间}` + 右上角关闭钮 |
| 玩家栏 ×3(按名次排序,行间分隔线) | 名次徽章 → 头像 → 昵称 / `ID:{playerid}`(两行)→ `基础分{n}` / `总得分{n}`(两行)→ `算奖分{n}` → `傍王分{n}` → 右侧大字总分 |
| 规则栏(浅绿底) | `规则:可查牌、傍王、爬坡算子` —— 据 `roomtype` 位串拼出已启用项 |
| 面板外底部 | `再来一局`(黄) `分享好友`(蓝) `复制战绩`(蓝) `退出房间`(红) |
- **名次徽章**:3 帧(1 金 / 2 橙 / 3 蓝,花瓣形),按总分排名取帧。
- **总分大字**:正分橙色带 `+`、负分蓝色带 `-`,复用 §3.6 的 `NUM_RESULT_WIN` / `NUM_RESULT_LOSE`。
> ⚠️ **四项分数当前协议给不出,需服务端补**,见 §7.5 **S-6**:
> `account` 只有「累积得分 + 每局得分数组」,没有「基础分 / 算奖分 / **傍王分**」这三项分解。
> 尤其 **傍王分无法从现有字段拆出**——`naward = 常规算奖 + 傍王王数` 已经把两者合并,`grade_aw` 是按合并后的 `N` 算出来的。
### 1.10 解散结算 `free_room` 【待设计】
@@ -847,12 +888,29 @@ EQW_RoomOptions = {
- **归零后停在 `0` 继续显示,不隐藏**:协议 §0.3 明确归零不触发任何动作、牌局原地等待该玩家;隐藏会被误读为「已超时/已跳过」。
- 精灵:群组 224,号段 1150–1159。
### 2.5 亮牌信息条 【图:多张】
### 2.5 主牌统计(自己)+ 报无主标签(他家)【图:多张】
位置 x≈35–245, y≈400–435,深色半透圆角条,内容见 §1.6 的 `liangpai` 表。
**这是两套不同的东西,别混为一谈**:
| 谁的 | 显示在 | 何时显示 | 数据来源 |
| --- | --- | --- | --- |
| **自己的主牌统计** | 手牌**上方偏左**(x≈35 y≈400 那条) | **有手牌时始终显示**,与报无主无关 | 前端据 `flower` 本地统计自己手牌 |
| **他家的主牌数 / 对数** | 左上、右上家**头像下方**的 `主N` `对N` 角标 | **仅可查牌模式 + 已报无主**(design §9) | `seatlist[seat][4]` = `[剩余主牌数, 剩余主对数]` |
**自己的主牌统计条**(群组 206):
- 文案 **`x对 x主`**(暂定,见 §7.2 T-9),例如 `3对 12主`。
- **始终显示、本地计算**——自己的手牌自己完全可见,不需要服务端下发,也不受查牌模式与报无主影响。
- 参考图里的 `主牌对子: 1对` 即此条。
**他家的 `主N` / `对N` 角标**(群组 202 / 203,结构见 §2.2):
- 一个**图片底 + 一个文字精灵**,两家各一组。
- 初始 `[-1,-1]` 时隐藏;一旦全场有人报无主,服务端整表刷新 `seatlist` 并下发,两家的角标同时出现。
- 不可查牌模式下**永不出现**。
> ⚠️ **协议里的 `liangpai` 字段目前没有对应的界面位置**。它是 design §8.2 的「庄家亮牌」——庄家埋牌后手牌达标时向**闲家**亮出的统计(主牌总数/对子数/拖拉机数、王数、7 数、2 数),与上面两者都不是一回事。服务端照常下发,前端**当前不渲染**。展示位置待定,见 §7.2 **T-30**。
- **仅闲家 + 可查牌模式 + 庄家达标**时显示;不查牌模式下**一律不出现**。
- 精灵:群组 206,号段 1030–1039。
### 2.6 底栏 【图:全部】
@@ -948,6 +1006,11 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§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 |
| 543 | `BTN_ACCOUNT_AGAIN` | 大局结算 `再来一局` | 1 | 黄色渐变 | 190 × 60 |
| 544 | `BTN_ACCOUNT_SHARE` | 大局结算 `分享好友` | 1 | 蓝色渐变 | 190 × 60 |
| 545 | `BTN_ACCOUNT_COPY` | 大局结算 `复制战绩` | 1 | 蓝色渐变 | 190 × 60 |
| 546 | `BTN_ACCOUNT_EXIT` | 大局结算 `退出房间` | 1 | 红色渐变 | 190 × 60 |
| 547 | `BTN_ACC_CLOSE` | 大局结算标题栏右上角关闭 | 1 | —— | 36 × 36 |
### 3.4 标记类
@@ -961,6 +1024,7 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| 576 | `LABEL_CARDTYPE` | 牌型标签 | **1** | **只做「毙」一种**(前端只展示这一个标签)。数据层仍保留 毙 / 垫 / 混合出牌 三种标识以备扩展,但当前不出图、不显示 | 76 × 24 |
| 577 | `MARK_CARD_GROUP` | 手牌牌型标记 | **3** | **帧1=`拖`(拖拉机)帧2=橙色五角星(正2 / 正7)帧3=蓝色五角星(除正2正7外的其他主牌)** | 26 × 26 |
| 578 | `MARK_SELECTED` | 手牌选中标记 | 1 | 待定(也可只用上浮,不出图) | 待定 |
| 579 | `BADGE_RANK` | 大局结算名次徽章(花瓣形) | **3** | 帧1=第1名(金) 帧2=第2名(橙) 帧3=第3名(蓝) | 44 × 44 |
### 3.5 面板底图
@@ -974,7 +1038,11 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| 606 | `PANEL_BOTTOM_CARDS` | 结算底牌区面板底 | 1 | 420 × 148 |
| 607 | `GLOW_RESULT` | 结算分数光晕背景 | 2 | 帧1=赢(橙) 帧2=输(蓝) | 225 × 100 |
| 608 | `BAR_FOOTER` | 底栏深色条 | 1 | 1280 × 72 |
| 609 | `MASK_DIM` | 算奖牌型叠加层的半透黑遮罩(可复用于其他弹窗) | 1 | 1280 × 720(纯色可拉伸,出 1 张小图即可) |
| 609 | `MASK_DIM` | 半透黑遮罩(算奖牌型 / 出牌历史 / 明牌 共用) | 1 | 1280 × 720(纯色可拉伸,出 1 张小图即可) |
| 610 | `PANEL_ACCOUNT` | 大局总结算面板底(圆角浅灰绿) | 1 | 1100 × 540 |
| 611 | `BAR_ACCOUNT_TITLE` | 大局结算标题栏(青绿底,含圆角上沿) | 1 | 1100 × 56 |
| 612 | `BAR_ACCOUNT_RULE` | 大局结算底部规则栏(浅绿底,含圆角下沿) | 1 | 1100 × 62 |
| 613 | `LINE_DIVIDER` | 玩家栏之间的分隔线 | 1 | 1060 × 2 |
### 3.6 数字与符号
@@ -994,10 +1062,10 @@ y≈648–720 深色条。左侧头像/昵称/分数由平台渲染(§0.2)
| ID | 键名 | 用途 | 帧数 | 帧说明 | 尺寸 |
| --- | --- | --- | --- | --- | --- |
| 651 | `BUBBLE_TISHI` | 踩 / 没分 / 有分 提示气泡 | **6**(3×2) | **帧1=踩·箭头向左 帧2=没分·箭头向左 帧3=有分·箭头向左 帧4=踩·箭头向右 帧5=没分·箭头向右 帧6=有分·箭头向右**。帧序内层是 `tip`(1踩/2没分/3有分)、外层是箭头朝向 → `frame = (arrowRight ? 3 : 0) + tip`。**箭头向左 = 头像在气泡左侧**、箭头向右 = 头像在气泡右侧 | 待定 |
| 652 | `TXT_BAOZHU` | 「报无主」提示【待设计】 | 1 | 待定 | 待定 |
| 653 | `TXT_SHUAICUO` | 「甩错」提示【待设计】 | 1 | 待定 | 待定 |
| ~~652~~ | ~~`TXT_BAOZHU`~~ | **不需要**:报无主不做全场提示(D-4),只让他家 `主N`/`对N` 角标出现 | —— | —— | —— |
| 653 | `BAR_SHUAICUO` | **`强甩失败`** 提示条底(深色半透,白字居中) | 1 | 深色半透圆角条,文字用文字精灵 | 363 × 50 |
| 654 | `TXT_GRADE_FLOAT` | 捡分飘字底(`20分` 金色) | 1 | 待定 | 待定 |
| 655 | `PANEL_BOTTOM_3S` | 70 分底牌公示 3 秒的标题/边框【待设计】 | 1 | 待定 | 待定 |
| ~~655~~ | ~~`PANEL_BOTTOM_3S`~~ | **不需要**:底牌 3 秒展示复用庄家翻底牌那套 UI(D-2),仅可见范围不同 | —— | —— | —— |
### 3.8 建房选项
@@ -1142,12 +1210,12 @@ design 与协议均**未定义任何音效**。以下是按牌类游戏常规推
| 精灵 ID | 键名 | 类型 | 用途 | 资源 / 帧 |
| --- | --- | --- | --- | --- |
| 1750–1752 | `BUBBLE_TISHI_LEFT/RIGHT/SELF` | 图片 ×3 | 踩/没分/有分 气泡(按发送者位置) | `BUBBLE_TISHI` 帧 = tip |
| 1753 | `TXT_BAOZHU` | 图片 | 报无主提示【待设计】 | `TXT_BAOZHU` |
| 1754 | `TXT_SHUAICUO` | 图片 | 甩错提示【待设计】 | `TXT_SHUAICUO` |
| ~~1753~~ | ~~`TXT_BAOZHU`~~ | —— | **不需要**(D-4) | —— |
| 1754 | `BAR_SHUAICUO` | 图片 | `强甩失败` 提示条底 | `BAR_SHUAICUO` |
| 1759 | `TXT_SHUAICUO` | 文字 | `强甩失败` 文案 | —— |
| 1755 | `GRADE_FLOAT_BG` | 图片 | 捡分飘字底 | `TXT_GRADE_FLOAT` |
| 1756 | `GRADE_FLOAT_TEXT` | 文字 | `20分` | —— |
| 1757 | `BOTTOM_3S_PANEL` | 图片 | 70 分底牌公示框【待设计】 | `PANEL_BOTTOM_3S` |
| 1758 | `ANCARD_3S_TEXT` | 文字 | 公示标题 + 3 秒倒计时 | —— |
| ~~1757 / 1758~~ | ~~`BOTTOM_3S_*`~~ | —— | **不需要**(D-2:底牌 3 秒展示复用底牌区翻牌,仅可见范围不同) | —— |
### 5.6 Layer 302 · 小局结算(群组 240)
@@ -1218,14 +1286,51 @@ for (var i = 0; i < cards.length; i++) {
> 精灵段 1001–2999 有近 2000 个空位(§0.3),预置的 ID 开销不构成压力;复制只用在「数量真的不定」的地方,避免给每处都背上显式清理的负担。
### 5.7 Layer 303–306 · 其余弹窗 【待设计】
### 5.7a Layer 303 · 大局总结算(群组 241,**玩家栏用精灵复制**)
| 图层 | 群组 | 号段 | 内容 |
| --- | --- | --- | --- |
| 303 | 241 | 1900–1949 | 大局总结算 / 解散结算:三家 × N 局成绩表 |
| 304 | 242 | 1950–1999 | 出牌历史:按轮次 × 3 家的牌列表(建议用 `DynamicSpriteList`) |
| 305 | 243 | 2000–2049 | 明牌面板:另两家未出主牌的具体牌面 |
| 306 | —— | —— | 解散投票【T-2:是否平台提供】 |
三条玩家栏结构完全相同、仅数据不同,用 `SpriteCopyUtils` 复制生成;面板外壳与按钮预置。
| 精灵 ID | 键名 | 类型 | 用途 | 资源 / 帧 |
| --- | --- | --- | --- | --- |
| 1900 | `ACC_PANEL_BG` | 图片 | 面板底(圆角浅灰绿) | `PANEL_ACCOUNT` |
| 1901 | `ACC_TITLE_BAR` | 图片 | 标题栏(青绿底) | `BAR_ACCOUNT_TITLE` |
| 1902 | `ACC_TITLE_TEXT` | 文字 | `二七王-3人 房号… 共…局 …` | —— |
| 1903 | `ACC_BTN_CLOSE` | 图片 | 右上角关闭 | `BTN_ACC_CLOSE` |
| 1904 | `ACC_RULE_BAR` | 图片 | 底部规则栏(浅绿底) | `BAR_ACCOUNT_RULE` |
| 1905 | `ACC_RULE_TEXT` | 文字 | `规则:可查牌、傍王、爬坡算子` | —— |
| **1910** | `ACC_ROW_CONTAINER` | **容器** | 玩家栏的复制父精灵 | —— |
| 1911 | `ACC_TPL_RANK` | **模板** | 名次徽章 | `BADGE_RANK`,帧 = 名次 1–3 |
| 1912 | `ACC_TPL_AVATAR` | **模板** | 头像 | 运行时贴玩家头像 |
| 1913 | `ACC_TPL_TEXT` | **模板** | 通用文字(昵称/ID/各项分数,一行一个) | —— |
| 1914 | `ACC_TPL_SCORE_NUM` | **模板** | 右侧总分大字(数字精灵) | `NUM_RESULT_WIN` / `NUM_RESULT_LOSE` |
| 1915 | `ACC_TPL_DIVIDER` | **模板** | 行间分隔线 | `LINE_DIVIDER` |
| 1920–1927 | `ACC_BTN_*` | 图片×4 + 文字×4 | `再来一局` / `分享好友` / `复制战绩` / `退出房间` | `BTN_ACCOUNT_*` |
**tag 分段**(`SpriteCopyUtils.createTagManager`):`rank 1–20` / `avatar 21–40` / `text 41–140`(每行 6 条文字)/ `score 141–160` / `divider 161–180`。
**清理**:关闭面板时 `removeRange` 清掉各段。
> 解散结算复用本面板(协议 §14.1,解散包也带 `account`),只是 `aset` 各项为 0。
### 5.7b Layer 304 / 305 · 出牌历史与明牌(群组 242 / 243,**牌用精灵复制**)
两者版式相同(遮罩 + 每家一排牌,照 `算奖牌型显示.png`),差别只在数据源与家数,**共用同一套渲染**:
| 精灵 ID | 键名 | 类型 | 用途 | 资源 / 帧 |
| --- | --- | --- | --- | --- |
| 1950 | `HIST_MASK` | 图片 | 半透黑遮罩(兼关闭热区) | `MASK_DIM` |
| 1951–1953 | `HIST_BOX_LEFT/RIGHT/SELF` | **容器** ×3 | 每家一个牌容器 | —— |
| 1954 | `HIST_CARD_TPL` | **模板** | 牌模板 | `CARD_FACE_L` |
| 1955–1957 | `HIST_LABEL_LEFT/RIGHT/SELF` | 文字 ×3 | 座位标签(昵称 / 张数) | —— |
| 2000 | `MING_MASK` | 图片 | 明牌遮罩 | `MASK_DIM` |
| 2001–2002 | `MING_BOX_A/B` | **容器** ×2 | 另两家各一个容器(明牌只看他家) | —— |
| 2003 | `MING_CARD_TPL` | **模板** | 牌模板 | `CARD_FACE_L` |
| 2004–2005 | `MING_LABEL_A/B` | 文字 ×2 | 座位标签 | —— |
- **出牌历史**:三家都显示,数据 = `pushlist` 摊平聚合到各家、按主牌序排序(§1.7 D-7)。
- **上一轮**:同一套精灵,数据只取 `pushlist` 末尾一项。
- **明牌**:只显示另两家,数据 = `mingpai.others[].zhucards`(§1.7 D-6)。
> 解散投票由平台提供(Layer 420),子游戏无需精灵。
### 5.8 建房规则选项(群组 250,2050–2099,预置)
@@ -1562,26 +1667,29 @@ codes/config/
## 7. 待补充清单
> **状态汇总**(2026-08-25):待确认规格 29 项**已全部有结论**(T-9 亮牌文案暂定 `x对 x主`;T-20 音效明确后续补充)。
> **§7.5 服务端待补项 5 条已全部完成**:S-1 `curmultiple`、S-2 `mustcard`、S-3(按手册、无需改服务端)、S-4 字段拆分、S-5 术语同步。
> **服务端侧无遗留项**;前端开工只等美术与编辑器资源(§7.4)。
> **§7.5 服务端待补项**:S-1…S-5 已完成;**新增 S-6(大局结算四项分数,阻塞 D-9)与 S-7(明牌按钮条件与服务端校验不一致,有泄露口子)待处理**。
> **设计稿只剩 D-8**(判定结果文字)。
### 7.1 待出设计稿的界面(后续统一补充)
### 7.1 待出设计稿的界面
**只剩 D-8 一项**(已定稿的保留在表内备查)。
| 编号 | 界面 | 状态 |
| --- | --- | --- |
| **D-8** | 判定结果文字(大光 / 小光 / 过庄 / 升N级 / 投降) | ⬜ **暂留**——待其余项落定后单独讨论 |
| ~~D-1~~ | 建房规则选项区 | ✅ 定稿:版式照 `创建房间选项…png`,内容按 §1.1 的 3 类别 / 4 选项组。**子游戏只做「类别标签 + 选项」内容区**,弹窗背景 / 标题 / 关闭 / 确认按钮均由平台提供 |
| ~~D-2~~ | 底牌 3 秒展示 | ✅ 定稿:**复用庄家翻开底牌那套 UI**(`叫分确定庄.png`),**不要倒计时**。非 70 分只有庄家能看、70 分全场能看——同一套界面,只差可见范围 |
| ~~D-3~~ | 甩错提示 | ✅ 定稿:桌面中央深色半透条 **`强甩失败`**(`强甩失败提示.png`),**不做收回动画**,直接落下那张被强制打出的最小主牌 |
| ~~D-4~~ | 报无主 | ✅ 定稿:**不做全场提示**。只让他家 `主N`/`对N` 角标出现;自己的主牌统计条本来就常显(§2.5) |
| ~~D-5~~ | 踩 / 没分 / 有分 | ✅ 定稿:底栏三按钮(占庄标区)+ 3×2 六帧气泡 |
| ~~D-6~~ | 明牌面板 | ✅ 定稿:版式照 `算奖牌型显示.png`(遮罩 + 每家一排牌)。**按钮显示条件收紧为「可查牌 + 自己是无主玩家」** → 见 **S-7** |
| ~~D-7~~ | 出牌历史面板 | ✅ 定稿:版式同上。**不按轮次**,按玩家聚合、用主牌序排序。前端摊平 `pushlist` 即可,无需改服务端 |
| ~~D-9~~ | 大局总结算 | ✅ 定稿:`大局结算.png`,玩家栏用精灵复制(§5.7a)。**四项分数协议缺** → 见 **S-6** |
| ~~D-10~~ | 解散结算 | ✅ 复用 D-9 面板;投票界面平台全包(Layer 420) |
| ~~D-11~~ | 准备标识 | ✅ 用平台的 |
| ~~D-12~~ | 左上家已出牌区 | ✅ 与右上家左右镜像 |
| ~~D-13~~ | `扣底` 查看面板 | ✅ 与 `底牌` 按钮复用同一个「8 张牌」展示面板,仅数据源不同 |
| 编号 | 界面 | 所属 | 说明 |
| --- | --- | --- | --- |
| 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-6 | 明牌面板 | §1.7 | 另两家未出主牌的具体牌面 |
| 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 | **已定**:用平台准备标识,子游戏不做 |
| ~~D-12~~ | ~~左上家已出牌区~~ | §2.3 | **已定**:与右上家左右镜像 |
| **D-13** | **`扣底` 按钮的查看面板** | §2.6 | 庄家查看自己埋的 8 张**埋牌底牌**;可与 `底牌` 按钮复用同一个「8 张牌」展示面板,仅数据源不同 |
### 7.2 待确认的规格问题
@@ -1589,7 +1697,7 @@ codes/config/
| 编号 | 问题 | 影响 |
| --- | --- | --- |
| ~~T-9~~ | ~~亮牌条文案~~ | **已暂定**:显示 `x对 x主`(见 §1.6) |
| ~~T-9~~ | ~~亮牌条文案~~ | **已暂定**:自己的主牌统计条显示 `x对 x主`、有手牌时常显(见 §2.5)。注意这条**不是** design §8.2 的 `liangpai`,后者展示位置另见 **T-30** |
| **T-20** | **整套音效**:清单、是否需要语音包、是否分男女声、倒计时提示音从第几秒起 | §4 全节;**已明确「等待后续补充」** |
**已定论(27 项)**
@@ -1624,6 +1732,13 @@ codes/config/
| ~~T-28~~ | `EQW_Layout` 为权威,配置区手工同步 + 验收检查 |
| ~~T-29~~ | 建房整体位置尺寸随 Layer 27 定,编辑器建好后回填 |
**新增未决**
| 编号 | 问题 | 影响 |
| --- | --- | --- |
| **T-30** | 协议 `liangpai`(design §8.2 庄家亮牌:主牌总数/对子数/拖拉机数、王数、7 数、2 数)**当前没有界面位置**。要不要显示?显示在哪(庄家头像旁?闲家侧的独立条?)? | 服务端照常下发,前端暂不渲染;确定后可能需新增一处 UI |
| **T-31** | 大局结算面板的「基础分」与「总得分」两行分别对应什么?参考图两者数值相同(`基础分30`/`总得分30`),是 mock 巧合还是确有区别? | S-6 的字段定义 |
#### T-5 手牌排序(已定)
前端排序**对齐服务端主牌序**(design §3),不另造一套——服务端 `class.arith.js` 的 `order_cards(mainflower, cards)` 即权威顺序(从大到小),下发的 `pushlist` 等字段也已按此排好。
@@ -1654,6 +1769,8 @@ codes/config/
| 13 | **底牌 / 埋牌底牌未写反**:`底牌` 按钮读 `bottomcards`、`扣底` 按钮读 `burycards`;内部变量 `bottomCards`/`buryCards` 一一对应 | §2.6 |
| 14 | 叫分按钮配色按**档位位置**取(非按子数),已叫过的档位置灰 | §1.3 |
| 15 | 自己发的提示本地回显后,收到失败回包时能撤掉 | §1.7 |
| 16 | 自己的主牌统计条**有手牌时常显**、不受查牌模式与报无主影响;他家 `主N`/`对N` 角标只在「可查牌 + 已报无主」时出现 | §2.5 |
| 17 | 出牌历史**按玩家聚合 + 主牌序排序**,不是按轮次分组 | §1.7 D-7 |
### 7.4 开发前置条件
@@ -1765,3 +1882,65 @@ data.bottom.cards // 结算包
| `mod.js` / `class.export.js` / `test/test_leak.js` 注释 | ✅ 随 S-4 一并更新 |
| `docs/compliance/*.md`(3 份,24 处) | ⬜ **有意不改**——这三份是历次核对的**历史记录**,反映当时的 design 表述;下轮核对按新 design 重做即可,不追溯修改 |
| `test/*.js` 其余注释(约 14 处) | ⬜ 仅注释、不影响行为,后续顺手清理 |
#### S-6 · 大局结算缺「基础分 / 算奖分 / 傍王分」三项分解(新增,⚠️ 阻塞 D-9)
**需求**:大局结算面板每行要显示 `基础分{n}` / `总得分{n}` / `算奖分{n}` / **`傍王分{n}`** 四项(见 `大局结算.png`)。
**现状**:协议 `account` 分组只有
```json
[[累积得分, [每局得分...]], ...]
```
拿不出上面四项中的任何一项分解。小局包 `aset.seatlist[i]` 虽有 `grade_jf`(捡分子数得分)与 `grade_aw`(算奖得分),但:
1. **只有小局值,没有大局累计**——大局要的是全部局数的累加;
2. **傍王分拆不出来**。这是最麻烦的一条:`naward = (庄家?常规算奖:0) + (傍王?王数:0)`,两个来源在**求和之后**才拿去算钱(`grade_aw = X×(2Ni−Nj−Nk)`)。等于说常规算奖与傍王的贡献在算钱前就已经混在一个 `N` 里,**事后无法从 `grade_aw` 反推各自占多少**。
**建议实现**:在 `class.paiju.js` 算 `grade_aw` 的地方,把 `N` 拆成 `N_chongguan` 与 `N_bangwang` **分别代入同一公式**各算一次,得到两个分量后再相加(相加结果必须等于现在的 `grade_aw`,可作为回归断言)。然后在 `account` 里逐项累计下发,例如:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `grade_base_total` | 整数 | 基础分(捡分子数得分 `grade_jf`)的大局累计 |
| `grade_aw_total` | 整数 | 常规算奖分的大局累计(**不含**傍王) |
| `grade_bw_total` | 整数 | 傍王分的大局累计 |
| `score` | 整数 | 总得分(已有) |
**需先确认**:面板上的「基础分」与「总得分」两行分别对应什么——参考图里两者数值相同(`基础分30` / `总得分30`),是 mock 巧合还是确有区别?【T-31】
#### S-7 · 明牌按钮的显示条件与服务端校验不一致(新增,⚠️ 与 design 冲突)
**需求(D-6)**:`明牌` 按钮在**「勾选可查牌 + 自己是无主玩家」**时才显示。
**冲突**:
| 来源 | 现在的规定 |
| --- | --- |
| design §9.3 | 「一旦有玩家的主牌全部打空……**为全体三人**(含刚刚报无主的这位玩家自己)显示另外两家的主牌数量……**同时界面上会出现一个「明牌」按钮**」——读作三人都出现 |
| 协议 / 服务端 | `mingpai` 的受理条件是 `have_baofu()`(**任一**玩家报无主);`chupai` 包的 `baozhu` 字段也是「是否**已有玩家**报无主」 |
| 新需求 | **请求者本人**无主才给看 |
**需要定**:
| 方案 | 做法 |
| --- | --- |
| **(a) 只改前端**(最小改动) | 服务端不动,前端按「自己无主」控制按钮显隐。缺点:服务端仍会受理非无主玩家的 `mingpai` 请求,规则靠前端自觉——**违反「服务器权威」红线**,不推荐 |
| **(b) 前后端一起改**(推荐) | 服务端 `mingpai` 的受理条件由 `have_baofu()` 改为「请求者本人 `seatlist[seat][4][0] == 0`」,前端同步;并**同步修订 design §9.3** 的措辞 |
| **(c) 维持 design** | 放弃新需求,仍按「任一玩家报无主 → 三人都能明牌」 |
**在确认前前端按 (b) 的显隐规则实现**(更严格、不会越权),但**服务端校验尚未改**——即当前状态下,一个有主牌的玩家若伪造请求仍能拿到明牌数据。**这是个真实的信息泄露口子,选定方案后要尽快补上。**
---
## 附:本文与其他文档的关系
| 文档 | 关系 |
| --- | --- |
| `server/games/erqiwang/docs/design/design.md` | **玩法规则权威源**。本文引用其条款,不重新定义规则;术语「底牌 / 埋牌底牌」已同步(见 §0.0) |
| `server/games/erqiwang/docs/protocol/packet_protocol.md` | **协议字段权威源**。本文的「渲染什么」全部溯源到具体字段 |
| `docs/client/development-guide/` | **前端规范权威源**。ID 范围、组件范式、红线均以其为准 |
| `docs_dev/uiref/*.png` | 参考图,15 张(牌桌态 + 叫分按钮面板 + 大局结算 + 强甩失败 + 1 张建房**样式范式**,末者内容属他游戏、不可照抄) |
| `client/js/gameabc-framework/ui/AlignmentUtils.js` | **布局求解的框架能力**。§6 的配置参数名与其入参一一对应,配置可原样喂入 |
| `client/js/gameabc-framework/ui/SpriteCopyUtils.js` | 精灵复制。用于算奖牌型、大局结算玩家栏、出牌历史 / 明牌(§5.6a 判据表) |
| `client/js/gameabc-framework/templates/*.template.js` | 常量文件写法蓝本。本清单落地为常量文件时按其格式 |