From 9ecbd2aa42346faca04e4d0cc64e5293220ac2ab Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 2 Apr 2019 02:38:37 +0800 Subject: [PATCH] api for actions & control --- _docs/api.md | 714 +++++++++++++++++++++++++++++++++++++- _docs/event.md | 12 +- _docs/personalization.md | 2 +- _server/MotaAction.g4 | 29 +- _server/editor_blockly.js | 4 +- libs/actions.js | 22 +- libs/control.js | 43 ++- libs/core.js | 5 +- libs/events.js | 8 +- main.js | 8 +- project/floors/sample1.js | 4 +- 11 files changed, 777 insertions(+), 74 deletions(-) diff --git a/_docs/api.md b/_docs/api.md index 873aa9a2..51e2b384 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -63,7 +63,7 @@ Elements页可以查看网页的源代码,调整css布局等。 ## 整体项目架构 -``` bash +``` text ├── /_server/ # 为可视化地图编辑器提供一些支持的目录 ├── /libs/ # ---- 系统库目录 ---- │ ├─ /thirdparty/ # 游戏所用到的第三方库文件 @@ -101,11 +101,11 @@ Elements页可以查看网页的源代码,调整css布局等。 └── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 ``` -`_server`为****,里面存放了地图编辑器相关的各项内容。 +`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 `libs`为**系统库目录**,里面存放了各个系统核心函数。 -从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数) +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 `project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 @@ -167,10 +167,11 @@ core.getFlag = function (name, defaultValue) { ### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + ```js // 重写ui.js中的_drawBook_drawBackground函数 core.ui._drawBook_drawBackground = function () { - // 调用core.drawBackground函数来绘制winskin // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); } @@ -178,8 +179,10 @@ core.ui._drawBook_drawBackground = function () { ### 重写点击楼传事件 +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 + ```js -// 重写点击楼传事件 +// 重写events.js的useFly函数,即点击楼传按钮时的事件 core.events.useFly = function (fromUserAction) { if (core.isMoving()) { core.drawTip("请先停止勇士行动"); @@ -192,10 +195,14 @@ core.events.useFly = function (fromUserAction) { } ``` +其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 + ### 楼层切换时根据flag来播放不同的音效 +整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 + ```js -// 重写events.js中的_changeFloor_beforeChange,修改音效值 +// 复制重写events.js中的_changeFloor_beforeChange,修改音效 core.events._changeFloor_beforeChange = function (info, callback) { // 直接替换原始函数中的 core.playSound('floor.mp3'); if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); @@ -203,7 +210,7 @@ core.events._changeFloor_beforeChange = function (info, callback) { if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); // ... - // 下面是原始函数中的代码 + // 下面是原始函数中的剩余代码,保持不变 window.setTimeout(function () { if (info.time == 0) core.events._changeFloor_changing(info, callback); @@ -245,16 +252,707 @@ core.maps.drawMap = function (floorId, callback) { } ``` -详见[`call`或`apply`的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 ## 附录:API列表 +这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 + +本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 + +如有任何疑问,请联系小艾寻求帮助。 + ### core.js +core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 + +但是,core中定义了很多游戏运行时的状态,这些状态很多都会被使用到。 + +``` text +core.__SIZE__, core.__PIXELS__ +游戏窗口大小;对于13x13的游戏而言这两个值分别是13和416,15x15来说分别是15和480。 + + +core.material +游戏中的所有资源列表,具体分为如下内容: +core.material.animates (动画) +core.material.bgms (背景音乐) +core.material.enemys (怪物信息,来自于 project/enemys.js) +core.material.icons (图标信息,来自于 project/icons.js) +core.material.images (图片素材,存放了各项素材图片如items.png等) + core.material.images.autotile (所有的自动元件图片) + core.material.images.tilesets (所有的额外素材图片) + core.material.images.images (用户引入的其他图片) +core.material.items (道具信息) +core.material.sounds (音效) + + +core.animateFrame +主要是记录和requestAnimationFrame相关的一些数据,常用的如下: +core.animateFrame.totalTime (游戏总的运行时时间) +core.animateFrame.weather (当前的天气信息) +core.animateFrame.asyncId (当前的异步处理事件的内容) + + +core.musicStatus +主要是记录和音效相关的内容,常用的如下: +core.musicStatus.bgmStatus (音乐开启状态) +core.musicStatus.soundStatus (音效开启状态) +core.musicStatus.playingBgm (当前正在播放的BGM) +core.musicStatus.lastBgm (最近一次尝试播放的BGM) +core.musicStatus.volume (当前的音量) +core.musicStatus.cachedBgms (背景音乐的缓存内从) +core.musicStatus.cacheBgmCount (背景音乐的缓存数量,默认值是4) + + +core.platform +游戏平台相关信息,常见的几个如下: +core.platform.isPC (是否是电脑端) +core.platform.isAndroid (是否是安卓端) +core.platform.isIOS (是否是iOS端) +core.platform.useLocalForage (是否开启了新版存档) +core.platform.extendKeyBoard (是否开启了拓展键盘) + + +core.domStyle +游戏的界面信息,包含如下几个: +core.domStyle.scale (当前的放缩比) +core.domStyle.isVertical (当前是否是竖屏状态) +core.domStyle.showStatusBar (当前是否显示状态栏) +core.domStyle.toolbarBtn (当前是否显示工具栏) + + +core.bigmap +当前的地图的尺寸信息,主要包含如下几个 +core.bigmap.width (当前地图的宽度) +core.bigmap.height (当前地图的高度) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素x) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素y) +core.bigmap.tempCanvas (一个临时画布,可以用来临时绘制很多东西) + + +core.saves +和存档相关的信息,包含如下几个: +core.saves.saveIndex (上次保存或读取的存档编号) +core.saves.ids (当前存在存档的编号列表) +core.saves.autosave (自动存档的信息) +core.saves.favorite (收藏的存档) +core.saves.favoriteNames (自定义存档的名称) + + +core.status +游戏的状态相关,是整个游戏中最重要的东西,其核心是如下几条: +请注意,每次重新开始、存档或读档时,core.status都会重新初始化。 +core.status.played (当前是否在游戏中) +core.status.gameOver (当前是否已经游戏结束,即win或lose) +core.status.hero (勇士信息;此项和全塔属性中的hero大体是对应的) + core.status.hero.name 勇士名 + core.status.hero.lv 当前等级 + core.status.hero.hpmax 当前生命上限 + core.status.hero.hp 当前生命值 + core.status.hero.manamax 当前魔力上限 + core.status.hero.mana 当前魔力值 + core.status.hero.atk 当前攻击力 + core.status.hero.def 当前防御力 + core.status.hero.mdef 当前魔防值 + core.status.hero.money 当前金币值 + core.status.hero.experience 当前经验值 + core.status.hero.loc 当前的位置信息 + core.status.hero.equipment 当前装上的装备 + core.status.hero.items 当前拥有的道具信息 + core.status.hero.flags 当前的各项flag信息 + core.status.hero.step 当前的步数值 + core.status.hero.statistics 当前的统计信息 +core.status.floorId (当前所在的楼层) +core.status.maps (所有的地图信息) +core.status.thisMap (当前的地图信息,等价于core.status.maps[core.status.floorId]) +core.status.bgmaps (所有背景层的信息) +core.status.fgmaps (所有的前景层的信息) +core.status.checkBlock (地图上的阻激夹域信息,也作为光环的缓存) +core.status.lockControl (当前是否是控制锁定状态) +core.status.automaticRoute (当前的自动寻路信息) +core.status.route (当前记录的录像) +core.status.replay (录像回放时要用到的信息) +core.status.shops (所有全局商店信息) +core.status.textAttribute (当前的文字属性,如颜色、背景等信息,和setText事件对应) +core.status.globalAttribute (当前的全局属性,如边框色、装备栏等) +core.status.curtainColor (当前色调层的颜色) +core.status.globalAnimateObjs (当前的全局帧动画效果) +core.status.floorAnimateObjs (当前的楼层贴图帧动画效果) +core.status.boxAnimateObjs (当前的盒子帧动画效果,例如怪物手册中的怪物) +core.status.autotileAnimateObjs (当前楼层的自动元件动画效果) +core.status.globalAnimateStatus (当前的帧动画的状态) +core.status.animateObjs (当前的播放动画信息) + + +core.floorIds +一个数组,表示所有的楼层ID,和全塔属性中的floorIds一致。 + + +core.floors +从楼层文件中读取全部的地图数据。 +和core.status.maps不同的是,后者在每次重新开始和读档时都会重置,也允许被修改(会存入存档)。 +而core.floors全程唯一,不允许被修改。 + + +core.statusBar +状态栏信息,例如状态栏图片,图标,以及各个内容的DOM定义等。 +core.statusBar.images (所有的系统图标,和icons.png对应) +core.statusBar.icons (状态栏中绘制的图标内容) + + +core.values +所有的全局数值信息,和全塔属性中的values一致。 +此项允许被直接修改,会存入存档。 + + +core.flags +所有的全塔开关,和全塔属性中的flags一致。 +此项不允许被直接修改,如有需要请使用“设置系统开关”事件,或者调用core.setGlobalFlag这个API。 + + +core.plugin +定义的插件函数。 + + +core.doFunc(func, _this) +执行一个函数,func为函数体或者插件中的函数名,_this为使用的this。 +如果func为一个字符串,则视为插件中的函数名,同时_this将被设置成core.plugin。 +此函数剩余参数将作为参数被传入func。 +``` + ### actions.js +actions.js主要是处理一些和用户交互相关的内容。 + +``` js +core.registerAction(action, name, func, priority) +注册一个用户交互行为。 +action:要注册的交互类型,如 ondown, onclick, keyDown 等等。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +priority:优先级;优先级高的被注册项将会被执行。此项可不填,默认为0。 +返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 + + +core.unregisterAction(action, name) +注销一个用户交互行为。 + + +core.doRegisteredAction(action) +执行一个用户交互行为。 +此函数将在该交互行为所注册的所有函数中,按照优先级从高到底依次执行。 +此函数剩余的参数将会作为参数传入该执行函数中。 +当某个执行函数返回true时将终止这一过程。 + + +core.onkeyDown(e) +当按下某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.onkeyUp(e) +当放开某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.pressKey(keyCode) +当按住某个键不动时的操作,目前只对方向键有效。 +如果需要添加对于其他键的长按,请复写_sys_onkeyDown和_sys_onkeyUp。 +请勿直接覆盖或调用此函数,如有需要请注册一个"pressKey"的交互函数。 + + +core.keyDown(keyCode) +当按下某个键时的操作,参数为该键的keyCode值。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDown"的交互函数。 + + +core.keyUp(keyCode, altKey, fromReplay) +当按下某个键时的操作,参数为该键的keyCode值。 +altKey标志了Alt键是否同时被按下,fromReplay表示是否是从录像回放中调用的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyUp"的交互函数。 + + +core.ondown(loc) +当点击屏幕时的操作。loc为点击的信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"ondown"的交互函数。 +注册的ondown交互函数需要接受x, y, px, py四个参数,代表点击的位置和像素坐标。 + + +core.onmove(loc) +当在屏幕上滑动时的操作。loc为当前的坐标信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmove"的交互函数。 +注册的onmove交互函数需要接受x, y, px, py四个参数,代表当前的的位置和像素坐标。 + + +core.onup() +当从屏幕上离开时的操作。请注意此函数是没有参数的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onup"的交互函数。 + + +core.onclick(x, y) +当点击屏幕上的某点位置时执行的操作,请注意这里的x和y是位置坐标。 +一般而言,一个完整的ondown到onup将触发一个onclick事件。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onclick"的交互函数。 + + +core.onmousewheel(direct) +当滚动鼠标滑轮时执行的操作。direct为滑轮方向,上为1,下为-1。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmousewheel"的交互函数。 + + +core.keyDownCtrl() +当长按Ctrl键不动时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 + + +core.longClick() +当长按住屏幕时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 +注册的交互函数如果某一项返回true,则之后仍然会继续触发该长按, +如果全部返回false则将停止本次长按行为,直到手指离开屏幕并重新进行长按为止。 +``` + ### control.js +control.js将负责整个游戏的核心控制系统,分为如下几个部分: +- requestAnimationFrame相关 +- 标题界面,开始和重新开始游戏 +- 自动寻路和人物行走相关 +- 画布、位置、阻激夹域、显伤等相关 +- 录像的回放相关 +- 存读档,自动存档,同步存档等相关 +- 人物属性和状态、位置、变量等相关 +- 天气、色调、音乐和音效的播放 +- 状态栏和工具栏相关 +- 界面resize相关 + +```text +// ------ requestAnimationFrame 相关 ------ // + +core.registerAnimationFrame(name, needPlaying, func) +注册一个animationFrame。它将在每次浏览器的帧刷新时(约16.6ms)被执行。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +needPlaying:如果此项为true,则仅在游戏开始后才会被执行(标题界面不执行) +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +func可以接受一个timestamp作为参数,表示从整个页面加载完毕到当前时刻所经过的毫秒数。 +如果func执行报错,将在控制台打出一条信息,并自动进行注销。 + + +core.unregisterAnimationFrame(name) +注销一个animationFrame,参数是你的上面的自定义名称。 + +// ------ 开始界面相关 ------ // + +core.showStartAnimate(noAnimate, callback) +重置所有内容并显示游戏标题界面。 +noAnimate如果为true则不会有淡入动画,callback为执行完毕的回调。 + + +core.hideStartAnimate(callback) +淡出隐藏游戏标题界面,callback为执行完毕的回调。 + + +core.isPlaying() +当前是否正在游戏中。 + + +core.restart() +重新开始游戏。本质上就是播放标题界面的BGM并调用showStartAnimate。 + + +core.confirmRestart() +确认用户是否需要重新开始。 + + +core.clearStatus() +清除所有的游戏状态和数据,包括状态栏的显示。 + +// ------ 自动寻路、人物行走 ------ // + +core.stopAutomaticRoute() +停止自动寻路的操作 + + +core.saveAndStopAutomaticRoute() +保存剩下的寻路路线并停止自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.continueAutomaticRoute() +继续剩下的自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.clearContinueAutomaticRoute() +清空剩下的自动寻路操作。 + + +core.setAutomaticRoute(destX, destY, stepPostfix) +尝试开始进行一个自动寻路。stepPostfix是鼠标拖动的路径。 +此函数将检测是否在寻路中(在则停止或双击瞬移),检测是否点击自己(转身或轻按), +检测是否能单击瞬移,最后找寻自动寻路路线并开始寻路。 + + +core.setAutoHeroMove(steps) +设置勇士的自动行走路线,并立刻开始行走。 + + +core.setHeroMoveInterval(callback) +设置勇士行走动画。callback是每一步行走完毕后的回调。 + + +core.moveOneStep(x, y) +每走完一步后执行的操作,被转发到了脚本编辑中。 + + +core.moveAction(callback) +尝试执行单步行走。callback是执行完毕的回调。 +如果勇士面对的方向是noPass的,将直接触发事件并执行回调。 + + +core.moveHero(direction, callback) +令勇士朝一个方向行走。如果设置了callback,则只会行走一步,并执行回调。 +否则,将一直朝该方向行走,直到core.status.heroStop为true为止。 + + +core.isMoving() +当前是否正在处于行走状态 + + +core.waitHeroToStop(callback) +停止勇士的行走,等待行动结束后,再异步执行回调。 + + +core.turnHero(direction) +转向。如果设置了direction则会转到该方向,否则会右转。该函数会自动计入录像。 + + +core.moveDirectly(destX, destY) +尝试瞬间移动到某点,被转发到了脚本编辑中。 +此函数返回非负值代表成功进行瞬移,返回值是省略的步数;如果返回-1则代表没有成功瞬移。 + + +core.tryMoveDirectly(destX, destY) +尝试单击瞬移到某点。 +如果该点可被直接瞬间移动到,则直接瞬移到该点;否则尝试瞬移到相邻的上下左右点并行走一步。 + + +core.drawHero(status, offset) +绘制勇士。 +status可选,为'stop','leftFoot'和'rightFoot'之一,不填或null默认是'stop'。 +offset可选,表示具体当前格子的偏移量。不填默认为0。 +此函数将重新计算地图的偏移量,调整窗口位置,绘制勇士和跟随者信息。 + +// ------ 画布、位置、阻激夹域、显伤 ------ // + +core.setGameCanvasTranslate(canvas, x, y) +设置某个画布的偏移量 + + +core.addGameCanvasTranslate(x, y) +加减所有系统画布(ui和data除外)的偏移量。主要是被“画面震动”所使用。 + + +core.updateViewport() +根据大地图的偏移量来更新窗口的视野范围。 + + +core.nextX(n) / core.nextY(m) +获得勇士面对的第n个位置的横纵坐标。n可不填,默认为1。 + + +core.nearHero(x, y) +判定某个点是否和勇士的距离不大于1。 + + +core.gatherFollowers() +聚集所有的跟随者到勇士的位置。 + + +core.updateFollowers() +更新跟随者们的坐标。 + + +core.updateCheckBlock(floorId) +更新阻激夹域的信息,被转发到了脚本编辑中。 + + +core.checkBlock() +检查勇士坐标点的阻激夹域信息。 + + +core.updateDamage(floorId, ctx) +更新全地图的显伤。floorId可选,默认为当前楼层。 +ctx可选,为画布;如果不为空,则将会绘制到该画布上而不是damage层上。 + +// ------ 录像相关 ------ // + +core.chooseReplayFile() +弹出选择文件窗口,让用户选择录像文件。 + + +core.startReplay(list) +开始播放一段录像。list为录像的操作数组。 + + +core.triggerReplay() +播放或暂停录像,实际上是pauseReplay或resumeReplay之一。 + + +core.pauseReplay() / core.resumeReplay() +暂停和继续录像播放。 + + +core.speedUpReplay() / core.speedDownReplay() +加速和减速录像播放。 + + +core.setReplaySpeed(speed) +直接设置录像回放速度。 + + +core.stopReplay(force) +停止录像回放。如果force为true则强制停止。 + + +core.rewindReplay() +回退一个录像节点。 + + +core.saveReplay() / core.bookReplay() / core.viewMapReplay() +回放录像时的存档、查看怪物手册、浏览地图操作。 + + +core.isReplaying() +当前是否正在录像播放中。 + + +core.registerReplayAction(name, func) +注册一个自定义的录像行为。 +name:自定义名称,可用户注销使用。 +func:具体执行录像的函数,是一个函数体或者插件中的函数名。 +func需要接受action参数,代表录像回放时的当前操作行为。 +如果func返回true,则代表成功处理了此次操作,返回false代表没有进行处理。 +请注意回放录像时的二次记录问题(即回放时录像会重新记录路线)。 + + +core.unregisterReplayAction(name) +注销一个录像行为。此函数一般不应当被使用。 + +// ------ 存读档相关 ------ // + +core.autosave(remoreLast) +进行一个自动存档,实际上是加入到缓存之中。 +removeLast如果为true则会从路线中删除最后一项再存(打怪开门前的状态)。 +在事件处理中不允许调用本函数,如有需要请呼出存档页面。 + + +core.checkAutosave() +将缓存的自动存档写入存储中。平均每五秒钟,或在窗口失去焦点时被执行。 + + +core.doSL(id, type) +实际执行一个存读档事件。id为存档编号,自动存档为'autoSave'。 +type只能为'save', 'load', 'replayLoad'之一,代表存档、读档和从存档回放录像。 + + +core.syncSave(type) / core.syncLoad() +向服务器同步存档,从服务器加载存档。type如果为'all'则会向服务器同步所有存档。 + + +core.saveData() +获得要存档的内容,实际转发到了脚本编辑中。 + + +core.loadData(data, callback) +实际执行一次读档行为,data为读取到的数据,callback为执行完毕的回调。 +实际转发到了脚本编辑中。 + + +core.getSave(index, callback) +获得某个存档位的存档。index为存档编号,0代表自动存档。 + + +core.getSaves(ids, callback) +获得若干个存档位的存档。ids为一个存档编号数组,0代表自动存档。 + + +core.getAllSaves(callback) +获得全部的存档内容。目前仅被同步全部存档和下载全部存档所调用。 + + +core.getSaveIndexes(callback) +刷新全部的存档信息,将哪些档位有存档的记录到core.saves.ids中。 + + +core.hasSave(index) +判定某个存档位是否存在存档。index为存档编号,0代表自动存档。 + + +core.removeSave(index) +删除某个存档。index为存档编号,0代表自动存档。 + + +// ------ 属性、状态、位置、变量等 ------ // + +core.setStatus(name, value) +设置勇士当前的某个属性。 + + +core.addStatus(name, value) +加减勇士当前的某个属性。等价于 core.setStatus(name, core.getStatus(name) + value) + + +core.getStatus(name) +获得勇士的某个原始属性值。 + + +core.getStatusOrDefault(status, name) +尝试从status中获得某个原始属性值;如果status为null或不存在对应属性值则从勇士属性中获取。 +此项在伤害计算函数中使用较多,例如传递新的攻击和防御来计算临界和1防减伤。 + + +core.getRealStatus(name) +获得勇士的某个计算属性值。该属性值是在加成buff之后得到的。 +该函数等价于 core.getStatus(name) * core.getBuff(name) + + +core.getRealStatusOrDefault(status, name) +尝试从status中获得某个原始属性值再进行增幅,如果不存在则获取勇士本身的计算属性值。 + + +core.setBuff(name, value) +设置勇士的某个属性的增幅值。value为1代表无增幅。 + + +core.addBuff(name, value) +增减勇士的某个属性的增幅值。等价于 core.setBuff(name, core.getBuff(name) + value) + + +core.getBuff(name) +获得勇士的某个属性的增幅值。默认值是1。 + + +core.setHeroLoc(name, value, noGather) +设置勇士位置属性。name只能为'x', 'y'和'direction'之一。 +如果noGather为true,则不会聚集所有的跟随者。 + + +core.getHeroLoc(name) +获得勇士的某个位置属性。如果name为null则直接返回core.status.hero.loc。 + + +core.getLvName(lv) +获得某个等级对应的名称,其在全塔属性的levelUp中定义。如果不存在则返回原始数值。 + + +core.setFlag(name, value) +设置某个自定义变量或flag。如果value为null则会调用core.removeFlag进行删除。 + + +core.addFlag(name, value) +加减某个自定义的变量或flag。等价于 core.setFlag(name, core.getFlag(name, 0) + value) + + +core.getFlag(name, defaultValue) +获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue值。 + + +core.hasFlag(name) +判定是否拥有某个自定义变量或flag。等价于 !!core.getFlag(name, 0) + + +core.removeFlag(name) +删除一个自定义变量或flag。 + + +core.lockControl() / core.unlockControl() +锁定和解锁控制。常常应用于事件处理。 + + +core.debug() +开启调试模式。此模式下可以按住Ctrl进行穿墙。 + +// ------ 天气,色调,音乐和音效 ------ // + +core.setWeather(type, level) +设置当前的天气。type只能为'rain', 'snow'或'fog',level为1-10之间代表强度信息。 + + +core.setCurtain(color, time, callback) +更改画面色调。color为更改到的色调,是个三元或四元组;time为渐变时间,0代表立刻切换。 + + +core.screenFlash(color, time, times, callback) +画面闪烁。color为色调,三元或四元组;time为单次闪烁时间,times为总闪烁次数。 + + +core.playBgm(bgm, startTime) +播放一个bgm。startTime可以控制开始时间,不填默认为0。 +如果bgm不存在、不被支持,或当前不允许播放背景音乐,则会跳过。 + + +core.pauseBgm() / core.resumeBgm() +暂停和恢复当前bgm的播放。 + + +core.triggerBgm() +更改当前bgm的播放状态。 + + +core.playSound(sound) / core.stopSound() +播放一个音效,停止全部音效。 +如果sound不存在、不被支持,或当前不允许播放音效,则会忽略。 + + +core.checkBgm() +检查bgm的状态。 +有的时候,刚打开页面时,浏览器是不允许自动播放标题界面bgm的,一定要经过一次用户操作行为。 +这时候我们可以给开始按钮增加core.checkBgm(),如果之前没有成功播放则重新播放。 + +// ------ 状态栏和工具栏相关 ------ // + +core.clearStatusBar() +清空状态栏的数据。 + + +core.updateStatusBar() +更新状态栏,被转发到了脚本编辑中。此函数还会根据是否在回放来设置工具栏的图标。 + + +core.showStatusBar() / core.hideStatusBar(showToolbox) +显示和隐藏状态栏。 +如果showToolbox为true,则在竖屏模式下不隐藏工具栏,方便手机存读档操作。 + + +core.updateHeroIcon() +更新状态栏上的勇士图标。 + + +core.updateGlobalAttribute() +更新全局属性,例如状态栏的背景图等。 + + +core.setToolbarButton(useButtom) +设置工具栏是否是拓展键盘。 + +// ------ resize 相关 ------ // + +core.registerResize(name, func) +注册一个resize函数。 +name为自定义名称,可供注销使用。 +func可以是一个函数,或插件中的函数名,可以接受一个obj作为参数。 +具体详见resize函数。 + + +core.unregisterResize(name) +注销一个resize函数。 + + +core.resize() +屏幕分辨率改变后的重新自适应。 +此函数将根据当前的屏幕分辨率信息,生成一个obj,并传入各个注册好的resize函数中执行。 +``` + ### enemys.js ### events.js diff --git a/_docs/event.md b/_docs/event.md index cac07dc8..fbd0b094 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -912,7 +912,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 **如果time指定为小于100,则视为没有楼层切换动画。** -### changePos:当前位置切换/勇士转向 +### changePos:当前位置切换/set勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 @@ -1126,15 +1126,15 @@ time为总移动的时间。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 -### setFg:更改画面色调 +### setCurtain:更改画面色调 -我们可以使用 `{"type": "setFg"}` 来更改画面色调。 +我们可以使用 `{"type": "setCurtain"}` 来更改画面色调。 ``` js [ - {"type": "setFg", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 - {"type": "setFg", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 - {"type": "setFg"} // 如果不指定color则恢复原样。 + {"type": "setCurtain", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 + {"type": "setCurtain", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 + {"type": "setCurtain"} // 如果不指定color则恢复原样。 ] ``` diff --git a/_docs/personalization.md b/_docs/personalization.md index 6edfb2a6..904b6e45 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -346,7 +346,7 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { "canUseItemEffect": "true" // 任何时候可用 ``` -除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyboard`)即可。 +除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyBoard`)即可。 ## 自定义怪物属性 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 2555b855..b7e93322 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -305,8 +305,8 @@ action | moveImage_s | showGif_0_s | showGif_1_s - | setFg_0_s - | setFg_1_s + | setCurtain_0_s + | setCurtain_1_s | screenFlash_s | setWeather_s | move_s @@ -1277,35 +1277,35 @@ var code = '{"type": "moveImage", "code": '+Int_0+toloc+EvalString_0+',"time": ' return code; */; -setFg_0_s +setCurtain_0_s : '更改画面色调' EvalString Colour '动画时间' Int? '不等待执行完毕' Bool Newline -/* setFg_0_s -tooltip : setFg: 更改画面色调,动画时间可不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 +/* setCurtain_0_s +tooltip : setCurtain: 更改画面色调,动画时间可不填 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 default : ["255,255,255,1",'rgba(255,255,255,1)',500,false] colour : this.soundColor var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); Int_0 = Int_0!=='' ?(', "time": '+Int_0):''; var async = Bool_0?', "async": true':''; -var code = '{"type": "setFg", "color": ['+EvalString_0+']'+Int_0 +async+'},\n'; +var code = '{"type": "setCurtain", "color": ['+EvalString_0+']'+Int_0 +async+'},\n'; return code; */; -setFg_1_s +setCurtain_1_s : '恢复画面色调' '动画时间' Int? '不等待执行完毕' Bool Newline -/* setFg_1_s -tooltip : setFg: 恢复画面色调,动画时间可不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 +/* setCurtain_1_s +tooltip : setCurtain: 恢复画面色调,动画时间可不填 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83 default : [500,false] colour : this.soundColor Int_0 = Int_0!=='' ?(', "time": '+Int_0):''; var async = Bool_0?', "async": true':''; -var code = '{"type": "setFg"'+Int_0 +async+'},\n'; +var code = '{"type": "setCurtain"'+Int_0 +async+'},\n'; return code; */; @@ -2433,11 +2433,12 @@ ActionParser.prototype.parseAction = function() { } break; case "setFg": // 颜色渐变 + case "setCurtain": if(this.isset(data.color)){ - this.next = MotaActionBlocks['setFg_0_s'].xmlText([ + this.next = MotaActionBlocks['setCurtain_0_s'].xmlText([ data.color,'rgba('+data.color+')',data.time||0,data.async||false,this.next]); } else { - this.next = MotaActionBlocks['setFg_1_s'].xmlText([ + this.next = MotaActionBlocks['setCurtain_1_s'].xmlText([ data.time||0,data.async||false,this.next]); } break; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 595a55ff..08fc8c74 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -141,8 +141,8 @@ editor_blockly = function () { MotaActionBlocks['animate_s'].xmlText(), MotaActionBlocks['showStatusBar_s'].xmlText(), MotaActionBlocks['hideStatusBar_s'].xmlText(), - MotaActionBlocks['setFg_0_s'].xmlText(), - MotaActionBlocks['setFg_1_s'].xmlText(), + MotaActionBlocks['setCurtain_0_s'].xmlText(), + MotaActionBlocks['setCurtain_1_s'].xmlText(), MotaActionBlocks['screenFlash_s'].xmlText(), MotaActionBlocks['setWeather_s'].xmlText(), MotaActionBlocks['playBgm_s'].xmlText(), diff --git a/libs/actions.js b/libs/actions.js index 917af40f..6b288e15 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -105,7 +105,7 @@ actions.prototype.doRegisteredAction = function (action) { return false; } -actions.prototype.checkReplaying = function () { +actions.prototype._checkReplaying = function () { if (core.isReplaying() && core.status.event.id != 'save' && (core.status.event.id || "").indexOf('book') != 0 && core.status.event.id != 'viewMaps') return true; @@ -114,7 +114,7 @@ actions.prototype.checkReplaying = function () { ////// 检查是否在录像播放中,如果是,则停止交互 actions.prototype._sys_checkReplay = function () { - if (this.checkReplaying()) return true; + if (this._checkReplaying()) return true; } ////// 按下某个键时 ////// @@ -145,7 +145,7 @@ actions.prototype.onkeyUp = function (e) { } actions.prototype._sys_onkeyUp_replay = function (e) { - if (this.checkReplaying()) { + if (this._checkReplaying()) { if (e.keyCode == 27) // ESCAPE core.stopReplay(); else if (e.keyCode == 90) // Z @@ -286,7 +286,7 @@ actions.prototype.keyUp = function (keyCode, altKey, fromReplay) { } actions.prototype._sys_keyUp_replay = function (keyCode, altKey, fromReplay) { - if (!fromReplay && this.checkReplaying()) return true; + if (!fromReplay && this._checkReplaying()) return true; } actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { @@ -389,7 +389,7 @@ actions.prototype._sys_keyUp = function (keyCode, altKey) { if (core.status.automaticRoute && core.status.automaticRoute.autoHeroMove) { core.stopAutomaticRoute(); } - core.stopHero(); + core.status.heroStop = true; return true; } @@ -539,8 +539,8 @@ actions.prototype._sys_onup = function () { return true; } -////// 获得点击事件相对左上角的坐标(0到12之间) ////// -actions.prototype.getClickLoc = function (x, y) { +////// 获得点击事件相对左上角的坐标 ////// +actions.prototype._getClickLoc = function (x, y) { var statusBar = {'x': 0, 'y': 0}; var size = 32; @@ -663,7 +663,7 @@ actions.prototype.onmousewheel = function (direct) { actions.prototype._sys_onmousewheel = function (direct) { // 向下滚动是 -1 ,向上是 1 - if (this.checkReplaying()) { + if (this._checkReplaying()) { // 滚轮控制速度 if (direct == 1) core.speedUpReplay(); if (direct == -1) core.speedDownReplay(); @@ -2065,7 +2065,7 @@ actions.prototype._clickLocalSaveSelect = function (x, y) { var selection = y - topIndex; core.status.event.selection = selection; if (selection < 2) { - core.getAllSaves(selection == 0 ? null : core.saves.saveIndex, function (saves) { + var callback = function (saves) { if (saves) { var content = { "name": core.firstData.name, @@ -2074,7 +2074,9 @@ actions.prototype._clickLocalSaveSelect = function (x, y) { } core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5save", JSON.stringify(content)); } - }) + }; + if (selection == 0) core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); } core.status.event.selection = 2; diff --git a/libs/control.js b/libs/control.js index c72fabff..df0d7796 100644 --- a/libs/control.js +++ b/libs/control.js @@ -371,7 +371,6 @@ control.prototype.confirmRestart = function (fromSettings) { }); } - ////// 清除游戏状态和数据 ////// control.prototype.clearStatus = function() { // 停止各个Timeout和Interval @@ -424,7 +423,7 @@ control.prototype.stopAutomaticRoute = function () { core.status.automaticRoute.destX=null; core.status.automaticRoute.destY=null; core.status.automaticRoute.lastDirection = null; - core.stopHero(); + core.status.heroStop = true; if (core.status.automaticRoute.moveStepBeforeStop.length==0) core.deleteCanvas('route'); } @@ -751,11 +750,6 @@ control.prototype.waitHeroToStop = function(callback) { } } -////// 停止勇士的移动状态 ////// -control.prototype.stopHero = function () { - core.status.heroStop = true; -} - ////// 转向 ////// control.prototype.turnHero = function(direction) { if (direction) { @@ -1274,7 +1268,12 @@ control.prototype._doReplayAction = function (action) { control.prototype._replay_finished = function () { core.status.replay.replaying = false; core.status.event.selection = 0; - core.ui.drawConfirmBox("录像播放完毕,你想退出播放吗?", function () { + var str = "录像播放完毕,你想退出播放吗?"; + if (core.status.route.length != core.status.replay.totalList.length + || core.subarray(core.status.route, core.status.replay.totalList) == null) { + str = "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗?"; + } + core.ui.drawConfirmBox(str, function () { core.ui.closePanel(); core.stopReplay(true); }, function () { @@ -1462,6 +1461,7 @@ control.prototype._replayAction_moveDirectly = function (action) { // 忽略连续的瞬移事件 while (core.status.replay.toReplay.length>0 && core.status.replay.toReplay[0].indexOf('move:')==0) { + core.status.route.push(action); action = core.status.replay.toReplay.shift(); } @@ -1617,13 +1617,15 @@ control.prototype._doSL_replayLoad_afterGet = function (id, data) { ////// 同步存档到服务器 ////// control.prototype.syncSave = function (type) { core.ui.drawWaiting("正在同步,请稍后..."); - core.getAllSaves(type=='all'?null:core.saves.saveIndex, function (saves) { - if (!saves) return core.drawText("没有要同步的存档"); + var callback = function (saves) { core.control._syncSave_http(type, saves); - }) + } + if (type == 'all') core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); } control.prototype._syncSave_http = function (type, saves) { + if (!saves) return core.drawText("没有要同步的存档"); var formData = new FormData(); formData.append('type', 'save'); formData.append('name', core.firstData.name); @@ -1748,8 +1750,7 @@ control.prototype.getSaves = function (ids, callback) { } } -control.prototype.getAllSaves = function (id, callback) { - if (id != null) return this.getSave(id, callback); +control.prototype.getAllSaves = function (callback) { var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;}) .sort(function(a,b) {return a-b;}), saves = []; this.getSaves(ids, function (data) { @@ -1792,7 +1793,7 @@ control.prototype.hasSave = function (index) { return core.saves.ids[index] || false; } -////// 删除一个或多个存档 +////// 删除某个存档 control.prototype.removeSave = function (index, callback) { if (index == 0 || index == "autoSave") { index = "autoSave"; @@ -1827,8 +1828,6 @@ control.prototype._updateFavoriteSaves = function () { core.setLocalStorage("favoriteName", core.saves.favoriteName); } -////// 加载某个存档 - // ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // ////// 设置勇士属性 ////// @@ -2019,7 +2018,7 @@ control.prototype._setWeather_createNodes = function (type, level) { } ////// 更改画面色调 ////// -control.prototype.setFg = function(color, time, callback) { +control.prototype.setCurtain = function(color, time, callback) { if (time == null) time=750; if (time<=0) time=0; if (!core.status.curtainColor) @@ -2037,10 +2036,10 @@ control.prototype.setFg = function(color, time, callback) { return; } - this._setFg_animate(core.status.curtainColor, color, time, callback); + this._setCurtain_animate(core.status.curtainColor, color, time, callback); } -control.prototype._setFg_animate = function (nowColor, color, time, callback) { +control.prototype._setCurtain_animate = function (nowColor, color, time, callback) { var per_time = 10, step = parseInt(time / per_time); var animate = setInterval(function() { nowColor = [ @@ -2068,8 +2067,8 @@ control.prototype.screenFlash = function (color, time, times, callback) { times = times || 1; time = time / 3; var nowColor = core.clone(core.status.curtainColor); - core.setFg(color, time, function() { - core.setFg(nowColor, time * 2, function() { + core.setCurtain(color, time, function() { + core.setCurtain(nowColor, time * 2, function() { if (times > 1) core.screenFlash(color, time * 3, times - 1, callback); else { @@ -2381,7 +2380,7 @@ control.prototype.updateGlobalAttribute = function (name) { } } -////// 改变工具栏为按钮1-7 ////// +////// 改变工具栏为按钮1-8 ////// control.prototype.setToolbarButton = function (useButton) { if (!core.domStyle.showStatusBar) { // 隐藏状态栏时检查竖屏 diff --git a/libs/core.js b/libs/core.js index 966aa4d0..c3040df4 100644 --- a/libs/core.js +++ b/libs/core.js @@ -16,8 +16,7 @@ function core() { 'ground': null, 'items': {}, 'enemys': {}, - 'icons': {}, - 'events': {} + 'icons': {} } this.timeout = { 'tipTimeout': null, @@ -84,8 +83,8 @@ function core() { this.domStyle = { scale: 1.0, isVertical: false, - toolbarBtn: false, showStatusBar: true, + toolbarBtn: false, } this.bigmap = { canvas: ["bg", "event", "event2", "fg", "damage"], diff --git a/libs/events.js b/libs/events.js index b8f10226..5b7ff3b9 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1068,13 +1068,17 @@ events.prototype._action_moveImage = function (data, x, y, prefix) { } events.prototype._action_setFg = function (data, x, y, prefix) { + return this._action_setCurtain(data, x, y, prefix); +} + +events.prototype._action_setCurtain = function (data, x, y, prefix) { if (data.async) { - core.setFg(data.color, data.time); + core.setCurtain(data.color, data.time); core.setFlag('__color__', data.color || null); core.doAction(); } else { - core.setFg(data.color, data.time, function () { + core.setCurtain(data.color, data.time, function () { core.setFlag('__color__', data.color || null); core.doAction(); }); diff --git a/main.js b/main.js index 9d26b266..6091c580 100644 --- a/main.js +++ b/main.js @@ -364,7 +364,7 @@ main.dom.body.onselectstart = function () { main.dom.data.onmousedown = function (e) { try { e.stopPropagation(); - var loc = main.core.getClickLoc(e.clientX, e.clientY); + var loc = main.core.actions._getClickLoc(e.clientX, e.clientY); if (loc == null) return; main.core.ondown(loc); } catch (ee) { main.log(ee); } @@ -374,7 +374,7 @@ main.dom.data.onmousedown = function (e) { main.dom.data.onmousemove = function (e) { try { e.stopPropagation(); - var loc = main.core.getClickLoc(e.clientX, e.clientY); + var loc = main.core.actions._getClickLoc(e.clientX, e.clientY); if (loc == null) return; main.core.onmove(loc); }catch (ee) { main.log(ee); } @@ -401,7 +401,7 @@ main.dom.data.onmousewheel = function(e) { main.dom.data.ontouchstart = function (e) { try { e.preventDefault(); - var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; main.core.ondown(loc); }catch (ee) { main.log(ee); } @@ -411,7 +411,7 @@ main.dom.data.ontouchstart = function (e) { main.dom.data.ontouchmove = function (e) { try { e.preventDefault(); - var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; main.core.onmove(loc); }catch (ee) { main.log(ee); } diff --git a/project/floors/sample1.js b/project/floors/sample1.js index a7d256df..92b3a0e8 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -151,7 +151,7 @@ main.floors.sample1= "type": "hide" }, { - "type": "setFg", + "type": "setCurtain", "color": [ 0, 0, @@ -184,7 +184,7 @@ main.floors.sample1= "2,11": [ "\t[杰克,thief]喂!醒醒!快醒醒!", { - "type": "setFg", + "type": "setCurtain", "time": 1500 }, "\t[hero]额,我这是在什么地方?",