二七王:前端事件常量与 GameState 骨架
GameState 是服务端快照的镜像、前端 SSOT:只镜像不派生、字段名对齐协议、 缺字段不兜底。reset 清对局态但保留 mySeat 与 options(跨局不变)。 事件只带「发生了什么」,数据由订阅者从 GameState 读,避免同一份数据两处存。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
///////////////////////////////////////////////////////////////
|
||||
////////// EQW_Events: 玩法事件常量 ///////////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
// 前端红线:框架保持游戏中立,玩法专属事件由子游戏追加到 EventBus.Events。
|
||||
//
|
||||
// 【事件只带「发生了什么」,不带数据】——订阅者从 EQW_GameState 读。
|
||||
// 这样同一份数据不会在事件载荷与状态里各存一份(SSOT)。
|
||||
// 唯一例外是 EQW_RPC_FAILED,它带 {rpc, errcode}:那是一次性的错误信息,
|
||||
// 不属于对局状态、不该进 GameState。
|
||||
var EQW_Events = EQW_Events || {
|
||||
|
||||
EQW_RESET: 'eqw.reset', //新一局发牌,全部清场
|
||||
EQW_HAND_CHANGED: 'eqw.hand_changed', //自己手牌变化
|
||||
EQW_CALL_CHANGED: 'eqw.call_changed', //叫分推进
|
||||
EQW_BANKER_SET: 'eqw.banker_set', //上庄
|
||||
EQW_MAIN_SET: 'eqw.main_set', //选主
|
||||
EQW_BURY_DONE: 'eqw.bury_done', //埋牌完成
|
||||
EQW_CARD_PLAYED: 'eqw.card_played', //有人出牌
|
||||
EQW_TRICK_END: 'eqw.trick_end', //一轮结束(chupai3 带 maxseat)
|
||||
EQW_TURN_CHANGED: 'eqw.turn_changed', //控制权或倒计时变化
|
||||
EQW_MINGPAI: 'eqw.mingpai', //收到明牌数据
|
||||
EQW_TIP: 'eqw.tip', //收到对家提示
|
||||
EQW_ASET_RESULT: 'eqw.aset_result', //小局结算
|
||||
EQW_ACCOUNT_RESULT: 'eqw.account_result', //大局/解散结算
|
||||
EQW_READY_CHANGED: 'eqw.ready_changed', //有人准备
|
||||
EQW_RESYNC_ALL: 'eqw.resync_all', //重连/开局全量重画
|
||||
EQW_RPC_FAILED: 'eqw.rpc_failed' //失败回包 {rpc, errcode}
|
||||
};
|
||||
|
||||
//追加到框架事件总线的事件表(框架不认识玩法事件,由子游戏注册)
|
||||
if (typeof EventBus !== 'undefined' && EventBus.Events) {
|
||||
for (var _eqwEvtKey in EQW_Events) {
|
||||
if (EQW_Events.hasOwnProperty(_eqwEvtKey)) {
|
||||
EventBus.Events[_eqwEvtKey] = EQW_Events[_eqwEvtKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
///////////////////////////////////////////////////////////////
|
||||
////////// EQW_GameState: 服务端快照的镜像(前端 SSOT)////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
// 前端红线「视图 = f(服务端快照)」的落点:本对象只存【服务端下发过的字段】,
|
||||
// 不存任何前端算出来的结论。手牌排序、牌面标记、花色统计等由 core/ 的纯函数
|
||||
// 在渲染时现算,不落地成状态。
|
||||
//
|
||||
// 三条硬约束:
|
||||
// 1. 只镜像不派生——存进来的必须是服务端给过的
|
||||
// 2. 字段名对齐协议——协议叫 currcall 就叫 currcall,便于逐条核对
|
||||
// 3. 缺字段不兜底——包里没有的保持原值,不填默认值掩盖;必需字段缺失
|
||||
// 则 console.error 并跳过该包(服务端漏发,修在服务端)
|
||||
var EQW_GameState = EQW_GameState || {
|
||||
|
||||
//数据分组名(snapshot / reset 按此遍历;新增分组要同步加进来)
|
||||
_GROUPS: ['room', 'aset', 'turn', 'call', 'my', 'table', 'result'],
|
||||
|
||||
//—— 房间级(跨小局)——
|
||||
room: {
|
||||
asetCount: 0, //总局数 ← fapai.asetcount / deskinfo.count / setRoomDes
|
||||
asetIdx: 0, //当前第几局 ← fapai.asetidx / deskinfo.idx
|
||||
mySeat: -1, //自己的座位 ← 平台 C_Player.seat
|
||||
playerScores: [], //三家总积分 ← deskinfo.PlayerInfo
|
||||
options: null //roomtype 解析结果
|
||||
},
|
||||
|
||||
//—— 小局级 ——
|
||||
aset: {
|
||||
step: 0, //1叫分 2选主/投降 3埋牌 5出牌 6结算
|
||||
banker: -1,
|
||||
call: -1, //庄家叫分
|
||||
multiple: 0, //基础子数
|
||||
flower: 0, //主牌花色(0 = 未选主)
|
||||
curmultiple: 0, //当前抓分倍数(带符号,服务端权威,前端不自算)
|
||||
grade: 0, //闲家已捡分
|
||||
baozhu: 0, //是否已有人报无主
|
||||
touxiang: 0 //是否允许投降
|
||||
},
|
||||
|
||||
//—— 当前控制权与倒计时(任何阶段都读这里)——
|
||||
turn: {
|
||||
seat: -1, //该谁操作
|
||||
countdown: 0 //展示用秒数;服务端无对应定时器,归零【不做任何界面推进】
|
||||
},
|
||||
|
||||
//—— 叫分过程 ——
|
||||
call: {
|
||||
currcall: 0,
|
||||
calls: [null, null, null] //null 未叫 / 0 不叫 / >0 叫了多少
|
||||
},
|
||||
|
||||
//—— 自己 ——
|
||||
my: {
|
||||
cards: [],
|
||||
mustCard: [], //本轮必出牌(服务端建议,只发给轮到的那家)
|
||||
bottomCards: [], //底牌(庄家恒有;闲家仅 70 分坐庄时有)
|
||||
buryCards: [] //埋牌底牌(仅庄家)
|
||||
},
|
||||
|
||||
//—— 桌面 ——
|
||||
table: {
|
||||
ancard3s: 0, //开底标志(70 分坐庄)
|
||||
playproc: null,
|
||||
pushlist: [],
|
||||
seatlist: [],
|
||||
liangpai: null,
|
||||
mingpai: null
|
||||
},
|
||||
|
||||
//—— 结算 ——
|
||||
result: {
|
||||
chupai: null,
|
||||
bottom: null,
|
||||
aset: null,
|
||||
account: null
|
||||
},
|
||||
|
||||
//新一局开局时清空对局态;room.mySeat 与 room.options 跨局不变,不清
|
||||
reset: function () {
|
||||
this.room.asetIdx = 0;
|
||||
this.room.playerScores = [];
|
||||
|
||||
this.aset = { step: 0, banker: -1, call: -1, multiple: 0, flower: 0,
|
||||
curmultiple: 0, grade: 0, baozhu: 0, touxiang: 0 };
|
||||
this.turn = { seat: -1, countdown: 0 };
|
||||
this.call = { currcall: 0, calls: [null, null, null] };
|
||||
this.my = { cards: [], mustCard: [], bottomCards: [], buryCards: [] };
|
||||
this.table = { ancard3s: 0, playproc: null, pushlist: [], seatlist: [],
|
||||
liangpai: null, mingpai: null };
|
||||
this.result = { chupai: null, bottom: null, aset: null, account: null };
|
||||
},
|
||||
|
||||
//深拷贝快照(只含数据分组,不含方法),供测试做「增量 vs 全量」比对
|
||||
snapshot: function () {
|
||||
var out = {};
|
||||
for (var i = 0; i < this._GROUPS.length; i++) {
|
||||
var g = this._GROUPS[i];
|
||||
out[g] = JSON.parse(JSON.stringify(this[g]));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user