diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 00000000..d564359d --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/HTML5魔塔样板使用指南.url b/HTML5魔塔样板使用指南.url new file mode 100644 index 00000000..a346778c --- /dev/null +++ b/HTML5魔塔样板使用指南.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,2 +[InternetShortcut] +IDList= +URL=http://ckcz123.github.io/mota-js/ diff --git a/README.md b/README.md index b3497d8a..f153d1a8 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,36 @@ ## 简介 -HTML5 canvas制作的魔塔样板,支持全平台;即使不会编程的用户,按照模板和说明文档也能很快做出一个魔塔游戏;对于有编程基础的用户,那就更好了,根据文档能做出更个性化的魔塔。 +HTML5 canvas制作的魔塔样板,支持全平台游戏! -## 文档 +**即使完全不会编程的用户,按照模板和说明文档也能很快做出一个魔塔游戏!** -* [Demo](http://ckcz123.com/games/template/) -* [Docs](http://ckcz123.github.io/mota-js) +* [Demo / 样板效果](http://ckcz123.com/games/template/) +* [Docs / 使用文档说明](http://ckcz123.github.io/mota-js) + +![样板](./docs/img/sample0.png) ## 更新说明 -- [x] 新增:战斗过程显示!可在全局Flag中开关,也可针对某一怪物单独设置 +#### 2017.12.16 + +- [x] 新增:战斗过程显示,可以在设置中关闭 - [x] 新增:勇士支持48*32(大图)的行走图 - [x] 新增:更改画面色调 - [x] 新增:文字显示支持自动换行 - [x] 部分修改状态栏UI -- [ ] 移除doc和pdf文档,现在请参考Web端,有很好的索引。 -- [x] 移除不再支持的全局变量中的bombTrigger选项 -- [x] 修复键盘开门导致openDoor重复触发的Bug -- [x] 更新快捷商店(文字显示&禁用) \ No newline at end of file +- [x] 增添Web的Markdown文档,移除原本的doc和pdf文档。 +- [x] 修复若干Bug。 + +#### 2017.12.9 + +- 发布初版HTML5魔塔样板 + + +## 联系我们 + +本塔由 `ckcz123` (百度ID `艾之葵`)编写,非常感谢 `iEcho` 在包括状态栏等方面的帮助。 + +HTML5魔塔交流群群号: `539113091` + +如有其它意见或建议,也可以通过发issues、或邮件至ckcz123.com联系我。 diff --git a/doc/H5样板使用指南.docx b/doc/H5样板使用指南.docx deleted file mode 100644 index 2740f119..00000000 Binary files a/doc/H5样板使用指南.docx and /dev/null differ diff --git a/doc/H5样板使用指南.pdf b/doc/H5样板使用指南.pdf deleted file mode 100644 index 28f3f9ce..00000000 Binary files a/doc/H5样板使用指南.pdf and /dev/null differ diff --git a/docs/api.md b/docs/api.md index 0d763ec2..51973cda 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,244 +1,247 @@ # 附录:API列表 -所有系统支持的API都列在了这里。可能被做塔时自定义JS脚本中涉及到的以红色字体标出,并有着详细的解释。 +所有系统支持的API都列在了这里。所有可能被用到的API都在前面用\*标记。 可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 -!> `core.js`:系统核心文件。所有核心逻辑处理都在此文件完成。 +!> **`core.js`:系统核心文件。所有核心逻辑处理都在此文件完成。** ``` js -core.status.floorId //获得当前层floorId -core.status.thisMap //获得当前层的地图信息 +* core.status.floorId // 获得当前层floorId +* core.status.thisMap // 获得当前层的地图信息 -//------ 初始化部分 ------ -core.init //初始化 -core.showStartAnimate //显示开始界面 -core.hideStartAnimate //隐藏开始界面 -core.setStartProgressVal //设置加载进度条进度 -core.setStartLoadTipText //设置加载进度条提示文字 -core.loader //加载图片和音频 -core.loadImage //加载图片 -core.loadSound //加载音频 -core.loadSoundItem //加载某一个音频 -core.isPlaying //游戏是否已经开始 -core.clearStatus //清除游戏状态和数据 -core.resetStatus //重置游戏状态和初始数据 -core.startGame //具体开始游戏 -core.restart //重新开始游戏;此函数将回到标题页面 +// ------ 初始化部分 ------ +core.init // 初始化 +core.showStartAnimate // 显示开始界面 +core.hideStartAnimate // 隐藏开始界面 +core.setStartProgressVal // 设置加载进度条进度 +core.setStartLoadTipText // 设置加载进度条提示文字 +core.loader // 加载图片和音频 +core.loadImage // 加载图片 +core.loadSound // 加载音频 +core.loadSoundItem // 加载某一个音频 +core.isPlaying // 游戏是否已经开始 +core.clearStatus // 清除游戏状态和数据 +core.resetStatus // 重置游戏状态和初始数据 +core.startGame // 具体开始游戏 +* core.restart // 重新开始游戏;此函数将回到标题页面 -//------ 键盘、鼠标事件 ------ -core.onKeyDown //按下某个键时 -core.onKeyUp //放开某个键时 -core.pressKey //按住某个键不动时 -core.keyDown //根据按下键的code来执行一系列操作 -core.keyUp //根据放开键的code来执行一系列操作 -core.ondown //点击(触摸)事件按下时 -core.onmove //当在触摸屏上滑动时 -core.onup //当点击(触摸)事件放开时 -core.getClickLoc //获得点击事件相对左上角的坐标(0到12之间) -core.onclick //具体点击屏幕上(x,y)点时,执行的操作 -core.onmousewheel //滑动鼠标滚轮时的操作(楼层传送时可用滚轮切换楼层) +// ------ 键盘、鼠标事件 ------ +core.onKeyDown // 按下某个键时 +core.onKeyUp // 放开某个键时 +core.pressKey // 按住某个键不动时 +core.keyDown // 根据按下键的code来执行一系列操作 +core.keyUp // 根据放开键的code来执行一系列操作 +core.ondown // 点击(触摸)事件按下时 +core.onmove // 当在触摸屏上滑动时 +core.onup // 当点击(触摸)事件放开时 +core.getClickLoc // 获得点击事件相对左上角的坐标(0到12之间) +core.onclick // 具体点击屏幕上(x,y)点时,执行的操作 +core.onmousewheel // 滑动鼠标滚轮时的操作(楼层传送时可用滚轮切换楼层) -//------ 自动寻路代码相关 ------ -core.clearAutomaticRouteNode //清除自动寻路路线 -core.stopAutomaticRoute //停止自动寻路操作 -core.continueAutomaticRoute //继续剩下的自动寻路操作 -core.clearContinueAutomaticRoute //清除剩下的自动寻路列表 -core.setAutomaticRoute //设置一个自动寻路 -core.automaticRoute //自动寻路算法,找寻最优路径 -core.fillPosWithPoint //显示离散的寻路点 -core.clearStepPostfix //清除已经寻路过的部分 +// ------ 自动寻路代码相关 ------ +core.clearAutomaticRouteNode // 清除自动寻路路线 +core.stopAutomaticRoute // 停止自动寻路操作 +core.continueAutomaticRoute // 继续剩下的自动寻路操作 +core.clearContinueAutomaticRoute // 清除剩下的自动寻路列表 +core.setAutomaticRoute // 设置一个自动寻路 +core.automaticRoute // 自动寻路算法,找寻最优路径 +core.fillPosWithPoint // 显示离散的寻路点 +core.clearStepPostfix // 清除已经寻路过的部分 -//------ 自动行走,行走控制 ------ -core.stopAutoHeroMove //停止勇士的自动行走 -core.setAutoHeroMove //设置勇士的自动行走路线 -core.autoHeroMove //让勇士开始自动行走 -core.setHeroMoveInterval //设置行走的效果动画 -core.setHeroMoveTriggerInterval //设置勇士行走过程中对途经事件的触发检测 -core.turnHero(direction) //设置勇士的方向(转向);如果指定了direction则会面向该方向,否则执行一个转向操作。 -core.moveHero //让勇士开始移动 -core.moveOneStep //每移动一格后执行的事件。中毒时在这里进行扣血判断。 -core.waitHeroToStop(callback) //停止勇士的一切行动,等待勇士行动结束后,再执行callback回调函数。 -core.stopHero //停止勇士的移动状态。 -core.drawHero //在hero层绘制勇士。 -core.setHeroLoc(name, value) //设置勇士的位置。name为”direction”,”x”,”y” -core.getHeroLoc(name) //获得勇士的位置。 -core.nextX //获得勇士面对位置的x坐标 -core.nextY //获得勇士面对位置的y坐标 +// ------ 自动行走,行走控制 ------ +core.stopAutoHeroMove // 停止勇士的自动行走 +core.setAutoHeroMove // 设置勇士的自动行走路线 +core.autoHeroMove // 让勇士开始自动行走 +core.setHeroMoveInterval // 设置行走的效果动画 +core.setHeroMoveTriggerInterval // 设置勇士行走过程中对途经事件的触发检测 +* core.turnHero(direction) // 设置勇士的方向(转向);如果指定了direction则会面向该方向,否则执行一个转向操作。 +core.moveHero // 让勇士开始移动 +core.moveOneStep // 每移动一格后执行的事件。中毒时在这里进行扣血判断。 +core.waitHeroToStop(callback) // 停止勇士的一切行动,等待勇士行动结束后,再执行callback回调函数。 +core.stopHero // 停止勇士的移动状态。 +core.drawHero // 在hero层绘制勇士。 +* core.setHeroLoc(name, value) // 设置勇士的位置。name为”direction”,”x”,”y” +* core.getHeroLoc(name) // 获得勇士的位置。 +* core.nextX // 获得勇士面对位置的x坐标 +* core.nextY // 获得勇士面对位置的y坐标 -//------ 地图和事件处理 ------ -core.openDoor(id, x, y, needKey, callback) //打开一扇位于 (x,y) 的门 -core.battle(id, x, y, force, callback) //进行战斗;force表示是否强制战斗 -core.trigger(x,y) //触发x,y点的事件 -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.setFont //设置某个canvas的文字字体 -core.setLineWidth //设置某个canvas的线宽度 -core.saveCanvas //保存某个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.noPassExists(x,y) //某个点是否不可通行 -core.noPass //某个点是否在区域内且不可通行 -core.npcExists(x,y) //某个点是否存在NPC -core.terrainExists(x,y) //某个点是否存在指定的地形 -core.stairExists(x,y) //某个点是否存在楼梯 -core.nearStair //当前位置是否在楼梯边 -core.enemyExists(x,y) //某个点是否存在怪物 -core.getBlock(x, y, floorId, needEnable) // 获得某个点的block。floorId指定目标楼层,needEnable如果为false则即使该点的事件处于禁用状态也将被返回(否则只有事件启用的点才被返回) -core.moveBlock //显示移动某块的动画,达到{“type”:”move”}的效果 -core.animateBlock //显示/隐藏某个块时的动画效果 -core.addBlock //将某个块从禁用变成启用状态 -core.removeBlock //将某个块从启用变成禁用状态 -core.removeBlockById //根据block的索引删除该块 -core.removeBlockByIds //一次性删除多个block -core.addGlobalAnimate //添加一个全局动画 -core.removeGlobalAnimate //删除一个或所有全局动画 -core.setGlobalAnimate //设置全局动画的显示效果 -core.setBoxAnimate //显示UI层某个box的动画(如怪物手册中怪物的动画) +// ------ 地图和事件处理 ------ +* core.openDoor(id, x, y, needKey, callback) // 打开一扇位于 (x,y) 的门 +* 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.fillText // 在某个canvas上绘制一段文字 +core.fillRect // 在某个canvas上绘制一个矩形 +core.strokeRect // 在某个canvas上绘制一个矩形的边框 +core.setFont // 设置某个canvas的文字字体 +core.setLineWidth // 设置某个canvas的线宽度 +core.saveCanvas // 保存某个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.noPassExists(x,y) // 某个点是否不可通行 +core.noPass // 某个点是否在区域内且不可通行 +* core.npcExists(x,y) // 某个点是否存在NPC +* core.terrainExists(x,y) // 某个点是否存在指定的地形 +* core.stairExists(x,y) // 某个点是否存在楼梯 +* core.nearStair // 当前位置是否在楼梯边 +* core.enemyExists(x,y) // 某个点是否存在怪物 +* core.getBlock(x, y, floorId, needEnable) // 获得某个点的block。floorId指定目标楼层,needEnable如果为false则即使该点的事件处于禁用状态也将被返回(否则只有事件启用的点才被返回) +core.moveBlock // 显示移动某块的动画,达到{“type”:”move”}的效果 +core.animateBlock // 显示/隐藏某个块时的动画效果 +core.addBlock // 将某个块从禁用变成启用状态 +core.removeBlock // 将某个块从启用变成禁用状态 +core.removeBlockById // 根据block的索引删除该块 +core.removeBlockByIds // 一次性删除多个block +core.addGlobalAnimate // 添加一个全局动画 +core.removeGlobalAnimate // 删除一个或所有全局动画 +core.setGlobalAnimate // 设置全局动画的显示效果 +core.setBoxAnimate // 显示UI层某个box的动画(如怪物手册中怪物的动画) core.drawBoxAnimate // 绘制UI层的box动画 -core.updateFg() //更新全地图的显伤 -core.itemCount //获得某个物品的个数 -core.hasItem //是否存在某个物品 -core.setItem //设置某个物品的个数 -core.removeItem //删除某个物品 -core.useItem // 使用某个物品;直接调用items.js中的useItem函数。 -core.canUseItem //能否使用某个物品。直接调用items.js中的canUseItem函数。 -core.addItem //增加某个物品的个数 -core.getItem //获得某个物品时的事件 -core.drawTip //左上角绘制一段提示 -core.drawText //地图中间绘制一段文字 +core.setFg // 色调渐变 +* core.updateFg // 更新全地图的显伤 +* core.itemCount // 获得某个物品的个数 +* core.hasItem // 是否存在某个物品 +* core.setItem // 设置某个物品的个数 +* core.removeItem // 删除某个物品 +* core.useItem // 使用某个物品;直接调用items.js中的useItem函数。 +* core.canUseItem // 能否使用某个物品。直接调用items.js中的canUseItem函数。 +* core.addItem // 增加某个物品的个数 +* core.getItem // 获得某个物品时的事件 +* core.drawTip // 左上角绘制一段提示 +* core.drawText // 地图中间绘制一段文字 -//------ 系统机制 ------ -core.replaceText //将文字中的${和}(表达式)进行替换 -core.calValue //计算表达式的值 -core.unshift //向某个数组前插入另一个数组或元素 -core.setLocalStorage //设置本地存储 -core.getLocalStorage //获得本地存储 -core.removeLocalStorage //移除本地存储 -core.clone //复制一个对象 -core.formatDate //格式化时间为字符串 -core.setTwoDigits //两位数显示 -core.win //获胜;将直接调用events.js中的win函数 -core.lose //失败;将直接调用events.js中的lose函数 -core.debug //进入Debug模式,攻防血和钥匙都调成很高的数值 -core.checkStatus //判断当前能否进入某个事件 -core.openBook //点击怪物手册时的打开操作 -core.useFly //点击楼层传送器时的打开操作 -core.openToolbox //点击工具栏时的打开操作 -core.save //点击保存按钮时的打开操作 -core.load //点击读取按钮时的打开操作 -core.doSL //实际进行存读档事件 -core.syncSave //存档同步操作 -core.saveData //存档到本地 -core.loadData //从本地读档 -core.setStatus //设置勇士属性 -core.getStatus //获得勇士属性 -core.setFlag //设置某个自定义变量或flag -core.getFlag //获得某个自定义变量或flag -core.hasFlag //是否存在某个自定义变量或flag,且值为true -core.insertAction //往当前事件列表之前插入一系列事件 -core.lockControl //锁定状态栏,常常用于事件处理 -core.unlockControl //解锁状态栏 -core.isset //判断某对象是否不为undefined也不会null -core.playSound //播放音频 -core.playBgm //播放背景音乐 -core.changeSoundStatus //切换声音状态 -core.enableSound //启用音效 -core.disableSound //禁用音效 -core.show //动画显示某对象 -core.hide //动画使某对象消失 -core.clearStatusBar //清空状态栏 -core.updateStatusBar //更新状态栏 -core.resize //屏幕分辨率改变后重新自适应 -core.resetSize //屏幕分辨率改变后重新自适应 +// ------ 系统机制 ------ +core.replaceText // 将文字中的${和}(表达式)进行替换 +core.calValue // 计算表达式的值 +core.splitText // 字符串自动换行的分割 +core.unshift // 向某个数组前插入另一个数组或元素 +core.setLocalStorage // 设置本地存储 +core.getLocalStorage // 获得本地存储 +core.removeLocalStorage // 移除本地存储 +core.clone // 复制一个对象 +core.formatDate // 格式化时间为字符串 +core.setTwoDigits // 两位数显示 +core.win // 获胜;将直接调用events.js中的win函数 +core.lose // 失败;将直接调用events.js中的lose函数 +core.debug // 进入Debug模式,攻防血和钥匙都调成很高的数值 +core.checkStatus // 判断当前能否进入某个事件 +core.openBook // 点击怪物手册时的打开操作 +core.useFly // 点击楼层传送器时的打开操作 +core.openToolbox // 点击工具栏时的打开操作 +core.save // 点击保存按钮时的打开操作 +core.load // 点击读取按钮时的打开操作 +core.doSL // 实际进行存读档事件 +core.syncSave // 存档同步操作 +core.saveData // 存档到本地 +core.loadData // 从本地读档 +* core.setStatus // 设置勇士属性 +* core.getStatus // 获得勇士属性 +* core.setFlag // 设置某个自定义变量或flag +* core.getFlag // 获得某个自定义变量或flag +* core.hasFlag // 是否存在某个自定义变量或flag,且值为true +core.insertAction // 往当前事件列表之前插入一系列事件 +* core.lockControl // 锁定状态栏,常常用于事件处理 +* core.unlockControl // 解锁状态栏 +* core.isset // 判断某对象是否不为undefined也不会null +* core.playSound // 播放音频 +* core.playBgm // 播放背景音乐 +core.changeSoundStatus // 切换声音状态 +core.enableSound // 启用音效 +core.disableSound // 禁用音效 +core.show // 动画显示某对象 +core.hide // 动画使某对象消失 +core.clearStatusBar // 清空状态栏 +core.updateStatusBar // 更新状态栏 +core.resize // 屏幕分辨率改变后重新自适应 +core.resetSize // 屏幕分辨率改变后重新自适应 -//------ core.js 结束 ------ +// ------ core.js 结束 ------ ``` -!> `data.js` 定义了一些初始化的数据信息。 +!> **`data.js` 定义了一些初始化的数据信息。** -!> `enemys.js` 定义了怪物信息。 +!> **`enemys.js` 定义了怪物信息。** ``` js -core.enemys.getSpecialText //获得特殊属性的文字 -core.enemys.getDamage //获得某个怪物的伤害 -core.enemys.getCritical //计算某个怪物的临界值 -core.enemys.getCriticalDamage //计算某个怪物的临界减伤 -core.enemys.getDefDamage //计算某个怪物的1防减伤 -core.enemys.calDamage //实际的伤害计算公式 -core.enemys.getCurrentEnemys //获得当前层剩下的的怪物列表 - +* core.enemys.getSpecialText // 获得特殊属性的文字 +* core.enemys.getDamage // 获得某个怪物的伤害 +* core.enemys.getExtraDamage // 获得某个怪物的额外伤害(吸血) +* core.enemys.getCritical // 计算某个怪物的临界值 +* core.enemys.getCriticalDamage // 计算某个怪物的临界减伤 +* core.enemys.getDefDamage // 计算某个怪物的1防减伤 +* core.enemys.calDamage // 实际的伤害计算公式 +core.enemys.getCurrentEnemys // 获得当前层剩下的的怪物列表 ``` -!> `events.js` 定义了各个事件的处理流程。 +!> **`events.js` 定义了各个事件的处理流程。** ``` js -core.events.startGame //开始游戏 -core.events.win //获胜 -core.events.lose //失败 -core.events.checkBlock //检查领域、夹击事件 -core.events.afterChangeFloor //楼层切换结束时的事件 -core.events.doEvents //开始执行一系列自定义事件 -core.events.doAction //执行当前自定义事件列表中的下一个事件 -core.events.insertAction //往当前自定义事件列表前插入若干个事件 -core.events.openShop //打开一个全局商店 -core.events.disableQuickShop //禁用一个快捷商店 -core.events.canUseQuickShop //当前能否使用快捷商店 -core.events.useItem //尝试使用道具 -core.events.afterBattle //战斗结束后触发的事件 -core.events.afterOpenDoor //开一个门后触发的事件 -core.events.passNet //经过一个路障 -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.startGame // 开始游戏 +* core.events.win // 获胜 +* core.events.lose // 失败 +core.events.checkBlock // 检查领域、夹击事件 +core.events.afterChangeFloor // 楼层切换结束时的事件 +core.events.doEvents // 开始执行一系列自定义事件 +core.events.doAction // 执行当前自定义事件列表中的下一个事件 +core.events.insertAction // 往当前自定义事件列表前插入若干个事件 +core.events.openShop // 打开一个全局商店 +core.events.disableQuickShop // 禁用一个快捷商店 +* core.events.canUseQuickShop // 当前能否使用快捷商店 +* core.events.useItem // 尝试使用道具 +core.events.afterBattle // 战斗结束后触发的事件 +core.events.afterOpenDoor // 开一个门后触发的事件 +core.events.passNet // 经过一个路障 +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 // 设置页面打开时 ``` !> `maps.js` 定义了地图,以及每个数字所代表的意义。 ``` js -core.maps.loadFloor //加载某个楼层(从剧本或存档中) -core.maps.getBlock //将数字替换成实际的内容 -core.maps.addEvent //向该楼层添加剧本的自定义事件 -core.maps.addChangeFloor //向该楼层添加剧本的楼层转换事件 -core.maps.initMaps //初始化所有地图 -core.maps.save //将当前地图重新变成数字,以便于存档 -core.maps.load //将存档中的地图信息重新读取出来 +core.maps.loadFloor // 加载某个楼层(从剧本或存档中) +core.maps.getBlock // 将数字替换成实际的内容 +core.maps.addEvent // 向该楼层添加剧本的自定义事件 +core.maps.addChangeFloor // 向该楼层添加剧本的楼层转换事件 +core.maps.initMaps // 初始化所有地图 +core.maps.save // 将当前地图重新变成数字,以便于存档 +core.maps.load // 将存档中的地图信息重新读取出来 ``` !> `ui.js` 定义了各种界面的绘制。 ``` js -core.ui.closePanel //结束一切事件和绘制,关闭UI窗口,返回游戏进程 -core.ui.drawTextBox //绘制一个对话框 -core.ui.drawChoices //绘制一个选项界面 -core.ui.drawConfirmBox //绘制一个确认/取消的警告页面 -core.ui.drawSettings //绘制系统菜单栏 -core.ui.drawQuickShop //绘制快捷商店选择栏 -core.ui.drawWaiting //绘制一个“请稍后”页面 -core.ui.drawSyncSave //绘制存档同步选项 -core.ui.drawPagination //绘制分页 -core.ui.drawEnemyBook //绘制怪物手册 -core.ui.drawFly //绘制楼层传送器 -core.ui.drawToolbox //绘制道具栏 -core.ui.drawSLPanel //绘制存档/读档界面 -core.ui.drawThumbnail //绘制一个缩略图 -core.ui.drawAbout //绘制“关于”界面 +core.ui.closePanel // 结束一切事件和绘制,关闭UI窗口,返回游戏进程 +core.ui.drawTextBox // 绘制一个对话框 +core.ui.drawChoices // 绘制一个选项界面 +core.ui.drawConfirmBox // 绘制一个确认/取消的警告页面 +core.ui.drawSettings // 绘制系统菜单栏 +core.ui.drawQuickShop // 绘制快捷商店选择栏 +core.ui.drawBattleAnimate // 绘制战斗过程 +core.ui.drawWaiting // 绘制一个“请稍后”页面 +core.ui.drawSyncSave // 绘制存档同步选项 +core.ui.drawPagination // 绘制分页 +core.ui.drawEnemyBook // 绘制怪物手册 +core.ui.drawFly // 绘制楼层传送器 +core.ui.drawToolbox // 绘制道具栏 +core.ui.drawSLPanel // 绘制存档/读档界面 +core.ui.drawThumbnail // 绘制一个缩略图 +core.ui.drawAbout // 绘制“关于”界面 ``` diff --git a/docs/element.md b/docs/element.md index 03db5135..eca3f69f 100644 --- a/docs/element.md +++ b/docs/element.md @@ -3,6 +3,7 @@ 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 请打开样板0层 `sample0.js` 进行参照对比。 + ![生成地图](./img/sample0.png) ## 道具 @@ -27,10 +28,11 @@ ## 怪物 -本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。 +本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。 如有自己的怪物素材需求请参见[自定义素材](./personalization#自定义素材)的内容。 怪物可以由特殊属性,每个怪物最多只能有一个特殊属性。怪物的特殊属性所对应的数字(special)在下面的`getSpecialText`中定义,请勿对已有的属性进行修改。 + ![怪物属性](./img/getSpecialText.png) 怪物的伤害计算在下面的`calDamage`函数中,如有自己需求的伤害计算公式请修改该函数的代码。 @@ -40,26 +42,31 @@ 拿到幸运金币后,打怪获得的金币将翻倍。 吸血怪需要在怪物后添加value,代表吸血的比例。 + ![怪物吸血](./img/blood.png) 中毒怪让勇士中毒后,每步扣减的生命值由`data.js`中的values定义。 衰弱怪让勇士衰弱后,攻防会暂时下降一定的数值(直到衰弱状态解除恢复);这个下降的数值同在`data.js`中的values定义。 + ![debuff](./img/debuff.png) 诅咒怪将让勇士陷入诅咒状态,诅咒状态下杀怪不获得金币和经验值。 领域怪需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 + ![怪物属性](./img/domainEnemy.png) 出于游戏性能的考虑,我们不可能每走一步都对领域和夹击进行检查。因此我们需要在本楼层的 checkBlock 中指明哪些点可能会触发领域和夹击事件,在这些点才会对领域和夹击进行检查和处理。 -!> 请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。 +![检查夹击和领域](./img/checkBlock.png) + +!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。** 本塔不支持阻击、激光、仇恨、自爆、退化等属性。 (这些属性基本都太恶心了,恶心到都不想加上去) -如有额外需求,可参见[自定义自定义怪物属性](./personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。 +如有额外需求,可参见[自定义怪物属性](./personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。 ### 路障、楼梯、传送门 @@ -68,9 +75,10 @@ 路障同样会尽量被自动寻路绕过。 有关楼梯和传送门,必须在该层样板的changeFloor里指定传送点的目标。 -![怪物属性](./img/changefloor.png) -!> 请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。如(6,0)代表最上面一行的正中间一列。 +![楼层转换](./img/changefloor.png) + +!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。如(6,0)代表最上面一行的正中间一列。** floorId指定的是目标楼层的唯一标识符(ID)。 @@ -83,8 +91,18 @@ floorId指定的是目标楼层的唯一标识符(ID)。 可以指定time,指定后切换动画时长为指定的数值。 楼梯和传送门默认可`"穿透"`。所谓穿透,就是当寻路穿过一个楼梯/传送门后,不会触发楼层传送事件,而是继续前进。通过系统Flag可以指定是否穿透,你也可以对每个传送点单独设置该项。 -![怪物属性](./img/floorset.png) + +![楼层转换穿透](./img/floorSet.png) + +  + +  上面就是整个样板中的各个元件说明。通过这种方式,你就已经可以做出一部没有任何事件的塔了。 尝试着做一个两到三层的塔吧! + +========================================================================================== + +[继续阅读下一章:事件](./event) + diff --git a/docs/event.md b/docs/event.md index 1b22bd7d..efdbd47d 100644 --- a/docs/event.md +++ b/docs/event.md @@ -6,57 +6,17 @@ 本塔所有的事件都是依靠触发`trigger`完成的。例如,勇士碰到一个门可以触发一个事件`openDoor`,勇士碰到怪物可以触发一个事件`battle`,勇士碰到一个(上面定义的)楼层传送点可以触发一个事件`changeFloor`,勇士穿过路障可以触发一个事件`passNet`,包括勇士到达一个指定的`checkBlock`也可以触发一个检查领域、夹击的事件。上面说的这些事件都是系统本身自带的,即类似于RMXP中的公共事件。 -``` js -events.prototype.init = function () { - this.events = { - 'battle': function (data, core, callback) { - core.battle(data.event.id, data.x, data.y); - if (core.isset(callback)) - callback(); - }, - 'getItem': function (data, core, callback) { - core.getItem(data.event.id, 1, data.x, data.y); - if (core.isset(callback)) - callback(); - }, - 'openDoor': function (data, core, callback) { - core.openDoor(data.event.id, data.x, data.y, true); - if (core.isset(callback)) - callback(); - }, - 'changeFloor': function (data, core, callback) { - var heroLoc = null; - if (core.isset(data.event.data.loc)) { - heroLoc = {'x': data.event.data.loc[0], 'y': data.event.data.loc[1]}; - if (core.isset(data.event.data.direction)) - heroLoc.direction = data.event.data.direction; - } - core.changeFloor(data.event.data.floorId, data.event.data.stair, - heroLoc, data.event.data.time, callback); - }, - 'passNet': function (data, core, callback) { - core.events.passNet(data); - if (core.isset(callback)) - callback(); - }, - "checkBlock": function (data, core, callback) { - core.events.checkBlock(data.x, data.y); - if (core.isset(callback)) - callback(); - }, - 'action': function (data, core, callback) { - core.events.doEvents(data.event.data, data.x, data.y); - if (core.isset(callback)) callback(); - } - } -} -``` - 上述这些默认的事件已经存在处理机制,不需要我们操心。我们真正所需要关心的,其实只是一个自定义的事件。 -!> 本塔中的所有自定义事件能且只能被其他事件触发。不存在RMXP里面那种,设置了某个变量为true后,一个事件被自动执行的问题。这点和RMXP的区别非常大,请务必注意。 +**本塔中的所有自定义事件能且只能被其他事件触发。不存在RMXP里面那种,设置了某个变量为true后,一个事件被自动执行的问题。这点和RMXP的区别非常大,请务必注意。** + +所有事件都存在两种状态:启用和禁用。 +- 启用状态下,该事件才处于可见状态,可被触发、交互与处理。 +- 禁用状态下该事件相当于不存在,不可见、不可被触发、不可交互。 + +所有事件默认情况下都是启用的,除非指定了`enable: false`。 +在事件列表中使用`type: show`和`type: hide`可以将一个禁用事件启用,或将一个启用事件给禁用。 -所有事件都存在两种状态:启用和禁用。启用状态下,该事件才处于可见状态,可被触发、交互与处理。禁用状态下该事件相当于不存在,不可见、不可被触发、不可交互。所有事件默认情况下都是启用的,除非指定了`enable: false`。在事件列表中使用`type: show`和`type: hide`可以将一个禁用事件启用,或将一个启用事件给禁用。这些都在后面会提到。 ## 自定义事件 @@ -66,15 +26,15 @@ events.prototype.init = function () { ``` js "events": { // 该楼的所有可能事件列表 - "x, y": { - "trigger": "action", // 触发的trigger, action代表自定义事件 - "enable": true, // 该事件初始状态下是否处于启用状态 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } + "x,y": { + "trigger": "action", // 触发的trigger, action代表自定义事件 + "enable": true, // 该事件初始状态下是否处于启用状态 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] + } } ``` @@ -82,7 +42,7 @@ events.prototype.init = function () { 我们上面提到,有很多系统已经默认的事件(例如开门、打怪等,相当于公共事件)。如果我们需要自定义一个事件,则需要`"trigger": "action"`,它表示该点是一个自定义事件。 -!> 如果系统本身存在事件(如一个怪物),且你指定了`"trigger": "action"`,则原事件会被覆盖。 +!> **如果系统本身存在事件(如一个怪物),且你指定了`"trigger": "action"`,则原事件会被覆盖。** 这种情况下一般需采用后面的afterBattle,afterOpenDoor和afterGetItem来进行事件的处理。 @@ -90,15 +50,15 @@ events.prototype.init = function () { ``` js "events": { // 该楼的所有可能事件列表 - "x, y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - "enable": true, // 该事件初始状态下是否处于启用状态 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } + "x,y": { + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + "enable": true, // 该事件初始状态下是否处于启用状态 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] + } } ``` @@ -108,52 +68,54 @@ events.prototype.init = function () { ``` js "events": { // 该楼的所有可能事件列表 - "x, y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } + "x,y": { + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] + } } ``` -`"data"`为实际执行的事件列表。类似于RMXP中的"脚本",也是由一系列事件顺序构成的(其中可以使用`if和choices`来进行条件判断或用户选择,后面会具体提到)。 +`"data"`为实际执行的事件列表。类似于RMXP中的"脚本",也是由一系列事件顺序构成的(其中可以使用`if`和`choices`来进行条件判断或用户选择,后面会具体提到)。 如果大括号里只有`"data"`,则可以省略大括号和`"data"`,直接写中括号数组,换句话说,上面和下面这种写法也是等价的,可以进行一下比较: ``` js "events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x, y": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] + // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 + "x,y": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` 这种简写方式可以极大方便地造塔者进行造塔。 -!> 请注意:如果该点初始的`enable`为`false`,或者该点本身有系统默认事件且需要覆盖(`trigger`),则必须采用上面那种大括号写的方式来定义。 +!> **请注意:如果该点初始的`enable`为`false`,或者该点本身有系统默认事件且需要覆盖(`trigger`),则必须采用上面那种大括号写的方式来定义。** + +  `"data"`中,是由一系列的自定义事件类型组成。每个元素类似于: ``` js "events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x, y": [ // 实际执行的事件列表 - {"type": "xxx", ...},// 事件1 - {"type": "xxx", ...},// 事件2 - // ... - // 按顺序写事件,直到结束 - ] + // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 + "x,y": [ // 实际执行的事件列表 + {"type": "xxx", ...}, // 事件1 + {"type": "xxx", ...}, // 事件2 + // ... + // 按顺序写事件,直到结束 + ] } ``` -`"type"`为该自定义事件的类型;而后面的…则为具体的一些事件参数。 +`"type"`为该自定义事件的类型;而后面的`...`则为具体的一些事件参数。 每次,系统都将取出数组中的下一个事件,并进行处理;直到数组中再无任何事件,才会完全结束本次自定义事件,恢复游戏状态。 @@ -165,13 +127,13 @@ events.prototype.init = function () { ``` js "events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x, y": [ // 实际执行的事件列表 - {"type": "text", "data": "在界面上的一段文字"},// 显示文字事件 - {"type": "text", "data": "这是第二段文字"},// 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] + // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 + "x,y": [ // 实际执行的事件列表 + {"type": "text", "data": "在界面上的一段文字"}, // 显示文字事件 + {"type": "text", "data": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 + ] } ``` @@ -179,36 +141,36 @@ events.prototype.init = function () { ``` js "events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x, y": [ // 实际执行的事件列表 - "在界面上的一段文字",// 直接简写,和下面写法完全等价 - {"type": "text", "data": "这是第二段文字"},// 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] + // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 + "x,y": [ // 实际执行的事件列表 + "在界面上的一段文字",// 直接简写,和下面写法完全等价 + {"type": "text", "data": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 + ] } ``` 所有文字事件均可以进行简写,系统会自动转成`{"type": "text"}`的形式。 -另外非常值得注意的一点就是,我们需要手动输入`\n`来进行换行: +值得注意的是,系统会自动对文字进行换行;不过我们也可以手动加入`\n`来换行。 ``` js "events": { // 该楼的所有可能事件列表 // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x, y": [ // 实际执行的事件列表 - "在界面上的一段文字",// 直接简写 - "这是第一行\n这是第二行\n这是第三行",// 显示第二个文字事件 + "x,y": [ // 实际执行的事件列表 + "这一段文字特别特别长,但是系统可以对它进行自动换行,因此我们无需手动换行", + "这是第一行\n这是第二行\n这是第三行", // ... // 按顺序写事件,直到结束 ] } ``` -我们可以给文字加上标题或图标,只要以`\t[…]`开头就可以,大致共有如下几种情况: +我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以,大致共有如下几种情况: - `\t[hero]` 显示勇士的图标和名字 -- `\t[monster_id]`显示某个怪物的图标和名字。`monster_id`在`enemys`中有定义,请前往参照。在`enemys`中有定义,请前往参照。 +- `\t[monster_id]`显示某个怪物的图标和名字。`monster_id`在`enemys`中有定义,请前往参照。 - 例如:`\t[blackMagician]` 将显示黑暗大法师的图标和名字。 - `\t[名字,npc_id]` 显示某个NPC的名字和图标。`npc_id`所对应的图标具体在`icons.js`中有定义,请前往参照。 - 例如:`\t[小妖精,fairy]` 将显示名字为"小妖精",且是仙子的图标。 @@ -216,43 +178,41 @@ events.prototype.init = function () { - 如果该中括号内只有一项,且不为`hero`也不为某个怪物的ID,则会直接显示。如 `\t[你死了]` 直接显示一个标题为"你死了"。 ``` js -"x, y": [ // 实际执行的事件列表 - "一段普通文字", - "\t[hero]这是一段勇士说的话", - "\t[blackMagician]这是一段黑暗大法师说的话", - "\t[小妖精,fairy]这是一段小妖精说的话,使用仙子(fairy)的图标", - "\t[你赢了]直接显示标题为【你赢了】", +"x,y": [ // 实际执行的事件列表 + "一段普通文字", + "\t[hero]这是一段勇士说的话", + "\t[blackMagician]这是一段黑暗大法师说的话", + "\t[小妖精,fairy]这是一段小妖精说的话,使用仙子(fairy)的图标", + "\t[你赢了]直接显示标题为【你赢了】", ] - ``` 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js -"x, y": [ // 实际执行的事件列表 - "1+2=${1+2}, 4*5+6=${4*5+6}", // 显示"1+2=3, 4*5+6=26" +"x,y": [ // 实际执行的事件列表 + "1+2=${1+2}, 4*5+6=${4*5+6}", // 显示"1+2=3, 4*5+6=26" ] - ``` 我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值。 ``` js -"x, y": [ // 实际执行的事件列表 - "你当前的攻击力是${status: atk}, 防御是${status: def}", - "你的攻防和的十倍是${10*(status: at+status: def)}", - "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", - "你访问某个老人的次数为${flag: man_times}", +"x,y": [ // 实际执行的事件列表 + "你当前的攻击力是${status:atk}, 防御是${status:def}", + "你的攻防和的十倍是${10*(status:atk+status:def)}", + "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", + "你访问某个老人的次数为${flag:man_times}", ] - ``` -- `status: xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(魔防值),money(金币),experience(经验)。 -- `item: xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 -- `flag: xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 +- `status:xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(魔防值),money(金币),experience(经验)。 +- `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 +- `flag:xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 + +另外,有个小`trick`。是否想立刻知道显示效果? +你可以用Chrome浏览器打开游戏,按Ctrl+Shift+I打开开发者工具,找到Console(控制台),并中输入`core.drawText("...")` 即可立刻看到文字显示的效果。适当调整文字,使得显示效果满意后,再复制粘贴到你的剧情文本中。 -另外,有个小`trick`。是否有时候会觉得不好找到\n(换行)的位置?有一个很简单的方法: -你可以用Chrome浏览器打开游戏,按Ctrl+Shift+I打开开发者工具,找到Console(控制台),并中输入`core.drawText("…")` 即可立刻看到文字显示的效果。适当添加\n,使得显示效果满意后,再复制粘贴到你的剧情文本中。 ![调试](./img/eventdebug.png) ### setValue:设置勇士的某个属性、道具个数,或某个变量/Flag的值 @@ -260,92 +220,106 @@ events.prototype.init = function () { `{"type": "setValue"}` 能修改勇士的某个属性、道具个数、或某个自定义变量或`Flag`的值。 其大致写法如下: -使用`setValue`需要指定`name和value`选项。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "setValue", "name": "status: atk", "value": "status:atk+10" } // 攻击提高10点 - {"type": "setValue", "name": "status: money", "value": "1000" } // 将金币数设为1000(不是+1000) - {"type": "setValue", "name": "status: hp", "value": "status:hp*2" } // 生命值翻倍 - {"type": "setValue", "name": "item: yellowKey", "value": "item:yellowKey+3" } // 黄钥匙个数加3 - {"type": "setValue", "name": "item: boom", "value": "item:boom+10" } // 炸弹个数+10 - {"type": "setValue", "name": "flag: man_times", "value": "0" } // 将变量man_times设为0 - {"type": "setValue", "name": "flag: man_times", "value": "flag:man_times+2*status:atk" } // 将变量man_times的值加上勇士的攻击数值的两倍 +"x,y": [ // 实际执行的事件列表 + {"type": "setValue", "name": "...", "value": "..."}, // 设置一个属性、道具或自定义Flag ] - ``` -name为你要修改的属性/道具/Flag,每次只能修改一个值。写法和上面完全相同,`status:xxx` 表示勇士一个属性,item: xxx 表示某个道具个数,`flag:xxx` 表示某个变量或flag值。参见上面的介绍。 +使用`setValue`需要指定`name`和`value`选项。 + +name为你要修改的属性/道具/Flag,每次只能修改一个值。写法和上面完全相同,`status:xxx` 表示勇士一个属性,`item:xxx` 表示某个道具个数,`flag:xxx` 表示某个变量或flag值。参见上面的介绍。 value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`的写法表示勇士当前属性,道具个数和某个变量/Flag值。 +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 + {"type": "setValue", "name": "status:money", "value": "1000" } // 将金币数设为1000(不是+1000) + {"type": "setValue", "name": "status:hp", "value": "status:hp*2" } // 生命值翻倍 + {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+3" } // 黄钥匙个数加3 + {"type": "setValue", "name": "item:boom", "value": "item:boom+10" } // 炸弹个数+10 + {"type": "setValue", "name": "flag:man_times", "value": "0" } // 将变量man_times设为0 + {"type": "setValue", "name": "flag:man_times", "value": "flag:man_times+2*status:atk" } // 将变量man_times的值加上勇士的攻击数值的两倍 +] +``` + + ### show: 将一个禁用事件启用 我们上面提到了,所有事件都必须靠其他事件驱动来完成,不存在当某个flag为true时自动执行的说法。那么,我们自然要有启用事件的写法。 + 使用`{"type":"show"}`可以将一个本身禁用的事件启用。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "show", "loc": [3, 6], "floorId": "MT1", "time": 500 } // 启用MT1层[3, 6]位置事件, 动画500ms - {"type": "show", "loc": [3, 6], "time": 500 } // 如果启用目标是当前层,则可以省略floorId项 - {"type": "show", "loc": [3, 6] } // 如果不指定动画时间,则立刻显示,否则动画下过逐渐显示,time为动画时间 +"x,y": [ // 实际执行的事件列表 + {"type": "show", "loc": [3,6], "floorId": "MT1", "time": 500 } // 启用MT1层[3,6]位置事件,动画500ms + {"type": "show", "loc": [3,6], "time": 500 } // 如果启用目标是当前层,则可以省略floorId项 + {"type": "show", "loc": [3,6]} // 如果不指定动画时间,则立刻显示,否则动画效果逐渐显示,time为动画时间 ] - ``` show事件需要用loc指定目标点的坐标;剩下有两个参数floorId和time。 + floorId为目标点的楼层,如果不是该楼层的事件(比如4楼小偷开2楼的门)则是必须的,如果是当前楼层可以忽略不写。 time为动画效果时间,如果指定了某个大于0的数,则会以动画效果慢慢从无到有显示,动画时间为该数值;如果不指定该选项则无动画直接立刻显示。 -!> 要注意的是,调用show事件后只是让该事件从禁用状态变成启用,从不可见不可交互变成可见可交互,但本身不会去执行该点的事件。 +!> **要注意的是,调用show事件后只是让该事件从禁用状态变成启用,从不可见不可交互变成可见可交互,但本身不会去执行该点的事件。** ### hide: 将一个启用事件禁用 `{"type":"hide"}`和show刚好相反,它会让一个已经启用的事件被禁用。 + 其参数和show也完全相同,loc指定事件的位置,floorId为楼层(同层可忽略),time指定的话事件会以动画效果从有到无慢慢消失。 + 但是和show事件有所区别的是:loc选项也可以忽略;如果忽略loc则使当前事件禁用。(即使禁用当前事件,也不会立刻结束当前正在进行的,而是仍然会依次将列表中剩下的事件执行完) + 请注意,一次性事件必须要加 `{"type":"hide"}`,尤其是例如走到某个点,触发对话或机关门(陷阱)这种,否则每次都会重复触发。 + NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}`,可以不写loc选项代表当前事件,可以指定time使NPC动画消失。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "hide", "loc": [3, 6], "floorId": "MT1", "time": 500 } // 启用MT1层[3, 6]位置事件, 动画500ms - {"type": "hide", "loc": [3, 6], "time": 500 } // 如果启用目标是当前层,则可以省略floorId项 - {"type": "hide", "loc": [3, 6] } // 如果不指定动画时间,则立刻显示,否则动画下过逐渐显示,time为动画时间 - {"type": "hide", "time": 500 } // 如果不指定loc选项则默认为当前点, 例如这个就是500ms消失当前对话的NPC - {"type": "hide" } // 无动画将当前事件禁用,常常适用于某个空地点(触发陷阱事件、触发机关门这种) +"x,y": [ // 实际执行的事件列表 + {"type": "hide", "loc": [3,6], "floorId": "MT1", "time": 500 } // 禁用MT1层[3,6]位置事件,动画500ms + {"type": "hide", "loc": [3,6], "time": 500 } // 如果启用目标是当前层,则可以省略floorId项 + {"type": "hide", "loc": [3,6] } // 如果不指定动画时间,则立刻消失,否则动画效果逐渐消失,time为动画时间 + {"type": "hide", "time": 500 } // 如果不指定loc选项则默认为当前点, 例如这个就是500ms消失当前对话的NPC + {"type": "hide" } // 无动画将当前事件禁用,常常适用于某个空地点(触发陷阱事件、触发机关门这种) ] - ``` ### trigger: 立即触发另一个地点的事件 `{"type":"trigger"}` 会立刻触发当层另一个地点的自定义事件。 + 上面我们说到,show事件会让一个禁用事件启用且可被交互;但是如果我想立刻让它执行应该怎么办呢?使用trigger就行。 + 其基本写法如下: ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "trigger", "loc": [3, 6], }, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 - "执行trigger后,这段文字将不会再被显示" +"x,y": [ // 实际执行的事件列表 + {"type": "trigger", "loc": [3, 6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 + "执行trigger后,这段文字将不会再被显示" ] - ``` 其后面带有loc选项,代表另一个地点的坐标。 -执行trigger事件后,当前事件将立刻被结束,剩下所有内容被忽略;然后重新启动另一个地点的action事件。例如上面这个例子,下面的文字将不会再被显示,而是直接跳转到`"3,6"`对应的事件列表从头执行。 + +执行trigger事件后,当前事件将立刻被结束,剩下所有内容被忽略;然后重新启动另一个地点的action事件。 + +例如上面这个例子,下面的文字将不会再被显示,而是直接跳转到`"3,6"`对应的事件列表从头执行。 ### revisit: 立即重启当前事件 revisit和trigger完全相同,只不过是立刻触发的还是本地点的事件 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "revisit" }, // 立即触发本事件,等价于{"type": "trigger", "loc": [3, 6], } - "执行revisit后,这段文字将不会再被显示" +"x,y": [ // 实际执行的事件列表 + {"type": "revisit"}, // 立即触发本事件,等价于{"type": "trigger", "loc": [x,y]} + "执行revisit后,这段文字将不会再被显示" ] - ``` revisit其实是trigger的简写,只不过是loc固定为当前点。 @@ -355,18 +329,19 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ### exit: 立刻结束当前事件 上面说到像商人一类,购买物品后可以立刻revisit重新访问,但是这样就相当于陷入了死循环导致无法离开。 + 可以使用`{"type":"exit"}`立刻结束事件。调用exit后,将立刻结束一切事件,清空事件列表,并返回游戏。 + 例如玩家点击商人的"离开"选项,则可以调用exit返回游戏。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "exit" }, // 立即结束事件并恢复游戏,一切列表中的事件都将不再被执行 - "执行exit后,这段文字将不会再被显示" +"x,y": [ // 实际执行的事件列表 + {"type": "exit" }, // 立即结束事件并恢复游戏,一切列表中的事件都将不再被执行 + "执行exit后,这段文字将不会再被显示" ] - ``` -#### update: 立刻更新状态栏和地图显伤 +### update: 立刻更新状态栏和地图显伤 当我们在上面调用show事件,显示一个怪物后,该怪物将不会有显伤显示。如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update"}` 即可。 @@ -376,17 +351,18 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 基本写法:`{"type": "sleep", "time": xxx}` ,其中xxx为指定的毫秒数。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "sleep", "time": 1000 }, // 等待1000ms - "等待1000ms后才开始执行这个事件" +"x,y": [ // 实际执行的事件列表 + {"type": "sleep", "time": 1000}, // 等待1000ms + "等待1000ms后才开始执行这个事件" ] - ``` ### battle: 强制战斗 -调用battle可强制与某怪物进行战斗(而无需去触碰到它) +调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 + 例如,《宿命的旋律》中,一区有个骷髅队长,当你拿了它周围三个物品时,就会立刻触发强制战斗事件。这时候就可以用`{"type": "battle"}` 实现。 + 其基本写法是: `{"type": "battle", "id": xxx}`,其中xxx为怪物ID。 ``` js @@ -405,7 +381,9 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ``` 上面就是样板层中右上角的强制战斗例子。 + 如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。 + 强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。强制战斗不会触发任何afterBattle里的事件。 ### openDoor: 开门 @@ -413,110 +391,169 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 调用`{"type":"openDoor"}`可以打开一扇门。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "openDoor", "loc": [3, 6], "floorId": "MT1" }, // 打开MT1层的[3, 6]位置的门 - {"type": "openDoor", "loc": [3, 6]}, // 如果是本层则可省略floorId +"x,y": [ // 实际执行的事件列表 + {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 + {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId ] - ``` loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层则可以忽略floorId选项。 + 如果loc所在的点是一个墙壁,则作为暗墙来开启。 -如果loc所在的点既不是门也不是墙壁,则忽略忽略本事件。 + +如果loc所在的点既不是门也不是墙壁,则忽略本事件。 + openDoor不会触发任何afterOpenDoor里的事件。 ### changeFloor: 楼层切换 在事件中也可以对楼层进行切换。一个比较典型的例子就是TSW中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。 + changeFloor的事件写法大致如下。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "changeFloor", "floorId": "sample0","loc": [10, 10], "direction": "left", "time": 1000 }, //后面几项依次为楼层id,楼层位置(不能放置在楼梯位置)两项为必填,勇士方向可选,切换时间也是可选。 +"x,y": [ // 实际执行的事件列表 + {"type": "changeFloor", "floorId": "sample0","loc": [10, 10], "direction": "left", "time": 1000 }, + //后面几项依次为楼层id,楼层位置(这两项为必填);勇士方向可选,切换时间也是可选。 ] - ``` 可以看到,与上面的楼梯、传送门的写法十分类似。 + 但是相比那个而言,不支持stair楼梯位置(只能写坐标),没有穿透选项。 + direction为可选的,指定的话将使勇士的朝向变成该方向 + time为可选的,指定的话将作为楼层切换动画的时间。 -### changePos: 当前位置切换 +### changePos: 当前位置切换/勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 + 这时候可以用changePos。其参数和changeFloor类似,但少了floorId和time两个选项。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "changePos", "loc": [10, 10], "direction": "left"}, // 直接切换勇士的坐标,loc为目标地点必填,后面勇士换位后方向可选 +"x,y": [ // 实际执行的事件列表 + {"type": "changePos", "loc": [10,10], "direction": "left"}, // 直接切换勇士的坐标,loc为目标地点,后面勇士换位后方向 + {"type": "changePos", "loc", [10,10]}, // 如无需指定方向则direction可省略 + {"type": "changePos", "direction": "left"} // loc也可省略,只指定direction;此时等价于当前勇士转向到某个方向。 ] - ``` ### openShop: 打开一个全局商店 使用openShop可以打开一个全局商店。有关全局商店的说明可参见[全局商店](#全局商店)。 +### disableShop: 禁用一个全局商店 + +使用disableShop可以永久禁用全局商店直到再次被openShop打开为止。有关全局商店的说明可参见[全局商店](#全局商店)。 + +### setFg: 更改画面色调 + +我们可以使用 `{"type": "setFg"}` 来更改画面色调。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "setFg", "color": [255,255,255], "time": 1000}, // 更改画面色调为纯白,动画时间1000毫秒 + {"type": "setFg", "color": [0,0,0]}, // 更改画面色调为纯黑,不指定动画时间(使用默认时间) + {"type": "setFg"} // 如果不指定color则恢复原样。 +] +``` + +color为需要更改画面色调的颜色。它是一个三元数组,分别指定目标颜色的R,G,B值。 +- 常见颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,255,0],等等。 + +如果color不指定则恢复原样。 + +time为可选的,如果指定,则会作为更改画面色调的时间。 + ### move: 让某个NPC/怪物移动 如果我们需要移动某个NPC或怪物,可以使用`{"type": "move"}`。 + 下面是该事件常见的写法: ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "move", "time": 750, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组 - {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 - "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 - ], "immediateHide": true }, //最后这项可选,制定为true则立刻消失,否则渐变消失 +"x,y": [ // 实际执行的事件列表 + {"type": "move", "time": 750, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组 + {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 + "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 + ], "immediateHide": true }, //immediateHide可选,制定为true则立刻消失,否则渐变消失 ] - ``` time选项必须指定,为每移动一步所需要用到的时间。 + steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。 + 如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。 + immediateHide为一个可选项,代表该事件移动完毕后是否立刻消失。如果该项指定了并为true,则移动完毕后直接消失,否则以动画效果消失。 + 值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用hide事件。 + 换句话说,当move事件被调用后,该点本身的事件将被禁用。 + move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediateHide决定是否立刻消失还是以time作为时间来动画效果消失。 如果想让move后的NPC/怪物仍然可以被交互,需采用如下的写法: +``` js +"4,3": [ // [4,3]是一个NPC,比如小偷 + {"type": "move", "time": 750, "steps": [ // 向上移动两格,每步750毫秒 + {"direction": "up", "value": 2}, + ], "immediateHide": true}, // 移动完毕立刻消失 + {"type": "show", "loc": [4,1]} // 指定[4,1]点的NPC立刻生效(显示) + {"type": "trigger", "loc": [4,1]} // 立刻触发[4,1]点的事件 +], +"4,1": { // [4,1]也是这个NPC,而且是向上移动两个的位置 + "enable": false, // 初始时需要是禁用状态,被show调用后将显示出来 + "data": [ + "\t[杰克,thief]这样看起来就好像移动过去后也可以被交互。" + ] +} +``` + 即,在移动的到达点指定一个初始禁用的相同NPC,然后move事件中指定immediateHide使立刻消失,并show该到达点坐标使其立刻显示(看起来就像没有消失),然后就可以触发目标点的事件了。 ### playSound: 播放音效 使用playSound可以立刻播放一个音效。 + 使用方法:`{"type": "playSound", "name": "item.ogg"}` + 值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 + 由于考虑到用户流量问题,每个额外音效最好不超过20KB。 ### win: 获得胜利 `{"type": "win", "reason": "xxx"}` 将会直接调用events.js中的win函数,并将reason作为参数传入。 + 该事件会显示获胜页面,并重新游戏。 -### lose: 游戏失败/Game Over +### lose: 游戏失败 `{"type": "lose", "reason": "xxx"}` 将会直接调用`events.js`中的lose函数,并将reason作为参数传入。 + 该事件会显示失败页面,并重新开始游戏。 ### if: 条件判断 使用`{"type": "if"}`可以对条件进行判断,根据判断结果将会选择不同的分支执行。 + 其大致写法如下: ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "if", "condition": "...", // 测试某个条件 - "true": [ // 条件成立则执行true里面的事件 +"x,y": [ // 实际执行的事件列表 + {"type": "if", "condition": "...", // 测试某个条件 + "true": [ // 条件成立则执行true里面的事件 + + ], + "false": [ // 条件不成立则执行false里的事件 - ], - "false": [ // 条件不成立则执行false里的事件 - - ] - }, + ] + }, ] ``` @@ -529,18 +566,18 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate 例如下面这个例子,每次将检查你的攻击力是否大于500,不是的场合将给你的攻击力加100点。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "if", "condition": "status:atk>500", // 判断攻击力是否大于500 - "true": [ // 条件成立则执行true里面的事件 - "你的攻击力已经大于500了!", - {"type": "exit"} // 结束 - ], - "false": [ // 条件不成立则执行false里的事件 - "你当前攻击力为${status:atk}, 不足500!\n给你增加100点攻击力!", - {"type": "setValue", "name": "status:atk", "value": "status:atk+100"}, //攻击力加100, 接着会执行revisit事件 - ] - }, - {"type", "revisit"}, //立刻重启本事件, 直到攻击力大于500后结束 +"x,y": [ // 实际执行的事件列表 + {"type": "if", "condition": "status:atk>500", // 判断攻击力是否大于500 + "true": [ // 条件成立则执行true里面的事件 + "你的攻击力已经大于500了!", + {"type": "exit"} // 立刻结束本事件 + ], + "false": [ // 条件不成立则执行false里的事件 + "你当前攻击力为${status:atk}, 不足500!\n给你增加100点攻击力!", + {"type": "setValue", "name": "status:atk", "value": "status:atk+100"}, // 攻击力加100, 接着会执行revisit事件 + ] + }, + {"type", "revisit"}, // 立刻重启本事件, 直到攻击力大于500后结束 ] ``` @@ -549,31 +586,34 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate - 给定的表达式(condition)一般需要返回true或false。 - `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为false。而JS中,`false==0`这个判断是成立的,因此我们可以简单使用 `"flag:npc_times==0"` 来判断某个NPC是否被访问过。 - 即使成功失败的场合不执行事件,对应的true或false数组也需要存在,不过简单的留空就好。 -- if可不断嵌套,一层套一层;如成立的场合再进行另一个if判断等。 +- if可以不断进行嵌套,一层套一层;如成立的场合再进行另一个if判断等。 - if语句内的内容执行完毕后将接着其后面的语句继续执行。 ### choices: 给用户提供选项 choices是一个很麻烦的事件,它将弹出一个列表供用户进行选择。 + 当用户做出了不同的选择,可以有着不同的分支处理。 + 其完全类似于RMXP中的"显示选择项","XX的场合",只不过同样是需要使用数组来定义。 + 其大致写法如下: ``` js "x, y": [ // 实际执行的事件列表 - {"type": "choices", "text": "...", // 提示文字 - "choices": [{ - "text": "选项1文字", "action": [ - // 选项1执行的事件 - ]}, - "text": "选项2文字", "action": [ - // 选项2执行的事件 - ]}, - "text": "选项3文字", "action": [ - // 选项3执行的事件 - ]}, - ], - }, + {"type": "choices", "text": "...", // 提示文字 + "choices": [ + {"text": "选项1文字", "action": [ + // 选项1执行的事件 + ]}, + {"text": "选项2文字", "action": [ + // 选项2执行的事件 + ]}, + {"text": "选项3文字", "action": [ + // 选项3执行的事件 + ]}, + ] + }, ] ``` @@ -592,28 +632,27 @@ choices为一个数组,其中每一项都是一个选项列表。 // 这部分逻辑相对比较长,细心看,很容易看懂的。 {"type": "if", "condition": "flag:woman_times==0", // 条件判断:是否从未访问过此商人。 "true": [ // 如果从未访问过该商人,显示一段文字 - "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用\nif 语句进行条件判断,以及 choices 提供\n选项来供用户进行选择。", - "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始\n将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门\n钥匙,并消失不再出现。", - "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,\n是很容易看懂并理解的。" + "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", + "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", + "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" // 第一次访问结束 ], "false": [ // 如果已经访问过该商人 {"type": "if", "condition": "flag:woman_times==8", // 条件判断:是否已经出售七把钥匙 "true": [ // 如果已经出售过七把钥匙,则直接结束 - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话\n我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大\n黄门钥匙吧,希望你能好好用它。", + "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", + "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", {"type": "setValue", "name": "item:bigKey", "value": "item:bigKey+1"}, // 获得一把大黄门钥匙 "\t[老人,woman]我先走了,拜拜~", {"type":"hide", "time": 500}, // 消失 {"type":"exit"} // 立刻结束当前事件。下面的 setValue 和 revisit 都不会再执行。 ], "false": [ // 否则,显示选择页面 - {"type": "choices", "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", // 显示一个选择页面 + {"type": "choices", "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", // 显示一个卖钥匙的选择页面 "choices": [ // 提供四个选项:黄钥匙、蓝钥匙、红钥匙、离开。前三个选项显示需要的金额 {"text": "黄钥匙(${9+flag:woman_times}金币)", "action": [ // 第一个选项,黄钥匙 // 选择该选项的执行内容 - // 条件判断:钱够不够 - {"type": "if", "condition": "status:money>=9+flag:woman_times", + {"type": "if", "condition": "status:money>=9+flag:woman_times", // 条件判断:钱够不够 "true": [ {"type": "setValue", "name": "status:money", "value": "status:money-(9+flag:woman_times)"}, // 扣减金钱 {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+1"}, // 增加黄钥匙 @@ -626,30 +665,10 @@ choices为一个数组,其中每一项都是一个选项列表。 } ]}, {"text": "蓝钥匙(${18+2*flag:woman_times}金币)", "action": [ // 第二个选项:蓝钥匙 - // 逻辑和上面黄钥匙完全相同,不赘述 - {"type": "if", "condition": "status:money>=18+2*flag:woman_times", - "true": [ - {"type": "setValue", "name": "status:money", "value": "status:money-(18+2*flag:woman_times)"}, - {"type": "setValue", "name": "item:blueKey", "value": "item:blueKey+1"}, - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - {"type": "revisit"} - ] - } + // 逻辑和上面黄钥匙完全相同,略 ]}, {"text": "红钥匙(${36+4*flag:woman_times}金币)", "action": [ // 第三个选项:红钥匙 - // 逻辑和上面黄钥匙完全相同,不赘述 - {"type": "if", "condition": "status:money>=36+4*flag:woman_times", - "true": [ - {"type": "setValue", "name": "status:money", "value": "status:money-(36+4*flag:woman_times)"}, - {"type": "setValue", "name": "item:redKey", "value": "item:redKey+1"}, - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - {"type": "revisit"} - ] - } + // 逻辑和上面黄钥匙完全相同,略 ]}, {"text": "离开", "action": [ // 第四个选项:离开 {"type": "exit"} // 立刻结束当前事件 @@ -670,18 +689,17 @@ choices为一个数组,其中每一项都是一个选项列表。 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 ``` js -"x, y": [ // 实际执行的事件列表 - {"type": "function", "function": function(){ // 执行一段js脚本 - // 这里写js代码 - alert(core.getStatus("atk")); // 弹窗显示勇士的攻击力 - }}, +"x,y": [ // 实际执行的事件列表 + {"type": "function", "function": function(){ // 执行一段js脚本 + // 这里写js代码 + alert(core.getStatus("atk")); // 弹窗显示勇士的攻击力 + }}, ] - ``` `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统所有支持的API都在附录中给出。 +系统所有支持的API都在[这里](./api)中给出。 这里只简单列出给一些最常见的API: @@ -702,78 +720,105 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 ## 全局商店 我们可以采用上面的choices方式来给出一个商店。这样的商店确实可以有效地进行操作,但是却是"非全局"的,换句话说,只有在碰到NPC的时候才能触发商店事件。 + 我们可以定义"全局商店",其可以直接被快捷栏中的"快捷商店"进行调用。换句话说,我们可以定义快捷商店,让用户在任意楼层都能快速使用商店。 + 全局商店定义在`data.js`中,找到shops一项。 ``` js "shops": { // 定义全局商店(即快捷商店) - "moneyShop1": { // 商店唯一ID - "name": "贪婪之神", // 商店名称(标题) - "icon": "blueShop", // 商店图标,blueShop为蓝色商店,pinkShop为粉色商店 - "textInList": "3楼金币商店", // 在快捷商店栏中显示的名称 - "use": "money", // 商店所要使用的。只能是"money"或"experience"。 - "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times作为参数计算。 - // 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。 - // 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。 - // 例如: "need": "25" 就是恒定需要25金币的商店; "need": "20+2*times" 就是第一次访问要20金币,以后每次递增2金币的商店。 - // 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。 - "choices": [ // 商店的选项 - {"text": "生命+800", "effect": "status:hp+=800"}, - // 如果有多个effect以分号分开,参见下面的经验商店 - {"text": "攻击+4", "effect": "status:atk+=4"}, - {"text": "防御+4", "effect": "status:def+=4"}, - {"text": "魔防+10", "effect": "status:mdef+=10"} - // effect只能对status和items进行操作,不能修改flag值。 - // 中间只能用+=符号(也就是只能增加某个属性或道具) - // 其他effect样例: - // "items:yellowKey+=1" 黄钥匙+1 - // "items:pickaxe+=3" 破墙镐+3 - ] - }, - "expShop1": { // 商店唯一ID - "name": "经验之神", - "icon": "pinkShop", - "textInList": "5楼经验商店", - "use": "experience", // 该商店使用的是经验进行计算 - "need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1,然后下面选项里给定具体数值 - "choices": [ - // 在choices中写need,可以针对每个选项都有不同的需求。 - // 这里的need同样可以以times作为参数,比如 "need": "100+20*times" - {"text": "等级+1", "need": "100", "effect": "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"}, - ] - } -}, + "moneyShop1": { // 商店唯一ID + "name": "贪婪之神", // 商店名称(标题) + "icon": "blueShop", // 商店图标,blueShop为蓝色商店,pinkShop为粉色商店 + "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 + "use": "money", // 商店所要使用的。只能是"money"或"experience"。 + "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times作为参数计算。 + // 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。 + // 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。 + // 例如: "need": "25" 就是恒定需要25金币的商店; "need": "20+2*times" 就是第一次访问要20金币,以后每次递增2金币的商店。 + // 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。 + + "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字,需手动加换行符。可以使用${need}表示上面的need值。 + "choices": [ // 商店的选项 + {"text": "生命+800", "effect": "status:hp+=800"}, + // 如果有多个effect以分号分开,参见下面的经验商店 + {"text": "攻击+4", "effect": "status:atk+=4"}, + {"text": "防御+4", "effect": "status:def+=4"}, + {"text": "魔防+10", "effect": "status:mdef+=10"} + // effect只能对status和items进行操作,不能修改flag值。且其中间只能用+=符号(也就是只能增加某个属性或道具) + // 其他effect样例: + // "items:yellowKey+=1" 黄钥匙+1 + // "items:pickaxe+=3" 破墙镐+3 + ] + }, + "expShop1": { // 商店唯一ID + "name": "经验之神", + "icon": "pinkShop", + "textInList": "1F经验商店", + "use": "experience", // 该商店使用的是经验进行计算 + "need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1,然后下面选项里给定具体数值 + "text": "勇敢的武士啊,给我若干经验就可以:", + "choices": [ + // 在choices中写need,可以针对每个选项都有不同的需求。 + // 这里的need同样可以以times作为参数,比如 "need": "100+20*times" + {"text": "等级+1", "need": "100", "effect": "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一项里。 每个全局商店有一个唯一标识符(ID),然后是一系列对该商店的定义。 -- name为商店的名称(打开商店后的标题) +- name 为商店的名称(打开商店后的标题) - icon 为商店的图标,blueShop为蓝色商店,pinkShop为粉色商店 - textInList 为其在快捷商店栏中显示的名称,如"3楼金币商店"等 - use 为消耗的类型,是金币(money)还是经验(experience)。 - need 是一个表达式,计算商店所需要用到的数值。 -- 可以将times作为参数,times为该商店已经访问过的次数,第一次访问时times是0。 -- 如果对于每个选项都需要不同的数值,这里设为"-1";可参见下面经验商店的例子。 + - 可以将times作为参数,times为该商店已经访问过的次数,第一次访问时times是0。 + - 如果对于每个选项都需要不同的数值,这里设为"-1";可参见下面经验商店的例子。 +- text 为商店所说的话。可以用${need}表示需要的数值。 - choices 为商店的各个选项,是一个list,每一项是一个选项 -- text为显示文字。请注意这里不支持 ${} 的表达式计算。 -- effect 为该选项的效果;effect只能对status或items进行操作,且必须是 `status:xxx+=yyy` 或 `item:xxx+=yyy`的形式。即中间必须是+=符号。 -- 如有多个effect(例如升级全属性提升),使用分号分开,参见经验商店的写法。 + - text为显示文字。请注意这里不支持 ${} 的表达式计算。 + - effect 为该选项的效果;effect只能对status或items进行操作,且必须是 `status:xxx+=yyy` 或 `item:xxx+=yyy`的形式。即中间必须是+=符号。 + - 如有多个effect(例如升级全属性提升),使用分号分开,参见经验商店的写法。 像这样定义了全局商店后,即可在快捷栏中看到。 + 请注意,快捷商店默认是不可被使用的。直到至少调用一次自定义事件中的 `{"type": "openShop"}` 打开商店后,才能真正在快捷栏中被使用。 +``` java +"1,0": [ // 金币商店 + // 打开商店前,你也可以添加自己的剧情 + // 例如,通过if来事件来判断是不是第一次访问商店,是的则显示一段文字(类似宿命的华音那样) + {"type": "openShop", "id": "moneyShop1"}, // 这里的id要和data.js中你定义的商店ID完全一致 + // 调用openShop事件后,所有当前事件都会被结束(同exit事件),然后打开一个全局商店 + + // 如果需要禁用商店,则需要调用disableShop事件 + {"type": "disableShop", "id": "moneyShop1"} +], +``` + +如果需要禁用一个全局商店,则简单的在事件中调用 `{"type": "disableShop"}` 即可。 + +禁用商店后商店将无法从快捷栏中进行使用,直到再次使用openShop打开商店为止。 + +另外需要注意的一点就是,每层楼都有一个 canUseQuickShop 选项。如果该选项置为false则无法在该层使用快捷商店。 + ## 系统引发的自定义事件 我们知道,所有自定义事件都是需要定义在`"x,y"`处,并且得让用户经过或撞上才能触发的。 + 但是有一系列的事件,例如战斗、获取道具、开门等,是系统已经预先设定好的事件,我们不能将其覆盖为自定义事件,否则原本的战斗等事件会被覆盖。 + 为了解决此问题,在每层的剧本中引入了三个元素:`afterBattle`, `afterGetItem`, `afterOpenDoor`。 -当某个战斗结束后,将执行`afterBattle`中,对应位置的事件。 -当获取某个道具后,将执行`afterGetItem`中,对应位置的事件。 -当开了某个门后,将执行`afterOpenDoor`中,对应位置的事件。 +- 当某个战斗结束后,将执行`afterBattle`中,对应位置的事件。 +- 当获取某个道具后,将执行`afterGetItem`中,对应位置的事件。 +- 当开了某个门后,将执行`afterOpenDoor`中,对应位置的事件。 + 例如,下面就是一个典型的杀怪开门的例子。每当杀死一个守卫机关门的怪物,将检查是否满足打开机关门的条件。如果是,则开启机关门。 ``` js @@ -799,7 +844,9 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 }, ``` -同样,为了实现类似于RMXP中,到达某一层后自动触发某段事件的效果,样板中还存在`firstArrive`事件。当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。 +同样,为了实现类似于RMXP中,到达某一层后自动触发某段事件的效果,样板中还存在`firstArrive`事件。 + +当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。 ## 开始,难度分歧,获胜与失败 @@ -855,11 +902,9 @@ events.prototype.win = function(reason) { }) }); } - ``` -其参数reason为获胜原因(即type:win事件里面的reason参数)。 -你可以在这里修改自己的获胜界面显示的文字。 +其参数reason为获胜原因(即type:win事件里面的reason参数)。你可以在这里修改自己的获胜界面显示的文字。 当失败(`{"type": "lose"}`,或者被怪强制战斗打死、被领域怪扣血死、中毒导致扣血死,路障导致扣血死等等)事件发生时,将调用`events.js`中的`lose`事件。其直接显示一段文字,并重新开始游戏。 @@ -876,5 +921,8 @@ events.prototype.lose = function(reason) { } ``` -其参数reason为失败原因。 -你可以在这里修改失败界面时显示的文字。 +其参数reason为失败原因。你可以在这里修改失败界面时显示的文字。 + +========================================================================================== + +[继续阅读下一章:个性化](./personalization) diff --git a/docs/img/checkBlock.png b/docs/img/checkBlock.png new file mode 100644 index 00000000..26635177 Binary files /dev/null and b/docs/img/checkBlock.png differ diff --git a/docs/img/chrome.png b/docs/img/chrome.png new file mode 100644 index 00000000..4f0f6e48 Binary files /dev/null and b/docs/img/chrome.png differ diff --git a/docs/img/dataInit.png b/docs/img/dataInit.png deleted file mode 100644 index f0d4266e..00000000 Binary files a/docs/img/dataInit.png and /dev/null differ diff --git a/docs/img/flag.png b/docs/img/flag.png new file mode 100644 index 00000000..b042ca8e Binary files /dev/null and b/docs/img/flag.png differ diff --git a/docs/img/flag1.png b/docs/img/flag1.png deleted file mode 100644 index f7794aca..00000000 Binary files a/docs/img/flag1.png and /dev/null differ diff --git a/docs/img/flag2.png b/docs/img/flag2.png deleted file mode 100644 index 9ed48e59..00000000 Binary files a/docs/img/flag2.png and /dev/null differ diff --git a/docs/img/floorset.png b/docs/img/floorset.png index 99c55ce8..67ea6f67 100644 Binary files a/docs/img/floorset.png and b/docs/img/floorset.png differ diff --git a/docs/img/imginfo.png b/docs/img/imginfo.png new file mode 100644 index 00000000..a05321b9 Binary files /dev/null and b/docs/img/imginfo.png differ diff --git a/docs/img/init.png b/docs/img/init.png new file mode 100644 index 00000000..15e01205 Binary files /dev/null and b/docs/img/init.png differ diff --git a/docs/index.md b/docs/index.md index eae32dc9..4fa736a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,3 +10,7 @@ > 这个魔塔样板,可以让你在完全不懂任何编程的情况下,做出自己的H5魔塔。不会代码?没关系!只要你想做,就能做出来! 继续查看文档的详细介绍,让你学会如何使用这一个样板来制作属于自己的HTML5魔塔。 + +========================================================================================== + +[继续阅读下一章:现在就做出自己的第一部H5魔塔!](./start) diff --git a/docs/personalization.md b/docs/personalization.md index 026ef310..0d7d04e5 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -5,18 +5,12 @@ ## 自定义素材 所有素材的图片都在`images`目录下。 - -`animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。 - -`enemys.png` 为所有怪物的图片。地图生成器中对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可) - -`heros.png`为勇士行走图。这里是`4*3`的,你也可以用`4*4`的,不过需要一些修改,之后会提到。 - -`items.png` 为所有道具的图标。 - -`npcs.png` 为所有NPC的图标,也是两帧。 - -`terrains.png` 为所有地形的图标。 +- `animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。 +- `enemys.png` 为所有怪物的图片。地图生成器中对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可) +- `heros.png`为勇士行走图。 +- `items.png` 为所有道具的图标。 +- `npcs.png` 为所有NPC的图标,也是两帧。 +- `terrains.png` 为所有地形的图标。 系统会读取`icon.js`文件,并获取每个ID对应的图标所在的位置。 @@ -27,6 +21,7 @@ 在使用自定义素材后,我们可以使用地图生成器来识别新的素材。打开同目录下的`meaning.txt`,按照已有的方式来增加或编辑内容即可。 第一列是地图生成器中的数字,第二列是它所在的文件名,第三列是坐标。 + ![地图含义](./img/mapmean.png) ### 使用自定义地形(路面、墙壁等) @@ -46,22 +41,20 @@ 如果`items.png`中不存在你需要的图标,则可以自己P一张图,将你需要的图标覆盖到某个用不到的图标上。或者也可以接着后面向下拉伸。 所有道具必须是`32x32`像素。 + P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。 要在系统中启用你的图标,你需要自己指定一个道具的ID(不能和任何已有的重名),然后进行如下操作: - -- 在`items.js`中道具的定义列表中编辑,修改你的自定义道具的名称,类型,说明文字等。 +1. 在`items.js`中道具的定义列表中编辑,修改你的自定义道具的名称,类型,说明文字等。 - 即捡即用类道具的cls为items,消耗类道具的cls为tools,永久类道具的cls为constants。 -- 在`icon.js`中,找到items一栏,往里面添加你的图标位置。 -- 在`maps.js`中,找到对应位置,往里面添加自己的数字和道具的一一对应关系。(该数字可任意指定,不能和已有的冲突),类似下面这样 +2. 在`icon.js`中,找到items一栏,往里面添加你的图标位置。 +3. 在`maps.js`中,找到对应位置,往里面添加自己的数字和道具的一一对应关系。(该数字可任意指定,不能和已有的冲突),类似下面这样 ``` js -if (id == 60) tmp.event = {'cls': 'items', 'id': 'curseWine'} // 解咒药水 -if (id == 61) tmp.event = {'cls': 'items', 'id': 'superWine'} // 万能药水 -if (id == 62) tmp.event = {'cls': 'items', 'id': 'knife'} // 屠龙匕首 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'} // 圣锤 +// 可以在这里添加自己的数字-道具对应关系 ``` 有关如何自行实现一个道具的效果,参见[自定义道具效果](#自定义道具效果)。 @@ -93,46 +86,19 @@ P图完毕后,可以在地图生成器中加入对应的数字和图标的对 1. 在`icon.js`中,找到npcs一栏,往里面添加你的图标位置。 2. 在`maps.js`中,找到对应位置,往里面添加自己的数字和NPC的一一对应关系。 -``` js -// 121-150 NPC - if (id == 121) tmp.event = {'cls': 'npcs', 'id': 'man'}; - if (id == 122) tmp.event = {'cls': 'npcs', 'id': 'woman'}; - if (id == 123) tmp.event = {'cls': 'npcs', 'id': 'thief'}; - if (id == 124) tmp.event = {'cls': 'npcs', 'id': 'fairy'}; - if (id == 125) tmp.event = {'cls': 'npcs', 'id': 'magician'}; - if (id == 126) tmp.event = {'cls': 'npcs', 'id': 'womanMagician'}; -``` - ### 使用自定义勇士图标 我们同样可以使用自定义的勇士图标,比如小可绒之类。 -拿一个勇士的行走图覆盖`hero.png`即可。 +直接拿一个勇士的行走图覆盖`hero.png`即可。 -请注意:勇士必须是`32x32`像素,不能使用超过`32x32`的图片。 +**支持任何行走大图,如`32x32`, `48x32`, `64x32`等等。** -覆盖了`hero.png`后,我们需要在`icons.js`中编辑图标的位置信息。 - -``` js -'heros': { - 'hero1': { - 'down': {'loc': 0, 'stop': 0, 'leftFoot': 1, 'rightFoot': 2}, - 'left': {'loc': 1, 'stop': 0, 'leftFoot': 1, 'rightFoot': 2}, - 'right': {'loc': 2, 'stop': 0, 'leftFoot': 1, 'rightFoot': 2}, - 'up': {'loc': 3, 'stop': 0, 'leftFoot': 1, 'rightFoot': 2} - } -}, -``` - -hero1为勇士ID,和`data.js`中的ID对应,一般不修改。 - -接着`down/left/right/up`是勇士四个朝向,每个朝向中的loc表示是在`hero.png`中的第几行;后面的stop,leftFoor,rightFoot分别是停止图,左脚行走图和右脚行走图在该行的第几列就行。 - -我们只需要覆盖图片后编辑这里即可。即使是`4x4`的行走图,也是没问题的(需要指定左脚和右脚所在的第几列)。 +  通过上述这几种方式,我们可以修改素材图片(使用自定义素材),指定数字并放入地图生成器中,然后在系统中进行启用。 -!> 请注意:强制要求所有素材都必须是`32x32`的,不然可能会造成不可预料的后果。 +!> **请注意:除了勇士行走图外,其他所有素材强制要求必须是`32x32`的,不然可能会造成不可预料的后果。** ## 自定义道具效果 @@ -148,56 +114,33 @@ hero1为勇士ID,和`data.js`中的ID对应,一般不修改。 1. 找到`getItemEffect`函数;所有即捡即用类道具的效果都在这里实现。 2. 算道具效果系数,或应该增加的值。 -3. 修改同样修改下面的`getItemEffectTip`函数,使提示文字相应变动。 - ``` js items.prototype.getItemEffect = function(itemId, itemNum) { var itemCls = core.material.items[itemId].cls; // 消耗品 if (itemCls === 'items') { - if (itemId === 'redJewel') core.status.hero.atk += core.values.redJewel; - if (itemId === 'blueJewel') core.status.hero.def += core.values.blueJewel; - if (itemId === 'greenJewel') core.status.hero.mdef += core.values.greenJewel; + var floor = parseInt(core.status.thisMap.name); // 获得当前楼层。此name和剧本中的name完全一致。 + var ratio = 1; // 道具效果系数 + if (floor>=11 && floor<=20 ) ratio = 2; // 11-20F(二区),道具效果翻倍 + if (floor>=21 && floor<=30 ) ratio = 3; // 21-30F(二区),道具效果三倍 + // ... 根据自己的需要来写 + + if (itemId === 'redJewel') core.status.hero.atk += core.values.redJewel * ratio; // 将初始效果乘以倍数 + if (itemId === 'blueJewel') core.status.hero.def += core.values.blueJewel * ratio; // 将初始效果乘以倍数 + if (itemId === 'greenJewel') core.status.hero.mdef += core.values.greenJewel * ratio; // 将初始效果乘以倍数 if (itemId == 'yellowJewel') { // 黄宝石属性:需自己定义 - core.status.hero.hp+=1000; - core.status.hero.atk+=6; - core.status.hero.def+=6; - core.status.hero.mdef+=10; - } - if (itemId === 'redPotion') core.status.hero.hp += core.values.redPotion; - if (itemId === 'bluePotion') core.status.hero.hp += core.values.bluePotion; - if (itemId === 'yellowPotion') core.status.hero.hp += core.values.yellowPotion; - if (itemId === 'greenPotion') core.status.hero.hp += core.values.greenPotion; - if (itemId === 'sword1') core.status.hero.atk += core.values.sword1; - if (itemId === 'sword2') core.status.hero.atk += core.values.sword2; - if (itemId == 'sword3') core.status.hero.atk += core.values.sword3; - if (itemId == 'sword4') core.status.hero.atk += core.values.sword4; - if (itemId === 'sword5') core.status.hero.atk += core.values.sword5; - if (itemId === 'shield1') core.status.hero.def += core.values.shield1; - if (itemId === 'shield2') core.status.hero.def += core.values.shield2; - if (itemId === 'shield3') core.status.hero.def += core.values.shield3; - if (itemId === 'shield4') core.status.hero.def += core.values.shield4; - if (itemId === 'shield5') core.status.hero.def += core.values.shield5; - if (itemId === 'bigKey') { // 只有是钥匙盒才会执行这一步 - core.status.hero.items.keys.yellowKey++; - core.status.hero.items.keys.blueKey++; - core.status.hero.items.keys.redKey++; - } - if (itemId == 'superPotion') core.status.hero.hp *= 2; - if (itemId == 'moneyPocket') core.status.hero.money += core.values.moneyPocket; - } - else { - core.addItem(itemId, itemNum); - } -} +// ... 下略 ``` +3. 修改同样修改下面的`getItemEffectTip`函数,使提示文字相应变动。 -!> 请注意这里`core.status.thisMap.name`获取的是当前层中,你在剧本文件里写的name那一项(即状态栏中的层数显示)。然后可以通过几个简单的if来判断应该增加的值。 +!> **请注意这里`core.status.thisMap.name`获取的是当前层中,你在剧本文件里写的name那一项(即状态栏中的层数显示)。然后可以通过几个简单的if来判断应该增加的值。** ### 消耗类道具(cls: tools);永久类道具(cls: constants) 如果要自己实现消耗类道具或永久类道具的使用效果,则需修改`items.js`中的canUseItem和useItem两个函数。 -具体过程比较复杂,需要一定的JS能力,在这里就不多说了,有需求可以找艾之葵进行了解。 + +具体过程比较复杂,需要一定的JS能力,在这里就不多说了,有需求可以找`艾之葵`进行了解。 + 但值得一提的是,我们可以使用`core.hasItem(name)` 来判断是否某个道具是否存在。例如下面是passNet(通过路障处理)的一部分: ``` js @@ -206,31 +149,7 @@ events.prototype.passNet = function (data) { // 有鞋子 if (core.hasItem('shoes')) return; if (data.event.id=='lavaNet') { // 血网 - core.status.hero.hp -= core.values.lavaDamage; - if (core.status.hero.hp<=0) { - core.status.hero.hp=0; - core.updateStatusBar(); - core.events.lose('lava'); - return; - } - core.drawTip('经过血网,生命-'+core.values.lavaDamage); - } - if (data.event.id=='poisonNet') { // 毒网 - if (core.hasFlag('poison')) return; - core.setFlag('poison', true); - } - if (data.event.id=='weakNet') { // 衰网 - if (core.hasFlag('weak')) return; - core.setFlag('weak', true); - core.status.hero.atk-=core.values.weakValue; - core.status.hero.def-=core.values.weakValue; - } - if (data.event.id=='curseNet') { // 咒网 - if (core.hasFlag('curse')) return; - core.setFlag('curse', true); - } - core.updateStatusBar(); -} +// ... 下略 ``` 我们进行了一个简单的判断,如果拥有绿鞋,则不进行任何路障的处理。 @@ -238,8 +157,36 @@ events.prototype.passNet = function (data) { ### 实战!拿到神圣盾后免疫吸血、领域、夹击效果 1. 在getItemEffect中修改拿到神圣盾时的效果,标记一个自定义Flag。 -2. 免疫吸血效果:在`enemys.js`的getDamage函数中,找到extra_damage,并编辑成如果存在神圣盾标记,额外伤害为0。 +``` js +if (itemId === 'shield5') { + core.status.hero.def += core.values.shield5; + core.setFlag("shield5", true); // 增加一个自定义Flag:已经拿到神圣盾 +} +``` +2. 免疫吸血效果:在`enemys.js`的getExtraDamage函数中,编辑成如果存在神圣盾标记,额外伤害为0。 +``` js +enemys.prototype.getExtraDamage = function (monster) { + var extra_damage = 0; + if (monster.special == 11) { // 吸血 + // 吸血的比例 + extra_damage = core.status.hero.hp * monster.value; + if (core.hasFlag("shield5")) extra_damage = 0; // 如果存在神圣盾,则免疫吸血 + extra_damage = parseInt(extra_damage); + } + return extra_damage; +} +``` 3. 免疫领域、夹击效果:在`events.js`中,找到checkBlock函数,并编辑成如果有神圣盾标记,则直接返回。 +``` js +////// 检查领域、夹击事件 ////// +events.prototype.checkBlock = function (x,y) { + if (core.hasFlag("shield5")) return; // 如果拥有神圣盾立刻返回,免疫领域和夹击 + var damage = 0; + // 获得四个方向的怪物 + var directions = [[0,-1],[-1,0],[0,1],[1,0]]; // 上,左,下,右 + var enemys = [null,null,null,null]; +// ... 下略 +``` 4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。 ## 自定义怪物属性 @@ -253,10 +200,18 @@ events.prototype.passNet = function (data) { 因此无敌属性可以这样设置: ``` js -if (hero_atk <= mon_def) return 999999999; // 不可战斗时请直接返回999999999 +// 我们需要对无敌属性指定一个数字,比如18 + +enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special) { + if (mon_special==18 && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架 + return 999999999; // 返回无限大 + + // 魔攻 + if (mon_special == 2) hero_def = 0; +// ... 下略 ``` -对于吸血怪的额外伤害计算在getDamage中的`extra_damage`中。 +对于吸血怪的额外伤害计算在getExtraDamage中。 对于毒衰弱怪物的战斗后结算在`events.js`中的afterBattle函数中。 @@ -264,7 +219,7 @@ if (hero_atk <= mon_def) return 999999999; // 不可战斗时请直接返回9999 `getCritical`, `getCriticalDamage`和`getDefDamage`三个函数依次计算的是该怪物的临界值、临界减伤和1防减伤。也可以适当进行修改。 -## 自定义地图 +## 根据难度分歧来自定义地图 遗憾的是,所有地图数据必须在剧本的map中指定,换句话说,我们无法在游戏进行中动态修改地图,比如为简单难度增加一个血瓶。 @@ -272,22 +227,22 @@ if (hero_atk <= mon_def) return 999999999; // 不可战斗时请直接返回9999 ``` js "firstArrive": [ // 第一次到该楼层触发的事件 - {"type": "if", "condition": "flag:hard!=3", // 判断是否困难难度 - "true": [ // 不为困难,则为普通或简单难度 - {"type": "show", "loc": [3, 6]} // 显示血瓶 - {"type": "if", "condition": "flag:hard==1", // 判断是否是简单难度 - "true": [ - {"type": "show", "loc": [3, 7]} // 简单难度则显示宝石 + {"type": "if", "condition": "flag:hard!=3", // 判断是否困难难度 + "true": [ // 不为困难,则为普通或简单难度 + {"type": "show", "loc": [3,6]} // 显示血瓶 + {"type": "if", "condition": "flag:hard==1", // 判断是否是简单难度 + "true": [ + {"type": "show", "loc": [3,7]} // 简单难度则显示宝石 + ], + "false": [] // 普通难度则只显示血瓶 + }, ], - "false": [] // 普通难度则只显示血瓶 - }, - ], - "false": [] // 困难难度,不进行任何操作 + "false": [] // 困难难度,不进行任何操作 }, ], "events": { - "3, 6": {"enable": false} // 比如[3, 6]点是一个血瓶,初始不可见 - "3, 7": {"enable": false} // 比如[3, 7]点是一个宝石,初始不可见 + "3,6": {"enable": false} // 比如[3,6]点是一个血瓶,初始不可见 + "3,7": {"enable": false} // 比如[3,7]点是一个宝石,初始不可见 } ``` @@ -296,3 +251,7 @@ if (hero_atk <= mon_def) return 999999999; // 不可战斗时请直接返回9999 当第一次到达该楼层时,进行一次判断;如果不为困难难度,则将血瓶显示出来;再判断是否为简单难度,如果是则再把宝石显示出来。 通过对`flag:hard`进行判断的方式,我们也可以达成“对于不同的难度有着不同的地图效果”。 + +========================================================================================== + +[继续阅读附录:所有API列表](./api) diff --git a/docs/start.md b/docs/start.md index b15063ee..4615f5d9 100644 --- a/docs/start.md +++ b/docs/start.md @@ -8,8 +8,8 @@ - Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“地图生成器.exe”即可) - 任一款现代浏览器。强烈推荐Chrome。 -- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。 -([VSCode下载地址](https://code.visualstudio.com/ ),群里的群文件中也有,强烈推荐值。) +- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。 +([VSCode下载地址](https://code.visualstudio.com/ ),群里的群文件中也有,强烈推荐之。) - RPG Maker XP,任一个魔塔样板(推荐魔塔样板7630) 只要满足了上述条件,你就可以开始做自己的塔啦! @@ -18,14 +18,14 @@ 类似于RMXP,本塔每层楼都是一个“剧本”,剧本内主要定义了本层的地图和各种事件。主函数将读取每个剧本,并生成实际的地图供游戏使用。 -我们打开 `libs/floors/` 目录,这个目录是所有剧本的目录。我们需要指定一个楼层名,例如MT1;然后,我们可以将`MT0.js`(模板)复制重命名为为`MT1.js`,并使用文本编辑器打开。(参见下面的图)。 +我们打开 `libs/floors/` 目录,这个目录是所有剧本的目录。我们需要指定一个楼层名,例如MT1;然后,我们可以将`MT0.js`(模板)复制重命名为为`MT1.js`,并使用文本编辑器打开。 + +![新建剧本](./img/script.png) 然后将楼层名改为MT1,floorId改名为MT1;title可以改成任意内容,将在切换楼层时进行显示(比如可以改成“1层小塔”)。 具体样板文件的每个要素都有详细的注释。我们最终的任务其实是,将每个楼层的剧本(地图&事件)给写完即可。 -![新建剧本](./img/script.png) - 换句话说,只需要简单的复制操作,我们就可以新建一个剧本了。 ## 绘制地图 @@ -33,6 +33,7 @@ 遗憾的是,我们的样板是没有像RMXP那样有着很好的UI界面,供大家直接进行绘图可视化操作的。然而,我们仍然可以利用已有的RMXP和魔塔样板,绘制好地图,然后利用目录中的“地图生成器”来转成样板所识别的格式。 首先,我们打开RMXP和魔塔样板,来到绘制地图页面。 + ![绘制地图](./img/rmxp1.png) 然后,任意绘制一张地图。 @@ -49,17 +50,21 @@ ![绘制地图](./img/rmxp3.png) -截图时请注意:只截取有效游戏空间内数据,并且有效空间内的范围必须是13\*13。(如果地图小于13\*13,请用星空或墙壁填充到13\*13)。 +截图时请注意:**只截取有效游戏空间内数据,并且有效空间内的范围必须是13x13。(如果地图小于13*13,请用星空或墙壁填充到13x13)。** + +确认地图的图片文件已经复制到剪切板后,我们打开工具中的“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。 -确认地图的图片文件已经复制到剪切板后,我们打开“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。 ![生成地图](./img/map1.png) 如果有识别不一致的存在,即生成的地图和实际的地图不符,则需要在左边的输入框内实际手动修改,然后再点“图片生成”即可。有关每个数字对应的图块名称,请参见images目录下的`meaning.txt` -!> 注:地图生成器默认只支持经典素材。如果有自定义素材需求(例如原版的1层小塔那种素材),请参见[自定义素材](./personalization#自定义素材)。 +!> **注1:地图生成器默认只支持经典素材。如果有自定义素材需求(例如原版的1层小塔那种素材),请参见[自定义素材](./personalization#自定义素材)。** +!> **注2:请确保截图范围刚好为13x13,并且保证每个位置的像素都是32x32。** 经过确认,生成的地图和原始地图保持一致后,点击“复制地图”,然后粘贴到刚刚剧本文件里的maps中。 + ![地图数组](./img/mapArray.png) + 通过这种在RMXP中画图,截图复制,再用地图生成器识别的方式,我们成功将我们需要的地图变成了样板可识别的格式。 ## 录入数据 @@ -73,29 +78,41 @@ 下面依次进行说明。 我们打开`data.js`文件,这里面定义了各种全局属性和勇士初始值。 -我们可以将本塔标题改名为“1层小塔”,游戏的唯一标识符叫onefloor,然后可以直接修改勇士的各项初始数据. -![初始数据](./img/dataInit.png) -!> 请注意,勇士的初始位置一栏,x为横坐标,y为纵坐标;即,x为从左到右第几列,y为从上到下第几行,均从0开始计算。 +我们可以将本塔标题改名为“1层小塔”, + +游戏的唯一标识符叫onefloor,然后可以直接修改勇士的各项初始数据. + +!> **注:name作为游戏的唯一标识符必须进行修改,否则可能会导致存档等出现问题。** + +![初始数据](./img/init.png) + +!> **请注意,勇士的初始位置一栏,x为横坐标,y为纵坐标;即,x为从左到右第几列,y为从上到下第几行,均从0开始计算。** 修改完初始化信息后,接下来我们需要修改道具的信息(比如宝石加攻防的数值,血瓶加生命的数值等)。还是在这个`data.js`文件,往下拉,找到values一项,并进行相应的设置 + ![修改数据](./img/modData.png) -然后,再设置一些系统Flag,以进行游戏。继续将`data.js`往下拉,我们注意到本塔是存在魔防的,不存在经验,因此我们可以简单地将enableMDef改为true,enableExperience改成false。 -![系统标志](./img/flag1.png) +然后,再设置一些系统Flag,以进行游戏。继续将`data.js`往下拉,我们注意到本塔是存在魔防的,不存在经验,因此我们可以简单地将enableMDef改为true,enableExperience改成false,enableDebuff改成false。 同理,本塔的破墙镐只能破面前的墙壁,因此`pickaxeFourDirections`需要改成`false`。 -![系统标志](./img/flag2.png) + +![系统标志](./img/flag.png) + 其他的几项暂时不会被涉及到,因此不用考虑。 全局变量修改完毕后,我们需要告诉主函数加载该楼层。打开`main.js`(该文件和index.html同级),找到`this.floorIds`项,将其值改为楼层ID即MT1。 + ![修改楼层数据](./img/floordata.png) 最后一步就是录入怪物数据。打开`enemys.js`文件,依次输入你在本塔内使用到的所有怪物的攻防血的数据。其中怪物的特殊属性(special项)与该文件下面的getSpecialText对应。 + ![怪物数据](./img/enemyarray.png) + 只需要修改自己用到的怪物属性即可,其他没有用到的怪物完全无所谓。 做完后保存所有文件,然后右键,选择使用chrome浏览器打开`index.html`,就能立刻看到自己的塔并开始游戏啦!是不是很简单呢! + ![保存](./img/save.png) ## 压缩与发布 @@ -107,8 +124,8 @@ - 手机端的部分浏览器如chrome也支持本地网页,可以下载到手机然后直接打开进行游戏。 - 在线版本:将游戏放到某个服务器上,大家在线联网游戏。 -离线版本的好处是:先全部下载后再游戏,无需考虑流量的问题,也可以支持高清音乐的播放。坏处是:没办法在多平台之间迁移(平台同步的锅),而浏览器打开本地文件有丢失存档的风险。 -在线版本的好处是:随时随地可以玩,可以多平台接档,还可以在后台看到一些统计信息,了解大概有多少人进行了游戏(如果需要);坏处是需要一个服务器,且还要考虑到用户流量的问题。 +**离线版本的好处是:先全部下载后再游戏,无需考虑流量的问题,也可以支持高清音乐的播放。坏处是:没办法在多平台之间迁移,无法及时获得游戏更新(需要重新下载),而浏览器打开本地文件有丢失存档的风险。 +在线版本的好处是:随时随地可以玩,可以多平台接档,还可以在后台看到一些统计信息,可以随时对游戏进行更新;坏处是需要一个服务器,且还要考虑到用户流量的问题。** 在此我们只讨论在线版本。当你决定发布游戏时,强烈建议先将JS代码进行压缩以节省可能的IO请求以及网络流量。直接打开同目录下的“JS代码压缩工具”进行压缩即可。 @@ -118,4 +135,32 @@ 然后就是发布帖子、链接二维码,能让任何人在任何时候任何平台上都能进行游戏啦!是不是很简单呢! -在下面的几章里,将对样板的各个元件、事件等依次进行介绍。 +## 注意事项和常见FAQ + +1. 截图请务必刚好截取13x13的图片,并需要保证每个位置必须为32x32像素。一般无放缩的RMXP符合条件。 +2. 游戏的唯一标识符name请务必修改。如果不修改可能会导致存档出现异常。 +3. 别忘了main.js中要修改floorIds指明所用到的所有楼层哦~ + +下面是几个常见的FAQ: + +**Q: 为什么截图识别不出来?** + +**A:** 请保证刚好为13x13,且每个位置必须32x32像素。如果不确定,可以保存你的截图,右键属性查看详细信息,看像素的宽高是不是在416左右。多少几十像素都是没关系的。 + +![图片大小](./img/imginfo.png) + +**Q: 打开游戏时卡死在了xxx.js加载完毕!无法进入游戏。** + +**A:** 最大的可能是因为少了逗号,或者反括号等等。一般而言VSCode都会有错误提示,你哪里少了东西。 + +如果没有,可以采用如下方式debug: + +Ctrl+Shift+I 打开Chrome的控制台,找到Console。 + +如果出现了语法错误,会有红色提示 **Unexpected xxx** ,找到后面文件名和行号,打开,使用VSCode检查该处是否存在问题,即可。 + +![检查错误](./img/chrome.png) + +========================================================================================== + +[继续阅读下一章:元件说明](./element) \ No newline at end of file diff --git a/fonts/number.ttf b/fonts/number.ttf deleted file mode 100644 index bcf90fae..00000000 Binary files a/fonts/number.ttf and /dev/null differ diff --git a/styles.css b/styles.css index 1b2dd7a2..c63e5e92 100644 --- a/styles.css +++ b/styles.css @@ -1,4 +1,4 @@ -html, body { +html, body { margin: 0; padding: 0; width: 100%; @@ -6,12 +6,7 @@ html, body { background-color: #000; overflow: hidden; } -/* -@font-face { - font-family: number; - src: url("fonts/number.ttf"); -} -*/ + #gameGroup { position: absolute; box-sizing: border-box; diff --git a/tools/JS代码压缩工具.exe b/tools/JS代码压缩工具.exe deleted file mode 100644 index 38eaaf25..00000000 Binary files a/tools/JS代码压缩工具.exe and /dev/null differ diff --git a/tools/地图生成器.exe b/tools/地图生成器.exe deleted file mode 100644 index 02cdb624..00000000 Binary files a/tools/地图生成器.exe and /dev/null differ diff --git a/tools/EcmaScript.NET.dll b/常用工具/EcmaScript.NET.dll similarity index 100% rename from tools/EcmaScript.NET.dll rename to 常用工具/EcmaScript.NET.dll diff --git a/常用工具/JS代码压缩工具.exe b/常用工具/JS代码压缩工具.exe new file mode 100644 index 00000000..a7a7f9ef Binary files /dev/null and b/常用工具/JS代码压缩工具.exe differ diff --git a/tools/Yahoo.Yui.Compressor.dll b/常用工具/Yahoo.Yui.Compressor.dll similarity index 100% rename from tools/Yahoo.Yui.Compressor.dll rename to 常用工具/Yahoo.Yui.Compressor.dll diff --git a/tools/伤害和临界值计算器.exe b/常用工具/伤害和临界值计算器.exe similarity index 100% rename from tools/伤害和临界值计算器.exe rename to 常用工具/伤害和临界值计算器.exe diff --git a/常用工具/地图生成器.exe b/常用工具/地图生成器.exe new file mode 100644 index 00000000..67eda508 Binary files /dev/null and b/常用工具/地图生成器.exe differ diff --git a/更新说明.txt b/更新说明.txt deleted file mode 100644 index 0f93d8dd..00000000 --- a/更新说明.txt +++ /dev/null @@ -1,10 +0,0 @@ -更新说明: -1. 新增:战斗过程显示!可在全局Flag中开关,也可针对某一怪物单独设置。 -2. 新增:勇士支持48*32(大图)的行走图。 √ -3. 新增:更改画面色调 √ -4. 新增:文字显示支持自动换行。 √ -5. 部分修改状态栏UI。 √ -6. 移除doc和pdf文档,现在请参考Web端,有很好的索引。 -7. 移除不再支持的全局变量中的bombTrigger选项 √ -8. 修复键盘开门导致openDoor重复触发的Bug。 √ -9. 更新快捷商店(文字显示&禁用) √