diff --git a/README.md b/README.md index 4ea48018..6738ca64 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,20 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 -### 2018.7.9 V2.3.3 +### 2018.7.21 V2.3.3 + +* [x] 将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 +* [x] 地图编辑器可以使用矩形方式绘制地图 +* [x] 瞬间移动可以指定存在事件的点(如怪物、门、楼梯等) +* [x] 事件:画面震动 +* [x] 事件:更新怪物数据 +* [x] 移动事件和跳跃事件增加“不消失”选项 +* [x] 获胜结局可以指定“不计入榜单” +* [x] 修改默认bgm +* [x] 修复读档开启战斗动画等Bug +* [x] 大量细节优化 + +### 2018.7.9 V2.3.2 * [x] 适配手机端的造塔页面 * [x] 启动服务的多开版本 @@ -265,8 +278,27 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 联系我们 -本塔由 [`ckcz123`](https://github.com/ckcz123) (百度ID `艾之葵`)编写。 +本样板主要由 [`ckcz123`](https://github.com/ckcz123) (百度ID `艾之葵`)编写。 HTML5魔塔交流群群号: `539113091` 如有其它意见或建议,也可以通过发[issues](https://github.com/ckcz123/mota-js/issues)、或邮件至[ckcz123@126.com](mailto:ckcz123@126.com)联系我。 + +## 贡献者 + +感谢对本样板做出贡献的人员: + +[@ckcz123](https://github.com/ckcz123) 本样板的的主要编写者。样板的运行时的核心代码,所有常用小工具,以及安卓APP等,都是其所写。 + +[@Vinlic](https://github.com/Vinlic) 第一个HTML5魔塔[纪元魔塔前传](https://tieba.baidu.com/p/4545234500)([游戏地址](http://vinlic.gitee.io/mota/),[开发记录贴](https://tieba.baidu.com/p/4397526540),[源代码](https://gitee.com/Vinlic/Mota))的编写者。 +该塔的[第三版内核](https://tieba.baidu.com/p/4738973089)是现在HTML5魔塔样板的前身,现在样板的很多核心逻辑控制,以及UI界面等相关代码都是继承于该塔。 + +[@zhaouv](https://github.com/zhaouv) V2.0的推动者,可视化地图编辑器和事件编辑器的制作者,手机端魔塔制作界面的编写者。现在我们能在V2.0用到方便快捷的可视化地图编辑器,以及通过拖动图块来编写事件,能在手机端造塔等,都需要归功于zhaouv的贡献。 + +[@iEcho](https://github.com/iEcho) V2.0的推动者,可视化地图编辑器的制作者,游戏界面自适应匹配的编写者。和zhaouv一起推动和开发了V2.0的制作。 + +[@wadxm](https://github.com/wadxm) iOS平台的APP(因为苹果政策无法上架)和启动服务mac版的开发者。我们现在能在mac上制作魔塔得归功于他。 + +[@fux4](https://github.com/fux4) 打通了RM和H5之间的障壁(从而使RM动画导出器和怪物数据导出器成为可能),同时也是部分新功能(如跳跃、跟随、画面震动)等的编写者。 + +以及[百度贴吧魔塔吧](https://tieba.baidu.com/f?kw=%E9%AD%94%E5%A1%94)和H5魔塔交流群`539113091`内的诸位魔塔爱好者们对本样板的大力支持! diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 16938dcb..3a19e2ac 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; */; @@ -954,14 +969,14 @@ return code; */; win_s - : '游戏胜利,结局' ':' EvalString? Newline + : '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool Newline /* win_s tooltip : win: 获得胜利, 该事件会显示获胜页面, 并重新游戏 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=win%EF%BC%9A%E8%8E%B7%E5%BE%97%E8%83%9C%E5%88%A9 -default : [""] -var code = '{"type": "win", "reason": "'+EvalString_0+'"},\n'; +default : ["",false] +var code = '{"type": "win", "reason": "'+EvalString_0+'", "norank": '+(Bool_0?1: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([ @@ -1699,7 +1717,7 @@ ActionParser.prototype.parseAction = function() { break; case "win": this.next = MotaActionBlocks['win_s'].xmlText([ - data.reason,this.next]); + data.reason,data.norank?true:false,this.next]); break; case "lose": this.next = MotaActionBlocks['lose_s'].xmlText([ diff --git a/_server/css/editor_mobile.css b/_server/css/editor_mobile.css index 5beeecdb..5eb06c2f 100644 --- a/_server/css/editor_mobile.css +++ b/_server/css/editor_mobile.css @@ -290,13 +290,15 @@ table.col td { padding: 0; border-bottom-width: 0px; border-top-width: 0px; + border-left-width: 0.117307vw; + border-right-width: 0.117307vw; } #mapColMark td:hover .colBlock { position: absolute; top: 4vw; height: 96vw; - width: 7.15vw; + width: 7.384615vw; z-index: 100; background-color: rgba(38, 166, 154, .5); } @@ -333,12 +335,14 @@ table.row td { padding: 0; border-left-width: 0px; border-right-width: 0px; + border-top-width: 0.117307vw; + border-bottom-width: 0.117397vw; } #mapRowMark td:hover .rowBlock { position: absolute; left: 4vw; - height: 7.15vw; + height: 7.384615vw; width: 96vw; z-index: 100; background-color: rgba(76, 34, 27, .5); 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..bb7c6d94 100644 --- a/docs/event.md +++ b/docs/event.md @@ -201,21 +201,21 @@ } ``` -我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以,大致共有如下几种情况: +我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以。 -- `\t[hero]` 显示勇士的图标和名字 -- `\t[monster_id]`显示某个怪物的图标和名字。`monster_id`在`enemys`中有定义,请前往参照。 - - 例如:`\t[blackMagician]` 将显示黑暗大法师的图标和名字。 -- `\t[名字,npc_id]` 显示某个NPC的名字和图标。`npc_id`所对应的图标具体在`icons.js`中有定义,请前往参照。 - - 例如:`\t[小妖精,fairy]` 将显示名字为"小妖精",且是仙子的图标。 -- `\t[标题]` 直接显示标题。 - - 如果该中括号内只有一项,且不为`hero`也不为某个怪物的ID,则会直接显示。如 `\t[你死了]` 直接显示一个标题为"你死了"。 +其一般写法是`\t[名字,ID]`,其中名字为你要显示的标题,ID为图块ID,只能为`hero`,或者NPC/怪物的图块ID。 + +如果不需要可以不写ID,则只会显示标题。 + +对于hero和怪物,也可以不写名字代表使用默认值。 ``` js "x,y": [ // 实际执行的事件列表 "一段普通文字", - "\t[hero]这是一段勇士说的话", - "\t[blackMagician]这是一段黑暗大法师说的话", + "\t[勇士,hero]这是一段勇士说的话", + "\t[hero]如果使用勇士默认名称也可以直接简写hero", + "\t[黑暗大法师,blackMagician]我是黑暗大法师", + "\t[blackMagician]如果使用怪物的默认名称也可以简写怪物id", "\t[小妖精,fairy]这是一段小妖精说的话,使用仙子(fairy)的图标", "\t[你赢了]直接显示标题为【你赢了】", ] @@ -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/editor.html b/editor.html index 20d3a0cf..198bbaa7 100644 --- a/editor.html +++ b/editor.html @@ -236,9 +236,11 @@ -
+ + 画线 画矩形 +


