diff --git a/.idea/mota-js.xml b/.idea/mota-js.xml
new file mode 100644
index 00000000..24643cc3
--- /dev/null
+++ b/.idea/mota-js.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000..e145e12f
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2018, Zhang Chen
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index db47a83d..2a03e535 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,21 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
## 更新说明
+### 2018.1.21 V1.3.2
+
+* [x] 增加录像和回放功能。
+* [x] 增加统计功能,现在能看到每部塔的游戏人数、通关人数和当前MAX了。
+* [x] 增加浏览地图功能,玩家可以快速查看每层楼的地图。
+* [x] 现在保存文件到本地,以及从本地文件读档了。
+* [x] 可以在全局开关中设置剑盾是否作为装备存在。
+* [x] 修复了部分已知Bug。
+
+### 2018.1.12 V1.3.1
+
+* [x] 增加虚拟键盘
+* [x] 增加自动存档(回退),A键可快速读档
+* [x] 修复几处较为严重的Bug
+
### 2018.1.1 V1.3
* [x] 支持全键盘操作。
diff --git a/_server/css/editor.css b/_server/css/editor.css
index 02b25b10..eb70fd55 100644
--- a/_server/css/editor.css
+++ b/_server/css/editor.css
@@ -47,6 +47,7 @@ body{
white-space: pre;
border: 1px solid #ddd;
border-radius: 2px;
+ overflow: auto;
}
#editTip{
position: absolute;
@@ -68,7 +69,6 @@ body{
margin-right: 20px;
margin-top: 5px;
}
-
#mid{
position: absolute;
left: 448px;
diff --git a/_server/vendor/polyfill.min.js b/_server/vendor/polyfill.min.js
new file mode 100644
index 00000000..80fe8dc8
--- /dev/null
+++ b/_server/vendor/polyfill.min.js
@@ -0,0 +1 @@
+!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(){}function n(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,f._immediateFn(function(){var i=1===e._state?n.onFulfilled:n.onRejected;if(null!==i){var r;try{r=i(e._value)}catch(e){return void o(n.promise,e)}t(n.promise,r)}else(1===e._state?t:o)(n.promise,e._value)})):e._deferreds.push(n)}function t(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof f)return e._state=3,e._value=n,void i(e);if("function"==typeof t)return void r(function(e,n){return function(){e.apply(n,arguments)}}(t,n),e)}e._state=1,e._value=n,i(e)}catch(n){o(e,n)}}function o(e,n){e._state=2,e._value=n,i(e)}function i(e){2===e._state&&0===e._deferreds.length&&f._immediateFn(function(){e._handled||f._unhandledRejectionFn(e._value)});for(var t=0,o=e._deferreds.length;o>t;t++)n(e,e._deferreds[t]);e._deferreds=null}function r(e,n){var i=!1;try{e(function(e){i||(i=!0,t(n,e))},function(e){i||(i=!0,o(n,e))})}catch(e){if(i)return;i=!0,o(n,e)}}function f(e){if(!(this instanceof f))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],r(e,this)}var u=setTimeout,c=f.prototype;c.catch=function(e){return this.then(null,e)},c.then=function(t,o){var i=new this.constructor(e);return n(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(t,o,i)),i},f.all=function(e){return new f(function(n,t){function o(e,f){try{if(f&&("object"==typeof f||"function"==typeof f)){var u=f.then;if("function"==typeof u)return void u.call(f,function(n){o(e,n)},t)}i[e]=f,0==--r&&n(i)}catch(e){t(e)}}if(!e||void 0===e.length)throw new TypeError("Promise.all accepts an array");var i=Array.prototype.slice.call(e);if(0===i.length)return n([]);for(var r=i.length,f=0;i.length>f;f++)o(f,i[f])})},f.resolve=function(e){return e&&"object"==typeof e&&e.constructor===f?e:new f(function(n){n(e)})},f.reject=function(e){return new f(function(n,t){t(e)})},f.race=function(e){return new f(function(n,t){for(var o=0,i=e.length;i>o;o++)e[o].then(n,t)})},f._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){u(e,0)},f._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var a=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==a)return a;throw Error("unable to locate global object")}();a.Promise||(a.Promise=f)});
diff --git a/_server/vm.js b/_server/vm.js
index 4c1644bf..d23e1c91 100644
--- a/_server/vm.js
+++ b/_server/vm.js
@@ -1,23 +1,14 @@
// vue 相关处理
-document.body.onmousedown = function(e){
- selectBox.isSelected = false;
- editor.info = {};
-}
-iconLib.onmousedown = function(e){
- e.stopPropagation();
-}
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+='['
@@ -42,7 +33,9 @@ var exportM = new Vue({
filestr += ']'+(yy==12?'':',\n');
}
pout.value = filestr;
-
+ editArea.mapArr = filestr;
+ this.isExport = true;
+ editArea.error = 0;
tip.whichShow = 2;
}
}
@@ -64,10 +57,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);
@@ -107,7 +105,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('],[');
@@ -128,7 +127,6 @@ var editArea = new Vue({
}
formatArrStr += ']'+(i==12?'':',\n');
}
-
return formatArrStr;
}
}
diff --git a/docs/api.md b/docs/api.md
index ebd28f99..a47385d8 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -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 // 让勇士开始移动
@@ -176,18 +178,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 +207,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 +252,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 +273,7 @@ core.events.changeLight // 改变亮灯(感叹号)的事件
* core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作
// ------ 点击事件和键盘事件的处理 ------
+core.events.longClick // 长按
core.events.keyDownCtrl // 按下Ctrl键时(快捷跳过对话)
core.events.clickConfirmBox // 确认框界面时的点击操作
core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作
@@ -273,6 +287,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 +312,7 @@ core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作
core.events.clickSyncSave // 同步存档界面时的点击操作
core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作
core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作
+core.events.clickKeyBoard // 虚拟键盘界面时的点击操作
core.events.clickAbout // “关于”界面时的点击操作
```
@@ -341,6 +359,7 @@ core.ui.drawPagination // 绘制分页
core.ui.drawEnemyBook // 绘制怪物手册
core.ui.drawBookDetail // 绘制怪物属性的详细信息
core.ui.drawFly // 绘制楼层传送器
+core.ui.drawMaps // 绘制浏览地图界面
core.ui.drawToolbox // 绘制道具栏
core.ui.drawSLPanel // 绘制存档/读档界面
core.ui.drawThumbnail // 绘制一个缩略图
diff --git a/docs/element.md b/docs/element.md
index ef764af5..19b1206b 100644
--- a/docs/element.md
+++ b/docs/element.md
@@ -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`来将吸血的数值加到自身上。
+

