From 9e9025e08980e727214fd40161547ffe643daa55 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 28 Apr 2019 20:29:51 +0800 Subject: [PATCH] openDoor async & battle loc & trigger keep --- _docs/event.md | 51 +++++++++++++++++------------------ _server/MotaAction.g4 | 57 +++++++++++++++++++++++++++++---------- _server/editor_blockly.js | 1 + libs/events.js | 21 ++++++++++----- 4 files changed, 83 insertions(+), 47 deletions(-) diff --git a/_docs/event.md b/_docs/event.md index 309e6a7e..dd104f48 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -180,7 +180,7 @@ 我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以。 -其一般写法是`\t[名字,ID]`,其中名字为你要显示的标题,ID为图块ID,只能为`hero`,或者NPC/怪物的图块ID。 +其一般写法是`\t[名字,ID]`,其中名字为你要显示的标题,ID为图块ID,只能为open`hero`,或者NPC/怪物的图块ID。 如果不需要可以不写ID,则只会显示标题。 @@ -574,24 +574,22 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` `{"type":"trigger"}` 会立刻触发当层另一个地点的自定义事件。 -上面我们说到,show事件会让一个禁用事件启用且可被交互;但是如果我想立刻让它执行应该怎么办呢?使用trigger就行。 - 其基本写法如下: ``` js [ {"type": "trigger", "loc": [3,6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 - "执行trigger后,这段文字将不会再被显示" + {"type": "trigger", "loc": [3,6], "keep": true}, // 触发loc位置的事件,不结束当前事件 ] ``` 其后面带有loc选项,代表另一个地点的坐标。 -执行trigger事件后,当前事件将立刻被结束,剩下所有内容被忽略;然后重新启动另一个地点的action事件。 +keep可选,如果此项为true则不会结束当前的事件列表,否则会中断当前的事件流。 -例如上面这个例子,下面的文字将不会再被显示,而是直接跳转到`"3,6"`对应的事件列表从头执行。 +从V2.5.4开始允许执行另一个点的系统事件,如打怪开门拾取道具等等,对应点的战后等事件也会被插入执行。 -!> 从V2.5.4开始,允许执行另一个点的“系统事件”,如打怪、开门、拾取道具等等。对应点的战后事件等也会被执行。 +值得注意的是,此事件会**改变当前点坐标**为目标点。 ### insert:插入公共事件或另一个地点的事件并执行 @@ -619,11 +617,12 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` - floorId可选,代表另一个地点所在的楼层;如果不写则默认为当前层。 - 从V2.6开始,还可以传可选的which,可以为`afterBattle`/`afterGetItem`/`afterOpenDoor`,代表插入该点的战后/获得道具后/开门后事件。 -和`type:trigger`不同的是,**`type:trigger`是立刻将当前事件结束(剩下所有内容都忽略),然后重新启动另一个地点的action事件。** +和`type:trigger`不同点如下: +- `type:trigger`只能指定当前楼层且会改变当前点坐标,`type:insert`可以跨楼层且不会改变当前点坐标。 +- `type:trigger`如果不设置`keep:true`则还会结束当前事件,`type:insert`而是将另一个点的事件插入到当前事件列表中执行。 +- `type:trigger`可以触发系统事件,`type:insert`只能触发该点的自定义事件或战后事件等。 -但是`type:insert`不会结束当前事件,而是直接将另一个地点的事件列表“插入”到当前事件列表中执行。 - -**这个过程中,当前事件不会被结束,当前的楼层和事件坐标不会发生改变。** 插入的事件执行完毕后,会继续执行接下来的内容。 +插入的事件执行完毕后,会继续执行接下来的内容。 从V2.6开始,插入事件允许传参。如果需要传参,则需要增加一个`args`数组。 @@ -840,28 +839,20 @@ name是可选的,代表目标行走图的文件名。 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 -其基本写法是: `{"type": "battle", "id": xxx}`,其中xxx为怪物ID。 - ``` js [ - "\t[blackKing]你终于还是来了。", - "\t[hero]放开我们的公主!", - "\t[blackKing]如果我不愿意呢?", - "\t[hero]无需多说,拔剑吧!", - {"type": "battle", "id": "blackKing"}, // 强制战斗 - // 如果战斗失败直接死亡,不会继续触发接下来的剧情。 - {"type": "hide", "loc": [10,2]}, // 战斗后需要手动使怪物消失;战斗后不会引发afterBattle事件。 - {"type": "openDoor", "loc": [8,7]}, // 开门口的机关门 - "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", - {"type": "hide"} // 隐藏本事件 + {"type": "battle", "id": "blackKing"}, // 强制战斗黑衣魔王 + {"type": "battle", "loc": [2,3]}, // 强制战斗(2,3)点的怪物 ], ``` -上面就是样板层中右上角的强制战斗例子。 +从V2.6开始,有两种写法: +- 写id,则视为和空降怪物进行强制战斗,不会执行一些战后事件,也不会隐藏任何点。 +- 写loc(可选,不填默认当前点),则视为和某点怪物进行强制战斗,会隐藏该点并且插入该点战后事件执行。 -如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。 +强制战斗不会自动存档,如果战斗失败则立刻死亡。 -强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。 +值得注意的是,如果是和某个点的怪物强制战斗,并且该点存在战后事件,执行时会**修改当前点坐标**为目标点。 ### openDoor:开门 @@ -871,7 +862,7 @@ name是可选的,代表目标行走图的文件名。 [ {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId - {"type": "openDoor", "loc": [3,6], "needKey": true} // 打开此门需要钥匙 + {"type": "openDoor", "loc": [3,6], "needKey": true, "async": true} // 打开此门需要钥匙,异步执行 ] ``` @@ -883,8 +874,12 @@ loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层 needKey是可选的,如果设置为true则需要钥匙才能打开此门。如果没有钥匙则跳过此事件。 +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + !> needKey仅对当前楼层开门有效!跨楼层的门仍然不需要钥匙即可打开,如有需求请自行判定。 +值得注意的是,如果该点存在开门事件,执行时会**修改当前点坐标**为目标点。 + ### closeDoor:关门 从V2.6开始提供了关门事件`{"type": "closeDoor"}`,拥有关门动画和对应的音效。 @@ -907,6 +902,8 @@ yellowWall, blueWall, whiteWall loc可选,为要关的位置,不填默认为当前点。 +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + 关门事件需要保证该点是空地,否则将无视此事件。 ### changeFloor:楼层切换 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 2d8a580e..5165f962 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -287,6 +287,7 @@ action | wait_s | waitAsync_s | battle_s + | battle_1_s | openDoor_s | closeDoor_s | changeFloor_s @@ -643,15 +644,16 @@ return code; */; trigger_s - : '触发事件' 'x' PosString ',' 'y' PosString Newline + : '触发事件' 'x' PosString ',' 'y' PosString '不结束当前事件' Bool Newline /* trigger_s tooltip : trigger: 立即触发另一个地点的事件 helpUrl : https://h5mota.com/games/template/docs/#/event?id=trigger%EF%BC%9A%E7%AB%8B%E5%8D%B3%E8%A7%A6%E5%8F%91%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%9C%B0%E7%82%B9%E7%9A%84%E4%BA%8B%E4%BB%B6 -default : ["0","0"] +default : ["0","0",false] colour : this.eventColor -var code = '{"type": "trigger", "loc": ['+PosString_0+','+PosString_1+']},\n'; +Bool_0 = Bool_0 ?', "keep": true':''; +var code = '{"type": "trigger", "loc": ['+PosString_0+','+PosString_1+']'+Bool_0+'},\n'; return code; */; @@ -984,14 +986,32 @@ var code = '{"type": "battle", "id": "'+IdString_0+'"},\n'; return code; */; + +battle_1_s + : '强制战斗' 'x' PosString? ',' 'y' PosString? Newline + + +/* battle_1_s +tooltip : battle: 强制战斗 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=battle%EF%BC%9A%E5%BC%BA%E5%88%B6%E6%88%98%E6%96%97 +default : ["","",""] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "battle"'+floorstr+'},\n'; +return code; +*/; + openDoor_s - : '开门' 'x' PosString? ',' 'y' PosString? '楼层' IdString? '需要钥匙' Bool? Newline + : '开门' 'x' PosString? ',' 'y' PosString? '楼层' IdString? '需要钥匙' Bool? '不等待执行完毕' Bool Newline /* openDoor_s tooltip : openDoor: 开门,楼层可不填表示当前层 helpUrl : https://h5mota.com/games/template/docs/#/event?id=opendoor%EF%BC%9A%E5%BC%80%E9%97%A8 -default : ["","","",false] +default : ["","","",false,false] colour : this.mapColor IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); var floorstr = ''; @@ -999,24 +1019,26 @@ if (PosString_0 && PosString_1) { floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; } Bool_0 = Bool_0 ? ', "needKey": true' : ''; -var code = '{"type": "openDoor"'+floorstr+IdString_0+Bool_0+'},\n'; +Bool_1 = Bool_1 ? ', "async": true' : ''; +var code = '{"type": "openDoor"'+floorstr+IdString_0+Bool_0+Bool_1+'},\n'; return code; */; closeDoor_s - : '关门' 'x' PosString? ',' 'y' PosString? 'ID' IdString Newline + : '关门' 'x' PosString? ',' 'y' PosString? 'ID' IdString '不等待执行完毕' Bool Newline /* closeDoor_s tooltip : closeDoor: 关门事件,需要该点本身无事件 helpUrl : https://h5mota.com/games/template/docs/#/event?id=opendoor%EF%BC%9A%E5%BC%80%E9%97%A8 -default : ["","","yellowDoor"] +default : ["","","yellowDoor",false] colour : this.mapColor var floorstr = ''; if (PosString_0 && PosString_1) { floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; } -var code = '{"type": "closeDoor", "id": "'+IdString_0+'"'+floorstr+'},\n'; +Bool_0 = Bool_0 ? ', "async": true' : ''; +var code = '{"type": "closeDoor", "id": "'+IdString_0+'"'+floorstr+Bool_0+'},\n'; return code; */; @@ -2532,12 +2554,12 @@ ActionParser.prototype.parseAction = function() { case "openDoor": // 开一个门, 包括暗墙 data.loc=data.loc||['',''] this.next = MotaActionBlocks['openDoor_s'].xmlText([ - data.loc[0],data.loc[1],data.floorId||'',data.needKey||false,this.next]); + data.loc[0],data.loc[1],data.floorId||'',data.needKey||false,data.async||false,this.next]); break; case "closeDoor": // 关一个门,需要该点无事件 data.loc=data.loc||['',''] this.next = MotaActionBlocks['closeDoor_s'].xmlText([ - data.loc[0],data.loc[1],data.id,this.next]); + data.loc[0],data.loc[1],data.id,data.async||false,this.next]); break; case "useItem": // 使用道具 this.next = MotaActionBlocks['useItem_s'].xmlText([ @@ -2552,12 +2574,19 @@ ActionParser.prototype.parseAction = function() { data.id,this.next]); break; case "battle": // 强制战斗 - this.next = MotaActionBlocks['battle_s'].xmlText([ - data.id,this.next]); + if (data.id) { + this.next = MotaActionBlocks['battle_s'].xmlText([ + data.id,this.next]); + } + else { + data.loc = data.loc || []; + this.next = MotaActionBlocks['battle_1_s'].xmlText([ + data.loc[0],data.loc[1],this.next]); + } break; case "trigger": // 触发另一个事件;当前事件会被立刻结束。需要另一个地点的事件是有效的 this.next = MotaActionBlocks['trigger_s'].xmlText([ - data.loc[0],data.loc[1],this.next]); + data.loc[0],data.loc[1],data.keep,this.next]); break; case "insert": // 强制插入另一个点的事件在当前事件列表执行,当前坐标和楼层不会改变 if (data.args instanceof Array) { diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 44b03be5..b81d675a 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -112,6 +112,7 @@ editor_blockly = function () { MotaActionBlocks['unfollow_s'].xmlText(), ], '地图处理':[ + MotaActionBlocks['battle_1_s'].xmlText(), MotaActionBlocks['openDoor_s'].xmlText(), MotaActionBlocks['closeDoor_s'].xmlText(), MotaActionBlocks['show_s'].xmlText(), diff --git a/libs/events.js b/libs/events.js index 1eb1ad15..cfa68e7d 100644 --- a/libs/events.js +++ b/libs/events.js @@ -419,20 +419,23 @@ events.prototype._openDoor_animate = function (id, x, y, callback) { var locked = core.status.lockControl; core.lockControl(); core.status.replay.animate = true; + core.removeBlock(x, y); + core.drawImage('event', core.material.images.animates, 0, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); var state = 0; var animate = window.setInterval(function () { + core.clearMap('event', 32 * x, 32 * y, 32, 32); state++; if (state == 4) { clearInterval(animate); - core.removeBlock(x, y); + delete core.animateFrame.asyncId[animate]; if (!locked) core.unLockControl(); core.status.replay.animate = false; core.events.afterOpenDoor(id, x, y, callback); return; } - core.clearMap('event', 32 * x, 32 * y, 32, 32); core.drawImage('event', core.material.images.animates, 32 * state, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); }, speed / Math.max(core.status.replay.speed, 1)); + core.animateFrame.asyncId[animate] = true; } ////// 开一个门后触发的事件 ////// @@ -1133,7 +1136,7 @@ events.prototype._action_openDoor = function (data, x, y, prefix) { var loc = this.__action_getLoc(data.loc, x, y, prefix); var floorId = data.floorId || core.status.floorId; if (floorId == core.status.floorId) { - core.openDoor(loc[0], loc[1], data.needKey, core.doAction); + this.__action_doAsyncFunc(data.async, core.openDoor, loc[0], loc[1], data.needKey); } else { core.removeBlock(loc[0], loc[1], floorId); @@ -1172,7 +1175,13 @@ events.prototype._action_disableShop = function (data, x, y, prefix) { } events.prototype._action_battle = function (data, x, y, prefix) { - this.battle(data.id, null, null, true, core.doAction); + if (data.id) { + this.battle(data.id, null, null, true, core.doAction); + } + else { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.battle(null, loc[0], loc[1], true, core.doAction); + } } events.prototype._action_trigger = function (data, x, y, prefix) { @@ -1180,9 +1189,9 @@ events.prototype._action_trigger = function (data, x, y, prefix) { var block = core.getBlock(loc[0], loc[1]); if (block != null && block.block.event.trigger) { block = block.block; - this.setEvents([], block.x, block.y); + this.setEvents(data.keep ? null : [], block.x, block.y); if (block.event.trigger == 'action') - this.setEvents(block.event.data); + this.insertAction(block.event.data); else { core.doSystemEvent(block.event.trigger, block, core.doAction); return;