diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index e6f97b4f..4fc0c32f 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -1000,7 +1000,7 @@ function_s /* function_s -tooltip : function: 自定义JS脚本 +tooltip : function: 自定义JS脚本\n可以双击进行多行编辑,常见可能会被用到的系统API请参见文档的附录 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=function%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89js%E8%84%9A%E6%9C%AC default : ["alert(core.getStatus(\"atk\"));"] colour : this.dataColor diff --git a/docs/api.md b/docs/api.md index 23fdd3a7..fbd09055 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,4 +1,4 @@ -# 附录:API列表 +# 附录: API列表 ?> 目前版本**v2.2.1**,上次更新时间:* {docsify-updated} * @@ -8,6 +8,8 @@ 可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 +**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。** + !> 最常用的新手向命令,强烈建议每个人了解 ``` text @@ -94,13 +96,14 @@ core.hasFlag('xyz') 返回是否存在某个变量且不为0。等价于 core.getFlag('xyz', 0)!=0 。 -core.insertAction(list) +core.insertAction(list, x, y, callback) 插入并执行一段自定义事件。在这里你可以写任意的自定义事件列表,有关详细写法请参见文档-事件。 +x和y如果设置则覆盖"当前事件点"的坐标,callback如果设置则覆盖事件执行完毕后的回调函数。 例如: core.insertAction(["楼层切换", {"type":"changeFloor", "floorId": "MT3"}]) 将依次显示剧情文本,并执行一个楼层切换的自定义事件。 -core.changeFloor(floorId, stair, heroLoc, time, callback) +core.changeFloor(floorId, stair, heroLoc, time, callback) [异步] 立刻切换到指定楼层。 floorId为目标楼层ID,stair为到达的目标楼梯,heroLoc为到达的指定点,time为动画时间,callback为切换完毕后的回调。 例如: @@ -140,12 +143,12 @@ core.nextX() core.nextY() 获得勇士面向的下一个位置的y坐标 -core.openDoor(id, x, y, needKey, callback) +core.openDoor(id, x, y, needKey, callback) [异步] 尝试开门操作。id为目标点的ID,x和y为坐标,needKey表示是否需要使用钥匙,callback为开门完毕后的回调函数。 例如:core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")}) -core.battle(id, x, y, force, callback) +core.battle(id, x, y, force, callback) [异步] 执行战斗事件。id为怪物的id,x和y为坐标,force为bool值表示是否是强制战斗,callback为战斗完毕后的回调函数。 例如:core.battle('greenSlime', null, null, true) @@ -220,8 +223,9 @@ core.drawTip(text, itemIcon) 在左上角绘制一段提示信息,2秒后消失。itemIcon为道具图标的索引。 -core.drawText(contents, callback) +core.drawText(contents, callback) [异步] 绘制一段文字。 +不推荐使用此函数,尽量使用core.insertAction(contents)来显示剧情文本。 core.closePanel() @@ -261,7 +265,7 @@ num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一 但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 -core.restart() +core.restart() [异步] 返回标题界面。 @@ -346,15 +350,16 @@ core.events.gameOver(ending, fromReplay) 该函数将提问是否上传和是否下载录像,并返回标题界面。 -core.events.doEvents(list, x, y, callback) +core.events.doEvents(list, x, y, callback) [异步] 开始执行某个事件。 +请不要执行此函数,尽量使用 core.insertAction(list, x, y, callback) 来开始执行一段事件。 core.events.doAction() 执行下一个事件。此函数中将对所有自定义事件类型分别处理。 -core.events.openShop(shopId, needVisited) +core.events.openShop(shopId, needVisited) [异步] 打开一个全局商店。needVisited表示是否需要该商店已被打开过。 @@ -438,12 +443,12 @@ core.utils.decodeRoute(route) 解压缩(解密)路线。 -core.utils.readFile(success, error, readType) +core.utils.readFile(success, error, readType) [异步] 尝试请求读取一个本地文件内容。 success和error为成功/失败后的回调,readType不设置则以文本读取,否则以DataUrl形式读取。 -core.utils.readFileContent(content) +core.utils.readFileContent(content) [异步] 文件读取完毕后的内容处理。 @@ -455,9 +460,9 @@ core.utils.copy(data) 尝试复制一段文本到剪切板。 -core.utils.http(type, url, formData, success, error, mimeType, responseType) +core.utils.http(type, url, formData, success, error) [异步] 发送一个异步HTTP请求。 type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 -success为成功后的回调,error为失败后的回调,最后两个参数如果设置则覆盖。 +success为成功后的回调,error为失败后的回调。 ``` diff --git a/docs/event.md b/docs/event.md index 2e3eda7b..7fe90b60 100644 --- a/docs/event.md +++ b/docs/event.md @@ -1169,7 +1169,24 @@ choices为一个数组,其中每一项都是一个选项列表。 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统所有支持的API都在[附录](api)中给出,请进行参照。 +系统常见可能会被造塔所用到的的API都在[附录:API列表](api)中给出,请进行参照。 + +**警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** + +[附录:API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 + +如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 + +举个例子,如果我们想随机切换到某个楼层的某个点,我们可以这么写自定义脚本: + +``` js +var toFloor = core.floorIds[core.rand(core.floorIds.length)]; // 随机一个楼层ID +var toX = core.rand(13), toY = core.rand(13); // 随机一个点 +core.insertAction([ + {"type": "changeFloor", "floorId": toFloor, "loc": [toX, toY]} // 插入一个changeFloor事件,并在该脚本结束后执行。 +]) +// 请勿直接调用 core.changeFloor(toFloor, ...),这个代码是异步的,会导致事件处理和录像出问题! +``` ## 同一个点的多事件处理 diff --git a/docs/personalization.md b/docs/personalization.md index d72a4e0b..f7a59f18 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -322,6 +322,39 @@ control.prototype.checkBlock = function () { !> terrains和animates的门ID必须完全一致,且以`Door`结尾;所对应的钥匙ID应当是把`Door`换成`Key`,这样才能对应的上! +## 覆盖楼传事件 + +对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。 + +要修改楼传事件,需要进行如下几步: + +1. 截获楼传的点击事件。在control.js中找到useFly函数,并将其替换成如下内容: +``` js +////// 点击楼层传送器时的打开操作 ////// +control.prototype.useFly = function (need) { + if (!core.status.heroStop) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` +2. 让录像记下楼传的使用。在items.js的useItem函数中找到记录路线的那几行,修改为: +``` js + // 记录路线 + if (itemId!='book') { // 把 `&& itemId!='fly'` 给删除 + core.status.route.push("item:"+itemId); + } +``` +3. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如: +``` js +"useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本 +"canUseItemEffect": "true" // 任何时候可用 +``` +修改时,请先把`null`改成空字符串`""`,然后再双击进行编辑。 + + ## 自定义装备 由于HTML5魔塔并不像RM那样存在一个装备界面可供我们对装备进行调整,但是我们也可以使用一个替代的方式实现这个目标。 @@ -409,6 +442,37 @@ this.useEquipment = function (itemId) { // 使用装备 `getCritical`, `getCriticalDamage`和`getDefDamage`三个函数依次计算的是该怪物的临界值、临界减伤和1防减伤。也可以适当进行修改。 +## 自定义快捷键 + +如果需要绑定某个快捷键为处理一段事件,也是可行的。 + +要修改按键,我们可以在`actions.js`的`keyUp`进行处理: + +比如,我们设置一个快捷键进行绑定,比如`W`,其keycode是87。(有关每个键的keycode搜一下就能得到) + +然后在`actions.js`的`keyUp`函数的`switch`中进行处理。 + +``` js +case 87: // W + if (core.status.heroStop) { + // ... 在这里写你要执行脚本 + // 请使用同步脚本,请勿执行任何异步代码,否则可能导致游戏过程或录像出现问题。 + core.insertAction([...]) // 例如,插入一段自定义事件并执行。 + + core.status.route.push("key:"+keyCode); // 录像的支持!这句话必须要加,不然录像回放会出错! + } + break; +``` + + +在勇士处于停止的条件下,按下W键时,将执行你写的脚本代码。请只使用同步脚本而不要使用异步代码,不然可能导致游戏出现问题。 + +`core.status.route.push("key:"+keyCode);` 这句话是对录像的支持,一定要加(这样录像播放时也会模拟该按键)。 + +!> H5不支持组合快捷键,所以不存在`W+1`这种组合快捷键的说法! + +!> 手机端可以通过长按任何位置调出虚拟键盘,再进行按键,和键盘按键是等价的效果! + ## 公共事件 在RM中,存在公共事件的说法;也就是通过某个指令来调用一系列事件的触发。 @@ -533,26 +597,20 @@ core.statusBar.skill.innerHTML = core.getFlag('skillName', '无'); // 使用flag ### 技能的触发 -我们可以按键触发技能。 +我们可以按键触发技能。有关绑定按键请参见[自定义快捷键](#自定义快捷键)。 -要修改按键,我们可以在`actions.js`的`keyUp`进行处理: - -我们设置一个快捷键进行绑定,比如`W`,其keycode是87。(有关每个键的keycode搜一下就能得到) - -!> H5不支持组合快捷键,所以不存在`W+1`这种组合快捷键的说法! - -然后在`actions.js`的`keyUp`函数的`switch`中进行处理。 +下面是一个很简单的例子,当勇士按下W后,如果魔力不小于5点则允许开启技能"二倍斩",再次按W则关闭技能。 ``` js case 87: // W if (core.status.heroStop) { // 当前停止状态;这个if需要加,不能在行走过程中触发不然容易出错。 if (core.getFlag('skill', 0)==0) { // 判断当前是否已经开了技能 - if (能开技能) { // 这里要写当前能否开技能的条件判断 + if (core.getStatus('mana')>=5) { // 这里要写当前能否开技能的条件判断,比如魔力值至少要多少 core.setFlag('skill', 1); // 开技能1 core.setFlag('skillName', '二倍斩'); // 设置技能名 } else { - core.drawTip("当前不能开技能!"); + core.drawTip("魔力不足,无法开技能"); } } else { // 关闭技能 @@ -612,6 +670,69 @@ if (core.getFlag('skill', 0)==1) { // 开启了技能1 通过上述这几种方式,我们就能成功的让H5支持技能啦! +## 多角色的支持 + +其实,我们的样板还能支持多角色的制作。比如《黑·白·间》之类的塔也是完全可以刻的。 + +你只需要如下几步来达到多角色的效果。 + +1. 每个勇士弄一张行走图。相关信息参见[自定义事件:setHeroIcon](event#setHeroIcon:更改角色行走图) +2. [覆盖楼传事件](#覆盖楼传事件),这样可以通过点工具栏的楼层传送按钮来切换角色。 +当然你也完全可以自己写一个道具来实现,或[自定义快捷键](#自定义快捷键)来进行绑定。 +3. 在脚本编辑的setInitData中初始化新角色的属性值。 +``` js +// 所有需要保存的内容;这些保存的内容不会多角色共用,在切换时会进行恢复。 +// 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化, +// 多勇士共用hp的话则删除hp,等等。 +var initData = { + "floorId": "MT0", // 该角色楼层ID + "icon": "hero2.png", // 角色的行走图名称 + "name": "2号角色", + "lv": 1, + "hp": 1000, + "atk": 10, + "def": 10, + "mdef": 0, + "loc": {"x": 0, "y": 0, "direction": "up"}, + // 不共用的数据都可以在这里加上定义 +} +core.setFlag("hero1", initData); // 将属性值存到变量中 +``` + +3. 道具(或快捷键)的脚本如下: +``` js +// 这个saveList和上面的初始化定义中的的key,除了不要icon(行走图名称)其他应完全相同。 +var saveList = ["floorId", "name", "lv", "hp", "atk", "def", "mdef", "loc"]; + +// 保存当前内容 +var toSave = {}; +saveList.forEach(function(name) { + if (name=='floorId') toSave[name] = core.status.floorId; // 楼层单独设置 + else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象 +}) + +var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID +var toHeroId = (currHeroId+1)%2; // 获得要切换到的角色ID,比如 0->1,1->0 + +core.setFlag("hero"+currHeroId, toSave); // 将当前勇士信息进行保存 + +var data = core.getFlag("hero"+toHeroId); // 获得要切换的勇士保存内容 + +// 将勇士属性值设置回来 +saveList.forEach(function(name) { + if (core.isset(core.status.hero[name]) && core.isset(loadData[name])) + core.status.hero[name] = core.clone(loadData[name]); +}) + +// 插入事件:改变勇士行走图并进行楼层切换 +core.insertAction([ + {"type": "setHeroIcon", "name": loadData.icon||"hero.png"}, // 改变行走图 + {"type": "changeFloor", "floorId": loadData.floorId, "loc": [loadData.loc.x, loadData.loc.y], + "direction": loadData.loc.direction, "time": 0} +]) +core.setFlag("heroId", toHeroId); // 保存切换到的勇士ID +``` + ## 根据难度分歧来自定义地图 遗憾的是,所有地图数据必须在剧本的map中指定,换句话说,我们无法在游戏进行中动态修改地图,比如为简单难度增加一个血瓶。