diff --git a/.idea/mota-js.xml b/.idea/mota-js.xml new file mode 100644 index 00000000..24643cc3 --- /dev/null +++ b/.idea/mota-js.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..e145e12f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, Zhang Chen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index db47a83d..66369b8a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ``` bash ├── /_server/ # 为可视化地图编辑器提供一些支持的目录 ├── /docs/ # 文档目录 +├── /animates/ # 动画目录 ├── /images/ # 所有图片素材目录 │ ├─ /常用素材/ # 可以被直接替换的素材 │ └─ *.png # 对应的某个具体的图片素材 @@ -32,6 +33,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! │ └─ ui.js # UI绘制信息,主要负责绘制各个UI窗口。 ├── /sounds/ # 音效目录 ├── /常用工具/ # 一些常用工具,可以辅助造塔 +│ ├─ RM动画导出器.exe # 能从RMXP中导出动画,以供H5使用。 http://github.com/ckcz123/animate_export/ │ ├─ JS代码压缩工具.exe # 能对Javascript代码进行压缩和整合,从而减少IO请求量。 http://github.com/ckcz123/JSCompressor/ │ ├─ 便捷PS工具.exe # 能只用复制和粘贴来快速对素材进行PS操作。 http://github.com/ckcz123/ps/ │ ├─ 地图生成器.exe # 能从一张截图识别出来具体的数字数组,方便复刻已有的塔。 http://github.com/ckcz123/map_generator/ @@ -45,6 +47,48 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 +### 2018.2.9 V1.4.1 + +* [x] 改变图块(setBlock事件)。 +* [x] 同一个点的多事件处理(做法详见文档)。 +* [x] 地图中每个块的可通行方向控制(悬崖效果)。 +* [x] 动画支持带旋转和翻转的帧。 +* [x] 现在可以允许用户丢弃道具了(例如不会再使用的装备)。 +* [x] 修复行走时按键会发生动画抖动问题。 +* [x] 修复无法打开战斗动画的Bug。 + +### 2018.2.6 V1.4 + +* [x] 支持动画。 +* [x] 瞬间移动。 +* [x] 支持天气系统,可以在剧本中设置默认天气。 +* [x] 新增自定义事件-图片显示。 +* [x] 同时可以在剧本中设定多个背景素材。 +* [x] 剧情文本特性控制,人物的对话框效果。 +* [x] 单存档同步到服务器,下载到文件和读取。 +* [x] 键盘支持自动寻路操作。 +* [x] 浏览地图模式下可以查看怪物数据。 +* [x] 未成功打怪和开门则不自动存档。 +* [x] 重新支持楼梯穿透。 +* [x] 支持多结局,成绩将分开统计。 +* [x] 重构全局动画、行走动画和行走检测,大幅提升性能。 +* [x] 修复所有已知Bug。 + +### 2018.1.21 V1.3.2 + +* [x] 增加录像和回放功能。 +* [x] 增加统计功能,现在能看到每部塔的游戏人数、通关人数和当前MAX了。 +* [x] 增加浏览地图功能,玩家可以快速查看每层楼的地图。 +* [x] 现在保存文件到本地,以及从本地文件读档了。 +* [x] 可以在全局开关中设置剑盾是否作为装备存在。 +* [x] 修复了部分已知Bug。 + +### 2018.1.12 V1.3.1 + +* [x] 增加虚拟键盘 +* [x] 增加自动存档(回退),A键可快速读档 +* [x] 修复几处较为严重的Bug + ### 2018.1.1 V1.3 * [x] 支持全键盘操作。 diff --git a/_server/css/editor.css b/_server/css/editor.css index 1ffb46b9..13e74df7 100644 --- a/_server/css/editor.css +++ b/_server/css/editor.css @@ -47,6 +47,7 @@ body{ white-space: pre; border: 1px solid #ddd; border-radius: 2px; + overflow: auto; } #editTip{ position: absolute; @@ -68,7 +69,6 @@ body{ margin-right: 20px; margin-top: 5px; } - #mid{ position: absolute; left: 448px; diff --git a/_server/editor.js b/_server/editor.js index 8741332d..a6dc7b18 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -1,5 +1,5 @@ function editor() { - this.version = "1.2"; + this.version = "2.0"; this.material = {}; } @@ -11,7 +11,7 @@ editor.prototype.init = function(callback){ editor.reset(function(){ editor.drawMapBg(); - var mapArray = core.maps.getMapArray(core.status.maps, core.status.floorId); + var mapArray = core.maps.getMapArray(core.status.maps[core.status.floorId].blocks); editor.map = mapArray.map(function(v){return v.map(function(v){return editor.ids[[editor.indexs[v][0]]]})}); editor.updateMap(); editor.currentFloorId=core.status.floorId; @@ -28,7 +28,7 @@ editor.prototype.init = function(callback){ editor.material.images=core.material.images; editor.listen(); // 开始监听事件 var hard = 'Hard'; - core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, core.initStatus.maps); + core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, null, core.initStatus.maps); //core.status.maps = core.clone(core.maps.initMaps(floorIds)); core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() { afterCoreReset(); @@ -298,7 +298,7 @@ editor.prototype.changeFloor = function(floorId,callback) { editor.currentFloorData.map = editor.map.map(function(v){return v.map(function(v){return v.idnum||v||0})}); core.changeFloor(floorId, null, core.firstData.hero.loc, null, function(){ editor.drawMapBg(); - var mapArray = core.maps.getMapArray(core.status.maps, core.status.floorId); + var mapArray = core.maps.getMapArray(core.status.maps[core.status.floorId].blocks); editor.map = mapArray.map(function(v){return v.map(function(v){return editor.ids[[editor.indexs[v][0]]]})}); editor.updateMap(); editor.currentFloorId=core.status.floorId; @@ -318,9 +318,11 @@ editor.prototype.listen = function() { }//在格子内画一个随机色块 function eToLoc(e) { + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop editor.loc = { - 'x': document.documentElement.scrollLeft+e.clientX - mid.offsetLeft-mapEdit.offsetLeft, - 'y': document.documentElement.scrollTop+e.clientY - mid.offsetTop-mapEdit.offsetTop, + 'x': scrollLeft+e.clientX - mid.offsetLeft-mapEdit.offsetLeft, + 'y': scrollTop+e.clientY - mid.offsetTop-mapEdit.offsetTop, 'size': 32 }; return editor.loc; }//返回可用的组件内坐标 @@ -453,9 +455,11 @@ editor.prototype.listen = function() { edata.onmousedown = function (e) { e.stopPropagation(); + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; var loc = { - 'x': document.documentElement.scrollLeft + e.clientX + iconLib.scrollLeft - right.offsetLeft-iconLib.offsetLeft, - 'y': document.documentElement.scrollTop + e.clientY + iconLib.scrollTop - right.offsetTop-iconLib.offsetTop, + 'x': scrollLeft + e.clientX + iconLib.scrollLeft - right.offsetLeft-iconLib.offsetLeft, + 'y': scrollTop + e.clientY + iconLib.scrollTop - right.offsetTop-iconLib.offsetTop, 'size': 32 }; editor.loc = loc; diff --git a/_server/vendor/polyfill.min.js b/_server/vendor/polyfill.min.js new file mode 100644 index 00000000..80fe8dc8 --- /dev/null +++ b/_server/vendor/polyfill.min.js @@ -0,0 +1 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(){}function n(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,f._immediateFn(function(){var i=1===e._state?n.onFulfilled:n.onRejected;if(null!==i){var r;try{r=i(e._value)}catch(e){return void o(n.promise,e)}t(n.promise,r)}else(1===e._state?t:o)(n.promise,e._value)})):e._deferreds.push(n)}function t(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof f)return e._state=3,e._value=n,void i(e);if("function"==typeof t)return void r(function(e,n){return function(){e.apply(n,arguments)}}(t,n),e)}e._state=1,e._value=n,i(e)}catch(n){o(e,n)}}function o(e,n){e._state=2,e._value=n,i(e)}function i(e){2===e._state&&0===e._deferreds.length&&f._immediateFn(function(){e._handled||f._unhandledRejectionFn(e._value)});for(var t=0,o=e._deferreds.length;o>t;t++)n(e,e._deferreds[t]);e._deferreds=null}function r(e,n){var i=!1;try{e(function(e){i||(i=!0,t(n,e))},function(e){i||(i=!0,o(n,e))})}catch(e){if(i)return;i=!0,o(n,e)}}function f(e){if(!(this instanceof f))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],r(e,this)}var u=setTimeout,c=f.prototype;c.catch=function(e){return this.then(null,e)},c.then=function(t,o){var i=new this.constructor(e);return n(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(t,o,i)),i},f.all=function(e){return new f(function(n,t){function o(e,f){try{if(f&&("object"==typeof f||"function"==typeof f)){var u=f.then;if("function"==typeof u)return void u.call(f,function(n){o(e,n)},t)}i[e]=f,0==--r&&n(i)}catch(e){t(e)}}if(!e||void 0===e.length)throw new TypeError("Promise.all accepts an array");var i=Array.prototype.slice.call(e);if(0===i.length)return n([]);for(var r=i.length,f=0;i.length>f;f++)o(f,i[f])})},f.resolve=function(e){return e&&"object"==typeof e&&e.constructor===f?e:new f(function(n){n(e)})},f.reject=function(e){return new f(function(n,t){t(e)})},f.race=function(e){return new f(function(n,t){for(var o=0,i=e.length;i>o;o++)e[o].then(n,t)})},f._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){u(e,0)},f._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var a=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==a)return a;throw Error("unable to locate global object")}();a.Promise||(a.Promise=f)}); diff --git a/_server/vm.js b/_server/vm.js index 3ae26442..8b227a19 100644 --- a/_server/vm.js +++ b/_server/vm.js @@ -10,15 +10,13 @@ iconLib.onmousedown = function(e){ } var exportM = new Vue({ el: '#exportM', - + data: { + isExport: false, + }, methods: { exportMap: function(){ editor.updateMap(); - if(editArea.error) { - tip.whichShow = 3; - - return; - } + var filestr=''; for (var yy = 0; yy < 13; yy++){ filestr+='[' @@ -43,7 +41,9 @@ var exportM = new Vue({ filestr += ']'+(yy==12?'':',\n'); } pout.value = filestr; - + editArea.mapArr = filestr; + this.isExport = true; + editArea.error = 0; tip.whichShow = 2; } } @@ -65,10 +65,15 @@ var editArea = new Vue({ mapArr: function (val, oldval) { var that = this; if(val=='') return; + if(exportM.isExport){ + exportM.isExport = false; + return; + } if(that.formatArr()){ that.error = 0; - clearTimeout(that.formatTimer); + setTimeout(function(){ + that.mapArr = that.formatArr(); that.drawMap(); tip.whichShow = 8 }, 1000); @@ -108,7 +113,8 @@ var editArea = new Vue({ }, formatArr: function(){ var formatArrStr = ''; - + var that = this; + clearTimeout(that.formatTimer); if(this.mapArr.split(/\D+/).join(' ').trim().split(' ').length != 169) return false; var arr = this.mapArr.replace(/\s+/g, '').split('],['); @@ -129,7 +135,6 @@ var editArea = new Vue({ } formatArrStr += ']'+(i==12?'':',\n'); } - return formatArrStr; } } diff --git a/docs/api.md b/docs/api.md index ebd28f99..ec92f8a5 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ # 附录:API列表 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 所有系统支持的API都列在了这里。所有可能被用到的API都在前面用\*标记。 @@ -35,7 +35,7 @@ main.statusBar.image.load.onclick // 点击状态栏中的读档按钮时 main.statusBar.image.settings.onclick // 点击状态栏中的系统菜单时 main.dom.playGame.onclick // 点击“开始游戏”时 main.dom.loadGame.onclick // 点击“载入游戏”时 -main.dom.aboutGame.onclick // 点击“关于本塔”时 +main.dom.replayGame.onclick // 点击“录像回放”时 main.dom.easyLevel.onclick // 点击“简单难度”时 main.dom.normalLevel.onclick // 点击“普通难度”时 main.dom.hardLevel.onclick // 点击“困难难度”时 @@ -56,6 +56,7 @@ core.hideStartAnimate // 隐藏游戏开始界面 core.setStartProgressVal // 设置加载进度条进度 core.setStartLoadTipText // 设置加载进度条提示文字 core.loader // 加载图片和音频 +core.loadAutotile // 加载Autotile core.loadImage // 加载图片 core.loadMusic // 加载音频 core.isPlaying // 游戏是否已经开始 @@ -93,6 +94,7 @@ core.setAutoHeroMove // 设置勇士的自动行走路线 core.autoHeroMove // 让勇士开始自动行走 core.setHeroMoveInterval // 设置行走的效果动画 core.setHeroMoveTriggerInterval // 设置勇士行走过程中对事件的触发检测 +core.moveAction // 实际每一步的行走过程 * core.turnHero(direction) // 设置勇士的方向(转向) core.canMoveHero // 勇士能否前往某方向 core.moveHero // 让勇士开始移动 @@ -146,7 +148,6 @@ core.addGlobalAnimate // 添加一个全局动画 core.removeGlobalAnimate // 删除一个或所有全局动画 core.setGlobalAnimate // 设置全局动画的显示效果 core.syncGlobalAnimate // 同步所有的全局动画效果 -core.setBoxAnimate // 显示UI层某个box的动画(如怪物手册中怪物的动画) core.drawBoxAnimate // 绘制UI层的box动画 core.updateCheckBlock // 更新领域、夹击、阻击的伤害地图 core.checkBlock // 检查并执行领域、夹击、阻击事件 @@ -176,18 +177,25 @@ core.getLocalStorage // 获得本地存储 core.removeLocalStorage // 移除本地存储 core.clone // 深拷贝一个对象 core.formatDate // 格式化时间为字符串 +core.formatDate2 // 格式化时间为最简字符串 core.setTwoDigits // 两位数显示 core.debug // 进入Debug模式,攻防血和钥匙都调成很高的数值 +core.replay // 开始回放 core.checkStatus // 判断当前能否进入某个事件 core.openBook // 点击怪物手册时的打开操作 core.useFly // 点击楼层传送器时的打开操作 core.openToolbox // 点击工具栏时的打开操作 +core.openQuickShop // 点击快捷商店时的打开操作 core.save // 点击保存按钮时的打开操作 core.load // 点击读取按钮时的打开操作 +core.openSettings // 点击设置按钮时的打开操作 +core.autosave // 自动存档 core.doSL // 实际进行存读档事件 core.syncSave // 存档同步操作 core.saveData // 存档到本地 core.loadData // 从本地读档 +core.encodeRoute // 将路线压缩 +core.decodeRoute // 将路线解压缩 * core.setStatus // 设置勇士属性 * core.getStatus // 获得勇士属性 core.getLvName // 获得某个等级的名称 @@ -198,6 +206,9 @@ core.insertAction // 往当前事件列表之前插入一系列事件 * core.lockControl // 锁定状态栏,常常用于事件处理 * core.unlockControl // 解锁状态栏 * core.isset // 判断某对象是否不为undefined也不会null +core.readFile // 读取一个本地文件内容 +core.download // 下载文件到本地 +core.copy // 复制一段文字到剪切板 * core.playBgm // 播放背景音乐 * core.pauseBgm // 暂停背景音乐的播放 * core.resumeBgm // 恢复背景音乐的播放 @@ -240,6 +251,7 @@ core.events.startGame // 游戏开始事件 * core.events.setInitData // 不同难度分别设置初始属性 * core.events.win // 游戏获胜事件 * core.events.lose // 游戏失败事件 +core.evens.gameOver // 游戏结束 core.events.afterChangeFloor // 转换楼层结束的事件 core.events.doEvents // 开始执行一系列自定义事件 core.events.doAction // 执行当前自定义事件列表中的下一个事件 @@ -260,6 +272,7 @@ core.events.changeLight // 改变亮灯(感叹号)的事件 * core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作 // ------ 点击事件和键盘事件的处理 ------ +core.events.longClick // 长按 core.events.keyDownCtrl // 按下Ctrl键时(快捷跳过对话) core.events.clickConfirmBox // 确认框界面时的点击操作 core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作 @@ -273,6 +286,9 @@ core.events.clickBookDetail // 怪物手册属性显示界面时的点击操作 core.events.clickFly // 楼层传送器界面时的点击操作 core.events.keyDownFly // 楼层传送器界面时,按下某个键的操作 core.events.keyUpFly // 楼层传送器界面时,放开某个键的操作 +core.events.clickViewMaps // 浏览地图界面时的点击操作 +core.events.keyDownViewMaps // 浏览地图界面时,按下某个键的操作 +core.events.keyUpViewMaps // 浏览地图界面时,放开某个键的操作 core.events.clickShop // 商店界面时的点击操作 core.events.keyDownShop // 商店界面时,按下某个键的操作 core.events.keyUpShop // 商店界面时,放开某个键的操作 @@ -295,6 +311,7 @@ core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作 core.events.clickSyncSave // 同步存档界面时的点击操作 core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作 core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作 +core.events.clickKeyBoard // 虚拟键盘界面时的点击操作 core.events.clickAbout // “关于”界面时的点击操作 ``` @@ -341,6 +358,7 @@ core.ui.drawPagination // 绘制分页 core.ui.drawEnemyBook // 绘制怪物手册 core.ui.drawBookDetail // 绘制怪物属性的详细信息 core.ui.drawFly // 绘制楼层传送器 +core.ui.drawMaps // 绘制浏览地图界面 core.ui.drawToolbox // 绘制道具栏 core.ui.drawSLPanel // 绘制存档/读档界面 core.ui.drawThumbnail // 绘制一个缩略图 diff --git a/docs/element.md b/docs/element.md index ef764af5..d66b2c41 100644 --- a/docs/element.md +++ b/docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 @@ -12,9 +12,9 @@ 本塔目前支持的所有道具列表在样板0层中已全部给出。当你在样板0层中拿到某个宝物时会有提示,这里不再赘述,详见拿到该道具的说明。 -大多数宝物都有默认的效果,十字架和屠龙匕首暂未定义,如有自己的需求可参见[自定义道具效果](personalization#自定义道具效果)。 +大多数宝物都有默认的效果,屠龙匕首暂未定义,如有自己的需求可参见[自定义道具效果](personalization#自定义道具效果)。 -!> 请注意,本塔没有"装备"的说法,所有剑盾拿到后将立刻作为攻防数值直接加到勇士的属性上。 +如需让剑盾变成装备,可以直接在`data.js`中设置`'equipment': true`即可。 拿到道具后将触发`afterGetItem`事件,有关事件的详细介绍请参见[事件](event)。 @@ -65,6 +65,7 @@ enemys.prototype.getSpecialText = function (enemyId) { if (this.hasSpecial(special, 18)) text.push("阻击"); if (this.hasSpecial(special, 19)) text.push("自爆"); if (this.hasSpecial(special, 20)) text.push("无敌"); + if (this.hasSpecial(special, 21)) text.push("退化"); return text.join(" "); } ``` @@ -94,6 +95,8 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 吸血怪需要在怪物后添加value,代表吸血的比例。 +可以给吸血怪添加`'add': true`来将吸血的数值加到自身上。 + ![怪物吸血](./img/blood.png) 中毒怪让勇士中毒后,每步扣减的生命值由`data.js`中的values定义。 @@ -106,7 +109,7 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 领域怪需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 -领域是十字伤害还是九宫格伤害由data.js中的全局变量`zoneSquare`设定。你也可以对该怪物自行进行设定。 +领域是十字伤害还是九宫格伤害由`zoneSquare`设定,如设置为true则为九宫格伤害,不指定或为false则为十字伤害。 `range`选项可选,代表该领域怪的范围,不写则默认为1。 @@ -118,6 +121,10 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 请注意如果吸血、领域、阻击中任何两个同时存在,则value会冲突。**因此请勿将吸血、领域或阻击放置在同一个怪物身上。** +退化怪需要在后面增加'atkValue'和'defValue'表示退化的数值。 + +![怪物退化](./img/tuihua.png) + 如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。 ## 路障,楼梯,传送门 @@ -142,9 +149,41 @@ floorId指定的是目标楼层的唯一标识符(ID)。 可以指定time,指定后切换动画时长为指定的数值。 -楼梯和传送门默认可`"穿透"`。所谓穿透,就是当寻路穿过一个楼梯/传送门后,不会触发楼层传送事件,而是继续前进。通过系统Flag可以指定是否穿透,你也可以对每个传送点单独设置该项。 +## 动画和天气系统 -![楼层转换穿透](./img/floorset.png) +现在我们的H5魔塔支持播放动画,也支持天气系统了。 + +要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后在main.js中定义。 + +``` js +this.animates = [// 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 + // 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 + "hand", "sword", "zone", "yongchang", "thunder" // 根据需求自行添加 +] +``` + +!> 动画必须是animate格式,名称不能使用中文,不能带空格或特殊字符。 + +目前暂时不支持带旋转和翻转的帧。 + +导出动画时可能会进行一些压缩以节省流量,因此清晰度可能不如原版。 + +动画播放时,是按照每秒20帧的速度(即50ms/帧)。 + +定义完毕后,我们可以调用`animate`事件来播放该动画,有关事件的详细介绍请参见[事件](event)。 + +!> 播放录像时,将默认忽略所有动画。 + +目前天气系统只支持雨和雪两种天气。 + +在每层楼的剧本文件里存在一个weather选项,表示该层楼的默认天气。 + +``` js +// 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain"或"snow"代表雨雪,第二项为1-10之间的数代表强度。 +"weather": ["snow",5] +``` + +我们也可以使用`setWeather`事件来设置当前天气,有关事件的详细介绍请参见[事件](event)。 ## 背景音乐 @@ -169,6 +208,8 @@ this.sounds = [ // 在此存放所有的SE,和文件名一致 !> mid格式是通过数学方法模拟出来的音乐效果,质量可能会和实际效果差距较大。 +!> **警告!** mid格式在手机端播放可能会特别卡,仍推荐直接使用mp3/ogg来播放。 + 定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。 **另外,考虑到用户的流量问题,将遵循如下规则:** @@ -190,17 +231,21 @@ this.sounds = [ // 在此存放所有的SE,和文件名一致 - **点任意块并拖动:** 指定寻路路线 - **单击勇士:** 转向 - **双击勇士:** 轻按(仅在轻按开关打开时有效) +- **长按任意位置:** 打开虚拟键盘 键盘操作快捷键如下: - **[CTRL]** 跳过对话 - **[X]** 打开/关闭怪物手册 - **[G]** 打开/关闭楼层传送器 +- **[A]** 读取自动存档 - **[S/D]** 打开/关闭存/读档页面 - **[K]** 打开/关闭快捷商店选择列表 - **[T]** 打开/关闭工具栏 - **[ESC]** 打开/关闭系统菜单 - **[H]** 打开帮助页面 +- **[Z]** 转向 +- **[R]** 回退 - **[SPACE]** 轻按(仅在轻按开关打开时有效) - **[1]** 快捷使用破墙镐 - **[2]** 快捷使用炸弹/圣锤 diff --git a/docs/event.md b/docs/event.md index 58d0917e..8bdf07cb 100644 --- a/docs/event.md +++ b/docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 @@ -16,7 +16,8 @@ - 启用状态下,该事件才处于可见状态,可被触发、交互与处理。 - 禁用状态下该事件相当于不存在,不可见、不可被触发、不可交互。 -所有事件默认情况下都是启用的,除非指定了`enable: false`。 +所有事件默认情况下都是启用的,除非指定了`enable: false`。 + 在事件列表中使用`type: show`和`type: hide`可以将一个禁用事件启用,或将一个启用事件给禁用。 @@ -213,6 +214,25 @@ ] ``` +除此以外,我们还能实现“对话框效果”,只要有`\b[...]`就可以。 + +- `\b[up]` 直接显示在当前点上方。同样把这里的up换成down则为下方。 + - 如果不存在当前点(如在firstArrive中调用),则显示在屏幕最上方(最下方) +- `\b[up,hero]` 显示在勇士上方。同样把这里的up换成down则为下方。 +- `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数且在0到12之间。 + +``` js +"x,y": [ // 实际执行的事件列表 + "\b[up]这段文字显示在当前点上方", + "\b[down]这段文字显示在当前点上方", + "\t[hero]\b[up,hero]这是一段勇士说的话,会显示在勇士上方", + "\t[小妖精,fairy]\b[down,2,2]这是一段小妖精说的话,会显示在(2,2)点下方", +] +``` + +!> `\t[...]`必须在`\b[...]`前面!不然两者都无法正常显示。 + + 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js @@ -241,6 +261,25 @@ ![调试](./img/eventdebug.png) +### setText:设置剧情文本的属性 + +使用`{"type": "setText"}`可以设置剧情文本的各项属性。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "setText", "position": "up", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3]}, + "这段话将显示在上方,标题为红色,正文为黄色,背景为透明度0.3的蓝色" +] +``` + +position为可选项,表示设置文字显示位置。只能为up(上),center(中)和down(下)三者。 + +title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 + +text为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示正文颜色。 + +background为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示背景色。 + ### tip:显示一段提示文字 `{"type": "tip"}`可以在左上角显示一段提示文字。 @@ -380,13 +419,40 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ] ``` +### setBlock:设置某个图块 + +我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 + {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 + {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 +] +``` + +floorId为可选的,表示要更改的目标楼层。如果忽略此项,则默认为当前楼层。 + +loc为可选的,表示要更改地图块的坐标。如果忽略此项,则默认为当前事件点。 + +number为**要更改到的数字**,有关“数字”的定义详见参见[素材的机制](personalization#素材的机制)。 + +图块更改后: + + - 其启用/禁用状态不会发生任何改变。原来是启用还是启用,原来是禁用还是禁用。 + - 可通行状态遵循覆盖原则,即**首先取该图块的默认noPass属性,如果剧本的events中定义该点的noPass则覆盖**。 + - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果剧本的events中定义了该点的trigger则覆盖**。 + +图块更改往往与[同一个点的多事件处理](#同一个点的多事件处理)相关。 + ### update: 立刻更新状态栏和地图显伤 -当我们在上面调用show事件,显示一个怪物后,该怪物将不会有显伤显示。如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update"}` 即可。 +如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update"}` 即可。 ### sleep: 等待多少毫秒 等价于RMXP中的"等待x帧",不过是以毫秒来计算。 + 基本写法:`{"type": "sleep", "time": xxx}` ,其中xxx为指定的毫秒数。 ``` js @@ -465,6 +531,8 @@ direction为可选的,指定的话将使勇士的朝向变成该方向 time为可选的,指定的话将作为楼层切换动画的时间。 +**time也可以置为0,如果为0则没有楼层切换动画。** + !> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。** ### changePos: 当前位置切换/勇士转向 @@ -489,6 +557,48 @@ time为可选的,指定的话将作为楼层切换动画的时间。 使用disableShop可以永久禁用全局商店直到再次被openShop打开为止。有关全局商店的说明可参见[全局商店](#全局商店)。 +### animate:显示动画 + +我们可以使用 `{"type": "animate"}` 来显示一段动画。 + +有关动画的详细介绍可参见[动画和天气系统](element#动画和天气系统)。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "animate", "name": "yongchang", "loc": [1,3]}, // 在(1,3)显示“咏唱魔法”动画 + {"type": "animate", "name": "zone", "loc": "hero"}, // 在勇士位置显示“领域”动画 + {"type": "animate", "name": "hand"} // 可以不指定loc,则默认为当前事件点 +] +``` + +name为动画名,**请确保动画在main.js中的this.animates中被定义过。** + +loc为动画的位置,可以是`[x,y]`表示在(x,y)点显示,也可以是字符串`"hero"`表示在勇士点显示。 + +loc可忽略,如果忽略则显示为事件当前点。 + +在动画播放结束后才会继续执行下一个事件。 + +### showImage:显示图片 + +我们可以使用 `{"type": "showImage"}` 来显示一张图片。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "showImage", "name": "bg", "loc": [231,297]}, // 在(231,297)显示bg.png + {"type": "showImage", "name": "1", "loc": [109,167]}, // 在(109,167)显示1.png + {"type": "showImage"} // 如果不指定name则清除所有图片。 +] +``` + +name为图片名。**请确保图片在main.js中的this.pngs中被定义过。** + +loc为图片左上角坐标,以像素为单位进行计算。 + +如果不指定name则清除所有显示的图片。 + +调用show/hide/move/animate等几个事件同样会清除所有显示的图片。 + ### setFg: 更改画面色调 我们可以使用 `{"type": "setFg"}` 来更改画面色调。 @@ -509,6 +619,26 @@ color为需要更改画面色调的颜色。它是一个数组,分别指定目 time为可选的,如果指定,则会作为更改画面色调的时间。 +### setWeather:更改天气 + +我们可以使用 `{"type": "setWeather"}` 来更改天气。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "setWeather", "name": "rain", "level": 6}, // 更改为雨天,强度为6级 + {"type": "setWeather", "name": "snow", "level": 3}, // 更改为雪天,强度为3级 + {"type": "setWeather"} // 更改回晴天 +] +``` + +name为天气选项。目前只支持`rain`和`snow`,即雨天和雪天。 + +level为天气的强度等级,在1-10之间。1级为最弱,10级为最强。 + +如果想改回晴天则直接不加任何参数。 + +!> 使用setWeather更改的天气在切换地图后会被目标地图的默认天气覆盖。 + ### move: 让某个NPC/怪物移动 如果我们需要移动某个NPC或怪物,可以使用`{"type": "move"}`。 @@ -798,6 +928,81 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 // …… ``` +## 同一个点的多事件处理 + +我们可以发现,就目前而且,每个点的事件是和该点进行绑定,并以该点坐标作为唯一索引来查询。 + +而有时候,我们往往需要在同一个点存在多个不同的事件。这涉及到同一个点的多事件处理。 + +我们可以依靠两来实现。**`setBlock`事件**和**if+flag的条件判断**。 + +下面以几个具体例子来进行详细说明。 + +### 打怪掉宝(怪物->道具) + +我们注意到怪物和道具都是系统默认事件,因此无需写events,而是直接在afterBattle中setBlock即可。 + +``` js +"afterBattle": { + "x,y": [ + {"type": "setBlock", "number": 21} // 变成黄钥匙。注意是当前点因此可省略floorId和loc + ] +} +``` + +### 打怪变成可对话的NPC(怪物->NPC) + +由于NPC是自定义事件,因此我们需要写events。注意到events中不覆盖trigger,则还是怪物时,存在系统trigger因此会战斗;变成NPC后没有系统trigger因此会触发自定义事件。 + +``` js +"events": { + "x,y": [ + "可对话的NPC" + ] +}, +"afterBattle": { + "x,y": [ + {"type": "setBlock", "number": 121} // 变成老人 + ] +} +``` + +### 获得圣水后变成墙 + +这个例子要求获得圣水时不前进(也就是不能走到圣水地方),然后把圣水位置变成墙。 + +因此需要我们需要覆盖系统trigger(getItem),并覆盖noPass。 + +通过if来判断有没有获得圣水,没有则触发圣水(生命x2)然后变成墙,否则不执行。 + +``` js +"events": { + "x,y": { + "trigger": "action", // 覆盖系统trigger,默认的getItem不会执行 + "noPass": true, // 覆盖可通行状态,不允许走到该点 + "data": [ + {"type": "if", "condition": "flag:hasSuperPotion", // 条件判断:是否喝过圣水 + "true": [], // 喝过了,不执行 + "false": [ + {"type":"setValue", "name":"status:hp", "value":"status:hp*2"}, // 生命翻倍 + {"type":"setBlock", "number": 1}, // 将该点变成墙 + {"type":"setValue", "name":"flag:hasSuperPotion", "value": "true"} // 标记已经喝过了 + ] + } + ] + ] +} +``` + + +总之,记住如下两点: + + - 可以使用setBlock来更改一个图块。 + - 可通行状态遵循覆盖原则,即**首先取该图块的默认noPass属性,如果剧本的events中定义该点的noPass则覆盖**。 + - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果剧本的events中定义了该点的trigger则覆盖**。 + - 可以通过if语句和flag来控制自定义事件具体走向哪个分支。 + - 如果弄不清楚系统trigger和自定义事件等的区别,也可以全部覆盖为自定义事件,然后通过type:battle,type:openDoor等来具体进行控制。 + ## 加点事件 打败怪物后可以进行加点。 @@ -844,8 +1049,9 @@ events.prototype.addPoint = function (enemy) { 全局商店定义在`data.js`中,找到shops一项。 ``` js -"shops": { // 定义全局商店(即快捷商店) - "moneyShop1": { // 商店唯一ID +"shops": [ // 定义全局商店(即快捷商店) + { + "id": "moneyShop1", // 商店唯一ID "name": "贪婪之神", // 商店名称(标题) "icon": "blueShop", // 商店图标,blueShop为蓝色商店,pinkShop为粉色商店 "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 @@ -870,7 +1076,8 @@ events.prototype.addPoint = function (enemy) { // "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍 ] }, - "expShop1": { // 商店唯一ID + { + "id": "expShop1", // 商店唯一ID "name": "经验之神", "icon": "pinkShop", "textInList": "1F经验商店", @@ -885,15 +1092,15 @@ events.prototype.addPoint = function (enemy) { {"text": "攻击+5", "need": "30", "effect": "status:atk+=5"}, {"text": "防御+5", "need": "30", "effect": "status:def+=5"}, ] - }, -}, + } +], ``` -全局商店全部定义在`data.js`中的shops一项里。 -每个全局商店有一个唯一标识符(ID),然后是一系列对该商店的定义。 +全局商店全部定义在`data.js`中的shops一项里。商店以数组形式存放,每一个商店都是其中的一个对象。 +- id 为商店的唯一标识符(ID),请确保任何两个商店的id都不相同 - name 为商店的名称(打开商店后的标题) -- icon 为商店的图标,blueShop为蓝色商店,pinkShop为粉色商店 +- icon 为商店的图标,在icons.js的npcs中定义。如woman可代表一个商人。 - textInList 为其在快捷商店栏中显示的名称,如"3楼金币商店"等 - use 为消耗的类型,是金币(money)还是经验(experience)。 - need 是一个表达式,计算商店所需要用到的数值。 @@ -967,6 +1174,26 @@ events.prototype.addPoint = function (enemy) { 当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。 +## 使用炸弹后的事件 + +上面的afterBattle事件只对和怪物进行战斗后才有会被处理。 + +如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在`events.js`里面的`afterUseBomb`函数进行处理: + +``` js +////// 使用炸弹/圣锤后的事件 ////// +events.prototype.afterUseBomb = function () { + // 这是一个使用炸弹也能开门的例子 + if (core.status.floorId=='xxx' && core.terrainExists(x0,y0,'specialDoor') // 某个楼层,该机关门存在 + && !core.enemyExists(x1,y1) && !core.enemyExists(x2,y2)) // 且守门的怪物都不存在 + { + core.insertAction([ // 插入事件 + {"type": "openDoor", "loc": [x0,y0]} // 开门 + ]) + } +} +``` + ## 战前剧情 有时候光战后事件`afterBattle`是不够的,我们可能还需要战前剧情,例如Boss战之前和Boss进行一段对话。 diff --git a/docs/img/blood.png b/docs/img/blood.png index 7d369e74..53d52171 100644 Binary files a/docs/img/blood.png and b/docs/img/blood.png differ diff --git a/docs/img/tuihua.png b/docs/img/tuihua.png new file mode 100644 index 00000000..de7d1836 Binary files /dev/null and b/docs/img/tuihua.png differ diff --git a/docs/index.md b/docs/index.md index 9f0059ef..7a358d0d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # HTML5 魔塔样板说明文档 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 diff --git a/docs/personalization.md b/docs/personalization.md index 9b4eb0ca..1f4fbd0b 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -1,6 +1,6 @@ # 个性化 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 @@ -27,27 +27,35 @@ 由于HTML5功能(素材)有限,导致了对很多比较复杂的素材(比如房子内)等无法有着较好的绘图方式。 -为了解决这个问题,我们允许用户自己放置一张图片作为某一层的背景素材。 +为了解决这个问题,我们允许用户自己放置一张或多张图片作为某一层的背景素材。 要启用这个功能,我们首先需要在`main.js`中将可能的图片进行加载。 ``` js -this.pngs = [ // 在此存放所有可能的背景图片;背景图片最好是416*416像素,其他分辨率会被强制缩放成416*416 - // 建议对于较大的图片,在网上使用在线的“图片压缩工具”来进行压缩,以节省流量 - "bg.png", // "yewai.png", +this.pngs = [ // 在此存放所有可能使用的图片,只能是png格式,可以不写后缀名 + // 图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 + // 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 + // 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 + "bg", // 依次向后添加 ]; ``` -!> 背景素材只支持png格式,且会被强制缩放到416*416。 +!> 背景素材只支持png格式。 -!> 请使用网上的一些[在线图片压缩工具](http://www.asqql.com/gifzip/)对png图片进行压缩,以节省流量。一张500KB的png图片可以被压缩到20-30KB,显示效果不会有太大差异。 +!> 请使用网上的一些[在线图片压缩工具](http://compresspng.com/zh/)对png图片进行压缩,以节省流量。一张500KB的png图片可以被压缩到20-30KB,显示效果不会有太大差异。 -之后,我们可以在每层剧本的`"png": "xxx"`里来定义该层的默认背景图片素材。 +之后,我们可以在每层剧本的`"png"`里来定义该层的默认背景图片素材。 ``` js -"png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。 +"png": [[x,y,"bg"]], // 背景图;你可以选择一张或多张png图片来作为背景素材。 +"png": [], // 无任何背景图 +"png": [[1,1,"house"], [6,7,"house2"]] // 在(1,1)放一个house.png,且(6,7)放house2.png ``` +png为一个数组,代表当前层所有作为背景素材的图片信息。 + +每一项为一个三元组,分别为该背景素材的x,y和图片名。其中x和y分别为横纵坐标,在0-12之间;图片名则必须在上面的this.pngs中定义过。 + 你的图片背景素材将会覆盖原来本身的背景层。 **如果你需要让某些点不可通行(比如你建了个房子,墙壁和家具等位置不让通行),则需在`events`中指定`{"noPass": false}`,参见[自定义事件](event#自定义事件)的写法。 @@ -213,15 +221,23 @@ if (itemId === 'shield5') { core.setFlag("shield5", true); // 增加一个自定义Flag:已经拿到神圣盾 } ``` -2. 免疫吸血效果:在`enemys.js`的getExtraDamage函数中,编辑成如果存在神圣盾标记,额外伤害为0。 +2. 免疫吸血效果:在`enemys.js`的伤害计算中,编辑成如果存在神圣盾标记,吸血伤害为0。 ``` js -enemys.prototype.getExtraDamage = function (monster) { - var extra_damage = 0; - if (this.hasSpecial(monster.special, 11)) { // 吸血 - // 吸血的比例 - extra_damage = core.status.hero.hp * monster.value; - if (core.hasFlag("shield5")) extra_damage = 0; // 如果存在神圣盾,则免疫吸血 - extra_damage = parseInt(extra_damage); +enemys.prototype.calDamage = function (monster, hero_hp, hero_atk, hero_def, hero_mdef) { +// ... 上略 + // 吸血 + if (this.hasSpecial(mon_special, 11)) { + var vampireDamage = hero_hp * monster.value; + + // 如果有神圣盾免疫吸血等可以在这里写 + if (core.hasFlag("shield5")) vampireDamage = 0; // 存在神圣盾,吸血伤害为0 + + vampireDamage = parseInt(vampireDamage); + // 加到自身 + if (monster.add) // 如果加到自身 + mon_hp += vampireDamage; + + initDamage += vampireDamage; } // ... 下略 ``` @@ -255,8 +271,6 @@ core.prototype.checkBlock = function () { 如果要修改伤害计算公式,请修改下面的calDamage函数。请注意,如果无法战斗,该函数必须返回`999999999`。 -对于吸血怪的额外伤害计算在getExtraDamage中。 - 对于毒衰弱怪物的战斗后结算在`events.js`中的afterBattle函数中。 对于领域、夹击、阻击怪物的检查在`events.js`中的checkBlock函数中。 diff --git a/docs/start.md b/docs/start.md index 8ddde2af..c9a838c0 100644 --- a/docs/start.md +++ b/docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 上次更新时间:* {docsify-updated} * +?> 目前版本**v1.4.1**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! @@ -9,7 +9,7 @@ 你需要有满足如下条件才能进行制作: - Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“启动服务.exe”即可) -- 任一款现代浏览器。强烈推荐Chrome。 +- Chrome浏览器。其他浏览器可能会导致本地服务器产生闪退等现象。 - 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。 - ([VSCode下载地址](https://code.visualstudio.com/),群里的群文件中也有,强烈推荐之。) @@ -28,6 +28,8 @@ * “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。 * “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。 +!> **警告:** 非Chrome浏览器(如Edge/IE等)下本地服务器可能表现不正常,会出现闪退等现象。请务必下载安装Chrome浏览器。 + ## 新建剧本 类似于RMXP,本塔每层楼都是一个“剧本”,剧本内主要定义了本层的地图和各种事件。主函数将读取每个剧本,并生成实际的地图供游戏使用。 @@ -147,7 +149,7 @@ 只需要修改自己用到的怪物属性即可,其他没有用到的怪物完全无所谓。 -做完后保存所有文件,然后右键,选择使用chrome浏览器打开`index.html`,就能立刻看到自己的塔并开始游戏啦!是不是很简单呢! +做完后保存所有文件,在本地服务器中“启动游戏”,就能立刻看到自己的塔并开始游戏啦!是不是很简单呢! ![保存](./img/save.png) diff --git a/editor.html b/editor.html index 1d03c983..4e1f4ebd 100644 --- a/editor.html +++ b/editor.html @@ -179,7 +179,7 @@
开始游戏 载入游戏 - 关于本塔 + 录像回放
简单 @@ -256,6 +256,8 @@ + + 此浏览器不支持HTML5
@@ -284,6 +286,7 @@ + diff --git a/index.html b/index.html index a8d12214..5c84fe0c 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@
开始游戏 载入游戏 - 关于本塔 + 录像回放
简单 @@ -109,11 +109,13 @@ + + 此浏览器不支持HTML5
- + \ No newline at end of file diff --git a/libs/core.js b/libs/core.js index 93926547..33de6eae 100644 --- a/libs/core.js +++ b/libs/core.js @@ -3,17 +3,8 @@ */ function core() { - this.dom = {}; - this.statusBar = {}; - this.canvas = {}; - this.images = []; - this.pngs = []; - this.bgms = []; - this.sounds = []; - this.floorIds = []; - this.floors = {}; - this.firstData = {}; this.material = { + 'animates': {}, 'images': {}, 'bgms': {}, 'sounds': {}, @@ -28,14 +19,27 @@ function core() { 'turnHeroTimeout': null, } this.interval = { - 'twoAnimate': null, - 'fourAnimate': null, - 'boxAnimate': null, 'heroMoveTriggerInterval': null, 'heroMoveInterval': null, - 'heroAutoMoveScan': null, "tipAnimate": null, - 'openDoorAnimate': null + 'openDoorAnimate': null, + 'animateInterval': null, + } + this.animateFrame = { + 'background': null, + 'globalAnimate': false, + 'twoTime': null, + 'fourTime': null, + 'boxTime': null, + 'moveTime': null, + 'speed': null, + 'weather': { + 'time': null, + 'type': null, + 'level': 0, + 'nodes': [], + 'data': null, + } } this.musicStatus = { 'audioContext': null, // WebAudioContext @@ -45,6 +49,21 @@ function core() { 'playingBgm': null, // 正在播放的BGM 'isPlaying': false, } + this.platform = { + 'isOnline': true, // 是否http + 'isPC': true, // 是否是PC + 'isAndroid': false, // 是否是Android + 'isIOS': false, // 是否是iOS + 'isWeChat': false, // 是否是微信 + 'isQQ': false, // 是否是QQ + 'isChrome': false, // 是否是Chrome + 'supportCopy': false, // 是否支持复制到剪切板 + + 'fileInput': null, // FileInput + 'fileReader': null, // 是否支持FileReader + 'successCallback': null, // 读取成功 + 'errorCallback': null, // 读取失败 + } // 样式 this.domStyle = { styles: [], @@ -62,23 +81,39 @@ function core() { 'maps': null, 'checkBlock': {}, // 显伤伤害 - // 勇士状态;自动寻路相关 - 'heroMoving': false, - 'heroStop': true, 'lockControl': false, - 'autoHeroMove': false, - 'automaticRouting': false, - 'automaticRouted': false, - 'autoStep': 0, - 'movedStep': 0, - 'destStep': 0, - 'automaticRoutingTemp': {'destX': 0, 'destY': 0, 'moveStep': []}, - 'autoStepRoutes': [], - 'holdingPath': 0, - 'stepPostfix': [], - 'mouseOutCheck': 1, - 'moveStepBeforeStop': [], + // 勇士移动状态 + 'heroMoving': 0, + 'heroStop': true, + + // 自动寻路相关 + 'automaticRoute': { + 'autoHeroMove': false, + 'autoStep': 0, + 'movedStep': 0, + 'destStep': 0, + 'destX': null, + 'destY': null, + 'autoStepRoutes': [], + 'moveStepBeforeStop': [], + 'cursorX': null, + 'cursorY': null, + }, + + // 按下键的时间:为了判定双击 + 'downTime': null, + + // 路线&回放 + 'route': [], + 'replay': { + 'replaying': false, + 'pausing': false, + 'animate': false, // 正在某段动画中 + 'toReplay': [], + 'totalList': [], + 'speed': 1.0 + }, // event事件 'saveIndex': null, @@ -89,6 +124,12 @@ function core() { 'selection': null, 'ui': null, }, + 'textAttribute': { + 'position': "center", + "title": [255,215,0,1], + "background": [0,0,0,0.85], + "text": [255,255,255,1], + }, 'curtainColor': null, 'usingCenterFly':false, 'openingDoor': null, @@ -99,22 +140,12 @@ function core() { 'boxAnimateObjs': [], }; this.status = {}; - this.flags = {}; } /////////// 系统事件相关 /////////// ////// 初始化 ////// -core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, sounds, floorIds, floors, coreData) { - core.dom = dom; - core.statusBar = statusBar; - core.canvas = canvas; - core.images = images; - core.pngs = pngs; - core.bgms = bgms; - core.sounds = sounds; - core.floorIds = floorIds; - core.floors = floors; +core.prototype.init = function (coreData) { for (var key in coreData) { core[key] = coreData[key]; } @@ -128,18 +159,24 @@ core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, soun } core.values = core.clone(core.data.values); core.firstData = core.data.getFirstData(); - core.initStatus.shops = core.firstData.shops; + + // core.initStatus.shops = core.firstData.shops; + core.firstData.shops.forEach(function (t) { + core.initStatus.shops[t.id] = t; + }) + core.dom.versionLabel.innerHTML = core.firstData.version; core.dom.logoLabel.innerHTML = core.firstData.title; document.title = core.firstData.title + " - HTML5魔塔"; document.getElementById("startLogo").innerHTML = core.firstData.title; core.material.items = core.items.getItems(); - core.initStatus.maps = core.maps.initMaps(floorIds); + core.initStatus.maps = core.maps.initMaps(core.floorIds); core.material.enemys = core.clone(core.enemys.getEnemys()); core.material.icons = core.icons.getIcons(); core.material.events = core.events.getEvents(); - if (location.protocol.indexOf("http")==0) { + core.platform.isOnline = location.protocol.indexOf("http")==0; + if (core.platform.isOnline) { window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; try { core.musicStatus.audioContext = new window.AudioContext(); @@ -149,12 +186,57 @@ core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, soun } } - // 音效设置部分 - var isPC = true; ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"].forEach(function (t) { - if (navigator.userAgent.indexOf(t)>=0) isPC=false; + if (navigator.userAgent.indexOf(t)>=0) { + if (t=='iPhone' || t=='iPad' || t=='iPod') core.platform.isIOS = true; + if (t=='Android') core.platform.isAndroid=true; + core.platform.isPC=false; + } }); - if (isPC) { + + try { + core.platform.supportCopy = document.queryCommandSupported("copy"); + } + catch (e) { + core.platform.supportCopy = false; + } + + var chrome=/Chrome\/(\d+)\./i.exec(navigator.userAgent); + if (core.isset(chrome) && parseInt(chrome[1])>=50) + core.platform.isChrome = true; + core.platform.isSafari = /Safari/i.test(navigator.userAgent) && !/Chrome/i.test(navigator.userAgent); + core.platform.isQQ = /QQ/i.test(navigator.userAgent); + core.platform.isWeChat = /MicroMessenger/i.test(navigator.userAgent); + + if (window.FileReader) { + core.platform.fileReader = new FileReader(); + core.platform.fileReader.onload = function () { + var content=core.platform.fileReader.result; + var obj=null; + try { + obj=JSON.parse(content); + if (core.isset(obj)) { + if (core.isset(core.platform.successCallback)) + core.platform.successCallback(obj); + return; + } + } + catch (e) { + console.log(e); + } + alert("不是有效的JSON文件!"); + + if (core.isset(core.platform.errorCallback)) + core.platform.errorCallback(); + + }; + core.platform.fileReader.onerror = function () { + if (core.isset(core.platform.errorCallback)) + core.platform.errorCallback(); + } + } + + if (core.platform.isPC) { // 如果是PC端直接加载 core.musicStatus.startDirectly = true; } @@ -173,7 +255,6 @@ core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, soun core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); core.setLocalStorage('soundStatus', core.musicStatus.soundStatus); - // switchs core.flags.battleAnimate = core.getLocalStorage('battleAnimate', core.flags.battleAnimate); core.flags.displayEnemyDamage = core.getLocalStorage('enemyDamage', core.flags.displayEnemyDamage); @@ -188,10 +269,184 @@ core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, soun // 设置勇士高度 core.material.icons.hero.height = core.material.images.hero.height/4; + core.setRequestAnimationFrame(); + core.showStartAnimate(); }); } +////// 设置requestAnimationFrame ////// +core.prototype.setRequestAnimationFrame = function () { + + (function() { + var lastTime = 0; + var vendors = ['webkit', 'moz']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字变了 + window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); + var id = window.setTimeout(function() { + callback(currTime + timeToCall); + }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + }()); + + core.animateFrame.speed = core.values.animateSpeed; + core.animateFrame.background = core.canvas.ui.createPattern(core.material.ground, "repeat"); + + var scan = { + 'up': {'x': 0, 'y': -1}, + 'left': {'x': -1, 'y': 0}, + 'down': {'x': 0, 'y': 1}, + 'right': {'x': 1, 'y': 0} + }; + + var draw = function(timestamp) { + + core.animateFrame.twoTime = core.animateFrame.twoTime||timestamp; + core.animateFrame.fourTime = core.animateFrame.fourTime||timestamp; + core.animateFrame.boxTime = core.animateFrame.boxTime||timestamp; + core.animateFrame.moveTime = core.animateFrame.moveTime||timestamp; + core.animateFrame.weather.time = core.animateFrame.weather.time||timestamp; + + // Global Animate + if (core.animateFrame.globalAnimate && core.isPlaying()) { + + if (timestamp-core.animateFrame.twoTime>core.animateFrame.speed && core.isset(core.status.twoAnimateObjs)) { + + for (var a = 0; a < core.status.twoAnimateObjs.length; a++) { + var obj = core.status.twoAnimateObjs[a]; + obj.status = (obj.status+1)%2; + core.canvas.event.clearRect(obj.x, obj.y, 32, 32); + core.canvas.event.drawImage(obj.image, obj.status * 32, obj.loc * 32, 32, 32, obj.x, obj.y, 32, 32); + } + + core.animateFrame.twoTime = timestamp; + } + + if (timestamp-core.animateFrame.fourTime>core.animateFrame.speed/2 && core.isset(core.status.fourAnimateObjs)) { + for (var a = 0; a < core.status.fourAnimateObjs.length; a++) { + var obj=core.status.fourAnimateObjs[a]; + obj.status = (obj.status+1)%4; + core.canvas.event.clearRect(obj.x, obj.y, 32, 32); + core.canvas.event.drawImage(obj.image, obj.status * 32, obj.loc * 32, 32, 32, obj.x, obj.y, 32, 32); + } + // fourtime = timestamp % fourDelta; + core.animateFrame.fourTime = timestamp; + } + + } + + // Box + if (timestamp-core.animateFrame.boxTime>core.animateFrame.speed && core.isset(core.status.boxAnimateObjs) && core.status.boxAnimateObjs.length>0) { + core.drawBoxAnimate(); + core.animateFrame.boxTime = timestamp; + } + + // Hero move + if (timestamp-core.animateFrame.moveTime>16 && core.isset(core.status.heroMoving) && core.status.heroMoving>0) { + var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); + if (core.status.heroMoving<=4) { + core.drawHero(direction, x, y, 'leftFoot', 4*core.status.heroMoving*scan[direction].x, 4*core.status.heroMoving*scan[direction].y); + } + else if (core.status.heroMoving<=8) { + core.drawHero(direction, x, y, 'rightFoot', 4*core.status.heroMoving*scan[direction].x, 4*core.status.heroMoving*scan[direction].y); + } + core.animateFrame.moveTime = timestamp; + } + + // weather + if (core.isPlaying() && timestamp-core.animateFrame.weather.time>30) { + if (core.animateFrame.weather.type == 'rain' && core.animateFrame.weather.level > 0) { + + core.clearMap('weather', 0, 0, 416, 416); + + core.canvas.weather.strokeStyle = 'rgba(174,194,224,0.8)'; + core.canvas.weather.lineWidth = 1; + core.canvas.weather.lineCap = 'round'; + + core.animateFrame.weather.nodes.forEach(function (p) { + core.canvas.weather.beginPath(); + core.canvas.weather.moveTo(p.x, p.y); + core.canvas.weather.lineTo(p.x + p.l * p.xs, p.y + p.l * p.ys); + core.canvas.weather.stroke(); + + p.x += p.xs; + p.y += p.ys; + if (p.x > 416 || p.y > 416) { + p.x = Math.random() * 416; + p.y = -10; + } + + }) + + core.canvas.weather.fill(); + + } + else if (core.animateFrame.weather.type == 'snow' && core.animateFrame.weather.level > 0) { + + core.clearMap('weather', 0, 0, 416, 416); + + core.canvas.weather.fillStyle = "rgba(255, 255, 255, 0.8)"; + core.canvas.weather.beginPath(); + + if (!core.isset(core.animateFrame.weather.data)) + core.animateFrame.weather.data = 0; + core.animateFrame.weather.data += 0.01; + + var angle = core.animateFrame.weather.data; + core.animateFrame.weather.nodes.forEach(function (p) { + core.canvas.weather.moveTo(p.x, p.y); + core.canvas.weather.arc(p.x, p.y, p.r, 0, Math.PI * 2, true); + + // update + p.x += Math.sin(angle) * 2; + p.y += Math.cos(angle + p.d) + 1 + p.r / 2; + + if (p.x > 416 + 5 || p.x < -5 || p.y > 416) { + if (Math.random() > 1 / 3) { + p.x = Math.random() * 416; + p.y = -10; + } + else { + if (Math.sin(angle) > 0) { + p.x = -5; + p.y = Math.random() * 416; + } + else { + p.x = 416 + 5; + p.y = Math.random() * 416; + } + } + } + + }) + + core.canvas.weather.fill(); + + } + core.animateFrame.weather.time = timestamp; + + } + window.requestAnimationFrame(draw); + } + window.requestAnimationFrame(draw); +} + ////// 显示游戏开始界面 ////// core.prototype.showStartAnimate = function (callback) { core.dom.startPanel.style.opacity=1; @@ -285,7 +540,7 @@ core.prototype.loadAutotile = function (callback) { core.material.images.autotile={}; var autotileIds = Object.keys(core.material.icons.autotile); if (autotileIds.length==0) { - core.loadMusic(callback); + core.loadAnimates(callback); return; } for (var x=0;x0){ var stepPostfix = []; var direction={'0':{'1':'down','-1':'up'},'-1':{'0':'left'},'1':{'0':'right'}}; @@ -841,8 +1268,16 @@ core.prototype.onup = function () { core.status.stepPostfix=[]; core.canvas.ui.clearRect(0, 0, 416,416); core.canvas.ui.restore(); - core.onclick(posx,posy,stepPostfix); - //posx,posy是寻路的目标点,stepPostfix是后续的移动 + + // 长按 + if (!core.status.lockControl && stepPostfix.length==0 && core.status.downTime!=null && new Date()-core.status.downTime>=1000) { + core.events.longClick(); + } + else { + //posx,posy是寻路的目标点,stepPostfix是后续的移动 + core.onclick(posx,posy,stepPostfix); + } + core.status.downTime=null; } } @@ -873,8 +1308,11 @@ core.prototype.getClickLoc = function (x, y) { ////// 具体点击屏幕上(x,y)点时,执行的操作 ////// core.prototype.onclick = function (x, y, stepPostfix) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; // console.log("Click: (" + x + "," + y + ")"); + stepPostfix=stepPostfix||[]; + // 非游戏屏幕内 if (x<0 || y<0 || x>12 || y>12) return; @@ -920,6 +1358,12 @@ core.prototype.onclick = function (x, y, stepPostfix) { return; } + // 查看地图 + if (core.status.event.id == 'viewMaps') { + core.events.clickViewMaps(x,y); + return; + } + // 开关 if (core.status.event.id == 'switchs') { core.events.clickSwitchs(x,y); @@ -962,6 +1406,11 @@ core.prototype.onclick = function (x, y, stepPostfix) { return; } + if (core.status.event.id == 'keyBoard') { + core.events.clickKeyBoard(x,y); + return; + } + // 关于 if (core.status.event.id == 'about') { core.events.clickAbout(x,y); @@ -985,10 +1434,26 @@ core.prototype.onclick = function (x, y, stepPostfix) { return; } + if (core.status.event.id == 'syncSelect') { + core.events.clickSyncSelect(x,y); + return; + } + + if (core.status.event.id == 'localSaveSelect') { + core.events.clickLocalSaveSelect(x,y); + return; + } + + if (core.status.event.id == 'cursor') { + core.events.clickCursor(x,y); + return; + } + } ////// 滑动鼠标滚轮时的操作 ////// core.prototype.onmousewheel = function (direct) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; // 向下滚动是 -1 ,向上是 1 // 楼层飞行器 @@ -1007,8 +1472,15 @@ core.prototype.onmousewheel = function (direct) { // 存读档 if (core.status.lockControl && (core.status.event.id == 'save' || core.status.event.id == 'load')) { - if (direct==1) core.ui.drawSLPanel(core.status.event.data - 6); - if (direct==-1) core.ui.drawSLPanel(core.status.event.data + 6); + if (direct==1) core.ui.drawSLPanel(core.status.event.data - 10); + if (direct==-1) core.ui.drawSLPanel(core.status.event.data + 10); + return; + } + + // 浏览地图 + if (core.status.lockControl && core.status.event.id == 'viewMaps') { + if (direct==1) core.ui.drawMaps(core.status.event.data+1); + if (direct==-1) core.ui.drawMaps(core.status.event.data-1); return; } } @@ -1031,30 +1503,35 @@ core.prototype.stopAutomaticRoute = function () { if (!core.status.played) { return; } - core.stopAutoHeroMove(); - core.status.automaticRouting = false; - core.status.automaticRouted = false; - core.status.autoStepRoutes = []; - core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []}; - if (core.status.moveStepBeforeStop.length==0) + core.status.automaticRoute.autoHeroMove = false; + core.status.automaticRoute.autoStep = 0; + core.status.automaticRoute.destStep = 0; + core.status.automaticRoute.movedStep = 0; + core.status.automaticRoute.autoStepRoutes = []; + core.status.automaticRoute.destX=null; + core.status.automaticRoute.destY=null; + core.stopHero(); + if (core.status.automaticRoute.moveStepBeforeStop.length==0) core.canvas.ui.clearRect(0, 0, 416, 416); } ////// 继续剩下的自动寻路操作 ////// core.prototype.continueAutomaticRoute = function () { // 此函数只应由events.afterOpenDoor和events.afterBattle调用 - var moveStep = core.status.moveStepBeforeStop; - core.status.moveStepBeforeStop = []; - if(moveStep.length===0)return; - if(moveStep.length===1 && moveStep[0].step===1)return; - core.status.automaticRouting = true; - core.setAutoHeroMove(moveStep); + var moveStep = core.status.automaticRoute.moveStepBeforeStop; + //core.status.automaticRoute.moveStepBeforeStop = []; + if(moveStep.length===0 || (moveStep.length===1 && moveStep[0].step===1)) { + core.status.automaticRoute.moveStepBeforeStop = []; + } + else { + core.setAutoHeroMove(moveStep); + } } ////// 清空剩下的自动寻路列表 ////// core.prototype.clearContinueAutomaticRoute = function () { core.canvas.ui.clearRect(0, 0, 416, 416); - core.status.moveStepBeforeStop=[]; + core.status.automaticRoute.moveStepBeforeStop=[]; } ////// 设置自动寻路路线 ////// @@ -1062,8 +1539,23 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { if (!core.status.played || core.status.lockControl) { return; } - else if (core.status.automaticRouting) { + // 正在寻路中 + if (core.status.automaticRoute.autoHeroMove) { + var lastX = core.status.automaticRoute.destX, lastY=core.status.automaticRoute.destY; core.stopAutomaticRoute(); + if (lastX==destX && lastY==destY) { + core.lockControl(); + setTimeout(function () { + core.unLockControl(); + if (core.canMoveDirectly(destX, destY)) { + core.clearMap('hero', 0, 0, 416, 416); + core.setHeroLoc('x', destX); + core.setHeroLoc('y', destY); + core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + core.status.route.push("move:"+destX+":"+destY); + } + }, 100); + } return; } if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y && stepPostfix.length==0) { @@ -1084,7 +1576,6 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { var step = 0; var tempStep = null; var moveStep; - core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []}; if (!(moveStep = core.automaticRoute(destX, destY))) { if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y){ moveStep=[]; @@ -1094,8 +1585,8 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { } } moveStep=moveStep.concat(stepPostfix); - core.status.automaticRoutingTemp.destX = destX; - core.status.automaticRoutingTemp.destY = destY; + core.status.automaticRoute.destX=destX; + core.status.automaticRoute.destY=destY; core.canvas.ui.save(); core.canvas.ui.clearRect(0, 0, 416, 416); core.canvas.ui.fillStyle = '#bfbfbf'; @@ -1110,12 +1601,14 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { step++; } else { - core.status.automaticRoutingTemp.moveStep.push({'direction': tempStep, 'step': step}); + //core.status.automaticRoutingTemp.moveStep.push({'direction': tempStep, 'step': step}); + core.status.automaticRoute.autoStepRoutes.push({'direction': tempStep, 'step': step}); step = 1; tempStep = moveStep[m].direction; } if (m == moveStep.length - 1) { - core.status.automaticRoutingTemp.moveStep.push({'direction': tempStep, 'step': step}); + // core.status.automaticRoutingTemp.moveStep.push({'direction': tempStep, 'step': step}); + core.status.automaticRoute.autoStepRoutes.push({'direction': tempStep, 'step': step}); core.canvas.ui.fillRect(moveStep[m].x * 32 + 10, moveStep[m].y * 32 + 10, 12, 12); } else { @@ -1163,13 +1656,9 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { } } core.canvas.ui.restore(); - core.status.automaticRouted = true; // 立刻移动 - core.status.automaticRouting = true; - // core.setAutoHeroMove(core.status.automaticRoutingTemp.moveStep); - core.setAutoHeroMove(core.status.automaticRoutingTemp.moveStep); - core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []}; + core.setAutoHeroMove(); } @@ -1201,15 +1690,21 @@ core.prototype.automaticRoute = function (destX, destY) { f=f%169; var nowX = parseInt(f / 13), nowY = f % 13; var nowIsArrow = false, nowId, nowBlock = core.getBlock(nowX,nowY); + /* if (nowBlock!=null){ nowId = nowBlock.block.event.id; nowIsArrow = nowId.slice(0, 5).toLowerCase() == 'arrow'; } + */ for (var direction in scan) { + /* if(nowIsArrow){ var nowArrow = nowId.slice(5).toLowerCase(); if (direction != nowArrow) continue; } + */ + if (!core.canMoveHero(nowX, nowY, direction)) + continue; var nx = nowX + scan[direction].x; var ny = nowY + scan[direction].y; @@ -1225,20 +1720,22 @@ core.prototype.automaticRoute = function (destX, destY) { var nextId, nextBlock = core.getBlock(nx,ny); if (nextBlock!=null){ nextId = nextBlock.block.event.id; + /* // 遇到单向箭头处理 var isArrow = nextId.slice(0, 5).toLowerCase() == 'arrow'; if(isArrow){ var nextArrow = nextId.slice(5).toLowerCase(); if ( (scan[direction].x + scan[nextArrow].x) == 0 && (scan[direction].y + scan[nextArrow].y) == 0 ) continue; } + */ // 绕过亮灯(因为只有一次通行机会很宝贵) if(nextId == "light") deepAdd=100; // 绕过路障 if (nextId.substring(nextId.length-3)=="Net") deepAdd=core.values.lavaDamage; // 绕过血瓶 if (!core.flags.potionWhileRouting && nextId.substring(nextId.length-6)=="Potion") deepAdd=20; - // 绕过可能的夹击点 - // if (nextBlock.block.event.trigger == 'checkBlock') deepAdd=200; + // 绕过传送点 + if (nextBlock.block.event.trigger == 'changeFloor') deepAdd = 10; } if (core.status.checkBlock.damage[nid]>0) deepAdd = core.status.checkBlock.damage[nid]; @@ -1277,6 +1774,7 @@ core.prototype.fillPosWithPoint = function (pos) { core.fillRect('ui', pos.x*32+12,pos.y*32+12,8,8, '#bfbfbf'); } +/* ////// 清除已经寻路过的部分 ////// core.prototype.clearStepPostfix = function () { if(core.status.mouseOutCheck >0){ @@ -1291,6 +1789,7 @@ core.prototype.clearStepPostfix = function () { core.canvas.ui.restore(); } } +*/ /////////// 寻路代码相关 END /////////// @@ -1298,53 +1797,27 @@ core.prototype.clearStepPostfix = function () { /////////// 自动行走 & 行走控制 /////////// -////// 停止勇士的自动行走 ////// -core.prototype.stopAutoHeroMove = function () { - core.status.autoHeroMove = false; - core.status.automaticRouting = false; - core.status.automaticRouted = false; - core.status.autoStep = 0; - core.status.destStep = 0; - core.status.movedStep = 0; - core.status.autoStepRoutes = []; - core.stopHero(); - clearInterval(core.interval.heroAutoMoveScan); -} - ////// 设置勇士的自动行走路线 ////// -core.prototype.setAutoHeroMove = function (steps, start) { +core.prototype.setAutoHeroMove = function (steps) { + steps=steps||core.status.automaticRoute.autoStepRoutes; if (steps.length == 0) { return; } - core.status.autoStepRoutes = steps; - core.status.autoStep = 0; - clearInterval(core.interval.heroAutoMoveScan); - core.interval.heroAutoMoveScan = window.setInterval(function () { - if (!core.status.autoHeroMove) { - if (core.status.autoStep == core.status.autoStepRoutes.length) { - core.stopAutoHeroMove(); - return; - } - core.autoHeroMove(core.status.autoStepRoutes[core.status.autoStep].direction, core.status.autoStepRoutes[core.status.autoStep].step); - core.status.autoStep++; - } - }, 80); -} - -////// 让勇士开始自动行走 ////// -core.prototype.autoHeroMove = function (direction, step) { - core.status.autoHeroMove = true; - core.status.destStep = step; - core.moveHero(direction); + core.status.automaticRoute.autoStepRoutes=steps; + core.status.automaticRoute.autoHeroMove = true; + core.status.automaticRoute.autoStep = 1; + core.status.automaticRoute.destStep = steps[0].step; + core.moveHero(steps[0].direction); } ////// 设置行走的效果动画 ////// core.prototype.setHeroMoveInterval = function (direction, x, y, callback) { - if (core.status.heroMoving) { + if (core.status.heroMoving>0) { return; } - core.status.heroMoving = true; - var moveStep = 0; + core.status.heroMoving=1; + // core.status.heroMoving = true; + // var moveStep = 0; var scan = { 'up': {'x': 0, 'y': -1}, 'left': {'x': -1, 'y': 0}, @@ -1352,107 +1825,135 @@ core.prototype.setHeroMoveInterval = function (direction, x, y, callback) { 'right': {'x': 1, 'y': 0} }; core.interval.heroMoveInterval = window.setInterval(function () { - moveStep++; + core.status.heroMoving++; + /* if (moveStep<=4) { core.drawHero(direction, x, y, 'leftFoot', 4*moveStep*scan[direction].x, 4*moveStep*scan[direction].y); } - else if (moveStep<=8) { + else if (moveStep<8) { core.drawHero(direction, x, y, 'rightFoot', 4*moveStep*scan[direction].x, 4*moveStep*scan[direction].y); } - if (moveStep==8) { + */ + if (core.status.heroMoving==8) { core.setHeroLoc('x', x+scan[direction].x); core.setHeroLoc('y', y+scan[direction].y); core.moveOneStep(); - if (core.status.heroStop) - core.drawHero(direction, core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + core.clearMap('hero', 0, 0, 416, 416); + core.drawHero(direction, core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + //if (core.status.heroStop) + // core.drawHero(direction, core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + clearInterval(core.interval.heroMoveInterval); + core.status.heroMoving = 0; if (core.isset(callback)) callback(); } - }, 12.5); + }, 12.5 / core.status.replay.speed); } -////// 设置勇士行走过程中对事件的触发检测 ////// -core.prototype.setHeroMoveTriggerInterval = function () { - var direction, x, y; +////// 实际每一步的行走过程 ////// +core.prototype.moveAction = function (callback) { + if (core.interval.openDoorAnimate!=null) return; // 开门判断 + if (core.status.heroMoving>0) return; var scan = { 'up': {'x': 0, 'y': -1}, 'left': {'x': -1, 'y': 0}, 'down': {'x': 0, 'y': 1}, 'right': {'x': 1, 'y': 0} }; - core.interval.heroMoveTriggerInterval = window.setInterval(function () { - if (!core.status.heroStop) { - direction = core.getHeroLoc('direction'); - x = core.getHeroLoc('x'); - y = core.getHeroLoc('y'); - var noPass = core.noPass(x + scan[direction].x, y + scan[direction].y), canMove = core.canMoveHero(); - if (noPass || !canMove) { - if (canMove) // 非箭头:触发 - core.trigger(x + scan[direction].x, y + scan[direction].y); - core.drawHero(direction, x, y, 'stop'); - if (core.status.autoHeroMove) { - core.status.movedStep++; - if (core.status.destStep == core.status.movedStep) { - core.status.autoHeroMove = false; - core.status.destStep = 0; - core.status.movedStep = 0; - core.status.moveStepBeforeStop=[]; + var direction = core.getHeroLoc('direction'); + var x = core.getHeroLoc('x'); + var y = core.getHeroLoc('y'); + var noPass = core.noPass(x + scan[direction].x, y + scan[direction].y), canMove = core.canMoveHero(); + if (noPass || !canMove) { + core.status.route.push(direction); + core.status.automaticRoute.moveStepBeforeStop = []; + if (canMove) // 非箭头:触发 + core.trigger(x + scan[direction].x, y + scan[direction].y); + core.drawHero(direction, x, y, 'stop'); + + // core.clearContinueAutomaticRoute(); + if (core.status.automaticRoute.moveStepBeforeStop.length==0) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + + /* + if (core.status.automaticRoute.autoHeroMove) { + + core.status.automaticRoute.movedStep++; + if (core.status.automaticRoute.destStep == core.status.automaticRoute.movedStep) { + core.status.automaticRoute.autoHeroMove = false; + core.status.automaticRoute.destStep = 0; + core.status.automaticRoute.movedStep = 0; + core.status.automaticRoute.moveStepBeforeStop=[]; + core.stopAutomaticRoute(); + } + } + else { + // core.status.heroStop = true; + core.stopHero(); + } + */ + if (core.isset(callback)) + callback(); + } + else { + core.setHeroMoveInterval(direction, x, y, function () { + if (core.status.automaticRoute.autoHeroMove) { + core.status.automaticRoute.movedStep++; + if (core.status.automaticRoute.destStep == core.status.automaticRoute.movedStep) { + if (core.status.automaticRoute.autoStep == core.status.automaticRoute.autoStepRoutes.length) { + core.clearContinueAutomaticRoute(); core.stopAutomaticRoute(); } - } - else { - core.status.heroStop = true; - } - return; - } - core.setHeroMoveInterval(direction, x, y, function () { - if (core.status.autoHeroMove) { - core.status.movedStep++; - if (core.status.destStep == core.status.movedStep) { - core.status.autoHeroMove = false; - core.status.destStep = 0; - core.status.movedStep = 0; - core.stopHero(); - core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + else { + core.status.automaticRoute.movedStep = 0; + core.status.automaticRoute.destStep = core.status.automaticRoute.autoStepRoutes[core.status.automaticRoute.autoStep].step; + core.setHeroLoc('direction', core.status.automaticRoute.autoStepRoutes[core.status.automaticRoute.autoStep].direction); + core.status.automaticRoute.autoStep++; } } - else if (core.status.heroStop) { - core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); - } - core.trigger(core.getHeroLoc('x'), core.getHeroLoc('y')); - clearInterval(core.interval.heroMoveInterval); - core.status.heroMoving = false; - core.checkBlock(); - }); - } - }, 50); + } + else if (core.status.heroStop) { + core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + } + core.status.route.push(direction); + core.trigger(core.getHeroLoc('x'), core.getHeroLoc('y')); + core.checkBlock(); + if (core.isset(callback)) callback(); + }); + } } -////// 设置勇士的方向(转向) ////// -core.prototype.turnHero = function(direction) { - if (core.isset(direction)) { - core.status.hero.loc.direction = direction; - } - else if (core.status.hero.loc.direction == 'up') core.status.hero.loc.direction = 'right'; +////// 转向 ////// +core.prototype.turnHero = function() { + if (core.status.hero.loc.direction == 'up') core.status.hero.loc.direction = 'right'; else if (core.status.hero.loc.direction == 'right') core.status.hero.loc.direction = 'down'; else if (core.status.hero.loc.direction == 'down') core.status.hero.loc.direction = 'left'; else if (core.status.hero.loc.direction == 'left') core.status.hero.loc.direction = 'up'; core.drawHero(core.status.hero.loc.direction, core.status.hero.loc.x, core.status.hero.loc.y, 'stop', 0, 0); - core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []}; core.canvas.ui.clearRect(0, 0, 416, 416); + core.status.route.push("turn"); } ////// 勇士能否前往某方向 ////// -core.prototype.canMoveHero = function() { - var direction = core.getHeroLoc('direction'); - var nowBlock = core.getBlock(core.getHeroLoc('x'),core.getHeroLoc('y')); +core.prototype.canMoveHero = function(x,y,direction,floorId) { + if (!core.isset(x)) x=core.getHeroLoc('x'); + if (!core.isset(y)) y=core.getHeroLoc('y'); + if (!core.isset(direction)) direction=core.getHeroLoc('direction'); + if (!core.isset(floorId)) floorId=core.status.floorId; + + // 检查当前块的cannotMove + var cannotMove = core.floors[floorId].cannotMove[x+","+y]; + if (core.isset(cannotMove) && cannotMove instanceof Array && cannotMove.indexOf(direction)>=0) + return false; + + var nowBlock = core.getBlock(x,y,floorId); if (nowBlock!=null){ nowId = nowBlock.block.event.id; var nowIsArrow = nowId.slice(0, 5).toLowerCase() == 'arrow'; if(nowIsArrow){ var nowArrow = nowId.slice(5).toLowerCase(); if (direction != nowArrow) { - // core.status.heroStop = true; - // core.turnHero(direction); return false; } } @@ -1463,7 +1964,7 @@ core.prototype.canMoveHero = function() { 'down': {'x': 0, 'y': 1}, 'right': {'x': 1, 'y': 0} }; - var nextBlock = core.getBlock(core.nextX(),core.nextY()); + var nextBlock = core.getBlock(x+scan[direction].x,y+scan[direction].y); if (nextBlock!=null){ nextId = nextBlock.block.event.id; // 遇到单向箭头处理 @@ -1471,8 +1972,6 @@ core.prototype.canMoveHero = function() { if(isArrow){ var nextArrow = nextId.slice(5).toLowerCase(); if ( (scan[direction].x + scan[nextArrow].x) == 0 && (scan[direction].y + scan[nextArrow].y) == 0 ) { - // core.status.heroStop = true; - // core.turnHero(direction); return false; } } @@ -1480,10 +1979,61 @@ core.prototype.canMoveHero = function() { return true; } +////// 能否瞬间移动 ////// +core.prototype.canMoveDirectly = function (destX,destY) { + if (!core.flags.enableMoveDirectly) return false; + + // 中毒状态:不能 + if (core.hasFlag('poison')) return false; + + var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); + if (fromX==destX&&fromY==destY) return false; + + // BFS + var visited=[], queue=[]; + visited[13*fromX+fromY]=true; + queue.push(13*fromX+fromY); + + var directions = [[-1,0],[1,0],[0,1],[0,-1]]; + while (queue.length>0) { + var now=queue.shift(), nowX=parseInt(now/13), nowY=now%13; + + for (var dir in directions) { + var nx=nowX+directions[dir][0], ny=nowY+directions[dir][1]; + if (nx<0||nx>=13||ny<0||ny>=13||visited[13*nx+ny]||core.getBlock(nx,ny)!=null||core.status.checkBlock.damage[13*nx+ny]>0) continue; + if (nx==destX&&ny==destY) return true; + visited[13*nx+ny]=true; + queue.push(13*nx+ny); + } + } + return false; +} + ////// 让勇士开始移动 ////// -core.prototype.moveHero = function (direction) { - core.setHeroLoc('direction', direction); - core.status.heroStop = false; +core.prototype.moveHero = function (direction, callback) { + // 如果正在移动,直接return + if (core.status.heroMoving>0) return; + if (core.isset(direction)) + core.setHeroLoc('direction', direction); + if (!core.isset(callback)) { // 如果不存在回调函数,则使用heroMoveTrigger + core.status.heroStop = false; + if (core.interval.heroMoveTriggerInterval==null) { + core.moveAction(); + core.interval.heroMoveTriggerInterval = setInterval(function () { + if (!core.status.heroStop) { + core.moveAction(); + } + else { + core.stopHero(); + } + }, 50) + } + } + else { // 否则,只向某个方向移动一步,然后调用callback + core.moveAction(function () { + callback(); + }) + } } /////// 使用事件让勇士移动。这个函数将不会触发任何事件 ////// @@ -1520,11 +2070,14 @@ core.prototype.eventMoveHero = function(steps, time, callback) { 'right': {'x': 1, 'y': 0} }; + core.status.replay.animate=true; + var animate=window.setInterval(function() { var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'); if (moveSteps.length==0) { clearInterval(animate); core.drawHero(core.getHeroLoc('direction'), x, y, 'stop'); + core.status.replay.animate=false; if (core.isset(callback)) callback(); } else { @@ -1544,11 +2097,12 @@ core.prototype.eventMoveHero = function(steps, time, callback) { moveSteps.shift(); } } - }, time/8); + }, time / 8 / core.status.replay.speed) } ////// 每移动一格后执行的事件 ////// core.prototype.moveOneStep = function() { + core.status.hero.steps++; // 中毒状态 if (core.hasFlag('poison')) { core.status.hero.hp -= core.values.poisonDamage; @@ -1567,8 +2121,10 @@ core.prototype.waitHeroToStop = function(callback) { core.stopAutomaticRoute(); core.clearContinueAutomaticRoute(); if (core.isset(callback)) { + core.status.replay.animate=true; core.lockControl(); setTimeout(function(){ + core.status.replay.animate=false; core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); callback(); }, 30); @@ -1578,13 +2134,16 @@ core.prototype.waitHeroToStop = function(callback) { ////// 停止勇士的移动状态 ////// core.prototype.stopHero = function () { core.status.heroStop = true; + clearInterval(core.interval.heroMoveTriggerInterval); + core.interval.heroMoveTriggerInterval=null; } ////// 绘制勇士 ////// core.prototype.drawHero = function (direction, x, y, status, offsetX, offsetY) { offsetX = offsetX || 0; offsetY = offsetY || 0; - core.clearAutomaticRouteNode(x, y); + var dx=offsetX==0?0:offsetX/Math.abs(offsetX), dy=offsetY==0?0:offsetY/Math.abs(offsetY); + core.clearAutomaticRouteNode(x+dx, y+dy); var heroIcon = core.material.icons.hero[direction]; x = x * 32; y = y * 32; @@ -1650,11 +2209,11 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) { if (core.isset(callback)) callback(); return; } - if (core.status.moveStepBeforeStop.length==0) { - core.status.moveStepBeforeStop=core.status.autoStepRoutes.slice(core.status.autoStep-1,core.status.autoStepRoutes.length); - if (core.status.moveStepBeforeStop.length>=1)core.status.moveStepBeforeStop[0].step-=core.status.movedStep; + if (core.status.automaticRoute.moveStepBeforeStop.length==0) { + core.status.automaticRoute.moveStepBeforeStop=core.status.automaticRoute.autoStepRoutes.slice(core.status.automaticRoute.autoStep-1,core.status.automaticRoute.autoStepRoutes.length); + if (core.status.automaticRoute.moveStepBeforeStop.length>=1)core.status.automaticRoute.moveStepBeforeStop[0].step-=core.status.automaticRoute.movedStep; } - core.stopHero(); + core.stopAutomaticRoute(); var speed=30; if (needKey) { @@ -1664,10 +2223,13 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) { core.drawTip("你没有" + core.material.items[key].name); else core.drawTip("无法开启此门"); core.clearContinueAutomaticRoute(); - if (core.isset(callback)) callback(); return; } } + + if (!core.isset(core.status.event.id)) // 自动存档 + core.autosave(true); + // open core.playSound("door.ogg"); var state = 0; @@ -1677,12 +2239,14 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) { speed=100; } var door = core.material.icons.animates[doorId]; + core.status.replay.animate=true; core.interval.openDoorAnimate = window.setInterval(function () { state++; if (state == 4) { clearInterval(core.interval.openDoorAnimate); core.interval.openDoorAnimate=null; core.removeBlock(x, y); + core.status.replay.animate=false; core.events.afterOpenDoor(id,x,y,callback); return; } @@ -1693,9 +2257,9 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) { ////// 战斗 ////// core.prototype.battle = function (id, x, y, force, callback) { - if (core.status.moveStepBeforeStop.length==0) { - core.status.moveStepBeforeStop=core.status.autoStepRoutes.slice(core.status.autoStep-1,core.status.autoStepRoutes.length); - if (core.status.moveStepBeforeStop.length>=1)core.status.moveStepBeforeStop[0].step-=core.status.movedStep; + if (core.status.automaticRoute.moveStepBeforeStop.length==0) { + core.status.automaticRoute.moveStepBeforeStop=core.status.automaticRoute.autoStepRoutes.slice(core.status.automaticRoute.autoStep-1,core.status.automaticRoute.autoStepRoutes.length); + if (core.status.automaticRoute.moveStepBeforeStop.length>=1)core.status.automaticRoute.moveStepBeforeStop[0].step-=core.status.automaticRoute.movedStep; } core.stopHero(); core.stopAutomaticRoute(); @@ -1707,7 +2271,11 @@ core.prototype.battle = function (id, x, y, force, callback) { core.clearContinueAutomaticRoute(); return; } - if (core.flags.battleAnimate) { + + if (!core.isset(core.status.event.id)) // 自动存档 + core.autosave(true); + + if (core.flags.battleAnimate&&!core.status.replay.replaying) { core.waitHeroToStop(function() { core.ui.drawBattleAnimate(id, function() { core.afterBattle(id, x, y, callback); @@ -1715,7 +2283,16 @@ core.prototype.battle = function (id, x, y, force, callback) { }); } else { - core.playSound('attack.ogg'); + + if (core.flags.equipment && core.getFlag('sword', 'sword0')!='sword0') { + core.playSound('zone.ogg'); + core.drawAnimate('sword', x, y); + } + else { + core.playSound('attack.ogg'); + core.drawAnimate('hand', x, y); + } + core.afterBattle(id, x, y, callback); } } @@ -1745,7 +2322,7 @@ core.prototype.afterBattle = function(id, x, y, callback) { if (core.flags.enableMoney) hint += ",金币+" + money; if (core.flags.enableExperience) - hint += ",经验+" + core.material.enemys[id].experience; + hint += ",经验+" + experience; core.drawTip(hint); // 打完怪物,触发事件 @@ -1765,12 +2342,25 @@ core.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=='changeFloor' && (core.status.autoHeroMove || core.status.autoStep= core.values.animateSpeed * 2 / animateValue) { animateCurrent++; animateTime = 0; @@ -2294,14 +2931,15 @@ core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { if (moveSteps.length==0) { if (immediateHide) opacityVal=0; else opacityVal -= 0.06; - core.setOpacity('data', opacityVal); - core.clearMap('data', nowX, nowY, 32, 32); - core.canvas.data.drawImage(blockImage, animateCurrent * 32, blockIcon * 32, 32, 32, nowX, nowY, 32, 32); + core.setOpacity('animate', opacityVal); + core.clearMap('animate', nowX, nowY, 32, 32); + core.canvas.animate.drawImage(blockImage, animateCurrent * 32, blockIcon * 32, 32, 32, nowX, nowY, 32, 32); if (opacityVal<=0) { clearInterval(animate); - core.loadCanvas('data'); - core.clearMap('data', 0, 0, 416, 416); - core.setOpacity('data', 1); + core.loadCanvas('animate'); + core.clearMap('animate', 0, 0, 416, 416); + core.setOpacity('animate', 1); + core.status.replay.animate=false; if (core.isset(callback)) callback(); } } @@ -2310,25 +2948,26 @@ core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { step++; nowX+=scan[moveSteps[0]].x*2; nowY+=scan[moveSteps[0]].y*2; - core.clearMap('data', nowX-32, nowY-32, 96, 96); + core.clearMap('animate', nowX-32, nowY-32, 96, 96); // 绘制 - core.canvas.data.drawImage(blockImage, animateCurrent * 32, blockIcon * 32, 32, 32, nowX, nowY, 32, 32); + core.canvas.animate.drawImage(blockImage, animateCurrent * 32, blockIcon * 32, 32, 32, nowX, nowY, 32, 32); if (step==16) { // 该移动完毕,继续 step=0; moveSteps.shift(); } } - }, time/16); + }, time / 16 / core.status.replay.speed); } ////// 显示/隐藏某个块时的动画效果 ////// core.prototype.animateBlock = function (x,y,type,time,callback) { if (type!='hide') type='show'; + core.status.replay.animate=true; - clearInterval(core.interval.tipAnimate); - core.saveCanvas('data'); - core.clearMap('data', 0, 0, 416, 416); + //clearInterval(core.interval.tipAnimate); + core.saveCanvas('animate'); + core.clearMap('animate', 0, 0, 416, 416); var block = core.getBlock(x,y,core.status.floorId,false); if (block==null) {// 不存在 @@ -2336,8 +2975,8 @@ core.prototype.animateBlock = function (x,y,type,time,callback) { return; } // 清空UI - core.clearMap('ui', 0, 0, 416, 416); - core.setAlpha('ui', 1.0); + //core.clearMap('ui', 0, 0, 416, 416); + //core.setAlpha('ui', 1.0); block=block.block; blockIcon = core.material.icons[block.event.cls][block.event.id]; @@ -2346,23 +2985,24 @@ core.prototype.animateBlock = function (x,y,type,time,callback) { var opacityVal = 0; if (type=='hide') opacityVal=1; - core.setOpacity('data', opacityVal); - core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + core.setOpacity('animate', opacityVal); + core.canvas.animate.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); var animate = window.setInterval(function () { if (type=='show') opacityVal += 0.1; else opacityVal -= 0.1; - core.setOpacity('data', opacityVal); - core.clearMap('data',block.x * 32, block.y * 32, 32, 32); - core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + core.setOpacity('animate', opacityVal); + core.clearMap('animate',block.x * 32, block.y * 32, 32, 32); + core.canvas.animate.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); if (opacityVal >=1 || opacityVal<=0) { clearInterval(animate); - core.loadCanvas('data'); - core.clearMap('data', 0, 0, 416, 416); - core.setOpacity('data', 1); + core.loadCanvas('animate'); + core.clearMap('animate', 0, 0, 416, 416); + core.setOpacity('animate', 1); + core.status.replay.animate=false; if (core.isset(callback)) callback(); } - }, time/10); + }, time / 10 / core.status.replay.speed); } ////// 将某个块从禁用变成启用状态 ////// @@ -2380,8 +3020,10 @@ core.prototype.showBlock = function(x, y, floodId) { blockImage = core.material.images[block.event.cls]; core.canvas.event.drawImage(core.material.images[block.event.cls], 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); core.addGlobalAnimate(block.event.animate, block.x * 32, block.y * 32, blockIcon, blockImage); - core.setGlobalAnimate(core.values.animateSpeed); + // core.setGlobalAnimate(core.values.animateSpeed); + core.syncGlobalAnimate(); } + core.updateStatusBar(); } } @@ -2480,6 +3122,7 @@ core.prototype.removeGlobalAnimate = function (x, y, all) { ////// 设置全局动画的显示效果 ////// core.prototype.setGlobalAnimate = function (speed) { if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; + /* clearInterval(core.interval.twoAnimate); clearInterval(core.interval.fourAnimate); var animateClose = false; @@ -2505,6 +3148,10 @@ core.prototype.setGlobalAnimate = function (speed) { animateClose = false; } }, speed / 2); + */ + core.syncGlobalAnimate(); + core.animateFrame.speed = speed; + core.animateFrame.globalAnimate = true; } ////// 同步所有的全局动画效果 ////// @@ -2517,30 +3164,86 @@ core.prototype.syncGlobalAnimate = function () { }) } -////// 显示UI层某个box的动画 ////// -core.prototype.setBoxAnimate = function () { - clearInterval(core.interval.boxAnimate); - if (core.status.boxAnimateObjs.length > 0) { - var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); - core.drawBoxAnimate(background); - core.interval.boxAnimate = setInterval(function () { - core.drawBoxAnimate(background); - }, core.values.animateSpeed); - } -} - ////// 绘制UI层的box动画 ////// -core.prototype.drawBoxAnimate = function (background) { +core.prototype.drawBoxAnimate = function () { for (var a = 0; a < core.status.boxAnimateObjs.length; a++) { var obj = core.status.boxAnimateObjs[a]; - obj.status = obj.status == 0 ? 1 : 0; + obj.status = ((obj.status||0)+1)%2; core.clearMap('ui', obj.bgx, obj.bgy, obj.bgsize, obj.bgsize); - core.fillRect('ui', obj.bgx, obj.bgy, obj.bgsize, obj.bgsize, background); + core.fillRect('ui', obj.bgx, obj.bgy, obj.bgsize, obj.bgsize, core.animateFrame.background); core.canvas.ui.drawImage(obj.image, obj.status * 32, obj.icon * 32, 32, 32, obj.x, obj.y, 32, 32); } } +////// 绘制动画 ////// +core.prototype.drawAnimate = function (name, x, y, callback) { + + // 正在播放录像:不显示动画 + if (core.isset(core.status.replay) && core.status.replay.replaying) { + if (core.isset(callback)) callback(); + return; + } + + // 检测动画是否存在 + if (!core.isset(core.material.animates[name]) || !core.isset(x) || !core.isset(y)) { + if (core.isset(callback)) callback(); + return; + } + + // 清空animate层 + clearInterval(core.interval.animateInterval); + core.clearMap('animate', 0, 0, 416, 416); + + // 开始绘制 + var animate = core.material.animates[name]; + var ratio = animate.ratio; + var centerX = 32*x+16, centerY = 32*y+16; + var index=0; + + var draw = function (index) { + core.clearMap('animate', 0, 0, 416, 416); + + var frame = animate.frames[index]; + frame.forEach(function (t) { + var image = animate.images[t.index]; + if (!core.isset(image)) return; + var realWidth = image.width * ratio * t.zoom / 100; + var realHeight = image.height * ratio * t.zoom / 100; + core.setAlpha('animate', t.opacity / 255); + + var cx = centerX+t.x, cy=centerY+t.y; + + if (!t.mirror && !t.angle) { + core.canvas.animate.drawImage(image, cx-realWidth/2, cy-realHeight/2, realWidth, realHeight); + } + else { + core.saveCanvas('animate'); + core.canvas.animate.translate(cx,cy); + if (t.angle) + core.canvas.animate.rotate(-t.angle*Math.PI/180); + if (t.mirror) + core.canvas.animate.scale(-1,1); + core.canvas.animate.drawImage(image, -realWidth/2, -realHeight/2, realWidth, realHeight); + core.loadCanvas('animate'); + } + }) + } + + draw(index++); + + core.interval.animateInterval = setInterval(function (t) { + if (index == animate.frames.length) { + clearInterval(core.interval.animateInterval); + core.clearMap('animate', 0, 0, 416, 416); + core.setAlpha('animate', 1); + if (core.isset(callback)) callback(); + return; + } + draw(index++); + }, 50); +} + ////// 更新领域、夹击、阻击的伤害地图 ////// core.prototype.updateCheckBlock = function() { core.status.checkBlock = {}; @@ -2554,7 +3257,7 @@ core.prototype.updateCheckBlock = function() { if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable) && block.event.cls=='enemys') { var id = block.event.id, enemy = core.enemys.getEnemys(id); if (core.isset(enemy)) { - core.status.checkBlock.map[13*block.x+block.y]=id; + core.status.checkBlock.map[13*block.x+block.y]=id; } } } @@ -2571,7 +3274,7 @@ core.prototype.updateCheckBlock = function() { // 存在领域 if (core.enemys.hasSpecial(enemy.special, 15)) { var range = enemy.range || 1; - var zoneSquare = core.flags.zoneSquare; + var zoneSquare = false; if (core.isset(enemy.zoneSquare)) zoneSquare=enemy.zoneSquare; for (var dx=-range;dx<=range;dx++) { for (var dy=-range;dy<=range;dy++) { @@ -2629,7 +3332,7 @@ core.prototype.updateCheckBlock = function() { core.status.checkBlock.betweenAttack[13*x+y]=true; var leftHp = core.status.hero.hp - core.status.checkBlock.damage[13*x+y]; if (leftHp>1) - core.status.checkBlock.damage[13*x+y] += parseInt((leftHp+1)/2); + core.status.checkBlock.damage[13*x+y] += parseInt((leftHp+(core.flags.betweenAttackCeil?0:1))/2); } } } @@ -2672,6 +3375,10 @@ core.prototype.checkBlock = function () { else if (damage>0) { core.drawTip('受到领域伤害'+damage+'点'); } + + core.playSound('zone.ogg'); + core.drawAnimate("zone", x, y); + if (core.status.hero.hp<=0) { core.status.hero.hp=0; core.updateStatusBar(); @@ -2692,107 +3399,172 @@ core.prototype.checkBlock = function () { ////// 阻击事件(动画效果) ////// core.prototype.snipe = function (snipes) { - core.waitHeroToStop(function() { - core.lockControl(); - var scan = { - 'up': {'x': 0, 'y': -1}, - 'left': {'x': -1, 'y': 0}, - 'down': {'x': 0, 'y': 1}, - 'right': {'x': 1, 'y': 0} - }; + var scan = { + 'up': {'x': 0, 'y': -1}, + 'left': {'x': -1, 'y': 0}, + 'down': {'x': 0, 'y': 1}, + 'right': {'x': 1, 'y': 0} + }; - snipes.forEach(function (snipe) { - var x=snipe.x, y=snipe.y, direction = snipe.direction; - snipe.nx = x+scan[snipe.direction].x; - snipe.ny = y+scan[snipe.direction].y; + snipes.forEach(function (snipe) { + var x=snipe.x, y=snipe.y, direction = snipe.direction; + snipe.nx = x+scan[snipe.direction].x; + snipe.ny = y+scan[snipe.direction].y; - core.removeGlobalAnimate(x, y); + core.removeGlobalAnimate(x, y); - var block = core.getBlock(x,y).block; + var block = core.getBlock(x,y).block; - snipe.blockIcon = core.material.icons[block.event.cls][block.event.id]; - snipe.blockImage = core.material.images[block.event.cls]; - var damage = core.enemys.getDamage(block.event.id); + snipe.blockIcon = core.material.icons[block.event.cls][block.event.id]; + snipe.blockImage = core.material.images[block.event.cls]; + var damage = core.enemys.getDamage(block.event.id); - var color = "#000000"; - if (damage <= 0) color = '#00FF00'; - else if (damage < core.status.hero.hp / 3) color = '#FFFFFF'; - else if (damage < core.status.hero.hp * 2 / 3) color = '#FFFF00'; - else if (damage < core.status.hero.hp) color = '#FF7F00'; - else color = '#FF0000'; + var color = "#000000"; + if (damage <= 0) color = '#00FF00'; + else if (damage < core.status.hero.hp / 3) color = '#FFFFFF'; + else if (damage < core.status.hero.hp * 2 / 3) color = '#FFFF00'; + else if (damage < core.status.hero.hp) color = '#FF7F00'; + else color = '#FF0000'; - if (damage >= 999999999) damage = "???"; - else if (damage > 100000) damage = (damage / 10000).toFixed(1) + "w"; + if (damage >= 999999999) damage = "???"; + else if (damage > 100000) damage = (damage / 10000).toFixed(1) + "w"; - snipe.damage = damage; - snipe.color = color; - snipe.block = core.clone(block); - }) + snipe.damage = damage; + snipe.color = color; + snipe.block = core.clone(block); + }) - var time = 500, step = 0; + var finishSnipe = function () { + snipes.forEach(function (t) { + core.removeBlock(t.x, t.y); + var nBlock = core.clone(t.block); + nBlock.x = t.nx; nBlock.y = t.ny; + core.status.thisMap.blocks.push(nBlock); + core.addGlobalAnimate(2, 32*t.nx, 32*t.ny, t.blockIcon, t.blockImage); + core.canvas.event.drawImage(t.blockImage, 0, t.blockIcon*32, 32, 32, 32*t.nx, 32*t.ny, 32, 32); + }); + core.syncGlobalAnimate(); + core.updateStatusBar(); + return; + } - var animateValue = 2; - var animateCurrent = 0; - var animateTime = 0; + if (core.status.replay.replaying) { + finishSnipe(); + } + else { + core.waitHeroToStop(function() { - core.canvas.fg.textAlign = 'left'; + core.lockControl(); - var animate=window.setInterval(function() { + var time = 500, step = 0; - step++; - animateTime += time / 16; - if (animateTime >= core.values.animateSpeed * 2 / animateValue) { - animateCurrent++; - animateTime = 0; - if (animateCurrent>=animateValue) animateCurrent=0; - } + var animateValue = 2; + var animateCurrent = 0; + var animateTime = 0; - snipes.forEach(function (snipe) { - var x=snipe.x, y=snipe.y, direction = snipe.direction; + core.canvas.fg.textAlign = 'left'; - var nowX=32*x+scan[direction].x*2*step, nowY=32*y+scan[direction].y*2*step; + var animate=window.setInterval(function() { - // 清空上一次 - core.clearMap('event', nowX-2*scan[direction].x, nowY-2*scan[direction].y, 32, 32); - core.clearMap('fg', nowX-2*scan[direction].x, nowY-2*scan[direction].y, 32, 32); - - core.canvas.event.drawImage(snipe.blockImage, animateCurrent*32, snipe.blockIcon*32, 32, 32, nowX, nowY, 32, 32); - - if (core.hasItem('book')) { - // drawFG - core.setFillStyle('fg', '#000000'); - core.canvas.fg.fillText(snipe.damage, nowX + 2, nowY + 30); - core.canvas.fg.fillText(snipe.damage, nowX, nowY + 30); - core.canvas.fg.fillText(snipe.damage, nowX + 2, nowY + 32); - core.canvas.fg.fillText(snipe.damage, nowX, nowY + 32); - - core.setFillStyle('fg', snipe.color); - core.canvas.fg.fillText(snipe.damage, nowX + 1, nowY + 31); + step++; + animateTime += time / 16; + if (animateTime >= core.values.animateSpeed * 2 / animateValue) { + animateCurrent++; + animateTime = 0; + if (animateCurrent>=animateValue) animateCurrent=0; } + snipes.forEach(function (snipe) { + var x=snipe.x, y=snipe.y, direction = snipe.direction; + + var nowX=32*x+scan[direction].x*2*step, nowY=32*y+scan[direction].y*2*step; + + // 清空上一次 + core.clearMap('event', nowX-2*scan[direction].x, nowY-2*scan[direction].y, 32, 32); + core.clearMap('fg', nowX-2*scan[direction].x, nowY-2*scan[direction].y, 32, 32); + + core.canvas.event.drawImage(snipe.blockImage, animateCurrent*32, snipe.blockIcon*32, 32, 32, nowX, nowY, 32, 32); + + if (core.hasItem('book')) { + // drawFG + core.setFillStyle('fg', '#000000'); + core.canvas.fg.fillText(snipe.damage, nowX + 2, nowY + 30); + core.canvas.fg.fillText(snipe.damage, nowX, nowY + 30); + core.canvas.fg.fillText(snipe.damage, nowX + 2, nowY + 32); + core.canvas.fg.fillText(snipe.damage, nowX, nowY + 32); + + core.setFillStyle('fg', snipe.color); + core.canvas.fg.fillText(snipe.damage, nowX + 1, nowY + 31); + } + + }) + + if (step==16) { // 移动完毕 + clearInterval(animate); + finishSnipe(); + // 不存在自定义事件 + if (core.status.event.id==null) + core.unLockControl(); + } + }, time/16); + }); + } + + +} + +////// 更改天气效果 ////// +core.prototype.setWeather = function (type, level) { + + // 非雨雪 + if (type!='rain' && type!='snow') { + core.clearMap('weather', 0, 0, 416, 416) + core.animateFrame.weather.type = null; + core.animateFrame.weather.level = 0; + core.animateFrame.weather.nodes = []; + return; + } + + level = parseInt(level); + + // 当前天气:则忽略 + if (type==core.animateFrame.weather.type && + (!core.isset(level) || 20*level==core.animateFrame.weather.level)) { + return; + } + + if (!core.isset(level)) level=5; + if (level<1) level=1; if (level>10) level=10; + level *= 20; + + core.clearMap('weather', 0, 0, 416, 416) + core.animateFrame.weather.type = type; + core.animateFrame.weather.level = level; + + core.animateFrame.weather.nodes = []; + + if (type == 'rain') { + for (var a=0;a1) color[3]=1; - if (time==0) { // 直接变色 - var nowR = parseInt(color[0]), nowG = parseInt(color[1]), nowB = parseInt(color[2]); - var toRGB = "#"+((1<<24)+(nowR<<16)+(nowG<<8)+nowB).toString(16).slice(1); - core.dom.curtain.style.background = toRGB; + core.dom.curtain.style.background = core.arrayToRGB(color); core.dom.curtain.style.opacity = color[3]; core.status.curtainColor = color; if (core.isset(callback)) callback(); @@ -2826,6 +3595,7 @@ core.prototype.setFg = function(color, time, callback) { } var step=0; + core.status.replay.animate=true; var changeAnimate = setInterval(function() { step++; @@ -2833,17 +3603,13 @@ core.prototype.setFg = function(color, time, callback) { var nowR = parseInt(fromColor[0]+(color[0]-fromColor[0])*step/25); var nowG = parseInt(fromColor[1]+(color[1]-fromColor[1])*step/25); var nowB = parseInt(fromColor[2]+(color[2]-fromColor[2])*step/25); - if (nowR<0) nowR=0; if (nowR>255) nowR=255; - if (nowG<0) nowG=0; if (nowG>255) nowG=255; - if (nowB<0) nowB=0; if (nowB>255) nowB=255; - - var toRGB = "#"+((1<<24)+(nowR<<16)+(nowG<<8)+nowB).toString(16).slice(1); - core.dom.curtain.style.background = toRGB; + core.dom.curtain.style.background = core.arrayToRGB([nowR,nowG,nowB]); core.dom.curtain.style.opacity = nowAlpha; if (step>=25) { clearInterval(changeAnimate); core.status.curtainColor = color; + core.status.replay.animate=false; if (core.isset(callback)) callback(); } }, time/25); @@ -2963,8 +3729,8 @@ core.prototype.removeItem = function (itemId) { } ////// 使用某个物品 ////// -core.prototype.useItem = function (itemId) { - core.items.useItem(itemId); +core.prototype.useItem = function (itemId, callback) { + core.items.useItem(itemId, callback); return; } @@ -2996,6 +3762,7 @@ core.prototype.getNextItem = function() { if (block==null) return; if (block.block.event.trigger=='getItem') { core.getItem(block.block.event.id, 1, nextX, nextY); + core.status.route.push("getNext"); } } @@ -3080,6 +3847,13 @@ core.prototype.drawTip = function (text, itemIcon) { ////// 地图中间绘制一段文字 ////// core.prototype.drawText = function (contents, callback) { if (core.isset(contents)) { + + // 合并 + if (core.isset(core.status.event)&&core.status.event.id=='action') { + core.insertAction(contents,null,null,callback); + return; + } + if (typeof contents == 'string') { contents = [{'content': contents}]; } @@ -3259,11 +4033,26 @@ core.prototype.formatDate = function(date) { +core.setTwoDigits(date.getHours())+":"+core.setTwoDigits(date.getMinutes())+":"+core.setTwoDigits(date.getSeconds()); } +////// 格式化时间为最简字符串 ////// +core.prototype.formatDate2 = function (date) { + if (!core.isset(date)) return ""; + return date.getFullYear()+core.setTwoDigits(date.getMonth()+1)+core.setTwoDigits(date.getDate()) + +core.setTwoDigits(date.getHours())+core.setTwoDigits(date.getMinutes())+core.setTwoDigits(date.getSeconds()); +} + ////// 两位数显示 ////// core.prototype.setTwoDigits = function (x) { return parseInt(x)<10?"0"+x:x; } +////// 数组转RGB ////// +core.prototype.arrayToRGB = function (color) { + var nowR = parseInt(color[0])||0, nowG = parseInt(color[1])||0, nowB = parseInt(color[2])||0; + if (nowR<0) nowR=0; if (nowB<0) nowB=0;if (nowG<0) nowG=0; + if (nowR>255) nowR=255; if (nowB>255) nowB=255; if (nowG>255) nowG=255; + return "#"+((1<<24)+(nowR<<16)+(nowG<<8)+nowB).toString(16).slice(1); +} + ////// 作弊 ////// core.prototype.debug = function() { core.setStatus('hp', 999999); @@ -3284,6 +4073,196 @@ core.prototype.debug = function() { core.drawTip("作弊成功"); } +////// 开始播放 ////// +core.prototype.startReplay = function (list) { + core.status.replay.replaying=true; + core.status.replay.pausing=false; + core.status.replay.speed=1.0; + core.status.replay.toReplay = core.clone(list); + core.status.replay.totalList = core.clone(list); + core.updateStatusBar(); + core.drawTip("开始播放"); + this.replay(); + return; +} + +////// 更改播放状态 ////// +core.prototype.triggerReplay = function () { + if (core.status.replay.pausing) this.resumeReplay(); + else this.pauseReplay(); +} + +////// 暂停播放 ////// +core.prototype.pauseReplay = function () { + if (!core.status.replay.replaying) return; + core.status.replay.pausing = true; + core.updateStatusBar(); + core.drawTip("暂停播放"); +} + +////// 恢复播放 ////// +core.prototype.resumeReplay = function () { + if (!core.status.replay.replaying) return; + core.status.replay.pausing = false; + core.updateStatusBar(); + core.drawTip("恢复播放"); + core.replay(); +} + +////// 加速播放 ////// +core.prototype.forwardReplay = function () { + if (!core.status.replay.replaying) return; + core.status.replay.speed = parseInt(10*core.status.replay.speed + 1)/10; + if (core.status.replay.speed>2.5) core.status.replay.speed=2.5; + core.drawTip("x"+core.status.replay.speed+"倍"); +} + +////// 减速播放 ////// +core.prototype.rewindReplay = function () { + if (!core.status.replay.replaying) return; + core.status.replay.speed = parseInt(10*core.status.replay.speed - 1)/10; + if (core.status.replay.speed<0.3) core.status.replay.speed=0.3; + core.drawTip("x"+core.status.replay.speed+"倍"); +} + +////// 停止播放 ////// +core.prototype.stopReplay = function () { + if (!core.status.replay.replaying) return; + core.status.replay.toReplay = []; + core.status.replay.totalList = []; + core.status.replay.replaying=false; + core.status.replay.pausing=false; + core.status.replay.speed=1.0; + core.updateStatusBar(); + core.drawTip("停止播放并恢复游戏"); +} + +////// 回放 ////// +core.prototype.replay = function () { + + if (!core.status.replay.replaying) return; // 没有回放 + if (core.status.replay.pausing) return; // 暂停状态 + if (core.status.replay.animate) return; // 正在某段动画中 + + if (core.status.replay.toReplay.length==0) { // 回放完毕 + core.stopReplay(); + core.insertAction("录像回放完毕!"); + return; + } + + var action=core.status.replay.toReplay.shift(); + + if (action=='up' || action=='down' || action=='left' || action=='right') { + core.moveHero(action, function () { + core.replay(); + }); + return; + } + else if (action.indexOf("item:")==0) { + var itemId = action.substring(5); + if (core.canUseItem(itemId)) { + var tools = Object.keys(core.status.hero.items.tools).sort(); + var constants = Object.keys(core.status.hero.items.constants).sort(); + var index; + if ((index=tools.indexOf(itemId))>=0 || (index=constants.indexOf(itemId)+100)>=100) { + core.ui.drawToolbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.useItem(itemId, function () { + core.replay(); + }); + }, 750); + } + return; + } + } + else if (action.indexOf("fly:")==0) { + var floorId=action.substring(4); + var toIndex=core.status.hero.flyRange.indexOf(floorId); + var nowIndex=core.status.hero.flyRange.indexOf(core.status.floorId); + if (core.hasItem('fly') && toIndex>=0 && nowIndex>=0) { + core.ui.drawFly(toIndex); + setTimeout(function () { + core.ui.closePanel(); + var stair=toIndex0) { + var shop=core.status.shops[shopId]; + if (core.isset(shop) && shop.visited) { // 商店可用 + var choices = shop.choices; + var topIndex = 6 - parseInt(choices.length / 2); + + core.status.event.selection = parseInt(selections.shift()); + + core.events.openShop(shopId, false); + var shopInterval = setInterval(function () { + if (!core.events.clickShop(6, topIndex+core.status.event.selection)) { + clearInterval(shopInterval); + core.stopReplay(); + core.drawTip("录像文件出错"); + return; + } + if (selections.length==0) { + clearInterval(shopInterval); + core.events.clickShop(6, topIndex+choices.length); + core.replay(); + return; + } + core.status.event.selection = parseInt(selections.shift()); + core.events.openShop(shopId, false); + + }, 750); + return; + } + } + } + else if (action=='turn') { + core.turnHero(); + core.replay(); + return; + } + else if (action=='getNext') { + if (core.flags.enableGentleClick && core.getBlock(core.nextX(), core.nextY())!=null) { + var nextX = core.nextX(), nextY = core.nextY(); + var block = core.getBlock(nextX, nextY); + if (block!=null && block.block.event.trigger=='getItem') { + core.getItem(block.block.event.id, 1, nextX, nextY); + core.status.route.push("getNext"); + core.replay(); + return; + } + } + } + else if (action.indexOf('move:')==0) { + var pos=action.substring(5).split(":"); + var x=parseInt(pos[0]), y=parseInt(pos[1]); + if (core.canMoveDirectly(x,y)) { + core.clearMap('hero', 0, 0, 416, 416); + core.setHeroLoc('x', x); + core.setHeroLoc('y', y); + core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + core.status.route.push("move:"+x+":"+y); + core.replay(); + return; + } + } + + core.stopReplay(); + core.insertAction("录像文件出错"); + +} + ////// 判断当前能否进入某个事件 ////// core.prototype.checkStatus = function (name, need, item) { if (need && core.status.event.id == name) { @@ -3302,13 +4281,27 @@ core.prototype.checkStatus = function (name, need, item) { } core.lockControl(); - core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []}; core.status.event.id = name; return true; } ////// 点击怪物手册时的打开操作 ////// core.prototype.openBook = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; + + // 当前是book,且从“浏览地图”打开 + if (core.status.event.id == 'book' && core.isset(core.status.event.selection)) { + core.status.boxAnimateObjs = []; + core.ui.drawMaps(core.status.event.selection); + return; + } + + // 从“浏览地图”页面打开 + if (core.status.event.id=='viewMaps') { + need=false; + core.status.event.selection = core.status.event.data; + } + if (!core.checkStatus('book', need, true)) return; core.useItem('book'); @@ -3316,6 +4309,7 @@ core.prototype.openBook = function (need) { ////// 点击楼层传送器时的打开操作 ////// core.prototype.useFly = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; if (!core.checkStatus('fly', need, true)) return; if (core.flags.flyNearStair && !core.nearStair()) { @@ -3338,43 +4332,85 @@ core.prototype.useFly = function (need) { ////// 点击工具栏时的打开操作 ////// core.prototype.openToolbox = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; if (!core.checkStatus('toolbox', need)) return; core.ui.drawToolbox(); } +////// 点击快捷商店按钮时的打开操作 ////// +core.prototype.openQuickShop = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; + if (!core.checkStatus('selectShop', need)) + return; + core.ui.drawQuickShop(); +} + ////// 点击保存按钮时的打开操作 ////// core.prototype.save = function(need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; if (!core.checkStatus('save', need)) return; - core.ui.drawSLPanel(core.status.saveIndex); + + var saveIndex = core.status.saveIndex; + var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; + + core.ui.drawSLPanel(10*page+offset); } ////// 点击读取按钮时的打开操作 ////// core.prototype.load = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; + + var saveIndex = core.getLocalStorage('saveIndex2', 1); + var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; // 游戏开始前读档 if (!core.isPlaying()) { core.status.event = {'id': 'load', 'data': null}; core.status.lockControl = true; core.dom.startPanel.style.display = 'none'; - core.ui.drawSLPanel(core.getLocalStorage('saveIndex', 1)); + core.ui.drawSLPanel(10*page+offset); return; } if (!core.checkStatus('load', need)) return; - core.ui.drawSLPanel(core.status.saveIndex); + core.ui.drawSLPanel(10*page+offset); +} + +////// 点击设置按钮时的操作 ////// +core.prototype.openSettings = function (need) { + if (core.isset(core.status.replay)&&core.status.replay.replaying) return; + if (!core.checkStatus('settings', need)) + return; + core.ui.drawSettings(); +} + +////// 自动存档 ////// +core.prototype.autosave = function (removeLast) { + var x; + if (removeLast) + x=core.status.route.pop(); + core.saveData("autoSave"); + if (removeLast) + core.status.route.push(x); } ////// 实际进行存读档事件 ////// core.prototype.doSL = function (id, type) { - core.status.saveIndex=id; if (type=='save') { + if (id=='autoSave') { + core.drawTip('不能覆盖自动存档!'); + return; + } if (core.saveData("save"+id)) { core.ui.closePanel(); core.drawTip('存档成功!'); - core.setLocalStorage('saveIndex', core.status.saveIndex); + if (id!="autoSave") { + core.status.saveIndex=id; + core.setLocalStorage('saveIndex2', core.status.saveIndex); + } } else { core.drawTip('存储空间不足,请覆盖已有的存档或在菜单栏中进行清理'); @@ -3382,7 +4418,7 @@ core.prototype.doSL = function (id, type) { return; } else if (type=='load') { - var data = core.getLocalStorage("save"+id, null); + var data = core.getLocalStorage(id=='autoSave'?id:"save"+id, null); if (!core.isset(data)) { core.drawTip("无效的存档"); return; @@ -3393,14 +4429,161 @@ core.prototype.doSL = function (id, type) { } core.ui.closePanel(); core.loadData(data, function() { - core.status.saveIndex=id; - core.setLocalStorage('saveIndex', core.status.saveIndex); core.drawTip("读档成功"); + if (id!="autoSave") { + core.status.saveIndex=id; + core.setLocalStorage('saveIndex2', core.status.saveIndex); + } }); return; } } +////// 同步存档到服务器 ////// +core.prototype.syncSave = function (type) { + var saves=null; + // data + if (type=='all') { + saves=[]; + for (var i=1;i<=150;i++) { + var data = core.getLocalStorage("save"+i, null); + if (core.isset(data)) { + saves.push(data); + } + } + } + else { + for (var i=150;i>=1;i--) { + saves=core.getLocalStorage("save"+i, null); + if (core.isset(saves)) { + break; + } + } + } + if (!core.isset(saves)) { + core.drawText("没有要同步的存档"); + return; + } + core.ui.drawWaiting("正在同步,请稍后..."); + + var formData = new FormData(); + formData.append('type', 'save'); + formData.append('name', core.firstData.name); + var save_text = JSON.stringify(saves); + formData.append('data', save_text); + + // send + var xhr = new XMLHttpRequest(); + xhr.open("POST", "/games/sync.php"); + xhr.onload = function(e) { + if (xhr.status==200) { + // console.log("同步成功。"); + var response = JSON.parse(xhr.response); + if (response.code<0) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+response.msg); + } + else { + core.drawText("同步成功!\n\n您的存档编号: "+response.code+"\n您的存档密码: "+response.msg+"\n\n请牢记以上两个信息(如截图等),在从服务器\n同步存档时使用。") + } + } + else { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:HTTP "+xhr.status); + } + }; + xhr.ontimeout = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:Timeout"); + } + xhr.onerror = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:XHR Error"); + } + xhr.send(formData); +} + +////// 从服务器加载存档 ////// +core.prototype.syncLoad = function () { + var id = prompt("请输入存档编号:"); + if (id==null || id=="") { + core.ui.drawSyncSave(); return; + } + var password = prompt("请输入存档密码:"); + if (password==null || password=="") { + core.ui.drawSyncSave(); return; + } + core.ui.drawWaiting("正在同步,请稍后..."); + + var formData = new FormData(); + formData.append('type', 'load'); + formData.append('name', core.firstData.name); + formData.append('id', id); + formData.append('password', password); + + // send + var xhr = new XMLHttpRequest(); + xhr.open("POST", "/games/sync.php"); + xhr.onload = function(e) { + if (xhr.status==200) { + // console.log("同步成功。"); + var response = JSON.parse(xhr.response); + switch (response.code) { + case 0: + // 成功 + var data=JSON.parse(response.msg); + // console.log(data); + + if (data instanceof Array) { + core.status.event.selection=1; + core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { + for (var i=1;i<=150;i++) { + if (i<=data.length) { + core.setLocalStorage("save"+i, data[i-1]); + } + else { + core.removeLocalStorage("save"+i); + } + } + core.drawText("同步成功!\n你的本地所有存档均已被覆盖。"); + }, function () { + core.status.event.selection=0; + core.ui.drawSyncSave(); + }) + } + else { + // 只覆盖单存档 + var index=150; + for (var i=150;i>=1;i--) { + if (core.getLocalStorage("save"+i, null)==null) + index=i; + else break; + } + core.setLocalStorage("save"+index, data); + core.drawText("同步成功!\n单存档已覆盖至存档"+index); + } + break; + case -1: + core.drawText("出错啦!\n存档编号"+id+"不存在!"); + break; + case -2: + core.drawText("出错啦!\n存档密码错误!"); + break; + default: + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+response.msg); + break; + } + } + else { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:HTTP "+xhr.status); + } + }; + xhr.ontimeout = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:Timeout"); + } + xhr.onerror = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:XHR Error"); + } + xhr.send(formData); +} +/* + ////// 存档同步操作 ////// core.prototype.syncSave = function(type) { if (type=='save') { @@ -3413,7 +4596,7 @@ core.prototype.syncSave = function(type) { formData.append('type', 'save'); formData.append('name', core.firstData.name); var saves = []; - for (var i=1;i<=180;i++) { + for (var i=1;i<=150;i++) { var data = core.getLocalStorage("save"+i, null); if (core.isset(data)) { saves.push(data); @@ -3440,13 +4623,11 @@ core.prototype.syncSave = function(type) { core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:HTTP "+xhr.status); } }; - xhr.ontimeout = function(e) { - console.log(e); - core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+e); + xhr.ontimeout = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:Timeout"); } - xhr.onerror = function(e) { - console.log(e); - core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+e); + xhr.onerror = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:XHR Error"); } xhr.send(formData); }, function() { @@ -3485,7 +4666,7 @@ core.prototype.syncSave = function(type) { // 成功 var data=JSON.parse(response.msg); // console.log(data); - for (var i=1;i<=180;i++) { + for (var i=1;i<=150;i++) { if (i<=data.length) { core.setLocalStorage("save"+i, data[i-1]); } @@ -3510,11 +4691,11 @@ core.prototype.syncSave = function(type) { core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:HTTP "+xhr.status); } }; - xhr.ontimeout = function(e) { - core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+e); + xhr.ontimeout = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:Timeout"); } - xhr.onerror = function(e) { - core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+e); + xhr.onerror = function() { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:XHR Error"); } xhr.send(formData); }, function() { @@ -3524,6 +4705,7 @@ core.prototype.syncSave = function(type) { } } +*/ ////// 存档到本地 ////// core.prototype.saveData = function(dataId) { @@ -3532,6 +4714,7 @@ core.prototype.saveData = function(dataId) { 'hero': core.clone(core.status.hero), 'hard': core.status.hard, 'maps': core.maps.save(core.status.maps), + 'route': core.encodeRoute(core.status.route), 'shops': {}, 'version': core.firstData.version, "time": new Date().getTime() @@ -3551,7 +4734,7 @@ core.prototype.saveData = function(dataId) { ////// 从本地读档 ////// core.prototype.loadData = function (data, callback) { - core.resetStatus(data.hero, data.hard, data.floorId, core.maps.load(data.maps)); + core.resetStatus(data.hero, data.hard, data.floorId, core.decodeRoute(data.route), core.maps.load(data.maps)); // load shop times for (var shop in core.status.shops) { @@ -3561,12 +4744,106 @@ core.prototype.loadData = function (data, callback) { core.events.afterLoadData(data); - core.changeFloor(data.floorId, null, data.hero.loc, null, function() { - core.setHeroMoveTriggerInterval(); + core.changeFloor(data.floorId, null, data.hero.loc, 0, function() { + //core.setHeroMoveTriggerInterval(); if (core.isset(callback)) callback(); }); } +////// 加密路线 ////// +core.prototype.encodeRoute = function (route) { + var ans=""; + var lastMove = "", cnt=0; + + var items=Object.keys(core.material.items).sort(); + var shops=Object.keys(core.initStatus.shops).sort(); + route.forEach(function (t) { + if (t=='up' || t=='down' || t=='left' || t=='right') { + if (t!=lastMove && cnt>0) { + ans+=lastMove.substring(0,1).toUpperCase(); + if (cnt>1) ans+=cnt; + cnt=0; + } + lastMove=t; + cnt++; + } + else { + if (cnt>0) { + ans+=lastMove.substring(0,1).toUpperCase(); + if (cnt>1) ans+=cnt; + cnt=0; + } + if (t.indexOf('item:')==0) + ans+="I"+items.indexOf(t.substring(5)); + else if (t.indexOf('fly:')==0) + ans+="F"+core.floorIds.indexOf(t.substring(4)); + else if (t.indexOf('choices:')==0) + ans+="C"+t.substring(8); + else if (t.indexOf('shop:')==0) { + var sp=t.substring(5).split(":"); + ans+="S"+shops.indexOf(sp[0])+":"+sp[1]; + } + else if (t=='turn') + ans+='T'; + else if (t=='getNext') + ans+='G'; + else if (t.indexOf('input:')==0) + ans+="P"+t.substring(6); + else if (t=='no') + ans+='N'; + else if (t.indexOf('move:')==0) { + ans+="M"+t.substring(5); + } + } + }); + if (cnt>0) { + ans+=lastMove.substring(0,1).toUpperCase(); + if (cnt>1) ans+=cnt; + } + return ans; +} + +////// 解密路线 ////// +core.prototype.decodeRoute = function (route) { + + if (!core.isset(route)) return route; + + var ans=[], index=0; + + var getNumber = function (noparse) { + var num=""; + while (index=0:(special!=0&&(special%100==test||this.hasSpecial(parseInt(special/100), test))); + + if (special instanceof Array) { + return special.indexOf(test)>=0; + } + + if (typeof special == 'number') { + return special!=0 && (special%100==test||this.hasSpecial(parseInt(special/100), test)); + } + + return false; } ////// 获得所有特殊属性的名称 ////// @@ -48,6 +57,7 @@ enemys.prototype.getSpecialText = function (enemyId) { if (this.hasSpecial(special, 18)) text.push("阻击"); if (this.hasSpecial(special, 19)) text.push("自爆"); if (this.hasSpecial(special, 20)) text.push("无敌"); + if (this.hasSpecial(special, 21)) text.push("退化"); return text; } @@ -76,16 +86,17 @@ enemys.prototype.getSpecialHint = function (enemy, special) { case 8: return "反击:战斗时,怪物每回合附加角色攻击的"+parseInt(100*core.values.counterAttack)+"%作为伤害,无视角色防御"; case 9: return "净化:战斗前,怪物附加勇士魔防的"+core.values.purify+"倍作为伤害"; case 10: return "模仿:怪物的攻防和勇士攻防相等"; - case 11: return "吸血:战斗前,怪物首先吸取角色的"+parseInt(100*enemy.value)+"%生命作为伤害"; + case 11: return "吸血:战斗前,怪物首先吸取角色的"+parseInt(100*enemy.value)+"%生命作为伤害"+(enemy.add?",并把伤害数值加到自身生命上":""); case 12: return "中毒:战斗后,勇士陷入中毒状态,每一步损失生命"+core.values.poisonDamage+"点"; case 13: return "衰弱:战斗后,勇士陷入衰弱状态,攻防暂时下降"+core.values.weakValue+"点"; case 14: return "诅咒:战斗后,勇士陷入诅咒状态,战斗无法获得金币和经验"; case 15: return "领域:经过怪物周围"+(enemy.range||1)+"格时自动减生命"+enemy.value+"点"; case 16: return "夹击:经过两只相同的怪物中间,勇士生命值变成一半"; - case 17: return "仇恨:战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得"+core.values.hatred+"点仇恨值)"; + case 17: return "仇恨:战斗前,怪物附加之前积累的仇恨值作为伤害"+(core.flags.hatredDecrease?";战斗后,释放一半的仇恨值":"")+"。(每杀死一个怪物获得"+core.values.hatred+"点仇恨值)"; case 18: return "阻击:经过怪物的十字领域时自动减生命"+enemy.value+"点,同时怪物后退一格"; case 19: return "自爆:战斗后勇士的生命值变成1"; case 20: return "无敌:勇士无法打败怪物,除非拥有十字架"; + case 21: return "退化:战斗后勇士永久下降"+(enemy.atkValue||0)+"点攻击和"+(enemy.defValue||0)+"点防御"; default: break; } return "" @@ -94,21 +105,14 @@ enemys.prototype.getSpecialHint = function (enemy, special) { ////// 获得某个怪物的伤害 ////// enemys.prototype.getDamage = function (monsterId) { var monster = core.material.enemys[monsterId]; - var hero_atk = core.status.hero.atk, hero_def = core.status.hero.def, hero_mdef = core.status.hero.mdef; - var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_special = monster.special; - var damage = this.calDamage(hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special, monster.n); - if (damage == 999999999) return damage; + var damage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); + if (damage >= 999999999) return damage; return damage + this.getExtraDamage(monster); } ////// 获得某个怪物的额外伤害 ////// enemys.prototype.getExtraDamage = function (monster) { var extra_damage = 0; - if (this.hasSpecial(monster.special, 11)) { // 吸血 - // 吸血的比例 - extra_damage = core.status.hero.hp * monster.value; - extra_damage = parseInt(extra_damage); - } if (this.hasSpecial(monster.special, 17)) { // 仇恨 extra_damage += core.getFlag('hatred', 0); } @@ -118,14 +122,15 @@ enemys.prototype.getExtraDamage = function (monster) { ////// 临界值计算 ////// enemys.prototype.getCritical = function (monsterId) { var monster = core.material.enemys[monsterId]; + // 坚固、模仿怪物没有临界! if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return "???"; - var last = this.calDamage(core.status.hero.atk, core.status.hero.def, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); + + var last = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); + if (last <= 0) return 0; for (var i = core.status.hero.atk + 1; i <= monster.hp + monster.def; i++) { - var damage = this.calDamage(i, core.status.hero.def, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); + var damage = this.calDamage(monster, core.status.hero.hp, i, core.status.hero.def, core.status.hero.mdef); if (damage < last) return i - core.status.hero.atk; last = damage; @@ -139,31 +144,45 @@ enemys.prototype.getCriticalDamage = function (monsterId) { if (c == '???') return '???'; if (c <= 0) return 0; var monster = core.material.enemys[monsterId]; - var last = this.calDamage(core.status.hero.atk, core.status.hero.def, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); - if (last == 999999999) return '???'; + var last = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); + if (last >= 999999999) return '???'; - return last - this.calDamage(core.status.hero.atk + c, core.status.hero.def, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); + return last - this.calDamage(monster, core.status.hero.hp, core.status.hero.atk + c, core.status.hero.def, core.status.hero.mdef); } ////// 1防减伤计算 ////// enemys.prototype.getDefDamage = function (monsterId) { var monster = core.material.enemys[monsterId]; - var nowDamage = this.calDamage(core.status.hero.atk, core.status.hero.def, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); - var nextDamage = this.calDamage(core.status.hero.atk, core.status.hero.def + 1, core.status.hero.mdef, - monster.hp, monster.atk, monster.def, monster.special, monster.n); - if (nowDamage == 999999999 || nextDamage == 999999999) return "???"; + var nowDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); + var nextDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def + 1, core.status.hero.mdef); + if (nowDamage >= 999999999 || nextDamage >= 999999999) return "???"; return nowDamage - nextDamage; } ////// 具体的伤害计算公式 ////// -enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special, n) { +enemys.prototype.calDamage = function (monster, hero_hp, hero_atk, hero_def, hero_mdef) { + + var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_special = monster.special; if (this.hasSpecial(mon_special, 20) && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架 return 999999999; // 返回无限大 + var initDamage = 0; // 战前伤害 + + // 吸血 + if (this.hasSpecial(mon_special, 11)) { + var vampireDamage = hero_hp * monster.value; + + // 如果有神圣盾免疫吸血等可以在这里写 + + vampireDamage = parseInt(vampireDamage); + // 加到自身 + if (monster.add) // 如果加到自身 + mon_hp += vampireDamage; + + initDamage += vampireDamage; + } + // 模仿 if (this.hasSpecial(mon_special,10)) { mon_atk = hero_atk; @@ -177,42 +196,51 @@ enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mo var per_damage = mon_atk - hero_def; if (per_damage < 0) per_damage = 0; - // 2连击 & 3连击 + // 2连击 & 3连击 & N连击 if (this.hasSpecial(mon_special, 4)) per_damage *= 2; if (this.hasSpecial(mon_special, 5)) per_damage *= 3; - if (this.hasSpecial(mon_special, 6)) per_damage *= (n||4); + if (this.hasSpecial(mon_special, 6)) per_damage *= (monster.n||4); var counterDamage = 0; // 反击 if (this.hasSpecial(mon_special, 8)) counterDamage += parseInt(core.values.counterAttack * hero_atk); // 先攻 - var damage = mon_special == 1 ? per_damage : 0; + if (this.hasSpecial(mon_special, 1)) + initDamage += per_damage; + // 破甲 - if (this.hasSpecial(mon_special, 7)) damage += parseInt(core.values.breakArmor * hero_def); + if (this.hasSpecial(mon_special, 7)) + initDamage += parseInt(core.values.breakArmor * hero_def); + // 净化 - if (this.hasSpecial(mon_special, 9)) damage = core.values.purify * hero_mdef; + if (this.hasSpecial(mon_special, 9)) + initDamage += parseInt(core.values.purify * hero_mdef); var turn = parseInt((mon_hp - 1) / (hero_atk - mon_def)); - var ans = damage + turn * per_damage + (turn + 1) * counterDamage; + var ans = initDamage + turn * per_damage + (turn + 1) * counterDamage; ans -= hero_mdef; - return core.flags.enableNegativeDamage?ans:Math.max(0, ans); + if (!core.flags.enableNegativeDamage) + ans=Math.max(0, ans); + + return ans; } ////// 获得当前楼层的怪物列表 ////// -enemys.prototype.getCurrentEnemys = function () { +enemys.prototype.getCurrentEnemys = function (floorId) { + floorId=floorId||core.status.floorId; var enemys = []; var used = {}; - var mapBlocks = core.status.thisMap.blocks; + var mapBlocks = core.status.maps[floorId].blocks; for (var b = 0; b < mapBlocks.length; b++) { if (core.isset(mapBlocks[b].event) && !(core.isset(mapBlocks[b].enable) && !mapBlocks[b].enable) && mapBlocks[b].event.cls == 'enemys') { var monsterId = mapBlocks[b].event.id; if (core.isset(used[monsterId])) continue; var monster = core.material.enemys[monsterId]; - var mon_atk = monster.atk, mon_def = monster.def; + var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def; // 坚固 if (this.hasSpecial(monster.special, 3) && mon_def < core.status.hero.atk - 1) mon_def = core.status.hero.atk - 1; @@ -228,11 +256,12 @@ enemys.prototype.getCurrentEnemys = function () { enemys.push({ 'id': monsterId, 'name': monster.name, - 'hp': monster.hp, + 'hp': mon_hp, 'atk': mon_atk, 'def': mon_def, 'money': monster.money, 'experience': monster.experience, + 'point': monster.point||0, // 加点 'special': specialText, 'damage': this.getDamage(monsterId), 'critical': this.getCritical(monsterId), diff --git a/libs/events.js b/libs/events.js index cf66d51e..533cb8f6 100644 --- a/libs/events.js +++ b/libs/events.js @@ -6,6 +6,7 @@ function events() { events.prototype.init = function () { this.events = { 'battle': function (data, core, callback) { + //core.autosave(true); core.battle(data.event.id, data.x, data.y); if (core.isset(callback)) callback(); @@ -16,9 +17,11 @@ events.prototype.init = function () { callback(); }, 'openDoor': function (data, core, callback) { - core.openDoor(data.event.id, data.x, data.y, true); - if (core.isset(callback)) - callback(); + //core.autosave(true); + core.openDoor(data.event.id, data.x, data.y, true, function () { + if (core.isset(callback)) callback(); + core.replay(); + }); }, 'changeFloor': function (data, core, callback) { var heroLoc = {}; @@ -27,7 +30,10 @@ events.prototype.init = function () { if (core.isset(data.event.data.direction)) heroLoc.direction = data.event.data.direction; core.changeFloor(data.event.data.floorId, data.event.data.stair, - heroLoc, data.event.data.time, callback); + heroLoc, data.event.data.time, function () { + if (core.isset(callback)) callback(); + core.replay(); + }); }, 'passNet': function (data, core, callback) { core.events.passNet(data); @@ -107,44 +113,136 @@ events.prototype.setInitData = function (hard) { ////// 游戏获胜事件 ////// events.prototype.win = function(reason) { + core.ui.closePanel(); + var replaying = core.status.replay.replaying; + core.stopReplay(); core.waitHeroToStop(function() { core.removeGlobalAnimate(0,0,true); core.clearMap('all'); // 清空全地图 core.drawText([ - "\t[结局2]恭喜通关!你的分数是${status:hp}。" + "\t[恭喜通关]你的分数是${status:hp}。" ], function () { - core.restart(); + core.events.gameOver('', replaying); }) }); } ////// 游戏失败事件 ////// events.prototype.lose = function(reason) { + core.ui.closePanel(); + var replaying = core.status.replay.replaying; + core.stopReplay(); core.waitHeroToStop(function() { core.drawText([ "\t[结局1]你死了。\n如题。" ], function () { - core.restart(); + core.events.gameOver(null, replaying); }); }) } +////// 游戏结束 ////// +events.prototype.gameOver = function (ending, fromReplay) { + + // 上传成绩 + var confirmUpload = function () { + + if (!core.isset(ending)) { + core.restart(); + return; + } + + var doUpload = function(username) { + if (username==null) username=""; + + // upload + var formData = new FormData(); + formData.append('type', 'score'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); + formData.append('hard', core.status.hard); + formData.append('username', username); + formData.append('ending', ending); + formData.append('lv', core.status.hero.lv); + formData.append('hp', core.status.hero.hp); + formData.append('atk', core.status.hero.atk); + formData.append('def', core.status.hero.def); + formData.append('mdef', core.status.hero.mdef); + formData.append('money', core.status.hero.money); + formData.append('experience', core.status.hero.experience); + formData.append('steps', core.status.hero.steps); + formData.append('route', core.encodeRoute(core.status.route)); + + var xhr = new XMLHttpRequest(); + xhr.open("POST", "/games/upload.php"); + xhr.send(formData); + + core.restart(); + } + + core.ui.drawConfirmBox("你想记录你的ID和成绩吗?", function () { + doUpload(prompt("请输入你的ID:")); + }, function () { + doUpload(""); + }) + + return; + } + + // 下载录像 + var confirmDownload = function () { + core.ui.closePanel(); + core.ui.drawConfirmBox("你想下载录像吗?", function () { + var obj = { + 'name': core.firstData.name, + 'version': core.firstData.version, + 'hard': core.status.hard, + 'route': core.encodeRoute(core.status.route) + } + core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify(obj)); + confirmUpload(); + }, function () { + confirmUpload(); + }) + } + + if (fromReplay) { + core.drawText("录像回放完毕!", function () { + core.restart(); + }); + } + else { + confirmDownload(); + } + +} + ////// 转换楼层结束的事件 ////// events.prototype.afterChangeFloor = function (floorId) { - if (!core.isset(core.status.event.id) && !core.hasFlag("visited_"+floorId)) { - this.doEvents(core.floors[floorId].firstArrive); + if (core.isset(core.status.event.id)) return; // 当前存在事件 + + if (!core.hasFlag("visited_"+floorId)) { + this.doEvents(core.floors[floorId].firstArrive, null, null, function () { + //core.autosave(); + }); core.setFlag("visited_"+floorId, true); + return; } + + // 自动存档 + //core.autosave(); } ////// 开始执行一系列自定义事件 ////// events.prototype.doEvents = function (list, x, y, callback) { + if (!core.isset(list)) return; + if (!(list instanceof Array)) { + list = [list]; + } + // 停止勇士 core.waitHeroToStop(function() { - if (!core.isset(list)) return; - if (!(list instanceof Array)) { - list = [list]; - } core.lockControl(); core.status.event = {'id': 'action', 'data': { 'list': core.clone(list), 'x': x, 'y': y, 'callback': callback @@ -156,7 +254,7 @@ events.prototype.doEvents = function (list, x, y, callback) { ////// 执行当前自定义事件列表中的下一个事件 ////// events.prototype.doAction = function() { // 清空boxAnimate和UI层 - clearInterval(core.interval.boxAnimate); + core.status.boxAnimateObjs = []; core.clearMap('ui', 0, 0, 416, 416); core.setAlpha('ui', 1.0); @@ -165,6 +263,7 @@ events.prototype.doAction = function() { if (core.isset(core.status.event.data.callback)) core.status.event.data.callback(); core.ui.closePanel(); + core.replay(); return; } @@ -178,13 +277,32 @@ events.prototype.doAction = function() { // 如果是文字:显示 if (typeof data == "string") { core.status.event.data.type='text'; - core.ui.drawTextBox(data); + // 如果是正在回放中,不显示 + if (core.status.replay.replaying) + core.events.doAction(); + else + core.ui.drawTextBox(data); return; } core.status.event.data.type=data.type; switch (data.type) { case "text": // 文字/对话 - core.ui.drawTextBox(data.data); + if (core.status.replay.replaying) + core.events.doAction(); + else + core.ui.drawTextBox(data.data); + break; + case "setText": // 设置文本状态 + if (data.position=='up'||data.position=='down'||data.position=='center') { + core.status.textAttribute.position=data.position; + } + ["background", "title", "text"].forEach(function (t) { + if (core.isset(data[t]) && (data[t] instanceof Array) && data[t].length>=3) { + if (data[t].length==3) data[t].push(1); + core.status.textAttribute[t]=data[t]; + } + }) + core.events.doAction(); break; case "tip": core.drawTip(core.replaceText(data.text)); @@ -216,6 +334,48 @@ events.prototype.doAction = function() { } else this.doAction(); break; + case "setBlock": // 设置某图块 + { + if (core.isset(data.loc)) { + x=data.loc[0]; + y=data.loc[1]; + } + var floorId = data.floorId||core.status.floorId; + var originBlock=core.getBlock(x,y,toId,false); + var block = core.maps.getBlock(x,y,data.number); + core.maps.addInfo(block); + core.maps.addEvent(block,x,y,core.floors[floorId].events[x+","+y]); + core.maps.addChangeFloor(block,x,y,core.floors[floorId].changeFloor[x+","+y]); + if (core.isset(block.event)) { + if (originBlock==null) { + core.status.maps[floorId].blocks.push(block); + } + else { + originBlock.block.id = data.number; + originBlock.block.event = block.event; + } + core.drawMap(floorId); + core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); + core.updateStatusBar(); + } + this.doAction(); + break; + } + case "animate": // 显示动画 + if (core.isset(data.loc)) { + if (data.loc == 'hero') { + x=core.getHeroLoc('x'); + y=core.getHeroLoc('y'); + } + else if (data.loc instanceof Array) { + x=data.loc[0]; + y=data.loc[1]; + } + } + core.drawAnimate(data.name, x, y, function () { + core.events.doAction(); + }) + break; case "move": // 移动事件 if (core.isset(data.loc)) { x=data.loc[0]; @@ -248,11 +408,22 @@ events.prototype.doAction = function() { core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop'); this.doAction(); break; + case "showImage": // 显示图片 + if (core.isset(data.loc) && core.isset(core.material.images.pngs[data.name])) { + core.canvas.animate.drawImage(core.material.images.pngs[data.name], data.loc[0], data.loc[1]); + } + else core.clearMap('animate', 0, 0, 416, 416); + this.doAction(); + break; case "setFg": // 颜色渐变 core.setFg(data.color, data.time, function() { core.events.doAction(); }); break; + case "setWeather": // 更改天气 + core.setWeather(data.name, data.level); + this.doAction(); + break; case "openDoor": // 开一个门,包括暗墙 var floorId=data.floorId || core.status.floorId; var block=core.getBlock(data.loc[0], data.loc[1], floorId); @@ -270,7 +441,13 @@ events.prototype.doAction = function() { this.doAction(); break; case "openShop": // 打开一个全局商店 - core.events.openShop(data.id); + if (core.status.replay.replaying) { // 正在播放录像,简单将visited置为true + core.status.shops[data.id].visited=true; + core.status.event.data.list = []; + this.doAction(); + } + else + core.events.openShop(data.id); break; case "disableShop": // 禁用一个全局商店 core.events.disableQuickShop(data.id); @@ -351,13 +528,38 @@ events.prototype.doAction = function() { this.doAction(); break; case "choices": // 提供选项 + if (core.status.replay.replaying) { + if (core.status.replay.toReplay.length==0) { // 回放完毕 + core.status.replay.replaying=false; + core.drawTip("录像回放完毕"); + } + else { + var action = core.status.replay.toReplay.shift(), index; + if (action.indexOf("choices:")==0 && ((index=parseInt(action.substring(8)))>=0) && index0) { - this.insertAction(todo); + this.insertAction(todo,x,y); } // 如果已有事件正在处理中 if (core.status.event.id == null) { core.continueAutomaticRoute(); } + else { + core.clearContinueAutomaticRoute(); + } if (core.isset(callback)) callback(); } @@ -613,12 +837,15 @@ events.prototype.afterOpenDoor = function(doorId,x,y,callback) { } if (todo.length>0) { - this.insertAction(todo); + this.insertAction(todo,x,y); } if (core.status.event.id == null) { core.continueAutomaticRoute(); } + else { + core.clearContinueAutomaticRoute(); + } if (core.isset(callback)) callback(); } @@ -678,6 +905,16 @@ events.prototype.afterChangeLight = function(x,y) { ////// 使用炸弹/圣锤后的事件 ////// events.prototype.afterUseBomb = function () { + // 这是一个使用炸弹也能开门的例子 + /* + if (core.status.floorId=='xxx' && core.terrainExists(x0,y0,'specialDoor') // 某个楼层,该机关门存在 + && !core.enemyExists(x1,y1) && !core.enemyExists(x2,y2)) // 且守门的怪物都不存在 + { + core.insertAction([ // 插入事件 + {"type": "openDoor", "loc": [x0,y0]} // 开门 + ]) + } + */ } @@ -696,6 +933,14 @@ events.prototype.afterLoadData = function(data) { /********** 点击事件、键盘事件 ************/ /****************************************/ +////// 长按 ////// +events.prototype.longClick = function () { + core.waitHeroToStop(function () { + // 绘制快捷键 + core.ui.drawKeyBoard(); + }); +} + ////// 按下Ctrl键时(快捷跳过对话) ////// events.prototype.keyDownCtrl = function () { if (core.status.event.id=='text') { @@ -756,6 +1001,8 @@ events.prototype.clickAction = function (x,y) { if (x >= 5 && x <= 7) { var topIndex = 6 - parseInt((choices.length - 1) / 2); if (y>=topIndex && y0) { if (keycode==38) { core.status.event.selection--; - if (core.status.event.selection<0) core.status.event.selection=0; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } if (keycode==40) { core.status.event.selection++; - if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -794,6 +1039,7 @@ events.prototype.keyUpAction = function (keycode) { var choices = data.choices; if (choices.length>0) { if (keycode==13 || keycode==32 || keycode==67) { + core.status.route.push("choices:"+core.status.event.selection); this.insertAction(choices[core.status.event.selection].action); this.doAction(); } @@ -815,7 +1061,12 @@ events.prototype.clickBook = function(x,y) { } // 返回 if (x>=10 && x<=12 && y==12) { - core.ui.closePanel(); + if (core.status.event.selection==null) + core.ui.closePanel(); + else { + core.status.boxAnimateObjs = []; + core.ui.drawMaps(core.status.event.selection); + } return; } // 怪物信息 @@ -843,7 +1094,12 @@ events.prototype.keyDownBook = function (keycode) { ////// 怪物手册界面时,放开某个键的操作 ////// events.prototype.keyUpBook = function (keycode) { if (keycode==27 || keycode==88) { - core.ui.closePanel(); + if (core.status.event.selection==null) + core.ui.closePanel(); + else { + core.status.boxAnimateObjs = []; + core.ui.drawMaps(core.status.event.selection); + } return; } if (keycode==13 || keycode==32 || keycode==67) { @@ -870,8 +1126,10 @@ events.prototype.clickFly = function(x,y) { var index=core.status.hero.flyRange.indexOf(core.status.floorId); var stair=core.status.event.data=8) { + core.ui.drawMaps(core.status.event.data-1); + } + else { + core.clearMap('data', 0, 0, 416, 416); + core.setOpacity('data', 1); + core.ui.closePanel(); + } +} + +////// 查看地图界面时,按下某个键的操作 ////// +events.prototype.keyDownViewMaps = function (keycode) { + if (keycode==37 || keycode==38) core.ui.drawMaps(core.status.event.data+1); + else if (keycode==39 || keycode==40) core.ui.drawMaps(core.status.event.data-1); + return; +} + +////// 查看地图界面时,放开某个键的操作 ////// +events.prototype.keyUpViewMaps = function (keycode) { + if (keycode==27 || keycode==13 || keycode==32 || keycode==67) { + core.clearMap('data', 0, 0, 416, 416); + core.setOpacity('data', 1); + core.ui.closePanel(); + } + if (keycode==88) { + core.openBook(false); + } + return; +} + ////// 商店界面时的点击操作 ////// events.prototype.clickShop = function(x,y) { var shop = core.status.event.data.shop; @@ -899,8 +1192,9 @@ events.prototype.clickShop = function(x,y) { if (x >= 5 && x <= 7) { var topIndex = 6 - parseInt(choices.length / 2); if (y>=topIndex && y eval(use)) { core.drawTip("你的"+use_text+"不足"); - return; + return false; } - eval(use+'-='+need); + core.status.event.data.actions.push(y-topIndex); + eval(use+'-='+need); core.setStatus('money', money); core.setStatus('experience', experience); @@ -930,27 +1225,28 @@ events.prototype.clickShop = function(x,y) { } // 离开 else if (y==topIndex+choices.length) { + if (core.status.event.data.actions.length>0) { + core.status.route.push("shop:"+core.status.event.data.id+":"+core.status.event.data.actions.join("")); + } + core.status.boxAnimateObjs = []; - core.setBoxAnimate(); if (core.status.event.data.fromList) core.ui.drawQuickShop(); else core.ui.closePanel(); } + else return false; } + return true; } ////// 商店界面时,按下某个键的操作 ////// events.prototype.keyDownShop = function (keycode) { - var shop = core.status.event.data.shop; - var choices = shop.choices; if (keycode==38) { core.status.event.selection--; - if (core.status.event.selection<0) core.status.event.selection=0; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } if (keycode==40) { core.status.event.selection++; - if (core.status.event.selection>choices.length) core.status.event.selection=choices.length; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -958,11 +1254,14 @@ events.prototype.keyDownShop = function (keycode) { ////// 商店界面时,放开某个键的操作 ////// events.prototype.keyUpShop = function (keycode) { if (keycode==27 || keycode==88) { - if (core.status.event.data.fromList) { - core.status.boxAnimateObjs = []; - core.setBoxAnimate(); - core.ui.drawQuickShop(); + if (core.status.event.data.actions.length>0) { + core.status.route.push("shop:"+core.status.event.data.id+":"+core.status.event.data.actions.join("")); } + + core.status.boxAnimateObjs = []; + + if (core.status.event.data.fromList) + core.ui.drawQuickShop(); else core.ui.closePanel(); return; @@ -982,7 +1281,7 @@ events.prototype.clickQuickShop = function(x, y) { if (x >= 5 && x <= 7) { var topIndex = 6 - parseInt(keys.length / 2); if (y>=topIndex && ykeys.length) core.status.event.selection=keys.length; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -1033,6 +1329,18 @@ events.prototype.clickToolbox = function(x,y) { core.ui.closePanel(); return; } + if (x>=10 && x<=12 && y<=1) { + if (!core.isset(core.status.event.data)) return; + if (!core.flags.enableDeleteItem) { + core.drawTip("不支持删除道具!"); + return; + } + core.removeItem(core.status.event.data); + core.status.event.data = null; + core.ui.drawToolbox(); + return; + } + var index=0; if (y==4||y==5||y==9||y==10) index=parseInt(x/2); else index=6+parseInt(x/2); @@ -1137,17 +1445,34 @@ events.prototype.keyUpToolbox = function (keycode) { this.clickToolboxIndex(core.status.event.selection); return; } + + if (keycode==46) { // delete + if (!core.isset(core.status.event.data)) return; + if (!core.flags.enableDeleteItem) { + core.drawTip("不支持删除道具!"); + return; + } + core.removeItem(core.status.event.data); + core.status.event.data = null; + core.ui.drawToolbox(); + return; + } + } ////// 存读档界面时的点击操作 ////// events.prototype.clickSL = function(x,y) { + + var index=core.status.event.data; + var page = parseInt(index/10), offset=index%10; + // 上一页 if ((x == 3 || x == 4) && y == 12) { - core.ui.drawSLPanel(core.status.event.data - 6); + core.ui.drawSLPanel(10*(page-1)+offset); } // 下一页 if ((x == 8 || x == 9) && y == 12) { - core.ui.drawSLPanel(core.status.event.data + 6); + core.ui.drawSLPanel(10*(page+1)+offset); } // 返回 if (x>=10 && x<=12 && y==12) { @@ -1158,50 +1483,77 @@ events.prototype.clickSL = function(x,y) { return; } - var page=parseInt((core.status.event.data-1)/6); var index=6*page+1; if (y>=1 && y<=4) { - if (x>=1 && x<=3) core.doSL(index, core.status.event.id); - if (x>=5 && x<=7) core.doSL(index+1, core.status.event.id); - if (x>=9 && x<=11) core.doSL(index+2, core.status.event.id); + if (x>=1 && x<=3) core.doSL("autoSave", core.status.event.id); + if (x>=5 && x<=7) core.doSL(5*page+1, core.status.event.id); + if (x>=9 && x<=11) core.doSL(5*page+2, core.status.event.id); } if (y>=7 && y<=10) { - if (x>=1 && x<=3) core.doSL(index+3, core.status.event.id); - if (x>=5 && x<=7) core.doSL(index+4, core.status.event.id); - if (x>=9 && x<=11) core.doSL(index+5, core.status.event.id); + if (x>=1 && x<=3) core.doSL(5*page+3, core.status.event.id); + if (x>=5 && x<=7) core.doSL(5*page+4, core.status.event.id); + if (x>=9 && x<=11) core.doSL(5*page+5, core.status.event.id); } } ////// 存读档界面时,按下某个键的操作 ////// events.prototype.keyDownSL = function(keycode) { + + var index=core.status.event.data; + var page = parseInt(index/10), offset=index%10; + if (keycode==37) { // left - core.ui.drawSLPanel(core.status.event.data - 1); + if (offset==0) { + core.ui.drawSLPanel(10*(page-1) + 5); + } + else { + core.ui.drawSLPanel(index - 1); + } return; } if (keycode==38) { // up - core.ui.drawSLPanel(core.status.event.data - 3); + if (offset<3) { + core.ui.drawSLPanel(10*(page-1) + offset + 3); + } + else { + core.ui.drawSLPanel(index - 3); + } return; } if (keycode==39) { // right - core.ui.drawSLPanel(core.status.event.data + 1); + if (offset==5) { + core.ui.drawSLPanel(10*(page+1)+1); + } + else { + core.ui.drawSLPanel(index + 1); + } return; } if (keycode==40) { // down - core.ui.drawSLPanel(core.status.event.data + 3); + if (offset>=3) { + core.ui.drawSLPanel(10*(page+1) + offset - 3); + } + else { + core.ui.drawSLPanel(index + 3); + } return; } if (keycode==33) { // PAGEUP - core.ui.drawSLPanel(core.status.event.data - 6); + core.ui.drawSLPanel(10*(page-1) + offset); return; } if (keycode==34) { // PAGEDOWN - core.ui.drawSLPanel(core.status.event.data + 6); + core.ui.drawSLPanel(10*(page+1) + offset); return; } } ////// 存读档界面时,放开某个键的操作 ////// events.prototype.keyUpSL = function (keycode) { + + var index=core.status.event.data; + var page = parseInt(index/10), offset=index%10; + if (keycode==27 || keycode==88 || (core.status.event.id == 'save' && keycode==83) || (core.status.event.id == 'load' && keycode==68)) { core.ui.closePanel(); if (!core.isPlaying()) { @@ -1210,7 +1562,12 @@ events.prototype.keyUpSL = function (keycode) { return; } if (keycode==13 || keycode==32 || keycode==67) { - core.doSL(core.status.event.data, core.status.event.id); + if (offset==0) { + core.doSL("autoSave", core.status.event.id); + } + else { + core.doSL(5*page+offset, core.status.event.id); + } return; } } @@ -1218,9 +1575,7 @@ events.prototype.keyUpSL = function (keycode) { ////// 系统设置界面时的点击操作 ////// events.prototype.clickSwitchs = function (x,y) { if (x<5 || x>7) return; - var choices = [ - "背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单" - ]; + var choices = core.status.event.ui.choices; var topIndex = 6 - parseInt((choices.length - 1) / 2); if (y>=topIndex && y=choices.length) core.status.event.selection=choices.length-1; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -1290,12 +1643,10 @@ events.prototype.keyDownSwitchs = function (keycode) { events.prototype.keyUpSwitchs = function (keycode) { if (keycode==27 || keycode==88) { core.status.event.selection=0; - core.ui.drawSettings(false); + core.ui.drawSettings(); return; } - var choices = [ - "背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单" - ]; + var choices = core.status.event.ui.choices; if (keycode==13 || keycode==32 || keycode==67) { var topIndex = 6 - parseInt((choices.length - 1) / 2); this.clickSwitchs(6, topIndex+core.status.event.selection); @@ -1306,9 +1657,7 @@ events.prototype.keyUpSwitchs = function (keycode) { ////// 系统菜单栏界面时的点击事件 ////// events.prototype.clickSettings = function (x,y) { if (x<5 || x>7) return; - var choices = [ - "系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏" - ]; + var choices = core.status.event.ui.choices; var topIndex = 6 - parseInt((choices.length - 1) / 2); if (y>=topIndex && y0) { + text+="\n当前MAX为"+ending.max+",最早由 "+(ending.username||"匿名")+" 于"+core.formatDate(new Date(1000*ending.timestamp))+"打出。"; + } + }) + }) + core.drawText(text); + } + } + else { + core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:HTTP "+xhr.status); + } + }; + xhr.ontimeout = function() { + core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:Timeout"); + } + xhr.onerror = function() { + core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:XHR Error"); + } + xhr.send(formData); break; case 6: + core.ui.drawHelp(); + break; + case 7: + core.ui.drawAbout(); + break; + case 8: core.ui.closePanel(); break; } @@ -1352,17 +1760,12 @@ events.prototype.clickSettings = function (x,y) { ////// 系统菜单栏界面时,按下某个键的操作 ////// events.prototype.keyDownSettings = function (keycode) { - var choices = [ - "系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏" - ]; if (keycode==38) { core.status.event.selection--; - if (core.status.event.selection<0) core.status.event.selection=0; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } if (keycode==40) { core.status.event.selection++; - if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -1373,9 +1776,7 @@ events.prototype.keyUpSettings = function (keycode) { core.ui.closePanel(); return; } - var choices = [ - "系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏" - ]; + var choices = core.status.event.ui.choices; if (keycode==13 || keycode==32 || keycode==67) { var topIndex = 6 - parseInt((choices.length - 1) / 2); this.clickSettings(6, topIndex+core.status.event.selection); @@ -1385,32 +1786,104 @@ events.prototype.keyUpSettings = function (keycode) { ////// 同步存档界面时的点击操作 ////// events.prototype.clickSyncSave = function (x,y) { if (x<5 || x>7) return; - var choices = [ - "同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单" - ]; + var choices = core.status.event.ui.choices; var topIndex = 6 - parseInt((choices.length - 1) / 2); if (y>=topIndex && y=1;i--) { + if (core.getLocalStorage("save"+i, null)==null) + index=i; + else break; + } + core.setLocalStorage("save"+index, data); + core.drawText("同步成功!\n单存档已覆盖至存档"+index); + } + }, function () { + + }); + break; + case 4: + core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify({ + 'name': core.firstData.name, + 'hard': core.status.hard, + 'route': core.encodeRoute(core.status.route) + })); + break; + case 5: + core.status.event.selection=1; + core.ui.drawConfirmBox("你确定要清空所有存档吗?", function() { + localStorage.clear(); + core.drawText("\t[操作成功]你的所有存档已被清空。"); + }, function() { + core.status.event.selection=5; + core.ui.drawSyncSave(); + }) + break; + case 6: + core.status.event.selection=3; + core.ui.drawSettings(); break; } @@ -1420,17 +1893,12 @@ events.prototype.clickSyncSave = function (x,y) { ////// 同步存档界面时,按下某个键的操作 ////// events.prototype.keyDownSyncSave = function (keycode) { - var choices = [ - "同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单" - ]; if (keycode==38) { core.status.event.selection--; - if (core.status.event.selection<0) core.status.event.selection=0; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } if (keycode==40) { core.status.event.selection++; - if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } @@ -1439,18 +1907,245 @@ events.prototype.keyDownSyncSave = function (keycode) { events.prototype.keyUpSyncSave = function (keycode) { if (keycode==27 || keycode==88) { core.status.event.selection=2; - core.ui.drawSettings(false); + core.ui.drawSettings(); return; } - var choices = [ - "同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单" - ]; + var choices = core.status.event.ui.choices; if (keycode==13 || keycode==32 || keycode==67) { var topIndex = 6 - parseInt((choices.length - 1) / 2); this.clickSyncSave(6, topIndex+core.status.event.selection); } } +////// 同步存档选择界面时的点击操作 ////// +events.prototype.clickSyncSelect = function (x, y) { + if (x<5 || x>7) return; + var choices = core.status.event.ui.choices; + + var topIndex = 6 - parseInt((choices.length - 1) / 2); + if (y>=topIndex && y7) return; + var choices = core.status.event.ui.choices; + + var topIndex = 6 - parseInt((choices.length - 1) / 2); + + var saves=null; + + if (y>=topIndex && y=1;i--) { + saves=core.getLocalStorage("save"+i, null); + if (core.isset(saves)) { + break; + } + } + break; + case 2: + break; + } + } + if (core.isset(saves)) { + var content = { + "name": core.firstData.name, + "version": core.firstData.version, + "data": saves + } + core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5save", JSON.stringify(content)); + } + core.status.event.selection=2; + core.ui.drawSyncSave(); +} + +////// 存档下载界面时,按下某个键的操作 ////// +events.prototype.keyDownLocalSaveSelect = function (keycode) { + if (keycode==38) { + core.status.event.selection--; + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); + } + if (keycode==40) { + core.status.event.selection++; + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); + } +} + +////// 存档下载界面时,放开某个键的操作 ////// +events.prototype.keyUpLocalSaveSelect = function (keycode) { + if (keycode==27 || keycode==88) { + core.status.event.selection=0; + core.ui.drawSettings(); + return; + } + var choices = core.status.event.ui.choices; + if (keycode==13 || keycode==32 || keycode==67) { + var topIndex = 6 - parseInt((choices.length - 1) / 2); + this.clickLocalSaveSelect(6, topIndex+core.status.event.selection); + } +} + +////// “虚拟键盘”界面时的点击操作 ////// +events.prototype.clickKeyBoard = function (x, y) { + if (y==3 && x>=1 && x<=11) { + core.ui.closePanel(); + core.keyUp(112+x-1); // F1-F12: 112-122 + } + if (y==4 && x>=1 && x<=10) { + core.ui.closePanel(); + core.keyUp(x==10?48:48+x); // 1-9: 49-57; 0: 48 + } + // 字母 + var lines = [ + ["Q","W","E","R","T","Y","U","I","O","P"], + ["A","S","D","F","G","H","J","K","L"], + ["Z","X","C","V","B","N","M"], + ]; + if (y==5 && x>=1 && x<=10) { + core.ui.closePanel(); + core.keyUp(lines[0][x-1].charCodeAt(0)); + } + if (y==6 && x>=1 && x<=9) { + core.ui.closePanel(); + core.keyUp(lines[1][x-1].charCodeAt(0)); + } + if (y==7 && x>=1 && x<=7) { + core.ui.closePanel(); + core.keyUp(lines[2][x-1].charCodeAt(0)); + } + if (y==8 && x>=1 && x<=11) { + core.ui.closePanel(); + if (x==1) core.keyUp(189); // - + if (x==2) core.keyUp(187); // = + if (x==3) core.keyUp(219); // [ + if (x==4) core.keyUp(221); // ] + if (x==5) core.keyUp(220); // \ + if (x==6) core.keyUp(186); // ; + if (x==7) core.keyUp(222); // ' + if (x==8) core.keyUp(188); // , + if (x==9) core.keyUp(190); // . + if (x==10) core.keyUp(191); // / + if (x==11) core.keyUp(192); // ` + } + if (y==9 && x>=1 && x<=10) { + core.ui.closePanel(); + if (x==1) core.keyUp(27); // ESC + if (x==2) core.keyUp(9); // TAB + if (x==3) core.keyUp(20); // CAPS + if (x==4) core.keyUp(16); // SHIFT + if (x==5) core.keyUp(17); // CTRL + if (x==6) core.keyUp(18); // ALT + if (x==7) core.keyUp(32); // SPACE + if (x==8) core.keyUp(8); // BACKSPACE + if (x==9) core.keyUp(13); // ENTER + if (x==10) core.keyUp(46); // DEL + } + if (y==10 && x>=9 && x<=11) + core.ui.closePanel(); +} + +////// 光标界面时的点击操作 ////// +events.prototype.clickCursor = function (x,y) { + + if (x==core.status.automaticRoute.cursorX && y==core.status.automaticRoute.cursorY) { + core.ui.closePanel(); + core.onclick(x,y,[]); + return; + } + core.status.automaticRoute.cursorX=x; + core.status.automaticRoute.cursorY=y; + core.ui.drawCursor(); +} + +////// 光标界面时,按下某个键的操作 ////// +events.prototype.keyDownCursor = function (keycode) { + if (keycode==37) { // left + core.status.automaticRoute.cursorX--; + core.ui.drawCursor(); + return; + } + if (keycode==38) { // up + core.status.automaticRoute.cursorY--; + core.ui.drawCursor(); + return; + } + if (keycode==39) { // right + core.status.automaticRoute.cursorX++; + core.ui.drawCursor(); + return; + } + if (keycode==40) { // down + core.status.automaticRoute.cursorY++; + core.ui.drawCursor(); + return; + } +} + +////// 光标界面时,放开某个键的操作 ////// +events.prototype.keyUpCursor = function (keycode) { + if (keycode==27 || keycode==88) { + core.ui.closePanel(); + return; + } + if (keycode==13 || keycode==32 || keycode==67 || keycode==69) { + core.ui.closePanel(); + core.onclick(core.status.automaticRoute.cursorX, core.status.automaticRoute.cursorY, []); + return; + } +} + ////// “关于”界面时的点击操作 ////// events.prototype.clickAbout = function () { if (core.isPlaying()) diff --git a/libs/items.js b/libs/items.js index aefa28ae..0422392d 100644 --- a/libs/items.js +++ b/libs/items.js @@ -20,6 +20,19 @@ items.prototype.getItems = function () { this.items.pickaxe.text = "可以破坏勇士四周的墙"; if (core.flags.bombFourDirections) this.items.bomb.text = "可以炸掉勇士四周的怪物"; + if (core.flags.equipment) { + this.items.sword1 = {'cls': 'constants', 'name': '铁剑', 'text': '一把很普通的铁剑,攻击+'+core.values.sword1}; + this.items.sword2 = {'cls': 'constants', 'name': '银剑', 'text': '一把很普通的银剑,攻击+'+core.values.sword2}; + this.items.sword3 = {'cls': 'constants', 'name': '骑士剑', 'text': '一把很普通的骑士剑,攻击+'+core.values.sword3}; + this.items.sword4 = {'cls': 'constants', 'name': '圣剑', 'text': '一把很普通的圣剑,攻击+'+core.values.sword4}; + this.items.sword5 = {'cls': 'constants', 'name': '神圣剑', 'text': '一把很普通的神圣剑,攻击+'+core.values.sword5}; + this.items.shield1 = {'cls': 'constants', 'name': '铁盾', 'text': '一个很普通的铁盾,防御+'+core.values.shield1}; + this.items.shield2 = {'cls': 'constants', 'name': '银盾', 'text': '一个很普通的银盾,防御+'+core.values.shield2}; + this.items.shield3 = {'cls': 'constants', 'name': '骑士盾', 'text': '一个很普通的骑士盾,防御+'+core.values.shield3}; + this.items.shield4 = {'cls': 'constants', 'name': '圣盾', 'text': '一个很普通的圣盾,防御+'+core.values.shield4}; + this.items.shield5 = {'cls': 'constants', 'name': '神圣盾', 'text': '一个很普通的神圣盾,防御+'+core.values.shield5}; + } + return this.items; } @@ -39,13 +52,18 @@ items.prototype.getItemEffect = function(itemId, itemNum) { ////// “即捡即用类”道具的文字提示 ////// items.prototype.getItemEffectTip = function(itemId) { - if (itemId in this.itemEffectTip)return eval(this.itemEffectTip[itemId]); + if (itemId in this.itemEffectTip && (!this.items[itemId].isEquipment || !core.flags.equipment)) { + return eval(this.itemEffectTip[itemId]); + } return ""; } ////// 使用道具 ////// -items.prototype.useItem = function (itemId) { - if (!this.canUseItem(itemId)) return; +items.prototype.useItem = function (itemId, callback) { + if (!this.canUseItem(itemId)) { + if (core.isset(callback)) callback(); + return; + } var itemCls = core.material.items[itemId].cls; if (itemId=='book') core.ui.drawBook(0); @@ -75,6 +93,7 @@ items.prototype.useItem = function (itemId) { // 上楼器/下楼器 core.changeFloor(core.status.event.data.id, null, {'direction': core.status.hero.loc.direction, 'x': core.status.event.data.x, 'y': core.status.event.data.y}, null, function (){ core.drawTip(core.material.items[itemId].name + "使用成功"); + core.replay(); }); } if (itemId == 'poisonWine') core.setFlag('poison', false); @@ -93,12 +112,42 @@ items.prototype.useItem = function (itemId) { } core.setFlag('curse', false); } + + // 剑 + if (itemId.indexOf("sword")==0) { + var now=core.getFlag('sword', 'sword0'); // 当前装备剑的ID + core.status.hero.atk -= core.values[now]; + core.setItem(now, 1); + core.status.hero.atk += core.values[itemId]; + core.setItem(itemId, 0); + core.setFlag('sword', itemId); + core.drawTip("已装备"+core.material.items[itemId].name); + } + // 盾 + if (itemId.indexOf("shield")==0) { + var now=core.getFlag('shield', 'shield0'); + core.status.hero.def -= core.values[now]; + core.setItem(now, 1); + core.status.hero.def += core.values[itemId]; + core.setItem(itemId, 0); + core.setFlag('shield', itemId); + core.drawTip("已装备"+core.material.items[itemId].name); + } + core.updateStatusBar(); + + // 记录路线 + if (itemId!='book' && itemId!='fly') { + core.status.route.push("item:"+itemId); + } + // 道具使用完毕:删除 if (itemCls=='tools') core.status.hero.items[itemCls][itemId]--; if (core.status.hero.items[itemCls][itemId]==0) delete core.status.hero.items[itemCls][itemId]; + + if (core.isset(callback)) callback(); } ////// 当前能否使用道具 ////// @@ -264,5 +313,9 @@ items.prototype.canUseItem = function (itemId) { if (itemId=='weakWine') return core.hasFlag('weak'); if (itemId=='curseWine') return core.hasFlag('curse'); if (itemId=='superWine') return core.hasFlag('poison') || core.hasFlag('weak') || core.hasFlag('curse'); + + // 剑盾 + if (itemId.indexOf("sword")==0 || itemId.indexOf("shield")==0) return true; + return false; } diff --git a/libs/maps.js b/libs/maps.js index 474a2bec..410314ed 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -18,28 +18,8 @@ maps.prototype.loadFloor = function (floorId, map) { for (var i = 0; i < 13; i++) { for (var j = 0; j < 13; j++) { var block = maps.getBlock(j, i, map[i][j]); - if (core.isset(block.event)) { - if (block.event.cls == 'enemys' && block.event.trigger==undefined) { - block.event.trigger = 'battle'; - } - if (block.event.cls == 'items' && block.event.trigger==undefined) { - block.event.trigger = 'getItem'; - } - if (block.event.noPass == undefined) { - if (block.event.cls=='enemys' || block.event.cls=='terrains' || block.event.cls=='npcs') { - block.event.noPass = true; - } - } - if (block.event.animate == undefined) { - if (block.event.cls=='enemys' || block.event.cls=='npcs') { - block.event.animate = 2; - } - if (block.event.cls == 'animates') { - block.event.animate = 4; - } - } - } - maps.addEvent(block,j,i,floor.events[j+","+i]); + maps.addInfo(block); + maps.addEvent(block,j,i,floor.events[j+","+i]) maps.addChangeFloor(block,j,i,floor.changeFloor[j+","+i]); if (core.isset(block.event)) blocks.push(block); } @@ -79,6 +59,31 @@ maps.prototype.getBlock = function (x, y, id) { return tmp; } +////// 添加一些信息到block上 ////// +maps.prototype.addInfo = function (block) { + if (core.isset(block.event)) { + if (block.event.cls == 'enemys' && block.event.trigger==undefined) { + block.event.trigger = 'battle'; + } + if (block.event.cls == 'items' && block.event.trigger==undefined) { + block.event.trigger = 'getItem'; + } + if (block.event.noPass == undefined) { + if (block.event.cls=='enemys' || block.event.cls=='terrains' || block.event.cls=='npcs') { + block.event.noPass = true; + } + } + if (block.event.animate == undefined) { + if (block.event.cls=='enemys' || block.event.cls=='npcs') { + block.event.animate = 2; + } + if (block.event.cls == 'animates') { + block.event.animate = 4; + } + } + } +} + ////// 向该楼层添加剧本的自定义事件 ////// maps.prototype.addEvent = function (block, x, y, event) { if (!core.isset(event)) return; @@ -178,16 +183,7 @@ maps.prototype.load = function (data, floorId) { } ////// 将当前地图重新变成二维数组形式 ////// -maps.prototype.getMapArray = function (maps, floorId){ - if (!core.isset(floorId)) { - var map = {}; - for (var id in maps) { - map[id] = this.getMapArray(maps, id); - } - return map; - } - - var thisFloor = maps[floorId]; +maps.prototype.getMapArray = function (blockArray){ var blocks = []; for (var x=0;x<13;x++) { @@ -196,7 +192,7 @@ maps.prototype.getMapArray = function (maps, floorId){ blocks[x].push(0); } } - thisFloor.blocks.forEach(function (block) { + blockArray.forEach(function (block) { if (!(core.isset(block.enable) && !block.enable)) blocks[block.y][block.x] = block.id; }); diff --git a/libs/thirdparty/mid.min.js b/libs/thirdparty/mid.min.js new file mode 100644 index 00000000..62466cc1 --- /dev/null +++ b/libs/thirdparty/mid.min.js @@ -0,0 +1 @@ +var sampleRate=44100;function AudioPlayer(b,f,a){sampleRate=b.sampleRate;var c=2;var g=4096*4;var d=b.createScriptProcessor(g,0,c);d.onaudioprocess=function(h){e(h)};function e(m){if(f.finished){if(a){f.reset();f.finished=false}else{d.disconnect();return}}var l=m.outputBuffer.getChannelData(0);var k=m.outputBuffer.getChannelData(1);var h=f.generate(g);for(var j=0;j>4;u.channel=t&15;u.type="channel";switch(i){case 8:u.subtype="noteOff";u.noteNumber=v;u.velocity=w.readInt8();return u;case 9:u.noteNumber=v;u.velocity=w.readInt8();if(u.velocity==0){u.subtype="noteOff"}else{u.subtype="noteOn"}return u;case 10:u.subtype="noteAftertouch";u.noteNumber=v;u.amount=w.readInt8();return u;case 11:u.subtype="controller";u.controllerType=v;u.value=w.readInt8();return u;case 12:u.subtype="programChange";u.programNumber=v;return u;case 13:u.subtype="channelAftertouch";u.amount=v;return u;case 14:u.subtype="pitchBend";u.value=v+(w.readInt8()<<7);return u;default:throw"Unrecognised MIDI event type: "+i}}}stream=Stream(g);var k=a(stream);if(k.id!="MThd"||k.length!=6){throw"Bad .mid file - header not found"}var f=Stream(k.data);var d=f.readInt16();var h=f.readInt16();var p=f.readInt16();if(p&32768){throw"Expressing time division in SMTPE frames is not supported yet"}else{ticksPerBeat=p}var j={"formatType":d,"trackCount":h,"ticksPerBeat":ticksPerBeat};var m=[];for(var e=0;e0){c.generateIntoBuffer(r,t,p);p+=r*2;s-=r;e-=r}i();g()}else{if(s>0){c.generateIntoBuffer(s,t,p);e-=s}break}}return t}function i(){var p=m.event;switch(p.type){case"meta":switch(p.subtype){case"setTempo":o=60000000/p.microsecondsPerBeat}break;case"channel":switch(p.subtype){case"noteOn":d[p.channel].noteOn(p.noteNumber,p.velocity);break;case"noteOff":d[p.channel].noteOff(p.noteNumber,p.velocity);break;case"programChange":d[p.channel].setProgram(p.programNumber);break}break}}function f(){for(var p=0;p127){i-=256}b+=1;return i}function c(){return b>=g.length}function d(){var j=0;while(true){var i=h();if(i&128){j+=(i&127);j<<=7}else{return j+i}}}return{"eof":c,"read":e,"readInt32":f,"readInt16":a,"readInt8":h,"readVarInt":d}}function SineGenerator(c){var a={"alive":true};var d=sampleRate/c;var b=0;a.generate=function(g,i,h){for(;h;h--){var f=b/d;var e=Math.sin(f*2*Math.PI);g[i++]+=e;g[i++]+=e;b++}};return a}function SquareGenerator(d,a){var b={"alive":true};var e=sampleRate/d;var c=0;b.generate=function(g,i,h){for(;h;h--){var f=((c/e)%1>a?1:-1);g[i++]+=f;g[i++]+=f;c++}};return b}function ADSRGenerator(c,i,k,h,l,j){var n={"alive":true};var f=sampleRate*h;var g=sampleRate*(h+l);var b=(i-k)/(g-f);var a=null;var e=null;var d=k/(sampleRate*j);var m=0;n.noteOff=function(){if(n.released){return}a=m;n.released=true;e=a+sampleRate*j};n.generate=function(p,t,s){if(!n.alive){return}var o=new Array(s*2);for(var r=0;r=0;g--){c[g].generate(f,j,h);if(!c[g].alive){c.splice(g,1)}}}return{"sampleRate":b,"addGenerator":e,"generate":a,"generateIntoBuffer":d}}; \ No newline at end of file diff --git a/libs/ui.js b/libs/ui.js index f3635f61..5ac798bc 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -15,7 +15,6 @@ main.instance.ui = new ui(); ////// 结束一切事件和绘制,关闭UI窗口,返回游戏进程 ////// ui.prototype.closePanel = function () { core.status.boxAnimateObjs = []; - core.setBoxAnimate(); core.clearMap('ui', 0, 0, 416, 416); core.setAlpha('ui', 1.0); core.unLockControl(); @@ -30,10 +29,11 @@ ui.prototype.drawTextBox = function(content) { // 获得name, image, icon var id=null, name=null, image=null, icon=null; - 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) { @@ -64,10 +64,55 @@ ui.prototype.drawTextBox = function(content) { } } + // 获得位置信息 + + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; + + var position = textAttribute.position, px=null, py=null, ydelta=0; + if (content.indexOf("\b[")==0 || content.indexOf("\\b[")==0) { + var index = content.indexOf("]"); + if (index>=0) { + var str = content.substring(2, index); + if (content.indexOf("\\b[")==0) str = content.substring(3, index); + content = content.substring(index + 1); + + var ss=str.split(","); + + if (ss[0]=='up' || ss[0]=='center' || ss[0]=='down') { + position=ss[0]; + if (core.status.event.id=='action') { + px = core.status.event.data.x; + py = core.status.event.data.y; + } + + if (ss.length>=2) { + if (ss[1]=='hero') { + px=core.getHeroLoc('x'); + py=core.getHeroLoc('y'); + ydelta = core.material.icons.hero.height-32; + } + else if (ss.length>=3) { + px=parseInt(ss[1]); + py=parseInt(ss[2]); + } + } + } + /* + if (ss.length==3) { + px=parseInt(ss[1]); + py=parseInt(ss[2]); + } + */ + + } + } + content = core.replaceText(content); var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); + core.status.boxAnimateObjs = []; core.clearMap('ui', 0, 0, 416, 416); + // var contents = content.split('\n'); // var contents = core.splitLines('ui', content, ); var left=10, right=416-2*left; @@ -77,16 +122,74 @@ ui.prototype.drawTextBox = function(content) { var validWidth = right-(content_left-left)-13; var contents = core.splitLines("ui", content, validWidth, '16px Verdana'); - var height = 416 - 10 - Math.min(416-24*(contents.length+1)-65, 250); - var top = (416-height)/2, bottom = height; + var height = 20 + 21*(contents.length+1) + (id=='hero'?core.material.icons.hero.height-10:core.isset(name)?32-10:0); + + + var xoffset = 6, yoffset = 22; + + var top; + if (position=='center') { + top = (416 - height) / 2; + } + else if (position=='up') { + if (px==null || py==null) { + top = 5; + } + else { + top = 32 * py - height - ydelta - yoffset; + } + } + else if (position=='down') { + if (px==null || py==null) { + top = 416 - height - 5; + } + else { + top = 32 * py + 32 + yoffset; + } + } // var left = 97, top = 64, right = 416 - 2 * left, bottom = 416 - 2 * top; - core.setAlpha('ui', 0.85); - core.fillRect('ui', left, top, right, bottom, '#000000'); - core.setAlpha('ui', 1); - core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); - core.status.boxAnimateObjs = []; - core.setBoxAnimate(); + //core.setAlpha('ui', 0.85); + core.setAlpha('ui', textAttribute.background[3]); + core.setFillStyle('ui', core.arrayToRGB(textAttribute.background)); + core.setStrokeStyle('ui', '#FFFFFF'); + + + core.fillRect('ui', left, top, right, height); + core.strokeRect('ui', left - 1, top - 1, right + 1, height + 1, '#FFFFFF', 2); + + var xoffset = 6; + + // draw triangle + if (position=='up' && core.isset(px) && core.isset(py)) { + core.canvas.ui.clearRect(32*px+xoffset, top+height-1, 32-2*xoffset, 2); + core.canvas.ui.beginPath(); + core.canvas.ui.moveTo(32*px+xoffset-1, top+height-1); + core.canvas.ui.lineTo(32*px+16, top+height+yoffset-2); + core.canvas.ui.lineTo(32*px+32-xoffset+1, top+height-1); + core.canvas.ui.moveTo(32*px+xoffset-1, top+height-1); + core.canvas.ui.closePath(); + core.canvas.ui.fill(); + // core.canvas.ui.stroke(); + // core.drawLine('ui', 32*px+4+1, top+height+1, 32*px + 28-1, top+height+1, core.arrayToRGB(textAttribute.background), 3); + core.drawLine('ui', 32*px+xoffset, top+height, 32*px+16, top+height+yoffset-2); + core.drawLine('ui', 32*px+32-xoffset, top+height, 32*px+16, top+height+yoffset-2); + } + if (position=='down' && core.isset(px) && core.isset(py)) { + core.canvas.ui.clearRect(32*px+xoffset, top-2, 32-2*xoffset, 3); + core.canvas.ui.beginPath(); + core.canvas.ui.moveTo(32*px+xoffset-1, top+1); + core.canvas.ui.lineTo(32*px+16-1, top-yoffset+2); + core.canvas.ui.lineTo(32*px+32-xoffset-1, top+1); + core.canvas.ui.moveTo(32*px+xoffset-1, top+1); + core.canvas.ui.closePath(); + core.canvas.ui.fill(); + // core.canvas.ui.stroke(); + // core.drawLine('ui', 32*px+4+1, top+height+1, 32*px + 28-1, top+height+1, core.arrayToRGB(textAttribute.background), 3); + core.drawLine('ui', 32*px+xoffset, top, 32*px+16, top-yoffset+2); + core.drawLine('ui', 32*px+32-xoffset, top, 32*px+16, top-yoffset+2); + } + // 名称 core.canvas.ui.textAlign = "left"; @@ -95,36 +198,42 @@ ui.prototype.drawTextBox = function(content) { if (core.isset(id)) { content_top = top+57; + core.setAlpha('ui', textAttribute.title[3]); + core.setFillStyle('ui', core.arrayToRGB(textAttribute.title)); + core.setStrokeStyle('ui', core.arrayToRGB(textAttribute.title)); if (id == 'hero') { var heroHeight=core.material.icons.hero.height; - core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, heroHeight+2, '#FFD700', 2); - core.fillText('ui', core.status.hero.name, content_left, top + 30, '#FFD700', 'bold 22px Verdana'); + 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.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']; core.canvas.ui.drawImage(core.material.images.hero, heroIcon.stop * 32, heroIcon.loc * heroHeight, 32, heroHeight, left+15, top+40, 32, heroHeight); } else { - core.fillText('ui', name, content_left, top + 30, '#FFD700', 'bold 22px Verdana'); + core.fillText('ui', name, content_left, top + 30, null, 'bold 22px Verdana'); if (core.isset(icon)) { - core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, 34, '#FFD700', 2); + core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, 34, null, 2); core.status.boxAnimateObjs = []; core.status.boxAnimateObjs.push({ 'bgx': left + 15, 'bgy': top + 40, 'bgsize': 32, 'image': image, 'x': left + 15, 'y': top + 40, 'icon': icon }); - core.setBoxAnimate(); + core.drawBoxAnimate(); } } } + core.setAlpha('ui', textAttribute.text[3]); + core.setFillStyle('ui', core.arrayToRGB(textAttribute.text)); + for (var i=0;i', 270, top+height-13, '#CCCCCC', '13px Verdana'); + // core.fillText('ui', '<点击任意位置继续>', 270, top+height-13, '#CCCCCC', '13px Verdana'); } ////// 绘制一个选项界面 ////// @@ -237,7 +346,7 @@ ui.prototype.drawChoices = function(content, choices) { 'bgx': left + 15, 'bgy': top + 30, 'bgsize': 32, 'image': image, 'x': left + 15, 'y': top + 30, 'icon': icon }); - core.setBoxAnimate(); + core.drawBoxAnimate(); } } } @@ -257,6 +366,8 @@ ui.prototype.drawChoices = function(content, choices) { if (choices.length>0) { if (!core.isset(core.status.event.selection)) core.status.event.selection=0; + if (core.status.event.selection<0) core.status.event.selection=0; + if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1; var len = core.canvas.ui.measureText(core.replaceText(choices[core.status.event.selection].text || choices[core.status.event.selection])).width; core.strokeRect('ui', 208-len/2-5, choice_top + 32 * core.status.event.selection - 20, len+10, 28, "#FFD700", 2); } @@ -271,7 +382,8 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) { core.status.event.data = {'yes': yesCallback, 'no': noCallback}; core.status.event.ui = text; - if (!core.isset(core.status.event.selection)) core.status.event.selection=1; + if (!core.isset(core.status.event.selection) || core.status.event.selection>1) core.status.event.selection=1; + if (core.status.event.selection<0) core.status.event.selection=0; var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); core.clearMap('ui', 0, 0, 416, 416); @@ -319,29 +431,26 @@ ui.prototype.drawSwitchs = function() { var choices = [ "背景音乐:"+(core.musicStatus.bgmStatus ? "[ON]" : "[OFF]"), "背景音效:"+(core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), - "战斗动画: " + (core.flags.battleAnimate ? "[ON]" : "[OFF]"), - "怪物显伤: " + (core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"), - "领域显伤: " + (core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), + "战斗动画: "+(core.flags.battleAnimate ? "[ON]" : "[OFF]"), + "怪物显伤: "+(core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"), + "领域显伤: "+(core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), + "下载离线版本", "返回主菜单" ]; this.drawChoices(null, choices); - } ////// 绘制系统菜单栏 ////// -ui.prototype.drawSettings = function (need) { - if (!core.checkStatus('settings', need)) - return; +ui.prototype.drawSettings = function () { + core.status.event.id = 'settings'; this.drawChoices(null, [ - "系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏" + "系统设置", "快捷商店", "浏览地图", "同步存档", "重新开始", "数据统计", "操作帮助", "关于本塔", "返回游戏" ]); } ////// 绘制快捷商店选择栏 ////// -ui.prototype.drawQuickShop = function (need) { - if (core.isset(need) && !core.checkStatus('selectShop', need)) - return; +ui.prototype.drawQuickShop = function () { core.status.event.id = 'selectShop'; @@ -369,6 +478,22 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { var monster = core.material.enemys[monsterId]; var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_money=monster.money, mon_exp = monster.experience, mon_special=monster.special; + var initDamage = 0; // 战前伤害 + + // 吸血 + if (core.enemys.hasSpecial(mon_special, 11)) { + var vampireDamage = hero_hp * monster.value; + + // 如果有神圣盾免疫吸血等可以在这里写 + + vampireDamage = parseInt(vampireDamage); + // 加到自身 + if (monster.add) // 如果加到自身 + mon_hp += vampireDamage; + + initDamage += vampireDamage; + } + hero_hp -= core.enemys.getExtraDamage(monster); if (core.enemys.hasSpecial(mon_special, 10)) { // 模仿 @@ -388,16 +513,9 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { if (core.enemys.hasSpecial(mon_special, 5)) turns=4; if (core.enemys.hasSpecial(mon_special, 6)) turns=1+(monster.n||4); - // 初始伤害 - var initDamage = 0; if (core.enemys.hasSpecial(mon_special, 7)) initDamage+=parseInt(core.values.breakArmor * hero_def); if (core.enemys.hasSpecial(mon_special, 9)) initDamage+=parseInt(core.values.purify * hero_mdef); - if (core.enemys.hasSpecial(mon_special, 11)) { // 吸血 - var extraDamage = monster.value * hero_hp; - initDamage+=parseInt(extraDamage); - } - if (core.enemys.hasSpecial(mon_special, 17)) initDamage+=core.getFlag('hatred', 0); hero_mdef-=initDamage; if (hero_mdef<0) { hero_hp+=hero_mdef; @@ -433,7 +551,6 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { core.setAlpha('data', 1); core.setOpacity('data', 1); core.status.boxAnimateObjs = []; - core.setBoxAnimate(); var margin = 35; var boxWidth = 40; @@ -464,7 +581,7 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { 'bgx': left + right - margin - 40, 'bgy': top+margin, 'bgsize': boxWidth, 'image': core.material.images.enemys, 'x': left + right - margin - 40 + (boxWidth-32)/2, 'y': top + margin + (boxWidth-32)/2, 'icon': core.material.icons.enemys[monsterId] }); - core.setBoxAnimate(); + core.drawBoxAnimate(); var lineWidth = 80; @@ -627,7 +744,6 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { // 战斗结束 clearInterval(battleInterval); core.status.boxAnimateObjs = []; - core.setBoxAnimate(); core.clearMap('ui', 0, 0, 416, 416); core.setAlpha('ui', 1.0); core.clearMap('data', 0, 0, 416, 416); @@ -654,12 +770,17 @@ ui.prototype.drawWaiting = function(text) { core.setAlpha('ui', 1); core.setFillStyle('ui', background); - var left = 97, top = 208 - 32 - 16, right = 416 - 2 * left, bottom = 416 - 2 * top; + core.setFont('ui', 'bold 17px Verdana'); + var text_length = core.canvas.ui.measureText(text).width; + + var right = Math.max(text_length+50, 220); + var left = 208-right/2, top = 208 - 32 - 16, bottom = 416 - 2 * top; + core.fillRect('ui', left, top, right, bottom, background); core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); core.canvas.ui.textAlign = "center"; - core.fillText('ui', text, 208, top + 56, "#FFFFFF", "bold 17px Verdana"); + core.fillText('ui', text, 208, top + 56, '#FFFFFF'); } @@ -669,11 +790,27 @@ ui.prototype.drawSyncSave = function () { core.status.event.id = 'syncSave'; this.drawChoices(null, [ - "同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单" + "同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "下载当前录像", "清空本地存档", "返回主菜单" ]); } +////// 绘制存档同步选择页面 ////// +ui.prototype.drawSyncSelect = function () { + core.status.event.id = 'syncSelect'; + this.drawChoices(null, [ + "同步本地所有存档", "只同步最新单存档", "返回上级菜单" + ]); +} + +////// 绘制单存档界面 ////// +ui.prototype.drawLocalSaveSelect = function () { + core.status.event.id = 'localSaveSelect'; + this.drawChoices(null, [ + "下载所有存档", "只下载最新单存档", "返回上级菜单" + ]); +} + ////// 绘制分页 ////// ui.prototype.drawPagination = function (page, totalPage) { @@ -696,10 +833,34 @@ ui.prototype.drawPagination = function (page, totalPage) { } +////// 绘制键盘光标 ////// +ui.prototype.drawCursor = function () { + + if (!core.isset(core.status.automaticRoute.cursorX)) + core.status.automaticRoute.cursorX=core.getHeroLoc('x'); + if (core.status.automaticRoute.cursorX<0) core.status.automaticRoute.cursorX=0; + if (core.status.automaticRoute.cursorX>12) core.status.automaticRoute.cursorX=12; + if (!core.isset(core.status.automaticRoute.cursorY)) + core.status.automaticRoute.cursorY=core.getHeroLoc('y'); + if (core.status.automaticRoute.cursorY<0) core.status.automaticRoute.cursorY=0; + if (core.status.automaticRoute.cursorY>12) core.status.automaticRoute.cursorY=12; + + core.status.event.id = 'cursor'; + core.lockControl(); + + core.clearMap('ui', 0, 0, 416, 416); + core.setAlpha('ui', 1); + + var width = 4; + core.strokeRect('ui', 32*core.status.automaticRoute.cursorX+width/2, 32*core.status.automaticRoute.cursorY+width/2, + 32-width, 32-width, '#FFD700', width); + +} + ////// 绘制怪物手册 ////// ui.prototype.drawBook = function (index) { - var enemys = core.enemys.getCurrentEnemys(); + var enemys = core.enemys.getCurrentEnemys(core.floorIds[core.status.event.selection]); var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); clearInterval(core.interval.tipAnimate); @@ -809,13 +970,13 @@ ui.prototype.drawBook = function (index) { } } - core.setBoxAnimate(); + core.drawBoxAnimate(); this.drawPagination(page, totalPage); } ////// 绘制怪物属性的详细信息 ////// ui.prototype.drawBookDetail = function (index) { - var enemys = core.enemys.getCurrentEnemys(); + var enemys = core.enemys.getCurrentEnemys(core.floorIds[core.status.event.selection]); if (enemys.length==0) return; if (index<0) index=0; if (index>=enemys.length) index=enemys.length-1; @@ -902,6 +1063,38 @@ ui.prototype.drawFly = function(page) { this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 20, 100, 273); } +////// 绘制浏览地图界面 ////// +ui.prototype.drawMaps = function (index) { + if (!core.isset(index)) index=core.floorIds.indexOf(core.status.floorId); + + if (index<0) index=0; + if (index>=core.floorIds.length) index=core.floorIds.length-1; + + core.lockControl(); + core.status.event.id = 'viewMaps'; + core.status.event.data = index; + + var floorId = core.floorIds[index]; + + clearTimeout(core.interval.tipAnimate); + + core.clearMap('ui', 0, 0, 416, 416); + core.setAlpha('ui', 1); + this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 0, 0, 416); + + core.clearMap('data', 0, 0, 416, 416); + core.setOpacity('data', 0.2); + core.canvas.data.textAlign = 'left'; + core.setFont('data', '16px Arial'); + + var text = core.floors[floorId].title; + var textX = 16, textY = 18, width = textX + core.canvas.data.measureText(text).width + 16, height = 42; + core.fillRect('data', 5, 5, width, height, '#000'); + core.setOpacity('data', 0.5); + core.fillText('data', text, textX + 5, textY + 15, '#fff'); + +} + ////// 绘制道具栏 ////// ui.prototype.drawToolbox = function(index) { @@ -1014,22 +1207,22 @@ ui.prototype.drawToolbox = function(index) { // 退出 core.canvas.ui.textAlign = 'center'; + core.fillText('ui', '删除道具', 370, 32,'#DDDDDD', 'bold 15px Verdana'); core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px Verdana'); } ////// 绘制存档/读档界面 ////// ui.prototype.drawSLPanel = function(index) { if (!core.isset(index)) index=1; - if (index<=0) index=1; - if (index>180) index=180; + if (index<0) index=0; + + var page = parseInt(index/10), offset=index%10; + if (page>=30) page=29; + if (offset>5) offset=5; + index=10*page+offset; core.status.event.data=index; - var page=parseInt((index-1)/6); - - // core.status.event.data = page; - // core.status.savePage = page; - core.clearMap('ui', 0, 0, 416, 416); core.setAlpha('ui', 0.85); core.fillRect('ui', 0, 0, 416, 416, '#000000'); @@ -1040,12 +1233,11 @@ ui.prototype.drawSLPanel = function(index) { var name=core.status.event.id=='save'?"存档":"读档"; for (var i=0;i<6;i++) { - var id=6*page+i+1; - var data=core.getLocalStorage("save"+id,null); - + var id=5*page+i; + var data=core.getLocalStorage(i==0?"autoSave":"save"+id, null); if (i<3) { - core.fillText('ui', name+id, (2*i+1)*u, 35, '#FFFFFF', "bold 17px Verdana"); - core.strokeRect('ui', (2*i+1)*u-size/2, 50, size, size, id==index?'#FFD700':'#FFFFFF', id==index?6:2); + core.fillText('ui', i==0?"自动存档":name+id, (2*i+1)*u, 35, '#FFFFFF', "bold 17px Verdana"); + core.strokeRect('ui', (2*i+1)*u-size/2, 50, size, size, i==offset?'#FFD700':'#FFFFFF', i==offset?6:2); if (core.isset(data) && core.isset(data.floorId)) { this.drawThumbnail(data.floorId, 'ui', core.maps.load(data.maps, data.floorId).blocks, (2*i+1)*u-size/2, 50, size, data.hero.loc); core.fillText('ui', core.formatDate(new Date(data.time)), (2*i+1)*u, 65+size, '#FFFFFF', '10px Verdana'); @@ -1057,7 +1249,7 @@ ui.prototype.drawSLPanel = function(index) { } else { core.fillText('ui', name+id, (2*i-5)*u, 230, '#FFFFFF', "bold 17px Verdana"); - core.strokeRect('ui', (2*i-5)*u-size/2, 245, size, size, id==index?'#FFD700':'#FFFFFF', id==index?6:2); + core.strokeRect('ui', (2*i-5)*u-size/2, 245, size, size, i==offset?'#FFD700':'#FFFFFF', i==offset?6:2); if (core.isset(data) && core.isset(data.floorId)) { this.drawThumbnail(data.floorId, 'ui', core.maps.load(data.maps, data.floorId).blocks, (2*i-5)*u-size/2, 245, size, data.hero.loc); core.fillText('ui', core.formatDate(new Date(data.time)), (2*i-5)*u, 260+size, '#FFFFFF', '10px Verdana'); @@ -1086,12 +1278,34 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL if (core.isset(core.floors[floorId].png)) { var png = core.floors[floorId].png; + /* if (core.isset(core.material.images.pngs[png])) { core.canvas.ui.drawImage(core.material.images.pngs[png], x, y, size, size); } + */ + + var ratio = size/416; + + if (typeof png == 'string') { + if (core.isset(core.material.images.pngs[png])) { + core.canvas.ui.drawImage(core.material.images.pngs[png], x, y, size, size); + } + } + else if (png instanceof Array) { + png.forEach(function (t) { + if (t.length!=3) return; + var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2]; + if (core.isset(dx) && core.isset(dy) && core.isset(core.material.images.pngs[p])) { + dx*=32; dy*=32; + var image = core.material.images.pngs[p]; + core.canvas.ui.drawImage(image, x+dx*ratio, y+dy*ratio, Math.min(size-dx*ratio, ratio*image.width), Math.min(size-dy*ratio, ratio*image.height)); + } + }) + } + } - var mapArray = core.maps.getMapArray(core.status.maps, floorId); + var mapArray = core.maps.getMapArray(blocks); for (var b in blocks) { var block = blocks[b]; if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable)) { @@ -1116,6 +1330,44 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL } } +ui.prototype.drawKeyBoard = function () { + core.lockControl(); + core.status.event.id = 'keyBoard'; + + core.clearMap('ui', 0, 0, 416, 416); + + var left = 16, top = 48, right = 416 - 2 * left, bottom = 416 - 2 * top; + var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); + core.fillRect('ui', left, top, right, bottom, background); + core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); + + core.canvas.ui.textAlign = "center"; + core.fillText('ui', "虚拟键盘", 208, top+35, "#FFD700", "bold 22px Verdana"); + + core.setFont('ui', '17px Verdana'); + core.setFillStyle('ui', '#FFFFFF'); + var offset = 128-9; + + var lines = [ + ["F1","F2","F3","F4","F5","F6","F7","F8","F9","10","11"], + ["1","2","3","4","5","6","7","8","9","0"], + ["Q","W","E","R","T","Y","U","I","O","P"], + ["A","S","D","F","G","H","J","K","L"], + ["Z","X","C","V","B","N","M"], + ["-","=","[","]","\\",";","'",",",".","/","`"], + ["ES","TA","CA","SH","CT","AL","SP","BS","EN","DE"] + ] + + lines.forEach(function (line) { + for (var i=0;i0) { // 检查 - core.setStatus("atk", core.getStatus("atk")+value); - // core.updateStatusBar(); // 和下面的 {"type": "update"} 等价,立即更新状态栏和地图显伤 - core.drawTip("操作成功,攻击+"+value); // 左上角气泡提示 - core.events.insertAction([ // 往当前事件列表前插入两条事件 - {"type": "update"}, // 更新状态栏和地图显伤 - "操作成功,攻击+"+value // 对话框提示 - ]); + + // 注意一下prompt对于录像是如何处理的 + var value; + if (core.status.replay.replaying) { + var action = core.status.replay.toReplay.shift(); + if (action.indexOf("input:")==0 ) { + value=parseInt(action.substring(6)); } + else { + core.stopReplay(); + core.drawTip("录像文件出错"); + return; + } + } + else { + value = prompt("请输入你要加攻击力的数值:"); + } + value = parseInt(value)||0; + core.status.route.push("input:"+value); + + if (value>0) { // 检查 + core.setStatus("atk", core.getStatus("atk")+value); + // core.updateStatusBar(); // 和下面的 {"type": "update"} 等价,立即更新状态栏和地图显伤 + core.drawTip("操作成功,攻击+"+value); // 左上角气泡提示 + core.events.insertAction([ // 往当前事件列表前插入两条事件 + {"type": "update"}, // 更新状态栏和地图显伤 + "操作成功,攻击+"+value // 对话框提示 + ]); } }`}, "\t[老人,womanMagician]具体可参见样板中本事件的写法。" @@ -305,6 +322,11 @@ main.floors.sample1 = }, "afterOpenDoor": { // 开完门后可能触发的事件列表 - } + }, + "cannotMove": { // 每个图块不可通行的方向 + // 可以在这里定义每个点不能前往哪个方向,例如悬崖边不能跳下去 + // "x,y": ["up", "left"], // (x,y)点不能往上和左走 + + }, } diff --git a/project/floors/sample2.js b/project/floors/sample2.js index bbdbe498..f5897d1f 100644 --- a/project/floors/sample2.js +++ b/project/floors/sample2.js @@ -9,8 +9,9 @@ main.floors.sample2 = "canFlyTo": false, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器) "canUseQuickShop": true, // 该层是否允许使用快捷商店 "defaultGround": "snowGround", // 默认地面的图块ID(terrains中) - // "png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。 + "png": [], // // 该层默认显示的所有图片;详细用法请查看文档“自定义素材”中的说明。 "color": [255,0,0,0.3], // 该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组。 + "weather": ["rain",10], // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain"或"snow"代表雨雪,第二项为1-10之间的数代表强度。 "bgm": "qianjin.mid", // 到达该层后默认播放的BGM。本项可忽略。 "map": [ // 地图数据,需要是13x13,建议使用地图生成器来生成 [5, 5, 5, 5, 5, 5, 87, 5, 5, 5, 5, 5, 5], @@ -28,8 +29,7 @@ main.floors.sample2 = [5, 5, 5, 5, 5, 5, 88, 5, 5, 5, 5, 5, 5], ], "firstArrive": [ // 第一次到该楼层触发的事件 - "\t[实战!]本楼将尝试复刻《宿命的旋律》40F剧情。", - "由于暂不支持一些动画效果,例如雷电、振动、天气渲染等等,因此做出来的效果远远比不上原版。\n\n不过作为抛砖引玉,还是能展示一下H5的能力。\n(开音效食用更加)" + "\t[实战!]本楼将尝试复刻《宿命的旋律》40F剧情。" ], "events": { // 该楼的所有可能事件列表 @@ -182,6 +182,7 @@ main.floors.sample2 = {"type": "show", "loc": [8,3], "time": 500}, // 依次显示四个角的法师 {"type": "sleep", "time": 500}, "\t[blackMagician]感受绝望吧!冥顽不化的蠢货!", + /* {"type": "hide", "loc": [4,3], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 {"type": "show", "loc": [4,3], "time": 150}, {"type": "hide", "loc": [4,6], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 @@ -190,8 +191,14 @@ main.floors.sample2 = {"type": "show", "loc": [8,6], "time": 150}, {"type": "hide", "loc": [8,3], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 {"type": "show", "loc": [8,3], "time": 150}, + */ + {"type": "animate", "name": "yongchang", "loc": [4,3]}, + {"type": "animate", "name": "yongchang", "loc": [4,6]}, + {"type": "animate", "name": "yongchang", "loc": [8,6]}, + {"type": "animate", "name": "yongchang", "loc": [8,3]}, {"type": "sleep", "time": 200}, {"type": "playSound", "name": "attack.ogg"}, // 播放攻击音效 + {"type": "animate", "name": "thunder", "loc": "hero"}, {"type": "sleep", "time": 200}, "\t[hero]唔……!!(吐血)", {"type": "playSound", "name": "item.ogg"}, @@ -267,6 +274,7 @@ main.floors.sample2 = "\t[小妖精,fairy]别小瞧咱!咱好歹也是妖精族里实力数一数二的存在!", {"type": "playSound", "name": "item.ogg"}, "\t[blackMagician]只会耍嘴皮子的恼人苍蝇!我倒要看看一块焦炭会不会说话!\n——招雷弹!!", + /* {"type": "hide", "loc": [4,3], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 {"type": "show", "loc": [4,3], "time": 150}, {"type": "hide", "loc": [4,6], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 @@ -275,9 +283,17 @@ main.floors.sample2 = {"type": "show", "loc": [8,6], "time": 150}, {"type": "hide", "loc": [8,3], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 {"type": "show", "loc": [8,3], "time": 150}, + */ + {"type": "animate", "name": "yongchang", "loc": [4,3]}, + {"type": "animate", "name": "yongchang", "loc": [4,6]}, + {"type": "animate", "name": "yongchang", "loc": [8,6]}, + {"type": "animate", "name": "yongchang", "loc": [8,3]}, {"type": "playSound", "name": "attack.ogg"}, // 播放攻击音效 + /* {"type": "hide", "loc": [6,6], "time": 150}, // 妖精也闪一下表示收到了伤害 {"type": "show", "loc": [6,6], "time": 150}, // 妖精也闪一下表示收到了伤害 + */ + {"type": "animate", "name": "thunder", "loc": [6,6]}, {"type": "sleep", "time": 500}, // 等待500毫秒 "\t[小妖精,fairy]切,这点伤痛跟他刚才经历的身心地狱相比根本就不算什么。", {"type": "playSound", "name": "item.ogg"}, @@ -386,6 +402,11 @@ main.floors.sample2 = }, "afterOpenDoor": { // 开完门后可能触发的事件列表 - } + }, + "cannotMove": { // 每个图块不可通行的方向 + // 可以在这里定义每个点不能前往哪个方向,例如悬崖边不能跳下去 + // "x,y": ["up", "left"], // (x,y)点不能往上和左走 + + }, } diff --git a/project/icons.js b/project/icons.js index f9a0e1b1..87200e94 100644 --- a/project/icons.js +++ b/project/icons.js @@ -1,215 +1,217 @@ icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = { - "hero": { - "down": {"loc": 0, "stop": 0, "leftFoot": 1, "rightFoot": 3}, - "left": {"loc": 1, "stop": 0, "leftFoot": 1, "rightFoot": 3}, - "right": {"loc": 2, "stop": 0, "leftFoot": 1, "rightFoot": 3}, - "up": {"loc": 3, "stop": 0, "leftFoot": 1, "rightFoot": 3} + 'hero': { + 'down': {'loc': 0, 'stop': 0, 'leftFoot': 1, 'rightFoot': 3}, + 'left': {'loc': 1, 'stop': 0, 'leftFoot': 1, 'rightFoot': 3}, + 'right': {'loc': 2, 'stop': 0, 'leftFoot': 1, 'rightFoot': 3}, + 'up': {'loc': 3, 'stop': 0, 'leftFoot': 1, 'rightFoot': 3} }, - "terrains": { - "ground": 0, - "grass": 1, - "grass2": 2, - "snowGround": 3, - "ground2": 4, - "ground3": 5, - "ground4": 6, - "sand": 7, - "ground5": 8, - "yellowWall2": 9, - "whiteWall2": 10, - "blueWall2": 11, - "blockWall": 12, - "grayWall": 13, - "white": 14, - "ground6": 15, - "soil": 16, - "yellowWall": 17, - "whiteWall": 18, - "blueWall": 19, - "star": 20, - "lava": 21, - "ice": 22, - "downFloor": 23, - "upFloor": 24, - "yellowDoor": 25, - "blueDoor": 26, - "redDoor": 27, - "greenDoor": 28, - "specialDoor": 29, - "steelDoor": 30, - "blueShop-left": 31, - "blueShop-right": 32, - "pinkShop-left": 33, - "pinkShop-right": 34, - "arrowUp": 35, - "arrowDown": 36, - "arrowLeft": 37, - "arrowRight": 38, - "light": 39, - "darkLight": 40 + 'terrains': { + 'ground': 0, + 'grass': 1, + 'grass2': 2, + 'yellowWall': 3, + 'whiteWall': 4, + 'blueWall': 5, + 'snowGround': 6, + 'ground2': 7, + 'ground3': 8, + 'ground4': 9, + 'sand': 10, + 'ground5': 11, + 'yellowWall2': 12, + 'whiteWall2': 13, + 'blueWall2': 14, + 'blockWall': 15, + 'grayWall': 16, + 'white': 17, + 'ground6': 18, + 'soil': 19, + 'star': 20, + 'lava': 21, + 'ice': 22, + 'downFloor': 23, + 'upFloor': 24, + 'yellowDoor': 25, + 'blueDoor': 26, + 'redDoor': 27, + 'greenDoor': 28, + 'specialDoor': 29, + 'steelDoor': 30, + 'blueShop-left': 31, + 'blueShop-right': 32, + 'pinkShop-left': 33, + 'pinkShop-right': 34, + 'arrowUp': 35, + 'arrowDown': 36, + 'arrowLeft': 37, + 'arrowRight': 38, + 'light': 39, + 'darkLight': 40 }, - "animates": { - "star": 0, - "lava": 1, - "waterWall": 2, - "yellowDoor": 3, - "blueDoor": 4, - "redDoor": 5, - "greenDoor": 6, - "specialDoor": 7, - "blueWallDoor": 8, - "yellowWallDoor": 9, - "whiteWallDoor": 10, - "steelDoor": 11, - "lavaDoor": 12, - "grayLavaDoor": 13, - "starDoor": 14, - "mockBlueWallDoor": 15, - "mockYellowWallDoor": 16, - "mockWhiteWallDoor": 17, - "iceYellowWallDoor": 18, - "starPortal": 19, - "exclamation": 20, - "portal": 21, - "switch": 22, - "lavaNet": 23, - "poisonNet": 24, - "weakNet": 25, - "curseNet": 26, - "downPortal": 27, - "leftPortal": 28, - "rightPortal": 29, - "upPortal": 30, - "water": 31, + 'animates': { + 'star': 0, + 'lava': 1, + 'waterWall': 2, + 'yellowDoor': 3, + 'blueDoor': 4, + 'redDoor': 5, + 'greenDoor': 6, + 'specialDoor': 7, + 'blueWallDoor': 8, + 'yellowWallDoor': 9, + 'whiteWallDoor': 10, + 'steelDoor': 11, + 'lavaDoor': 12, + 'grayLavaDoor': 13, + 'starDoor': 14, + 'mockBlueWallDoor': 15, + 'mockYellowWallDoor': 16, + 'mockWhiteWallDoor': 17, + 'iceYellowWallDoor': 18, + 'starPortal': 19, + 'exclamation': 20, + 'portal': 21, + 'switch': 22, + 'lavaNet': 23, + 'poisonNet': 24, + 'weakNet': 25, + 'curseNet': 26, + 'downPortal': 27, + 'leftPortal': 28, + 'rightPortal': 29, + 'upPortal': 30, + 'water': 31, }, - "npcs": { - "man": 0, - "woman": 1, - "thief": 2, - "fairy": 3, - "magician": 4, - "womanMagician": 5, - "oldMan": 6, - "child": 7, - "wood": 8, - "pinkShop": 9, - "blueShop": 10, - "princess": 11 + 'npcs': { + 'man': 0, + 'woman': 1, + 'thief': 2, + 'fairy': 3, + 'magician': 4, + 'womanMagician': 5, + 'oldMan': 6, + 'child': 7, + 'wood': 8, + 'pinkShop': 9, + 'blueShop': 10, + 'princess': 11 }, - "enemys": { - "greenSlime": 0, - "redSlime": 1, - "blackSlime": 2, - "slimelord": 3, - "bat": 4, - "bigBat": 5, - "redBat": 6, - "vampire": 7, - "skeleton": 8, - "skeletonSoilder": 9, - "skeletonCaptain": 10, - "ghostSkeleton": 11, - "zombie": 12, - "zombieKnight": 13, - "rock": 14, - "slimeMan": 15, - "bluePriest": 16, - "redPriest": 17, - "brownWizard": 18, - "redWizard": 19, - "yellowGuard": 20, - "blueGuard": 21, - "redGuard": 22, - "swordsman": 23, - "soldier": 24, - "yellowKnight": 25, - "redKnight": 26, - "darkKnight": 27, - "blackKing": 28, - "yellowKing": 29, - "greenKing": 30, - "blueKnight": 31, - "goldSlime": 32, - "poisonSkeleton": 33, - "poisonBat": 34, - "steelRock": 35, - "skeletonPriest": 36, - "skeletonKing": 37, - "skeletonWizard": 38, - "redSkeletonCaption": 39, - "badHero": 40, - "demon": 41, - "demonPriest": 42, - "goldHornSlime": 43, - "redKing": 44, - "whiteKing": 45, - "blackMagician": 46, - "silverSlime": 47, - "swordEmperor": 48, - "whiteHornSlime": 49, - "badPrincess": 50, - "badFairy": 51, - "grayPriest": 52, - "redSwordsman": 53, - "whiteGhost": 54, - "poisonZombie": 55, - "magicDragon": 56, - "octopus": 57, - "darkFairy": 58, - "greenKnight": 59, + 'enemys': { + 'greenSlime': 0, + 'redSlime': 1, + 'blackSlime': 2, + 'slimelord': 3, + 'bat': 4, + 'bigBat': 5, + 'redBat': 6, + 'vampire': 7, + 'skeleton': 8, + 'skeletonSoilder': 9, + 'skeletonCaptain': 10, + 'ghostSkeleton': 11, + 'zombie': 12, + 'zombieKnight': 13, + 'rock': 14, + 'slimeMan': 15, + 'bluePriest': 16, + 'redPriest': 17, + 'brownWizard': 18, + 'redWizard': 19, + 'yellowGuard': 20, + 'blueGuard': 21, + 'redGuard': 22, + 'swordsman': 23, + 'soldier': 24, + 'yellowKnight': 25, + 'redKnight': 26, + 'darkKnight': 27, + 'blackKing': 28, + 'yellowKing': 29, + 'greenKing': 30, + 'blueKnight': 31, + 'goldSlime': 32, + 'poisonSkeleton': 33, + 'poisonBat': 34, + 'steelRock': 35, + 'skeletonPriest': 36, + 'skeletonKing': 37, + 'skeletonWizard': 38, + 'redSkeletonCaption': 39, + 'badHero': 40, + 'demon': 41, + 'demonPriest': 42, + 'goldHornSlime': 43, + 'redKing': 44, + 'whiteKing': 45, + 'blackMagician': 46, + 'silverSlime': 47, + 'swordEmperor': 48, + 'whiteHornSlime': 49, + 'badPrincess': 50, + 'badFairy': 51, + 'grayPriest': 52, + 'redSwordsman': 53, + 'whiteGhost': 54, + 'poisonZombie': 55, + 'magicDragon': 56, + 'octopus': 57, + 'darkFairy': 58, + 'greenKnight': 59, }, - "items": { - "yellowKey": 0, - "blueKey": 1, - "redKey": 2, - "greenKey": 3, - "steelKey": 4, - "bigKey": 6, - "redJewel": 16, - "blueJewel": 17, - "greenJewel": 18, - "yellowJewel": 19, - "redPotion": 20, - "bluePotion": 21, - "greenPotion": 22, - "yellowPotion": 23, - "sword1": 50, - "sword2": 51, - "sword3": 52, - "sword4": 53, - "sword5": 54, - "shield1": 55, - "shield2": 56, - "shield3": 57, - "shield4": 58, - "shield5": 59, - "book": 9, - "fly": 12, - "pickaxe": 45, - "icePickaxe": 44, - "bomb": 43, - "centerFly": 13, - "upFly": 15, - "downFly": 14, - "coin": 11, - "snow": 41, - "cross": 40, - "superPotion": 29, - "earthquake": 8, - "poisonWine": 24, - "weakWine": 25, - "curseWine": 27, - "superWine": 28, - "knife": 42, - "moneyPocket": 46, - "shoes": 47, - "hammer": 48 + 'items': { + 'yellowKey': 0, + 'blueKey': 1, + 'redKey': 2, + 'greenKey': 3, + 'steelKey': 4, + 'bigKey': 6, + 'redJewel': 16, + 'blueJewel': 17, + 'greenJewel': 18, + 'yellowJewel': 19, + 'redPotion': 20, + 'bluePotion': 21, + 'greenPotion': 22, + 'yellowPotion': 23, + 'sword0': 60, + 'sword1': 50, + 'sword2': 51, + 'sword3': 52, + 'sword4': 53, + 'sword5': 54, + 'shield0': 61, + 'shield1': 55, + 'shield2': 56, + 'shield3': 57, + 'shield4': 58, + 'shield5': 59, + 'book': 9, + 'fly': 12, + 'pickaxe': 45, + 'icePickaxe': 44, + 'bomb': 43, + 'centerFly': 13, + 'upFly': 15, + 'downFly': 14, + 'coin': 11, + 'snow': 41, + 'cross': 40, + 'superPotion': 29, + 'earthquake': 8, + 'poisonWine': 24, + 'weakWine': 25, + 'curseWine': 27, + 'superWine': 28, + 'knife': 42, + 'moneyPocket': 46, + 'shoes': 47, + 'hammer': 48 }, - "autotile": { // 所有的Autotile列表;后面的index简单取0即可 - "autotile": 0, - "autotile1": 0, - "autotile2": 0, - "autotile3": 0, + 'autotile': { // 所有的Autotile列表;后面的index简单取0即可 + 'autotile': 0, + 'autotile1': 0, + 'autotile2': 0, + 'autotile3': 0, } } \ No newline at end of file diff --git a/project/images/forward.png b/project/images/forward.png new file mode 100644 index 00000000..6701f077 Binary files /dev/null and b/project/images/forward.png differ diff --git a/project/images/items.png b/project/images/items.png index c1458fe0..626b63b7 100644 Binary files a/project/images/items.png and b/project/images/items.png differ diff --git a/project/images/pause.png b/project/images/pause.png new file mode 100644 index 00000000..db4f3aea Binary files /dev/null and b/project/images/pause.png differ diff --git a/project/images/play.png b/project/images/play.png new file mode 100644 index 00000000..34cfd72f Binary files /dev/null and b/project/images/play.png differ diff --git a/project/images/rewind.png b/project/images/rewind.png new file mode 100644 index 00000000..bafb4ee1 Binary files /dev/null and b/project/images/rewind.png differ diff --git a/project/images/stop.png b/project/images/stop.png new file mode 100644 index 00000000..8a2a1219 Binary files /dev/null and b/project/images/stop.png differ diff --git a/project/images/terrains.png b/project/images/terrains.png index 31ff78cf..15d492c3 100644 Binary files a/project/images/terrains.png and b/project/images/terrains.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates0:经典.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates0:经典.png new file mode 100644 index 00000000..c1c1cb1a Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates0:经典.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates1:旋转门.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates1:旋转门.png new file mode 100644 index 00000000..3a6cdbb5 Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates1:旋转门.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates2:四方门.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates2:四方门.png new file mode 100644 index 00000000..dead2aea Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/animates2:四方门.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/items0:经典.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/items0:经典.png index c1458fe0..626b63b7 100644 Binary files a/project/images/常用素材:如需使用请直接替换目录中的对应文件/items0:经典.png and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/items0:经典.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/items1:方块宝石.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/items1:方块宝石.png new file mode 100644 index 00000000..b7226d92 Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/items1:方块宝石.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/四方机关门.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/四方机关门.png new file mode 100644 index 00000000..5f31373f Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/四方机关门.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石1.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石1.png new file mode 100644 index 00000000..d930ea4e Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石1.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石2.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石2.png new file mode 100644 index 00000000..4749c79d Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石2.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石3.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石3.png new file mode 100644 index 00000000..6a1d977a Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石3.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石4.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石4.png new file mode 100644 index 00000000..612e1efa Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/宝石4.png differ diff --git a/project/images/常用素材:如需使用请直接替换目录中的对应文件/旋转机关门.png b/project/images/常用素材:如需使用请直接替换目录中的对应文件/旋转机关门.png new file mode 100644 index 00000000..ce9bc323 Binary files /dev/null and b/project/images/常用素材:如需使用请直接替换目录中的对应文件/旋转机关门.png differ diff --git a/project/items.js b/project/items.js index b7ee75dc..84bbcbf7 100644 --- a/project/items.js +++ b/project/items.js @@ -2,12 +2,12 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = { "items" : { - + // 钥匙 'yellowKey': {'cls': 'keys', 'name': '黄钥匙'}, 'blueKey': {'cls': 'keys', 'name': '蓝钥匙'}, 'redKey': {'cls': 'keys', 'name': '红钥匙'}, - + // 宝石、血瓶 'redJewel': {'cls': 'items', 'name': '红宝石'}, 'blueJewel': {'cls': 'items', 'name': '蓝宝石'}, 'greenJewel': {'cls': 'items', 'name': '绿宝石'}, @@ -16,20 +16,22 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = 'bluePotion': {'cls': 'items', 'name': '蓝血瓶'}, 'yellowPotion': {'cls': 'items', 'name': '黄血瓶'}, 'greenPotion': {'cls': 'items', 'name': '绿血瓶'}, - 'sword1': {'cls': 'items', 'name': '铁剑'}, - 'sword2': {'cls': 'items', 'name': '银剑'}, - 'sword3': {'cls': 'items', 'name': '骑士剑'}, - 'sword4': {'cls': 'items', 'name': '圣剑'}, - 'sword5': {'cls': 'items', 'name': '神圣剑'}, - 'shield1': {'cls': 'items', 'name': '铁盾'}, - 'shield2': {'cls': 'items', 'name': '银盾'}, - 'shield3': {'cls': 'items', 'name': '骑士盾'}, - 'shield4': {'cls': 'items', 'name': '圣盾'}, - 'shield5': {'cls': 'items', 'name': '神圣盾'}, + 'sword1': {'cls': 'items', 'name': '铁剑', 'isEquipment': true}, + 'sword2': {'cls': 'items', 'name': '银剑', 'isEquipment': true}, + 'sword3': {'cls': 'items', 'name': '骑士剑', 'isEquipment': true}, + 'sword4': {'cls': 'items', 'name': '圣剑', 'isEquipment': true}, + 'sword5': {'cls': 'items', 'name': '神圣剑', 'isEquipment': true}, + 'shield1': {'cls': 'items', 'name': '铁盾', 'isEquipment': true}, + 'shield2': {'cls': 'items', 'name': '银盾', 'isEquipment': true}, + 'shield3': {'cls': 'items', 'name': '骑士盾', 'isEquipment': true}, + 'shield4': {'cls': 'items', 'name': '圣盾', 'isEquipment': true}, + 'shield5': {'cls': 'items', 'name': '神圣盾', 'isEquipment': true}, 'superPotion': {'cls': 'items', 'name': '圣水'}, 'moneyPocket': {'cls': 'items', 'name': '金钱袋'}, - + // 物品 + 'sword0': {'cls': 'constants', 'name': '折断的剑', 'text': '没有任何作用的剑,相当于脱掉装备。'}, + 'shield0': {'cls': 'constants', 'name': '残破的盾', 'text': '没有任何作用的盾,相当于脱掉装备。'}, 'book': {'cls': 'constants', 'name': '怪物手册', 'text': '可以查看当前楼层各怪物属性'}, 'fly': {'cls': 'constants', 'name': '楼层传送器', 'text': '可以自由往来去过的楼层'}, 'coin': {'cls': 'constants', 'name': '幸运金币', 'text': '持有时打败怪物可得双倍金币'}, @@ -38,7 +40,7 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = 'knife': {'cls': 'constants', 'name': '屠龙匕首', 'text': '该道具尚未被定义'}, 'shoes': {'cls': 'constants', 'name': '绿鞋', 'text': '持有时无视负面地形'}, - + // 道具 'bigKey': {'cls': 'tools', 'name': '大黄门钥匙', 'text': '可以开启当前层所有黄门'}, 'greenKey': {'cls': 'tools', 'name': '绿钥匙', 'text': '可以打开一扇绿门'}, 'steelKey': {'cls': 'tools', 'name': '铁门钥匙', 'text': '可以打开一扇铁门'}, diff --git a/project/maps.comment.js b/project/maps.comment.js index 374ed0c4..4271cb6e 100644 --- a/project/maps.comment.js +++ b/project/maps.comment.js @@ -8,7 +8,7 @@ maps_comment_90f36752_8815_4be8_b32b_d7fad1d0542e = '3':' 蓝墙', '4':' 星空', '5':' 岩浆', - '6':' 岩浆', + '6':' 冰面', '7':' 蓝色商店左', '8':' 蓝色商店右', '9':' 粉色商店左', diff --git a/project/maps.js b/project/maps.js index 0865925e..686f768c 100644 --- a/project/maps.js +++ b/project/maps.js @@ -5,10 +5,10 @@ maps_90f36752_8815_4be8_b32b_d7fad1d0542e = // 0-20 地形 '1':{'cls': 'terrains', 'id': 'yellowWall'}, // 黄墙 '2':{'cls': 'terrains', 'id': 'whiteWall'}, // 白墙 - '3':{'cls': 'terrains', 'id': 'blueWall'}, // 白墙 + '3':{'cls': 'terrains', 'id': 'blueWall'}, // 蓝墙 '4':{'cls': 'animates', 'id': 'star', 'noPass': true}, // 星空 '5':{'cls': 'animates', 'id': 'lava', 'noPass': true}, // 岩浆 - '6':{'cls': 'terrains', 'id': 'ice'}, // 岩浆 + '6':{'cls': 'terrains', 'id': 'ice'}, // 冰面 '7':{'cls': 'terrains', 'id': 'blueShop-left'}, // 蓝色商店左 '8':{'cls': 'terrains', 'id': 'blueShop-right'}, // 蓝色商店右 '9':{'cls': 'terrains', 'id': 'pinkShop-left'}, // 粉色商店左 diff --git a/project/sounds/zone.ogg b/project/sounds/zone.ogg new file mode 100644 index 00000000..aef5fcbc Binary files /dev/null and b/project/sounds/zone.ogg differ diff --git a/styles.css b/styles.css index b7031156..0c8a944f 100644 --- a/styles.css +++ b/styles.css @@ -21,7 +21,7 @@ position: fixed; top: 10px; left: 10px; - z-index: 13; + z-index: 15; } #startPanel { @@ -32,7 +32,7 @@ left: 0; background-color: #fff; overflow: hidden; - z-index: 9; + z-index: 11; } #startTop { @@ -42,7 +42,7 @@ top: 0; left: 0; background-color: #000; - z-index: 12; + z-index: 14; } #startTopProgressBar { @@ -52,7 +52,7 @@ position: absolute; top: 5%; background-color: #fff; - z-index: 13; + z-index: 15; } #startTopProgress { @@ -67,7 +67,7 @@ position: absolute; top: 8%; left: 5%; - z-index: 13; + z-index: 15; } #startBackground { @@ -77,12 +77,12 @@ height: 100%; width: auto; transform:translate(-50%,-50%); - z-index: 10; + z-index: 12; } #startLogo { position: absolute; - z-index: 10; + z-index: 12; left: 0; right: 0; margin-left: auto; @@ -90,12 +90,12 @@ margin-top: 8%; max-width: 100%; text-align: center; - font: bold 4rem 华文行楷; + font: bold 4rem STXingkai; } #startTitle { position: absolute; - z-index: 11; + z-index: 13; } #startButtonGroup { @@ -106,7 +106,7 @@ background-color: #000; opacity: 0.85; display: none; - z-index: 10; + z-index: 12; bottom: 0; margin-bottom: 7%; } @@ -142,12 +142,12 @@ display: none; color: #fff; background-color: #000; - z-index: 8; + z-index: 10; } #logoLabel { margin-top: 8%; - font: bold 3rem 华文行楷; + font: bold 3rem STXingkai; margin-left: auto; margin-right: auto; } @@ -169,8 +169,8 @@ box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; - background: url(project/images/ground.png) round; - z-index: 7; + background: url(project/images/ground.png) repeat; + z-index: 9; display: none; } #statusBar .status{ @@ -198,8 +198,8 @@ } #toolBar { position: absolute; - background: url(project/images/ground.png) round; - z-index: 6; + background: url(project/images/ground.png) repeat; + z-index: 8; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -234,7 +234,7 @@ span#poison, span#weak, span#curse { } #curtain { - z-index: 5; + z-index: 7; position: absolute; opacity: 0; background: #000000; @@ -256,12 +256,20 @@ span#poison, span#weak, span#curse { z-index: 4; } -#ui { +#animate { + z-index: 5; +} + +#weather { z-index: 6; } +#ui { + z-index: 8; +} + #data { - z-index: 7; + z-index: 9; } .clearfix:before, diff --git a/启动服务.exe b/启动服务.exe index e6f4a85c..2ec60d88 100644 Binary files a/启动服务.exe and b/启动服务.exe differ diff --git a/常用工具/Newtonsoft.Json.dll b/常用工具/Newtonsoft.Json.dll new file mode 100644 index 00000000..67b9d351 Binary files /dev/null and b/常用工具/Newtonsoft.Json.dll differ diff --git a/常用工具/RM动画导出器.exe b/常用工具/RM动画导出器.exe new file mode 100644 index 00000000..0e762d67 Binary files /dev/null and b/常用工具/RM动画导出器.exe differ diff --git a/常用工具/rgss.dll b/常用工具/rgss.dll new file mode 100644 index 00000000..1b3f0854 Binary files /dev/null and b/常用工具/rgss.dll differ diff --git a/常用工具/便捷PS工具.exe b/常用工具/便捷PS工具.exe index a85e29fd..3d453d0c 100644 Binary files a/常用工具/便捷PS工具.exe and b/常用工具/便捷PS工具.exe differ diff --git a/快捷键说明.txt b/快捷键说明.txt index 61cb65b4..0e0fcf45 100644 --- a/快捷键说明.txt +++ b/快捷键说明.txt @@ -3,14 +3,18 @@ === 全局 === [↑][↓][←][→] 移动勇士 [CTRL] 跳过对话 +[Z] 转向 [X] 打开/关闭怪物手册 [G] 打开/关闭楼层传送器 +[A] 读取自动存档 [S] 打开/关闭存档页面 [D] 打开/关闭读档页面 [K] 打开/关闭快捷商店选择列表 [T] 打开/关闭工具栏 [ESC] 打开/关闭系统菜单 +[E] 显示光标 [H] 打开帮助页面 +[R] 回放 [SPACE] 轻按(仅在轻按开关打开时有效) [1] 快捷使用破墙镐 [2] 快捷使用炸弹/圣锤(先检测有没有炸弹,没有再检测圣锤) diff --git a/更新说明.txt b/更新说明.txt index 6ee14efa..63fe424a 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,4 +1,54 @@ -HTML5魔塔样板V1.3: +HTML5魔塔样板V1.4.1 + +改变图块(setBlock事件)。 +同一个点的多事件处理(做法详见文档)。 +地图中每个块的可通行方向控制(悬崖效果)。 +动画支持带旋转和翻转的帧。 +现在可以允许用户丢弃道具了(例如不会再使用的装备)。 +修复行走时按键会发生动画抖动问题。 +修复无法打开战斗动画的Bug。 + +----------------------------------------------------------------------- + +HTML5魔塔样板V1.4 + +动画!动画!!动画!!! +瞬间移动。 +支持天气系统,可以在剧本中设置默认天气。 +新增自定义事件-图片显示。 +同时可以在剧本中设定多个背景素材。 +剧情文本特性控制,人物的对话框效果。 +单存档同步到服务器,下载到文件和读取。 +键盘支持自动寻路操作。 +浏览地图模式下可以查看怪物数据。 +未成功打怪和开门则不自动存档。 +重新支持楼梯穿透。 +支持多结局,成绩将分开统计。 +重构全局动画、行走动画和行走检测,大幅提升性能。 +修复所有已知Bug。 + +----------------------------------------------------------------------- + +HTML5魔塔样板V1.3.2 + +增加录像和回放功能。 +增加统计功能,现在能看到每部塔的游戏人数、通关人数和当前MAX了。 +增加浏览地图功能,玩家可以快速查看每层楼的地图。 +现在保存文件到本地,以及从本地文件读档了。 +可以在全局开关中设置剑盾是否作为装备存在。 +修复了部分已知Bug。 + +----------------------------------------------------------------------- + +HTML5魔塔样板V1.3.1: + +增加虚拟键盘。 +增加自动存档(回退),A键可快速读档。 +修复几处较为严重的Bug。 + +----------------------------------------------------------------------- + +HTML5魔塔样板V1.3: 支持全键盘操作。 支持将某个图片作为某层的背景素材。