From 216e8119c42e929c87b6b619ccead43de25b21a9 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sun, 4 Feb 2024 21:18:02 +0800 Subject: [PATCH] refactor: plugins-fiveLayer --- public/libs/core.js | 24 +++- public/libs/events.js | 82 +++++++++++- public/libs/maps.js | 39 +++++- public/main.js | 124 ++++++++--------- public/project/functions.js | 45 +++++++ public/project/plugins.js | 227 +++++++++++++++++++++++++++++++- src/core/index.ts | 62 ++++++++- src/core/plugin.ts | 2 + src/game/system.ts | 74 +++++++++-- src/plugin/animateController.ts | 7 +- src/plugin/game/fiveLayer.ts | 219 ------------------------------ src/plugin/game/index.ts | 16 +-- src/plugin/game/replay.ts | 43 ------ src/plugin/keyCodes.ts | 4 +- src/plugin/ui/toolbox.tsx | 2 - 15 files changed, 597 insertions(+), 373 deletions(-) delete mode 100644 src/plugin/game/fiveLayer.ts diff --git a/public/libs/core.js b/public/libs/core.js index 3d0ad6b..a76b384 100644 --- a/public/libs/core.js +++ b/public/libs/core.js @@ -277,6 +277,7 @@ core.prototype.init = async function (coreData, callback) { this._forwardFuncs(); for (var key in coreData) core[key] = coreData[key]; await this._loadGameProcess(); + await this._loadPluginAsync(); this._init_flags(); this._init_platform(); this._init_others(); @@ -302,9 +303,9 @@ core.prototype.init = async function (coreData, callback) { }; core.prototype.initSync = function (coreData, callback) { - this._loadGameProcessSync(); this._forwardFuncs(); for (var key in coreData) core[key] = coreData[key]; + this._loadGameProcessSync(); this._init_flags(); this._init_platform(); this._init_others(); @@ -314,6 +315,27 @@ core.prototype.initSync = function (coreData, callback) { }); }; +core.prototype._loadPluginAsync = async function () { + if (!main.useCompress) { + await main.loadScript(`project/plugins.js?v=${main.version}`); + } else { + await main.loadScript(`project/plugins.min.js?v=${main.version}`); + } + for (const [key, value] of Object.entries( + plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 + )) { + try { + value.call(plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1); + } catch (e) { + throw new Error(`Plugin '${key}' init fail. Error info: ${e}`); + } + } +}; + +core.prototype._loadPluginSync = function () { + main.loadMod('project', 'plugins', () => 0); +}; + core.prototype._loadGameProcess = async function () { // 加载游戏进程代码 if (main.pluginUseCompress) { diff --git a/public/libs/events.js b/public/libs/events.js index d6a5ed7..c0e0e11 100644 --- a/public/libs/events.js +++ b/public/libs/events.js @@ -369,7 +369,73 @@ events.prototype.doSystemEvent = function (type, data, callback) { ////// 触发(x,y)点的事件 ////// events.prototype.trigger = function (x, y, callback) { - // see src/plugin/game/loopMap.js + var _executeCallback = function () { + // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) + // 所以这里强制callback被异步触发 + if (callback) { + setTimeout(callback, 1); // +1是为了录像检测系统 + } + return; + }; + if (core.status.gameOver) return _executeCallback(); + if (core.status.event.id == 'action') { + core.insertAction( + { + type: 'function', + function: + 'function () { core.events._trigger_inAction(' + + x + + ',' + + y + + '); }', + async: true + }, + null, + null, + null, + true + ); + return _executeCallback(); + } + if (core.status.event.id) return _executeCallback(); + + var block = core.getBlock(x, y); + if (block == null) return _executeCallback(); + + // 执行该点的脚本 + if (block.event.script) { + core.clearRouteFolding(); + try { + eval(block.event.script); + } catch (ee) { + console.error(ee); + } + } + + // 碰触事件 + if (block.event.event) { + core.clearRouteFolding(); + core.insertAction(block.event.event, block.x, block.y); + // 不再执行该点的系统事件 + return _executeCallback(); + } + + if (block.event.trigger && block.event.trigger != 'null') { + var noPass = block.event.noPass, + trigger = block.event.trigger; + if (noPass) core.clearAutomaticRouteNode(x, y); + + // 转换楼层能否穿透 + if ( + trigger == 'changeFloor' && + !noPass && + this._trigger_ignoreChangeFloor(block) + ) + return _executeCallback(); + core.status.automaticRoute.moveDirectly = false; + this.doSystemEvent(trigger, block); + } + return _executeCallback(); }; events.prototype._trigger_inAction = function (x, y) { @@ -587,7 +653,7 @@ events.prototype._openDoor_animate = function (block, x, y, callback) { ////// 开一个门后触发的事件 ////// events.prototype.afterOpenDoor = function (doorId, x, y) { - // Deprecated. See hook#afterOpenDoor. + this.eventdata.afterOpenDoor(doorId, x, y); }; events.prototype._sys_getItem = function (data, callback) { @@ -646,7 +712,7 @@ events.prototype.getItem = function (id, num, x, y, isGentleClick, callback) { }; events.prototype.afterGetItem = function (id, x, y, isGentleClick) { - // Deprecated. See hook#afterGetItem + this.eventdata.afterGetItem(id, x, y, isGentleClick); }; ////// 获得面前的物品(轻按) ////// @@ -686,7 +752,15 @@ events.prototype._getNextItem = function (direction, noRoute) { }; events.prototype._sys_changeFloor = function (data, callback) { - // see src/plugin/game/loopMap.js + data = data.event.data; + var heroLoc = {}; + if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] }; + if (data.direction) heroLoc.direction = data.direction; + if (core.status.event.id != 'action') core.status.event.id = null; + core.changeFloor(data.floorId, data.stair, heroLoc, data.time, function () { + core.replay(); + if (callback) callback(); + }); }; ////// 楼层切换 ////// diff --git a/public/libs/maps.js b/public/libs/maps.js index 6aa2790..ffc38a7 100644 --- a/public/libs/maps.js +++ b/public/libs/maps.js @@ -698,7 +698,44 @@ maps.prototype.getMapBlocksObj = function (floorId, noCache) { ////// 将背景前景层变成二维数组的形式 ////// maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { - // see src/plugin/game/loopMap.js + floorId = floorId || core.status.floorId; + if (!floorId) return []; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + if (!noCache && core.status[name + 'maps'][floorId]) + return core.status[name + 'maps'][floorId]; + + var arr = + main.mode == 'editor' && + !(window.editor && editor.uievent && editor.uievent.isOpen) + ? core.cloneArray(editor[name + 'map']) + : null; + if (arr == null) + arr = core.cloneArray(core.floors[floorId][name + 'map'] || []); + + for (var y = 0; y < height; ++y) { + if (arr[y] == null) arr[y] = Array(width).fill(0); + } + (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach(function ( + one + ) { + arr[one[1]][one[0]] = one[2] || 0; + }); + (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach(function ( + one + ) { + arr[one[1]][one[0]] = 0; + }); + if (main.mode == 'editor') { + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; + } + } + } + if (core.status[name + 'maps']) core.status[name + 'maps'][floorId] = arr; + return arr; }; maps.prototype.getBgMapArray = function (floorId) { diff --git a/public/main.js b/public/main.js index 425019f..7b1f400 100644 --- a/public/main.js +++ b/public/main.js @@ -5,7 +5,7 @@ function main() { this.version = '2.A'; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 this.useCompress = false; // 是否使用压缩文件 - this.pluginUseCompress = false; + this.pluginUseCompress = false; // 是否是构建版样板,不用改动 // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。 // 请注意,只有useCompress是false时才会读取floors目录下的文件,为true时会直接读取libs目录下的floors.min.js文件。 // 如果要进行剧本的修改请务必将其改成false。 @@ -804,74 +804,74 @@ main.prototype.listen = function () { }; ////// 手机端的按钮1-7 ////// - // main.statusBar.image.btn1.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 49, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn1.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 49, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn2.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 50, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn2.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 50, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn3.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 51, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn3.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 51, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn4.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 52, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn4.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 52, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn5.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 53, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn5.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 53, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn6.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 54, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn6.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 54, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn7.onclick = function (e) { - // e.stopPropagation(); - // core.onkeyUp({ - // keyCode: 55, - // altKey: core.getLocalStorage('altKey') - // }); - // }; + main.statusBar.image.btn7.onclick = function (e) { + e.stopPropagation(); + core.onkeyUp({ + keyCode: 55, + altKey: core.getLocalStorage('altKey') + }); + }; - // main.statusBar.image.btn8.onclick = function (e) { - // e.stopPropagation(); - // if (core.getLocalStorage('altKey')) { - // core.removeLocalStorage('altKey'); - // core.drawTip('Alt模式已关闭。'); - // main.statusBar.image.btn8.style.filter = ''; - // } else { - // core.setLocalStorage('altKey', true); - // core.drawTip('Alt模式已开启;此模式下1~7按钮视为Alt+1~7。'); - // main.statusBar.image.btn8.style.filter = 'sepia(1) contrast(1.5)'; - // } - // }; + main.statusBar.image.btn8.onclick = function (e) { + e.stopPropagation(); + if (core.getLocalStorage('altKey')) { + core.removeLocalStorage('altKey'); + core.drawTip('Alt模式已关闭。'); + main.statusBar.image.btn8.style.filter = ''; + } else { + core.setLocalStorage('altKey', true); + core.drawTip('Alt模式已开启;此模式下1~7按钮视为Alt+1~7。'); + main.statusBar.image.btn8.style.filter = 'sepia(1) contrast(1.5)'; + } + }; window.onblur = function () { if (core && core.control) { diff --git a/public/project/functions.js b/public/project/functions.js index fd48fff..9bd6959 100644 --- a/public/project/functions.js +++ b/public/project/functions.js @@ -254,6 +254,51 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { core.changeFloor(toId, stair, loc, null, callback); return true; + }, + afterGetItem: function () { + // 获得一个道具后触发的事件 + // itemId:获得的道具ID;x和y是该道具所在的坐标 + // isGentleClick:是否是轻按触发的 + + const potionItems = [ + 'redPotion', + 'bluePotion', + 'yellowPotion', + 'greenPotion' + ]; + + if (potionItems.includes(itemId)) core.playSound('回血'); + else core.playSound('获得道具'); + + const todo = []; + // 检查该点的获得道具后事件。 + if (core.status.floorId == null) return; + const event = + core.floors[core.status.floorId].afterGetItem[`${x},${y}`]; + if ( + event && + (event instanceof Array || + !isGentleClick || + !event.disableOnGentleClick) + ) { + core.unshift(todo, event); + } + + if (todo.length > 0) core.insertAction(todo, x, y); + }, + afterOpenDoor: function () { + // 开一个门后触发的事件 + const todo = []; + // 检查该点的获得开门后事件。 + if (core.status.floorId == null) return; + const event = + core.floors[core.status.floorId].afterOpenDoor[`${x},${y}`]; + if (event) core.unshift(todo, event); + + if (todo.length > 0) core.insertAction(todo, x, y); + + if (core.status.event.id == null) core.continueAutomaticRoute(); + else core.clearContinueAutomaticRoute(); } }, control: { diff --git a/public/project/plugins.js b/public/project/plugins.js index 4279d2e..0ceac9d 100644 --- a/public/project/plugins.js +++ b/public/project/plugins.js @@ -1,8 +1,229 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { init: function () { - // 这看不到插件,插件全放到plugin文件夹里面了,要看的话去 关于游戏 的开源地址里面看 - // 直接把仓库clone下来,或者下载zip都行 - // 脚本编辑同理,一部分在plugin里面 + // 对于一部分的常用插件,样板已经内置,同时配有开关,可以在设置里面开关 + // 以及那些只提供api的插件也是已经内置 + // 这里内置的插件只有不容易开关的插件 this._afterLoadResources = function () {}; + }, + fiveLayer: function () { + // 注册插件 + Mota.Plugin.register('fiveLayer_g', { init }, init); + // 创建新图层 + function createCanvas(name, zIndex) { + if (!name) return; + var canvas = document.createElement('canvas'); + canvas.id = name; + canvas.className = 'gameCanvas no-anti-aliasing'; + // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 + if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0; + // 将图层插入进游戏内容 + document.getElementById('gameDraw').appendChild(canvas); + var ctx = canvas.getContext('2d'); + core.canvas[name] = ctx; + + return canvas; + } + + function init() { + var bg2Canvas = createCanvas('bg2', 20); + var fg2Canvas = createCanvas('fg2', 63); + // 大地图适配 + core.bigmap.canvas = [ + 'bg2', + 'fg2', + 'bg', + 'event', + 'event2', + 'fg', + 'damage' + ]; + core.initStatus.bg2maps = {}; + core.initStatus.fg2maps = {}; + + if (main.mode == 'editor') { + /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ + // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) + // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) + document + .getElementById('mapEdit') + .insertBefore(bg2Canvas, document.getElementById('event')); + // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) + document + .getElementById('mapEdit') + .insertBefore(fg2Canvas, document.getElementById('ebm')); + // 原本有三个图层 从4开始添加 + var num = 4; + // 新增图层存入editor.dom中 + editor.dom.bg2c = core.canvas.bg2.canvas; + editor.dom.bg2Ctx = core.canvas.bg2; + editor.dom.fg2c = core.canvas.fg2.canvas; + editor.dom.fg2Ctx = core.canvas.fg2; + editor.dom.maps.push('bg2map', 'fg2map'); + editor.dom.canvas.push('bg2', 'fg2'); + + // 创建编辑器上的按钮 + var createCanvasBtn = name => { + // 电脑端创建按钮 + var input = document.createElement('input'); + // layerMod4/layerMod5 + var id = 'layerMod' + num++; + // bg2map/fg2map + var value = name + 'map'; + input.type = 'radio'; + input.name = 'layerMod'; + input.id = id; + input.value = value; + editor.dom[id] = input; + input.onchange = () => { + editor.uifunctions.setLayerMod(value); + }; + return input; + }; + + var createCanvasBtn_mobile = name => { + // 手机端往选择列表中添加子选项 + var input = document.createElement('option'); + var id = 'layerMod' + num++; + var value = name + 'map'; + input.name = 'layerMod'; + input.value = value; + editor.dom[id] = input; + return input; + }; + if (!editor.isMobile) { + var input = createCanvasBtn('bg2'); + var input2 = createCanvasBtn('fg2'); + // 获取事件层及其父节点 + var child = document.getElementById('layerMod'), + parent = child.parentNode; + // 背景层2插入事件层前 + parent.insertBefore(input, child); + // 不能直接更改背景层2的innerText 所以创建文本节点 + var txt = document.createTextNode('背2'); + // 插入事件层前(即新插入的背景层2前) + parent.insertBefore(txt, child); + // 向最后插入前景层2(即插入前景层后) + parent.appendChild(input2); + var txt2 = document.createTextNode('前2'); + parent.appendChild(txt2); + } else { + var input = createCanvasBtn_mobile('bg2'); + var input2 = createCanvasBtn_mobile('fg2'); + // 手机端因为是选项 所以可以直接改innerText + input.innerText = '背景2'; + input2.innerText = '前景2'; + var parent = document.getElementById('layerMod'); + parent.insertBefore(input, parent.children[1]); + parent.appendChild(input2); + } + } + + maps.prototype._loadFloor_doNotCopy = function () { + return [ + 'firstArrive', + 'eachArrive', + 'blocks', + 'parallelDo', + 'map', + 'bgmap', + 'fgmap', + 'bg2map', + 'fg2map', + 'events', + 'changeFloor', + 'afterBattle', + 'afterGetItem', + 'afterOpenDoor', + 'cannotMove', + 'enemy' + ]; + }; + ////// 绘制背景和前景层 ////// + maps.prototype._drawBg_draw = function ( + floorId, + toDrawCtx, + cacheCtx, + config + ) { + config.ctx = cacheCtx; + core.maps._drawBg_drawBackground(floorId, config); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages( + floorId, + config.ctx, + 'bg', + null, + null, + config.onMap + ); + core.maps._drawBgFgMap(floorId, 'bg', config); + if (config.onMap) { + core.drawImage( + toDrawCtx, + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + core.clearMap('bg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'bg2', config); + if (config.onMap) + core.drawImage( + 'bg2', + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + config.ctx = toDrawCtx; + }; + maps.prototype._drawFg_draw = function ( + floorId, + toDrawCtx, + cacheCtx, + config + ) { + config.ctx = cacheCtx; + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages( + floorId, + config.ctx, + 'fg', + null, + null, + config.onMap + ); + core.maps._drawBgFgMap(floorId, 'fg', config); + if (config.onMap) { + core.drawImage( + toDrawCtx, + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + core.clearMap('fg2'); + core.clearMap(cacheCtx); + } + core.maps._drawBgFgMap(floorId, 'fg2', config); + if (config.onMap) + core.drawImage( + 'fg2', + cacheCtx.canvas, + core.bigmap.v2 ? -32 : 0, + core.bigmap.v2 ? -32 : 0 + ); + config.ctx = toDrawCtx; + }; + ////// 移动判定 ////// + maps.prototype._generateMovableArray_arrays = function (floorId) { + return { + bgArray: this.getBgMapArray(floorId), + fgArray: this.getFgMapArray(floorId), + eventArray: this.getMapArray(floorId), + bg2Array: this._getBgFgMapArray('bg2', floorId), + fg2Array: this._getBgFgMapArray('fg2', floorId) + }; + }; + } } }; diff --git a/src/core/index.ts b/src/core/index.ts index a4a9ed2..30a4981 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -12,15 +12,39 @@ import { mainSetting, settingStorage } from './main/setting'; -import { KeyCode } from '@/plugin/keyCodes'; +import { KeyCode, ScanCode } from '@/plugin/keyCodes'; import { status } from '@/plugin/ui/statusBar'; import './plugin'; import './package'; import { AudioPlayer } from './audio/audio'; import { CustomToolbar } from './main/custom/toolbar'; -import { Hotkey } from './main/custom/hotkey'; -import { Keyboard } from './main/custom/keyboard'; +import { + Hotkey, + checkAssist, + isAssist, + unwarpBinary +} from './main/custom/hotkey'; +import { Keyboard, generateKeyboardEvent } from './main/custom/keyboard'; import './main/layout'; +import { MComponent, icon, m } from './main/layout'; +import { createSettingComponents } from './main/init/settings'; +import { + createToolbarComponents, + createToolbarEditorComponents +} from './main/init/toolbar'; +import { VirtualKey } from './main/init/misc'; +import * as utils from '@/plugin/utils'; +import * as use from '@/plugin/use'; +import * as mark from '@/plugin/mark'; +import * as keyCodes from '@/plugin/keyCodes'; +import { addAnimate, removeAnimate } from '@/plugin/animateController'; +import * as bookTools from '@/plugin/ui/book'; +import * as commonTools from '@/plugin/ui/common'; +import * as equipboxTools from '@/plugin/ui/equipbox'; +import * as fixedTools from '@/plugin/ui/fixed'; +import * as flyTools from '@/plugin/ui/fly'; +import * as statusBarTools from '@/plugin/ui/statusBar'; +import * as toolboxTools from '@/plugin/ui/toolbox'; // ----- 类注册 Mota.register('class', 'AudioPlayer', AudioPlayer); @@ -36,8 +60,16 @@ Mota.register('class', 'SettingDisplayer', SettingDisplayer); Mota.register('class', 'SoundController', SoundController); Mota.register('class', 'SoundEffect', SoundEffect); Mota.register('class', 'UiController', UiController); +Mota.register('class', 'MComponent', MComponent); // ----- 函数注册 - +Mota.register('fn', 'm', m); +Mota.register('fn', 'icon', icon); +Mota.register('fn', 'unwrapBinary', unwarpBinary); +Mota.register('fn', 'checkAssist', checkAssist); +Mota.register('fn', 'isAssist', isAssist); +Mota.register('fn', 'generateKeyboardEvent', generateKeyboardEvent); +Mota.register('fn', 'addAnimate', addAnimate); +Mota.register('fn', 'removeAnimate', removeAnimate); // ----- 变量注册 Mota.register('var', 'mainUi', mainUi); Mota.register('var', 'fixedUi', fixedUi); @@ -46,7 +78,29 @@ Mota.register('var', 'sound', sound); Mota.register('var', 'gameKey', gameKey); Mota.register('var', 'mainSetting', mainSetting); Mota.register('var', 'KeyCode', KeyCode); +Mota.register('var', 'ScanCode', ScanCode); Mota.register('var', 'settingStorage', settingStorage); Mota.register('var', 'status', status); // ----- 模块注册 +Mota.register('module', 'CustomComponents', { + createSettingComponents, + createToolbarComponents, + createToolbarEditorComponents +}); +Mota.register('module', 'MiscComponents', { + VirtualKey +}); +Mota.register('module', 'RenderUtils', utils); +Mota.register('module', 'Use', use); +Mota.register('module', 'Mark', mark); +Mota.register('module', 'KeyCodes', keyCodes); +Mota.register('module', 'UITools', { + book: bookTools, + common: commonTools, + equipbox: equipboxTools, + fixed: fixedTools, + fly: flyTools, + statusBar: statusBarTools, + toolbox: toolboxTools +}); diff --git a/src/core/plugin.ts b/src/core/plugin.ts index 26cedbd..72857f8 100644 --- a/src/core/plugin.ts +++ b/src/core/plugin.ts @@ -27,6 +27,7 @@ import * as frag from '@/plugin/fx/frag'; import * as use from '@/plugin/use'; import * as gameCanvas from '@/plugin/fx/gameCanvas'; import * as smooth from '@/plugin/fx/smoothView'; +import * as shader from './fx/shader'; Mota.Plugin.register('shadow_r', shadow, shadow.init); Mota.Plugin.register('gameShadow_r', gameShadow, gameShadow.init); @@ -38,3 +39,4 @@ Mota.Plugin.register('frag_r', frag, frag.init); Mota.Plugin.register('use_r', use); Mota.Plugin.register('gameCanvas_r', gameCanvas); Mota.Plugin.register('smooth_r', smooth, smooth.init); +Mota.Plugin.register('shader_r', shader); diff --git a/src/game/system.ts b/src/game/system.ts index b6a1acd..beeb05d 100644 --- a/src/game/system.ts +++ b/src/game/system.ts @@ -7,8 +7,16 @@ import type { IndexedEventEmitter } from '@/core/common/eventEmitter'; import type { loading } from './game'; -import type { Hotkey } from '@/core/main/custom/hotkey'; -import type { Keyboard } from '@/core/main/custom/keyboard'; +import type { + Hotkey, + unwarpBinary, + checkAssist, + isAssist +} from '@/core/main/custom/hotkey'; +import type { + Keyboard, + generateKeyboardEvent +} from '@/core/main/custom/keyboard'; import type { CustomToolbar } from '@/core/main/custom/toolbar'; import type { Focus, GameUi, UiController } from '@/core/main/custom/ui'; import type { gameListener, hook } from './game'; @@ -17,8 +25,25 @@ import type { GameStorage } from '@/core/main/storage'; import type { DamageEnemy, EnemyCollection } from './enemy/damage'; import type { specials } from './enemy/special'; import type { Range } from '@/plugin/game/range'; -import type { KeyCode } from '@/plugin/keyCodes'; +import type { KeyCode, ScanCode } from '@/plugin/keyCodes'; import type { Ref } from 'vue'; +import type { MComponent, m, icon } from '@/core/main/layout'; +import type { addAnimate, removeAnimate } from '@/plugin/animateController'; +import type { createSettingComponents } from '@/core/main/init/settings'; +import type { + createToolbarComponents, + createToolbarEditorComponents +} from '@/core/main/init/toolbar'; +import type * as use from '@/plugin/use'; +import type * as mark from '@/plugin/mark'; +import type * as keyCodes from '@/plugin/keyCodes'; +import type * as bookTools from '@/plugin/ui/book'; +import type * as commonTools from '@/plugin/ui/common'; +import type * as equipboxTools from '@/plugin/ui/equipbox'; +import type * as fixedTools from '@/plugin/ui/fixed'; +import type * as flyTools from '@/plugin/ui/fly'; +import type * as statusBarTools from '@/plugin/ui/statusBar'; +import type * as toolboxTools from '@/plugin/ui/toolbox'; import type * as battle from './enemy/battle'; import type * as hero from './hero'; @@ -27,7 +52,7 @@ interface ClassInterface { EventEmitter: typeof EventEmitter; IndexedEventEmitter: typeof IndexedEventEmitter; Disposable: typeof Disposable; - // 定义于渲染进程,录像中会进行polyfill,但是不执行任何内容 + // 定义于渲染进程,录像验证使用会报错 GameStorage: typeof GameStorage; MotaSetting: typeof MotaSetting; SettingDisplayer: typeof SettingDisplayer; @@ -41,7 +66,7 @@ interface ClassInterface { SoundEffect: typeof SoundEffect; SoundController: typeof SoundController; BgmController: typeof BgmController; - // todo: 放到插件 ShaderEffect: typeof ShaderEffect; + MComponent: typeof MComponent; // 定义于游戏进程,渲染进程依然可用 Range: typeof Range; EnemyCollection: typeof EnemyCollection; @@ -52,11 +77,15 @@ type _IBattle = typeof battle; type _IHero = typeof hero; interface FunctionInterface extends _IBattle, _IHero { - // 定义于渲染进程,录像中会进行polyfill,但是不执行任何内容 - readyAllResource(): void; - // 定义于游戏进程,渲染进程依然可用 - - // todo + // 定义于渲染进程,录像验证中会出错 + m: typeof m; + icon: typeof icon; + unwrapBinary: typeof unwarpBinary; + checkAssist: typeof checkAssist; + isAssist: typeof isAssist; + generateKeyboardEvent: typeof generateKeyboardEvent; + addAnimate: typeof addAnimate; + removeAnimate: typeof removeAnimate; } interface VariableInterface { @@ -69,7 +98,7 @@ interface VariableInterface { mainUi: UiController; fixedUi: UiController; KeyCode: typeof KeyCode; - // isMobile: boolean; + ScanCode: typeof ScanCode; bgm: BgmController; sound: SoundController; settingStorage: GameStorage; @@ -79,7 +108,23 @@ interface VariableInterface { enemySpecials: typeof specials; } -interface ModuleInterface {} +interface ModuleInterface { + CustomComponents: { + createSettingComponents: typeof createSettingComponents; + createToolbarComponents: typeof createToolbarComponents; + createToolbarEditorComponents: typeof createToolbarEditorComponents; + }; + Use: typeof use; + Mark: typeof mark; + KeyCodes: typeof keyCodes; + UITools: typeof bookTools & + typeof commonTools & + typeof equipboxTools & + typeof fixedTools & + typeof flyTools & + typeof statusBarTools & + typeof toolboxTools; +} interface SystemInterfaceMap { class: ClassInterface; @@ -516,7 +561,8 @@ function r( fn: (this: T, packages: PackageInterface) => void, thisArg?: T ) { - if (!main.replayChecking) fn.call(thisArg as T, MPackage.requireAll()); + if (!main.replayChecking || main.mode === 'editor') + fn.call(thisArg as T, MPackage.requireAll()); } /** @@ -535,7 +581,7 @@ function rf any, T>( thisArg?: T ): (this: T, ...params: Parameters) => ReturnType | undefined { // @ts-ignore - if (main.replayChecking) return () => {}; + if (main.replayChecking || main.mode === 'editor') return () => {}; else { return (...params) => { return fn.call(thisArg, ...params); diff --git a/src/plugin/animateController.ts b/src/plugin/animateController.ts index c5beb57..bd9ac7a 100644 --- a/src/plugin/animateController.ts +++ b/src/plugin/animateController.ts @@ -1,8 +1,10 @@ +import { loading } from '@/game/game'; + const animation: ((time: number) => void)[] = []; let animateTime = 0; -export default function init() { +loading.once('coreInit', () => { core.registerAnimationFrame('animateController', true, time => { if (time - animateTime <= core.values.animateSpeed) return; for (const fn of animation) { @@ -10,8 +12,7 @@ export default function init() { } animateTime = core.animateFrame.animateTime; }); - return { addAnimate, removeAnimate }; -} +}); /** * 添加一个动画 diff --git a/src/plugin/game/fiveLayer.ts b/src/plugin/game/fiveLayer.ts deleted file mode 100644 index f4531f5..0000000 --- a/src/plugin/game/fiveLayer.ts +++ /dev/null @@ -1,219 +0,0 @@ -// @ts-nocheck - -// 创建新图层 -function createCanvas(name, zIndex) { - if (!name) return; - var canvas = document.createElement('canvas'); - canvas.id = name; - canvas.className = 'gameCanvas no-anti-aliasing'; - // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 - if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0; - // 将图层插入进游戏内容 - document.getElementById('gameDraw').appendChild(canvas); - var ctx = canvas.getContext('2d'); - core.canvas[name] = ctx; - - return canvas; -} - -export function init() { - var bg2Canvas = createCanvas('bg2', 20); - var fg2Canvas = createCanvas('fg2', 63); - // 大地图适配 - core.bigmap.canvas = [ - 'bg2', - 'fg2', - 'bg', - 'event', - 'event2', - 'fg', - 'damage' - ]; - core.initStatus.bg2maps = {}; - core.initStatus.fg2maps = {}; - - if (main.mode == 'editor') { - /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ - // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) - // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) - document - .getElementById('mapEdit') - .insertBefore(bg2Canvas, document.getElementById('event')); - // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) - document - .getElementById('mapEdit') - .insertBefore(fg2Canvas, document.getElementById('ebm')); - // 原本有三个图层 从4开始添加 - var num = 4; - // 新增图层存入editor.dom中 - editor.dom.bg2c = core.canvas.bg2.canvas; - editor.dom.bg2Ctx = core.canvas.bg2; - editor.dom.fg2c = core.canvas.fg2.canvas; - editor.dom.fg2Ctx = core.canvas.fg2; - editor.dom.maps.push('bg2map', 'fg2map'); - editor.dom.canvas.push('bg2', 'fg2'); - - // 创建编辑器上的按钮 - var createCanvasBtn = name => { - // 电脑端创建按钮 - var input = document.createElement('input'); - // layerMod4/layerMod5 - var id = 'layerMod' + num++; - // bg2map/fg2map - var value = name + 'map'; - input.type = 'radio'; - input.name = 'layerMod'; - input.id = id; - input.value = value; - editor.dom[id] = input; - input.onchange = () => { - editor.uifunctions.setLayerMod(value); - }; - return input; - }; - - var createCanvasBtn_mobile = name => { - // 手机端往选择列表中添加子选项 - var input = document.createElement('option'); - var id = 'layerMod' + num++; - var value = name + 'map'; - input.name = 'layerMod'; - input.value = value; - editor.dom[id] = input; - return input; - }; - if (!editor.isMobile) { - var input = createCanvasBtn('bg2'); - var input2 = createCanvasBtn('fg2'); - // 获取事件层及其父节点 - var child = document.getElementById('layerMod'), - parent = child.parentNode; - // 背景层2插入事件层前 - parent.insertBefore(input, child); - // 不能直接更改背景层2的innerText 所以创建文本节点 - var txt = document.createTextNode('背2'); - // 插入事件层前(即新插入的背景层2前) - parent.insertBefore(txt, child); - // 向最后插入前景层2(即插入前景层后) - parent.appendChild(input2); - var txt2 = document.createTextNode('前2'); - parent.appendChild(txt2); - } else { - var input = createCanvasBtn_mobile('bg2'); - var input2 = createCanvasBtn_mobile('fg2'); - // 手机端因为是选项 所以可以直接改innerText - input.innerText = '背景2'; - input2.innerText = '前景2'; - var parent = document.getElementById('layerMod'); - parent.insertBefore(input, parent.children[1]); - parent.appendChild(input2); - } - } - - maps.prototype._loadFloor_doNotCopy = function () { - return [ - 'firstArrive', - 'eachArrive', - 'blocks', - 'parallelDo', - 'map', - 'bgmap', - 'fgmap', - 'bg2map', - 'fg2map', - 'events', - 'changeFloor', - 'afterBattle', - 'afterGetItem', - 'afterOpenDoor', - 'cannotMove', - 'enemy' - ]; - }; - ////// 绘制背景和前景层 ////// - maps.prototype._drawBg_draw = function ( - floorId, - toDrawCtx, - cacheCtx, - config - ) { - config.ctx = cacheCtx; - core.maps._drawBg_drawBackground(floorId, config); - // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 - core.maps._drawFloorImages( - floorId, - config.ctx, - 'bg', - null, - null, - config.onMap - ); - core.maps._drawBgFgMap(floorId, 'bg', config); - if (config.onMap) { - core.drawImage( - toDrawCtx, - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - core.clearMap('bg2'); - core.clearMap(cacheCtx); - } - core.maps._drawBgFgMap(floorId, 'bg2', config); - if (config.onMap) - core.drawImage( - 'bg2', - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - config.ctx = toDrawCtx; - }; - maps.prototype._drawFg_draw = function ( - floorId, - toDrawCtx, - cacheCtx, - config - ) { - config.ctx = cacheCtx; - // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 - core.maps._drawFloorImages( - floorId, - config.ctx, - 'fg', - null, - null, - config.onMap - ); - core.maps._drawBgFgMap(floorId, 'fg', config); - if (config.onMap) { - core.drawImage( - toDrawCtx, - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - core.clearMap('fg2'); - core.clearMap(cacheCtx); - } - core.maps._drawBgFgMap(floorId, 'fg2', config); - if (config.onMap) - core.drawImage( - 'fg2', - cacheCtx.canvas, - core.bigmap.v2 ? -32 : 0, - core.bigmap.v2 ? -32 : 0 - ); - config.ctx = toDrawCtx; - }; - ////// 移动判定 ////// - maps.prototype._generateMovableArray_arrays = function (floorId) { - return { - bgArray: this.getBgMapArray(floorId), - fgArray: this.getFgMapArray(floorId), - eventArray: this.getMapArray(floorId), - bg2Array: this._getBgFgMapArray('bg2', floorId), - fg2Array: this._getBgFgMapArray('fg2', floorId) - }; - }; -} diff --git a/src/plugin/game/index.ts b/src/plugin/game/index.ts index 5374d54..e5829bd 100644 --- a/src/plugin/game/index.ts +++ b/src/plugin/game/index.ts @@ -1,38 +1,24 @@ -/* @__PURE__ */ import './dev/hotReload'; // 仅开发会用到 -import * as fiveLayer from './fiveLayer'; +/* @__PURE__ */ import './dev/hotReload.js'; // 仅开发会用到 import * as heroFourFrames from './fx/heroFourFrames'; import * as itemDetail from './fx/itemDetail'; import * as replay from './replay'; import * as ui from './ui'; import * as rewrite from './fx/rewrite'; import * as halo from './fx/halo'; -import * as loopMap from './loopMap'; import * as removeMap from './removeMap'; import * as shop from './shop'; -import * as skill from './skill'; -import * as skillTree from './skillTree'; -import * as study from './study'; -import * as towerBoss from './towerBoss'; import * as utils from './utils'; -import * as chase from './chase'; import * as remainEnemy from './enemy/remainEnemy'; import * as checkBlock from './enemy/checkblock'; Mota.Plugin.register('utils_g', utils); -Mota.Plugin.register('loopMap_g', loopMap, loopMap.init); Mota.Plugin.register('shop_g', shop); Mota.Plugin.register('replay_g', replay, replay.init); -Mota.Plugin.register('skillTree_g', skillTree); Mota.Plugin.register('removeMap_g', removeMap); -Mota.Plugin.register('chase_g', chase); -Mota.Plugin.register('skill_g', skill); -Mota.Plugin.register('towerBoss_g', towerBoss); -Mota.Plugin.register('fiveLayer_g', fiveLayer, fiveLayer.init); Mota.Plugin.register('heroFourFrames_g', heroFourFrames, heroFourFrames.init); Mota.Plugin.register('rewrite_g', rewrite, rewrite.init); Mota.Plugin.register('itemDetail_g', itemDetail, itemDetail.init); Mota.Plugin.register('halo_g', halo); -Mota.Plugin.register('study_g', study); Mota.Plugin.register('remainEnemy_g', remainEnemy); Mota.Plugin.register('checkBlock_g', checkBlock, checkBlock.init); // todo: 这几个不应该放到插件 diff --git a/src/plugin/game/replay.ts b/src/plugin/game/replay.ts index 6c96016..a37cf42 100644 --- a/src/plugin/game/replay.ts +++ b/src/plugin/game/replay.ts @@ -1,7 +1,3 @@ -import { upgradeSkill } from './skillTree'; - -const replayableSettings = ['autoSkill']; - let cliping = false; let startIndex = 0; @@ -21,45 +17,6 @@ export function clip(...replace: string[]) { } export function init() { - // 注册修改设置的录像操作 - core.registerReplayAction('settings', name => { - if (!name.startsWith('set:')) return false; - const [, setting, value] = name.split(':'); - const v = eval(value); - if (typeof v !== 'boolean') return false; - if (!replayableSettings.includes(setting)) return false; - flags[setting] = v; - core.status.route.push(name); - core.replay(); - return true; - }); - - core.registerReplayAction('upgradeSkill', name => { - if (!name.startsWith('skill:')) return false; - const skill = parseInt(name.slice(6)); - upgradeSkill(skill); - core.status.route.push(name); - core.replay(); - return true; - }); - - core.registerReplayAction('study', name => { - // todo - // if (!name.startsWith('study:')) return false; - // const [num, x, y] = name - // .slice(6) - // .split(',') - // .map(v => parseInt(v)); - // if (!canStudySkill(num)) return false; - // const id = core.getBlockId(x, y); - // const enemy = core.getEnemyInfo(id, void 0, x, y); - // if (!enemy.special.includes(num)) return false; - // studySkill(enemy, num); - // core.status.route.push(name); - // core.replay(); - return true; - }); - // 商店 let shopOpened = false; let openedShopId = ''; diff --git a/src/plugin/keyCodes.ts b/src/plugin/keyCodes.ts index 4af6901..2f1ce09 100644 --- a/src/plugin/keyCodes.ts +++ b/src/plugin/keyCodes.ts @@ -8,7 +8,7 @@ * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx * But these are "more general", as they should work across browsers & OS`s. */ -export enum KeyCode { +export enum KeyCode { DependsOnKbLayout = -1, /** @@ -223,7 +223,7 @@ export enum KeyCode { /** * keyboardEvent.code */ -export const enum ScanCode { +export enum ScanCode { DependsOnKbLayout = -1, None, Hyper, diff --git a/src/plugin/ui/toolbox.tsx b/src/plugin/ui/toolbox.tsx index d4df3d8..af318b3 100644 --- a/src/plugin/ui/toolbox.tsx +++ b/src/plugin/ui/toolbox.tsx @@ -1,5 +1,3 @@ -import { has } from '../utils'; - interface ClsMap { tools: '消耗道具'; constants: '永久道具';