// ============================================================================ // 精灵事件控制器 (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; }