Files
erqiwang_youle/client/js/01_SubGame/codes/config/Layout_Action.js
T
joywayerandClaude Opus 5 92edb6a3f5 二七王:布局求解器补齐显式失败与运行时注入契约,配置补 runtime/targetKind 声明
求解器(ui/LayoutSolver.js):
- 按方向校验 anchor:竖排只收 top/bottom/center、横排只收 left/right/center,
  未知 direction 一并拒绝。框架 AlignmentUtils 的 switch 对写错方向的 anchor 落到
  default(居中),笔误会被静默吞掉——ROOM_CATEGORY_COLUMN 就是这么把前两行摆到画布外的。
- grid 校验合并 ctx 后的 cols/rows 必须是数字:缺 rows 时 capacity=NaN 让超载守卫失效、
  循环一次不跑,静默返回空数组(一个矩形都不产出)。
- line/fan/grid 要求 anchorX/anchorY 必须是数字(框架的 anchorY||0 会把缺失变成 0);
  point 要求 x/y 必须是数字。
- apply() 精灵数与矩形数不等时抛错,不再静默截断:截断会让多余精灵停在上一手牌的旧坐标上。
- 统一运行时注入:ctx 里的 INJECT_KEYS(target/x/y/w/h/anchorX/anchorY/rows/
  itemWidth/itemHeight)覆盖配置同名字段,优先级 ctx > bySeat > base。
- line + items 竖排显式拒绝(只实现了水平)。

配置:
- ROOM_CATEGORY_COLUMN anchor 由 'left' 改为 'top'(竖排语义)。
- ROOM_OPTION_OVERFLOW_GRID 补 rows(runtime)、槽尺寸与 anchor 沿用 ROOM_OPTION_ROW,
  cols 直接引用 ROOM_OPTION_MAX_PER_ROW;ROOM_OPTION_ROW 补上注释里已声明的 itemHeight。
- 所有依赖运行时注入的节点加 runtime 声明,所有 attach 节点加 targetKind
  (sprite / layout / platform),显式区分「忘了写」与「故意延后到运行时」。

守卫(tests/test_constants.js):
- 求解冒烟改为只对 runtime 声明过的键注入假值,没声明却缺字段的照常抛错变红。
- 补矩形数量断言(line/fan/grid 按项数、point/attach 恒 1)——此前只查数组与坐标类型,
  空数组照样通过,「少画了几张牌」对守卫完全不可见。
- attach.target 改为只在 targetKind 指定的那一个命名空间里校验存在。
- 补 runtime 声明自身的合法性检查与布局节点总数(62)钉死。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:24:16 +08:00