diff --git a/libs/control.js b/libs/control.js index f5d6cb94..9daee5eb 100644 --- a/libs/control.js +++ b/libs/control.js @@ -377,7 +377,7 @@ control.prototype.clearContinueAutomaticRoute = function () { ////// 瞬间移动 ////// control.prototype.moveDirectly = function (destX, destY) { var ignoreSteps = core.canMoveDirectly(destX, destY); - if (ignoreSteps>0) { + if (ignoreSteps>=0) { core.clearMap('hero', 0, 0, 416, 416); var lastDirection = core.status.route[core.status.route.length-1]; if (['left', 'right', 'up', 'down'].indexOf(lastDirection)>=0) @@ -393,6 +393,22 @@ control.prototype.moveDirectly = function (destX, destY) { return false; } +////// 尝试瞬间移动 ////// +control.prototype.tryMoveDirectly = function (destX, destY) { + if (Math.abs(core.getHeroLoc('x')-destX)+Math.abs(core.getHeroLoc('y')-destY)<=1) + return false; + var testMove = function (dx, dy, dir) { + if (dx<0 || dx>12 || dy<0 || dy>12) return false; + if (core.control.moveDirectly(dx, dy)) { + if (core.isset(dir)) core.moveHero(dir, function() {}); + return true; + } + return false; + } + return testMove(destX,destY) || testMove(destX-1, destY, "right") || testMove(destX,destY-1,"down") + || testMove(destX,destY+1,"up") || testMove(destX+1,destY,"left"); +} + ////// 设置自动寻路路线 ////// control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { if (!core.status.played || core.status.lockControl) { @@ -406,7 +422,7 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { core.status.automaticRoute.moveDirectly = true; setTimeout(function () { if (core.status.automaticRoute.moveDirectly && core.status.heroMoving==0) { - core.control.moveDirectly(destX, destY); + core.control.tryMoveDirectly(destX, destY); } core.status.automaticRoute.moveDirectly = false; }, 100); @@ -431,7 +447,7 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { // 单击瞬间移动 if (core.status.automaticRoute.clickMoveDirectly && core.status.heroStop) { - if (core.control.moveDirectly(destX, destY)) + if (core.control.tryMoveDirectly(destX, destY)) return; } @@ -715,8 +731,19 @@ control.prototype.moveAction = function (callback) { } if (core.status.event.id!='ski') core.status.route.push(direction); - core.trigger(core.getHeroLoc('x'), core.getHeroLoc('y')); + + // 检查是不是无事件的道具 + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + var block = core.getBlock(nowx,nowy); + var hasTrigger = false; + if (block!=null && block.block.event.trigger=='getItem' && + !core.isset(core.floors[core.status.floorId].afterGetItem[nowx+","+nowy])) { + hasTrigger = true; + core.trigger(nowx, nowy); + } core.checkBlock(); + if (!hasTrigger && !core.status.gameOver) + core.trigger(nowx, nowy); if (core.isset(callback)) callback(); }); } @@ -1841,7 +1868,7 @@ control.prototype.replay = function () { core.useItem(itemId, function () { core.replay(); }); - }, 750 / core.status.replay.speed); + }, 750 / Math.max(1, core.status.replay.speed)); } return; } @@ -1861,7 +1888,7 @@ control.prototype.replay = function () { core.changeFloor(floorId, stair, null, null, function () { core.replay(); }); - }, 750 / core.status.replay.speed); + }, 750 / Math.max(1, core.status.replay.speed)); return; } } @@ -1894,7 +1921,7 @@ control.prototype.replay = function () { core.status.event.selection = parseInt(selections.shift()); core.events.openShop(shopId, false); - }, 750 / core.status.replay.speed); + }, 750 / Math.max(1, core.status.replay.speed)); return; } } @@ -1917,10 +1944,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 / Math.max(1, core.status.replay.speed)); return; } } diff --git a/libs/core.js b/libs/core.js index e6a35f7f..82fdd9ef 100644 --- a/libs/core.js +++ b/libs/core.js @@ -73,6 +73,7 @@ function core() { } this.initStatus = { 'played': false, + 'gameOver': false, // 勇士属性 'hero': {}, @@ -144,6 +145,7 @@ function core() { 'curtainColor': null, 'usingCenterFly':false, 'openingDoor': null, + 'isSkiing': false, // 动画 'globalAnimateObjs': [], @@ -665,13 +667,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); } ////// 显示/隐藏某个块时的动画效果 ////// @@ -764,6 +766,21 @@ core.prototype.updateFg = function () { core.control.updateFg(); } +////// 测试是否拥有某个特殊属性 ////// +core.prototype.hasSpecial = function (special, test) { + return core.enemys.hasSpecial(special, test); +} + +////// 判断能否战斗 ////// +core.prototype.canBattle = function(enemyId) { + return core.enemys.canBattle(enemyId); +} + +////// 获得伤害数值 ////// +core.prototype.getDamage = function(enemy) { + return core.enemys.getDamage(enemy); +} + ////// 获得某个物品的个数 ////// core.prototype.itemCount = function (itemId) { return core.items.itemCount(itemId); diff --git a/libs/enemys.js b/libs/enemys.js index e6037779..058389a4 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -5,8 +5,8 @@ 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.hasSpecial = function (a, b) {return core.enemys.hasSpecial(a, b)}; + this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys; + if (main.mode=='play') this.enemydata.hasSpecial = function (a, b) {return core.enemys.hasSpecial(a, b)}; } ////// 获得一个或所有怪物数据 ////// diff --git a/libs/events.js b/libs/events.js index a0eaaee6..877e83b9 100644 --- a/libs/events.js +++ b/libs/events.js @@ -30,6 +30,7 @@ events.prototype.init = function () { heroLoc = {'x': data.event.data.loc[0], 'y': data.event.data.loc[1]}; if (core.isset(data.event.data.direction)) heroLoc.direction = data.event.data.direction; + if (core.status.event.id!='action') core.status.event.id=null; core.changeFloor(data.event.data.floorId, data.event.data.stair, heroLoc, data.event.data.time, function () { if (core.isset(callback)) callback(); @@ -115,17 +116,19 @@ events.prototype.setInitData = function (hard) { } ////// 游戏获胜事件 ////// -events.prototype.win = function (reason) { - return this.eventdata.win(reason); +events.prototype.win = function (reason, norank) { + core.status.gameOver = true; + return this.eventdata.win(reason, norank); } ////// 游戏失败事件 ////// events.prototype.lose = function (reason) { + core.status.gameOver = true; return this.eventdata.lose(reason); } ////// 游戏结束 ////// -events.prototype.gameOver = function (ending, fromReplay) { +events.prototype.gameOver = function (ending, fromReplay, norank) { // 清空图片和天气 core.clearMap('animate', 0, 0, 416, 416); @@ -188,6 +191,7 @@ events.prototype.gameOver = function (ending, fromReplay) { formData.append('money', core.status.hero.money); formData.append('experience', core.status.hero.experience); formData.append('steps', core.status.hero.steps); + formData.append('norank', norank||0); formData.append('seed', core.getFlag('seed')); formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime/1000)); formData.append('route', core.encodeRoute(core.status.route)); @@ -461,7 +465,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 +485,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; @@ -744,7 +748,7 @@ events.prototype.doAction = function() { core.status.route.push("choices:"+index); core.events.insertAction(data.choices[index].action); core.events.doAction(); - }, 750 / core.status.replay.speed) + }, 750 / Math.max(1, core.status.replay.speed)) } else { core.stopReplay(); @@ -776,14 +780,10 @@ events.prototype.doAction = function() { this.doAction(); break; case "win": - core.events.win(data.reason, function () { - core.events.doAction(); - }); + core.events.win(data.reason, data.norank); break; case "lose": - core.events.lose(data.reason, function () { - core.events.doAction(); - }); + core.events.lose(data.reason); break; case "function": { @@ -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(); @@ -866,7 +871,7 @@ events.prototype.doAction = function() { ////// 往当前事件列表之前添加一个或多个事件 ////// events.prototype.insertAction = function (action, x, y, callback) { - if (core.status.event.id == null) { + if (core.status.event.id != 'action') { this.doEvents(action, x, y, callback); } else { @@ -1017,6 +1022,7 @@ events.prototype.battle = function (id, x, y, force, callback) { ////// 触发(x,y)点的事件 ////// events.prototype.trigger = function (x, y) { + core.status.isSkiing = false; var mapBlocks = core.status.thisMap.blocks; var noPass; for (var b = 0; b < mapBlocks.length; b++) { @@ -1028,6 +1034,8 @@ events.prototype.trigger = function (x, y) { if (core.isset(mapBlocks[b].event) && core.isset(mapBlocks[b].event.trigger)) { var trigger = mapBlocks[b].event.trigger; + if (trigger == 'ski') core.status.isSkiing = true; + // 转换楼层能否穿透 if (trigger=='changeFloor' && !noPass) { var canCross = core.flags.portalWithoutTrigger; @@ -1307,6 +1315,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]; @@ -1517,7 +1584,7 @@ events.prototype.ski = function (direction) { } else { core.moveHero(direction, function () { - if (core.status.event.id=='ski') { + if (core.status.event.id=='ski' && !core.status.isSkiing) { core.status.event.id=null; core.unLockControl(); core.replay(); diff --git a/libs/maps.js b/libs/maps.js index 2461a69c..624d68d2 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -257,17 +257,17 @@ maps.prototype.canMoveHero = function(x,y,direction,floorId) { maps.prototype.canMoveDirectly = function (destX,destY) { // 不可瞬间移动请返回0 - if (!core.flags.enableMoveDirectly) return 0; + if (!core.flags.enableMoveDirectly) return -1; // 中毒状态:不能 - if (core.hasFlag('poison')) return 0; + if (core.hasFlag('poison')) return -1; var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); if (fromX==destX&&fromY==destY) return 0; // 可以无视起点事件 // if (core.getBlock(fromX,fromY)!=null||core.status.checkBlock.damage[13*fromX+fromY]>0) - // return 0; + // return -1; // BFS var visited=[], queue=[]; @@ -286,7 +286,7 @@ maps.prototype.canMoveDirectly = function (destX,destY) { queue.push(13*nx+ny); } } - return 0; + return -1; } maps.prototype.drawBlock = function (block, animate, dx, dy) { @@ -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/libs/ui.js b/libs/ui.js index ec919d78..5a28b569 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -278,8 +278,10 @@ ui.prototype.drawTextBox = function(content) { if (ss.length==1) { // id id=ss[0]; - // monster - if (id!='hero') { + if (id=='hero') { + name = core.status.hero.name; + } + else { if (core.isset(core.material.enemys[id])) { name = core.material.enemys[id].name; @@ -305,20 +307,35 @@ ui.prototype.drawTextBox = function(content) { } } else { - id='npc'; name=ss[0]; - if (core.isset(core.material.icons.npc48[ss[1]])) { + id = 'npc'; + if (ss[1]=='hero') { + id = 'hero'; + } + else if (core.isset(core.material.icons.npc48[ss[1]])) { image = core.material.images.npc48; icon = core.material.icons.npc48[ss[1]]; iconHeight = 48; animate=4; } - else { + else if (core.isset(core.material.icons.npcs[ss[1]])){ image = core.material.images.npcs; icon = core.material.icons.npcs[ss[1]]; iconHeight = 32; animate=2; } + else if (core.isset(core.material.icons.enemy48[ss[1]])) { + image = core.material.images.enemy48; + icon = core.material.icons.enemy48[ss[1]]; + iconHeight = 48; + animate=4; + } + else if (core.isset(core.material.icons.enemys[ss[1]])) { + image = core.material.images.enemys; + icon = core.material.icons.enemys[ss[1]]; + iconHeight = 32; + animate=2; + } } } } @@ -459,7 +476,7 @@ ui.prototype.drawTextBox = function(content) { if (id == 'hero') { var heroHeight=core.material.icons.hero.height; core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, heroHeight+2, null, 2); - core.fillText('ui', core.status.hero.name, content_left, top + 30, null, 'bold 22px Verdana'); + core.fillText('ui', name, content_left, top + 30, null, 'bold 22px Verdana'); core.clearMap('ui', left + 15, top + 40, 32, heroHeight); core.fillRect('ui', left + 15, top + 40, 32, heroHeight, background); var heroIcon = core.material.icons.hero['down']; @@ -542,17 +559,20 @@ ui.prototype.drawChoices = function(content, choices) { if (core.isset(content)) { // 获得name, image, icon - if (content.indexOf("\t[")==0) { + if (content.indexOf("\t[")==0 || content.indexOf("\\t[")==0) { var index = content.indexOf("]"); if (index>=0) { var str=content.substring(2, index); + if (content.indexOf("\\t[")==0) str=content.substring(3, index); content=content.substring(index+1); var ss=str.split(","); if (ss.length==1) { // id id=ss[0]; - // monster - if (id!='hero') { + if (id=='hero') { + name = core.status.hero.name; + } + else { if (core.isset(core.material.enemys[id])) { name = core.material.enemys[id].name; @@ -578,20 +598,35 @@ ui.prototype.drawChoices = function(content, choices) { } } else { - id='npc'; name=ss[0]; - if (core.isset(core.material.icons.npc48[ss[1]])) { + id = 'npc'; + if (ss[1]=='hero') { + id = 'hero'; + } + else if (core.isset(core.material.icons.npc48[ss[1]])) { image = core.material.images.npc48; icon = core.material.icons.npc48[ss[1]]; iconHeight = 48; animate=4; } - else { + else if (core.isset(core.material.icons.npcs[ss[1]])){ image = core.material.images.npcs; icon = core.material.icons.npcs[ss[1]]; iconHeight = 32; animate=2; } + else if (core.isset(core.material.icons.enemy48[ss[1]])) { + image = core.material.images.enemy48; + icon = core.material.icons.enemy48[ss[1]]; + iconHeight = 48; + animate=4; + } + else if (core.isset(core.material.icons.enemys[ss[1]])) { + image = core.material.images.enemys; + icon = core.material.icons.enemys[ss[1]]; + iconHeight = 32; + animate=2; + } } } } @@ -631,7 +666,7 @@ ui.prototype.drawChoices = function(content, choices) { if (id == 'hero') { var heroHeight = core.material.icons.hero.height; core.strokeRect('ui', left + 15 - 1, top + 30 - 1, 34, heroHeight+2, '#DDDDDD', 2); - core.fillText('ui', core.status.hero.name, title_offset, top + 27, '#FFD700', 'bold 19px Verdana'); + core.fillText('ui', name, title_offset, top + 27, '#FFD700', 'bold 19px Verdana'); core.clearMap('ui', left + 15, top + 30, 32, heroHeight); core.fillRect('ui', left + 15, top + 30, 32, heroHeight, background); var heroIcon = core.material.icons.hero['down']; 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..9bd9eda5 100644 --- a/project/functions.js +++ b/project/functions.js @@ -50,7 +50,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.events.afterLoadData(); }, ////// 游戏获胜事件 ////// -"win" : function(reason) { +"win" : function(reason, norank) { // 游戏获胜事件 core.ui.closePanel(); var replaying = core.status.replay.replaying; @@ -61,7 +61,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.drawText([ "\t[" + (reason||"恭喜通关") + "]你的分数是${status:hp}。" ], function () { - core.events.gameOver(reason||'', replaying); + core.events.gameOver(reason||'', replaying, norank); }) }); }, @@ -306,7 +306,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.enemys.updateEnemys(); } }, -"enemy": { +"enemys": { "getSpecials" : function() { // 获得怪物的特殊属性,每一行定义一个特殊属性。 // 分为三项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 diff --git a/project/images/enemys.png b/project/images/enemys.png index 01d4396f..2c29805b 100644 Binary files a/project/images/enemys.png and b/project/images/enemys.png differ diff --git a/更新说明.txt b/更新说明.txt index 27bf7932..acdc3a25 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,12 +1,15 @@ HTML5魔塔样板V2.3.3 -将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 √ +将怪物特殊属性定义和伤害计算函数移动到脚本编辑中 +地图编辑器可以使用矩形方式绘制地图 +瞬间移动可以指定存在事件的点(如怪物、门、楼梯等) 事件:画面震动 -事件:更新怪物数据 √ +事件:更新怪物数据 移动事件和跳跃事件增加“不消失”选项 -修改默认bgm √ -修复读档开启战斗动画等Bug √ -大量细节优化 √ +获胜结局可以指定“不计入榜单” +修改默认bgm +修复读档开启战斗动画等Bug +大量细节优化 -----------------------------------------------------------------------