diff --git a/README.md b/README.md index 4ea48018..34eaf69e 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,19 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 -### 2018.7.9 V2.3.3 +### 2018.7.21 V2.3.3 + +HTML5魔塔样板V2.3.3 + +* [x] 将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 +* [x] 事件:画面震动 +* [x] 事件:更新怪物数据 +* [x] 移动事件和跳跃事件增加“不消失”选项 +* [x] 修改默认bgm +* [x] 修复读档开启战斗动画等Bug +* [x] 大量细节优化 + +### 2018.7.9 V2.3.2 * [x] 适配手机端的造塔页面 * [x] 启动服务的多开版本 diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 16938dcb..c35e3b0d 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -206,6 +206,7 @@ action | follow_s | unfollow_s | animate_s + | viberate_s | showImage_0_s | showImage_1_s | animateImage_0_s @@ -652,6 +653,20 @@ var code = '{"type": "unfollow"' + EvalString_0 + '},\n'; return code; */; +viberate_s + : '画面震动' '时间' Int Newline + + +/* viberate_s +tooltip : viberate: 画面震动 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=viberate%ef%bc%9a%e7%94%bb%e9%9d%a2%e9%9c%87%e5%8a%a8 +default : [2000] +colour : this.soundColor +Int_0 = Int_0 ?(', "time": '+Int_0):''; +var code = '{"type": "viberate"' + Int_0 + '},\n'; +return code; +*/; + animate_s : '显示动画' IdString '位置' EvalString? Newline @@ -819,20 +834,20 @@ return code; */; move_s - : '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' Int? '消失时无动画时间' Bool BGNL? StepString Newline + : '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool BGNL? StepString Newline /* move_s tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=move%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AAnpc%E6%80%AA%E7%89%A9%E7%A7%BB%E5%8A%A8 -default : ["","",500,null,"上右3下2左上左2"] +default : ["","",500,false,"上右3下2左上左2"] colour : this.eventColor var floorstr = ''; if (PosString_0 && PosString_1) { floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; } Int_0 = Int_0 ?(', "time": '+Int_0):''; -var code = '{"type": "move"'+floorstr+''+Int_0+', "steps": '+JSON.stringify(StepString_0)+', "immediateHide": '+Bool_0+'},\n'; +var code = '{"type": "move"'+floorstr+''+Int_0+', "steps": '+JSON.stringify(StepString_0)+', "keep": '+Bool_0+'},\n'; return code; */; @@ -851,13 +866,13 @@ return code; */; jump_s - : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? BGNL? '动画时间' Int? '消失时无动画时间' Bool Newline + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? '动画时间' Int? '不消失' Bool Newline /* jump_s tooltip : jump: 让某个NPC/怪物跳跃 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=jump%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AANPC%2F%E6%80%AA%E7%89%A9%E8%B7%B3%E8%B7%83 -default : ["","","","",500,null] +default : ["","","","",500,true] colour : this.eventColor var floorstr = ''; if (PosString_0 && PosString_1) { @@ -867,7 +882,7 @@ if (PosString_2 && PosString_3) { floorstr += ', "to": ['+PosString_2+','+PosString_3+']'; } Int_0 = Int_0 ?(', "time": '+Int_0):''; -var code = '{"type": "jump"'+floorstr+''+Int_0+', "immediateHide": '+Bool_0+'},\n'; +var code = '{"type": "jump"'+floorstr+''+Int_0+', "keep": '+Bool_0+'},\n'; return code; */; @@ -1235,7 +1250,7 @@ Floor_List /*Floor_List ['floorId',':before',':next']*/; Stair_List - : '坐标'|'上楼'|'下楼' + : '坐标'|'上楼梯'|'下楼梯' /*Stair_List ['loc','upFloor','downFloor']*/; SetTextPosition_List @@ -1526,7 +1541,7 @@ ActionParser.prototype.parseAction = function() { case "move": // 移动事件 data.loc=data.loc||['','']; this.next = MotaActionBlocks['move_s'].xmlText([ - data.loc[0],data.loc[1],data.time||0,data.immediateHide,this.StepString(data.steps),this.next]); + data.loc[0],data.loc[1],data.time||0,data.keep,this.StepString(data.steps),this.next]); break; case "moveHero": this.next = MotaActionBlocks['moveHero_s'].xmlText([ @@ -1536,7 +1551,7 @@ ActionParser.prototype.parseAction = function() { data.from=data.from||['','']; data.to=data.to||['','']; this.next = MotaActionBlocks['jump_s'].xmlText([ - data.from[0],data.from[1],data.to[0],data.to[1],data.time||0,data.immediateHide,this.next]); + data.from[0],data.from[1],data.to[0],data.to[1],data.time||0,data.keep,this.next]); break; case "jumpHero": // 跳跃勇士 data.loc=data.loc||['',''] @@ -1569,6 +1584,9 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['animate_s'].xmlText([ data.name,animate_loc,this.next]); break; + case "viberate": // 画面震动 + this.next = MotaActionBlocks['viberate_s'].xmlText([data.time||0, this.next]); + break; case "showImage": // 显示图片 if(this.isset(data.name)){ this.next = MotaActionBlocks['showImage_0_s'].xmlText([ diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index d5898b3e..b35decf8 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -116,6 +116,7 @@ editor_blockly = function () { '', MotaActionBlocks['sleep_s'].xmlText(), MotaActionBlocks['wait_s'].xmlText(), + MotaActionBlocks['viberate_s'].xmlText(), MotaActionBlocks['animate_s'].xmlText(), MotaActionBlocks['setFg_0_s'].xmlText(), MotaActionBlocks['setFg_1_s'].xmlText(), diff --git a/_server/functions.comment.js b/_server/functions.comment.js index cc426cae..0b2901ae 100644 --- a/_server/functions.comment.js +++ b/_server/functions.comment.js @@ -87,19 +87,7 @@ functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = } } }, - "ui": { - "_leaf": false, - "_type": "object", - "_data": { - "drawAbout": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "绘制“关于”界面" - } - } - }, - "enemy": { + "enemys": { "_leaf": false, "_type": "object", "_data": { @@ -123,6 +111,18 @@ functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = } } }, + "ui": { + "_leaf": false, + "_type": "object", + "_data": { + "drawAbout": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "绘制“关于”界面" + } + } + }, "plugins": { "_leaf": false, "_type": "object", diff --git a/docs/event.md b/docs/event.md index cfeb84ed..6f159aaa 100644 --- a/docs/event.md +++ b/docs/event.md @@ -655,6 +655,10 @@ name为可选的,是要取消跟随的行走图文件名。 如果name省略,则会取消所有的跟随效果。 +### viberate:画面震动 + +使用 `{"type": "viberate", "time": 2000}` 可以造成画面震动效果,后面time可以指定震动时间。 + ### animate:显示动画 我们可以使用 `{"type": "animate"}` 来显示一段动画。 @@ -812,7 +816,7 @@ level为天气的强度等级,在1-10之间。1级为最弱,10级为最强 {"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),loc为位置可选,steps为移动数组 {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 - ], "immediateHide": true }, //immediateHide可选,制定为true则立刻消失,否则渐变消失 + ], "keep": true }, // keep可选,如果为true则不消失,否则渐变消失 ] ``` @@ -824,13 +828,13 @@ steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}` 如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。 -immediateHide为一个可选项,代表该事件移动完毕后是否立刻消失。如果该项指定了并为true,则移动完毕后直接消失,否则以动画效果消失。 +keep为一个可选项,代表该事件移动完毕后是否消失。如果该项指定了并为true,则移动完毕后将不消失,否则以动画效果消失。 值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用该点的hide事件。 换句话说,当move事件被调用后,该点本身的事件将被禁用。 -move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediateHide决定是否立刻消失还是以time作为时间来动画效果消失。 +如果指定了`"keep": true`,则相当于会在目标地点触发一个`setBlock`事件;如需能继续对话交互请在目标地点再写事件。 如果想让move后的NPC/怪物仍然可以被交互,需采用如下的写法: @@ -838,11 +842,9 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate "4,3": [ // [4,3]是一个NPC,比如小偷 {"type": "move", "time": 750, "steps": [ // 向上移动两格,每步750毫秒 {"direction": "up", "value": 2}, - ], "immediateHide": true}, // 移动完毕立刻消失 - {"type": "show", "loc": [4,1]} // 指定[4,1]点的NPC立刻生效(显示) - {"type": "trigger", "loc": [4,1]} // 立刻触发[4,1]点的事件 + ], "keep": true}, // 移动完毕后不消失 ], -"4,1": { // [4,1]也是这个NPC,而且是向上移动两个的位置 +"4,1": { // [4,1]为目标地点 "enable": false, // 初始时需要是禁用状态,被show调用后将显示出来 "data": [ "\t[杰克,thief]这样看起来就好像移动过去后也可以被交互。" @@ -879,7 +881,7 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate ``` js "x,y": [ // 实际执行的事件列表 - {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "immediateHide": true}, + {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true}, ] ``` @@ -889,13 +891,9 @@ to为要跳跃到的坐标。可以省略,如果省略则跳跃到当前坐标 time选项必须指定,为全程跳跃所需要用到的时间。 -immediateHide为一个可选项,同上代表该跳跃完毕后是否立刻消失。如果该项指定了并为true,则跳跃完毕后直接消失,否则以动画效果消失。 +keep为一个可选项,同上代表该跳跃完毕后是否不消失。如果该项指定了并为true,则跳跃完毕后不会消失,否则以动画效果消失。 -值得注意的是,当调用jump事件时,实际上是使事件脱离了原始地点。 - -为了避免冲突,同move事件一样规定:jump事件会自动调用该点的hide事件。 - -如果是本地跳跃,需要在跳跃完毕后再启用事件。 +如果指定了`"keep": true`,则相当于会在目标地点触发一个`setBlock`事件;如需能继续对话交互请在目标地点再写事件。 ### jumpHero:跳跃勇士 diff --git a/libs/control.js b/libs/control.js index f5d6cb94..f9048919 100644 --- a/libs/control.js +++ b/libs/control.js @@ -1917,10 +1917,17 @@ control.prototype.replay = function () { } } else if (action.indexOf('move:')==0) { + while (core.status.replay.toReplay.length>0 && + core.status.replay.toReplay[0].indexOf('move:')==0) { + action = core.status.replay.toReplay.shift(); + } + var pos=action.substring(5).split(":"); var x=parseInt(pos[0]), y=parseInt(pos[1]); if (core.control.moveDirectly(x,y)) { - core.replay(); + setTimeout(function () { + core.replay(); + }, 750 / core.status.replay.speed); return; } } diff --git a/libs/core.js b/libs/core.js index e6a35f7f..9707a9fb 100644 --- a/libs/core.js +++ b/libs/core.js @@ -665,13 +665,13 @@ core.prototype.getBlockId = function (x, y, floorId, needEnable) { } ////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// -core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { - core.maps.moveBlock(x,y,steps,time,immediateHide,callback) +core.prototype.moveBlock = function(x,y,steps,time,keep,callback) { + core.maps.moveBlock(x,y,steps,time,keep,callback) } ////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// -core.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { - core.maps.jumpBlock(sx,sy,ex,ey,time,immediateHide,callback); +core.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { + core.maps.jumpBlock(sx,sy,ex,ey,time,keep,callback); } ////// 显示/隐藏某个块时的动画效果 ////// diff --git a/libs/enemys.js b/libs/enemys.js index e6037779..57a48049 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -5,7 +5,7 @@ function enemys() { ////// 初始化 ////// enemys.prototype.init = function () { this.enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; - this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemy; + this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys; this.enemydata.hasSpecial = function (a, b) {return core.enemys.hasSpecial(a, b)}; } diff --git a/libs/events.js b/libs/events.js index a0eaaee6..45df159c 100644 --- a/libs/events.js +++ b/libs/events.js @@ -461,7 +461,7 @@ events.prototype.doAction = function() { x=core.calValue(data.loc[0]); y=core.calValue(data.loc[1]); } - core.moveBlock(x,y,data.steps,data.time,data.immediateHide,function() { + core.moveBlock(x,y,data.steps,data.time,data.keep,function() { core.events.doAction(); }) break; @@ -481,7 +481,7 @@ events.prototype.doAction = function() { ex=core.calValue(data.to[0]); ey=core.calValue(data.to[1]); } - core.jumpBlock(sx,sy,ex,ey,data.time,data.immediateHide,function() { + core.jumpBlock(sx,sy,ex,ey,data.time,data.keep,function() { core.events.doAction(); }); break; @@ -807,6 +807,11 @@ events.prototype.doAction = function() { core.updateStatusBar(); this.doAction(); break; + case "viberate": + core.events.vibrate(data.time, function () { + core.events.doAction(); + }) + break; case "sleep": // 等待多少毫秒 if (core.status.replay.replaying) core.events.doAction(); @@ -1307,6 +1312,65 @@ events.prototype.setVolume = function (value, time, callback) { }, time / 32); } +////// 画面震动 ////// +events.prototype.vibrate = function(time, callback) { + + if (core.isset(core.status.replay)&&core.status.replay.replaying) { + if (core.isset(callback)) callback(); + return; + } + + core.status.replay.animate=true; + + var setGameCanvasTranslate=function(x,y){ + for(var ii=0,canvas;canvas=core.dom.gameCanvas[ii];ii++){ + if(['data','ui'].indexOf(canvas.getAttribute('id'))!==-1)continue; + canvas.style.transform='translate('+x+'px,'+y+'px)'; + canvas.style.webkitTransform='translate('+x+'px,'+y+'px)'; + canvas.style.OTransform='translate('+x+'px,'+y+'px)'; + canvas.style.MozTransform='translate('+x+'px,'+y+'px)'; + } + } + + if (!core.isset(time) || time<1000) time=1000; + + var shake_duration = time*3/50; + var shake_speed = 5; + var shake_power = 5; + var shake_direction = 1; + var shake = 0; + + var update = function() { + if(shake_duration >= 1 || shake != 0){ + var delta = (shake_power * shake_speed * shake_direction) / 10.0; + if(shake_duration <= 1 && shake * (shake + delta) < 0){ + shake = 0; + }else{ + shake += delta; + } + if(shake > shake_power * 2){ + shake_direction = -1; + } + if(shake < - shake_power * 2){ + shake_direction = 1; + } + if(shake_duration >= 1){ + shake_duration -= 1 + } + } + } + + var animate=setInterval(function(){ + update(); + setGameCanvasTranslate(shake,0); + if(shake_duration===0) { + clearInterval(animate); + core.status.replay.animate=false; + if (core.isset(callback)) callback(); + } + }, 50/3); +} + ////// 打开一个全局商店 ////// events.prototype.openShop = function(shopId, needVisited) { var shop = core.status.shops[shopId]; diff --git a/libs/maps.js b/libs/maps.js index 2461a69c..fb29d1e6 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -557,9 +557,8 @@ maps.prototype.getBlockId = function (x, y, floorId, needEnable) { } ////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// -maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { +maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { time = time || 500; - core.status.replay.animate=true; core.clearMap('animate', 0, 0, 416, 416); @@ -568,6 +567,9 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { if (core.isset(callback)) callback(); return; } + var id = block.block.id; + + core.status.replay.animate=true; // 需要删除该块 core.removeBlock(x,y); @@ -625,7 +627,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { // 已经移动完毕,消失 if (moveSteps.length==0) { - if (immediateHide) opacityVal=0; + if (keep) opacityVal=0; else opacityVal -= 0.06; core.setOpacity('animate', opacityVal); core.clearMap('animate', nowX, nowY-height+32, 32, height); @@ -634,6 +636,11 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { clearInterval(animate); core.clearMap('animate', 0, 0, 416, 416); core.setOpacity('animate', 1); + // 不消失 + if (keep) { + core.setBlock(id, nowX/32, nowY/32); + core.showBlock(nowX/32, nowY/32); + } core.status.replay.animate=false; if (core.isset(callback)) callback(); } @@ -656,15 +663,17 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { } ////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// -maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { +maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { time = time || 500; - core.status.replay.animate=true; core.clearMap('animate', 0, 0, 416, 416); var block = core.getBlock(sx,sy); if (block==null) { if (core.isset(callback)) callback(); return; } + var id = block.block.id; + + core.status.replay.animate=true; // 需要删除该块 core.removeBlock(sx,sy); @@ -724,7 +733,7 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { core.canvas.animate.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, drawX(), drawY()-height+32, 32, height); } else { - if (immediateHide) opacityVal=0; + if (keep) opacityVal=0; else opacityVal -= 0.06; core.setOpacity('animate', opacityVal); core.clearMap('animate', drawX(), drawY()-height+32, 32, height); @@ -733,6 +742,10 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { clearInterval(animate); core.clearMap('animate', 0, 0, 416, 416); core.setOpacity('animate', 1); + if (keep) { + core.setBlock(id, ex, ey); + core.showBlock(ex, ey); + } core.status.replay.animate=false; if (core.isset(callback)) callback(); } @@ -873,6 +886,7 @@ maps.prototype.removeBlockByIds = function (floorId, ids) { maps.prototype.setBlock = function (number, x, y, floorId) { floorId = floorId || core.status.floorId; if (!core.isset(number) || !core.isset(x) || !core.isset(y)) return; + if (x<0 || x>12 || y<0 || y>12) return; var originBlock=core.getBlock(x,y,floorId,false); var block = core.maps.initBlock(x,y,number); diff --git a/main.js b/main.js index 20ccef09..964ab271 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,7 @@ function main() { //------------------------ 用户修改内容 ------------------------// - this.version = "2.3.2"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 + this.version = "2.3.3"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 this.useCompress = false; // 是否使用压缩文件 // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。 diff --git a/project/data.js b/project/data.js index 0af6ae06..b8aadbe8 100644 --- a/project/data.js +++ b/project/data.js @@ -26,7 +26,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "firstData" : { "title": "魔塔样板", "name": "template", - "version": "Ver 2.3.2", + "version": "Ver 2.3.3", "floorId": "sample0", "hero": { "name": "阳光", diff --git a/project/functions.js b/project/functions.js index 1800c1c8..da43dd86 100644 --- a/project/functions.js +++ b/project/functions.js @@ -306,7 +306,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.enemys.updateEnemys(); } }, -"enemy": { +"enemys": { "getSpecials" : function() { // 获得怪物的特殊属性,每一行定义一个特殊属性。 // 分为三项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 diff --git a/更新说明.txt b/更新说明.txt index 27bf7932..c795932a 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,12 +1,12 @@ HTML5魔塔样板V2.3.3 -将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 √ +将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 事件:画面震动 -事件:更新怪物数据 √ +事件:更新怪物数据 移动事件和跳跃事件增加“不消失”选项 -修改默认bgm √ -修复读档开启战斗动画等Bug √ -大量细节优化 √ +修改默认bgm +修复读档开启战斗动画等Bug +大量细节优化 -----------------------------------------------------------------------