105 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
///////////////////////////////////////////////////////////////
////////// EQW_Layout: 阶段操作区配置(清单 §6.8)//////////////
///////////////////////////////////////////////////////////////
// 【纯数据文件】无函数、无副作用;依赖 LayoutConstants.js 先加载
// (引用其 EQW_Layout.TEXT_STYLE.*,属 EQW_Layout 内部自引用)。
var EQW_Layout = EQW_Layout || {};
//===== 叫分面板 =====
EQW_Layout.CALL_PANEL_BG = { kind: 'point', x: 355, y: 140, w: 575, h: 170 };
EQW_Layout.CALL_HINT_BG = { kind: 'point', x: 470, y: 100, w: 345, h: 33 };
//14 档位按钮:grid 布局(清单 §6.1 示例,7×2)
EQW_Layout.CALL_BTN_GRID = {
kind: 'grid', anchorX: 640, anchorY: 160, cols: 7, rows: 2,
itemWidth: 74, itemHeight: 56, spacingX: 7, spacingY: 24,
anchor: 'center', fillOrder: 'row'
};
//档位分数文字:贴对应档位按钮居中。CALL_BTN_1..14 是 14 个固定精灵,但本节点是共用模板,
//target 由调用方按第几档注入(不预置单一 target)。宽高随数字位数变化,清单未给固定值,
//资源 NUM_CALL_SCORE 单字位 30×40 仅供参考,不作为本节点的 w/h
EQW_Layout.CALL_BTN_SCORE_TEXT = {
kind: 'attach', targetKind: 'sprite', hAlign: 'center', vAlign: 'middle', offsetY: 4,
runtime: ['target', 'w', 'h']
};
//档位子数角标:贴对应档位按钮右上角,纯文字无底图,清单未给宽高(同上,调用方注入 target)
EQW_Layout.CALL_BTN_MULTIPLE_BADGE = {
kind: 'attach', targetKind: 'sprite', corner: 'topRight', offsetX: -30, offsetY: 2,
runtime: ['target', 'w', 'h']
};
EQW_Layout.CALL_BTN_NONE = { kind: 'point', x: 553, y: 345, w: 184, h: 57 };
//自己叫分大字
EQW_Layout.CALL_SELF_SCORE_TEXT = {
kind: 'point', x: 570, y: 320, w: 140, h: 70, textStyle: EQW_Layout.TEXT_STYLE.CALL_BIG
};
//===== 选主 / 投降面板 =====
EQW_Layout.MAIN_TITLE_BG = { kind: 'point', x: 330, y: 315, w: 425, h: 50 };
//4 花色 + 投降:投降不显示时只排前 4 项,anchor:left 保证前 4 个位置不变
EQW_Layout.MAIN_SUIT_LINE = {
kind: 'line', direction: 'horizontal', anchorX: 330, anchorY: 373,
itemWidth: 128, itemHeight: 55, spacing: 9, anchor: 'left'
};
//花色图标:贴对应花色按钮左侧(资源 SUIT_ICON_L 46×46)。target 由调用方按花色序号注入
EQW_Layout.MAIN_SUIT_ICON = {
kind: 'attach', targetKind: 'sprite', hAlign: 'left', vAlign: 'middle', offsetX: 14, w: 46, h: 46,
runtime: ['target']
};
//张数大字:贴对应花色按钮居中,纯文字,清单未给宽高(调用方注入 target)
EQW_Layout.MAIN_SUIT_COUNT_TEXT = {
kind: 'attach', targetKind: 'sprite', hAlign: 'center', vAlign: 'middle', offsetX: 14,
runtime: ['target', 'w', 'h']
};
//对数角标底:贴对应花色按钮右上角(资源 BADGE_MULTIPLE 帧1 34×18)。
//角标文字复用同一矩形,不单独配置(调用方注入 target)
EQW_Layout.MAIN_SUIT_PAIR_BADGE = {
kind: 'attach', targetKind: 'sprite', corner: 'topRight', offsetX: -36, offsetY: 2, w: 34, h: 18,
runtime: ['target']
};
//===== 埋牌 / 出牌操作条 =====
//埋牌操作条:提示 / 倒计时(CD_SELF) / 埋牌,逐项不等宽(清单 §6.1)
EQW_Layout.BURY_OPERATION_BAR = {
kind: 'line', direction: 'horizontal', anchorX: 640, anchorY: 165,
itemHeight: 63, spacing: 45, anchor: 'center',
items: [
{ key: 'BURY_BTN_TIP', width: 180 },
{ key: 'CD_SELF', width: 60 },
{ key: 'BURY_BTN_SUBMIT', width: 180 }
]
};
//出牌操作条:倒计时(CD_SELF) / 出牌
EQW_Layout.PLAY_OPERATION_BAR = {
kind: 'line', direction: 'horizontal', anchorX: 640, anchorY: 345,
itemHeight: 55, spacing: 35, anchor: 'center',
items: [
{ key: 'CD_SELF', width: 75 },
{ key: 'PLAY_BTN_SUBMIT', width: 157 }
]
};
//===== 浮层 =====
//捡分飘字:起始位置在此。动画参数(floatRise / floatDuration)见 EQW_Anim.SCORE_FLOAT,不在此重复(SSOT)
EQW_Layout.GRADE_FLOAT = { kind: 'point', x: 570, y: 180, w: 170, h: 55 };
//状态提示条 · 等待类(如『等待庄家选主』)。与即时反馈类共用同一套精灵 BAR_TOAST/TXT_TOAST,
//仅位置预设不同,调用方按提示类型二选一应用(清单 §2.8:收敛为 3 条)
EQW_Layout.TOAST_WAIT = { kind: 'point', x: 500, y: 373, w: 320, h: 47 };
//状态提示条 · 即时反馈类(如『强甩失败』)。自动消失时长见 EQW_Anim.TOAST_DURATION,不在此重复(SSOT)
EQW_Layout.TOAST_INSTANT = { kind: 'point', x: 462, y: 172, w: 363, h: 50 };