Merge remote-tracking branch 'refs/remotes/ckcz123/master' into 2.0-for-merge

Conflicts:
_server/vm.js
drawMapGUI.html
index.html
libs/core.js
libs/data.js
libs/enemys.js
libs/icons.js
libs/items.js
libs/maps.js
main.js
project/floors/sample1.js
project/images/yewai.png
project/images/常用素材:如需使用请直接替换目录中的对应文件/items1:圆形宝石.png
styles.css
This commit is contained in:
YouWei Zhao 2018-02-10 16:35:38 +08:00
commit 85c4d8a622
70 changed files with 4143 additions and 1052 deletions

12
.idea/mota-js.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

29
LICENSE.md Normal file
View File

@ -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.

View File

@ -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] 支持全键盘操作。

View File

@ -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;

View File

@ -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;

1
_server/vendor/polyfill.min.js vendored Normal file
View File

@ -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)});

View File

@ -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;
}
}

View File

@ -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 // 绘制一个缩略图

View File

@ -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]** 快捷使用炸弹/圣锤

View File

@ -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为可选项表示设置文字显示位置。只能为upcenter和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} // 变成老人
]
}
```
### 获得圣水后变成墙
这个例子要求获得圣水时不前进(也就是不能走到圣水地方),然后把圣水位置变成墙。
因此需要我们需要覆盖系统triggergetItem并覆盖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:battletype: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进行一段对话。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
docs/img/tuihua.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1,6 +1,6 @@
# HTML5 魔塔样板说明文档
?> 上次更新时间:* {docsify-updated} *
?> 目前版本**v1.4.1**上次更新时间:* {docsify-updated} *
众所周知魔塔的趋势是向移动端发展贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中NekoRPG有着比较大的局限性游戏感较差更是完全没法在iOS上运行。而一些APP的魔塔虽然可用但是必须要下载安装对于Android和iOS还必须开发不同的版本非常麻烦。

View File

@ -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为一个数组代表当前层所有作为背景素材的图片信息。
每一项为一个三元组分别为该背景素材的xy和图片名。其中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函数中。

View File

@ -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浏览器。其他浏览器可能会导致本地服务器产生闪退等现象
- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如WebStormVSCode或者至少也要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)

View File

@ -179,7 +179,7 @@
<div id='startButtons'>
<span class='startButton' id='playGame'>开始游戏</span>
<span class='startButton' id='loadGame'>载入游戏</span>
<span class='startButton' id='aboutGame'>关于本塔</span>
<span class='startButton' id='replayGame'>录像回放</span>
</div>
<div id='levelChooseButtons'>
<span class='startButton' id='easyLevel'>简单</span>
@ -256,6 +256,8 @@
<!-- <canvas class='gameCanvas' id='event' width='416' height='416'></canvas> -->
<canvas class='gameCanvas' id='fg' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='hero' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='animate' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='weather' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='ui' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='data' width='416' height='416'>此浏览器不支持HTML5</canvas>
</div>
@ -284,6 +286,7 @@
<script src='_server/vendor/vue.min.js'></script>
<!-- <script src="https://cdn.bootcss.com/vue/2.5.13/vue.js"></script> -->
<script src='_server/vendor/polyfill.min.js'></script>
<script src='_server/fs.js'></script>
<script src='_server/editor_file.js'></script>
<script src='_server/editor_mode.js'></script>

View File

@ -32,7 +32,7 @@
<div id='startButtons'>
<span class='startButton' id='playGame'>开始游戏</span>
<span class='startButton' id='loadGame'>载入游戏</span>
<span class='startButton' id='aboutGame'>关于本塔</span>
<span class='startButton' id='replayGame'>录像回放</span>
</div>
<div id='levelChooseButtons'>
<span class='startButton' id='easyLevel'>简单</span>
@ -109,11 +109,13 @@
<canvas class='gameCanvas' id='event' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='fg' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='hero' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='animate' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='weather' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='ui' width='416' height='416'></canvas>
<canvas class='gameCanvas' id='data' width='416' height='416'>此浏览器不支持HTML5</canvas>
</div>
<script id='mainScript' src='main.js'></script>
<script>main.init();main.listen();</script>
<script src='libs/thirdparty/mid.js'></script>
<script src='libs/thirdparty/mid.min.js'></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,16 @@ enemys.prototype.getEnemys = function (enemyId) {
////// 判断是否含有某特殊属性 //////
enemys.prototype.hasSpecial = function (special, test) {
return (special instanceof Array)?special.indexOf(test)>=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),

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;
});

1
libs/thirdparty/mid.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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<contents.length;i++) {
core.fillText('ui', contents[i], content_left, content_top, '#FFFFFF', '16px Verdana');
content_top+=24;
core.fillText('ui', contents[i], content_left, content_top, null, '16px Verdana');
content_top+=21;
}
core.fillText('ui', '<点击任意位置继续>', 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;i<line.length;i++) {
core.fillText('ui', line[i], 48+32*i, offset);
}
offset+=32;
});
core.fillText("ui", "返回游戏", 416-80, offset-3, '#FFFFFF', 'bold 15px Verdana');
}
////// 绘制“关于”界面 //////
ui.prototype.drawAbout = function() {
@ -1139,9 +1391,10 @@ ui.prototype.drawAbout = function() {
// 名称
core.canvas.ui.textAlign = "left";
core.fillText('ui', "HTML5 魔塔样板", text_start, top+35, "#FFD700", "bold 22px Verdana");
core.fillText('ui', "作者: 艾之葵", text_start, top + 80, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', 'HTML5魔塔交流群539113091', text_start, top+112);
// TODO: 写自己的“关于”页面
core.fillText('ui', "版本: "+core.firstData.version, text_start, top + 80, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "作者: 艾之葵", text_start, top + 112);
core.fillText('ui', 'HTML5魔塔交流群539113091', text_start, top+112+32);
// TODO: 写自己的“关于”页面每次增加32像素即可
}
////// 绘制帮助页面 //////
@ -1151,11 +1404,14 @@ ui.prototype.drawHelp = function () {
"[CTRL] 跳过对话\n" +
"[X] 打开/关闭怪物手册\n" +
"[G] 打开/关闭楼层传送器\n" +
"[A] 读取自动存档(回退)\n" +
"[S/D] 打开/关闭存/读档页面\n" +
"[K] 打开/关闭快捷商店选择列表\n" +
"[T] 打开/关闭工具栏\n" +
"[ESC] 打开/关闭系统菜单\n" +
"[E] 显示光标\n" +
"[H] 打开帮助页面\n"+
"[R] 回放\n"+
"[SPACE] 轻按(仅在轻按开关打开时有效)\n" +
"[1] 快捷使用破墙镐\n" +
"[2] 快捷使用炸弹/圣锤\n" +
@ -1165,6 +1421,7 @@ ui.prototype.drawHelp = function () {
"点任意块: 寻路并移动\n"+
"点任意块并拖动: 指定寻路路线\n"+
"单击勇士: 转向\n"+
"双击勇士: 轻按(仅在轻按开关打开时有效)"
"双击勇士: 轻按(仅在轻按开关打开时有效)\n"+
"长按任意位置:打开虚拟键盘"
]);
}

85
main.js
View File

@ -2,7 +2,7 @@ function main() {
//------------------------ 用户修改内容 ------------------------//
this.version = "0.1"; // 游戏版本号如果更改了游戏内容建议修改此version以免造成缓存问题。
this.version = "1.4.1"; // 游戏版本号如果更改了游戏内容建议修改此version以免造成缓存问题。
//------------------------ 用户修改内容 END ------------------------//
@ -31,7 +31,7 @@ function main() {
'startButtons': document.getElementById('startButtons'),
'playGame': document.getElementById('playGame'),
'loadGame': document.getElementById('loadGame'),
'aboutGame': document.getElementById('aboutGame'),
'replayGame': document.getElementById('replayGame'),
'levelChooseButtons': document.getElementById('levelChooseButtons'),
'easyLevel': document.getElementById('easyLevel'),
'normalLevel': document.getElementById('normalLevel'),
@ -77,6 +77,19 @@ function main() {
'load': document.getElementById("img-load"),
'settings': document.getElementById("img-settings")
},
'icons': {
'book': null,
'fly': null,
'toolbox': null,
'save': null,
'load': null,
'settings': null,
'rewind': null, // 减速
'forward': null, // 加速
'play': null, // 播放
'pause': null, // 暂停
'stop': null, // 停止
},
'floor': document.getElementById('floor'),
'lv': document.getElementById('lv'),
'hp': document.getElementById('hp'),
@ -107,6 +120,11 @@ main.prototype.init = function (mode) {
main.mode = mode;
if (mode === 'editor')main.editor = {'disableGlobalAnimate':true};
}
Object.keys(this.statusBar.icons).forEach(function (t) {
var image=new Image();
image.src="project/images/"+t+".png";
main.statusBar.icons[t] = image;
})
main.loadPureData(function(){
var mainData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main;
for(var ii in mainData)main[ii]=mainData[ii];
@ -119,7 +137,11 @@ main.prototype.init = function (mode) {
coreData[name] = main[name];
}
main.loaderFloors(function() {
main.core.init(main.dom, main.statusBar, main.canvas, main.images, main.pngs, main.bgms, main.sounds, main.floorIds, main.floors, coreData);
["dom", "statusBar", "canvas", "images", "pngs",
"animates", "bgms", "sounds", "floorIds", "floors"].forEach(function (t) {
coreData[t] = main[t];
})
main.core.init(coreData);
main.core.resize(main.dom.body.clientWidth, main.dom.body.clientHeight);
});
});
@ -323,12 +345,23 @@ main.dom.data.ontouchend = function () {
////// 点击状态栏中的怪物手册时 //////
main.statusBar.image.book.onclick = function () {
if (core.isset(core.status.replay) && core.status.replay.replaying) {
core.triggerReplay();
return;
}
if (main.core.isPlaying())
main.core.openBook(true);
}
////// 点击状态栏中的楼层传送器时 //////
main.statusBar.image.fly.onclick = function () {
if (core.isset(core.status.replay) && core.status.replay.replaying) {
core.stopReplay();
return;
}
if (main.core.isPlaying())
main.core.useFly(true);
}
@ -342,17 +375,29 @@ main.statusBar.image.toolbox.onclick = function () {
////// 点击状态栏中的快捷商店时 //////
main.statusBar.image.shop.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawQuickShop(true);
main.core.openQuickShop(true);
}
////// 点击状态栏中的存档按钮时 //////
main.statusBar.image.save.onclick = function () {
if (core.isset(core.status.replay) && core.status.replay.replaying) {
core.rewindReplay();
return;
}
if (main.core.isPlaying())
main.core.save(true);
}
////// 点击状态栏中的读档按钮时 //////
main.statusBar.image.load.onclick = function () {
if (core.isset(core.status.replay) && core.status.replay.replaying) {
core.forwardReplay();
return;
}
if (main.core.isPlaying())
main.core.load(true);
}
@ -360,7 +405,7 @@ main.statusBar.image.load.onclick = function () {
////// 点击状态栏中的系统菜单时 //////
main.statusBar.image.settings.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawSettings(true);
main.core.openSettings(true);
}
////// 点击“开始游戏”时 //////
@ -380,9 +425,33 @@ main.dom.loadGame.onclick = function() {
main.core.load();
}
////// 点击“关于本塔”时 //////
main.dom.aboutGame.onclick = function () {
main.core.ui.drawAbout();
////// 点击“录像回放”时 //////
main.dom.replayGame.onclick = function () {
core.readFile(function (obj) {
if (obj.name!=core.firstData.name) {
alert("存档和游戏不一致!");
return;
}
if (core.isset(obj.version) && obj.version!=core.firstData.version) {
alert("游戏版本不一致!");
return;
}
if (!core.isset(obj.route) || !core.isset(obj.hard)) {
alert("无效的录像!");
return;
}
core.dom.startPanel.style.display = 'none';
core.resetStatus(core.firstData.hero, obj.hard, core.firstData.floorId, null, core.initStatus.maps);
core.events.setInitData(obj.hard);
core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() {
//core.setHeroMoveTriggerInterval();
core.startReplay(core.decodeRoute(obj.route));
});
}, function () {
})
}
////// 点击“简单难度”时 //////

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"ratio":2,"bitmaps":["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAD6SURBVHhe7dQ9SsVAFAXg+MNTeAhaWCrWLsHGUlCwsHMXgo2NjZ31a1yBVtaCrSvQ1i24hSSegbuEBAS/Dw7M3JnqZEgHAAAAAAAAAAAAAP/NOI5rySI5GobhNDlJ9uuYqaXo9RR83Pf9U/KevGV/kxwkW+2D1FWmkmKXKfYuZf8k38lrZpeZHSaLusaU2ktOyRcp+zP5Sl6yv858t53VNaaWcrdT9mPSXvpHskrxO3XMnNpvJYW3F/+Q9XmNmVv7raT055R+n/VejZlbyt5M6VfJbdYbNWZureyUvkzOagQAAAAAAAAAAAAAAAAAAAAAAAAAAADwd3XdL135q9NaPZ6+AAAAAElFTkSuQmCC","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJ4SURBVHhe7de7alRRFMbx3DSIgqiNIBJsBUUESyutBAsLO9/CWtL4BtorlgFBRGIEEQwxMSBiMWouGo2TCyaTC0lmJjM5ey+/HVfeYPZB8P+DNefsS/WdxZ5zugAAAAAAAAAAAAAAAAAAAID/lZkdVnX7ELmkkA+C1rU3xngthHBf1xsa9+1vQmcdhK7qUdAXFPh4URSjur+r6vdt6DQP/ZDqkkL/ZlZs6jqh8WnfghzU1UcOQlclmop3fBk5KPA+hXxRYb9LiWtsujR0Oe5b0Gke+hUF/TaFLtHrh29BDgr9jEJOoReqeur2RPfTvgWdpnzTG8yAQm6oYgq8uWfWLvaD1y+vkNko3G6FPJ9CT7ZbZpVVs9m1/Ydw3bchB3X9+dTxqVLwk4tmH1T1VnjlW5CDGj11/XDq+HTgfFwye/rVbKpmNjzDx1N2Cn8jhb+qF8mRObOX381m1sPww4od8y3IQZn3KPzdogjNyorZ8xmzaXX9cjOe8y3IxcNvNtq2OV41ezFrttYMv30ZOSn8dOavqBrjC3+PnfVGvOfLyE0P4HH6qhr9Zfbmp7p/Lg74EnJT+LdbeyGk4F/PhfhgMp7yJeSm9/yTavza2HyIYwuhPTRkvb6E3NT5fQr/2cRS2HhfDfVB/Qn7EnJLf7rq/pvTq6H9uRa2fBplSOGrTixvhdrUWqj7NMqi8HsV/shsLez5FMq004qXq+th14cok879/upO+FKp2GGfQll09PSsNOLVxXq85VMoUzr7F7fDIx+iTOnNR8fP2UGOnvKl8NPx8+RTPOpTKFt6CH4LAAAAAAAAAAAAAAAAAAAAAAAAAACAf1RX1x9x4x3SPdhnTAAAAABJRU5ErkJggg==","","","","","","","","","","","","","","","","","",""],"frame_max":5,"frames":[[[0,8,32,30,100]],[[0,-16,32,50,130]],[[0,-24,40,80,150]],[[0,-48,24,90,180],[1,-8,40,100,255]],[[0,-56,24,100,200],[1,-8,24,100,255]]]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,8 @@ comment_c456ea59_6018_45ef_8bcc_211a24c627dc =
'items':{
'cls': "只能取keys(钥匙) items(宝石、血瓶) constants(物品) tools(道具)\n$select({\"values\":[\"keys\",\"items\",\"constants\",\"tools\"]})$end",
'name': '名称',
'text': '道具在道具栏中显示的描述'
'text': '道具在道具栏中显示的描述',
'isEquipment': '物品是否属于装备(仅在core.flags.equipment时有效)\n$select({\"values\":[true,false]})$end'
},
'itemEffect':'cls为items的即捡即用类物品的效果,执行时会对这里的字符串执行eval()',
'itemEffectTip':'cls为items的即捡即用类物品,在获得时左上角额外显示的文字,执行时会对这里的字符串执行eval()得到字符串'

View File

@ -3,7 +3,8 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"main": {
"useCompress": " 是否使用压缩文件 \n 当你即将发布你的塔时请使用“JS代码压缩工具”将所有js代码进行压缩然后将这里的useCompress改为true。 \n 请注意只有useCompress是false时才会读取floors目录下的文件为true时会直接读取libs目录下的floors.min.js文件。 \n 如果要进行剧本的修改请务必将其改成false。 \n$select({\"values\":[false]})$end",
"floorIds": " 在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器的顺序和上楼器/下楼器的顺序 \n$leaf(true)$end",
"pngs": " 在此存放所有可能的背景图片背景图片最好是416*416像素其他分辨率会被强制缩放成416*416 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具”来进行压缩,以节省流量 \n 有关使用自定义背景图,请参见文档的“自定义素材”说明 \n\n 依次向后添加 \n$leaf(true)$end",
"pngs": " 在此存放所有可能使用的图片只能是png格式可以不写后缀名 \n 图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加 \n$leaf(true)$end",
"animates": " 在此存放所有可能使用的动画必须是animate格式在这里不写后缀名 \n 动画必须放在animates目录下文件名不能使用中文不能带空格或特殊字符 \n \"jianji\", \"thunder\" \n 根据需求自行添加 \n$leaf(true)$end",
"bgms": " 在此存放所有的bgm和文件名一致。第一项为默认播放项 \n 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n$leaf(true)$end",
"sounds": " 在此存放所有的SE和文件名一致 \n 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n$leaf(true)$end"
},
@ -40,10 +41,11 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"poison": " 游戏过程中的变量或flags \n 毒 ",
"weak": " 衰 ",
"curse": " 咒 "
}
},
"steps": " 行走步数统计 ",
},
"startText": " 游戏开始前剧情。如果无剧情直接留一个空数组即可。 \n$leaf(true)$end",
"shops": {
"shops": "全局商店\n$leaf(true)$end",/*{
"moneyShop1": {
"name": " 定义全局商店(即快捷商店) \n 商店唯一ID \n 商店名称(标题) ",
"icon": " 商店图标blueShop为蓝色商店pinkShop为粉色商店 ",
@ -95,7 +97,7 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
}
]
}
},
},*/
"levelUp": [
" 经验升级所需要的数值,是一个数组 \n 第一项为初始等级可以简单留空也可以写name \n 每一个里面可以含有三个参数 need, name, effect \n need为所需要的经验数值是一个正整数。请确保need所需的依次递增 \n name为该等级的名称也可以省略代表使用系统默认值本项将显示在状态栏中 \n effect为本次升级所执行的操作可由若干项组成由分号分开 \n 其中每一项写法和上面的商店完全相同同样必须是X+=Y的形式Y是一个表达式同样可以使用status:xxx或item:xxx代表勇士的某项数值/道具个数 \n$leaf(true)$end",
{
@ -121,6 +123,8 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"bluePotion": " 蓝血瓶加血数值 ",
"yellowPotion": " 黄血瓶加血数值 ",
"greenPotion": " 绿血瓶加血数值 ",
"sword0": " 默认装备折断的剑的攻击力 ",
"shield0": " 默认装备残破的盾的防御力 ",
"sword1": " 铁剑加攻数值 ",
"shield1": " 铁盾加防数值 ",
"sword2": " 银剑加攻数值 ",
@ -150,8 +154,11 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"pickaxeFourDirections": " 使用破墙镐是否四个方向都破坏如果false则只破坏面前的墙壁 \n$select({\"values\":[true,false]})$end",
"bombFourDirections": " 使用炸弹是否四个方向都会炸如果false则只炸面前的怪物即和圣锤等价 \n$select({\"values\":[true,false]})$end",
"bigKeyIsBox": " 如果此项为true则视为钥匙盒红黄蓝钥匙+1若为false则视为大黄门钥匙 \n$select({\"values\":[true,false]})$end",
"equipment": " 剑和盾是否直接作为装备。如果此项为true则作为装备需要在道具栏使用否则将直接加属性。 \n$select({\"values\":[true,false]})$end",
"enableDeleteItem": " 是否允许删除(丢弃)道具 \n$select({\"values\":[true,false]})$end",
"enableNegativeDamage": " /****** 怪物相关 ******/ \n 是否支持负伤害(回血) \n$select({\"values\":[true,false]})$end",
"zoneSquare": " 领域类型。如果此项为true则为九宫格伤害为false则为十字伤害 \n$select({\"values\":[true,false]})$end",
"hatredDecrease": " 是否在和仇恨怪战斗后减一半的仇恨值此项为false则和仇恨怪不会扣减仇恨值。 \n$select({\"values\":[true,false]})$end",
"betweenAttackCeil": " 夹击方式是向上取整还是向下取整。如果此项为true则为向上取整为false则为向下取整 \n$select({\"values\":[true,false]})$end",
"startDirectly": " /****** 系统相关 ******/ \n 点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面 \n$select({\"values\":[true,false]})$end",
"canOpenBattleAnimate": " 是否允许用户开启战斗过程如果此项为false则下面两项均强制视为false \n$select({\"values\":[true,false]})$end",
"showBattleAnimateConfirm": " 是否在游戏开始时提供“是否开启战斗动画”的选项 \n$select({\"values\":[true,false]})$end",
@ -159,7 +166,9 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"displayEnemyDamage": " 是否地图怪物显伤;用户可以手动在菜单栏中开关 \n$select({\"values\":[true,false]})$end",
"displayExtraDamage": " 是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关 \n$select({\"values\":[true,false]})$end",
"enableGentleClick": " 是否允许轻触(获得面前物品) \n$select({\"values\":[true,false]})$end",
"potionWhileRouting": " 寻路算法是否经过血瓶如果该项为false则寻路算法会自动尽量绕过血瓶 \n$select({\"values\":[true,false]})$end",
"enableViewMaps": " 是否支持在菜单栏中查看所有楼层的地图 \n$select({\"values\":[true,false]})$end",
"portalWithoutTrigger": " 经过楼梯、传送门时是否能“穿透”。穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件 \n$select({\"values\":[true,false]})$end",
"potionWhileRouting": " 寻路算法是否经过血瓶如果该项为false则寻路算法会自动尽量绕过血瓶 \n$select({\"values\":[true,false]})$end"
"enableMoveDirectly": " 是否允许瞬间移动 \n$select({\"values\":[true,false]})$end"
}
}

View File

@ -10,18 +10,25 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"sample0", "sample1", "sample2"
],// 在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器的顺序和上楼器/下楼器的顺序
"pngs" : [
"bg.png", "yewai.png", // 在此存放所有可能的背景图片背景图片最好是416*416像素其他分辨率会被强制缩放成416*416
// 建议对于较大的图片,在网上使用在线的“图片压缩工具”来进行压缩,以节省流量
// 有关使用自定义背景图,请参见文档的“自定义素材”说明
"bg", // 在此存放所有可能使用的图片只能是png格式可以不写后缀名
// 图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。
// 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
// 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量
// 依次向后添加
],
"animates" : [
"hand", "sword", "zone", "yongchang", // "jianji", "thunder"
// 在此存放所有可能使用的动画必须是animate格式在这里不写后缀名
// 动画必须放在animates目录下文件名不能使用中文不能带空格或特殊字符
// 根据需求自行添加
],
"bgms" : [
'058-Slow01.mid', 'bgm.mp3', 'qianjin.mid', 'star.mid',
'bgm.mp3', 'qianjin.mid', 'star.mid',
// 在此存放所有的bgm和文件名一致。第一项为默认播放项
// 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
],
"sounds" : [
'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg',
'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg', 'zone.ogg'
// 在此存放所有的SE和文件名一致
// 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
],
@ -29,7 +36,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"firstData" : {
"title": "魔塔样板", // 游戏名,将显示在标题页面以及切换楼层的界面中
"name": "template", // 游戏的唯一英文标识符。由英文、数字、下划线组成不能超过20个字符。
"version": "Ver 1.0.0 (Beta)", // 当前游戏版本;版本不一致的存档不能通用。
"version": "Ver 1.4.1", // 当前游戏版本;版本不一致的存档不能通用。
"floorId": "sample0", // 初始楼层ID
"hero": {
"name": "阳光", // 勇士初始数据
@ -57,19 +64,19 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
// 毒
"weak": false, // 衰
"curse": false, // 咒
}
},
"steps": 0, // 行走步数统计
},
"startText": [
"Hi欢迎来到 HTML5 魔塔样板!\n\n本样板由艾之葵制作可以让你在不会写任何代码\n的情况下也能做出属于自己的H5魔塔",
"这里游戏开始时的剧情。\n定义在data.js的startText处。\n\n你可以在这里写上自己的内容。",
"赶快来试一试吧!"
], // 游戏开始前剧情。如果无剧情直接留一个空数组即可。
"shops": {
"moneyShop1": {
"name": "贪婪之神", // 定义全局商店(即快捷商店)
// 商店唯一ID
// 商店名称(标题)
"icon": "blueShop", // 商店图标blueShop为蓝色商店pinkShop为粉色商店
"shops": [ // 定义全局商店(即快捷商店)
{
"id": "moneyShop1", // 商店唯一ID
"name": "贪婪之神", // 商店名称(标题)
"icon": "blueShop", // 商店图标在icons.js中的npc一项定义
"textInList": "1F金币商店", // 在快捷商店栏中显示的名称
"use": "money", // 商店所要使用的。只能是"money"或"experience"。
"need": "20+10*times*(times+1)", // 商店需要的金币/经验数值可以是一个表达式以times作为参数计算。
@ -78,8 +85,8 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
// 例如: "need": "25" 就是恒定需要25金币的商店 "need": "20+2*times" 就是第一次访问要20金币以后每次递增2金币的商店。
// 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。
"text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字,需手动加换行符。可以使用${need}表示上面的need值。
"choices": [
{"text": "生命+800", "effect": "status:hp+=800"}, // 商店的选项
"choices": [ // 商店的选项
{"text": "生命+800", "effect": "status:hp+=800"},
// 如果有多个effect以分号分开参见下面的经验商店
{"text": "攻击+4", "effect": "status:atk+=4"},
{"text": "防御+4", "effect": "status:def+=4"},
@ -92,24 +99,24 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
// "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍
]
},
"expShop1": {
"name": "经验之神", // 商店唯一ID
{
"id": "expShop1", // 商店唯一ID
"name": "经验之神",
"icon": "pinkShop",
"textInList": "1F经验商店",
"use": "experience", // 该商店使用的是经验进行计算
"need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1然后下面选项里给定具体数值
"text": "勇敢的武士啊,给我若干经验就可以:",
"choices": [
{"text": "等级+1", "need": "100", "effect": "status:lv+=1;status:hp+=1000;status:atk+=7;status:def+=7"},
// 在choices中写need可以针对每个选项都有不同的需求。
// 这里的need同样可以以times作为参数比如 "need": "100+20*times"
{"text": "等级+1", "need": "100", "effect": "status:lv+=1;status:hp+=1000;status:atk+=7;status:def+=7"},
// 多个effect直接以分号分开即可。如上面的意思是生命+1000攻击+7防御+7。
{"text": "攻击+5", "need": "30", "effect": "status:atk+=5"},
{"text": "防御+5", "need": "30", "effect": "status:def+=5"},
]
},
},
}
],
"levelUp": [
{}, // 经验升级所需要的数值,是一个数组
// 第一项为初始等级可以简单留空也可以写name
@ -150,6 +157,8 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"bluePotion": 250, // 蓝血瓶加血数值
"yellowPotion": 500, // 黄血瓶加血数值
"greenPotion": 800, // 绿血瓶加血数值
"sword0": 0, // 默认装备折断的剑的攻击力
"shield0": 0, // 默认装备残破的盾的防御力
"sword1": 10, // 铁剑加攻数值
"shield1": 10, // 铁盾加防数值
"sword2": 20, // 银剑加攻数值
@ -190,10 +199,13 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"pickaxeFourDirections": true, // 使用破墙镐是否四个方向都破坏如果false则只破坏面前的墙壁
"bombFourDirections": true, // 使用炸弹是否四个方向都会炸如果false则只炸面前的怪物即和圣锤等价
"bigKeyIsBox": false, // 如果此项为true则视为钥匙盒红黄蓝钥匙+1若为false则视为大黄门钥匙
"equipment": false, // 剑和盾是否直接作为装备。如果此项为true则作为装备需要在道具栏使用否则将直接加属性。
"enableDeleteItem": true, // 是否允许删除(丢弃)道具
"enableNegativeDamage": true, /****** 怪物相关 ******/
// 是否支持负伤害(回血)
"zoneSquare": false, // 领域类型。如果此项为true则为九宫格伤害为false则为十字伤害
"hatredDecrease": true, // 是否在和仇恨怪战斗后减一半的仇恨值此项为false则和仇恨怪不会扣减仇恨值。
"betweenAttackCeil": false, // 夹击方式是向上取整还是向下取整。如果此项为true则为向上取整为false则为向下取整
"startDirectly": false, /****** 系统相关 ******/
// 点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面
@ -203,7 +215,9 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"displayEnemyDamage": true, // 是否地图怪物显伤;用户可以手动在菜单栏中开关
"displayExtraDamage": false, // 是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关
"enableGentleClick": true, // 是否允许轻触(获得面前物品)
"portalWithoutTrigger": true, // 经过楼梯、传送门时是否能“穿透”。穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件
"potionWhileRouting": false, // 寻路算法是否经过血瓶如果该项为false则寻路算法会自动尽量绕过血瓶
"enableViewMaps": true, // 是否支持在菜单栏中查看所有楼层的地图
"portalWithoutTrigger": true, // 经过楼梯、传送门时是否能“穿透”。穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件
"enableMoveDirectly": true, // 是否允许瞬间移动
}
}

View File

@ -15,11 +15,11 @@ enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
'zombie': {'name': '兽人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'zombieKnight': {'name': '兽人武士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'rock': {'name': '石头人', 'hp': 100, 'atk': 120, 'def': 0, 'money': 4, 'experience': 0, 'special': 3},
'slimeMan': {'name': '影子战士', 'hp': 100, 'atk': 0, 'def': 0, 'money': 11, 'experience': 0, 'special': 10}, // 模仿怪的攻防设为0就好
'slimeMan': {'name': '影子战士', 'hp': 100, 'atk': 0, 'def': 0, 'money': 11, 'experience': 0, 'special': [10,21], 'atkValue': 2, 'defValue': 3}, // 退化怪可以在后面写atkValue和defValue表示退化的数值
'bluePriest': {'name': '初级法师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 3, 'experience': 0, 'special': 2, 'point': 1}, // 'point'可以在打败怪物后进行加点,详见文档说明。
'redPriest': {'name': '高级法师', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100, 'zoneSquare': true}, // 领域怪需要加value表示领域伤害的数值zoneSquare代表是否九宫格伤害
'redWizard': {'name': '高级巫师', 'hp': 1000, 'atk': 1200, 'def': 0, 'money': 160, 'experience': 0, 'special': 15, 'value': 200, 'range': 2}, // range可选代表领域伤害的范围不加默认为1
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100, 'range': 2}, // 领域怪需要加value表示领域伤害的数值range可选代表领域伤害的范围不加默认为1
'redWizard': {'name': '高级巫师', 'hp': 1000, 'atk': 1200, 'def': 0, 'money': 160, 'experience': 0, 'special': 15, 'value': 200, 'zoneSquare': true}, // zoneSquare可选代表是否九宫格伤害true为是九宫格伤害false或不设置为十字伤害
'yellowGuard': {'name': '初级卫兵', 'hp': 100, 'atk': 120, 'def': 0, 'money': 10, 'experience': 0, 'special': 0},
'blueGuard': {'name': '中级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'redGuard': {'name': '高级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
@ -46,7 +46,7 @@ enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
'goldHornSlime': {'name': '金角怪', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'redKing': {'name': '红衣魔王', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'whiteKing': {'name': '白衣武士', 'hp': 100, 'atk': 120, 'def': 0, 'money': 17, 'experience': 0, 'special': 16},
'blackMagician': {'name': '黑暗大法师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 12, 'experience': 0, 'special': 11, 'value': 1/3, 'bomb': false}, // 吸血怪需要在后面添加value代表吸血比例
'blackMagician': {'name': '黑暗大法师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 12, 'experience': 0, 'special': 11, 'value': 1/3, 'add': true, 'bomb': false}, // 吸血怪需要在后面添加value代表吸血比例添加add: true可以将吸血的伤害加到自身
'silverSlime': {'name': '银头怪', 'hp': 100, 'atk': 120, 'def': 0, 'money': 15, 'experience': 0, 'special': 14},
'swordEmperor': {'name': '剑圣', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'whiteHornSlime': {'name': '尖角怪', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},

View File

@ -9,8 +9,9 @@ main.floors.MT0 =
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "ground", // 默认地面的图块IDterrains中
// "png": "bg.png", // 背景图你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
"png": [], // 该层默认显示的所有图片;详细用法请查看文档“自定义素材”中的说明。
// "color": [0,0,0,0.3], // 该层的默认画面色调。本项可不写代表无色调如果写需要是一个RGBA数组。
// "weather": ["snow",5], // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain"或"snow"代表雨雪第二项为1-10之间的数代表强度。
// "bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
@ -32,6 +33,11 @@ main.floors.MT0 =
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
}
},
"cannotMove": { // 每个图块不可通行的方向
// 可以在这里定义每个点不能前往哪个方向,例如悬崖边不能跳下去
// "x,y": ["up", "left"], // (x,y)点不能往上和左走
},
}

View File

@ -9,8 +9,9 @@ main.floors.sample0 =
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "ground", // 默认地面的图块IDterrains中
// "png": "bg.png", // 背景图你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
"png": [], // 该层默认显示的所有图片;详细用法请查看文档“自定义素材”中的说明。
// "color": [0,0,0,0.3] // 该层的默认画面色调。本项可不写代表无色调如果写需要是一个RGBA数组。
// "weather": ["snow",5], // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain"或"snow"代表雨雪第二项为1-10之间的数代表强度。
"bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
[0, 0, 220, 0, 0, 20, 87, 3, 65, 64, 44, 43, 42],
@ -29,7 +30,7 @@ main.floors.sample0 =
],
"firstArrive": [ // 第一次到该楼层触发的事件
"\t[样板提示]首次到达某层可以触发 firstArrive 事件该事件可类似于RMXP中的“自动执行脚本”。\n\n本事件支持一切的事件类型常常用来触发对话例如",
"\t[hero]我是谁?我从哪来?我又要到哪去?",
"\t[hero]\b[up,hero]我是谁?我从哪来?我又要到哪去?",
"\t[仙子,fairy]你问我...?我也不知道啊...",
"本层主要对道具、门、怪物等进行介绍,有关事件的各种信息在下一层会有更为详细的说明。",
],
@ -37,9 +38,9 @@ main.floors.sample0 =
"10,9": [ // 守着道具的老人
"\t[老人,man]这些是本样板支持的所有的道具。\n\n道具分为三类items, constants, tools。\nitems 为即捡即用类道具,例如宝石、血瓶、剑盾等。\nconstants 为永久道具,例如怪物手册、楼层传送器、幸运金币等。\ntools 为消耗类道具,例如破墙镐、炸弹、中心对称飞行器等。\n\n后两类道具在工具栏中可以看到并使用。",
"\t[老人,man]有关道具效果定义在items.js中。\n目前大多数道具已有默认行为如有自定义的需求则需在items.js中修改代码。",
"\t[老人,man]\b[up]有关道具效果定义在items.js中。\n目前大多数道具已有默认行为如有自定义的需求则需在items.js中修改代码。",
"\t[老人,man]constants 和 tools 各最多只允许12种多了会导致图标溢出。",
"\t[老人,man]拾取道具结束后可触发 afterGetItem 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。",
"\t[老人,man]\b[up]拾取道具结束后可触发 afterGetItem 事件。\n\n有关事件的各种信息在下一层会有更为详细的说明。",
{"type": "hide", "time": 500} // 消失
],
"10,11": [ // 守着门的老人
@ -51,7 +52,6 @@ main.floors.sample0 =
"\t[老人,womanMagician]这些是路障、楼梯、传送门。",
"\t[老人,womanMagician]血网的伤害数值、中毒后每步伤害数值、衰弱时攻防下降的数值,都在 data.js 内定义。\n\n路障同样会尽量被自动寻路绕过。",
"\t[老人,womanMagician]楼梯和传送门需要在changeFloor中定义目标楼层和位置可参见样板里已有的的写法。",
"\t[老人,womanMagician]楼梯和传送门是否可“穿透”由data.js中的全局变量所决定你也可以单独设置。\n穿透的意思是自动寻路得到的路径中间经过了楼梯行走时是否触发楼层转换事件。\n例如下面的“下箭头”就是不能穿透的。",
{"type": "hide", "time": 500}
],
"2,8": [ // 守着第一批怪物的老人
@ -76,7 +76,6 @@ main.floors.sample0 =
{"type": "hide", "time": 500}
]
},
},
"changeFloor": { // 楼层转换事件该事件不能和上面的events有冲突同位置点否则会被覆盖
"6,0": {"floorId": "sample1", "stair": "downFloor"}, // 目标点sample1层的下楼梯位置
@ -86,11 +85,11 @@ main.floors.sample0 =
"2,12": {"floorId": "sample0", "loc": [2,12]},
"3,12": {"floorId": "sample0", "loc": [6,1], "direction": "up"}, // 切换楼层后勇士面对上方
"4,12": {"floorId": "sample0", "loc": [0,9], "direction": "left", "time": 1000}, // 切换楼层后勇士面对左边切换动画1000ms
"5,12": {"floorId": "sample0", "loc": [6,10], "portalWithoutTrigger": false}, // 不能穿透
"6,12": {"floorId": "sample0", "loc": [10,10], "direction": "left", "time": 1000, "portalWithoutTrigger": false},
"5,12": {"floorId": "sample0", "loc": [6,10], "time": 0, "portalWithoutTrigger": false}, // time=0表示无切换时间
"6,12": {"floorId": "sample0", "loc": [10,10], "direction": "left", "time": 1000},
},
"afterBattle": { // 战斗后可能触发的事件列表
"2,6": ["\t[ghostSkeleton]不可能,你怎么可能打败我!\n一个打败怪物触发的事件"]
"2,6": ["\t[ghostSkeleton]不可能,你怎么可能打败我!\n一个打败怪物触发的事件"],
},
"afterGetItem": { // 获得道具后可能触发的事件列表
"11,8": ["由于状态栏放不下绿钥匙和铁门钥匙均视为tools放入工具栏中。\n碰到绿门和铁门仍然会自动使用开门。"],
@ -111,7 +110,12 @@ main.floors.sample0 =
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
"11,12": ["你开了一个绿门触发了一个afterOpenDoor事件"]
}
},
"cannotMove": { // 每个图块不可通行的方向
// 可以在这里定义每个点不能前往哪个方向,例如悬崖边不能跳下去
// "x,y": ["up", "left"], // (x,y)点不能往上和左走
},
}

View File

@ -9,8 +9,9 @@ main.floors.sample1 =
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "grass", // 默认地面的图块IDterrains中
"png": "bg.png", // 背景图你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
"png": [[0,0,"bg"]], // // 该层默认显示的所有图片;详细用法请查看文档“自定义素材”中的说明。
// "color": [0,0,0,0.3] // 该层的默认画面色调。本项可不写代表无色调如果写需要是一个RGBA数组。
"weather": ["snow",6], // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain"或"snow"代表雨雪第二项为1-10之间的数代表强度。
// "bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
[7, 131, 8, 152, 9, 130, 10, 152, 166, 165, 132, 165, 166],
@ -35,8 +36,8 @@ main.floors.sample1 =
"4,10": [ // 走到中间时的提示
"\t[样板提示]本层楼将会对各类事件进行介绍。",
"左边是一个仿50层的陷阱做法上方是商店、快捷商店的使用方法右上是一个典型的杀怪开门的例子右下是各类可能的NPC事件。",
"本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)",
"openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nsetFg: 更改画面色调\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频",
"本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nanimate: 显示动画\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)",
"openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nshowImage: 显示图片\nsetFg: 更改画面色调\nsetWeather: 更改天气\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频",
"if: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中欢迎您宝贵的意见。",
"有关各事件的样例可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后需要调用{\"type\": \"hide\"}该事件才不会再次出现。",
{"type": "hide"}
@ -259,18 +260,34 @@ main.floors.sample1 =
"\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的JS脚本。\n本塔支持的所有主要API会在doc文档内给出。",
"\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后会将你的输入结果直接加到你的攻击力上。",
{"type": "function", "function": `function() { // 自己写JS脚本并执行
var value = prompt("请输入你要加攻击力的数值:"); // 弹出一个输入框让用户输入数据
if (value!=null) {
value=parseInt(value);
if (value>0) { // 检查
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)点不能往上和左走
},
}

