二七王:布局求解器补齐显式失败与运行时注入契约,配置补 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>
This commit is contained in:
@@ -9,45 +9,70 @@ var EQW_Layout = EQW_Layout || {};
|
||||
//两层嵌套:类别竖排(ROOM_CATEGORY_COLUMN)+ 类别内选项槽横排(ROOM_OPTION_ROW)。
|
||||
//本节数值按参考图比例估,实际以平台建房界面尺寸为准(清单 §6.9b 原文,不是本文件误差)。
|
||||
|
||||
//类别竖排。itemWidth 清单未给——类别行宽度等于平台内容区实际宽度,由调用方按实际面板尺寸处理
|
||||
//类别竖排。itemWidth 清单未给——类别行宽度等于平台内容区实际宽度,由调用方按实际面板尺寸注入。
|
||||
//anchor 必须是竖排语义的 top/bottom/center:写成水平语义的 'left' 会被框架 switch 的 default
|
||||
//当成居中(3 个类别时算出 y=[-56,-16,24],前两行跑到画布外),求解器现已按方向校验并抛错
|
||||
EQW_Layout.ROOM_CATEGORY_COLUMN = {
|
||||
kind: 'line', direction: 'vertical', anchorX: 0, anchorY: 0,
|
||||
itemHeight: 32, spacing: 8, anchor: 'left'
|
||||
itemHeight: 32, spacing: 8, anchor: 'top',
|
||||
runtime: ['itemWidth']
|
||||
};
|
||||
|
||||
//类别标题:贴所在类别行。该行是 ROOM_CATEGORY_COLUMN 求解结果的第 N 项,不是固定精灵,
|
||||
//target 由调用方按类别序号注入。资源 ROOM_CAT_TITLE 60×24(清单 §3)
|
||||
EQW_Layout.ROOM_CATEGORY_TITLE = { kind: 'attach', hAlign: 'left', vAlign: 'middle', offsetX: 0, w: 60, h: 24 };
|
||||
EQW_Layout.ROOM_CATEGORY_TITLE = {
|
||||
kind: 'attach', targetKind: 'layout', hAlign: 'left', vAlign: 'middle', offsetX: 0, w: 60, h: 24,
|
||||
runtime: ['target']
|
||||
};
|
||||
|
||||
//类别内选项槽横排。anchorY 清单未给(随所在类别行的 y 浮动,由调用方按该行位置注入);
|
||||
//itemHeight 清单未给,沿用 ROOM_CATEGORY_COLUMN.itemHeight(同一行内选择框与文字纵向居中)
|
||||
EQW_Layout.ROOM_OPTION_ROW = {
|
||||
kind: 'line', direction: 'horizontal', anchorX: 70,
|
||||
itemWidth: 150, spacing: 0, anchor: 'left'
|
||||
itemWidth: 150, itemHeight: EQW_Layout.ROOM_CATEGORY_COLUMN.itemHeight, spacing: 0, anchor: 'left',
|
||||
runtime: ['anchorY']
|
||||
};
|
||||
|
||||
//选项组之间的额外间隔(同类别下两个选项组之间比组内选项多留的间距)
|
||||
EQW_Layout.ROOM_OPTION_GROUP_GAP = 40;
|
||||
|
||||
//选择框:贴所在选项槽左侧(资源 ROOM_OPT_BOX 22×22)。target 由调用方按选项槽注入
|
||||
EQW_Layout.ROOM_OPTION_BOX = { kind: 'attach', hAlign: 'left', vAlign: 'middle', w: 22, h: 22 };
|
||||
//(槽是 ROOM_OPTION_ROW 求解结果的第 N 项,故 targetKind 是 layout 而非精灵键)
|
||||
EQW_Layout.ROOM_OPTION_BOX = {
|
||||
kind: 'attach', targetKind: 'layout', hAlign: 'left', vAlign: 'middle', w: 22, h: 22,
|
||||
runtime: ['target']
|
||||
};
|
||||
|
||||
//选项文字:贴同槽选择框右侧,纯文字,清单未给宽高。
|
||||
//选项文字:贴同槽选择框右侧(target 是该槽的 ROOM_BOX_* 精灵,由调用方按选项配置的 box 键注入),
|
||||
//纯文字,清单未给宽高,由调用方按渲染出的文案尺寸注入。
|
||||
//选中态变橙见 EQW_Layout.TEXT_STYLE.ROOM_OPT(normal / selected 两态,由调用方按选中状态二选一)
|
||||
EQW_Layout.ROOM_OPTION_TEXT = { kind: 'attach', hAlign: 'right', vAlign: 'middle', offsetX: 8 };
|
||||
EQW_Layout.ROOM_OPTION_TEXT = {
|
||||
kind: 'attach', targetKind: 'sprite', hAlign: 'right', vAlign: 'middle', offsetX: 8,
|
||||
runtime: ['target', 'w', 'h']
|
||||
};
|
||||
|
||||
//房卡附注:贴扣卡组最后一个选项(固定精灵 ROOM_BOX_CARD_2)右侧
|
||||
//房卡附注:贴扣卡组最后一个选项(固定精灵 ROOM_BOX_CARD_2)右侧。纯文字,宽高随文案运行时注入
|
||||
EQW_Layout.ROOM_CARD_COST_NOTE = {
|
||||
kind: 'attach', target: 'ROOM_BOX_CARD_2', hAlign: 'right', vAlign: 'middle', offsetX: 20,
|
||||
textStyle: EQW_Layout.TEXT_STYLE.ROOM_CARD_COST_NOTE
|
||||
kind: 'attach', targetKind: 'sprite', target: 'ROOM_BOX_CARD_2',
|
||||
hAlign: 'right', vAlign: 'middle', offsetX: 20,
|
||||
textStyle: EQW_Layout.TEXT_STYLE.ROOM_CARD_COST_NOTE,
|
||||
runtime: ['w', 'h']
|
||||
};
|
||||
|
||||
//折行:选项数超过 maxPerRow 时改用 grid。二七王每组最多 2 项,不会触发(清单 §6.9b 原文);
|
||||
//清单原文是 grid{cols:maxPerRow, spacingX:0, spacingY:8, fillOrder:'row'},cols 直接取自 maxPerRow,
|
||||
//故与 spacingX/spacingY/fillOrder 一并记为已定的四个字段;itemWidth / itemHeight / anchorX / anchorY
|
||||
//清单未给(该分支未被实际使用)
|
||||
//故与 spacingX/spacingY/fillOrder 一并记为已定的四个字段。
|
||||
//rows 由实际选项数决定(ceil(选项数 / cols)),anchorX/anchorY 随所在类别行浮动 —— 三者都是运行时值,
|
||||
//在 runtime 里显式声明;缺 rows 时求解器会抛错,不再静默返回空数组(一个矩形都不产出)。
|
||||
//itemWidth / itemHeight 清单同样未给:折行摆的还是同一批选项槽,沿用 ROOM_OPTION_ROW 的槽尺寸,
|
||||
//不另编一套数值;anchor 同理沿用 ROOM_OPTION_ROW 的 'left'(折行前后左边缘对齐一致)
|
||||
EQW_Layout.ROOM_OPTION_MAX_PER_ROW = 4;
|
||||
EQW_Layout.ROOM_OPTION_OVERFLOW_GRID = { kind: 'grid', cols: 4, spacingX: 0, spacingY: 8, fillOrder: 'row' };
|
||||
EQW_Layout.ROOM_OPTION_OVERFLOW_GRID = {
|
||||
kind: 'grid', cols: EQW_Layout.ROOM_OPTION_MAX_PER_ROW, spacingX: 0, spacingY: 8, fillOrder: 'row',
|
||||
itemWidth: EQW_Layout.ROOM_OPTION_ROW.itemWidth, itemHeight: EQW_Layout.ROOM_OPTION_ROW.itemHeight,
|
||||
anchor: 'left',
|
||||
runtime: ['rows', 'anchorX', 'anchorY']
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
////////// EQW_RoomOptions: 建房规则选项配置(清单 §1.1)///////
|
||||
|
||||
Reference in New Issue
Block a user