中毒怪让勇士中毒后,每步扣减的生命值由`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'表示退化的数值。
+
+
+
如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。
## 路障,楼梯,传送门
@@ -142,10 +149,6 @@ floorId指定的是目标楼层的唯一标识符(ID)。
可以指定time,指定后切换动画时长为指定的数值。
-楼梯和传送门默认可`"穿透"`。所谓穿透,就是当寻路穿过一个楼梯/传送门后,不会触发楼层传送事件,而是继续前进。通过系统Flag可以指定是否穿透,你也可以对每个传送点单独设置该项。
-
-
-
## 背景音乐
本塔支持BGM和SE的播放。
@@ -169,6 +172,8 @@ this.sounds = [ // 在此存放所有的SE,和文件名一致
!> mid格式是通过数学方法模拟出来的音乐效果,质量可能会和实际效果差距较大。
+!> **警告!** mid格式在手机端播放可能会特别卡,仍推荐直接使用mp3/ogg来播放。
+
定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。
**另外,考虑到用户的流量问题,将遵循如下规则:**
@@ -190,17 +195,21 @@ this.sounds = [ // 在此存放所有的SE,和文件名一致
- **点任意块并拖动:** 指定寻路路线
- **单击勇士:** 转向
- **双击勇士:** 轻按(仅在轻按开关打开时有效)
+- **长按任意位置:** 打开虚拟键盘
键盘操作快捷键如下:
- **[CTRL]** 跳过对话
- **[X]** 打开/关闭怪物手册
- **[G]** 打开/关闭楼层传送器
+- **[A]** 读取自动存档
- **[S/D]** 打开/关闭存/读档页面
- **[K]** 打开/关闭快捷商店选择列表
- **[T]** 打开/关闭工具栏
- **[ESC]** 打开/关闭系统菜单
- **[H]** 打开帮助页面
+- **[Z]** 转向
+- **[R]** 回退
- **[SPACE]** 轻按(仅在轻按开关打开时有效)
- **[1]** 快捷使用破墙镐
- **[2]** 快捷使用炸弹/圣锤
diff --git a/docs/event.md b/docs/event.md
index 58d0917e..f1ef08d2 100644
--- a/docs/event.md
+++ b/docs/event.md
@@ -16,7 +16,8 @@
- 启用状态下,该事件才处于可见状态,可被触发、交互与处理。
- 禁用状态下该事件相当于不存在,不可见、不可被触发、不可交互。
-所有事件默认情况下都是启用的,除非指定了`enable: false`。
+所有事件默认情况下都是启用的,除非指定了`enable: false`。
+
在事件列表中使用`type: show`和`type: hide`可以将一个禁用事件启用,或将一个启用事件给禁用。
@@ -465,6 +466,8 @@ direction为可选的,指定的话将使勇士的朝向变成该方向
time为可选的,指定的话将作为楼层切换动画的时间。
+**time也可以置为0,如果为0则没有楼层切换动画。**
+
!> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。**
### changePos: 当前位置切换/勇士转向
@@ -844,8 +847,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 +874,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 +890,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 +972,26 @@ events.prototype.addPoint = function (enemy) {
当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。
+## 使用炸弹后的事件
+
+上面的afterBattle事件只对和怪物进行战斗后才有会被处理。
+
+如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在`events.js`里面的`afterUseBomb`函数进行处理:
+
+``` js
+////// 使用炸弹/圣锤后的事件 //////
+events.prototype.afterUseBomb = function () {
+ // 这是一个使用炸弹也能开门的例子
+ if (core.status.floorId=='xxx' && core.terrainExists(x0,y0,'specialDoor') // 某个楼层,该机关门存在
+ && !core.enemyExists(x1,y1) && !core.enemyExists(x2,y2)) // 且守门的怪物都不存在
+ {
+ core.insertAction([ // 插入事件
+ {"type": "openDoor", "loc": [x0,y0]} // 开门
+ ])
+ }
+}
+```
+
## 战前剧情
有时候光战后事件`afterBattle`是不够的,我们可能还需要战前剧情,例如Boss战之前和Boss进行一段对话。
diff --git a/docs/img/blood.png b/docs/img/blood.png
index 7d369e74..53d52171 100644
Binary files a/docs/img/blood.png and b/docs/img/blood.png differ
diff --git a/docs/img/tuihua.png b/docs/img/tuihua.png
new file mode 100644
index 00000000..de7d1836
Binary files /dev/null and b/docs/img/tuihua.png differ
diff --git a/docs/start.md b/docs/start.md
index 8ddde2af..49676e78 100644
--- a/docs/start.md
+++ b/docs/start.md
@@ -9,7 +9,7 @@
你需要有满足如下条件才能进行制作:
- Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“启动服务.exe”即可)
-- 任一款现代浏览器。强烈推荐Chrome。
+- Chrome浏览器。其他浏览器可能会导致本地服务器产生闪退等现象。
- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。
- ([VSCode下载地址](https://code.visualstudio.com/),群里的群文件中也有,强烈推荐之。)
@@ -28,6 +28,8 @@
* “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。
* “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。
+!> **警告:** 非Chrome浏览器(如Edge/IE等)下本地服务器可能表现不正常,会出现闪退等现象。请务必下载安装Chrome浏览器。
+
## 新建剧本
类似于RMXP,本塔每层楼都是一个“剧本”,剧本内主要定义了本层的地图和各种事件。主函数将读取每个剧本,并生成实际的地图供游戏使用。
@@ -147,7 +149,7 @@
只需要修改自己用到的怪物属性即可,其他没有用到的怪物完全无所谓。
-做完后保存所有文件,然后右键,选择使用chrome浏览器打开`index.html`,就能立刻看到自己的塔并开始游戏啦!是不是很简单呢!
+做完后保存所有文件,在本地服务器中“启动游戏”,就能立刻看到自己的塔并开始游戏啦!是不是很简单呢!

diff --git a/drawMapGUI.html b/drawMapGUI.html
index bddabf11..2a32d6c4 100644
--- a/drawMapGUI.html
+++ b/drawMapGUI.html
@@ -77,31 +77,34 @@
+
+
+
-