diff --git a/README.md b/README.md
index bb72521e..bf22c60d 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
│ └─ *.png # 对应的某个具体的图片素材
├── /libs/ # JS源代码目录
│ ├─ /floors/ # 剧本文件,记录了每个地图的数据和事件
+│ ├─ /thirdparty/ # 游戏所用到的第三方库文件
│ ├─ core.js # 系统核心文件
│ ├─ data.js # 记录了勇士的初始化信息、各个全局变量和全局Flag值
│ ├─ enemys.js # 记录了怪物的信息,包括怪物的数据和特殊属性、伤害计算公式、临界值计算等。
@@ -44,7 +45,21 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
## 更新说明
-### 2017.12.21
+### 2017.12.31 V1.3
+
+* [x] 支持全键盘操作。
+* [x] 支持将某个图片作为某层的背景素材。
+* [x] 便捷PS工具支持更改图片色相。
+* [x] 支持经验升级(进阶/境界塔)。
+* [x] 打败怪物可以进行加点(加点塔)。
+* [x] 增加阻击、N连击等属性;在怪物手册有属性显示。
+* [x] 支持九宫格领域和大范围领域。
+* [x] 增加负伤。
+* [x] 支持各种BGM的播放。
+* [x] 支持不同层使用不同的地面素材;支持多个Autotile同时存在。
+* [x] 许多细节进行了优化,一些已知的Bug进行了修复。
+
+### 2017.12.21 V1.2
* [x] 新增:本地HTTP服务器。
* [x] 新增:可视化地图编辑工具。
@@ -57,7 +72,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
* [x] 新增:更多的默认素材,现在对于大多数地图风格无需P图,直接替换即可。
* [x] 添加部分自定义事件,部分细节优化,一些已知的Bug进行了修复。
-### 2017.12.16
+### 2017.12.16 V1.1
* [x] 新增:战斗过程显示,可以在设置中关闭
* [x] 新增:勇士支持48*32(大图)的行走图
@@ -67,7 +82,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
* [x] 增添Web的Markdown文档,移除原本的doc和pdf文档。
* [x] 修复若干Bug。
-### 2017.12.9
+### 2017.12.9 V1.0
* [x] 发布初版HTML5魔塔样板
diff --git a/docs/api.md b/docs/api.md
index a315eb1e..7720163b 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -6,32 +6,39 @@
可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。
+!> **`main.js`:游戏入口。所有其他JS文件都是被此文件加载。**
+
+``` js
+
+```
+
!> **`core.js`:系统核心文件。所有核心逻辑处理都在此文件完成。**
``` js
* core.status.floorId // 获得当前层floorId
* core.status.thisMap // 获得当前层的地图信息
+* core.status.maps // 获得所有楼层的地图信息
+* core.floors // 获得所有楼层的剧本
// ------ 初始化部分 ------
core.init // 初始化
-core.showStartAnimate // 显示开始界面
-core.hideStartAnimate // 隐藏开始界面
+core.showStartAnimate // 显示游戏开始界面
+core.hideStartAnimate // 隐藏游戏开始界面
core.setStartProgressVal // 设置加载进度条进度
core.setStartLoadTipText // 设置加载进度条提示文字
core.loader // 加载图片和音频
core.loadImage // 加载图片
-core.loadSound // 加载音频
-core.loadSoundItem // 加载某一个音频
+core.loadMusic // 加载音频
core.isPlaying // 游戏是否已经开始
core.clearStatus // 清除游戏状态和数据
core.resetStatus // 重置游戏状态和初始数据
-core.startGame // 具体开始游戏
+core.startGame // 开始游戏
* core.restart // 重新开始游戏;此函数将回到标题页面
// ------ 键盘、鼠标事件 ------
core.onKeyDown // 按下某个键时
core.onKeyUp // 放开某个键时
-core.pressKey // 按住某个键不动时
+core.pressKey // 按住某个键时
core.keyDown // 根据按下键的code来执行一系列操作
core.keyUp // 根据放开键的code来执行一系列操作
core.ondown // 点击(触摸)事件按下时
@@ -39,14 +46,14 @@ core.onmove // 当在触摸屏上滑动时
core.onup // 当点击(触摸)事件放开时
core.getClickLoc // 获得点击事件相对左上角的坐标(0到12之间)
core.onclick // 具体点击屏幕上(x,y)点时,执行的操作
-core.onmousewheel // 滑动鼠标滚轮时的操作(楼层传送时可用滚轮切换楼层)
+core.onmousewheel // 滑动鼠标滚轮时的操作
// ------ 自动寻路代码相关 ------
core.clearAutomaticRouteNode // 清除自动寻路路线
core.stopAutomaticRoute // 停止自动寻路操作
core.continueAutomaticRoute // 继续剩下的自动寻路操作
-core.clearContinueAutomaticRoute // 清除剩下的自动寻路列表
-core.setAutomaticRoute // 设置一个自动寻路
+core.clearContinueAutomaticRoute // 清空剩下的自动寻路列表
+core.setAutomaticRoute // 设置自动寻路路线
core.automaticRoute // 自动寻路算法,找寻最优路径
core.fillPosWithPoint // 显示离散的寻路点
core.clearStepPostfix // 清除已经寻路过的部分
@@ -56,13 +63,15 @@ core.stopAutoHeroMove // 停止勇士的自动行走
core.setAutoHeroMove // 设置勇士的自动行走路线
core.autoHeroMove // 让勇士开始自动行走
core.setHeroMoveInterval // 设置行走的效果动画
-core.setHeroMoveTriggerInterval // 设置勇士行走过程中对途经事件的触发检测
-* core.turnHero(direction) // 设置勇士的方向(转向);如果指定了direction则会面向该方向,否则执行一个转向操作。
+core.setHeroMoveTriggerInterval // 设置勇士行走过程中对事件的触发检测
+* core.turnHero(direction) // 设置勇士的方向(转向)
+core.canMoveHero // 勇士能否前往某方向
core.moveHero // 让勇士开始移动
+core.eventMoveHero // 使用事件让勇士移动。这个函数将不会触发任何事件。
core.moveOneStep // 每移动一格后执行的事件。中毒时在这里进行扣血判断。
core.waitHeroToStop(callback) // 停止勇士的一切行动,等待勇士行动结束后,再执行callback回调函数。
core.stopHero // 停止勇士的移动状态。
-core.drawHero // 在hero层绘制勇士。
+core.drawHero // 绘制勇士。
* core.setHeroLoc(name, value) // 设置勇士的位置。name为”direction”,”x”,”y”
* core.getHeroLoc(name) // 获得勇士的位置。
* core.nextX // 获得勇士面对位置的x坐标
@@ -73,42 +82,49 @@ core.drawHero // 在hero层绘制勇士。
* core.battle(id, x, y, force, callback) // 进行战斗;force表示是否强制战斗
core.afterBattle // 战斗完毕
core.trigger(x,y) // 触发x,y点的事件
-* core.changeFloor(floorId, stair, heroLoc, time, callback) // 楼层切换floorId为目标楼层Id,stair可指定为上/下楼梯,time动画时间
-core.mapChangeAnimate // 实际切换的动画效果
-core.clearMap // 清除地图显示
+* core.changeFloor(floorId, stair, heroLoc, time, callback) // 楼层切换。floorId为目标楼层Id,stair可指定为上/下楼梯,time动画时间
+core.mapChangeAnimate // 地图切换动画效果
+core.clearMap // 清除地图
core.fillText // 在某个canvas上绘制一段文字
core.fillRect // 在某个canvas上绘制一个矩形
core.strokeRect // 在某个canvas上绘制一个矩形的边框
+core.drawLine // 在某个canvas上绘制一条线
core.setFont // 设置某个canvas的文字字体
core.setLineWidth // 设置某个canvas的线宽度
core.saveCanvas // 保存某个canvas状态
-core.loadCanvas // 读取某个canvas状态
+core.loadCanvas // 加载某个canvas状态
core.setStrokeStyle // 设置某个canvas边框属性
core.setAlpha // 设置某个canvas的alpha值
core.setOpacity // 设置某个canvas的透明度
core.setFillStyle // 设置某个canvas的绘制属性(如颜色等)
* core.drawMap(mapId, callback) // 绘制某张地图。mapId为地图Id,绘制完毕将执行callback回调函数。
+core.drawAutotile // 绘制Autotile
+core.drawAutotileBlock // 绘制Autotile的某一块
* core.noPassExists(x,y) // 某个点是否不可通行
core.noPass // 某个点是否在区域内且不可通行
* core.npcExists(x,y) // 某个点是否存在NPC
-* core.terrainExists(x,y) // 某个点是否存在指定的地形
+* core.terrainExists(x,y) // 某个点是否存在(指定的)地形
* core.stairExists(x,y) // 某个点是否存在楼梯
* core.nearStair // 当前位置是否在楼梯边
-* core.enemyExists(x,y) // 某个点是否存在怪物
+* core.enemyExists(x,y) // 某个点是否存在(指定的)怪物
* core.getBlock(x, y, floorId, needEnable) // 获得某个点的block。floorId指定目标楼层,needEnable如果为false则即使该点的事件处于禁用状态也将被返回(否则只有事件启用的点才被返回)
core.moveBlock // 显示移动某块的动画,达到{“type”:”move”}的效果
core.animateBlock // 显示/隐藏某个块时的动画效果
-core.addBlock // 将某个块从禁用变成启用状态
+core.showBlock // 将某个块从禁用变成启用状态
core.removeBlock // 将某个块从启用变成禁用状态
core.removeBlockById // 根据block的索引删除该块
core.removeBlockByIds // 一次性删除多个block
core.addGlobalAnimate // 添加一个全局动画
core.removeGlobalAnimate // 删除一个或所有全局动画
core.setGlobalAnimate // 设置全局动画的显示效果
+core.syncGlobalAnimate // 同步所有的全局动画效果
core.setBoxAnimate // 显示UI层某个box的动画(如怪物手册中怪物的动画)
core.drawBoxAnimate // 绘制UI层的box动画
-core.setFg // 色调渐变
-* core.updateFg // 更新全地图的显伤
+core.updateCheckBlock // 更新领域、夹击、阻击的伤害地图
+core.checkBlock // 检查并执行领域、夹击、阻击事件
+core.snipe // 阻击事件(动画效果)
+core.setFg // 更改画面色调
+* core.updateFg // 更新全地图显伤
* core.itemCount // 获得某个物品的个数
* core.hasItem // 是否存在某个物品
* core.setItem // 设置某个物品的个数
@@ -116,23 +132,23 @@ core.setFg // 色调渐变
* core.useItem // 使用某个物品;直接调用items.js中的useItem函数。
* core.canUseItem // 能否使用某个物品。直接调用items.js中的canUseItem函数。
* core.addItem // 增加某个物品的个数
-* core.getItem // 获得某个物品时的事件
+core.getNextItem // 获得面前的物品(轻按)
+* core.getItem // 获得某个物品
* core.drawTip // 左上角绘制一段提示
* core.drawText // 地图中间绘制一段文字
// ------ 系统机制 ------
core.replaceText // 将文字中的${和}(表达式)进行替换
core.calValue // 计算表达式的值
-core.splitText // 字符串自动换行的分割
+core.doEffect // 执行一个表达式的effect操作
+core.splitLines // 字符串自动换行的分割
core.unshift // 向某个数组前插入另一个数组或元素
core.setLocalStorage // 设置本地存储
core.getLocalStorage // 获得本地存储
core.removeLocalStorage // 移除本地存储
-core.clone // 复制一个对象
+core.clone // 深拷贝一个对象
core.formatDate // 格式化时间为字符串
core.setTwoDigits // 两位数显示
-core.win // 获胜;将直接调用events.js中的win函数
-core.lose // 失败;将直接调用events.js中的lose函数
core.debug // 进入Debug模式,攻防血和钥匙都调成很高的数值
core.checkStatus // 判断当前能否进入某个事件
core.openBook // 点击怪物手册时的打开操作
@@ -146,6 +162,7 @@ core.saveData // 存档到本地
core.loadData // 从本地读档
* core.setStatus // 设置勇士属性
* core.getStatus // 获得勇士属性
+core.getLvName // 获得某个等级的名称
* core.setFlag // 设置某个自定义变量或flag
* core.getFlag // 获得某个自定义变量或flag
* core.hasFlag // 是否存在某个自定义变量或flag,且值为true
@@ -153,17 +170,16 @@ core.insertAction // 往当前事件列表之前插入一系列事件
* core.lockControl // 锁定状态栏,常常用于事件处理
* core.unlockControl // 解锁状态栏
* core.isset // 判断某对象是否不为undefined也不会null
-* core.playSound // 播放音频
* core.playBgm // 播放背景音乐
-core.changeSoundStatus // 切换声音状态
-core.enableSound // 启用音效
-core.disableSound // 禁用音效
+* core.pauseBgm // 暂停背景音乐的播放
+* core.resumeBgm // 恢复背景音乐的播放
+* core.playSound // 播放音频
core.show // 动画显示某对象
core.hide // 动画使某对象消失
core.clearStatusBar // 清空状态栏
core.updateStatusBar // 更新状态栏
core.resize // 屏幕分辨率改变后重新自适应
-core.resetSize // 屏幕分辨率改变后重新自适应
+core.domRenderer // 渲染DOM
// ------ core.js 结束 ------
```
@@ -173,53 +189,105 @@ core.resetSize // 屏幕分辨率改变后重新自适应
!> **`enemys.js` 定义了怪物信息。**
``` js
-* core.enemys.getSpecialText // 获得特殊属性的文字
+core.enemys.init // 初始化
+* core.enemys.getEnemys // 获得一个或所有怪物数据
+* core.enemys.hasSpecial // 判断是否含有某特殊属性
+* core.enemys.getSpecialText // 获得所有特殊属性的名称
+* core.enemys.getSpecialHint // 获得每个特殊属性的说明
* core.enemys.getDamage // 获得某个怪物的伤害
-* core.enemys.getExtraDamage // 获得某个怪物的额外伤害(吸血)
-* core.enemys.getCritical // 计算某个怪物的临界值
-* core.enemys.getCriticalDamage // 计算某个怪物的临界减伤
-* core.enemys.getDefDamage // 计算某个怪物的1防减伤
-* core.enemys.calDamage // 实际的伤害计算公式
-core.enemys.getCurrentEnemys // 获得当前层剩下的的怪物列表
+* core.enemys.getExtraDamage // 获得某个怪物的额外伤害
+* core.enemys.getCritical // 临界值计算
+* core.enemys.getCriticalDamage // 临界减伤计算
+* core.enemys.getDefDamage // 1防减伤计算
+* core.enemys.calDamage // 具体的伤害计算公式
+core.enemys.getCurrentEnemys // 获得当前楼层的怪物列表
```
!> **`events.js` 定义了各个事件的处理流程。**
``` js
-* core.events.startGame // 开始游戏
-* core.events.win // 获胜
-* core.events.lose // 失败
-core.events.checkBlock // 检查领域、夹击事件
-core.events.afterChangeFloor // 楼层切换结束时的事件
+core.events.init // 初始化
+core.events.getEvents // 获得一个或所有系统事件类型
+core.events.startGame // 游戏开始事件
+* core.events.setInitData // 不同难度分别设置初始属性
+* core.events.win // 游戏获胜事件
+* core.events.lose // 游戏失败事件
+core.events.afterChangeFloor // 转换楼层结束的事件
core.events.doEvents // 开始执行一系列自定义事件
core.events.doAction // 执行当前自定义事件列表中的下一个事件
-core.events.insertAction // 往当前自定义事件列表前插入若干个事件
+core.events.insertAction // 往当前事件列表之前添加一个或多个事件
core.events.openShop // 打开一个全局商店
-core.events.disableQuickShop // 禁用一个快捷商店
+core.events.disableQuickShop // 禁用一个全局商店
* core.events.canUseQuickShop // 当前能否使用快捷商店
+* core.events.checkLvUp // 检查升级事件
* core.events.useItem // 尝试使用道具
+core.events.addPoint // 加点事件
core.events.afterBattle // 战斗结束后触发的事件
core.events.afterOpenDoor // 开一个门后触发的事件
core.events.passNet // 经过一个路障
-core.events.beforeSaveData // 即将存档前可以执行的操作
-core.events.afterLoadData // 读档后,载入事件前可以执行的操作
+core.events.changeLight // 改变亮灯(感叹号)的事件
+* core.events.afterChangeLight // 改变亮灯之后,可以触发的事件
+* core.events.afterUseBomb // 使用炸弹/圣锤后的事件
+* core.events.beforeSaveData // 即将存档前可以执行的操作
+* core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作
-// ------ 界面上的点击事件 ------
-core.events.clickAction // 自定义事件处理时,对用户点击的处理
-core.events.clickBook // 怪物手册打开时,对用户点击的处理
-core.events.clickFly // 楼层传送器打开时,对用户点击的处理
-core.events.clickShop // 全局商店打开时,对用户点击的处理
-core.events.clickQuickShop // 快捷商店选项打开时
-core.events.clickToolbox // 工具栏打开时
-core.events.clickSL // 存/读档界面打开时
-core.events.clickSettings // 设置页面打开时
+// ------ 点击事件和键盘事件的处理 ------
+core.events.keyDownCtrl // 按下Ctrl键时(快捷跳过对话)
+core.events.clickConfirmBox // 确认框界面时的点击操作
+core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作
+core.events.clickAction // 自定义事件时的点击操作
+core.events.keyDownAction // 自定义事件时,按下某个键的操作
+core.events.keyUpAction // 自定义事件时,放开某个键的操作
+core.events.clickBook // 怪物手册界面的点击操作
+core.events.keyDownBook // 怪物手册界面时,按下某个键的操作
+core.events.keyUpBook // 怪物手册界面时,放开某个键的操作
+core.events.clickBookDetail // 怪物手册属性显示界面时的点击操作
+core.events.clickFly // 楼层传送器界面时的点击操作
+core.events.keyDownFly // 楼层传送器界面时,按下某个键的操作
+core.events.keyUpFly // 楼层传送器界面时,放开某个键的操作
+core.events.clickShop // 商店界面时的点击操作
+core.events.keyDownShop // 商店界面时,按下某个键的操作
+core.events.keyUpShop // 商店界面时,放开某个键的操作
+core.events.clickQuickShop // 快捷商店界面时的点击操作
+core.events.keyDownQuickShop // 快捷商店界面时,按下某个键的操作
+core.events.keyUpQuickShop // 快捷商店界面时,放开某个键的操作
+core.events.clickToolbox // 工具栏界面时的点击操作
+core.events.clickToolboxIndex // 选择工具栏界面中某个Index后的操作
+core.events.keyDownToolbox // 工具栏界面时,按下某个键的操作
+core.events.keyUpToolbox // 工具栏界面时,放开某个键的操作
+core.events.clickSL // 存读档界面时的点击操作
+core.events.keyDownSL // 存读档界面时,按下某个键的操作
+core.events.keyUpSL // 存读档界面时,放开某个键的操作
+core.events.clickSwitchs // 系统设置界面时的点击操作
+core.events.keyDownSwitchs // 系统设置界面时,按下某个键的操作
+core.events.keyUpSwitchs // 系统设置界面时,放开某个键的操作
+core.events.clickSettings // 系统菜单栏界面时的点击事件
+core.events.keyDownSettings // 系统菜单栏界面时,按下某个键的操作
+core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作
+core.events.clickSyncSave // 同步存档界面时的点击操作
+core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作
+core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作
+core.events.clickAbout // “关于”界面时的点击操作
```
-!> `maps.js` 定义了地图,以及每个数字所代表的意义。
+!> `icons.js` 定义了素材ID和它在图片上的索引的对应关系。
+
+!> `items.js` 定义了每个道具的名称,以及使用效果。
+
+``` js
+core.items.init // 初始化
+core.items.getItems // 获得所有道具
+core.items.getItemEffect // “即捡即用类”道具的使用效果
+core.items.getItemEffectTip // “即捡即用类”道具的文字提示
+* core.items.useItem // 使用道具
+* core.items.cauUseItem // 当前能否使用道具
+```
+
+!> `maps.js` 定义了数字-ID的对应关系。
``` js
core.maps.loadFloor // 加载某个楼层(从剧本或存档中)
-core.maps.getBlock // 将数字替换成实际的内容
+core.maps.getBlock // 数字和ID的对应关系
core.maps.addEvent // 向该楼层添加剧本的自定义事件
core.maps.addChangeFloor // 向该楼层添加剧本的楼层转换事件
core.maps.initMaps // 初始化所有地图
@@ -234,16 +302,19 @@ core.ui.closePanel // 结束一切事件和绘制,关闭UI窗口,返回游
core.ui.drawTextBox // 绘制一个对话框
core.ui.drawChoices // 绘制一个选项界面
core.ui.drawConfirmBox // 绘制一个确认/取消的警告页面
+core.ui.drawSwitchs // 绘制系统设置界面
core.ui.drawSettings // 绘制系统菜单栏
core.ui.drawQuickShop // 绘制快捷商店选择栏
-core.ui.drawBattleAnimate // 绘制战斗过程
-core.ui.drawWaiting // 绘制一个“请稍后”页面
-core.ui.drawSyncSave // 绘制存档同步选项
+core.ui.drawBattleAnimate // 绘制战斗动画
+core.ui.drawWaiting // 绘制等待界面
+core.ui.drawSyncSave // 绘制存档同步界面
core.ui.drawPagination // 绘制分页
core.ui.drawEnemyBook // 绘制怪物手册
+core.ui.drawBookDetail // 绘制怪物属性的详细信息
core.ui.drawFly // 绘制楼层传送器
core.ui.drawToolbox // 绘制道具栏
core.ui.drawSLPanel // 绘制存档/读档界面
core.ui.drawThumbnail // 绘制一个缩略图
core.ui.drawAbout // 绘制“关于”界面
+core.ui.drawHelp // 绘制帮助界面
```
diff --git a/docs/element.md b/docs/element.md
index f515cc10..f946a59f 100644
--- a/docs/element.md
+++ b/docs/element.md
@@ -32,7 +32,7 @@
## 怪物
-本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。
+本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。
如有自己的怪物素材需求请参见[自定义素材](personalization#自定义素材)的内容。
怪物可以有特殊属性,每个怪物可以有多个自定义属性。
@@ -42,14 +42,15 @@
``` js
enemys.prototype.getSpecialText = function (enemyId) {
if (enemyId == undefined) return "";
- var special = this.enemys[enemyId].special;
+ var enemy = this.enemys[enemyId];
+ var special = enemy.special;
var text = [];
if (this.hasSpecial(special, 1)) text.push("先攻");
if (this.hasSpecial(special, 2)) text.push("魔攻");
if (this.hasSpecial(special, 3)) text.push("坚固");
if (this.hasSpecial(special, 4)) text.push("2连击");
if (this.hasSpecial(special, 5)) text.push("3连击");
- if (this.hasSpecial(special, 6)) text.push("4连击");
+ if (this.hasSpecial(special, 6)) text.push((enemy.n||4)+"连击");
if (this.hasSpecial(special, 7)) text.push("破甲");
if (this.hasSpecial(special, 8)) text.push("反击");
if (this.hasSpecial(special, 9)) text.push("净化");
@@ -61,22 +62,36 @@ enemys.prototype.getSpecialText = function (enemyId) {
if (this.hasSpecial(special, 15)) text.push("领域");
if (this.hasSpecial(special, 16)) text.push("夹击");
if (this.hasSpecial(special, 17)) text.push("仇恨");
+ if (this.hasSpecial(special, 18)) text.push("阻击");
+ if (this.hasSpecial(special, 19)) text.push("自爆");
+ if (this.hasSpecial(special, 20)) text.push("无敌");
return text.join(" ");
}
```
-如果需要双属性,则采用100x+y的写法。例如 103 则视为同时拥有1和3的属性,即先攻且坚固。同理1314为衰弱诅咒双属性。
+多属性可采用数组的写法,比如`'special': [1,3]`视为同时拥有先攻和坚固属性;`'special': [5,10,14,18]`视为拥有3连击、魔防、诅咒、阻击四个属性。
-如果需要三属性,则采用10000x+100y+z的写法。例如71116视为同时拥有7,11和16的属性,即破甲、吸血、夹击。
+本塔支持战斗动画,在`data.js`中存在三个全局选项:`canOpenBattleAnimate`, `showBattleAnimateConfirm`, `battleAnimate`。
-四个乃至更多的属性以此类推。
+- `canOpenBattleAnimate`代表是否允许用户开启战斗动画。如果你添加了一些自定义属性,且不想修改战斗界面的UI,则可以将其关闭。
+- `showBattleAnimateConfirm`代表是否在游戏开始时给用户提供开启动画的选项。对于一些偏向于萌新的塔,可以开启此项。
+- `battleAnimate`代表是否默认开启战斗动画。此项会被用户存储的设置给覆盖。
+- 如果`canOpenBattleAnimate`为false,则后面两个也强制为false。
-怪物的伤害计算在下面的`calDamage`函数中,如有自己需求的伤害计算公式请修改该函数的代码。
+怪物可以负伤,在`data.js`的全局变量`enableNegativeDamage`中指定。
+
+下面的`getSpecialHint`函数则给定了每个特殊属性的详细描述。这个描述将在怪物手册中看到。
+
+**打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。**
如果`data.js`中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。
拿到幸运金币后,打怪获得的金币将翻倍。
+N连击怪物的special是6,且我们可以为它定义n代表实际连击数。参见样板中剑王的写法。
+
+
+
吸血怪需要在怪物后添加value,代表吸血的比例。

@@ -91,11 +106,17 @@ enemys.prototype.getSpecialText = function (enemyId) {
领域怪需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。
-
+领域是十字伤害还是九宫格伤害由data.js中的全局变量`zoneSquare`设定。你也可以对该怪物自行进行设定。
-请注意如果吸血和领域同时存在,则value会冲突。**因此请勿将吸血和领域放置在同一个怪物身上。**
+`range`选项可选,代表该领域怪的范围,不写则默认为1。
-本塔暂不支持阻击、激光、自爆、退化等属性。
+
+
+阻击怪同样需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。
+
+!> 阻击怪后退的地点不能有任何事件存在,即使是已经被禁用的自定义事件!
+
+请注意如果吸血、领域、阻击中任何两个同时存在,则value会冲突。**因此请勿将吸血、领域或阻击放置在同一个怪物身上。**
如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。
@@ -125,6 +146,68 @@ floorId指定的是目标楼层的唯一标识符(ID)。

+### 背景音乐
+
+本塔支持BGM和SE的播放。
+
+要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在main.js中进行定义
+
+``` js
+this.bgms = [ // 在此存放所有的bgm,和文件名一致。第一项为默认播放项
+ // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
+ '058-Slow01.mid', 'bgm.mp3', 'qianjin.mid', 'star.mid'
+];
+this.sounds = [ // 在此存放所有的SE,和文件名一致
+ // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
+ 'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg'
+]
+```
+
+!> 音频名不能使用中文,不能带空格或特殊字符。
+
+目前BGM支持主流的音乐格式,如mp3, ogg, mid格式等。SE则不支持mid格式的播放。
+
+!> mid格式是通过数学方法模拟出来的音乐效果,质量可能会和实际效果差距较大。
+
+定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。
+
+**另外,考虑到用户的流量问题,将遵循如下规则:**
+
+- **如果用户当前使用的电脑,则默认开启音乐效果,并播放默认BGM**
+- **如果用户当前使用的手机,且处于Wifi状态,则默认开启音乐效果,并播放默认BGM**
+- **其他情况,将默认关闭音乐效果,只有在用户在菜单栏中点击“音乐开关”后才会播放音乐**
+
+!> iOS平台以及部分浏览器不支持获得当前网络状态,此时即使在使用Wifi也必须要用户点击“音乐开关”才能播放音乐。
+
+### 操作说明
+
+本塔主要支持鼠标(触摸屏)操作和键盘操作。
+
+鼠标(触摸屏)操作说明如下:
+
+- **点状态栏中图标:** 进行对应的操作
+- **点任意块:** 寻路并移动
+- **点任意块并拖动:** 指定寻路路线
+- **单击勇士:** 转向
+- **双击勇士:** 轻按(仅在轻按开关打开时有效)
+
+键盘操作快捷键如下:
+
+- **[CTRL]** 跳过对话
+- **[X]** 打开/关闭怪物手册
+- **[G]** 打开/关闭楼层传送器
+- **[S/D]** 打开/关闭存/读档页面
+- **[K]** 打开/关闭快捷商店选择列表
+- **[T]** 打开/关闭工具栏
+- **[ESC]** 打开/关闭系统菜单
+- **[H]** 打开帮助页面
+- **[SPACE]** 轻按(仅在轻按开关打开时有效)
+- **[1]** 快捷使用破墙镐
+- **[2]** 快捷使用炸弹/圣锤
+- **[3]** 快捷使用中心对称飞行器
+
+以上快捷键也能在游戏菜单中的操作说明中看到。
+
diff --git a/docs/event.md b/docs/event.md
index e85983cf..c7fe63fe 100644
--- a/docs/event.md
+++ b/docs/event.md
@@ -31,6 +31,7 @@
"x,y": {
"trigger": "action", // 触发的trigger, action代表自定义事件
"enable": true, // 该事件初始状态下是否处于启用状态
+ "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。
"data": [ // 实际执行的事件列表
// 事件1
// 事件2
@@ -55,6 +56,7 @@
"x,y": {
// 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略
"enable": true, // 该事件初始状态下是否处于启用状态
+ "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。
"data": [ // 实际执行的事件列表
// 事件1
// 事件2
@@ -73,6 +75,28 @@
"x,y": {
// 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略
// 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false
+ "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。
+ "data": [ // 实际执行的事件列表
+ // 事件1
+ // 事件2
+ // ...
+ ]
+ }
+}
+```
+
+`"noPass"`为该点是否可通行的标记。`true`代表该点不可通行,`false`代表该点可通行。
+
+对于目前所有的素材,都存在默认的是否可通行状态。如果你在该点指定`noPass`,则原本的可通行状态会被覆盖。
+
+因此,除非你想覆盖默认的可通行选项(比如将一个空地设为不可通行),否则该项可以忽略。
+
+``` js
+"events": { // 该楼的所有可能事件列表
+ "x,y": {
+ // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略
+ // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false
+ // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略
"data": [ // 实际执行的事件列表
// 事件1
// 事件2
@@ -88,7 +112,7 @@
``` js
"events": { // 该楼的所有可能事件列表
- // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号
+ // 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号
"x,y": [ // 实际执行的事件列表
// 事件1
// 事件2
@@ -99,7 +123,7 @@
这种简写方式可以极大方便地造塔者进行造塔。
-!> **请注意:如果该点初始的`enable`为`false`,或者该点本身有系统默认事件且需要覆盖(`trigger`),则必须采用上面那种大括号写的方式来定义。**
+!> **请注意:如果该点初始的`enable`为`false`,或者该点本身有系统默认事件且需要覆盖(`trigger`),或者你想覆盖该点的默认通行状态,则必须采用上面那种大括号写的方式来定义。**
@@ -399,7 +423,9 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不
如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。
-强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。强制战斗不会触发任何afterBattle里的事件。
+打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](#加点事件)。
+
+强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。
### openDoor: 开门
@@ -418,8 +444,6 @@ loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层
如果loc所在的点既不是门也不是墙壁,则忽略本事件。
-openDoor不会触发任何afterOpenDoor里的事件。
-
### changeFloor: 楼层切换
在事件中也可以对楼层进行切换。一个比较典型的例子就是TSW中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。
@@ -555,6 +579,26 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate
不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。
+### playBgm: 播放背景音乐
+
+使用playBgm可以播放一个背景音乐。
+
+使用方法:`{"type": "playBgm", "name": "bgm.mp3"}`
+
+值得注意的是,额外添加进文件的背景音乐,需在main.js中this.bgms里加载它。
+
+目前支持mp3/ogg/wav/mid等多种格式的音乐播放。
+
+有关BGM播放的详细说明参见[背景音乐](element#背景音乐)
+
+### pauseBgm: 暂停背景音乐
+
+使用`{"type": "pauseBgm"}`可以暂停背景音乐的播放。
+
+### resumeBgm: 恢复背景音乐
+
+使用`{"type": "resumeBgm"}`可以恢复背景音乐的播放。
+
### playSound: 播放音效
使用playSound可以立刻播放一个音效。
@@ -563,10 +607,6 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate
值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。
-!> 自定义添加的音效名请勿包含`-`,否则将无法正常使用!
-
-由于考虑到用户流量问题,每个额外音效最好不超过20KB。
-
### win: 获得胜利
`{"type": "win", "reason": "xxx"}` 将会直接调用events.js中的win函数,并将reason作为参数传入。
@@ -758,6 +798,43 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用
// ……
```
+## 加点事件
+
+打败怪物后可以进行加点。
+
+如果要对某个怪物进行加点操作,则首先需要修改该怪物的点数值,即在怪物定义的后面添加`point`,代表怪物本身的加点数值。
+
+``` js
+... 'def': 0, 'money': 1, 'experience': 1, 'special': 0, 'point': 1}, // 在怪物后面添加point代表怪物的加点数
+```
+
+然后在`events.js`文件中找到`addPoint`函数。它将返回一个choices事件。修改此函数为我们需要的加点项即可。
+
+``` js
+////// 加点 //////
+events.prototype.addPoint = function (enemy) {
+ var point = enemy.point; // 获得该怪物的point
+ if (!core.isset(point) || point<=0) return [];
+
+ // 加点,返回一个choices事件
+ return [
+ {"type": "choices",
+ "choices": [ // 提供三个选项:对于每一点,攻击+1/防御+2/生命+200
+ {"text": "攻击+"+(1*point), "action": [
+ {"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)}
+ ]},
+ {"text": "防御+"+(2*point), "action": [
+ {"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)}
+ ]},
+ {"text": "生命+"+(200*point), "action": [
+ {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)}
+ ]},
+ ]
+ }
+ ];
+}
+```
+
## 全局商店
我们可以采用上面的choices方式来给出一个商店。这样的商店确实可以有效地进行操作,但是却是"非全局"的,换句话说,只有在碰到NPC的时候才能触发商店事件。
@@ -778,7 +855,6 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用
// 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。
// 例如: "need": "25" 就是恒定需要25金币的商店; "need": "20+2*times" 就是第一次访问要20金币,以后每次递增2金币的商店。
// 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。
-
"text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字,需手动加换行符。可以使用${need}表示上面的need值。
"choices": [ // 商店的选项
{"text": "生命+800", "effect": "status:hp+=800"},
@@ -786,10 +862,12 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用
{"text": "攻击+4", "effect": "status:atk+=4"},
{"text": "防御+4", "effect": "status:def+=4"},
{"text": "魔防+10", "effect": "status:mdef+=10"}
- // effect只能对status和item进行操作,不能修改flag值。且其中间只能用+=符号(也就是只能增加某个属性或道具)
+ // effect只能对status和item进行操作,不能修改flag值。
+ // 必须是X+=Y的形式,其中Y可以是一个表达式,以status:xxx或item:xxx为参数
// 其他effect样例:
// "item:yellowKey+=1" 黄钥匙+1
// "item:pickaxe+=3" 破墙镐+3
+ // "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍
]
},
"expShop1": { // 商店唯一ID
@@ -802,13 +880,13 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用
"choices": [
// 在choices中写need,可以针对每个选项都有不同的需求。
// 这里的need同样可以以times作为参数,比如 "need": "100+20*times"
- {"text": "等级+1", "need": "100", "effect": "status:hp+=1000;status:atk+=7;status:def+=7"},
+ {"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"},
]
- }
-}
+ },
+},
```
全局商店全部定义在`data.js`中的shops一项里。
@@ -889,37 +967,90 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用
当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。
+## 战前剧情
+
+有时候光战后事件`afterBattle`是不够的,我们可能还需要战前剧情,例如Boss战之前和Boss进行一段对话。
+
+要使用战前剧情,首先你需要覆盖该店的系统默认事件,改成你自己的自定义事件,然后在战前剧情后调用`{"type": "battle"}`强制战斗。
+
+值得注意的是,使用这种自定义事件来覆盖系统的默认战斗事件时,可以增加`displayDamage`代表该点是否需要显伤。此项可省略,默认为有显伤。
+
+``` js
+"x,y": { // (x,y)为该怪物坐标
+ "trigger": "action", // 覆盖该点本身默认事件,变成自定义事件
+ "displayDamage": true, // 覆盖后,该点是否有显伤;此项可忽略,默认为有。
+ "data": [ // 该点的自定义事件列表
+ // ... 战前剧情
+ {"type": "battle", "id": "xxx"}, // 强制战斗
+ // ... 战后剧情;请注意上面的强制战斗不会使怪物消失,如有需要请调动{"type": "hide"}
+ ]
+}
+```
+
+## 经验升级(进阶/境界塔)
+
+本塔也支持经验升级,即用户杀怪获得经验后,可以到达某些数值自动进阶,全面提升属性。
+
+要经验升级,你需要先在`data.js`中的全局变量中启用。你需要将`enableExperience`启用经验,且`enableLevelUp`启用进阶。同时你也可以将`enableLv`置为true以在状态栏中显示当前等级(境界)。
+
+同时,你还需要在`data.js`中的`levelUp`来定义每一个进阶所需要的生命值,以及进阶时的效果。
+
+``` js
+"levelUp": [ // 经验升级所需要的数值,是一个数组
+ {}, // 第一项为初始等级,可以简单留空,也可以写name
+
+ // 每一个里面可以含有三个参数 need, name, effect
+ // need为所需要的经验数值,是一个正整数。请确保need所需的依次递增
+ // name为该等级的名称,也可以省略代表使用系统默认值;本项将显示在状态栏中
+ // effect为本次升级所执行的操作,可由若干项组成,由分号分开
+ // 其中每一项写法和上面的商店完全相同,同样必须是X+=Y的形式,Y是一个表达式,同样可以使用status:xxx或item:xxx代表勇士的某项数值/道具个数
+ {"need": 20, "name": "第二级", "effect": "status:hp+=2*(status:atk+status:def);status:atk+=10;status:def+=10"}, // 先将生命提升攻防和的2倍;再将攻击+10,防御+10
+
+ // effect也允许写一个function,代表本次升级将会执行的操作,比如可以显示一段提示文字,或者触发一个事件
+ {"need": 40, "effect": function () {
+ core.drawText("恭喜升级!");
+ core.status.hero.hp *= 2;
+ core.status.hero.atk += 100;
+ core.status.hero.def += 100;
+ }},
+
+ // 依次往下写需要的数值即可
+]
+```
+
+`levelUp`是一个数组,里面分别定义了每个等级的信息。里面每一项是一个object,主要有三个参数`need`, `name`, `effect`
+- `need` 该等级所需要的经验值,是一个正整数。请确保数组中的need依次递增。
+- `name` 该等级的名称,比如“佣兵下级”等。该项可以忽略,以使用系统默认的等级。该项将显示在状态栏中。
+- `effect` 为本次等级执行的操作。它有两种写法:字符串,或函数。
+ - 如果`effect`为字符串,则和上面的全局商店的写法完全相同。可由分号分开,每一项为X+=Y的形式,X为你要修改的勇士属性/道具个数,Y为一个表达式。
+ - 如果`effect`为函数,则也允许写一个`function`,来代表本次升级将会执行的操作。
+
## 开始,难度分歧,获胜与失败
-游戏开始时将调用`events.js`中的startGame函数。
+游戏开始时将调用`events.js`中的`startGame`函数。
-它将显示`data.js`中的startText内容(可以修改成自己的),并正式开始游戏。
+它将显示`data.js`中的startText内容(可以修改成自己的),提供战斗动画开启选择,设置初始福利,并正式开始游戏。
+
+我们可以修改`setInitData`函数来对于不同难度分别设置初始属性。
其参数hard为以下三个字符串之一:`"Easy"`, `"Normal"`, `"Hard"`,分别对应三个难度。针对不同的难度,我们可以设置一些难度分歧。
``` js
-////// 游戏开始事件 //////
-events.prototype.startGame = function (hard) {
-
- if (core.status.isStarting) return;
- core.status.isStarting = true;
-
- core.hideStartAnimate(function() {
- core.drawText(core.clone(core.firstData.startText), function() {
- core.startGame(hard);
- if (hard=='Easy') { // 简单难度
- core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度
- // 可以在此设置一些初始福利,比如设置初始生命值可以调用:
- // core.setStatus("hp", 10000);
- }
- if (hard=='Normal') { // 普通难度
- core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度
- }
- if (hard=='Hard') { // 困难难度
- core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度
- }
- });
- })
+////// 不同难度分别设置初始属性 //////
+events.prototype.setInitData = function (hard) {
+ if (hard=='Easy') { // 简单难度
+ core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度
+ // 可以在此设置一些初始福利,比如设置初始生命值可以调用:
+ // core.setStatus("hp", 10000);
+ // 赠送一把黄钥匙可以调用
+ // core.setItem("yellowKey", 1);
+ }
+ if (hard=='Normal') { // 普通难度
+ core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度
+ }
+ if (hard=='Hard') { // 困难难度
+ core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度
+ }
}
```
diff --git a/docs/img/domainenemy.png b/docs/img/domainenemy.png
deleted file mode 100644
index b74eabcb..00000000
Binary files a/docs/img/domainenemy.png and /dev/null differ
diff --git a/docs/img/nattack.png b/docs/img/nattack.png
new file mode 100644
index 00000000..011b0c55
Binary files /dev/null and b/docs/img/nattack.png differ
diff --git a/docs/img/ps.png b/docs/img/ps.png
index 9e0837e3..9e28c864 100644
Binary files a/docs/img/ps.png and b/docs/img/ps.png differ
diff --git a/docs/img/zone.png b/docs/img/zone.png
new file mode 100644
index 00000000..e5577a9f
Binary files /dev/null and b/docs/img/zone.png differ
diff --git a/docs/index.html b/docs/index.html
index bc546416..3cd3980b 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -2,7 +2,7 @@
-
+ HTML5魔塔样板
@@ -15,7 +15,7 @@
window.$docsify = {
homepage: 'index.md',
loadSidebar: true,
- name: 'H5魔塔样板',
+ name: 'HTML5魔塔样板',
repo: 'https://github.com/ckcz123/mota-js',
// basepath: '../docs/',
diff --git a/docs/personalization.md b/docs/personalization.md
index 72abc06c..9b4eb0ca 100644
--- a/docs/personalization.md
+++ b/docs/personalization.md
@@ -8,7 +8,7 @@
所有素材的图片都在`images`目录下。
- `animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。
-- `autotile.png` 为Autotile块。全塔只支持一种Autotile,其ID为20。
+- `autotile.png` 为Autotile块。
- `enemys.png` 为所有怪物的图片。其对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可)
- `heros.png` 为勇士行走图。
- `items.png` 为所有道具的图标。
@@ -19,10 +19,45 @@
### 使用预定义的素材
-在images目录的“默认素材”下给定了若干预定义的自定义素材。包括野外(草地),星空,木板等等都已经被预先给定。
+在images目录的“默认素材”下给定了若干预定义的自定义素材。
如果你需要某个素材已经存在,则可以直接将其覆盖images目录下的同名文件,就能看到效果。
+### 使用自己的图片作为某层楼的背景素材
+
+由于HTML5功能(素材)有限,导致了对很多比较复杂的素材(比如房子内)等无法有着较好的绘图方式。
+
+为了解决这个问题,我们允许用户自己放置一张图片作为某一层的背景素材。
+
+要启用这个功能,我们首先需要在`main.js`中将可能的图片进行加载。
+
+``` js
+this.pngs = [ // 在此存放所有可能的背景图片;背景图片最好是416*416像素,其他分辨率会被强制缩放成416*416
+ // 建议对于较大的图片,在网上使用在线的“图片压缩工具”来进行压缩,以节省流量
+ "bg.png", // "yewai.png",
+];
+```
+
+!> 背景素材只支持png格式,且会被强制缩放到416*416。
+
+!> 请使用网上的一些[在线图片压缩工具](http://www.asqql.com/gifzip/)对png图片进行压缩,以节省流量。一张500KB的png图片可以被压缩到20-30KB,显示效果不会有太大差异。
+
+之后,我们可以在每层剧本的`"png": "xxx"`里来定义该层的默认背景图片素材。
+
+``` js
+"png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。
+```
+
+你的图片背景素材将会覆盖原来本身的背景层。
+
+**如果你需要让某些点不可通行(比如你建了个房子,墙壁和家具等位置不让通行),则需在`events`中指定`{"noPass": false}`,参见[自定义事件](event#自定义事件)的写法。
+
+``` js
+"events": {
+ "x,y": {"noPass": true} // (x,y)点不可通行
+}
+```
+
### 使用便捷PS工具生成素材
如果我们有更多的素材要求,我们可以使用“便捷PS工具”进行处理。
@@ -31,6 +66,8 @@
我们可以打开有需求改变的素材,和我们需要被替换的素材,然后简单的Ctrl+C和Ctrl+V操作即可。
+便捷PS工具同样支持图片色相的修改,和RMXP几乎完全相同。
+
用这种方式,我们能极快地替换或素材,包括需要新增的怪物。
### 添加素材到游戏
@@ -39,63 +76,73 @@
这是因为,该素材没有被定义,无法被游戏所识别。
+#### 素材的机制
+
+本塔所有的素材都拥有三个属性:**ID**,**索引**,**数字**。
+- **ID** 为该素材的唯一标识符,任何两个素材的ID都不能相同。
+- **索引** 为该素材的在对应图片上的图标索引,即该素材是图片上的第几个。
+- **数字** 为该素材的对应数字,以方便地图的生成和存储。
+
+**`ID-索引` 对应关系定义在icons.js文件中。该文件将唯一确定一个ID在图片上所在的位置。**
+
+**`ID-数字` 对应关系定义在maps.js文件的getBlock函数中。该函数将唯一确定一个ID对应的数字是多少。**
+
+如果需要添加一个素材到游戏,则必须为其分配一个唯一标识符,并同时修改`icons.js`和`maps.js`两个文件。
+
#### 新添加自定义地形(路面、墙壁等)
如果你在terrains.png中新增了一行:
-1. 指定一个唯一的英文ID,不能和terrains中现有的重复。
-2. 进入icons.js,在terrains分类下进行添加(对应图标在图片上的位置,即index)
-3. 指定一个数字,在maps.js的getBlock下类似进行添加。
-``` js
-if (id == 13) tmp.event = {'cls': 'animates', 'id': 'weakNet', 'noPass': false, 'trigger': 'passNet'}; // 衰网
-if (id == 14) tmp.event = {'cls': 'animates', 'id': 'curseNet', 'noPass': false, 'trigger': 'passNet'}; // 咒网
-if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
-// 可以在此处类似添加数字-ID对应关系,但不能和任何已有的数字重复
-// autotile: 20
-if (id == 20) tmp.event = {'cls': 'autotile', 'id': 'autotile', 'noPass': true}; // autotile
-```
+1. 指定一个唯一的英文ID,不能和现有的重复。
+2. 进入icons.js,在terrains分类下进行添加索引(对应图标在图片上的位置,即index)
+
+**如果你无须在游戏内使用本地形,而仅仅是将其作为“背景图”使用,则操作如下:**
+3. 修改对应楼层的剧本文件的`defaultGround`项,改成新的ID。
+
+**如果你要在游戏内使用本地形,则操作如下:**
+3. 指定一个数字,在maps.js的getBlock函数下类似进行添加。
+
+#### 新添加Autotile
+
+如果你需要新增一个Autotile:
+
+1. 将新的Autotile图片复制到images目录下。
+2. 进入icons.js,在autotile分类下进行添加该文件的名称,索引简单的写0。
+3. 指定一个数字,在maps.js的getBlock函数下类似进行添加。
+
+!> Autotile的ID和文件名完全相同!且其ID/文件名不能含有中文、空格或特殊字符。
#### 新添加道具
如果你需要新增一个未被定义的道具:
-1. 指定一个唯一的英文ID,不能和items中现有的重复。
-2. 进入icons.js,在items分类下进行添加(对应图标在图片上的位置,即index)
+1. 指定一个唯一的英文ID,不能和现有的重复。
+2. 进入icons.js,在items分类下进行添加索引(对应图标在图片上的位置,即index)
3. 指定一个数字,在maps.js的getBlock下类似进行添加。
4. 在items.js中仿照其他道具,来添加道具的信息。
-``` js
-if (id == 63) tmp.event = {'cls': 'items', 'id': 'moneyPocket'} // 金钱袋
-if (id == 64) tmp.event = {'cls': 'items', 'id': 'shoes'} // 绿鞋
-if (id == 65) tmp.event = {'cls': 'items', 'id': 'hammer'} // 圣锤
-// 可以在这里添加自己的数字-ID对应关系,但不能和任何已有的数字重复
-```
-
有关如何自行实现一个道具的效果,参见[自定义道具效果](#自定义道具效果)。
#### 新添加怪物
如果我们需要新添加怪物,请在enemys.png中新增一行,然后复制粘贴上四帧怪物图的**1和3帧**。
+你可以通过便捷PS工具的“更改色相”来将红头怪变成橙头怪等。
+
然后执行如下操作:
1. 指定一个唯一的英文ID,不能和enemys中现有的重复。
-2. 进入icons.js,在enemys分类下进行添加(对应图标在图片上的位置,即index)
+2. 进入icons.js,在enemys分类下进行添加索引(对应图标在图片上的位置,即index)
3. 在maps.js的getBlock下继续进行添加。请注意其ID为200开始的顺序,即如果新增一行为261,依次类推
4. 在enemys.js中仿照其他怪物,来添加怪物的信息。
-``` js
-if (id == 258) tmp.event = {'cls': 'enemys', 'id': 'octopus'};
-if (id == 259) tmp.event = {'cls': 'enemys', 'id': 'fairy'};
-if (id == 260) tmp.event = {'cls': 'enemys', 'id': 'greenKnight'};
-// 在此依次添加,数字要求是递增的
-```
-
有关如何自行实现一个怪物的特殊属性或伤害计算公式,参见[怪物的特殊属性](#怪物的特殊属性)。
#### 新添加NPC
-类似同上,给NPC指定ID,在icons.js中指定ID-index关系,在maps.js中指定ID-数字的关系,即可。
+1. 指定一个唯一的英文ID,不能和现有的重复。
+2. 进入icons.js,在npcs分类下进行添加索引(对应图标在图片上的位置,即index)
+3. 指定一个数字,在maps.js的getBlock下类似进行添加。
### 地图生成器使用自定义素材
@@ -178,18 +225,24 @@ enemys.prototype.getExtraDamage = function (monster) {
}
// ... 下略
```
-3. 免疫领域、夹击效果:在`core.js`中,找到updateCheckBlock函数,并编辑成如果有神圣盾标记,则直接返回。
+3. 免疫领域、夹击、阻击效果:在`core.js`中,找到checkBlock函数,并编辑成如果有神圣盾标记,则将伤害变成0。
``` js
-// 更新领域、显伤点
-core.prototype.updateCheckBlock = function() {
- if (!core.isset(core.status.thisMap)) return;
- if (!core.isset(core.status.checkBlockMap)) core.updateCheckBlockMap();
- core.status.checkBlock = [];
- if (core.hasItem('shield5')) return; // 如拥有神圣盾则直接返回
- for (var x=0;x<13;x++) {
- for (var y=0;y<13;y++) {
- // 计算(x,y)点伤害
- var damage = 0;
+// 检查领域、夹击、阻击事件
+core.prototype.checkBlock = function () {
+ var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
+ var damage = core.status.checkBlock.damage[13*x+y];
+ if (damage>0) {
+ if (core.hasFlag("shield5")) damage = 0; // 如果存在神圣盾,则将伤害变成0
+ core.status.hero.hp -= damage;
+
+ // 检查阻击事件
+ var snipe = [];
+ var scan = {
+ 'up': {'x': 0, 'y': -1},
+ 'left': {'x': -1, 'y': 0},
+ 'down': {'x': 0, 'y': 1},
+ 'right': {'x': 1, 'y': 0}
+ }
// ... 下略
```
4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。
@@ -198,42 +251,15 @@ core.prototype.updateCheckBlock = function() {
如果你对现有的怪物不满意,想自行添加怪物属性(例如让怪物拥有双属性乃至更多属性),也是可以的。具体参见`enemys.js`文件。
-你需自己指定一个special数字,修改getSpecialText函数。
+你需自己指定一个special数字,修改getSpecialText函数(属性名)和getSpecialHint函数(属性提示文字)。
如果要修改伤害计算公式,请修改下面的calDamage函数。请注意,如果无法战斗,该函数必须返回`999999999`。
-因此无敌属性可以这样设置:
-
-先给无敌属性指定一个数字,例如18,在getSpecialText中定义
-
-``` js
-// ... 上略
- if (this.hasSpecial(special, 15)) text.push("领域");
- if (this.hasSpecial(special, 16)) text.push("夹击");
- if (this.hasSpecial(special, 17)) text.push("仇恨");
- if (this.hasSpecial(special, 18)) text.push("无敌"); // 添加无敌的显示
- return text.join(" ");
-}
-
-```
-
-然后修改calDamage,如果无敌属性且勇士没有拥有十字架,则立刻返回无穷大。
-
-``` js
-enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special) {
- if (this.hasSpecial(mon_special, 18) && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架
- return 999999999; // 返回无限大
-
- // 魔攻
- if (this.hasSpecial(mon_special, 2)) hero_def = 0;
-// ... 下略
-```
-
对于吸血怪的额外伤害计算在getExtraDamage中。
对于毒衰弱怪物的战斗后结算在`events.js`中的afterBattle函数中。
-对于领域、夹击怪物的检查在`events.js`中的checkBlock函数中。
+对于领域、夹击、阻击怪物的检查在`events.js`中的checkBlock函数中。
`getCritical`, `getCriticalDamage`和`getDefDamage`三个函数依次计算的是该怪物的临界值、临界减伤和1防减伤。也可以适当进行修改。
diff --git a/docs/start.md b/docs/start.md
index 5bcf3cfb..8ddde2af 100644
--- a/docs/start.md
+++ b/docs/start.md
@@ -38,7 +38,23 @@
然后将楼层名改为MT1,floorId改名为MT1;title可以改成任意内容,将在切换楼层时进行显示(比如可以改成“1层小塔”)。
-具体样板文件的每个要素都有详细的注释。我们最终的任务其实是,将每个楼层的剧本(地图&事件)给写完即可。
+具体样板文件的每个要素如下:
+- **`floorId`** 楼层唯一标识符;必须和文件名,以及 `main.floors.xxx` 完全一致
+- **`title`** 楼层中文名,将在切换楼层时进行显示
+- **`canFlyTo`** 当前楼层可否被楼传器飞到。如果该层不能飞到,则也在该层也不允许使用楼传器。
+- **`canUseQuickShop`** 当前楼层可否使用快捷商店。
+- **`defaultGround`** 该层的背景(地面)素材。需要是在`icon.js`里`terrains`中定义的一个ID,如`ground`, `grass2`等等。
+- **`color`** 该层的画面色调。本项可选,如果不写则色调为默认值(无色调),否则是一个RGBA数组,比如`[255,0,0,0.3]`等。
+- **`bgm`** 到达该层后默认播放的BGM。本项可忽略。
+- **`map`** 本层地图,需要是13x13数组,建议使用地图生成器或者可视化地图编辑器制作。
+- **`firstArrive`** 第一次到该楼层触发的事件
+- **`events`** 该楼的所有可能事件列表
+- **`changeFloor`** 楼层转换事件;该事件不能和上面的events有冲突(同位置点),否则会被覆盖
+- **`afterBattle`** 战斗后可能触发的事件列表
+- **`afterGetItem`** 获得道具后可能触发的事件列表
+- **`afterOpenDoor`** 开完门后可能触发的事件列表
+
+我们最终的任务其实是,将每个楼层的剧本(地图&事件)给写完即可。
换句话说,只需要简单的复制操作,我们就可以新建一个剧本了。
diff --git a/images/yewai.png b/images/yewai.png
new file mode 100644
index 00000000..ca42c618
Binary files /dev/null and b/images/yewai.png differ
diff --git a/images/常用素材:如需使用请直接替换目录中的对应文件/autotile1.png b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile1.png
new file mode 100644
index 00000000..2dacd883
Binary files /dev/null and b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile1.png differ
diff --git a/images/常用素材:如需使用请直接替换目录中的对应文件/autotile2.png b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile2.png
new file mode 100644
index 00000000..496bdc9a
Binary files /dev/null and b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile2.png differ
diff --git a/images/常用素材:如需使用请直接替换目录中的对应文件/autotile3.png b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile3.png
new file mode 100644
index 00000000..c7a9179d
Binary files /dev/null and b/images/常用素材:如需使用请直接替换目录中的对应文件/autotile3.png differ
diff --git a/libs/core.js b/libs/core.js
index e863fb15..c09757da 100644
--- a/libs/core.js
+++ b/libs/core.js
@@ -7,6 +7,7 @@ function core() {
this.statusBar = {};
this.canvas = {};
this.images = [];
+ this.pngs = [];
this.bgms = [];
this.sounds = [];
this.floorIds = [];
@@ -104,11 +105,13 @@ function core() {
/////////// 系统事件相关 ///////////
-core.prototype.init = function (dom, statusBar, canvas, images, bgms, sounds, floorIds, floors, coreData) {
+////// 初始化 //////
+core.prototype.init = function (dom, statusBar, canvas, images, pngs, bgms, sounds, floorIds, floors, coreData) {
core.dom = dom;
core.statusBar = statusBar;
core.canvas = canvas;
core.images = images;
+ core.pngs = pngs;
core.bgms = bgms;
core.sounds = sounds;
core.floorIds = floorIds;
@@ -137,8 +140,6 @@ core.prototype.init = function (dom, statusBar, canvas, images, bgms, sounds, fl
core.material.icons = core.icons.getIcons();
core.material.events = core.events.getEvents();
-
-
if (location.protocol.indexOf("http")==0) {
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
try {
@@ -192,6 +193,7 @@ core.prototype.init = function (dom, statusBar, canvas, images, bgms, sounds, fl
});
}
+////// 显示游戏开始界面 //////
core.prototype.showStartAnimate = function (callback) {
core.dom.startPanel.style.opacity=1;
core.dom.startPanel.style.display="block";
@@ -218,6 +220,7 @@ core.prototype.showStartAnimate = function (callback) {
}, 20);
}
+////// 隐藏游戏开始界面 //////
core.prototype.hideStartAnimate = function (callback) {
var opacityVal = 1;
var startAnimate = window.setInterval(function () {
@@ -231,14 +234,17 @@ core.prototype.hideStartAnimate = function (callback) {
}, 20);
}
+////// 设置加载进度条进度 //////
core.prototype.setStartProgressVal = function (val) {
core.dom.startTopProgress.style.width = val + '%';
}
+////// 设置加载进度条提示文字 //////
core.prototype.setStartLoadTipText = function (text) {
core.dom.startTopLoadTips.innerHTML = text;
}
+////// 加载图片和音频 //////
core.prototype.loader = function (callback) {
var loadedImageNum = 0, allImageNum = 0, allSoundNum = 0;
allImageNum = core.images.length;
@@ -253,29 +259,44 @@ core.prototype.loader = function (callback) {
core.setStartLoadTipText(imgName + ' 加载完毕...');
core.setStartProgressVal(loadedImageNum * (100 / allImageNum));
if (loadedImageNum == allImageNum) {
-
- // 加载Autotile
- core.material.images.autotile={};
- var autotileIds = Object.keys(core.material.icons.autotile);
- for (var x=0;x0){
@@ -800,6 +834,7 @@ core.prototype.onup = function () {
}
}
+////// 获得点击事件相对左上角的坐标(0到12之间) //////
core.prototype.getClickLoc = function (x, y) {
var statusBar = {'x': 0, 'y': 0};
@@ -824,6 +859,7 @@ core.prototype.getClickLoc = function (x, y) {
return loc;
}
+////// 具体点击屏幕上(x,y)点时,执行的操作 //////
core.prototype.onclick = function (x, y, stepPostfix) {
// console.log("Click: (" + x + "," + y + ")");
@@ -939,6 +975,7 @@ core.prototype.onclick = function (x, y, stepPostfix) {
}
+////// 滑动鼠标滚轮时的操作 //////
core.prototype.onmousewheel = function (direct) {
// 向下滚动是 -1 ,向上是 1
@@ -951,8 +988,8 @@ core.prototype.onmousewheel = function (direct) {
// 怪物手册
if (core.status.lockControl && core.status.event.id == 'book') {
- if (direct==1) core.ui.drawEnemyBook(core.status.event.data - 1);
- if (direct==-1) core.ui.drawEnemyBook(core.status.event.data + 1);
+ if (direct==1) core.ui.drawBook(core.status.event.data - 1);
+ if (direct==-1) core.ui.drawBook(core.status.event.data + 1);
return;
}
@@ -971,11 +1008,13 @@ core.prototype.onmousewheel = function (direct) {
/////////// 寻路代码相关 ///////////
+////// 清除自动寻路路线 //////
core.prototype.clearAutomaticRouteNode = function (x, y) {
if (core.status.event.id==null)
core.canvas.ui.clearRect(x * 32 + 5, y * 32 + 5, 27, 27);
}
+////// 停止自动寻路操作 //////
core.prototype.stopAutomaticRoute = function () {
if (!core.status.played) {
return;
@@ -989,6 +1028,7 @@ core.prototype.stopAutomaticRoute = function () {
core.canvas.ui.clearRect(0, 0, 416, 416);
}
+////// 继续剩下的自动寻路操作 //////
core.prototype.continueAutomaticRoute = function () {
// 此函数只应由events.afterOpenDoor和events.afterBattle调用
var moveStep = core.status.moveStepBeforeStop;
@@ -999,11 +1039,13 @@ core.prototype.continueAutomaticRoute = function () {
core.setAutoHeroMove(moveStep);
}
+////// 清空剩下的自动寻路列表 //////
core.prototype.clearContinueAutomaticRoute = function () {
core.canvas.ui.clearRect(0, 0, 416, 416);
core.status.moveStepBeforeStop=[];
}
+////// 设置自动寻路路线 //////
core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
if (!core.status.played || core.status.lockControl) {
return;
@@ -1118,7 +1160,8 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
core.status.automaticRoutingTemp = {'destX': 0, 'destY': 0, 'moveStep': []};
}
-// BFS
+
+////// 自动寻路算法,找寻最优路径 //////
core.prototype.automaticRoute = function (destX, destY) {
var startX = core.getHeroLoc('x');
var startY = core.getHeroLoc('y');
@@ -1217,10 +1260,12 @@ core.prototype.automaticRoute = function (destX, destY) {
return ans;
}
+////// 显示离散的寻路点 //////
core.prototype.fillPosWithPoint = function (pos) {
core.fillRect('ui', pos.x*32+12,pos.y*32+12,8,8, '#bfbfbf');
}
+////// 清除已经寻路过的部分 //////
core.prototype.clearStepPostfix = function () {
if(core.status.mouseOutCheck >0){
core.status.mouseOutCheck--;
@@ -1241,6 +1286,7 @@ core.prototype.clearStepPostfix = function () {
/////////// 自动行走 & 行走控制 ///////////
+////// 停止勇士的自动行走 //////
core.prototype.stopAutoHeroMove = function () {
core.status.autoHeroMove = false;
core.status.automaticRouting = false;
@@ -1253,6 +1299,7 @@ core.prototype.stopAutoHeroMove = function () {
clearInterval(core.interval.heroAutoMoveScan);
}
+////// 设置勇士的自动行走路线 //////
core.prototype.setAutoHeroMove = function (steps, start) {
if (steps.length == 0) {
return;
@@ -1272,12 +1319,14 @@ core.prototype.setAutoHeroMove = function (steps, start) {
}, 80);
}
+////// 让勇士开始自动行走 //////
core.prototype.autoHeroMove = function (direction, step) {
core.status.autoHeroMove = true;
core.status.destStep = step;
core.moveHero(direction);
}
+////// 设置行走的效果动画 //////
core.prototype.setHeroMoveInterval = function (direction, x, y, callback) {
if (core.status.heroMoving) {
return;
@@ -1309,6 +1358,7 @@ core.prototype.setHeroMoveInterval = function (direction, x, y, callback) {
}, 12.5);
}
+////// 设置勇士行走过程中对事件的触发检测 //////
core.prototype.setHeroMoveTriggerInterval = function () {
var direction, x, y;
var scan = {
@@ -1322,7 +1372,7 @@ core.prototype.setHeroMoveTriggerInterval = function () {
direction = core.getHeroLoc('direction');
x = core.getHeroLoc('x');
y = core.getHeroLoc('y');
- var noPass = core.noPass(x + scan[direction].x, y + scan[direction].y), canMove = core.canMove();
+ var noPass = core.noPass(x + scan[direction].x, y + scan[direction].y), canMove = core.canMoveHero();
if (noPass || !canMove) {
if (canMove) // 非箭头:触发
core.trigger(x + scan[direction].x, y + scan[direction].y);
@@ -1365,6 +1415,7 @@ core.prototype.setHeroMoveTriggerInterval = function () {
}, 50);
}
+////// 设置勇士的方向(转向) //////
core.prototype.turnHero = function(direction) {
if (core.isset(direction)) {
core.status.hero.loc.direction = direction;
@@ -1378,7 +1429,8 @@ core.prototype.turnHero = function(direction) {
core.canvas.ui.clearRect(0, 0, 416, 416);
}
-core.prototype.canMove = function() {
+////// 勇士能否前往某方向 //////
+core.prototype.canMoveHero = function() {
var direction = core.getHeroLoc('direction');
var nowBlock = core.getBlock(core.getHeroLoc('x'),core.getHeroLoc('y'));
if (nowBlock!=null){
@@ -1416,11 +1468,13 @@ core.prototype.canMove = function() {
return true;
}
+////// 让勇士开始移动 //////
core.prototype.moveHero = function (direction) {
core.setHeroLoc('direction', direction);
core.status.heroStop = false;
}
+/////// 使用事件让勇士移动。这个函数将不会触发任何事件 //////
core.prototype.eventMoveHero = function(steps, time, callback) {
time = time || 100;
@@ -1481,6 +1535,7 @@ core.prototype.eventMoveHero = function(steps, time, callback) {
}, time/8);
}
+////// 每移动一格后执行的事件 //////
core.prototype.moveOneStep = function() {
// 中毒状态
if (core.hasFlag('poison')) {
@@ -1495,6 +1550,7 @@ core.prototype.moveOneStep = function() {
}
}
+////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback //////
core.prototype.waitHeroToStop = function(callback) {
core.stopAutomaticRoute();
core.clearContinueAutomaticRoute();
@@ -1507,10 +1563,12 @@ core.prototype.waitHeroToStop = function(callback) {
}
}
+////// 停止勇士的移动状态 //////
core.prototype.stopHero = function () {
core.status.heroStop = true;
}
+////// 绘制勇士 //////
core.prototype.drawHero = function (direction, x, y, status, offsetX, offsetY) {
offsetX = offsetX || 0;
offsetY = offsetY || 0;
@@ -1523,13 +1581,54 @@ core.prototype.drawHero = function (direction, x, y, status, offsetX, offsetY) {
core.canvas.hero.drawImage(core.material.images.hero, heroIcon[status] * 32, heroIcon.loc * height, 32, height, x + offsetX, y + offsetY + 32-height, 32, height);
}
+////// 设置勇士的位置 //////
+core.prototype.setHeroLoc = function (itemName, itemVal) {
+ if (itemVal == '++') {
+ core.status.hero.loc[itemName]++;
+ return;
+ }
+ else if (itemVal == '--') {
+ core.status.hero.loc[itemName]--;
+ return;
+ }
+ core.status.hero.loc[itemName] = itemVal;
+}
+
+////// 获得勇士的位置 //////
+core.prototype.getHeroLoc = function (itemName) {
+ if (!core.isset(itemName)) return core.status.hero.loc;
+ return core.status.hero.loc[itemName];
+}
+
+////// 获得勇士面对位置的x坐标 //////
+core.prototype.nextX = function() {
+ var scan = {
+ 'up': {'x': 0, 'y': -1},
+ 'left': {'x': -1, 'y': 0},
+ 'down': {'x': 0, 'y': 1},
+ 'right': {'x': 1, 'y': 0}
+ };
+ return core.getHeroLoc('x')+scan[core.getHeroLoc('direction')].x;
+}
+
+////// 获得勇士面对位置的y坐标 //////
+core.prototype.nextY = function () {
+ var scan = {
+ 'up': {'x': 0, 'y': -1},
+ 'left': {'x': -1, 'y': 0},
+ 'down': {'x': 0, 'y': 1},
+ 'right': {'x': 1, 'y': 0}
+ };
+ return core.getHeroLoc('y')+scan[core.getHeroLoc('direction')].y;
+}
+
/////////// 自动行走 & 行走控制 END ///////////
/////////// 地图处理 ///////////
-// 开门
+////// 开门 //////
core.prototype.openDoor = function (id, x, y, needKey, callback) {
if (core.interval.openDoorAnimate!=null) return;
@@ -1580,7 +1679,7 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) {
}, speed)
}
-// 战斗
+////// 战斗 //////
core.prototype.battle = function (id, x, y, force, callback) {
if (core.status.moveStepBeforeStop.length==0) {
core.status.moveStepBeforeStop=core.status.autoStepRoutes.slice(core.status.autoStep-1,core.status.autoStepRoutes.length);
@@ -1609,6 +1708,7 @@ core.prototype.battle = function (id, x, y, force, callback) {
}
}
+////// 战斗完毕 //////
core.prototype.afterBattle = function(id, x, y, callback) {
core.status.hero.hp -= core.enemys.getDamage(id);
if (core.status.hero.hp<=0) {
@@ -1641,6 +1741,7 @@ core.prototype.afterBattle = function(id, x, y, callback) {
}
+////// 触发(x,y)点的事件 //////
core.prototype.trigger = function (x, y) {
var mapBlocks = core.status.thisMap.blocks;
var noPass;
@@ -1667,7 +1768,7 @@ core.prototype.trigger = function (x, y) {
}
}
-// 楼层切换
+////// 楼层切换 //////
core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback) {
time = time || 800;
time /= 20;
@@ -1676,6 +1777,8 @@ core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback)
core.stopAutomaticRoute();
core.clearContinueAutomaticRoute();
core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title;
+ if (!core.isset(stair) && !core.isset(heroLoc))
+ heroLoc = core.status.hero.loc;
if (core.isset(stair)) {
if (!core.isset(heroLoc)) heroLoc={};
var blocks = core.status.maps[floorId].blocks;
@@ -1710,6 +1813,11 @@ core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback)
core.statusBar.floor.style.fontStyle = 'italic';
else core.statusBar.floor.style.fontStyle = 'normal';
+ // 更改BGM
+ if (core.isset(core.floors[floorId].bgm)) {
+ core.playBgm(core.floors[floorId].bgm);
+ }
+
// 不存在事件时,更改画面色调
if (core.status.event.id == null) {
// 默认画面色调
@@ -1750,7 +1858,7 @@ core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback)
}, 50);
}
-// 地图切换
+////// 地图切换动画效果 //////
core.prototype.mapChangeAnimate = function (mode, time, callback) {
if (mode == 'show') {
core.show(core.dom.floorMsgGroup, time, function () {
@@ -1764,6 +1872,7 @@ core.prototype.mapChangeAnimate = function (mode, time, callback) {
}
}
+////// 清除地图 //////
core.prototype.clearMap = function (map, x, y, width, height) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1775,6 +1884,7 @@ core.prototype.clearMap = function (map, x, y, width, height) {
}
}
+////// 在某个canvas上绘制一段文字 //////
core.prototype.fillText = function (map, text, x, y, style, font) {
if (core.isset(style)) {
core.setFillStyle(map, style);
@@ -1785,6 +1895,7 @@ core.prototype.fillText = function (map, text, x, y, style, font) {
core.canvas[map].fillText(text, x, y);
}
+////// 在某个canvas上绘制一个矩形 //////
core.prototype.fillRect = function (map, x, y, width, height, style) {
if (core.isset(style)) {
core.setFillStyle(map, style);
@@ -1792,6 +1903,7 @@ core.prototype.fillRect = function (map, x, y, width, height, style) {
core.canvas[map].fillRect(x, y, width, height);
}
+////// 在某个canvas上绘制一个矩形的边框 //////
core.prototype.strokeRect = function (map, x, y, width, height, style, lineWidth) {
if (core.isset(style)) {
core.setStrokeStyle(map, style);
@@ -1802,6 +1914,7 @@ core.prototype.strokeRect = function (map, x, y, width, height, style, lineWidth
core.canvas[map].strokeRect(x, y, width, height);
}
+////// 在某个canvas上绘制一条线 //////
core.prototype.drawLine = function (map, x1, y1, x2, y2, style, lineWidth) {
if (core.isset(style)) {
core.setStrokeStyle(map, style);
@@ -1815,10 +1928,12 @@ core.prototype.drawLine = function (map, x1, y1, x2, y2, style, lineWidth) {
core.canvas[map].stroke();
}
+////// 设置某个canvas的文字字体 //////
core.prototype.setFont = function (map, font) {
core.canvas[map].font = font;
}
+////// 设置某个canvas的线宽度 //////
core.prototype.setLineWidth = function (map, lineWidth) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1828,14 +1943,17 @@ core.prototype.setLineWidth = function (map, lineWidth) {
core.canvas[map].lineWidth = lineWidth;
}
+////// 保存某个canvas状态 //////
core.prototype.saveCanvas = function (map) {
core.canvas[map].save();
}
+////// 加载某个canvas状态 //////
core.prototype.loadCanvas = function (map) {
core.canvas[map].restore();
}
+////// 设置某个canvas边框属性 //////
core.prototype.setStrokeStyle = function (map, style) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1847,6 +1965,7 @@ core.prototype.setStrokeStyle = function (map, style) {
}
}
+////// 设置某个canvas的alpha值 //////
core.prototype.setAlpha = function (map, alpha) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1856,6 +1975,7 @@ core.prototype.setAlpha = function (map, alpha) {
else core.canvas[map].globalAlpha = alpha;
}
+////// 设置某个canvas的透明度 //////
core.prototype.setOpacity = function (map, opacity) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1865,6 +1985,7 @@ core.prototype.setOpacity = function (map, opacity) {
else core.canvas[map].canvas.style.opacity = opacity;
}
+////// 设置某个canvas的绘制属性(如颜色等) //////
core.prototype.setFillStyle = function (map, style) {
if (map == 'all') {
for (var m in core.canvas) {
@@ -1876,11 +1997,7 @@ core.prototype.setFillStyle = function (map, style) {
}
}
-/**
- * 地图绘制
- * @param mapName 地图ID
- * @param callback 绘制完毕后的回调函数
- */
+////// 绘制某张地图 //////
core.prototype.drawMap = function (mapName, callback) {
var mapData = core.status.maps[mapName];
var mapBlocks = mapData.blocks;
@@ -1999,219 +2116,54 @@ core.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top){
drawBlockByIndex(ctx, dx+left, dy+top, core.material.images['autotile'][block.event.id], index, size);
}
}
-core.prototype.drawMap_old = function (mapName, callback) {
- var mapData = core.status.maps[mapName];
- var mapBlocks = mapData.blocks;
- core.status.floorId = mapName;
- core.status.thisMap = mapData;
- core.clearMap('all');
- core.removeGlobalAnimate(null, null, true);
- var groundId = core.floors[mapName].defaultGround || "ground";
- var blockIcon = core.material.icons.terrains[groundId];
- var blockImage = core.material.images.terrains;
- for (var x = 0; x < 13; x++) {
- for (var y = 0; y < 13; y++) {
- core.canvas.bg.drawImage(blockImage, 0, blockIcon * 32, 32, 32, x * 32, y * 32, 32, 32);
- }
- }
- var autotileMaps = [];
- for (var b = 0; b < mapBlocks.length; b++) {
- // 事件启用
- var block = mapBlocks[b];
- if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable)) {
- if (block.event.cls == 'autotile') {
- // core.drawAutotile();
- autotileMaps[13*block.x + block.y] = block.event.id;
- continue;
- }
- else {
- blockIcon = core.material.icons[block.event.cls][block.event.id];
- blockImage = core.material.images[block.event.cls];
- core.canvas.event.drawImage(core.material.images[block.event.cls], 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
- core.addGlobalAnimate(block.event.animate, block.x * 32, block.y * 32, blockIcon, blockImage);
- }
- }
- }
- core.drawAutotile(mapName, 'event', autotileMaps, 0, 0, 32);
- core.setGlobalAnimate(core.values.animateSpeed);
-
- if (core.isset(callback))
- callback();
-}
-
-core.prototype.drawAutotile_old = function (floorId, canvas, autotileMaps, left, top, size, autotileId) {
-
- if (!core.isset(autotileId)) {
- var autotileIds = {};
- autotileMaps.forEach(function (t) {
- if (core.isset(t)) autotileIds[t]=true;
- });
- Object.keys(autotileIds).forEach(function (t) {
- core.drawAutotile(floorId, canvas, autotileMaps, left, top, size, t);
- })
- return;
- }
-
- var isAutotile = function(x, y) {
- if (x<0 || x>12 || y<0 || y>12) return 1;
- return autotileMaps[13*x+y]==autotileId?1:0;
- }
- for (var xx=0;xx<13;xx++) {
- for (var yy=0;yy<13;yy++) {
- if (isAutotile(xx, yy)) {
- // 绘制autotile
- var id=isAutotile(xx, yy - 1) + 2 * isAutotile(xx - 1, yy) + 4 * isAutotile(xx, yy + 1) + 8 * isAutotile(xx + 1, yy);
- core.drawAutotileBlock(floorId, canvas, left + xx * size, top + yy * size, size, core.material.images.autotile[autotileId], id);
- }
- }
- }
- for (var xx=0;xx<13;xx++) {
- for (var yy=0;yy<13;yy++) {
- if (isAutotile(xx, yy) + isAutotile(xx + 1, yy) + isAutotile(xx + 1, yy + 1) + isAutotile(xx, yy + 1) != 3) continue;
- if (!isAutotile(xx, yy)) {
- core.drawAutotileBlock(floorId, canvas, left + xx * size + size, top + yy * size + size, size, core.material.images.autotile[autotileId], 16);
- }
- if (!isAutotile(xx + 1, yy)) {
- core.drawAutotileBlock(floorId, canvas, left + xx * size + size / 2, top + yy * size + size, size, core.material.images.autotile[autotileId], 17);
- }
- if (!isAutotile(xx + 1, yy + 1)) {
- core.drawAutotileBlock(floorId, canvas, left + xx * size + size / 2, top + yy * size + size / 2, size, core.material.images.autotile[autotileId], 18);
- }
- if (!isAutotile(xx, yy + 1)) {
- core.drawAutotileBlock(floorId, canvas, left + xx * size + size, top + yy * size + size / 2, size, core.material.images.autotile[autotileId], 19);
- }
- }
- }
-}
-
-core.prototype.drawAutotileBlock = function (floorId, map, x, y, size, autotile, index) {
- var canvas = core.canvas[map];
- var groundId = core.floors[floorId].defaultGround || "ground";
- var blockIcon = core.material.icons.terrains[groundId];
- var blockImage = core.material.images.terrains;
- switch (index) {
- case 0:
- canvas.drawImage(autotile, 0, 0, 32, 32, x, y, size, size);
- break;
- case 1:
- canvas.drawImage(autotile, 0, 3 * 32, 16, 32, x, y, size / 2, size);
- canvas.drawImage(autotile, 2 * 32 + 16, 3 * 32, 16, 32, x + size / 2, y, size / 2, size);
- break;
- case 2:
- canvas.drawImage(autotile, 2 * 32, 32, 32, 16, x, y, size, size / 2);
- canvas.drawImage(autotile, 2 * 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2);
- break;
- case 3:
- canvas.drawImage(autotile, 2 * 32, 3 * 32, 32, 32, x, y, size, size);
- break;
- case 4:
- canvas.drawImage(autotile, 0, 1 * 32, 16, 32, x, y, size / 2, size);
- canvas.drawImage(autotile, 2 * 32 + 16, 1 * 32, 16, 32, x + size / 2, y, size / 2, size);
- break;
- case 5:
- canvas.drawImage(autotile, 0, 2 * 32, 16, 32, x, y, size / 2, size);
- canvas.drawImage(autotile, 2 * 32 + 16, 2 * 32, 16, 32, x + size / 2, y, size / 2, size);
- break;
- case 6:
- canvas.drawImage(autotile, 2 * 32, 1 * 32, 32, 32, x, y, size, size);
- break;
- case 7:
- canvas.drawImage(autotile, 2 * 32, 2 * 32, 32, 32, x, y, size, size);
- break;
- case 8:
- canvas.drawImage(autotile, 0, 32, 32, 16, x, y, size, size / 2);
- canvas.drawImage(autotile, 0, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2);
- break;
- case 9:
- canvas.drawImage(autotile, 0, 3 * 32, 32, 32, x, y, size, size);
- break;
- case 10:
- canvas.drawImage(autotile, 32, 32, 32, 16, x, y, size, size / 2);
- canvas.drawImage(autotile, 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2);
- break;
- case 11:
- canvas.drawImage(autotile, 32, 3 * 32, 32, 32, x, y, size, size);
- break;
- case 12:
- canvas.drawImage(autotile, 0, 32, 32, 32, x, y, size, size);
- break;
- case 13:
- canvas.drawImage(autotile, 0, 2 * 32, 32, 32, x, y, size, size);
- break;
- case 14:
- canvas.drawImage(autotile, 32, 32, 32, 32, x, y, size, size);
- break;
- case 15:
- canvas.drawImage(autotile, 32, 2 * 32, 32, 32, x, y, size, size);
- break;
- case 16:
- canvas.clearRect(x, y, size / 2, size / 2);
- canvas.drawImage(blockImage, 0, blockIcon * 32, 16, 16, x, y, size / 2, size / 2);
- canvas.drawImage(autotile, 2 * 32, 0, 16, 16, x, y, size / 2, size / 2);
- break;
- case 17:
- canvas.clearRect(x, y, size / 2, size / 2);
- canvas.drawImage(blockImage, 0, blockIcon * 32, 16, 16, x, y, size / 2, size / 2);
- canvas.drawImage(autotile, 2 * 32 + 16, 0, 16, 16, x, y, size / 2, size / 2);
- break;
- case 18:
- canvas.clearRect(x, y, size / 2, size / 2);
- canvas.drawImage(blockImage, 0, blockIcon * 32, 16, 16, x, y, size / 2, size / 2);
- canvas.drawImage(autotile, 2 * 32 + 16, 16, 16, 16, x, y, size / 2, size / 2);
- break;
- case 19:
- canvas.clearRect(x, y, size / 2, size / 2);
- canvas.drawImage(blockImage, 0, blockIcon * 32, 16, 16, x, y, size / 2, size / 2);
- canvas.drawImage(autotile, 2 * 32, 16, 16, 16, x, y, size / 2, size / 2);
- break;
- }
-}
-
-core.prototype.autotileExists = function (x, y, floorId) {
- var block = core.getBlock(x,y,floorId);
- if (block==null) return false;
- return block.block.event.cls == 'autotile';
-}
+////// 某个点是否不可通行 //////
core.prototype.noPassExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return core.isset(block.block.event.noPass) && block.block.event.noPass;
}
+////// 某个点是否在区域内且不可通行 //////
core.prototype.noPass = function (x, y) {
return x<0 || x>12 || y<0 || y>12 || core.noPassExists(x,y);
}
+////// 某个点是否存在NPC //////
core.prototype.npcExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls == 'npcs';
}
+////// 某个点是否存在(指定的)地形 //////
core.prototype.terrainExists = function (x, y, id, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
- return block.block.event.cls=='terrains' && block.block.event.id==id;
+ return block.block.event.cls=='terrains' && (core.isset(id)?block.block.event.id==id:true);
}
+////// 某个点是否存在楼梯 //////
core.prototype.stairExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls=='terrains' && (block.block.event.id=='upFloor' || block.block.event.id=='downFloor');
}
+////// 当前位置是否在楼梯边 //////
core.prototype.nearStair = function() {
var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
return core.stairExists(x,y) || core.stairExists(x-1,y) || core.stairExists(x,y-1) || core.stairExists(x+1,y) || core.stairExists(x,y+1);
}
+////// 某个点是否存在(指定的)怪物 //////
core.prototype.enemyExists = function (x, y, id,floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls=='enemys' && (core.isset(id)?block.block.event.id==id:true);
}
+////// 获得某个点的block //////
core.prototype.getBlock = function (x, y, floorId, needEnable) {
if (!core.isset(floorId)) floorId=core.status.floorId;
if (!core.isset(needEnable)) needEnable=true;
@@ -2225,6 +2177,7 @@ core.prototype.getBlock = function (x, y, floorId, needEnable) {
return null;
}
+////// 显示移动某块的动画,达到{“type”:”move”}的效果 //////
core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) {
time = time || 500;
@@ -2324,6 +2277,7 @@ core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) {
}, time/16);
}
+////// 显示/隐藏某个块时的动画效果 //////
core.prototype.animateBlock = function (x,y,type,time,callback) {
if (type!='hide') type='show';
@@ -2366,8 +2320,8 @@ core.prototype.animateBlock = function (x,y,type,time,callback) {
}, time/10);
}
-// 显示一个事件
-core.prototype.addBlock = function(x,y,floodId) {
+////// 将某个块从禁用变成启用状态 //////
+core.prototype.showBlock = function(x, y, floodId) {
floodId = floodId || core.status.floorId;
var block = core.getBlock(x,y,floodId,false);
if (block==null) return; // 不存在
@@ -2386,6 +2340,7 @@ core.prototype.addBlock = function(x,y,floodId) {
}
}
+////// 将某个块从启用变成禁用状态 //////
core.prototype.removeBlock = function (x, y, floorId) {
floorId = floorId || core.status.floorId;
@@ -2405,7 +2360,7 @@ core.prototype.removeBlock = function (x, y, floorId) {
core.updateFg();
}
-
+////// 根据block的索引删除该块 //////
core.prototype.removeBlockById = function (index, floorId) {
var blocks = core.status.maps[floorId].blocks;
@@ -2424,12 +2379,14 @@ core.prototype.removeBlockById = function (index, floorId) {
blocks[index].enable = false;
}
+////// 一次性删除多个block //////
core.prototype.removeBlockByIds = function (floorId, ids) {
ids.sort(function (a,b) {return b-a}).forEach(function (id) {
core.removeBlockById(id, floorId);
});
}
+////// 添加一个全局动画 //////
core.prototype.addGlobalAnimate = function (animateMore, x, y, loc, image) {
if (animateMore == 2) {
core.status.twoAnimateObjs.push({
@@ -2451,6 +2408,7 @@ core.prototype.addGlobalAnimate = function (animateMore, x, y, loc, image) {
}
}
+////// 删除一个或所有全局动画 //////
core.prototype.removeGlobalAnimate = function (x, y, all) {
if (all == true) {
core.status.twoAnimateObjs = [];
@@ -2470,6 +2428,7 @@ core.prototype.removeGlobalAnimate = function (x, y, all) {
}
}
+////// 设置全局动画的显示效果 //////
core.prototype.setGlobalAnimate = function (speed) {
clearInterval(core.interval.twoAnimate);
clearInterval(core.interval.fourAnimate);
@@ -2498,6 +2457,7 @@ core.prototype.setGlobalAnimate = function (speed) {
}, speed / 2);
}
+////// 同步所有的全局动画效果 //////
core.prototype.syncGlobalAnimate = function () {
core.status.twoAnimateObjs.forEach(function (t) {
t.status=0;
@@ -2507,6 +2467,7 @@ core.prototype.syncGlobalAnimate = function () {
})
}
+////// 显示UI层某个box的动画 //////
core.prototype.setBoxAnimate = function () {
clearInterval(core.interval.boxAnimate);
if (core.status.boxAnimateObjs.length > 0) {
@@ -2518,6 +2479,7 @@ core.prototype.setBoxAnimate = function () {
}
}
+////// 绘制UI层的box动画 //////
core.prototype.drawBoxAnimate = function (background) {
for (var a = 0; a < core.status.boxAnimateObjs.length; a++) {
var obj = core.status.boxAnimateObjs[a];
@@ -2529,7 +2491,7 @@ core.prototype.drawBoxAnimate = function (background) {
}
}
-// 更新领域、显伤
+////// 更新领域、夹击、阻击的伤害地图 //////
core.prototype.updateCheckBlock = function() {
core.status.checkBlock = {};
if (!core.isset(core.status.thisMap)) return;
@@ -2623,8 +2585,8 @@ core.prototype.updateCheckBlock = function() {
}
}
+////// 检查并执行领域、夹击、阻击事件 //////
core.prototype.checkBlock = function () {
- // 检查领域、夹击事件
var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
var damage = core.status.checkBlock.damage[13*x+y];
if (damage>0) {
@@ -2650,14 +2612,14 @@ core.prototype.checkBlock = function () {
}
}
- if (core.status.checkBlock.betweenAttack[13*x+y]) {
+ if (core.status.checkBlock.betweenAttack[13*x+y] && damage>0) {
core.drawTip('受到夹击,生命变成一半');
}
// 阻击
- else if (snipe.length>0) {
+ else if (snipe.length>0 && damage>0) {
core.drawTip('受到阻击伤害'+damage+'点');
}
- else {
+ else if (damage>0) {
core.drawTip('受到领域伤害'+damage+'点');
}
if (core.status.hero.hp<=0) {
@@ -2678,6 +2640,7 @@ core.prototype.checkBlock = function () {
}
}
+////// 阻击事件(动画效果) //////
core.prototype.snipe = function (snipes) {
core.waitHeroToStop(function() {
core.lockControl();
@@ -2782,6 +2745,7 @@ core.prototype.snipe = function (snipes) {
});
}
+////// 更改画面色调 //////
core.prototype.setFg = function(color, time, callback) {
if (!core.isset(time)) time=750;
if (time<=0) time=0;
@@ -2836,47 +2800,7 @@ core.prototype.setFg = function(color, time, callback) {
}
-
-core.prototype.setHeroLoc = function (itemName, itemVal) {
- if (itemVal == '++') {
- core.status.hero.loc[itemName]++;
- return;
- }
- else if (itemVal == '--') {
- core.status.hero.loc[itemName]--;
- return;
- }
- core.status.hero.loc[itemName] = itemVal;
-}
-
-core.prototype.getHeroLoc = function (itemName) {
- if (!core.isset(itemName)) return core.status.hero.loc;
- return core.status.hero.loc[itemName];
-}
-
-core.prototype.nextX = function() {
- var scan = {
- 'up': {'x': 0, 'y': -1},
- 'left': {'x': -1, 'y': 0},
- 'down': {'x': 0, 'y': 1},
- 'right': {'x': 1, 'y': 0}
- };
- return core.getHeroLoc('x')+scan[core.getHeroLoc('direction')].x;
-}
-
-core.prototype.nextY = function () {
- var scan = {
- 'up': {'x': 0, 'y': -1},
- 'left': {'x': -1, 'y': 0},
- 'down': {'x': 0, 'y': 1},
- 'right': {'x': 1, 'y': 0}
- };
- return core.getHeroLoc('y')+scan[core.getHeroLoc('direction')].y;
-}
-
-/**
- * 更新显伤
- */
+////// 更新全地图显伤 //////
core.prototype.updateFg = function () {
if (!core.isset(core.status.thisMap) || !core.isset(core.status.thisMap.blocks)) return;
@@ -2891,8 +2815,19 @@ core.prototype.updateFg = function () {
core.canvas.fg.textAlign = 'left';
for (var b = 0; b < mapBlocks.length; b++) {
var x = mapBlocks[b].x, y = mapBlocks[b].y;
- if (core.isset(mapBlocks[b].event) && mapBlocks[b].event.cls == 'enemys' && mapBlocks[b].event.trigger == 'battle'
+ if (core.isset(mapBlocks[b].event) && mapBlocks[b].event.cls == 'enemys'
&& !(core.isset(mapBlocks[b].enable) && !mapBlocks[b].enable)) {
+
+ // 非系统默认的战斗事件(被覆盖)
+ if (mapBlocks[b].event.trigger != 'battle') {
+ // 判断显伤
+ var event = core.floors[core.status.floorId].events[x+","+y];
+ if (core.isset(event) && !(event instanceof Array)) {
+ if (core.isset(event.displayDamage) && !event.displayDamage)
+ continue;
+ }
+ }
+
var id = mapBlocks[b].event.id;
var damage = core.enemys.getDamage(id);
@@ -2939,9 +2874,7 @@ core.prototype.updateFg = function () {
}
}
-/**
- * 物品处理 start
- */
+////// 获得某个物品的个数 //////
core.prototype.itemCount = function (itemId) {
if (!core.isset(itemId) || !core.isset(core.material.items[itemId])) return 0;
var itemCls = core.material.items[itemId].cls;
@@ -2949,10 +2882,12 @@ core.prototype.itemCount = function (itemId) {
return core.isset(core.status.hero.items[itemCls][itemId]) ? core.status.hero.items[itemCls][itemId] : 0;
}
+////// 是否存在某个物品 //////
core.prototype.hasItem = function (itemId) {
return core.itemCount(itemId) > 0;
}
+////// 设置某个物品的个数 //////
core.prototype.setItem = function (itemId, itemNum) {
var itemCls = core.material.items[itemId].cls;
if (itemCls == 'items') return;
@@ -2962,6 +2897,7 @@ core.prototype.setItem = function (itemId, itemNum) {
core.status.hero.items[itemCls][itemId] = itemNum;
}
+////// 删除某个物品 //////
core.prototype.removeItem = function (itemId) {
if (!core.hasItem(itemId)) return false;
var itemCls = core.material.items[itemId].cls;
@@ -2973,15 +2909,18 @@ core.prototype.removeItem = function (itemId) {
return true;
}
+////// 使用某个物品 //////
core.prototype.useItem = function (itemId) {
core.items.useItem(itemId);
return;
}
+////// 能否使用某个物品 //////
core.prototype.canUseItem = function (itemId) {
return core.items.canUseItem(itemId);
}
+////// 增加某个物品的个数 //////
core.prototype.addItem = function (itemId, itemNum) {
var itemData = core.material.items[itemId];
var itemCls = itemData.cls;
@@ -2996,6 +2935,7 @@ core.prototype.addItem = function (itemId, itemNum) {
core.status.hero.items[itemCls][itemId] += itemNum;
}
+////// 获得面前的物品(轻按) //////
core.prototype.getNextItem = function() {
if (!core.status.heroStop || !core.flags.enableGentleClick) return;
var nextX = core.nextX(), nextY = core.nextY();
@@ -3006,6 +2946,7 @@ core.prototype.getNextItem = function() {
}
}
+////// 获得某个物品 //////
core.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) {
// core.getItemAnimate(itemId, itemNum, itemX, itemY);
core.playSound('item.ogg');
@@ -3027,6 +2968,7 @@ core.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) {
else if (core.isset(callback)) callback();
}
+////// 左上角绘制一段提示 //////
core.prototype.drawTip = function (text, itemIcon) {
var textX, textY, width, height, hide = false, opacityVal = 0;
clearInterval(core.interval.tipAnimate);
@@ -3082,6 +3024,7 @@ core.prototype.drawTip = function (text, itemIcon) {
}, 30);
}
+////// 地图中间绘制一段文字 //////
core.prototype.drawText = function (contents, callback) {
if (core.isset(contents)) {
if (typeof contents == 'string') {
@@ -3124,18 +3067,19 @@ core.prototype.drawText = function (contents, callback) {
/////////// 地图相关 END ///////////
-/**
- * 系统机制 start
- */
-// 替换文本中的数为实际值
+
+
+/////////// 系统机制 ///////////
+
+////// 将文字中的${和}(表达式)进行替换 //////
core.prototype.replaceText = function (text) {
return text.replace(/\${([^}]+)}/g, function (word, value) {
return core.calValue(value);
});
}
-
+////// 计算表达式的值 //////
core.prototype.calValue = function (value) {
value=value.replace(/status:([\w\d_]+)/g, "core.getStatus('$1')");
value=value.replace(/item:([\w\d_]+)/g, "core.itemCount('$1')");
@@ -3143,6 +3087,7 @@ core.prototype.calValue = function (value) {
return eval(value);
}
+////// 执行一个表达式的effect操作 //////
core.prototype.doEffect = function (expression) {
// 必须使用"+="
var arr = expression.split("+=");
@@ -3158,6 +3103,7 @@ core.prototype.doEffect = function (expression) {
}
}
+////// 字符串自动换行的分割 //////
core.prototype.splitLines = function(canvas, text, maxLength, font) {
if (core.isset(font)) core.setFont(canvas, font);
@@ -3182,6 +3128,7 @@ core.prototype.splitLines = function(canvas, text, maxLength, font) {
return contents;
}
+////// 向某个数组前插入另一个数组或元素 //////
core.prototype.unshift = function (a,b) {
if (!(a instanceof Array) || !core.isset(b)) return;
if (b instanceof Array) {
@@ -3193,6 +3140,7 @@ core.prototype.unshift = function (a,b) {
return a;
}
+////// 设置本地存储 //////
core.prototype.setLocalStorage = function(key, value) {
try {
localStorage.setItem(core.firstData.name + "_" + key, JSON.stringify(value));
@@ -3203,16 +3151,20 @@ core.prototype.setLocalStorage = function(key, value) {
return false;
}
}
+
+////// 获得本地存储 //////
core.prototype.getLocalStorage = function(key, defaultValue) {
var value = localStorage.getItem(core.firstData.name+"_"+key);
if (core.isset(value)) return JSON.parse(value);
return defaultValue;
}
+////// 移除本地存储 //////
core.prototype.removeLocalStorage = function (key) {
localStorage.removeItem(core.firstData.name+"_"+key);
}
+////// 深拷贝一个对象 //////
core.prototype.clone = function (data) {
if (!core.isset(data)) return data;
// date
@@ -3247,25 +3199,19 @@ core.prototype.clone = function (data) {
return data;
}
+////// 格式化时间为字符串 //////
core.prototype.formatDate = function(date) {
if (!core.isset(date)) return "";
return date.getFullYear()+"-"+core.setTwoDigits(date.getMonth()+1)+"-"+core.setTwoDigits(date.getDate())+" "
+core.setTwoDigits(date.getHours())+":"+core.setTwoDigits(date.getMinutes())+":"+core.setTwoDigits(date.getSeconds());
}
+////// 两位数显示 //////
core.prototype.setTwoDigits = function (x) {
return parseInt(x)<10?"0"+x:x;
}
-core.prototype.win = function(reason) {
- core.events.win(reason);
-}
-
-core.prototype.lose = function(reason) {
- core.events.lose(reason);
-}
-
-// 作弊
+////// 作弊 //////
core.prototype.debug = function() {
core.setStatus('hp', 999999);
core.setStatus('atk', 10000);
@@ -3285,9 +3231,10 @@ core.prototype.debug = function() {
core.drawTip("作弊成功");
}
-core.prototype.checkStatus = function (name, need, item, clearData) {
+////// 判断当前能否进入某个事件 //////
+core.prototype.checkStatus = function (name, need, item) {
if (need && core.status.event.id == name) {
- core.ui.closePanel(clearData);
+ core.ui.closePanel();
return false;
}
@@ -3307,12 +3254,14 @@ core.prototype.checkStatus = function (name, need, item, clearData) {
return true;
}
+////// 点击怪物手册时的打开操作 //////
core.prototype.openBook = function (need) {
- if (!core.checkStatus('book', need, true, true))
+ if (!core.checkStatus('book', need, true))
return;
core.useItem('book');
}
+////// 点击楼层传送器时的打开操作 //////
core.prototype.useFly = function (need) {
if (!core.checkStatus('fly', need, true))
return;
@@ -3334,18 +3283,21 @@ core.prototype.useFly = function (need) {
return;
}
+////// 点击工具栏时的打开操作 //////
core.prototype.openToolbox = function (need) {
if (!core.checkStatus('toolbox', need))
return;
core.ui.drawToolbox();
}
+////// 点击保存按钮时的打开操作 //////
core.prototype.save = function(need) {
if (!core.checkStatus('save', need))
return;
core.ui.drawSLPanel(core.status.saveIndex);
}
+////// 点击读取按钮时的打开操作 //////
core.prototype.load = function (need) {
// 游戏开始前读档
@@ -3362,6 +3314,7 @@ core.prototype.load = function (need) {
core.ui.drawSLPanel(core.status.saveIndex);
}
+////// 实际进行存读档事件 //////
core.prototype.doSL = function (id, type) {
core.status.saveIndex=id;
if (type=='save') {
@@ -3395,6 +3348,7 @@ core.prototype.doSL = function (id, type) {
}
}
+////// 存档同步操作 //////
core.prototype.syncSave = function(type) {
if (type=='save') {
core.status.event.selection=1;
@@ -3523,6 +3477,7 @@ core.prototype.syncSave = function(type) {
}
+////// 存档到本地 //////
core.prototype.saveData = function(dataId) {
var data = {
'floorId': core.status.floorId,
@@ -3545,6 +3500,7 @@ core.prototype.saveData = function(dataId) {
return core.setLocalStorage(dataId, data);
}
+////// 从本地读档 //////
core.prototype.loadData = function (data, callback) {
core.resetStatus(data.hero, data.hard, data.floorId, core.maps.load(data.maps));
@@ -3563,24 +3519,29 @@ core.prototype.loadData = function (data, callback) {
});
}
+////// 设置勇士属性 //////
core.prototype.setStatus = function (statusName, statusVal) {
core.status.hero[statusName] = statusVal;
}
+////// 获得勇士属性 //////
core.prototype.getStatus = function (statusName) {
return core.status.hero[statusName];
}
+////// 获得某个等级的名称 //////
core.prototype.getLvName = function () {
if (core.status.hero.lv>core.firstData.levelUp.length) return core.status.hero.lv;
return core.firstData.levelUp[core.status.hero.lv-1].name || core.status.hero.lv;
}
+////// 设置某个自定义变量或flag //////
core.prototype.setFlag = function(flag, value) {
if (!core.isset(core.status.hero)) return;
core.status.hero.flags[flag]=value;
}
+////// 获得某个自定义变量或flag //////
core.prototype.getFlag = function(flag, defaultValue) {
if (!core.isset(core.status.hero)) return defaultValue;
var value = core.status.hero.flags[flag];
@@ -3588,24 +3549,28 @@ core.prototype.getFlag = function(flag, defaultValue) {
return defaultValue;
}
-// 只有不为0或false时才会返回true
+////// 是否存在某个自定义变量或flag,且值为true //////
core.prototype.hasFlag = function(flag) {
if (core.getFlag(flag)) return true;
return false;
}
+////// 往当前事件列表之前插入一系列事件 //////
core.prototype.insertAction = function (list) {
core.events.insertAction(list);
}
+////// 锁定状态栏,常常用于事件处理 //////
core.prototype.lockControl = function () {
core.status.lockControl = true;
}
+////// 解锁状态栏 //////
core.prototype.unLockControl = function () {
core.status.lockControl = false;
}
+////// 判断某对象是否不为undefined也不会null //////
core.prototype.isset = function (val) {
if (val == undefined || val == null) {
return false;
@@ -3613,6 +3578,7 @@ core.prototype.isset = function (val) {
return true
}
+////// 播放背景音乐 //////
core.prototype.playBgm = function (bgm) {
// 如果不允许播放
@@ -3648,6 +3614,7 @@ core.prototype.playBgm = function (bgm) {
}
}
+////// 暂停背景音乐的播放 //////
core.prototype.pauseBgm = function () {
// 直接暂停播放
try {
@@ -3662,7 +3629,12 @@ core.prototype.pauseBgm = function () {
}
}
+////// 恢复背景音乐的播放 //////
core.prototype.resumeBgm = function () {
+
+ // 如果不允许播放
+ if (!core.musicStatus.bgmStatus) return;
+
// 恢复BGM
try {
if (core.isset(core.musicStatus.playingBgm)) {
@@ -3682,6 +3654,7 @@ core.prototype.resumeBgm = function () {
}
}
+////// 播放音频 //////
core.prototype.playSound = function (sound) {
// 如果不允许播放
@@ -3715,27 +3688,7 @@ core.prototype.playSound = function (sound) {
}
}
-core.prototype.changeSoundStatus = function () {
- if (core.musicStatus.soundStatus) {
- main.core.disabledSound();
- }
- else {
- main.core.enabledSound();
- }
-}
-
-core.prototype.enabledSound = function () {
- core.musicStatus.soundStatus = true;
- core.playBgm('bgm', 'mp3');
- core.setLocalStorage('soundStatus', true);
-}
-
-core.prototype.disabledSound = function () {
- core.musicStatus.playedBgm.pause();
- core.musicStatus.soundStatus = false;
- core.setLocalStorage('soundStatus', false);
-}
-
+////// 动画显示某对象 //////
core.prototype.show = function (obj, speed, callback) {
if (!core.isset(speed)) {
obj.style.display = 'block';
@@ -3756,6 +3709,7 @@ core.prototype.show = function (obj, speed, callback) {
}, speed);
}
+////// 动画使某对象消失 //////
core.prototype.hide = function (obj, speed, callback) {
if (!core.isset(speed)) {
obj.style.display = 'none';
@@ -3776,8 +3730,7 @@ core.prototype.hide = function (obj, speed, callback) {
}
-////// 状态栏相关 //////
-
+////// 清空状态栏 //////
core.prototype.clearStatusBar = function() {
var statusList = ['floor', 'lv', 'hp', 'atk', 'def', 'mdef', 'money', 'experience', 'up', 'yellowKey', 'blueKey', 'redKey', 'poison', 'weak', 'curse', 'hard'];
statusList.forEach(function (e) {
@@ -3787,13 +3740,11 @@ core.prototype.clearStatusBar = function() {
core.statusBar.image.fly.style.opacity = 0.3;
}
-/**
- * 更新状态栏
- */
+////// 更新状态栏 //////
core.prototype.updateStatusBar = function () {
// 检查等级
- core.checkLvUp();
+ core.events.checkLvUp();
// 检查HP上限
if (core.values.HPMAX>0) {
@@ -3843,32 +3794,7 @@ core.prototype.updateStatusBar = function () {
core.updateFg();
}
-core.prototype.checkLvUp = function () {
- if (!core.flags.enableLevelUp || core.status.hero.lv>=core.firstData.levelUp.length) return;
- // 计算下一个所需要的数值
- var need=core.firstData.levelUp[core.status.hero.lv].need;
- if (!core.isset(need)) return;
- if (core.status.hero.experience>=need) {
- // 升级
- core.status.hero.lv++;
- var effect = core.firstData.levelUp[core.status.hero.lv-1].effect;
- if (typeof effect == "string") {
- if (effect.indexOf("function")==0) {
- eval("("+effect+")()");
- }
- else {
- effect.split(";").forEach(function (t) {
- core.doEffect(t);
- });
- }
- }
- else if (effect instanceof Function) {
- effect();
- }
- core.checkLvUp();
- }
-}
-
+////// 屏幕分辨率改变后重新自适应 //////
core.prototype.resize = function(clientWidth, clientHeight) {
// 默认画布大小
@@ -4173,6 +4099,7 @@ core.prototype.resize = function(clientWidth, clientHeight) {
core.domRenderer();
}
+////// 渲染DOM //////
core.prototype.domRenderer = function(){
core.dom.statusBar.style.display = 'block';
diff --git a/libs/enemys.js b/libs/enemys.js
index f9e29705..1738102d 100644
--- a/libs/enemys.js
+++ b/libs/enemys.js
@@ -2,6 +2,7 @@ function enemys() {
}
+////// 初始化 //////
enemys.prototype.init = function () {
// 怪物属性初始化定义:
this.enemys = {
@@ -68,17 +69,20 @@ enemys.prototype.init = function () {
}
}
+////// 获得一个或所有怪物数据 //////
enemys.prototype.getEnemys = function (enemyId) {
- if (enemyId == undefined) {
+ if (!core.isset(enemyId)) {
return this.enemys;
}
return this.enemys[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)));
}
+////// 获得所有特殊属性的名称 //////
enemys.prototype.getSpecialText = function (enemyId) {
if (enemyId == undefined) return "";
var enemy = this.enemys[enemyId];
@@ -107,7 +111,7 @@ enemys.prototype.getSpecialText = function (enemyId) {
return text;
}
-////// 获得每个属性的文字提示 //////
+////// 获得每个特殊属性的说明 //////
enemys.prototype.getSpecialHint = function (enemy, special) {
if (!core.isset(special)) {
var hints = [];
@@ -147,6 +151,7 @@ enemys.prototype.getSpecialHint = function (enemy, special) {
return ""
}
+////// 获得某个怪物的伤害 //////
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;
@@ -156,6 +161,7 @@ enemys.prototype.getDamage = function (monsterId) {
return damage + this.getExtraDamage(monster);
}
+////// 获得某个怪物的额外伤害 //////
enemys.prototype.getExtraDamage = function (monster) {
var extra_damage = 0;
if (this.hasSpecial(monster.special, 11)) { // 吸血
@@ -169,7 +175,7 @@ enemys.prototype.getExtraDamage = function (monster) {
return extra_damage;
}
-// 临界值计算
+////// 临界值计算 //////
enemys.prototype.getCritical = function (monsterId) {
var monster = core.material.enemys[monsterId];
if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return "???";
@@ -187,7 +193,7 @@ enemys.prototype.getCritical = function (monsterId) {
return 0;
}
-// 临界减伤计算
+////// 临界减伤计算 //////
enemys.prototype.getCriticalDamage = function (monsterId) {
var c = this.getCritical(monsterId);
if (c == '???') return '???';
@@ -201,7 +207,7 @@ enemys.prototype.getCriticalDamage = function (monsterId) {
monster.hp, monster.atk, monster.def, monster.special, monster.n);
}
-// 1防减伤计算
+////// 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,
@@ -212,6 +218,7 @@ enemys.prototype.getDefDamage = function (monsterId) {
return nowDamage - nextDamage;
}
+////// 具体的伤害计算公式 //////
enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special, n) {
if (this.hasSpecial(mon_special, 20) && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架
@@ -254,7 +261,7 @@ enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mo
return core.flags.enableNegativeDamage?ans:Math.max(0, ans);
}
-// 获得当前楼层的怪物列表
+////// 获得当前楼层的怪物列表 //////
enemys.prototype.getCurrentEnemys = function () {
var enemys = [];
var used = {};
diff --git a/libs/events.js b/libs/events.js
index cd866e90..cf66d51e 100644
--- a/libs/events.js
+++ b/libs/events.js
@@ -2,6 +2,7 @@ function events() {
}
+////// 初始化 //////
events.prototype.init = function () {
this.events = {
'battle': function (data, core, callback) {
@@ -45,9 +46,9 @@ events.prototype.init = function () {
}
}
-// 初始化
+////// 获得一个或所有系统事件类型 //////
events.prototype.getEvents = function (eventName) {
- if (eventName == undefined) {
+ if (!core.isset(eventName)) {
return this.events;
}
return this.events[eventName];
@@ -87,12 +88,14 @@ events.prototype.startGame = function (hard) {
})
}
-////// 简单难度设置初始福利 //////
+////// 不同难度分别设置初始属性 //////
events.prototype.setInitData = function (hard) {
if (hard=='Easy') { // 简单难度
core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度
// 可以在此设置一些初始福利,比如设置初始生命值可以调用:
// core.setStatus("hp", 10000);
+ // 赠送一把黄钥匙可以调用
+ // core.setItem("yellowKey", 1);
}
if (hard=='Normal') { // 普通难度
core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度
@@ -102,9 +105,8 @@ events.prototype.setInitData = function (hard) {
}
}
-////// 游戏结束事件 //////
+////// 游戏获胜事件 //////
events.prototype.win = function(reason) {
- // 获胜
core.waitHeroToStop(function() {
core.removeGlobalAnimate(0,0,true);
core.clearMap('all'); // 清空全地图
@@ -116,8 +118,8 @@ events.prototype.win = function(reason) {
});
}
+////// 游戏失败事件 //////
events.prototype.lose = function(reason) {
- // 失败
core.waitHeroToStop(function() {
core.drawText([
"\t[结局1]你死了。\n如题。"
@@ -133,20 +135,9 @@ events.prototype.afterChangeFloor = function (floorId) {
this.doEvents(core.floors[floorId].firstArrive);
core.setFlag("visited_"+floorId, true);
}
-
- // 播放BGM
- if (floorId == 'sample0') {
- core.playBgm('bgm.mp3');
- }
- if (floorId == 'sample1') {
- core.playBgm('star.mid');
- }
- if (floorId == 'sample2') {
- core.playBgm('qianjin.mid');
- }
}
-////// 实际事件的处理 //////
+////// 开始执行一系列自定义事件 //////
events.prototype.doEvents = function (list, x, y, callback) {
// 停止勇士
core.waitHeroToStop(function() {
@@ -162,6 +153,7 @@ events.prototype.doEvents = function (list, x, y, callback) {
});
}
+////// 执行当前自定义事件列表中的下一个事件 //////
events.prototype.doAction = function() {
// 清空boxAnimate和UI层
clearInterval(core.interval.boxAnimate);
@@ -172,7 +164,7 @@ events.prototype.doAction = function() {
if (core.status.event.data.list.length==0) {
if (core.isset(core.status.event.data.callback))
core.status.event.data.callback();
- core.ui.closePanel(false);
+ core.ui.closePanel();
return;
}
@@ -201,12 +193,12 @@ events.prototype.doAction = function() {
case "show": // 显示
if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) {
core.animateBlock(data.loc[0],data.loc[1],'show', data.time, function () {
- core.addBlock(data.loc[0],data.loc[1],data.floorId);
+ core.showBlock(data.loc[0],data.loc[1],data.floorId);
core.events.doAction();
});
}
else {
- core.addBlock(data.loc[0],data.loc[1],data.floorId)
+ core.showBlock(data.loc[0],data.loc[1],data.floorId)
this.doAction();
}
break;
@@ -418,7 +410,7 @@ events.prototype.insertAction = function (action) {
}
}
-////// 打开商店 //////
+////// 打开一个全局商店 //////
events.prototype.openShop = function(shopId, needVisited) {
var shop = core.status.shops[shopId];
shop.times = shop.times || 0;
@@ -460,35 +452,11 @@ events.prototype.openShop = function(shopId, needVisited) {
core.ui.drawChoices(content, choices);
}
+////// 禁用一个全局商店 //////
events.prototype.disableQuickShop = function (shopId) {
core.status.shops[shopId].visited = false;
}
-////// 降低难度 //////
-
-events.prototype.decreaseHard = function() {
- core.drawTip("本塔不支持降低难度!");
- /*
- if (core.status.hard == 0) {
- core.drawTip("当前已是难度0,不能再降低难度了");
- return;
- }
- var add = 100, x=core.status.hard;
- while (x<10) {
- x++; add*=2;
- }
- core.ui.drawConfirmBox("本次操作可生命+" + add + ",确定吗?", function () {
- core.status.hero.hp += add;
- core.status.hard--;
- core.updateStatusBar();
- core.ui.closePanel();
- core.drawTip("降低难度成功,生命+" + add);
- }, function () {
- core.ui.drawSettings(false);
- });
- */
-}
-
////// 能否使用快捷商店 //////
events.prototype.canUseQuickShop = function(shopIndex) {
if (core.isset(core.floors[core.status.floorId].canUseQuickShop) && !core.isset(core.floors[core.status.floorId].canUseQuickShop))
@@ -497,9 +465,36 @@ events.prototype.canUseQuickShop = function(shopIndex) {
return null;
}
+////// 检查升级事件 //////
+events.prototype.checkLvUp = function () {
+ if (!core.flags.enableLevelUp || core.status.hero.lv>=core.firstData.levelUp.length) return;
+ // 计算下一个所需要的数值
+ var need=core.firstData.levelUp[core.status.hero.lv].need;
+ if (!core.isset(need)) return;
+ if (core.status.hero.experience>=need) {
+ // 升级
+ core.status.hero.lv++;
+ var effect = core.firstData.levelUp[core.status.hero.lv-1].effect;
+ if (typeof effect == "string") {
+ if (effect.indexOf("function")==0) {
+ eval("("+effect+")()");
+ }
+ else {
+ effect.split(";").forEach(function (t) {
+ core.doEffect(t);
+ });
+ }
+ }
+ else if (effect instanceof Function) {
+ effect();
+ }
+ this.checkLvUp();
+ }
+}
+
////// 尝试使用道具 //////
events.prototype.useItem = function(itemId) {
- core.ui.closePanel(false);
+ core.ui.closePanel();
if (itemId=='book') {
core.openBook(false);
@@ -522,7 +517,7 @@ events.prototype.useItem = function(itemId) {
else core.drawTip("当前无法使用"+core.material.items[itemId].name);
}
-////// 加点 //////
+////// 加点事件 //////
events.prototype.addPoint = function (enemy) {
var point = enemy.point;
if (!core.isset(point) || point<=0) return [];
@@ -531,22 +526,21 @@ events.prototype.addPoint = function (enemy) {
return [
{"type": "choices",
"choices": [
- {"text": "生命+"+(200*point), "action": [
- {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)}
- ]},
{"text": "攻击+"+(1*point), "action": [
{"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)}
]},
{"text": "防御+"+(2*point), "action": [
{"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)}
]},
+ {"text": "生命+"+(200*point), "action": [
+ {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)}
+ ]},
]
}
];
-
}
-/****** 打完怪物 ******/
+////// 战斗结束后触发的事件 //////
events.prototype.afterBattle = function(enemyId,x,y,callback) {
// 毒衰咒的处理
@@ -607,7 +601,7 @@ events.prototype.afterBattle = function(enemyId,x,y,callback) {
}
-/****** 开完门 ******/
+////// 开一个门后触发的事件 //////
events.prototype.afterOpenDoor = function(doorId,x,y,callback) {
var todo = [];
@@ -628,7 +622,7 @@ events.prototype.afterOpenDoor = function(doorId,x,y,callback) {
if (core.isset(callback)) callback();
}
-/****** 经过路障 ******/
+////// 经过一个路障 //////
events.prototype.passNet = function (data) {
// 有鞋子
if (core.hasItem('shoes')) return;
@@ -659,6 +653,7 @@ events.prototype.passNet = function (data) {
core.updateStatusBar();
}
+////// 改变亮灯(感叹号)的事件 //////
events.prototype.changeLight = function(x, y) {
var block = core.getBlock(x, y);
if (block==null) return;
@@ -675,32 +670,33 @@ events.prototype.changeLight = function(x, y) {
this.afterChangeLight(x,y);
}
-// 改变灯后的事件
+////// 改变亮灯之后,可以触发的事件 //////
events.prototype.afterChangeLight = function(x,y) {
}
-// 使用炸弹/圣锤后的事件
+////// 使用炸弹/圣锤后的事件 //////
events.prototype.afterUseBomb = function () {
}
-// 存档事件前一刻的处理
+////// 即将存档前可以执行的操作 //////
events.prototype.beforeSaveData = function(data) {
}
-// 读档事件后,载入事件前,对数据的处理
+////// 读档事件后,载入事件前,可以执行的操作 //////
events.prototype.afterLoadData = function(data) {
}
-/******************************************/
-/*********** 界面上的点击事件 ***************/
-/******************************************/
+/****************************************/
+/********** 点击事件、键盘事件 ************/
+/****************************************/
+////// 按下Ctrl键时(快捷跳过对话) //////
events.prototype.keyDownCtrl = function () {
if (core.status.event.id=='text') {
core.drawText();
@@ -712,6 +708,7 @@ events.prototype.keyDownCtrl = function () {
}
}
+////// 点击确认框时 //////
events.prototype.clickConfirmBox = function (x,y) {
if ((x == 4 || x == 5) && y == 7 && core.isset(core.status.event.data.yes))
core.status.event.data.yes();
@@ -719,6 +716,7 @@ events.prototype.clickConfirmBox = function (x,y) {
core.status.event.data.no();
}
+////// 键盘操作确认框时 //////
events.prototype.keyUpConfirmBox = function (keycode) {
if (keycode==37) {
core.status.event.selection=0;
@@ -742,7 +740,7 @@ events.prototype.keyUpConfirmBox = function (keycode) {
}
}
-// 正在处理事件时的点击操作...
+////// 自定义事件时的点击操作 //////
events.prototype.clickAction = function (x,y) {
if (core.status.event.data.type=='text') {
@@ -765,6 +763,7 @@ events.prototype.clickAction = function (x,y) {
}
}
+////// 自定义事件时,按下某个键的操作 //////
events.prototype.keyDownAction = function (keycode) {
if (core.status.event.data.type=='choices') {
var data = core.status.event.data.current;
@@ -784,6 +783,7 @@ events.prototype.keyDownAction = function (keycode) {
}
}
+////// 自定义事件时,放开某个键的操作 //////
events.prototype.keyUpAction = function (keycode) {
if (core.status.event.data.type=='text' && (keycode==13 || keycode==32 || keycode==67)) {
this.doAction();
@@ -801,48 +801,49 @@ events.prototype.keyUpAction = function (keycode) {
}
}
-// 怪物手册
+////// 怪物手册界面的点击操作 //////
events.prototype.clickBook = function(x,y) {
// 上一页
if ((x == 3 || x == 4) && y == 12) {
- core.ui.drawEnemyBook(core.status.event.data - 6);
+ core.ui.drawBook(core.status.event.data - 6);
return;
}
// 下一页
if ((x == 8 || x == 9) && y == 12) {
- core.ui.drawEnemyBook(core.status.event.data + 6);
+ core.ui.drawBook(core.status.event.data + 6);
return;
}
// 返回
if (x>=10 && x<=12 && y==12) {
- core.ui.closePanel(true);
+ core.ui.closePanel();
return;
}
// 怪物信息
- // var index = parseInt(y/2);
var data = core.status.event.data;
if (core.isset(data) && y<12) {
var page=parseInt(data/6);
var index=6*page+parseInt(y/2);
- core.ui.drawEnemyBook(index);
+ core.ui.drawBook(index);
core.ui.drawBookDetail(index);
}
return;
}
+////// 怪物手册界面时,按下某个键的操作 //////
events.prototype.keyDownBook = function (keycode) {
- if (keycode==37) core.ui.drawEnemyBook(core.status.event.data-6);
- if (keycode==38) core.ui.drawEnemyBook(core.status.event.data-1);
- if (keycode==39) core.ui.drawEnemyBook(core.status.event.data+6);
- if (keycode==40) core.ui.drawEnemyBook(core.status.event.data+1);
- if (keycode==33) core.ui.drawEnemyBook(core.status.event.data-6);
- if (keycode==34) core.ui.drawEnemyBook(core.status.event.data+6);
+ if (keycode==37) core.ui.drawBook(core.status.event.data-6);
+ if (keycode==38) core.ui.drawBook(core.status.event.data-1);
+ if (keycode==39) core.ui.drawBook(core.status.event.data+6);
+ if (keycode==40) core.ui.drawBook(core.status.event.data+1);
+ if (keycode==33) core.ui.drawBook(core.status.event.data-6);
+ if (keycode==34) core.ui.drawBook(core.status.event.data+6);
return;
}
+////// 怪物手册界面时,放开某个键的操作 //////
events.prototype.keyUpBook = function (keycode) {
if (keycode==27 || keycode==88) {
- core.ui.closePanel(true);
+ core.ui.closePanel();
return;
}
if (keycode==13 || keycode==32 || keycode==67) {
@@ -854,13 +855,13 @@ events.prototype.keyUpBook = function (keycode) {
}
}
-events.prototype.clickBookDetail = function (x,y) {
+////// 怪物手册属性显示界面时的点击操作 //////
+events.prototype.clickBookDetail = function () {
core.clearMap('data', 0, 0, 416, 416);
-
core.status.event.id = 'book';
-
}
+////// 楼层传送器界面时的点击操作 //////
events.prototype.clickFly = function(x,y) {
if ((x==10 || x==11) && y==9) core.ui.drawFly(core.status.event.data-1);
if ((x==10 || x==11) && y==5) core.ui.drawFly(core.status.event.data+1);
@@ -875,12 +876,14 @@ events.prototype.clickFly = function(x,y) {
return;
}
+////// 楼层传送器界面时,按下某个键的操作 //////
events.prototype.keyDownFly = function (keycode) {
if (keycode==37 || keycode==38) core.ui.drawFly(core.status.event.data+1);
else if (keycode==39 || keycode==40) core.ui.drawFly(core.status.event.data-1);
return;
}
+////// 楼层传送器界面时,放开某个键的操作 //////
events.prototype.keyUpFly = function (keycode) {
if (keycode==71 || keycode==27 || keycode==88)
core.ui.closePanel();
@@ -889,7 +892,7 @@ events.prototype.keyUpFly = function (keycode) {
return;
}
-// 商店
+////// 商店界面时的点击操作 //////
events.prototype.clickShop = function(x,y) {
var shop = core.status.event.data.shop;
var choices = shop.choices;
@@ -936,6 +939,7 @@ events.prototype.clickShop = function(x,y) {
}
}
+////// 商店界面时,按下某个键的操作 //////
events.prototype.keyDownShop = function (keycode) {
var shop = core.status.event.data.shop;
var choices = shop.choices;
@@ -951,6 +955,7 @@ events.prototype.keyDownShop = function (keycode) {
}
}
+////// 商店界面时,放开某个键的操作 //////
events.prototype.keyUpShop = function (keycode) {
if (keycode==27 || keycode==88) {
if (core.status.event.data.fromList) {
@@ -971,7 +976,7 @@ events.prototype.keyUpShop = function (keycode) {
return;
}
-// 快捷商店
+////// 快捷商店界面时的点击操作 //////
events.prototype.clickQuickShop = function(x, y) {
var shopList = core.status.shops, keys = Object.keys(shopList);
if (x >= 5 && x <= 7) {
@@ -992,6 +997,7 @@ events.prototype.clickQuickShop = function(x, y) {
}
}
+////// 快捷商店界面时,按下某个键的操作 //////
events.prototype.keyDownQuickShop = function (keycode) {
var shopList = core.status.shops, keys = Object.keys(shopList);
if (keycode==38) {
@@ -1006,6 +1012,7 @@ events.prototype.keyDownQuickShop = function (keycode) {
}
}
+////// 快捷商店界面时,放开某个键的操作 //////
events.prototype.keyUpQuickShop = function (keycode) {
if (keycode==27 || keycode==75 || keycode==88) {
core.ui.closePanel();
@@ -1019,11 +1026,11 @@ events.prototype.keyUpQuickShop = function (keycode) {
return;
}
-// 工具栏
+////// 工具栏界面时的点击操作 //////
events.prototype.clickToolbox = function(x,y) {
// 返回
if (x>=10 && x<=12 && y==12) {
- core.ui.closePanel(false);
+ core.ui.closePanel();
return;
}
var index=0;
@@ -1033,6 +1040,7 @@ events.prototype.clickToolbox = function(x,y) {
this.clickToolboxIndex(index);
}
+////// 选择工具栏界面中某个Index后的操作 //////
events.prototype.clickToolboxIndex = function(index) {
var items = null;
var ii=index;
@@ -1053,6 +1061,7 @@ events.prototype.clickToolboxIndex = function(index) {
}
}
+////// 工具栏界面时,按下某个键的操作 //////
events.prototype.keyDownToolbox = function (keycode) {
if (!core.isset(core.status.event.data)) return;
@@ -1116,6 +1125,7 @@ events.prototype.keyDownToolbox = function (keycode) {
}
}
+////// 工具栏界面时,放开某个键的操作 //////
events.prototype.keyUpToolbox = function (keycode) {
if (keycode==84 || keycode==27 || keycode==88) {
core.ui.closePanel();
@@ -1129,7 +1139,7 @@ events.prototype.keyUpToolbox = function (keycode) {
}
}
-// 存读档
+////// 存读档界面时的点击操作 //////
events.prototype.clickSL = function(x,y) {
// 上一页
if ((x == 3 || x == 4) && y == 12) {
@@ -1141,7 +1151,7 @@ events.prototype.clickSL = function(x,y) {
}
// 返回
if (x>=10 && x<=12 && y==12) {
- core.ui.closePanel(false);
+ core.ui.closePanel();
if (!core.isPlaying()) {
core.showStartAnimate();
}
@@ -1162,6 +1172,7 @@ events.prototype.clickSL = function(x,y) {
}
}
+////// 存读档界面时,按下某个键的操作 //////
events.prototype.keyDownSL = function(keycode) {
if (keycode==37) { // left
core.ui.drawSLPanel(core.status.event.data - 1);
@@ -1189,6 +1200,7 @@ events.prototype.keyDownSL = function(keycode) {
}
}
+////// 存读档界面时,放开某个键的操作 //////
events.prototype.keyUpSL = function (keycode) {
if (keycode==27 || keycode==88 || (core.status.event.id == 'save' && keycode==83) || (core.status.event.id == 'load' && keycode==68)) {
core.ui.closePanel();
@@ -1203,6 +1215,7 @@ events.prototype.keyUpSL = function (keycode) {
}
}
+////// 系统设置界面时的点击操作 //////
events.prototype.clickSwitchs = function (x,y) {
if (x<5 || x>7) return;
var choices = [
@@ -1256,6 +1269,7 @@ events.prototype.clickSwitchs = function (x,y) {
}
}
+////// 系统设置界面时,按下某个键的操作 //////
events.prototype.keyDownSwitchs = function (keycode) {
var choices = [
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单"
@@ -1272,6 +1286,7 @@ events.prototype.keyDownSwitchs = function (keycode) {
}
}
+////// 系统设置界面时,放开某个键的操作 //////
events.prototype.keyUpSwitchs = function (keycode) {
if (keycode==27 || keycode==88) {
core.status.event.selection=0;
@@ -1288,7 +1303,7 @@ events.prototype.keyUpSwitchs = function (keycode) {
}
-// 菜单栏
+////// 系统菜单栏界面时的点击事件 //////
events.prototype.clickSettings = function (x,y) {
if (x<5 || x>7) return;
var choices = [
@@ -1335,6 +1350,7 @@ events.prototype.clickSettings = function (x,y) {
return;
}
+////// 系统菜单栏界面时,按下某个键的操作 //////
events.prototype.keyDownSettings = function (keycode) {
var choices = [
"系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏"
@@ -1351,6 +1367,7 @@ events.prototype.keyDownSettings = function (keycode) {
}
}
+////// 系统菜单栏界面时,放开某个键的操作 //////
events.prototype.keyUpSettings = function (keycode) {
if (keycode==27 || keycode==88) {
core.ui.closePanel();
@@ -1365,6 +1382,7 @@ events.prototype.keyUpSettings = function (keycode) {
}
}
+////// 同步存档界面时的点击操作 //////
events.prototype.clickSyncSave = function (x,y) {
if (x<5 || x>7) return;
var choices = [
@@ -1400,6 +1418,7 @@ events.prototype.clickSyncSave = function (x,y) {
return;
}
+////// 同步存档界面时,按下某个键的操作 //////
events.prototype.keyDownSyncSave = function (keycode) {
var choices = [
"同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单"
@@ -1416,6 +1435,7 @@ events.prototype.keyDownSyncSave = function (keycode) {
}
}
+////// 同步存档界面时,放开某个键的操作 //////
events.prototype.keyUpSyncSave = function (keycode) {
if (keycode==27 || keycode==88) {
core.status.event.selection=2;
@@ -1431,11 +1451,11 @@ events.prototype.keyUpSyncSave = function (keycode) {
}
}
+////// “关于”界面时的点击操作 //////
events.prototype.clickAbout = function () {
if (core.isPlaying())
- core.ui.closePanel(false);
+ core.ui.closePanel();
else
core.showStartAnimate();
}
-/*********** 点击事件 END ***************/
diff --git a/libs/floors/MT0.js b/libs/floors/MT0.js
index c8d90ad4..624b8e91 100644
--- a/libs/floors/MT0.js
+++ b/libs/floors/MT0.js
@@ -8,6 +8,9 @@ main.floors.MT0 = {
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "ground", // 默认地面的图块ID(terrains中)
+ // "png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
+ // "color": [0,0,0,0.3], // 该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组。
+ // "bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据,需要是13x13,建议使用地图生成器来生成
],
diff --git a/libs/floors/sample0.js b/libs/floors/sample0.js
index 1fab84e8..45f5e3fb 100644
--- a/libs/floors/sample0.js
+++ b/libs/floors/sample0.js
@@ -8,6 +8,9 @@ main.floors.sample0 = {
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "ground", // 默认地面的图块ID(terrains中)
+ // "png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
+ // "color": [0,0,0,0.3] // 该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组。
+ "bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据,需要是13x13,建议使用地图生成器来生成
[0, 0, 220, 0, 0, 20, 87, 3, 65, 64, 44, 43, 42],
[0, 246, 0, 246, 0, 20, 0, 3, 58, 59, 60, 61, 41],
diff --git a/libs/floors/sample1.js b/libs/floors/sample1.js
index 3934514b..ee2d9b4e 100644
--- a/libs/floors/sample1.js
+++ b/libs/floors/sample1.js
@@ -8,6 +8,9 @@ main.floors.sample1 = {
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "grass", // 默认地面的图块ID(terrains中)
+ "png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
+ // "color": [0,0,0,0.3] // 该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组。
+ // "bgm": "bgm.mp3", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据,需要是13x13,建议使用地图生成器来生成
[7, 131, 8, 152, 9, 130, 10, 152, 166, 165, 132, 165, 166],
[0, 0, 0, 0, 0, 0, 0, 152, 165, 164, 0, 162, 165],
diff --git a/libs/floors/sample2.js b/libs/floors/sample2.js
index 0dcff5ab..5e440aa4 100644
--- a/libs/floors/sample2.js
+++ b/libs/floors/sample2.js
@@ -8,7 +8,9 @@ main.floors.sample2 = {
"canFlyTo": false, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"canUseQuickShop": true, // 该层是否允许使用快捷商店
"defaultGround": "snowGround", // 默认地面的图块ID(terrains中)
- "color": [255,0,0,0.3], // 可以设置该层的默认背景色调(RGBA);本项可省略
+ // "png": "bg.png", // 背景图;你可以选择一张png图片来作为背景素材。详细用法请参见文档“自定义素材”中的说明。
+ "color": [255,0,0,0.3], // 该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组。
+ "bgm": "qianjin.mid", // 到达该层后默认播放的BGM。本项可忽略。
"map": [ // 地图数据,需要是13x13,建议使用地图生成器来生成
[5, 5, 5, 5, 5, 5, 87, 5, 5, 5, 5, 5, 5],
[5, 4, 4, 4, 4, 1, 0, 1, 4, 4, 4, 4, 5],
@@ -200,10 +202,26 @@ main.floors.sample2 = {
{"type": "trigger", "loc": [6,6]} // 立刻触发妖精事件
]
},
- "4,3": {"trigger":"action","enable":false}, // 四个角的大法师,添加trigger:action可避免该点出现显伤
- "8,3": {"trigger":"action","enable":false}, // 四个角的大法师
- "4,6": {"trigger":"action","enable":false}, // 四个角的大法师
- "8,6": {"trigger":"action","enable":false}, // 四个角的大法师
+ "4,3": { // 四个角的大法师,
+ "trigger": "action",
+ "displayDamage": false,
+ "enable":false
+ },
+ "8,3": { // 四个角的大法师,
+ "trigger": "action",
+ "displayDamage": false,
+ "enable":false
+ },
+ "4,6": { // 四个角的大法师,
+ "trigger": "action",
+ "displayDamage": false,
+ "enable":false
+ },
+ "8,6": { // 四个角的大法师,
+ "trigger": "action",
+ "displayDamage": false,
+ "enable":false
+ },
"6,6": { // 妖精
"enable":false, // 初始时禁用状态
diff --git a/libs/items.js b/libs/items.js
index ae21b209..e603d3a4 100644
--- a/libs/items.js
+++ b/libs/items.js
@@ -2,6 +2,7 @@ function items() {
}
+////// 初始化 //////
items.prototype.init = function () {
this.items = {
// 钥匙
@@ -59,7 +60,7 @@ items.prototype.init = function () {
}
}
-// 初始化道具
+////// 获得所有道具 //////
items.prototype.getItems = function () {
// 大黄门钥匙?钥匙盒?
if (core.flags.bigKeyIsBox)
@@ -74,7 +75,7 @@ items.prototype.getItems = function () {
main.instance.items = new items();
-
+////// “即捡即用类”道具的使用效果 //////
items.prototype.getItemEffect = function(itemId, itemNum) {
var itemCls = core.material.items[itemId].cls;
// 消耗品
@@ -115,6 +116,7 @@ items.prototype.getItemEffect = function(itemId, itemNum) {
}
}
+////// “即捡即用类”道具的文字提示 //////
items.prototype.getItemEffectTip = function(itemId) {
if (itemId === 'redJewel') return ",攻击+"+core.values.redJewel;
if (itemId === 'blueJewel') return ",防御+"+core.values.blueJewel;
@@ -140,14 +142,12 @@ items.prototype.getItemEffectTip = function(itemId) {
return "";
}
-
-
+////// 使用道具 //////
items.prototype.useItem = function (itemId) {
- // 使用道具
if (!this.canUseItem(itemId)) return;
var itemCls = core.material.items[itemId].cls;
- if (itemId=='book') core.ui.drawEnemyBook(0);
+ if (itemId=='book') core.ui.drawBook(0);
if (itemId=='fly') core.ui.drawFly(core.status.hero.flyRange.indexOf(core.status.floorId));
if (itemId == 'earthquake' || itemId == 'bomb' || itemId == 'pickaxe' || itemId=='icePickaxe'
|| itemId == 'snow' || itemId == 'hammer' || itemId=='bigKey') {
@@ -198,12 +198,11 @@ items.prototype.useItem = function (itemId) {
delete core.status.hero.items[itemCls][itemId];
}
+////// 当前能否使用道具 //////
items.prototype.canUseItem = function (itemId) {
// 没有道具
if (!core.hasItem(itemId)) return false;
- var itemCls = core.material.items[itemId].cls;
-
if (itemId == 'book') return true;
if (itemId == 'fly') return core.status.hero.flyRange.indexOf(core.status.floorId)>=0;
if (itemId == 'pickaxe') {
diff --git a/libs/maps.js b/libs/maps.js
index bc1360be..3eb6a7b0 100644
--- a/libs/maps.js
+++ b/libs/maps.js
@@ -1,6 +1,7 @@
function maps() {}
maps.prototype.init = function() {}
+////// 加载某个楼层(从剧本或存档中) //////
maps.prototype.loadFloor = function (floorId, map) {
var floor = core.floors[floorId];
var content = {};
@@ -13,7 +14,7 @@ maps.prototype.loadFloor = function (floorId, map) {
for (var i = 0; i < 13; i++) {
for (var j = 0; j < 13; j++) {
var block = this.getBlock(j, i, map[i][j]);
- if (block.event != undefined) {
+ if (core.isset(block.event)) {
if (block.event.cls == 'enemys' && block.event.trigger==undefined) {
block.event.trigger = 'battle';
}
@@ -34,7 +35,7 @@ maps.prototype.loadFloor = function (floorId, map) {
}
}
}
- this.addEvent(block,j,i,floor.events[j+","+i],floor.defaultGround || "ground")
+ this.addEvent(block,j,i,floor.events[j+","+i])
this.addChangeFloor(block,j,i,floor.changeFloor[j+","+i]);
if (core.isset(block.event)) blocks.push(block);
}
@@ -44,6 +45,7 @@ maps.prototype.loadFloor = function (floorId, map) {
return content;
}
+////// 数字和ID的对应关系 //////
maps.prototype.getBlock = function (x, y, id) {
var enable=null;
id = ""+id;
@@ -80,11 +82,11 @@ maps.prototype.getBlock = function (x, y, id) {
if (id == 14) tmp.event = {'cls': 'animates', 'id': 'curseNet', 'noPass': false, 'trigger': 'passNet'}; // 咒网
if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
// 在这里添加更多地形
- // 如果空地不足,可以从180以后开始继续放,只要不和现有的数字冲突即可
+ // 如果空位不足,可以从180以后开始继续放,只要不和现有的数字冲突即可
// Autotile
if (id == 20) tmp.event = {'cls': 'autotile', 'id': 'autotile', 'noPass': true}; // autotile
- // 更多的autotile从151到160,只要不和现有的数字冲突即可
+ // 更多的autotile从151到160等,只要不和现有的数字冲突即可
if (id == 151) tmp.event = {'cls': 'autotile', 'id': 'autotile1', 'noPass': true};
if (id == 152) tmp.event = {'cls': 'autotile', 'id': 'autotile2', 'noPass': true};
if (id == 153) tmp.event = {'cls': 'autotile', 'id': 'autotile3', 'noPass': true};
@@ -255,10 +257,11 @@ maps.prototype.getBlock = function (x, y, id) {
return tmp;
}
-maps.prototype.addEvent = function (block, x, y, event, ground) {
+////// 向该楼层添加剧本的自定义事件 //////
+maps.prototype.addEvent = function (block, x, y, event) {
if (!core.isset(event)) return;
if (!core.isset(block.event)) { // 本身是空地?
- block.event = {'cls': 'terrains', 'id': ground, 'noPass': false};
+ block.event = {'cls': 'terrains', 'id': 'none', 'noPass': false};
}
// event是字符串或数组?
if (typeof event == "string") {
@@ -270,6 +273,10 @@ maps.prototype.addEvent = function (block, x, y, event, ground) {
if (!core.isset(event.data))
event.data = [];
+ // 覆盖noPass
+ if (core.isset(event.noPass))
+ block.event.noPass = event.noPass;
+
// 覆盖enable
if (!core.isset(block.enable) && core.isset(event.enable)) {
block.enable=event.enable;
@@ -290,11 +297,13 @@ maps.prototype.addEvent = function (block, x, y, event, ground) {
}
}
+////// 向该楼层添加剧本的楼层转换事件 //////
maps.prototype.addChangeFloor = function (block, x, y, event, ground) {
if (!core.isset(event)) return;
this.addEvent(block, x, y, {"trigger": "changeFloor", "data": event}, ground);
}
+////// 初始化所有地图 //////
maps.prototype.initMaps = function (floorIds) {
var maps = {};
for (var i=0;i', 270, top+height-13, '#CCCCCC', '13px Verdana');
}
-// 绘制选项事件
+////// 绘制一个选项界面 //////
ui.prototype.drawChoices = function(content, choices) {
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
@@ -273,12 +263,7 @@ ui.prototype.drawChoices = function(content, choices) {
return;
}
-/**
- * 绘制确认/取消警告
- * @param text
- * @param yesCallback
- * @param noCallback
- */
+////// 绘制一个确认/取消的警告页面 //////
ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) {
core.lockControl();
@@ -327,10 +312,8 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) {
}
-////// 绘制开关界面 //////
+////// 绘制系统设置界面 //////
ui.prototype.drawSwitchs = function() {
- // 背景音乐、背景音效、战斗动画、怪物显伤、领域显伤、返回
-
core.status.event.id = 'switchs';
var choices = [
@@ -345,10 +328,7 @@ ui.prototype.drawSwitchs = function() {
}
-/**
- * 绘制菜单栏
- * @param need
- */
+////// 绘制系统菜单栏 //////
ui.prototype.drawSettings = function (need) {
if (!core.checkStatus('settings', need))
return;
@@ -358,6 +338,7 @@ ui.prototype.drawSettings = function (need) {
]);
}
+////// 绘制快捷商店选择栏 //////
ui.prototype.drawQuickShop = function (need) {
if (core.isset(need) && !core.checkStatus('selectShop', need))
return;
@@ -374,7 +355,7 @@ ui.prototype.drawQuickShop = function (need) {
this.drawChoices(null, choices);
}
-
+////// 绘制战斗动画 //////
ui.prototype.drawBattleAnimate = function(monsterId, callback) {
// UI层
@@ -662,10 +643,7 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) {
}, 500);
}
-/**
- * 绘制“请等候...”
- * @param text
- */
+////// 绘制等待界面 //////
ui.prototype.drawWaiting = function(text) {
core.lockControl();
@@ -685,9 +663,7 @@ ui.prototype.drawWaiting = function(text) {
}
-/**
- * 绘制“存档同步”选项
- */
+////// 绘制存档同步界面 //////
ui.prototype.drawSyncSave = function () {
core.status.event.id = 'syncSave';
@@ -698,11 +674,7 @@ ui.prototype.drawSyncSave = function () {
}
-/**
- * 绘制“分页”
- * @param page
- * @param totalPage
- */
+////// 绘制分页 //////
ui.prototype.drawPagination = function (page, totalPage) {
core.setFont('ui', 'bold 15px Verdana');
@@ -724,11 +696,8 @@ ui.prototype.drawPagination = function (page, totalPage) {
}
-/**
- * 绘制怪物手册
- * @param index 怪物索引
- */
-ui.prototype.drawEnemyBook = function (index) {
+////// 绘制怪物手册 //////
+ui.prototype.drawBook = function (index) {
var enemys = core.enemys.getCurrentEnemys();
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
@@ -844,6 +813,7 @@ ui.prototype.drawEnemyBook = function (index) {
this.drawPagination(page, totalPage);
}
+////// 绘制怪物属性的详细信息 //////
ui.prototype.drawBookDetail = function (index) {
var enemys = core.enemys.getCurrentEnemys();
if (enemys.length==0) return;
@@ -906,10 +876,7 @@ ui.prototype.drawBookDetail = function (index) {
core.fillText('data', '<点击任意位置继续>', 270, top+height-13, '#CCCCCC', '13px Verdana');
}
-/**
- * 绘制楼传器
- * @param page
- */
+////// 绘制楼层传送器 //////
ui.prototype.drawFly = function(page) {
if (page<0) page=0;
@@ -935,6 +902,7 @@ ui.prototype.drawFly = function(page) {
this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 20, 100, 273);
}
+////// 绘制道具栏 //////
ui.prototype.drawToolbox = function(index) {
var tools = Object.keys(core.status.hero.items.tools).sort();
@@ -1049,7 +1017,7 @@ ui.prototype.drawToolbox = function(index) {
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;
@@ -1103,6 +1071,7 @@ ui.prototype.drawSLPanel = function(index) {
this.drawPagination(page+1, 30);
}
+////// 绘制一个缩略图 //////
ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroLoc) {
core.clearMap(canvas, x, y, size, size);
var groundId = core.floors[floorId].defaultGround || "ground";
@@ -1123,9 +1092,11 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL
continue;
}
else {
- var blockIcon = core.material.icons[block.event.cls][block.event.id];
- var blockImage = core.material.images[block.event.cls];
- core.canvas[canvas].drawImage(blockImage, 0, blockIcon * 32, 32, 32, x + block.x * persize, y + block.y * persize, persize, persize);
+ if (block.event.id!='none') {
+ var blockIcon = core.material.icons[block.event.cls][block.event.id];
+ var blockImage = core.material.images[block.event.cls];
+ core.canvas[canvas].drawImage(blockImage, 0, blockIcon * 32, 32, 32, x + block.x * persize, y + block.y * persize, persize, persize);
+ }
}
}
}
@@ -1138,9 +1109,7 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL
}
}
-/**
- * 绘制"关于"
- */
+////// 绘制“关于”界面 //////
ui.prototype.drawAbout = function() {
if (!core.isPlaying()) {
@@ -1166,18 +1135,9 @@ ui.prototype.drawAbout = function() {
core.fillText('ui', "作者: 艾之葵", text_start, top + 80, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top+112);
// TODO: 写自己的“关于”页面
- /*
- core.fillText('ui', "原作: ss433_2", text_start, top + 112, "#FFFFFF", "bold 17px Verdana");
- core.fillText('ui', "制作工具: WebStorm", text_start, top + 144, "#FFFFFF", "bold 17px Verdana");
- core.fillText('ui', "测试平台: Chrome/微信/iOS", text_start, top + 176, "#FFFFFF", "bold 17px Verdana");
- core.fillText('ui', '特别鸣谢: ss433_2', text_start, top+208);
- var len = core.canvas.ui.measureText('特别鸣谢: ').width;
- core.fillText('ui', 'iEcho', text_start+len, top+240);
- core.fillText('ui', '打Dota的喵', text_start+len, top+272);
- core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top+304);
- */
}
+////// 绘制帮助页面 //////
ui.prototype.drawHelp = function () {
core.drawText([
"\t[键盘快捷键列表]"+
diff --git a/main.js b/main.js
index e56ca78f..a34c18c4 100644
--- a/main.js
+++ b/main.js
@@ -1,4 +1,33 @@
function main() {
+
+ //------------------------ 用户修改内容 ------------------------//
+
+ this.version = "0.1"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。
+
+ this.useCompress = false; // 是否使用压缩文件
+ // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。
+ // 请注意,只有useCompress是false时才会读取floors目录下的文件,为true时会直接读取libs目录下的floors.min.js文件。
+ // 如果要进行剧本的修改请务必将其改成false。
+
+ this.floorIds = [ // 在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器的顺序和上楼器/下楼器的顺序
+ "sample0", "sample1", "sample2"
+ ];
+ this.pngs = [ // 在此存放所有可能的背景图片;背景图片最好是416*416像素,其他分辨率会被强制缩放成416*416
+ // 建议对于较大的图片,在网上使用在线的“图片压缩工具”来进行压缩,以节省流量
+ // 有关使用自定义背景图,请参见文档的“自定义素材”说明
+ "bg.png", "yewai.png", // 依次向后添加
+ ];
+ this.bgms = [ // 在此存放所有的bgm,和文件名一致。第一项为默认播放项
+ // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
+ '058-Slow01.mid', 'bgm.mp3', 'qianjin.mid', 'star.mid',
+ ];
+ this.sounds = [ // 在此存放所有的SE,和文件名一致
+ // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
+ 'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg',
+ ];
+
+ //------------------------ 用户修改内容 END ------------------------//
+
this.dom = {
'body': document.body,
'gameGroup': document.getElementById('gameGroup'),
@@ -46,14 +75,7 @@ function main() {
this.images = [
'animates', 'enemys', 'hero', 'items', 'npcs', 'terrains'
];
- this.bgms = [ // 在此存放所有的bgm,和文件名一致。第一项为默认播放项
- // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
- '058-Slow01.mid', 'bgm.mp3', 'qianjin.mid', 'star.mid'
- ];
- this.sounds = [ // 在此存放所有的SE,和文件名一致
- // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好
- 'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg'
- ]
+
this.statusBar = {
'image': {
'floor': document.getElementById('img-floor'),
@@ -90,20 +112,6 @@ function main() {
'curse': document.getElementById('curse'),
'hard': document.getElementById("hard")
}
-
- //------------------------ 用户修改内容 ------------------------//
- this.version = "0.1"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。
-
- this.useCompress = false; // 是否使用压缩文件
- // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。
- // 请注意,只有useCompress是false时才会读取floors目录下的文件,为true时会直接读取libs目录下的floors.min.js文件。
- // 如果要进行剧本的修改请务必将其改成false。
-
- this.floorIds = [ // 在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器的顺序和上楼器/下楼器的顺序
- "sample0", "sample1", "sample2"
- ]
- //------------------------ 用户修改内容 END ------------------------//
-
this.floors = {}
this.instance = {};
this.canvas = {};
@@ -122,7 +130,7 @@ main.prototype.init = function () {
coreData[name] = main[name];
}
main.loaderFloors(function() {
- main.core.init(main.dom, main.statusBar, main.canvas, main.images, main.bgms, main.sounds, main.floorIds, main.floors, coreData);
+ main.core.init(main.dom, main.statusBar, main.canvas, main.images, main.pngs, main.bgms, main.sounds, main.floorIds, main.floors, coreData);
main.core.resize(main.dom.body.clientWidth, main.dom.body.clientHeight);
})
});
diff --git a/更新说明.txt b/更新说明.txt
index 83a62ff8..6ee14efa 100644
--- a/更新说明.txt
+++ b/更新说明.txt
@@ -1,12 +1,40 @@
-全键盘操作 √
-经验升级(进阶) √
-增加阻击、多连击等属性;属性显示;加点; √
-九宫格领域、大范围领域 √
-Ctrl快速跳过对话 √
-增加负伤 √
-背景音乐 √
-支持不同层使用不同的地面素材 √
-支持多个Autotile同时存在 √
-直接内嵌了诸多默认的terrains素材 √
-自动定位到上次存/读档位置 √
-每一层可以默认色调 √
+HTML5魔塔样板V1.3:
+
+支持全键盘操作。
+支持将某个图片作为某层的背景素材。
+便捷PS工具支持更改图片色相。
+支持经验升级(进阶/境界塔)。
+打败怪物可以进行加点(加点塔)。
+增加阻击、N连击等属性;在怪物手册有属性显示。
+支持九宫格领域和大范围领域。
+增加负伤。
+支持各种BGM的播放。
+支持不同层使用不同的地面素材;支持多个Autotile同时存在。
+许多细节进行了优化,一些已知的Bug进行了修复。
+
+-----------------------------------------------------------------------
+
+HTML5魔塔样板V1.2:
+
+新增:本地HTTP服务器。
+新增:可视化地图编辑工具。
+新增:便捷PS工具。
+移除了meaning.txt,现在“地图生成器”将直接从js文件中读取数字和图块对应关系。
+新增:对Autotile图块的支持。
+新增:怪物支持多种属性;添加仇恨属性。
+移除了不再支持的checkBlock,现在对于领域和夹击无需再手动指定可能的点了。
+新增:单向箭头、感叹号(单次通行)的支持。
+新增:更多的默认素材,现在对于大多数地图风格无需P图,直接替换即可。
+添加部分自定义事件,部分细节优化,一些已知的Bug进行了修复。
+
+-----------------------------------------------------------------------
+
+HTML5魔塔样板V1.1:
+
+新增:战斗过程显示,可以在设置中关闭
+新增:勇士支持48*32(大图)的行走图
+新增:更改画面色调
+新增:文字显示支持自动换行
+部分修改状态栏UI
+增添Web的Markdown文档,移除原本的doc和pdf文档。
+修复若干Bug。