Files

371 lines
14 KiB
JavaScript

// ============================================================================
// 精灵事件控制器 (gameabc-framework / system, ES5标准)
// 基于 gameabc 引擎的精灵交互事件分发框架——游戏中立,可被任意子游戏复用
// ============================================================================
/**
* SpriteEventController —— 通用精灵交互事件分发器
*
* 职责:
* 接收平台引擎透传的精灵交互事件(按下/长按/松开/拖拽/绘制前/绘制),
* 按「精灵 ID」分发到注册的处理函数;并提供「全局绘制钩子」供需要对每个
* 绘制事件统一处理的子游戏能力(如牌面标记叠绘)挂载。
*
* 中立原则:
* 本控制器不含任何具体玩法逻辑。玩法专属的“每帧绘制”处理由子游戏通过
* registerGlobalDraw(handler) 注册,框架只负责遍历回调,不认识具体业务。
*
* 接入方式(由子游戏受限入口 Game_Modify.* 转调):
* Game_Modify.utlmousedown → SpriteEventController.handleMouseDown(...)
* Game_Modify.utlmousedown_nomove → handleMouseDownNoMove(...)
* Game_Modify.mouseup → handleMouseUp(...)
* Game_Modify.utlmousemove → handleMouseMove(...)
* Game_Modify.utlgamemydrawbegin → handleDrawBegin(...)
* Game_Modify.gamemydraw → handleDraw(...)
*
* 严格 ES5,确保浏览器/平台运行时兼容。
*/
var SpriteEventController = {
// ========================================================================
// 精灵交互事件处理器映射表(按精灵 ID 注册)
// ========================================================================
/** 精灵点击事件处理器 { spriteId: handler } */
mouseDownHandlers: {},
/** 精灵长按事件处理器(点击后未移动) */
mouseDownNoMoveHandlers: {},
/** 精灵松开事件处理器 */
mouseUpHandlers: {},
/** 精灵拖拽移动事件处理器 */
mouseMoveHandlers: {},
/** 精灵绘制前事件处理器 */
drawBeginHandlers: {},
/** 精灵绘制事件处理器 */
drawHandlers: {},
/**
* 全局绘制钩子列表:对【每个】绘制事件都会被调用(不按精灵 ID 过滤)。
* 供子游戏注册“需要扫描所有绘制精灵”的通用能力(如标记叠绘),保持框架中立。
*/
drawGlobalHandlers: [],
// ========================================================================
// 事件处理器注册方法
// ========================================================================
registerMouseDown: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.mouseDownHandlers[spriteId] = handler;
return true;
},
registerMouseDownNoMove: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.mouseDownNoMoveHandlers[spriteId] = handler;
return true;
},
registerMouseUp: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.mouseUpHandlers[spriteId] = handler;
return true;
},
registerMouseMove: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.mouseMoveHandlers[spriteId] = handler;
return true;
},
registerDrawBegin: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.drawBeginHandlers[spriteId] = handler;
return true;
},
registerDraw: function(spriteId, handler) {
if (typeof spriteId !== 'number' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的注册参数');
return false;
}
this.drawHandlers[spriteId] = handler;
return true;
},
/**
* 注册全局绘制钩子(每个绘制事件都会被回调,参数为精灵 ID)。
* 用于子游戏的“扫描所有绘制精灵”类通用处理;框架不认识其业务含义。
* @param {Function} handler - function(spriteId){}
*/
registerGlobalDraw: function(handler) {
if (typeof handler !== 'function') {
console.error('❌ SpriteEventController: 无效的全局绘制钩子');
return false;
}
this.drawGlobalHandlers.push(handler);
return true;
},
// ========================================================================
// 批量注册
// ========================================================================
registerBatch: function(spriteIds, eventType, handler) {
if (!Array.isArray(spriteIds) || typeof eventType !== 'string' || typeof handler !== 'function') {
console.error('❌ SpriteEventController: 批量注册参数错误');
return false;
}
var registerMethod = null;
switch (eventType) {
case 'mouseDown': registerMethod = this.registerMouseDown; break;
case 'mouseDownNoMove': registerMethod = this.registerMouseDownNoMove; break;
case 'mouseUp': registerMethod = this.registerMouseUp; break;
case 'mouseMove': registerMethod = this.registerMouseMove; break;
case 'draw': registerMethod = this.registerDraw; break;
case 'drawBegin': registerMethod = this.registerDrawBegin; break;
default:
console.error('❌ SpriteEventController: 未知的事件类型: ' + eventType);
return false;
}
var successCount = 0;
for (var i = 0; i < spriteIds.length; i++) {
if (registerMethod.call(this, spriteIds[i], handler)) {
successCount++;
}
}
// console.log('📊 批量注册完成: ' + successCount + '/' + spriteIds.length + ' (' + eventType + ')');
return successCount === spriteIds.length;
},
// ========================================================================
// 事件分发处理方法(由 Game_Modify 转调)
// ========================================================================
handleMouseDown: function(gameid, spid, downx, downy, no1, no2, no3, no4, no5, no6) {
var handler = this.mouseDownHandlers[spid];
if (handler) {
try {
handler.call(this, {
gameid: gameid, spriteId: spid, x: downx, y: downy,
params: [no1, no2, no3, no4, no5, no6]
});
return true;
} catch (error) {
console.error('❌ 精灵点击事件处理失败:', error);
return false;
}
}
return false;
},
handleMouseDownNoMove: function(gameid, spid, downx, downy, timelong, no1, no2, no3, no4, no5) {
var handler = this.mouseDownNoMoveHandlers[spid];
if (handler) {
try {
handler.call(this, {
gameid: gameid, spriteId: spid, x: downx, y: downy, duration: timelong,
params: [no1, no2, no3, no4, no5]
});
return true;
} catch (error) {
console.error('❌ 精灵长按事件处理失败:', error);
return false;
}
}
return false;
},
handleMouseUp: function(gameid, spid_down, downx, downy, spid_up, upx, upy, timelong, no1, no2) {
var downHandler = this.mouseUpHandlers[spid_down];
if (downHandler) {
try {
downHandler.call(this, {
gameid: gameid,
downSpriteId: spid_down,
upSpriteId: spid_up,
downPos: { x: downx, y: downy },
upPos: { x: upx, y: upy },
duration: timelong,
isClick: spid_down === spid_up,
params: [no1, no2]
});
return true;
} catch (error) {
console.error('❌ 精灵松开事件处理失败:', error);
return false;
}
}
return false;
},
handleMouseMove: function(gameid, spid, downx, downy, movex, movey, timelong, offmovex, offmovey, no1) {
var handler = this.mouseMoveHandlers[spid];
if (handler) {
try {
handler.call(this, {
gameid: gameid, spriteId: spid,
downPos: { x: downx, y: downy },
currentPos: { x: movex, y: movey },
offset: { x: offmovex, y: offmovey },
duration: timelong,
params: [no1]
});
return true;
} catch (error) {
console.error('❌ 精灵拖拽事件处理失败:', error);
return false;
}
}
return false;
},
handleDrawBegin: function(gameid, spid, times, timelong, no2, no3, no4, no5, no6, no7) {
// 通用裁剪区域应用(DynamicSpriteList 等注册的裁剪)
if (typeof GameABCUtils !== 'undefined' && GameABCUtils.Clip) {
GameABCUtils.Clip.checkAndApply(spid);
}
var handler = this.drawBeginHandlers[spid];
if (handler) {
try {
handler.call(this, {
gameid: gameid, spriteId: spid, times: times, timelong: timelong,
params: [no2, no3, no4, no5, no6, no7]
});
return true;
} catch (error) {
console.error('❌ 精灵绘制前事件处理失败:', error);
return false;
}
}
return false;
},
handleDraw: function(gameid, spid, times, timelong, no2, no3, no4, no5, no6, no7) {
// 先执行全局绘制钩子(子游戏注册的“扫描所有绘制精灵”类处理,框架不认识其业务)
for (var g = 0; g < this.drawGlobalHandlers.length; g++) {
try {
this.drawGlobalHandlers[g](spid);
} catch (gErr) {
console.error('❌ 全局绘制钩子执行失败:', gErr);
}
}
var handler = this.drawHandlers[spid];
if (handler) {
try {
handler.call(this, {
gameid: gameid, spriteId: spid, times: times, timelong: timelong,
params: [no2, no3, no4, no5, no6, no7]
});
return true;
} catch (error) {
console.error('❌ 精灵绘制事件处理失败:', error);
return false;
}
}
return false;
},
// ========================================================================
// 工具方法
// ========================================================================
unregister: function(spriteId, eventType) {
if (typeof spriteId !== 'number') {
console.error('❌ SpriteEventController: 无效的精灵ID');
return false;
}
if (eventType) {
switch (eventType) {
case 'mouseDown': delete this.mouseDownHandlers[spriteId]; break;
case 'mouseDownNoMove': delete this.mouseDownNoMoveHandlers[spriteId]; break;
case 'mouseUp': delete this.mouseUpHandlers[spriteId]; break;
case 'mouseMove': delete this.mouseMoveHandlers[spriteId]; break;
case 'draw': delete this.drawHandlers[spriteId]; break;
case 'drawBegin': delete this.drawBeginHandlers[spriteId]; break;
default:
console.error('❌ SpriteEventController: 未知的事件类型: ' + eventType);
return false;
}
} else {
delete this.mouseDownHandlers[spriteId];
delete this.mouseDownNoMoveHandlers[spriteId];
delete this.mouseUpHandlers[spriteId];
delete this.mouseMoveHandlers[spriteId];
delete this.drawHandlers[spriteId];
delete this.drawBeginHandlers[spriteId];
}
return true;
},
getStats: function() {
return {
mouseDown: Object.keys(this.mouseDownHandlers).length,
mouseDownNoMove: Object.keys(this.mouseDownNoMoveHandlers).length,
mouseUp: Object.keys(this.mouseUpHandlers).length,
mouseMove: Object.keys(this.mouseMoveHandlers).length,
draw: Object.keys(this.drawHandlers).length,
drawBegin: Object.keys(this.drawBeginHandlers).length,
drawGlobal: this.drawGlobalHandlers.length
};
},
clear: function() {
this.mouseDownHandlers = {};
this.mouseDownNoMoveHandlers = {};
this.mouseUpHandlers = {};
this.mouseMoveHandlers = {};
this.drawHandlers = {};
this.drawBeginHandlers = {};
this.drawGlobalHandlers = [];
// console.log('🧹 已清空所有精灵事件处理器');
},
init: function() {
// console.log('🚀 SpriteEventController 已初始化');
var dependencies = ['set_self', 'get_self', 'ifast_mydrawbmp'];
var missing = [];
for (var i = 0; i < dependencies.length; i++) {
if (typeof window[dependencies[i]] === 'undefined') {
missing.push(dependencies[i]);
}
}
if (missing.length > 0) {
console.warn('⚠️ 缺少依赖接口:', missing.join(', '));
console.warn('请确保已正确加载 gameabc.min.js');
}
return missing.length === 0;
}
};
// 自动初始化(浏览器/平台运行时)
if (typeof window !== 'undefined') {
SpriteEventController.init();
}
// Node 测试环境导出
if (typeof module !== 'undefined' && module.exports) {
module.exports = SpriteEventController;
}