View File

@ -9,8 +9,9 @@ main.floors.sample2 =
"canFlyTo": false, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "snowGround", // 默认地面的图块IDterrains中
// "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)点不能往上和左走
},
}

View File

@ -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,
}
}

BIN
project/images/forward.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 32 KiB

BIN
project/images/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

BIN
project/images/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
project/images/rewind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

BIN
project/images/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -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': '可以打开一扇铁门'},

View File

@ -8,7 +8,7 @@ maps_comment_90f36752_8815_4be8_b32b_d7fad1d0542e =
'3':' 蓝墙',
'4':' 星空',
'5':' 岩浆',
'6':' 岩浆',
'6':' 冰面',
'7':' 蓝色商店左',
'8':' 蓝色商店右',
'9':' 粉色商店左',

View File

@ -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'}, // 粉色商店左

BIN
project/sounds/zone.ogg Normal file

Binary file not shown.

View File

@ -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,

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
常用工具/rgss.dll Normal file

Binary file not shown.

Binary file not shown.

View File

@ -3,14 +3,18 @@
=== 全局 ===
[↑][↓][←][→] 移动勇士
[CTRL] 跳过对话
[Z] 转向
[X] 打开/关闭怪物手册
[G] 打开/关闭楼层传送器
[A] 读取自动存档
[S] 打开/关闭存档页面
[D] 打开/关闭读档页面
[K] 打开/关闭快捷商店选择列表
[T] 打开/关闭工具栏
[ESC] 打开/关闭系统菜单
[E] 显示光标
[H] 打开帮助页面
[R] 回放
[SPACE] 轻按(仅在轻按开关打开时有效)
[1] 快捷使用破墙镐
[2] 快捷使用炸弹/圣锤(先检测有没有炸弹,没有再检测圣锤)

View File

@ -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
支持全键盘操作。
支持将某个图片作为某层的背景素材。