/////////////////////////////////////////////////////////////// ////////// EQW_Layout / EQW_RoomOptions: 建房规则选项(清单 §6.9b / §1.1) /////////////////////////////////////////////////////////////// // 【纯数据文件】无函数、无副作用;依赖 LayoutConstants.js 先加载 // (引用其 EQW_Layout.TEXT_STYLE.*,属 EQW_Layout 内部自引用)。 var EQW_Layout = EQW_Layout || {}; //===== 建房规则选项区布局(清单 §6.9b)===== //两层嵌套:类别竖排(ROOM_CATEGORY_COLUMN)+ 类别内选项槽横排(ROOM_OPTION_ROW)。 //本节数值按参考图比例估,实际以平台建房界面尺寸为准(清单 §6.9b 原文,不是本文件误差)。 //类别竖排。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: 'top', runtime: ['itemWidth'] }; //类别标题:贴所在类别行。该行是 ROOM_CATEGORY_COLUMN 求解结果的第 N 项,不是固定精灵, //target 由调用方按类别序号注入。资源 ROOM_CAT_TITLE 60×24(清单 §3) 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, 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 由调用方按选项槽注入 //(槽是 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', targetKind: 'sprite', hAlign: 'right', vAlign: 'middle', offsetX: 8, runtime: ['target', 'w', 'h'] }; //房卡附注:贴扣卡组最后一个选项(固定精灵 ROOM_BOX_CARD_2)右侧。纯文字,宽高随文案运行时注入 EQW_Layout.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 一并记为已定的四个字段。 //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: 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)/////// /////////////////////////////////////////////////////////////// //整个界面由本配置驱动渲染,加一个选项 = 配置里加一条,不改渲染逻辑(工程总则 §5 OCP / §6 配置优先)。 //type: 'radio' 单选 / 'checkbox' 多选 / 'radioOptional' 单选可不选 //选择框帧号 = (useCheckbox ? 3 : 1) + (selected ? 1 : 0),useCheckbox = (checkbox 或 radioOptional) // //【精灵键由配置给出】每个类别声明自己的标题精灵 titleSprite,每个选项声明自己的选择框精灵 box //与文字精灵 text,附注声明 noteSprite——渲染器一律按键名取精灵,【不得按下标拼字符串】 //(组的 key 是 'rules'、精灵却叫 ROOM_BOX_RULE_1,本来就拼不出来)。 // //【roomtype 拼串只在一处实现】拼串就是遍历本配置,不得在别处按下标硬拼 //(与服务端 class.config.js parse() 对称的 SSOT 要求)。bit/value 统一挂在【每个 item 上】: // - radio:组内各 item 写同一个 bit,选中哪个就把该 item 的 value 写进该位; // - checkbox:勾选写 item.value、不勾写 '0'。 //这样拼串循环对两种 type 是同一个形状(item → 写 value 到 bit),不必按组/项两种层级分支 var EQW_RoomOptions = EQW_RoomOptions || { categories: [ { titleFrame: 1, titleSprite: 'ROOM_CAT_TITLE_1', /* 牌局 */ groups: [ { key: 'aset', type: 'radio', items: [ { label: '6局', value: '0', bit: 0, box: 'ROOM_BOX_ASET_1', text: 'ROOM_TEXT_ASET_1' }, { label: '12局', value: '1', bit: 0, box: 'ROOM_BOX_ASET_2', text: 'ROOM_TEXT_ASET_2' } ] }, { key: 'card', type: 'radio', note: 'cardCost', noteSprite: 'ROOM_TEXT_CARD_COST', items: [ { label: '房主扣卡', value: '0', bit: 1, box: 'ROOM_BOX_CARD_1', text: 'ROOM_TEXT_CARD_1' }, { label: 'AA每人扣卡', value: '1', bit: 1, box: 'ROOM_BOX_CARD_2', text: 'ROOM_TEXT_CARD_2' } ] } ] }, { titleFrame: 2, titleSprite: 'ROOM_CAT_TITLE_2', /* 模式 */ groups: [ { key: 'peek', type: 'radio', items: [ { label: '可查牌', value: '0', bit: 4, box: 'ROOM_BOX_PEEK_1', text: 'ROOM_TEXT_PEEK_1' }, { label: '不查牌', value: '1', bit: 4, box: 'ROOM_BOX_PEEK_2', text: 'ROOM_TEXT_PEEK_2' } ] } ] }, { titleFrame: 3, titleSprite: 'ROOM_CAT_TITLE_3', /* 规则 */ groups: [ { key: 'rules', type: 'checkbox', items: [ { label: '傍王', value: '1', bit: 2, box: 'ROOM_BOX_RULE_1', text: 'ROOM_TEXT_RULE_1' }, { label: '爬坡', value: '1', bit: 3, box: 'ROOM_BOX_RULE_2', text: 'ROOM_TEXT_RULE_2' } ] } ] } ], //房卡消耗联动(协议 §0.5):不硬编码在 UI 代码里 cardCost: { '0': { '0': '房主2张', '1': '房主4张' }, //房主扣卡:6局 / 12局 '1': { '0': '每人1张', '1': '每人2张' } //AA 每人扣卡:6局 / 12局 } };