Merge remote-tracking branch 'refs/remotes/ckcz123/v2.0' into refactoring-editor

This commit is contained in:
YouWei Zhao 2019-04-05 20:25:57 -04:00
commit 28c678d1c4
29 changed files with 4064 additions and 1884 deletions

689
_docs/_api.md Normal file
View File

@ -0,0 +1,689 @@
# 附录: API列表
?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} *
**这里只列出所有可能会被造塔者用到的常用API更多的有关内容请在代码内进行查询。**
如有任何疑问,请联系小艾寻求帮助。
可以在chrome浏览器的控制台中`ctrl+shift+I`找到Console中直接进行调用以查看效果。
**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。**
!> 最常用的新手向命令,强烈建议每个人了解
``` text
core.status.floorId
获得当前层的floorId。
core.status.maps
获得所有楼层的地图信息。
core.status.thisMap
获得当前楼层信息其等价于core.status.maps[core.status.floorId]。
core.floors
获得所有楼层的信息。例如core.floors[core.status.floorId].events可获得本楼层的所有自定义事件。
core.status.hero
获得当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值。
core.material.enemys
获得所有怪物信息。例如core.material.enemys.greenSlime就是获得绿色史莱姆的属性数据。
core.material.items
获得所有道具的信息。
core.debug()
开启调试模式。此模式下可以按Ctrl键进行穿墙并忽略一切事件。
此模式下不可回放录像和上传成绩。
core.updateStatusBar()
立刻刷新状态栏和地图显伤。
core.setStatus('atk', 1000)
将攻击力设置为1000这里把atk可以改成hp, def, mdef, money, experience等等。
本句等价于 core.status.hero.atk = 1000
core.getStatus('atk')
返回当前攻击力数值。本句等价于 core.status.hero.atk。
core.setHeroLoc('x', 5)
设置勇士位置。这句话的意思是将勇士当前位置的横坐标设置为5。
同理可以设置勇士纵坐标 core.setHeroLoc('y', 3)。
值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;如需立刻重新绘制地图还需调用:
core.clearMap('hero'); core.drawHero();
来对界面进行更新。
core.setItem('pickaxe', 10)
将破墙镐个数设置为10个。这里可以写任何道具的ID。
core.addItem('pickaxe', 2)
将破墙镐的个数增加2个无任何特效。这里可以写任何道具的ID。
core.getItem('pickaxe', 4)
令勇士获得4个破墙镐。这里可以写任何道具的ID。
和addItem相比使用getItem会播放获得道具的音效也会在左上角绘制获得提示。
core.removeItem('pickaxe', 3)
删除3个破墙镐。第二项可忽略默认值为1。
core.itemCount('pickaxe')
返回当前破墙镐的个数。这里可以写任何道具的ID。
core.hasItem('pickaxe')
返回当前是否存在某个道具。等价于 core.itemCount('pickaxe')>0 。
core.getEquip(0)
获得0号装备类型武器的当前装备的itemId。如果不存在则返回null。
这里可以写任意装备类型从0开始和全塔属性中的equipName一一对应。
core.hasEquip('sword1')
获得当前某个具体的装备是否处于正在被装备状态。
core.setFlag('xyz', 2)
设置某个flag/变量的值为2。这里可以写任何的flag变量名。
core.getFlag('xyz', 7)
获得某个flag/变量的值;如果该变量不存在,则返回第二个参数。
比如 core.getFlag('point', 2) 则获得变量point的值如果该变量从未定义过则返回2。
core.hasFlag('xyz')
返回是否存在某个变量且不为0。等价于 core.getFlag('xyz', 0)!=0 。
core.removeFlag('xyz')
删除某个flag/变量。
core.insertAction(list, x, y, callback)
插入并执行一段自定义事件。在这里你可以写任意的自定义事件列表,有关详细写法请参见文档-事件。
x和y如果设置则覆盖"当前事件点"的坐标callback如果设置则覆盖事件执行完毕后的回调函数。
例如: core.insertAction(["楼层切换", {"type":"changeFloor", "floorId": "MT3"}])
将依次显示剧情文本,并执行一个楼层切换的自定义事件。
--------
从V2.5.4开始提出了“公共事件”的说法,这里也可以插入一个公共事件名。
例如core.insertAction("毒衰咒处理") 将插入公共事件“毒衰咒处理”。
core.changeFloor(floorId, stair, heroLoc, time, callback) [异步]
立刻切换到指定楼层。
floorId为目标楼层IDstair为到达的目标楼梯heroLoc为到达的指定点time为动画时间callback为切换完毕后的回调。
例如:
core.changeFloor('MT2', 'upFloor', null, 600) 切换到MT2层的上楼点动画事件600ms
core.changeFloor('MT5', null, {'x': 3, 'y': 6}, 0) 无动画切换到MT5层的(3,6)位置。
core.resetMap()
重置当前楼层地图和楼层属性。
此函数参数有三种形式:
- 不加任何参数表示重置当前层core.resetMap()
- 加上一个floorId表示重置某一层core.resetMap("MT1")
- 使用一个数组表示重置若干层core.resetMap(["MT1", "MT2", "MT3"])
---------------------------
** 说明从V2.5.5开始存档方式发生了改变,在编辑器修改了地图后现在将直接生效,无需再重置地图。
R
录像回放的快捷键;这不是一个控制台命令,但是也把它放在这里供使用。
录像回放在修改地图或新增数据后会很有用。
```
!> 一些相对高级的命令,针对有一定脚本经验的人
``` text
========== 可直接从core中调用的最常被使用的函数 ==========
core.js实际上是所有API的入口路由核心API的实现在其他几个文件中core.js主要进行转发操作。
core.nextX(n)
获得勇士面向的第n个位置的x坐标n可以省略默认为1即正前方
core.nextY(n)
获得勇士面向的第n个位置的y坐标n可以省略默认为1即正前方
core.nearHero(x, y)
判断某个点是否和勇士的距离不超过1。
core.openDoor(id, x, y, needKey, callback) [异步]
尝试开门操作。id为目标点的IDx和y为坐标needKey表示是否需要使用钥匙callback为开门完毕后的回调函数。
id可为null代表使用地图上的值。
例如core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")})
此函数返回true代表成功开门并将执行callback回调返回false代表无法开门且不会执行回调函数。
core.battle(id, x, y, force, callback) [异步]
执行战斗事件。id为怪物的idx和y为坐标force为bool值表示是否是强制战斗callback为战斗完毕后的回调函数。
id可为null代表使用地图上的值。
例如core.battle('greenSlime', null, null, true)
core.trigger(x, y) [异步]
触发某个地点的事件。
core.isReplaying()
当前是否正在录像播放中
core.drawBlock(block)
重绘某个图块。block应为core.status.thisMap.blocks中的一项。
core.drawMap(floorId, callback)
重绘某一层的地图。floorId为要绘制楼层的floorIdcallback为绘制完毕后的回调函数。
core.terrainExists(x, y, id, floorId)
检测某个点是否存在(指定的)地形。
x和y为坐标id为地形ID可为null表示任意地形floorId为楼层ID可忽略表示当前楼层。
core.enemyExists(x, y, id, floorId)
检测某个点是否存在(指定的)怪物。
x和y为坐标id为怪物ID可为null表示任意怪物floorId为楼层ID可忽略表示当前楼层。
core.getBlock(x, y, floorId, showDisable)
获得某个点的当前图块信息。
x和y为坐标floorId为楼层ID可忽略或null表示当前楼层。
showDisable如果为true则对于禁用的点和事件也会进行返回。
如果该点不存在图块则返回null。
否则,返回值如下: {"index": xxx, "block": xxx}
其中index为该点在该楼层blocks数组中的索引block为该图块实际内容。
core.getBlockId(x, y, floorId, showDisable)
获得某个点的图块ID。
x和y为坐标floorId为楼层ID可忽略或null表示当前楼层。
showDisable如果为true则对于禁用的点和事件也会进行返回。
如果该点不存在图块则返回null否则返回该点的图块ID。
core.getBlockCls(x, y, floorId, showDisable)
获得某个点的图块cls。
x和y为坐标floorId为楼层ID可忽略或null表示当前楼层。
showDisable如果为true则对于禁用的点和事件也会进行返回。
如果该点不存在图块则返回null否则返回该点的图块cls。
core.showBlock(x, y, floorId)
将某个点从禁用变成启用状态。
core.hideBlock(x, y, floorId)
将某个点从启用变成禁用状态,但不会对其进行删除。
此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。
core.removeBlock(x, y, floorId)
将从启用变成禁用状态,并尽可能将其从地图上删除。
和hideBlock相比如果该点不存在自定义事件比如门或普通的怪物则将直接从地图中删除。
如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。
core.setBlock(number, x, y, floorId)
改变图块。number为要改变到的图块数字x和y为坐标floorId为楼层ID可忽略表示当前楼层。
core.useItem(itemId, noRoute, callback)
尝试使用某个道具。itemId为道具IDnoRoute如果为真则该道具的使用不计入录像。
callback为成功或失败后的回调。
core.canUseItem(itemId)
返回当前能否使用某个道具。
core.loadEquip(itemId, callback)
装备上某个装备。itemId为装备的IDcallback为成功或失败后的回调。
core.unloadEquip(equipType, callback)
卸下某个部位的装备。equipType为装备类型从0开始callback为成功或失败后的回调。
core.getNextItem()
轻按。
core.drawTip(text, itemIcon)
在左上角绘制一段提示信息2秒后消失。itemIcon为道具图标的索引。
core.drawText(contents, callback) [异步]
绘制一段文字。
不推荐使用此函数尽量使用core.insertAction(contents)来显示剧情文本。
core.closePanel()
结束一切事件和绘制关闭UI窗口返回游戏进程。
core.replaceText(text)
将一段文字中的${}进行计算并替换。
core.calValue(value, prefix, need, times)
计算表达式的实际值。这个函数可以传入status:atk等这样的参数。
core.getLocalStorage(key, defaultValue)
从localStorage中获得某个数据已被parse如果对应的key不存在则返回defaultValue。
core.getLocalForage(key, defaultValue, successCallback, errorCallback)
从localForage中获得某个数据已被parse如果对应的key不存在则返回defaultValue。
如果成功则通过successCallback回调失败则通过errorCallback回调。
core.hasSave(index)
判定当前某个存档位是否存在存档返回true/false。
index为存档编号0代表自动存档大于0则为正常的存档位。
core.clone(data)
深拷贝某个对象。
core.isset(x)
测试x是否不为null不为undefined也不为NaN。
core.rand(num)
使用伪种子生成伪随机数。该随机函数能被录像支持。
num如果设置大于0则生成一个[0, num-1]之间的数否则生成一个0到1之间的浮点数。
此函数为伪随机算法SL大法无效。即多次SL后调用的该函数返回的值都是相同的。
core.rand2(num)
使用系统的随机数算法得到的随机数。该随机函数能被录像支持。
num如果设置大于0则生成一个[0, num-1]之间的数否则生成一个0到2147483647之间的整数。
此函数使用了系统的Math.random()函数支持SL大法。
但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。
core.restart() [异步]
返回标题界面。
========== core.actions.XXX 和游戏控制相关的函数 ==========
actions.js主要用来进行用户交互行为的处理。
所有用户行为,比如按键、点击、滑动等等,都会被此文件接收并进行操作。
========== core.control.XXX 和游戏控制相关的函数 ==========
control.js主要用来进行游戏控制比如行走控制、自动寻路、存读档等等游戏核心内容。
core.control.setGameCanvasTranslate(canvasId, x, y)
设置大地图的偏移量
core.control.updateViewport()
更新大地图的可见区域
core.control.gatherFollowers()
立刻聚集所有的跟随者
core.control.replay()
回放下一个操作
========== core.enemys.XXX 和怪物相关的函数 ==========
enemys.js主要用来进行怪物相关的内容比如怪物的特殊属性伤害和临界计算等。
core.enemys.hasSpecial(special, test)
测试怪物是否含有某个特殊属性。
常见用法: core.enemys.hasSpecial(monster.special, 3) ## 测试是否拥有坚固
core.enemys.getSpecialText(enemyId)
返回一个列表包含该怪物ID对应的所有特殊属性。
core.enemys.getSpecialHint(enemy, special)
获得怪物某个(或全部)特殊属性的文字说明。
core.enemys.canBattle(enemyId, x, y, floorId)
返回当前能否战胜某个怪物。
后面三个参数是怪物坐标和楼层。
core.enemys.getDamage(enemyId, x, y, floorId)
返回当前对某个怪物的战斗伤害。如果无法战斗返回null。
后面三个参数是怪物坐标和楼层。
core.enemys.getExtraDamage(enemyId)
返回某个怪物会对勇士造成的额外伤害(不可被魔防抵消),例如仇恨、固伤等等。
core.enemys.nextCriticals(enemyId, number, x, y, floorId)
返回一个列表为接下来number可忽略默认为1个该怪物的临界值和临界减伤。
列表每一项类似 [x,y] 表示临界值为x且临界减伤为y。
如果无临界值,则返回空列表。
core.enemys.getDefDamage(enemyId, k, x, y, floorId)
获得k可忽略默认为1防减伤值。
core.enemys.getDamageInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId)
获得实际战斗信息,比如伤害,回合数,每回合伤害等等。
此函数是实际战斗过程的计算。
core.enemys.calDamage(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId)
获得在某个勇士属性下怪物伤害实际返回的是上面getDamageInfo中伤害的数值。
core.enemys.getCurrentEnemys(floorId)
获得某一层楼剩余所有怪物的信息(供怪物手册使用)
========== core.events.XXX 和事件相关的函数 ==========
events.js主要用来进行事件处理比如自定义事件以及某些条件下可能会被触发的事件。
大多数事件API都在脚本编辑中存在这里只列出部分比较重要的脚本编辑中不存在的API。
core.events.gameOver(ending, fromReplay)
游戏结束并上传的事件。
该函数将提问是否上传和是否下载录像,并返回标题界面。
core.events.doEvents(list, x, y, callback) [异步]
开始执行某个事件。
请不要执行此函数,尽量使用 core.insertAction(list, x, y, callback) 来开始执行一段事件。
core.events.doAction()
执行下一个事件。此函数中将对所有自定义事件类型分别处理。
core.events.getCommonEvent(name)
根据名称获得一个公共事件如果不存在对应的公共事件则返回null。
core.events.openShop(shopId, needVisited) [异步]
打开一个全局商店。needVisited表示是否需要该商店已被打开过。
core.events.disableQuickShop(shopId)
禁用一个全局商店
core.events.canUseQuickShop(shopId)
当前能否使用某个快捷商店
core.events.setHeroIcon(name)
设置勇士行走图
========== core.items.XXX 和道具相关的函数 ==========
items.js将处理和道具相关的内容比如道具的使用获取和删除等等。
core.items.compareEquipment(equipId1, equipId2)
比较两个装备的属性变化值
========== core.loader.XXX 和游戏加载相关的函数 ==========
loader.js将主要用来进行资源的加载比如加载音乐、图片、动画等等。
========== core.maps.XXX 和地图处理相关的函数 ==========
maps.js主要用来进行地图相关的的操作。包括绘制地图获取地图上的点等等。
core.maps.getNumberById(id)
根据ID来获得对应的数字。如果该ID不存在对应的数字则返回0。
core.maps.canMoveHero(x,y,direction,floorId)
判断能否前往某个方向。x,y为坐标可忽略为当前点direction为方向可忽略为当前方向。
floorId为楼层ID可忽略为当前楼层。
core.maps.canMoveDirectly(destX, destY)
判断当前能否瞬间移动到某个点。
该函数如果返回0则不可瞬间移动大于0则可以瞬间移动且返回值是跨度即少走的步数
core.maps.removeBlockById(index, floorId)
根据索引删除或禁用某块。
core.maps.removeBlockByIds(floorId, ids)
根据索引删除或禁用若干块。
core.maps.drawAnimate(name, x, y, callback)
播放一段动画name为动画名需在全塔属性注册x和y为坐标0-12之间callback可选为播放完毕的回调函数。
播放过程是异步的如需等待播放完毕请使用insertAction插入一条type:waitAsync事件。
此函数将随机返回一个数字id为此异步动画的唯一标识符。
core.maps.stopAnimate(id, doCallback)
立刻停止一个异步动画。
id为该动画的唯一标识符由drawAnimate函数返回doCallback可选若为true则会执行该动画所绑定的回调函数。
========== core.ui.XXX 和对话框绘制相关的函数 ==========
ui.js主要用来进行UI窗口的绘制比如对话框、怪物手册、楼传器、存读档界面等等。
core.ui.getContextByName(canvas)
根据画布名找到一个画布的context支持系统画布和自定义画布。如果不存在画布返回null。
也可以传画布的context自身则返回自己。
core.clearMap(name)
清空某个画布图层。
name为画布名可以是系统画布之一也可以是任意自定义动态创建的画布名还可以直接传画布的context本身。下同
如果name也可以是'all'若为all则为清空所有系统画布。
core.ui.fillText(name, text, x, y, style, font)
在某个画布上绘制一段文字。
text为要绘制的文本x,y为要绘制的坐标style可选为绘制的样式font可选为绘制的字体。下同
core.ui.fillBoldText(name, text, x, y, style, font)
在某个画布上绘制一个描黑边的文字。
core.ui.fillRect(name, x, y, width, height, style)
绘制一个矩形。style可选为绘制样式。
core.ui.strokeRect(name, x, y, width, height, style)
绘制一个矩形的边框。
core.ui.drawLine(name, x1, y1, x2, y2, style, lineWidth)
绘制一条线。lineWidth可选为线宽。
core.ui.drawArrow(name, x1, y1, x2, y2, style, lineWidth)
绘制一个箭头。
core.ui.setFont(name, font) / core.ui.setLineWidth(name, lineWidth)
设置一个画布的字体/线宽。
core.ui.setAlpha(name, font) / core.ui.setOpacity(name, font)
设置一个画布的绘制不透明度和画布本身的不透明度。
两者区别如下:
- setAlpha是设置"接下来绘制的内容的不透明度"不会对已经绘制的内容产生影响。比如setAlpha('ui', 0.5)则会在接下来的绘制中使用0.5的不透明度。
- setOpacity是设置"画布本身的不透明度"已经绘制的内容也会产生影响。比如我已经在UI层绘制了一段文字再setOpacity则也会看起来变得透明。
尽量不要对系统画布使用setOpacity因为会对已经绘制的内容产生影响自定义创建的画布则不受此限制。
core.ui.setFillStyle(name, style) / core.ui.setStrokeStyle(name, style)
设置一个画布的填充样式/描边样式。
core.ui.setTextAlign(name, align)
设置一个画布的文字对齐模式。
core.ui.calWidth(name, text, font)
计算一段文字在画布上的绘制宽度
font可选如果存在则会先设置该画布上的字体。
core.ui.drawImage(name, image, x, y, w, h, x1, y1, w1, h1)
在一个画布上绘制图片。
name为画布名可以是系统画布之一也可以是任意自定义动态创建的画布名还可以直接传画布的context本身。
image为要绘制的图片可以是一个全塔属性中定义的图片名会从images中去获取图片本身或者一个画布。
后面的8个坐标参数与canvas的drawImage的八个参数完全相同。
请查看 http://www.w3school.com.cn/html5/canvas_drawimage.asp 了解更多。
core.ui.createCanvas(name, x, y, width, height, zIndex)
动态创建一个画布。name为要创建的画布名如果已存在则会直接取用当前存在的。
x,y为创建的画布相对窗口左上角的像素坐标width,height为创建的长宽。
zIndex为创建的纵向高度关系到画布之间的覆盖z值高的将覆盖z值低的系统画布的z值可在个性化中查看。
返回创建的画布的context也可以通过core.dymCanvas[name]调用。
core.ui.relocateCanvas(name, x, y)
重新定位一个自定义画布。
core.ui.resizeCanvas(name, x, y)
重新设置一个自定义画布的大小。
core.ui.deleteCanvas(name)
删除一个自定义画布。
core.ui.deleteAllCanvas()
清空所有的自定义画布。
core.ui.drawThumbnail(floorId, canvas, blocks, x, y, size, heroLoc, heroIcon)
绘制一个缩略图,比如楼传器界面,存读档界面等情况。
floorId为目标楼层IDcanvas为要绘制到的图层blocks为要绘制的所有图块。
x,y为该图层开始绘制的起始点坐标size为每一格的像素heroLoc为勇士坐标heroIcon为勇士图标。
========== core.utils.XXX 工具类的辅助函数 ==========
utils.js主要用来进行一些辅助函数的计算。
core.utils.splitLines(canvas, text, maxLength, font)
自动切分长文本的换行。
canvas为图层text为要自动换行的内容maxLength为每行最长像素font为文本的字体。
core.utils.cropImage(image, size)
纵向对图片进行切分(裁剪)。
core.utils.push(a,b)
向某个数组后插入另一个数组或元素
core.utils.unshift(a, b)
向某个数组前插入另一个数组或元素
core.utils.encodeBase64(str)
Base64加密字符串
core.utils.decodeBase64(str)
Base64解密字符串
core.utils.formatBigNumber(x, onMap)
大数据的格式化
core.utils.subarray(a, b)
检查b是否是a的从头开始子串。
如果是则返回a删去b的一段否则返回null。
core.utils.same(a, b)
比较a和b两个对象是否相同
core.utils.clamp(x, a, b)
将x限制在[a,b]之间的范围内
core.utils.arrayToRGB(color)
将形如[255,0,0]之类的数组转成#FF0000这样的RGB形式。
core.utils.arrayToRGBA(color)
将形如[255,0,0,1]之类的数组转成rgba(255,0,0,1)这样的RGBA形式。
core.utils.encodeRoute(list)
压缩加密路线。可以使用core.encodeRoute(core.status.route)来压缩当前路线。
core.utils.decodeRoute(route)
解压缩(解密)路线。
core.utils.readFile(success, error, readType) [异步]
尝试请求读取一个本地文件内容。
success和error为成功/失败后的回调readType不设置则以文本读取否则以DataUrl形式读取。
core.utils.readFileContent(content) [异步]
文件读取完毕后的内容处理。
core.utils.download(filename, content)
尝试生成并下载一个文件。
core.utils.copy(data)
尝试复制一段文本到剪切板。
core.utils.http(type, url, formData, success, error) [异步]
发送一个异步HTTP请求。
type为'GET'或者'POST'url为目标地址formData如果是POST请求则为表单数据。
success为成功后的回调error为失败后的回调。
```

View File

@ -3,4 +3,5 @@
- [元件说明](element)
- [事件](event)
- [个性化](personalization)
- [脚本](script)
- [附录API列表](api)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
_docs/img/console.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
_docs/img/console1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
_docs/img/elements.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
_docs/img/plugin.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
_docs/img/sources.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -1,6 +1,6 @@
# 个性化
?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} *
?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} *
有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。
@ -21,10 +21,14 @@ HTML5魔塔是使用画布canvas来绘制存在若干个图层它们
- route**[D]**:路线层;主要用来绘制勇士的行走路线图。 (z-index: 95)
- paint**[D]**绘图层主要用来进行绘图模式。z-index: 95)
- curtain色调层用来控制当前楼层的画面色调 (z-index: 125)
- image1\~50**[D]**图片层用来绘制图片等操作。z-index: 100+code, 101~150也就是图片编号在1~25的在色调层之下26~50的在色调层之上
- uiUI层用来绘制一切UI窗口如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 160)
- image1\~50**[D]**图片层用来绘制图片等操作。z-index: 100+code, 101~150
- uiUI层用来绘制一切UI窗口如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140)
- data数据层用来绘制一些顶层的或更新比较快的数据如左上角的提示战斗界面中数据的变化等等。 (z-index: 170)
请注意显示图片事件将自动创建一个图片层z-index是100+图片编号。
色调层的z-index是25ui层的z-index是140因此图片编号在1~24的将被色调层遮挡25~40的将被ui层遮挡41~50的将遮挡UI层。
### 动态创建canvas
从V2.5.3开始可以在H5样板中任意动态创建canvas并进行使用。
@ -104,11 +108,11 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana');
从V2.5.4开始,贴图也允许进行帧动画,只要设置第五项的数值。
``` js
"images": [[96,120,"bg.jpg",0]], // 背景图;你可以选择一张或多张图片来作为背景/前景素材。
"images": [], // 无任何背景图
"images": [[32,32,"house.png",0], [160,170,"bed.png",1]] // 在(32,32)放一个house.png在背景层且(160,170)放bed.png在前景层
"images": [[96,120,"tree.png",2]] // 如果写2则会自动调节遮挡效果
"images": [[64,0,"x.png",1,4]] // 这是一个前景层的4帧动画贴图
[[96,120,"bg.jpg",0]] // 背景图;你可以选择一张或多张图片来作为背景/前景素材。
[] // 无任何背景图
[[32,32,"house.png",0], [160,170,"bed.png",1]] // 在(32,32)放一个house.png在背景层且(160,170)放bed.png在前景层
[[96,120,"tree.png",2]] // 如果写2则会自动调节遮挡效果
[[64,0,"x.png",1,4]] // 这是一个前景层的4帧动画贴图
```
images为一个数组代表当前层所有作为背景素材的图片信息。每一项为一个五元组分别为该背景素材的xy图片名遮挡方式和帧数。
@ -129,18 +133,21 @@ images为一个数组代表当前层所有作为背景素材的图片信息
关于楼层贴图和前景、背景层的层叠覆盖关系,默认是:**地板 - 背景贴图 - 背景图块 - 事件 - 勇士 - 前景贴图 - 前景图块**。
可以通过修改`libs/maps.js`的`drawMap`函数中下面三行的顺序来改变其覆盖关系。
可以通过修改`libs/maps.js`的`drawBg`和`drawFg`函数来改变其覆盖关系。
``` js
// ----- 可以调整这三行的顺序来修改覆盖关系;同层画布上,后绘制的覆盖先绘制的
// ----- ui.js的drawThumbnail函数也需要对应进行修改。
// 绘制楼层贴图
core.maps.drawFloorImages(floorId, images);
// 绘制背景层图块
core.maps.drawBgFgMap(floorId, core.canvas.bg, "bg", true);
// 绘制前景层图块
core.maps.drawBgFgMap(floorId, core.canvas.fg, "fg", true);
////// 绘制背景层 //////
maps.prototype.drawBg = function (floorId, ctx) {
var onMap = ctx == null;
if (onMap) {
ctx = core.canvas.bg;
core.clearMap(ctx);
}
this._drawBg_drawBackground(floorId, ctx);
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。
this._drawFloorImages(floorId, ctx, 'bg');
this._drawBgFgMap(floorId, ctx, 'bg', onMap);
}
```
楼层贴图可以被事件隐藏和显示,详见[隐藏贴图](event#hideFloorImg隐藏贴图)的写法。
@ -192,7 +199,7 @@ ID必须由数字字母下划线组成数字在1000以内且均不能和
之后刷新编辑器即可。
对于怪物和道具,我们也可以进行自动注册只需要点击“自动注册”按钮将对该栏下所有未注册的素材进行自动注册自动分配ID和数字
我们也可以进行自动注册只需要点击“自动注册”按钮将对该栏下所有未注册的素材进行自动注册自动分配ID和数字
素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。
@ -206,85 +213,6 @@ ID必须由数字字母下划线组成数字在1000以内且均不能和
2. 下拉框选择autotile然后点“追加”
3. 看到成功的提示后刷新编辑器即可。
<!--
1. 将新的Autotile图片复制到images目录下。文件名必须是字母数字和下划线组成。
2. 进入icons.js在autotile分类下进行添加该文件的名称索引简单的写0。
3. 指定一个数字在maps.js中类似进行添加。
!> Autotile的ID和文件名应确保完全相同
-->
<!--
#### 新添加自定义地形(路面、墙壁等)
如果你在terrains.png中新增了一行
1. 指定一个唯一的英文ID不能和现有的重复。
2. 进入icons.js在terrains分类下进行添加索引对应图标在图片上的位置即index
**如果你无须在游戏内使用本地形,而仅仅是将其作为“背景图”使用,则操作如下:**
3. 修改对应楼层的剧本文件的`defaultGround`项改成新的ID。
**如果你要在游戏内使用本地形,则操作如下:**
3. 指定一个数字在maps.js中类似进行添加。
#### 新添加Autotile
如果你需要新增一个Autotile
1. 将新的Autotile图片复制到images目录下。
2. 进入icons.js在autotile分类下进行添加该文件的名称索引简单的写0。
3. 指定一个数字在maps.js中类似进行添加。
!> Autotile的ID和文件名完全相同且其ID/文件名不能含有中文、空格或特殊字符。
!> V2.0版本不能在地图编辑器中添加Autotile请按上面的操作来执行。
#### 新添加道具
如果你需要新增一个未被定义的道具:
1. 指定一个唯一的英文ID不能和现有的重复。
2. 进入icons.js在items分类下进行添加索引对应图标在图片上的位置即index
3. 指定一个数字在maps.js中类似进行添加。
4. 在items.js中仿照其他道具来添加道具的信息。
有关如何自行实现一个道具的效果,参见[自定义道具效果](#自定义道具效果)。
#### 新添加怪物
如果我们需要新添加怪物请在enemys.png中新增一行。
你可以通过便捷PS工具的“更改色相”来将红头怪变成橙头怪等。
然后执行如下操作:
1. 指定一个唯一的英文ID不能和enemys中现有的重复。
2. 进入icons.js在enemys分类下进行添加索引对应图标在图片上的位置即index
3. 在maps.js中继续进行添加。
4. 在enemys.js中仿照其他怪物来添加怪物的信息。
!> 如果是48x32的怪物素材请放在enemy48.png中然后在icons.js的enemy48下添加索引。
有关如何自行实现一个怪物的特殊属性或伤害计算公式,参见[怪物的特殊属性](#怪物的特殊属性)。
#### 新添加NPC
1. 指定一个唯一的英文ID不能和现有的重复。
2. 进入icons.js在npcs分类下进行添加索引对应图标在图片上的位置即index
3. 指定一个数字在maps.js的getBlock下类似进行添加。
!> 如果是48x32的怪物素材请放在npc48.png中然后在icons.js的npc48下添加索引。
-->
### 地图生成器使用自定义素材
地图生成器是直接从js文件中读取数字-图标对应关系的。
因此在你修改了icons.js和maps.js两个文件也就是将素材添加到游戏后地图生成器的对应关系也将同步更新。
### 额外素材
从V2.4.2开始HTML5魔塔样板开始支持额外素材。
@ -300,6 +228,7 @@ ID必须由数字字母下划线组成数字在1000以内且均不能和
**该素材的宽高必须都是32的倍数且图片上的总图块数不超过1000即最多有1000个32*32的图块在该图片上。**
```js
// 在全塔属性中的tilesets导入素材
"tilesets": ["1.png", "2.png"] // 导入两个额外素材文件名分别是1.png和2.png
```
@ -350,19 +279,6 @@ core.status.hero.atk += core.values.redJewel + 2*ratio
具体过程比较复杂需要一定的JS能力在这里就不多说了有需求可以找`艾之葵`进行了解。
但值得一提的是,我们可以使用`core.hasItem(name)` 来判断是否某个道具是否存在。例如下面是passNet通过路障处理的一部分
``` js
/****** 经过路障 ******/
events.prototype.passNet = function (data) {
// 有鞋子
if (core.hasItem('shoes')) return;
if (data.event.id=='lavaNet') { // 血网
// ... 下略
```
我们进行了一个简单的判断,如果拥有绿鞋,则不进行任何路障的处理。
### 实战!拿到神圣盾后免疫吸血、领域、夹击效果
1. 在itemEffect中修改拿到神圣盾时的效果标记一个自定义Flag。
@ -421,109 +337,20 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) {
对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。
要修改楼传事件,需要进行如下步:
要修改楼传事件,需要进行如下步:
1. 截获楼传的点击事件。在control.js中找到useFly函数并将其替换成如下内容
``` js
////// 点击楼层传送器时的打开操作 //////
control.prototype.useFly = function (need) {
if (core.isMoving()) {
core.drawTip("请先停止勇士行动");
return;
}
if (core.status.lockControl || core.status.event.id != null) return;
if (core.canUseItem('fly')) core.useItem('fly');
else core.drawTip("当前无法使用"+core.material.items.fly.name);
}
```
1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](script#重写点击楼传事件)。
2. 修改楼传的使用事件。和其他永久道具一样在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如
``` js
"useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本
"canUseItemEffect": "true" // 任何时候可用
```
修改时,请先把`null`改成空字符串`""`,然后再双击进行编辑。
<!--
## 自定义装备
由于HTML5魔塔并不像RM那样存在一个装备界面可供我们对装备进行调整但是我们也可以使用一个替代的方式实现这个目标。
### 装备的实现原理
在HTML5中装备将全部看成是永久道具constants同时对于每个装备位置勇士都定义一个flag域表示正在装备的内容。
例如:当勇士获得银剑时,将获得一个永久道具“银剑”;当使用这个银剑,首先检查勇士当前是否装备了武器(比如铁剑),如果
装备了则先脱掉装备(减去已装备的铁剑所加的属性,并获得永久道具铁剑),然后再穿上新的银剑。
同时由于脱剑术和脱盾术的存在我们还需要一个“脱掉装备”的永久道具比如sword0使用它可以脱掉对应位置的装备。
### 装备加值的修改
要启用装备首先需要在data.js全塔属性中设置`'equipment': true`。此时,游戏内将自动将剑盾变成装备的存在,其
所加的数值就是全塔属性中对应的数值比如铁剑sword1就加的全塔属性中sword1的值
有时候,我们还会有一个装备加多种属性的需求,此时需要将对应的项从数值改变成一个对象。
``` js
"sword1": {"atk": 10, "def": 0, "mdef": 5}, // 铁剑加10攻和5魔防
"shield1": {"atk": 0, "def": 10, "mdef": 10}, // 铁盾加10防和10魔防
```
通过这种方式,当穿上装备时,将会给你的三围分别加上对应项的数值(支持负数,比如装剑减防御)。
### 新增剑盾
样板默认提供了铁剑(盾),银剑(盾),骑士剑(盾),圣剑(盾)和神圣剑(盾)这五类装备。但有时候,五类是不够的,
我们可能还需要更多的剑盾。
要增加更多的剑盾,我们需要进行如下步骤:(以新增剑为例)
1. 新注册一个素材到游戏;**其ID必须是sword+数字的形式比如sword6等。**同理如果是盾就必须是shield+数字的形式比如shield6。
2. 将其cls设置为`constants``useItemEffect`和`canUseItem`直接复制其他几个剑盾的内容。
(即:`useItemEffect`必须为`"core.plugin.useEquipment(itemId)"``canUseItemEffect`必须为`"true"`)。
3. 使用VSCode或其他文本编辑器直接打开`data.js`文件,并在`"values"`中仿照已有的内容添加剑盾的属性。
(例如:`"sword6": 100`,或者`"sword6": `{"atk": 100, "def": 0, "mdef": 50}`
!> 请注意新的剑的ID必须是sword+数字新的盾的ID必须是shield+数字的形式,不然装备将无效!
### 新增其他部位的装备
如果我们还需要新增更多部位的装备,比如戒指、首饰等等,也是可以的,只需要依次进行如下步骤:(以新建戒指为例)
1. 选择一个装备的部位的ID比如假设我们的装备部位为戒指那么我们可以选择"ring"作为装备的部位。
2. 定义一个空戒指,相当于脱掉戒指。注册一个素材到游戏,**其ID必须是ring0**即部位ID+数字0
3. 同上述新建剑盾的方式来设置ring0这个空戒指的属性。请注意打开data.js后设置的其值必须为0。
(即:必须是`"ring0": 0`)
3. 创建更多的戒指每加一个新的戒指到游戏其ID必须是**ring+数字**的形式,比如`ring1`, `ring2`,等等。
4. 对于每一个创建的戒指按照上述新建剑盾的方式设置属性图块属性打开data.js直接编辑
(例如:`"ring2": {"atk": 3, "def": 5, "mdef": 8}`
5. **切换到脚本编辑 - 自定义插件编写,在`this.useEquipment`中仿照着新增一行代表戒指的使用。**
``` js
this.useEquipment = function (itemId) { // 使用装备
_useEquipment(itemId, "sword", "atk"); // 剑
_useEquipment(itemId, "shield", "def"); // 盾
_useEquipment(itemId, "ring", "mdef"); // 新增的戒指
}
```
我们仿照着新增一行`_useEquipment(itemId, "ring", "mdef")`。
其中第二项为装备部位的ID比如我们上述定义的是ring第三项表示“如果values中该值为数字则加到什么属性上”。
比如如果我们这里写mdef那么我们假设在data.js的values中设置`"ring1": 20`则使用该戒指会增加20点魔防。
如果你定义的是对象`{"atk": xxx, "def": xxx, "mdef": xxx}`的形式,则不会受到第三项的影响。
!> 必须对于每一个装备部位定义唯一一个不同的ID脱掉装备空装备必须是该ID加数字0的形式其他有效装备必须是
该ID+正整数的形式,不然会出错!
-->
除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyBoard`)即可。
## 自定义怪物属性
如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑-getSpecials。
如果你对现有的怪物不满意想自行添加怪物属性也是可以的。具体参见脚本编辑的getSpecials。
你需自己指定一个special数字修改属性名和属性提示文字。后两者可以直接写字符串或写个函数传入怪物。
@ -552,7 +379,8 @@ case 89: // 使用该按键的keyCode比如Y键就是89
// ... 在这里写你要执行脚本
// **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为**
if (core.hasItem('...')) {
core.useItem('...');
core.status.route.push("key:0"); // 记录按键到录像中
core.useItem('...', true); // 第二个参数true代表该次使用道具是被按键触发的使用过程不计入录像
}
break;
@ -560,6 +388,10 @@ case 89: // 使用该按键的keyCode比如Y键就是89
强烈建议所有新增的自定义快捷键均给个对应的永久道具可点击,以方便手机端的行为。
使用`core.status.route.push("key:"+keyCode)`可以将这次按键记录在录像中。
!> 如果记录了按键且使用道具的话需要将useItem的第二个参数设为true避免重复记录
可以使用altKey来判断Alt键是否被同时按下。
## 公共事件
@ -574,13 +406,9 @@ case 89: // 使用该按键的keyCode比如Y键就是89
## 插件系统
在H5中提供了“插件”系统。具体参见“脚本编辑 - 插件编写”
在H5中提供了“插件”系统。在V2.6中提供了一个插件下拉框,用户可以自行创建和写插件
![插件编写](./img/plugin.png)
当我们在这上面定义了自己需要的函数(插件后),就可以通过任何方式进行调用。
在这个插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。
在插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。
下面是一个很简单的例子我编写一个插件函数其效果是让勇士生命值变成原来的x倍并令面前的图块消失。
@ -599,11 +427,13 @@ this.myfunc = function(x) {
网站上也提供了一个[插件库](https://h5mota.com/plugins/),欢迎大家把自己写的插件进行共享。
从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。
详见[函数的转发](script#函数的转发)。
## 标题界面事件化
从V2.5.3开始,我们可以将标题界面的绘制和游戏开始用事件来完成。可以通过绘制画布、
全塔属性flags中的startUsingCanvas可以决定是否开启标题界面事件化。
从V2.5.3开始我们可以将标题界面的绘制和游戏开始用事件来完成。可以通过绘制画布、全塔属性flags中的startUsingCanvas可以决定是否开启标题界面事件化。
然后就可以使用“事件流”的形式来绘制标题界面、提供选项等等。
@ -619,36 +449,70 @@ this.myfunc = function(x) {
从V2.5.3以后,我们可以给手机端增加按键了,这样将非常有利于技能的释放。
用户在竖屏模式下点击工具栏,就会在工具栏按钮和快捷键模式之间进行切换。
用户在菜单栏打开“拓展键盘”后,在竖屏模式下点击工具栏,就会在工具栏按钮和快捷键模式之间进行切换。
切换到快捷键模式后可以点1-7分别等价于在电脑端按键1-7
切换到快捷键模式后可以点1-8分别等价于在电脑端按键1-8
可以在脚本编辑的onKeyUp中定义每个快捷键的使用效果比如使用道具或释放技能等。
默认值下1使用破2使用炸3使用飞4使用其他存在的道具5-7未定义。可以相应修改成自己的效果。
默认值下1使用破2使用炸3使用飞4使用其他存在的道具5-8未定义。可以相应修改成自己的效果。
也可以替换icons.png中的对应图标以及修改main.js中`main.statusBar.image.btn1~7`中的onclick事件来自定义按钮和对应按键。
也可以替换icons.png中的对应图标以及修改main.js中`main.statusBar.image.btn1~8`中的onclick事件来自定义按钮和对应按键。
非竖屏模式下、回放录像中、隐藏状态栏中,将不允许进行切换。
## 自定义状态栏(新增显示项)
## 自绘状态栏
从V2.5.3开始允许自绘状态栏。要自绘状态栏,则应该打开全塔属性中的`statusCanvas`开关。
自绘模式下,全塔属性中的`statusCanvasRowsOnMobile`将控制竖屏模式下的状态栏行数。
开启自绘模式后,可以在脚本编辑的`drawStatusBar`中自行进行绘制。
横屏模式下的状态栏为`129x416`15x15则是`149x480`);竖屏模式下的状态栏为`416*(32*rows+9)`15x15是480
具体可详见脚本编辑的`drawStatusBar`函数。
## 自定义状态栏的显示项
在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground等等。
但是,如果我们还想新增其他项目的显示,比如攻速或者暴击,该怎么办?
需要进行如下几个操作:
我们可以[自绘状态栏](#自绘状态栏),或者采用下面两个方式之一来新增。
### 利用已有项目
一个最为简单的方式是,直接利用已有项目。
例如,如果本塔中没有技能栏,则可以使用技能栏所对应的显示项。
1. 覆盖project/icons.png中技能的图标
2. 打开全塔属性的enableSkill开关
3. 在脚本编辑-updateStatusBar中可以直接替换技能栏的显示内容
```
// 设置技能栏
if (core.flags.enableSkill) {
// 替换成你想显示的内容比如你定义的一个flag:abc。
core.setStatusBarInnerHTML('skill', core.getFlag("abc", 0));
}
```
### 额外新增新项目
如果是在需要给状态栏新定义项目,则需要进行如下几个操作:
1. 定义ID比如攻速我就定义speed暴击可以简单的定义baoji你也可以定义其他的ID但是不能和已有的重复。这里以speed为例。
2. 在index.html的statusBar中44行起进行该状态栏项的定义。仿照其他几项插在其应当显示的位置注意替换掉相应的ID。
2. 在index.html的statusBar中46行起进行该状态栏项的定义。仿照其他几项插在其应当显示的位置注意替换掉相应的ID。
``` html
<div class="status" id="speedCol">
<img id="img-speed">
<p class='statusLabel' id='speed'></p>
</div>
```
3. 在editor.html中的statusBar323行起仿照第二点同样添加这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。
4. 使用便捷PS工具打开icons.png新增一行并将魔力的图标P上去记下其索引比如37从0开始数
3. 在editor.html中的statusBar383行起仿照第二点同样添加这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。
4. 使用便捷PS工具打开project/icons.png新增一行并将魔力的图标P上去记下其索引比如37从0开始数
5. 在main.js的this.statusBar中增加图片、图标和内容的定义。
``` js
this.statusBar = {
@ -674,17 +538,15 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0);
## 技能塔的支持
其实在HTML5上制作技能塔是完全可行的。
要支持技能塔,可能需要如下几个方面:
从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。
- 魔力(和上限)的添加;技能的定义
- 状态栏的显示
- 技能的触发(按键与录像问题)
- 技能的效果
从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。
### 魔力的定义添加;技能的定义
从V2.5开始提供了status:mana选项可以直接代表当前魔力值。
@ -697,6 +559,8 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0);
如果flag:skill不为0则代表当前处于某个技能开启状态且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。
!> 关于魔力上限样板中默认没有提供status:manamax
### 状态栏的显示
从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。
@ -706,9 +570,14 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0);
``` js
// 设置魔力值
if (core.flags.enableMana) {
// 也可以使用flag:manaMax来表示最大魔力值
// core.status.hero.mana = Math.max(core.status.hero.mana, core.getFlag('manaMax', 10));
// core.statusBar.mana.innerHTML = core.status.hero.mana + "/" + core.getFlag('manaMax', 10);
// status:manamax 只有在非负时才生效。
if (core.status.hero.manamax != null && core.status.hero.manamax >= 0) {
core.status.hero.mana = Math.min(core.status.hero.mana, core.status.hero.manamax);
core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.status.hero.manamax);
}
else {
core.setStatusBarInnerHTML("mana", core.status.hero.mana);
}
}
// 设置技能栏
if (core.flags.enableSkill) {
@ -761,14 +630,15 @@ else { // 关闭技能
case 87: // W开启技能“二倍斩”
// 检测技能栏是否开启,是否拥有“二倍斩”这个技能道具
if (core.flags.enableSkill && core.hasItem('skill1')) {
core.useItem('skill1');
core.status.route.push("key:87");
core.useItem('skill1', true);
}
break;
```
在勇士处于停止的条件下按下W键时判断技能的道具是否存在如果存在再使用它。
!> 123这三个键被默认绑定到了破炸飞如果想用的话也是一样只不过是把已有的实现进行替换
!> 由于现在手机端存在拓展键盘也强烈建议直接覆盖1-8的使用效果这样手机端使用也非常方便
### 技能的效果
@ -816,141 +686,6 @@ if (core.flags.enableSkill) {
通过上述这几种方式我们就能成功的让H5支持技能啦
## 成就系统
我们还可以给HTML5魔塔增加成就系统。注意到成就是和游戏相关因此需要使用getLocalStorage而不是getFlag判定。
可将下面的代码粘贴到脚本编辑 - 插件编写中。
``` js
// 所有成就项的定义
this.achievements = [
// 每行一个分别定义flag、名称、描述、是否存在提示、成就点数
{"flag": "a1", "name": "成就1", "text": "成就1的达成描述", "hint": false, "point": 1},
// 可以继续往后新增其他的。
];
// 达成成就;如 core.plugin.achieve("a1") 即达成a1对应的成就
this.achieve = function (flag) {
// 获得已达成的成就如果跟存档而不是跟游戏则改成getFlag
var achieved = core.getLocalStorage("achievements", []);
var point = core.getLocalStorage("achievePoint", 0);
// 已经获得该成就
if (achieved.indexOf(flag)>=0) return;
// 尝试达成成就;找到对应的成就项
this.achievements.forEach(function (one) {
if (one.flag == flag) {
// 执行达成成就的操作;也可以自行在上面加上达成成就后的事件
core.insertAction("\t[达成成就:"+one.name+"]"+one.text);
point += one.point || 0;
}
});
achieved.push(flag);
// 存入localStorage中如果跟存档走则使用setFlag
core.setLocalStorage("achievements", achieved);
core.setLocalStorage("achievePoint", point);
}
// 获得所有成就说明这里简单使用两个insertAction你也可以修改成自己的实现
// 简单一点的可以使用insertAction+剧情文本稍微复杂一点的可以使用图片化文本等更复杂的可以自绘UI。
this.getAchievements = function () {
var achieved = core.getLocalStorage("achievements", []);
var yes = [], no = [];
// 对所有成就进行遍历
this.achievements.forEach(function (one) {
// 检测是否达成
if (achieved.indexOf(one.flag)>=0) {
yes.push(one.name+""+one.text);
}
else {
no.push(one.name+""+(one.hint?one.text:"达成条件请自行探索"));
}
});
core.insertAction([
"\t[已达成的成就]"+(yes.length==0?"暂无":yes.join("\n")),
"\t[尚未达成的成就]"+(no.length==0?"暂无":no.join("\n"))
]);
}
```
## 多角色的支持
其实,我们的样板还能支持多角色的制作。比如《黑·白·间》之类的塔也是完全可以刻的。
你只需要如下几步来达到多角色的效果。
1. 每个角色弄一张行走图。相关信息参见[自定义事件setHeroIcon](event#setHeroIcon更改角色行走图)。
2. [覆盖楼传事件](#覆盖楼传事件),这样可以通过点工具栏的楼层传送按钮来切换角色。当然你也完全可以自己写一个道具,或[自定义快捷键](#自定义快捷键)来进行绑定。
3. 将下述代码直接贴入脚本编辑 - 插件编写中。
``` js
// 所有需要保存的内容;这些保存的内容不会多角色共用,在切换时会进行恢复。
// 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化,
// 多角色共用hp的话则删除hp等等。总之不共用的属性都在这里进行定义就好。
var hero1 = { // 1号勇士默认的是0号
"floorId": "MT0", // 该角色楼层ID
"icon": "hero1.png", // 角色的行走图名称
"name": "1号角色",
"lv": 1,
"hp": 1000,
"atk": 10,
"def": 10,
"mdef": 0,
"loc": {"x": 0, "y": 0, "direction": "up"},
// 如果道具不共用就将下面这句话取消注释
// "items": {"keys":{"yellowKey":0,"blueKey":0,"redKey":0},"tools":{},"constants":{}}
}
// 也可以类似新增其他勇士
// var hero2 = { ...
var heroCount = 2; // 包含默认的在内总共多少个勇士,该值需手动修改。
// 初始化该勇士
this.initHeros = function () {
core.status.hero.icon = "hero.png";
core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中
// core.setFlag("hero2", core.clone(hero2)); // 更多的勇士...
}
// 切换勇士
this.changeHero = function (toHeroId) {
var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID
if (!core.isset(toHeroId)) {
toHeroId = (currHeroId+1)%heroCount;
}
if (currHeroId == toHeroId) return;
var saveList = Object.keys(hero1);
// 保存当前内容
var toSave = {};
saveList.forEach(function(name) {
if (name=='floorId') toSave[name] = core.status.floorId; // 楼层单独设置
else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象
})
core.setFlag("hero"+currHeroId, toSave); // 将当前角色信息进行保存
var data = core.getFlag("hero"+toHeroId); // 获得要切换的角色保存内容
// 设置角色的属性值
saveList.forEach(function(name) {
if (name != 'floorId')
core.status.hero[name] = core.clone(data[name]);
})
// 插入事件:改变角色行走图并进行楼层切换
core.insertAction([
{"type": "setHeroIcon", "name": data.icon||"hero.png"}, // 改变行走图
{"type": "changeFloor", "floorId": data.floorId, "loc": [data.loc.x, data.loc.y],
"direction": data.loc.direction, "time": 0} // 楼层切换事件
])
core.setFlag("heroId", toHeroId); // 保存切换到的角色ID
}
```
3. 在脚本编辑 - setInitData中加上`core.plugin.initHeros()`来初始化新勇士。(写在`core.events.afterLoadData()`后,反大括号之前。)
4. 如果需要切换角色包括事件、道具或者快捷键等可以直接调用自定义JS脚本`core.plugin.changeHero();`进行切换。也可以指定参数调用`core.plugin.changeHero(1)`来切换到某个具体的勇士上。
## 系统使用的flag变量
众所周知自定义flag变量都可以任意定义并取用未定义直接取用的flag默认值为0
@ -979,4 +714,4 @@ this.getAchievements = function () {
==========================================================================================
[继续阅读附录所有API列表](api)
[继续阅读脚本](script)

324
_docs/script.md Normal file
View File

@ -0,0 +1,324 @@
# 脚本
?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} *
在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。
## 控制台的使用
在Chrome浏览器中Ctrl+Shift+I可打开控制台。
![](img/console.jpg)
控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。
有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。
### Console命令行
Console页为命令行。可以在这里输入一些命令进行调试。
比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。
更多的API可参见[附录API列表](#附录API列表)。
除此以外游戏中的报错等信息也是可以在Console中进行查看的。
![](img/console1.jpg)
### Sources断点调试
Sources页可以查看JS源代码并进行断点调试等。
例如,如果相对脚本编辑中的伤害计算函数进行断点调试:
1. 在左边找到`project/functions.js`,单击打开文件
2. 并找到对应的行可以Ctrl+F搜索比如搜索`getDamageInfo`
3. 在行号上点一下打断点,会出现一个蓝色标签
之后,当代码运行到你的断点处时,将自动停止运行。
![](img/sources.jpg)
可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。
图中红色框内有几个按钮,从左到右分别是:**继续执行****执行到下一行****进入当前函数****跳出当前函数****单步执行**。
通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。
红圈下方是Call Stack即当前的函数调用链从哪些地方调用过来的
Sources还有更多有趣的功能在此不做介绍有兴趣的可自行网上搜索了解。
### Elements网页元素查看
Elements页可以查看网页的源代码调整css布局等。
![](img/elements.jpg)
不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。
手机模式下,左边可以对屏幕分辨率进行调整和模拟。
这可以很有效的帮我们进行测试样板在手机端的表现。
## 整体项目架构
``` text
├── /_server/ # 为可视化地图编辑器提供一些支持的目录
├── /libs/ # ---- 系统库目录 ----
│ ├─ /thirdparty/ # 游戏所用到的第三方库文件
│ ├─ actions.js # 用户交互处理
│ ├─ core.js # 系统核心文件(游戏入口,接口&转发)
│ ├─ control.js # 游戏逻辑控制
│ ├─ data.js # 全塔属性等
│ ├─ enemys.js # 怪物相关处理
│ ├─ events.js # 各个事件的执行
│ ├─ icons.js # 图标和素材
│ ├─ items.js # 道具效果
│ ├─ loader.js # 各个资源加载
│ ├─ maps.js # 地图数据和绘制
│ ├─ ui.js # UI窗口绘制
│ └─ utils.js # 工具类函数
├── /project/ # ---- 项目目录 ----
│ ├─ /animates/ # 动画目录
│ ├─ /floors/ # 楼层文件
│ ├─ /images/ # 图片素材
│ ├─ /sounds/ # bgm和音效
│ ├─ data.js # 全塔属性
│ ├─ enemys.js # 怪物属性
│ ├─ events.js # 公共事件
│ ├─ functions.js # 脚本编辑
│ ├─ icons.js # 素材和ID的对应关系定义
│ ├─ items.js # 道具的定义和效果
│ ├─ maps.js # 地图和数字的对应关系
│ └─ plugins.js # 自定义插件
├── /常用工具/ # 辅助造塔的小工具
├── editor.html # 地图编辑器
├── editor-mobile.html # 手机版的地图编辑器
├── index.html # 主程序,游戏的入口
├── main.js # JS程序的入口将动态对所需JS进行加载
├── style.css # 游戏所需要用到的样式表
└── 启动服务.exe # 一个本地的HTTP服务器通过它来运行游戏
```
`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。
`libs`为**系统库目录**,里面存放了各个系统核心函数。
从V2.6开始请勿直接修改libs下的代码如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。
`project`为**项目目录**你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。
## 函数的转发
在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。
例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的:
```js
////// 获得某个自定义变量或flag //////
control.prototype.getFlag = function(name, defaultValue) {
if (!core.status.hero) return defaultValue;
var value = core.status.hero.flags[name];
return value != null ? value : defaultValue;
}
```
也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。
但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`而不需要中间的control。
为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数转发到core中执行**。
上述`getFlag`代码的转发实际上是增加了如下函数:
```js
////// getFlag函数的转发 //////
core.getFlag = function (name, defaultValue) {
return core.control.getFlag(name, defaultValue);
}
// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag()
```
转发是自动完成的,其满足如下两条规则:
- **在libs中其他文件定义的函数如果不以下划线`_`开头,就会进行转发。**
- **如果core中已经存在同名函数则会在控制台中打出一条报错信息并不转发该函数。**
具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。
!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发!
例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。
## 插件编写
插件编写是H5魔塔的一个重大特点从V2.0.1引入,并逐渐发扬光大。
对于有一定脚本经验的人来说,可以编写插件来实现各种各样的功能,包括且不仅限于拓展功能的实现,系统代码的复写等等。
在V2.5.5以前插件位置都在脚本编辑中从V2.6开始则迁移到了新的下拉框中,并进行了切分。
你也可以创建自己的插件。
![](img/plugin.jpg)
新的插件切分和原来的单插件使用方法完全一致,单纯进行了切分而已。可参见已有的`init`和`drawLight`的样例。
拆分的意义主要是将各个可能的功能独立出来,避免单个框内内容太长,过大和混杂等。
在V2.6中,应当每个独立的额外功能实现都新建一个自己的插件,这样也方便进行拓展,例如打包迁移到别的塔上,或发布在网页插件库中。
另外一点需要注意的是,所有插件的初始化都会在系统资源加载之前,此时图片等资源尚未进行加载。
在所有资源加载完毕时将会执行init插件中的_afterLoadResources函数可以在这里对资源进行一些操作比如切分图片等。
```js
function () {
console.log("插件编写测试");
// 可以写一些直接执行的代码
// 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。
// 请勿在这里对包括bgm图片等资源进行操作。
this._afterLoadResources = function () {
// 本函数将在所有资源加载完毕后,游戏开启前被执行
// 可以在这个函数里面对资源进行一些操作,比如切分图片等。
// 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。
// var arr = core.splitImage("assets.png", 32, 32);
// for (var i = 0; i < arr.length; i++) {
// core.material.images.images["asset"+i+".png"] = arr[i];
// }
}
// 可以在任何地方如afterXXX或自定义脚本事件调用函数方法为 core.plugin.xxx();
// 从V2.6开始插件中用this.XXX方式定义的函数也会被转发到core中详见文档-脚本-函数的转发。
}
```
网站上提供了一个插件库,[https://h5mota.com/plugins/](https://h5mota.com/plugins/),上面有一些大家分享的插件,可供使用。
可以查看附录中的[API列表](api)来查看所有的系统API内容。
## 复写函数
样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。
在V2.6以前需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题
- 不容易记得自己修改过什么,而且如果改错了很麻烦
- 例如,直接修改了某函数加了新功能,结果过段时间发现不需要,想删掉,但是这时候已经很难找到自己改过了什么了。
- 或者如果代码改错了不断往上面打补丁也只会使得libs越来越乱最后连自己做过什么都不记得。
- 不容易随着新样板接档进行迁移
- 不方便能整理成新的插件在别的塔使用总不能让别的塔也去修改libs吧
- ……
好消息的是从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。
函数复写的好处如下:
- 不会影响系统原有代码。
- 即使写错了或不需要了,也只用把插件中的函数注释或删除即可,不会对原来的系统代码产生任何影响。
- 清晰明了。很容易方便知道自己修改过什么,尤其是可以和系统原有代码进行对比。
- 方便整理成新的插件,给其他的塔使用。
如果我想对xxx文件中的yyy函数进行重写其模式一般是`core.xxx.yyy = function (参数列表) { ... }`
下面是几个例子,从简单到复杂。
### 重写怪物手册的背景图绘制使用winskin而不是默认的黑色
直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。
```js
// 重写ui.js中的_drawBook_drawBackground函数
core.ui._drawBook_drawBackground = function () {
// core.__PIXELS__为定义的一个宏对于13x13的值是416对于15x15的值是480
core.drawBackground(0, 0, core.__PIXELS__, core.__PIXELS__);
}
```
### 重写点击楼传事件
重写点击楼传事件使得点击楼传按钮时能使用一个道具比如item:fly
```js
// 重写events.js的useFly函数即点击楼传按钮时的事件
core.events.useFly = function (fromUserAction) {
if (core.isMoving()) {
core.drawTip("请先停止勇士行动");
return;
}
if (core.status.lockControl || core.status.event.id != null) return;
if (core.canUseItem('fly')) core.useItem('fly');
else core.drawTip("当前无法使用"+core.material.items.fly.name);
}
```
其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。
### 楼层切换时根据flag来播放不同的音效
整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。
```js
// 复制重写events.js中的_changeFloor_beforeChange修改音效
core.events._changeFloor_beforeChange = function (info, callback) {
// 直接替换原始函数中的 core.playSound('floor.mp3');
if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3');
if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3');
if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3');
// ...
// 下面是原始函数中的剩余代码,保持不变
window.setTimeout(function () {
if (info.time == 0)
core.events._changeFloor_changing(info, callback);
else
core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () {
core.events._changeFloor_changing(info, callback);
});
}, 25)
}
```
### 每次打开全局商店时播放一个音效
打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。
然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。
```js
var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来
core.events.openShop = function (shopId, needVisited) {
core.playSound("shop.mp3"); // 播放一个音效
return openShop(shopId, needVisited); // 直接调用原始函数
}
```
### 每次绘制地图前在控制台打出一条信息
绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。
由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。
但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。
```js
var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来
core.maps.drawMap = function (floorId, callback) {
console.log("drawMap..."); // 控制台打出一条信息
return drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps
}
```
详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。
==========================================================================================
[继续阅读下一章API列表](api)

View File

@ -305,8 +305,8 @@ action
| moveImage_s
| showGif_0_s
| showGif_1_s
| setFg_0_s
| setFg_1_s
| setCurtain_0_s
| setCurtain_1_s
| screenFlash_s
| setWeather_s
| move_s
@ -324,6 +324,7 @@ action
| win_s
| lose_s
| if_s
| if_1_s
| switch_s
| while_s
| break_s
@ -1278,35 +1279,35 @@ var code = '{"type": "moveImage", "code": '+Int_0+toloc+EvalString_0+',"time": '
return code;
*/;
setFg_0_s
setCurtain_0_s
: '更改画面色调' EvalString Colour '动画时间' Int? '不等待执行完毕' Bool Newline
/* setFg_0_s
tooltip : setFg: 更改画面色调,动画时间可不填
helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83
/* setCurtain_0_s
tooltip : setCurtain: 更改画面色调,动画时间可不填
helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83
default : ["255,255,255,1",'rgba(255,255,255,1)',500,false]
colour : this.soundColor
var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/;
if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1');
Int_0 = Int_0!=='' ?(', "time": '+Int_0):'';
var async = Bool_0?', "async": true':'';
var code = '{"type": "setFg", "color": ['+EvalString_0+']'+Int_0 +async+'},\n';
var code = '{"type": "setCurtain", "color": ['+EvalString_0+']'+Int_0 +async+'},\n';
return code;
*/;
setFg_1_s
setCurtain_1_s
: '恢复画面色调' '动画时间' Int? '不等待执行完毕' Bool Newline
/* setFg_1_s
tooltip : setFg: 恢复画面色调,动画时间可不填
helpUrl : https://h5mota.com/games/template/docs/#/event?id=setfg%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83
/* setCurtain_1_s
tooltip : setCurtain: 恢复画面色调,动画时间可不填
helpUrl : https://h5mota.com/games/template/docs/#/event?id=setcurtain%EF%BC%9A%E6%9B%B4%E6%94%B9%E7%94%BB%E9%9D%A2%E8%89%B2%E8%B0%83
default : [500,false]
colour : this.soundColor
Int_0 = Int_0!=='' ?(', "time": '+Int_0):'';
var async = Bool_0?', "async": true':'';
var code = '{"type": "setFg"'+Int_0 +async+'},\n';
var code = '{"type": "setCurtain"'+Int_0 +async+'},\n';
return code;
*/;
@ -1482,15 +1483,16 @@ return code;
*/;
playSound_s
: '播放音效' EvalString Newline
: '播放音效' EvalString '停止之前音效' Bool? Newline
/* playSound_s
tooltip : playSound: 播放音效
helpUrl : https://h5mota.com/games/template/docs/#/event?id=playsound%EF%BC%9A%E6%92%AD%E6%94%BE%E9%9F%B3%E6%95%88
default : ["item.mp3"]
default : ["item.mp3",false]
colour : this.soundColor
var code = '{"type": "playSound", "name": "'+EvalString_0+'"},\n';
Bool_0 = Bool_0 ? ', "stop": true' : '';
var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+'},\n';
return code;
*/;
@ -1587,6 +1589,20 @@ var code = ['{"type": "if", "condition": "',expression_0,'",\n',
return code;
*/;
if_1_s
: '如果' ':' expression BGNL? Newline action+ BEND Newline
/* if_1_s
tooltip : if: 条件判断
helpUrl : https://h5mota.com/games/template/docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD
colour : this.eventColor
var code = ['{"type": "if", "condition": "',expression_0,'",\n',
'"true": [\n',action_0,'],\n',
'},\n'].join('');
return code;
*/;
switch_s
: '多重分歧 条件判定' ':' expression BGNL? Newline switchCase+ BEND Newline
@ -1603,14 +1619,16 @@ return code;
*/;
switchCase
: '如果是' expression '的场合' BGNL? Newline action+
: '如果是' expression '的场合' '不跳出' Bool BGNL? Newline action+
/* switchCase
tooltip : 选项的选择
helpUrl : https://h5mota.com/games/template/docs/#/event?id=switch%EF%BC%9A%E5%A4%9A%E9%87%8D%E6%9D%A1%E4%BB%B6%E5%88%86%E6%AD%A7
default : ["", false]
colour : this.subColor
var code = '{"case": "'+expression_0+'", "action": [\n'+action_0+']},\n';
Bool_0 = Bool_0?', "nobreak": true':'';
var code = '{"case": "'+expression_0+'"'+Bool_0+', "action": [\n'+action_0+']},\n';
return code;
*/;
@ -1639,13 +1657,13 @@ return code;
*/;
choicesContext
: '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour '不跳出' Bool BGNL? Newline action+
: '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour BGNL? Newline action+
/* choicesContext
tooltip : 选项的选择
helpUrl : https://h5mota.com/games/template/docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9
default : ["提示文字:红钥匙","","",null,false]
default : ["提示文字:红钥匙","",""]
colour : this.subColor
if (EvalString_1) {
var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/;
@ -1655,19 +1673,19 @@ if (EvalString_1) {
EvalString_1 = ', "color": "'+EvalString_1+'"';
}
IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):'';
var nobreak = Bool_0?', "nobreak": true':'';
var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']'+nobreak+'},\n';
var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']},\n';
return code;
*/;
confirm_s
: '显示确认框' ':' EvalString BGNL? '确定' ':' BGNL? Newline action+ '取消' ':' BGNL? Newline action+ BEND Newline
: '显示确认框' ':' EvalString BGNL? '确定的场合' ':' '(默认选中' Bool '' BGNL? Newline action+ '取消的场合' ':' BGNL? Newline action+ BEND Newline
/* confirm_s
tooltip : 弹出确认框
helpUrl : https://h5mota.com/games/template/docs/#/
default : ["确认要???吗?"]
var code = ['{"type": "confirm", "text": "',EvalString_0,'",\n',
default : ["确认要xxx吗?",false]
Bool_0 = Bool_0?', "default": true':''
var code = ['{"type": "confirm"'+Bool_0+', "text": "',EvalString_0,'",\n',
'"yes": [\n',action_0,'],\n',
'"no": [\n',action_1,']\n',
'},\n'].join('');
@ -2448,11 +2466,12 @@ ActionParser.prototype.parseAction = function() {
}
break;
case "setFg": // 颜色渐变
case "setCurtain":
if(this.isset(data.color)){
this.next = MotaActionBlocks['setFg_0_s'].xmlText([
this.next = MotaActionBlocks['setCurtain_0_s'].xmlText([
data.color,'rgba('+data.color+')',data.time||0,data.async||false,this.next]);
} else {
this.next = MotaActionBlocks['setFg_1_s'].xmlText([
this.next = MotaActionBlocks['setCurtain_1_s'].xmlText([
data.time||0,data.async||false,this.next]);
}
break;
@ -2511,7 +2530,7 @@ ActionParser.prototype.parseAction = function() {
break;
case "playSound":
this.next = MotaActionBlocks['playSound_s'].xmlText([
data.name,this.next]);
data.name,data.stop,this.next]);
break;
case "playBgm":
this.next = MotaActionBlocks['playBgm_s'].xmlText([
@ -2579,16 +2598,23 @@ ActionParser.prototype.parseAction = function() {
data.text,this.next]);
break;
case "if": // 条件判断
this.next = MotaActionBlocks['if_s'].xmlText([
// MotaActionBlocks['evalString_e'].xmlText([data.condition]),
this.tryToUseEvFlag_e('evalString_e', [data.condition]),
this.insertActionList(data["true"]),
this.insertActionList(data["false"]),
this.next]);
if (data["false"]) {
this.next = MotaActionBlocks['if_s'].xmlText([
this.tryToUseEvFlag_e('evalString_e', [data.condition]),
this.insertActionList(data["true"]),
this.insertActionList(data["false"]),
this.next]);
}
else {
this.next = MotaActionBlocks['if_1_s'].xmlText([
this.tryToUseEvFlag_e('evalString_e', [data.condition]),
this.insertActionList(data["true"]),
this.next]);
}
break;
case "confirm": // 显示确认框
this.next = MotaActionBlocks['confirm_s'].xmlText([
this.EvalString(data.text),
this.EvalString(data.text), data["default"],
this.insertActionList(data["yes"]),
this.insertActionList(data["no"]),
this.next]);
@ -2597,7 +2623,7 @@ ActionParser.prototype.parseAction = function() {
var case_caseList = null;
for(var ii=data.caseList.length-1,caseNow;caseNow=data.caseList[ii];ii--) {
case_caseList=MotaActionBlocks['switchCase'].xmlText([
this.isset(caseNow.case)?MotaActionBlocks['evalString_e'].xmlText([caseNow.case]):"值",this.insertActionList(caseNow.action),case_caseList]);
this.isset(caseNow.case)?MotaActionBlocks['evalString_e'].xmlText([caseNow.case]):"值",caseNow.nobreak,this.insertActionList(caseNow.action),case_caseList]);
}
this.next = MotaActionBlocks['switch_s'].xmlText([
// MotaActionBlocks['evalString_e'].xmlText([data.condition]),
@ -2608,7 +2634,7 @@ ActionParser.prototype.parseAction = function() {
var text_choices = null;
for(var ii=data.choices.length-1,choice;choice=data.choices[ii];ii--) {
text_choices=MotaActionBlocks['choicesContext'].xmlText([
choice.text,choice.icon,choice.color,'rgba('+choice.color+')',choice.nobreak,this.insertActionList(choice.action),text_choices]);
choice.text,choice.icon,choice.color,'rgba('+choice.color+')',this.insertActionList(choice.action),text_choices]);
}
this.next = MotaActionBlocks['choices_s'].xmlText([
this.isset(data.text)?this.EvalString(data.text):null,'','',text_choices,this.next]);

View File

@ -187,6 +187,11 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc =
"_type": "textarea",
"_data": "初始生命值"
},
"manamax": {
"_leaf": true,
"_type": "textarea",
"_data": "魔力上限此项非负才会生效null或小于0都不会生效"
},
"mana": {
"_leaf": true,
"_type": "textarea",

View File

@ -70,9 +70,9 @@ editor_blockly = function () {
MotaActionBlocks['lose_s'].xmlText(),
MotaActionBlocks['choices_s'].xmlText([
'选择剑或者盾','流浪者','man',MotaActionBlocks['choicesContext'].xmlText([
'剑','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]),
'剑','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]),
MotaActionBlocks['choicesContext'].xmlText([
'盾','','',null,null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]),
'盾','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]),
])
])
]),
@ -111,9 +111,10 @@ editor_blockly = function () {
],
'事件控制':[
MotaActionBlocks['if_s'].xmlText(),
MotaActionBlocks['if_1_s'].xmlText(),
MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [
{"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]},
{"action": []},
{"action": [], "nobreak": true},
{"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]},
]}),
MotaActionBlocks['while_s'].xmlText(),
@ -142,8 +143,8 @@ editor_blockly = function () {
MotaActionBlocks['animate_s'].xmlText(),
MotaActionBlocks['showStatusBar_s'].xmlText(),
MotaActionBlocks['hideStatusBar_s'].xmlText(),
MotaActionBlocks['setFg_0_s'].xmlText(),
MotaActionBlocks['setFg_1_s'].xmlText(),
MotaActionBlocks['setCurtain_0_s'].xmlText(),
MotaActionBlocks['setCurtain_1_s'].xmlText(),
MotaActionBlocks['screenFlash_s'].xmlText(),
MotaActionBlocks['setWeather_s'].xmlText(),
MotaActionBlocks['playBgm_s'].xmlText(),

View File

@ -105,7 +105,7 @@ actions.prototype.doRegisteredAction = function (action) {
return false;
}
actions.prototype.checkReplaying = function () {
actions.prototype._checkReplaying = function () {
if (core.isReplaying() && core.status.event.id != 'save'
&& (core.status.event.id || "").indexOf('book') != 0 && core.status.event.id != 'viewMaps')
return true;
@ -114,7 +114,7 @@ actions.prototype.checkReplaying = function () {
////// 检查是否在录像播放中,如果是,则停止交互
actions.prototype._sys_checkReplay = function () {
if (this.checkReplaying()) return true;
if (this._checkReplaying()) return true;
}
////// 按下某个键时 //////
@ -145,7 +145,7 @@ actions.prototype.onkeyUp = function (e) {
}
actions.prototype._sys_onkeyUp_replay = function (e) {
if (this.checkReplaying()) {
if (this._checkReplaying()) {
if (e.keyCode == 27) // ESCAPE
core.stopReplay();
else if (e.keyCode == 90) // Z
@ -286,7 +286,7 @@ actions.prototype.keyUp = function (keyCode, altKey, fromReplay) {
}
actions.prototype._sys_keyUp_replay = function (keyCode, altKey, fromReplay) {
if (!fromReplay && this.checkReplaying()) return true;
if (!fromReplay && this._checkReplaying()) return true;
}
actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) {
@ -389,7 +389,7 @@ actions.prototype._sys_keyUp = function (keyCode, altKey) {
if (core.status.automaticRoute && core.status.automaticRoute.autoHeroMove) {
core.stopAutomaticRoute();
}
core.stopHero();
core.status.heroStop = true;
return true;
}
@ -539,8 +539,8 @@ actions.prototype._sys_onup = function () {
return true;
}
////// 获得点击事件相对左上角的坐标0到12之间 //////
actions.prototype.getClickLoc = function (x, y) {
////// 获得点击事件相对左上角的坐标 //////
actions.prototype._getClickLoc = function (x, y) {
var statusBar = {'x': 0, 'y': 0};
var size = 32;
@ -663,7 +663,7 @@ actions.prototype.onmousewheel = function (direct) {
actions.prototype._sys_onmousewheel = function (direct) {
// 向下滚动是 -1 ,向上是 1
if (this.checkReplaying()) {
if (this._checkReplaying()) {
// 滚轮控制速度
if (direct == 1) core.speedUpReplay();
if (direct == -1) core.speedDownReplay();
@ -891,6 +891,21 @@ actions.prototype._clickAction = function (x, y) {
core.doAction();
}
}
return;
}
if (core.status.event.data.type == 'confirm') {
if ((x == this.HSIZE-2 || x == this.HSIZE-1) && y == this.HSIZE+1) {
core.status.route.push("choices:0");
core.insertAction(core.status.event.ui.yes);
core.doAction();
}
if ((x == this.HSIZE+2 || x == this.HSIZE+1) && y == this.HSIZE+1) {
core.status.route.push("choices:1");
core.insertAction(core.status.event.ui.no);
core.doAction();
}
return;
}
}
@ -898,6 +913,12 @@ actions.prototype._clickAction = function (x, y) {
actions.prototype._keyDownAction = function (keycode) {
if (core.status.event.data.type == 'choices') {
this._keyDownChoices(keycode);
return;
}
if (core.status.event.data.type == 'confirm' && (keycode == 37 || keycode == 39)) {
core.status.event.selection = 1 - core.status.event.selection;
core.drawConfirmBox(core.status.event.ui.text);
return;
}
}
@ -924,6 +945,15 @@ actions.prototype._keyUpAction = function (keycode) {
if (choices.length > 0) {
this._selectChoices(choices.length, keycode, this._clickAction);
}
return;
}
if (core.status.event.data.type == 'confirm'&& (keycode == 13 || keycode == 32 || keycode == 67)) {
core.status.route.push("choices:" + core.status.event.selection);
if (core.status.event.selection == 0)
core.insertAction(core.status.event.ui.yes);
else core.insertAction(core.status.event.ui.no);
core.doAction();
return;
}
}
@ -958,6 +988,7 @@ actions.prototype._clickBook = function (x, y) {
var index = this.HSIZE * page + parseInt(y / 2);
core.ui.drawBook(index);
core.ui.drawBookDetail(index);
return;
}
return;
}
@ -1101,17 +1132,20 @@ actions.prototype._clickViewMaps = function (x, y) {
index++;
if (index < core.floorIds.length)
core.ui.drawMaps(index);
return;
}
else if (y >= this.HSIZE + 2 && (mh == this.SIZE || (x >= per && x <= this.LAST - per))) {
if (y >= this.HSIZE + 2 && (mh == this.SIZE || (x >= per && x <= this.LAST - per))) {
index--;
while (index >= 0 && index != now && core.status.maps[core.floorIds[index]].cannotViewMap)
index--;
if (index >= 0)
core.ui.drawMaps(index);
return;
}
else if (x >= per && x <= this.LAST - per && y >= this.HSIZE - 1 && y <= this.HSIZE + 1) {
if (x >= per && x <= this.LAST - per && y >= this.HSIZE - 1 && y <= this.HSIZE + 1) {
core.clearMap('data');
core.ui.closePanel();
return;
}
}
@ -1217,7 +1251,9 @@ actions.prototype._clickQuickShop = function (x, y) {
// 离开
else if (y == topIndex + keys.length)
core.ui.closePanel();
return;
}
return;
}
////// 快捷商店界面时,放开某个键的操作 //////
@ -1255,7 +1291,7 @@ actions.prototype._clickToolbox = function (x, y) {
core.ui.drawToolbox(core.status.event.selection);
}
if (y == this.LAST && constantsPage > 1) {
core.status.event.data.toolsPage--;
core.status.event.data.constantsPage--;
core.ui.drawToolbox(core.status.event.selection);
}
}
@ -1904,11 +1940,11 @@ actions.prototype._clickSettings = function (x, y) {
core.ui.drawKeyBoard();
break;
case 2:
core.clearSelector();
core.clearUI();
core.ui.drawMaps();
break;
case 3:
core.clearSelector();
core.clearUI();
core.ui.drawPaint();
break;
case 4:
@ -1920,7 +1956,7 @@ actions.prototype._clickSettings = function (x, y) {
core.ui.drawGameInfo();
break;
case 6:
return core.confirmRestart(true);
return core.confirmRestart();
case 7:
core.ui.closePanel();
break;
@ -2065,7 +2101,7 @@ actions.prototype._clickLocalSaveSelect = function (x, y) {
var selection = y - topIndex;
core.status.event.selection = selection;
if (selection < 2) {
core.getAllSaves(selection == 0 ? null : core.saves.saveIndex, function (saves) {
var callback = function (saves) {
if (saves) {
var content = {
"name": core.firstData.name,
@ -2074,7 +2110,9 @@ actions.prototype._clickLocalSaveSelect = function (x, y) {
}
core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5save", JSON.stringify(content));
}
})
};
if (selection == 0) core.getAllSaves(callback);
else core.getSave(core.saves.saveIndex, callback);
}
core.status.event.selection = 2;
@ -2209,7 +2247,7 @@ actions.prototype._clickReplay_fromBeginning = function () {
actions.prototype._clickReplay_fromLoad = function () {
core.status.event.id = 'replayLoad';
core.status.event.selection = null;
core.ui.clearSelector();
core.clearUI();
var saveIndex = core.saves.saveIndex;
var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page;
core.ui.drawSLPanel(10 * page + offset);
@ -2448,7 +2486,7 @@ actions.prototype._onupPaint = function () {
core.status.event.data.x = null;
core.status.event.data.y = null;
// 保存
core.paint[core.status.floorId] = lzw_encode(core.utils.encodeCanvas(core.dymCanvas.paint).join(","));
core.paint[core.status.floorId] = lzw_encode(core.utils._encodeCanvas(core.dymCanvas.paint).join(","));
}
actions.prototype.setPaintMode = function (mode) {
@ -2496,7 +2534,7 @@ actions.prototype.loadPaint = function () {
core.clearMap('paint');
var value = core.paint[core.status.floorId];
if (value) value = lzw_decode(value).split(",");
core.utils.decodeCanvas(value, 32 * core.bigmap.width, 32 * core.bigmap.height);
core.utils._decodeCanvas(value, 32 * core.bigmap.width, 32 * core.bigmap.height);
core.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0);
core.drawTip("读取绘图文件成功");

View File

@ -308,7 +308,7 @@ control.prototype.showStartAnimate = function (noAnimate, callback) {
this._showStartAnimate_resetDom();
if (core.flags.startUsingCanvas || noAnimate)
return this._showStartAnimate_finished(core.flags.startUsingCanvas, callback);
core.hide(core.dom.startTop, 20, function () {
core.hideWithAnimate(core.dom.startTop, 20, function () {
core.control._showStartAnimate_finished(false, callback);
});
}
@ -324,8 +324,8 @@ control.prototype._showStartAnimate_resetDom = function () {
core.status.played = false;
core.clearStatus();
core.clearMap('all');
core.deleteAllCanvas();
core.dom.musicBtn.style.display = 'block';
core.setMusicBtn();
// 重置音量
core.events.setVolume(1, 0);
core.updateStatusBar();
@ -340,7 +340,7 @@ control.prototype._showStartAnimate_finished = function (start, callback) {
////// 隐藏游戏开始界面 //////
control.prototype.hideStartAnimate = function (callback) {
core.hide(core.dom.startPanel, 20, callback);
core.hideWithAnimate(core.dom.startPanel, 20, callback);
}
////// 游戏是否已经开始 //////
@ -348,30 +348,6 @@ control.prototype.isPlaying = function() {
return core.status.played;
}
////// 重新开始游戏;此函数将回到标题页面 //////
control.prototype.restart = function() {
this.showStartAnimate();
core.playBgm(main.startBgm);
}
////// 询问是否需要重新开始 //////
control.prototype.confirmRestart = function (fromSettings) {
core.status.event.selection = 1;
core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () {
core.ui.closePanel();
core.restart();
}, function () {
if (fromSettings) {
core.status.event.selection = 3;
core.ui.drawSettings();
}
else {
core.ui.closePanel();
}
});
}
////// 清除游戏状态和数据 //////
control.prototype.clearStatus = function() {
// 停止各个Timeout和Interval
@ -424,7 +400,7 @@ control.prototype.stopAutomaticRoute = function () {
core.status.automaticRoute.destX=null;
core.status.automaticRoute.destY=null;
core.status.automaticRoute.lastDirection = null;
core.stopHero();
core.status.heroStop = true;
if (core.status.automaticRoute.moveStepBeforeStop.length==0)
core.deleteCanvas('route');
}
@ -472,8 +448,10 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return;
if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return;
// 找寻自动寻路路线
var moveStep = core.automaticRoute(destX, destY).concat(stepPostfix);
if (moveStep.length == 0) return core.deleteCanvas('route');
var moveStep = core.automaticRoute(destX, destY);
if (moveStep.length == 0 && (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y || stepPostfix.length == 0))
return;
moveStep = moveStep.concat(stepPostfix);
core.status.automaticRoute.destX=destX;
core.status.automaticRoute.destY=destY;
this._setAutomaticRoute_drawRoute(moveStep);
@ -659,7 +637,7 @@ control.prototype._moveAction_moving = function (callback) {
core.updateStatusBar();
// 检查该点是否是滑冰
if (core.getBgFgNumber('bg') == 167) {
if (core.getBgNumber() == 167) {
core.insertAction("滑冰事件", null, null, null, true);
}
@ -749,11 +727,6 @@ control.prototype.waitHeroToStop = function(callback) {
}
}
////// 停止勇士的移动状态 //////
control.prototype.stopHero = function () {
core.status.heroStop = true;
}
////// 转向 //////
control.prototype.turnHero = function(direction) {
if (direction) {
@ -1272,7 +1245,12 @@ control.prototype._doReplayAction = function (action) {
control.prototype._replay_finished = function () {
core.status.replay.replaying = false;
core.status.event.selection = 0;
core.ui.drawConfirmBox("录像播放完毕,你想退出播放吗?", function () {
var str = "录像播放完毕,你想退出播放吗?";
if (core.status.route.length != core.status.replay.totalList.length
|| core.subarray(core.status.route, core.status.replay.totalList) == null) {
str = "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗";
}
core.ui.drawConfirmBox(str, function () {
core.ui.closePanel();
core.stopReplay(true);
}, function () {
@ -1460,6 +1438,7 @@ control.prototype._replayAction_moveDirectly = function (action) {
// 忽略连续的瞬移事件
while (core.status.replay.toReplay.length>0 &&
core.status.replay.toReplay[0].indexOf('move:')==0) {
core.status.route.push(action);
action = core.status.replay.toReplay.shift();
}
@ -1615,13 +1594,15 @@ control.prototype._doSL_replayLoad_afterGet = function (id, data) {
////// 同步存档到服务器 //////
control.prototype.syncSave = function (type) {
core.ui.drawWaiting("正在同步,请稍后...");
core.getAllSaves(type=='all'?null:core.saves.saveIndex, function (saves) {
if (!saves) return core.drawText("没有要同步的存档");
var callback = function (saves) {
core.control._syncSave_http(type, saves);
})
}
if (type == 'all') core.getAllSaves(callback);
else core.getSave(core.saves.saveIndex, callback);
}
control.prototype._syncSave_http = function (type, saves) {
if (!saves) return core.drawText("没有要同步的存档");
var formData = new FormData();
formData.append('type', 'save');
formData.append('name', core.firstData.name);
@ -1635,7 +1616,7 @@ control.prototype._syncSave_http = function (type, saves) {
else {
core.drawText((type=='all'?"所有存档":"存档"+core.saves.saveIndex)+"同步成功!\n\n您的存档编号 "
+response.code+"\n您的存档密码 "+response.msg
+"\n\n请牢记以上两个信息如截图等在从服务器\n同步存档时使用。")
+"\n\n请牢记以上两个信息如截图等在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r")
}
}, function (e) {
core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因"+e);
@ -1746,8 +1727,7 @@ control.prototype.getSaves = function (ids, callback) {
}
}
control.prototype.getAllSaves = function (id, callback) {
if (id != null) return this.getSave(id, callback);
control.prototype.getAllSaves = function (callback) {
var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;})
.sort(function(a,b) {return a-b;}), saves = [];
this.getSaves(ids, function (data) {
@ -1790,7 +1770,7 @@ control.prototype.hasSave = function (index) {
return core.saves.ids[index] || false;
}
////// 删除一个或多个存档
////// 删除个存档
control.prototype.removeSave = function (index, callback) {
if (index == 0 || index == "autoSave") {
index = "autoSave";
@ -1825,8 +1805,6 @@ control.prototype._updateFavoriteSaves = function () {
core.setLocalStorage("favoriteName", core.saves.favoriteName);
}
////// 加载某个存档
// ------ 属性状态位置buff变量锁定控制等 ------ //
////// 设置勇士属性 //////
@ -2017,7 +1995,7 @@ control.prototype._setWeather_createNodes = function (type, level) {
}
////// 更改画面色调 //////
control.prototype.setFg = function(color, time, callback) {
control.prototype.setCurtain = function(color, time, callback) {
if (time == null) time=750;
if (time<=0) time=0;
if (!core.status.curtainColor)
@ -2035,10 +2013,10 @@ control.prototype.setFg = function(color, time, callback) {
return;
}
this._setFg_animate(core.status.curtainColor, color, time, callback);
this._setCurtain_animate(core.status.curtainColor, color, time, callback);
}
control.prototype._setFg_animate = function (nowColor, color, time, callback) {
control.prototype._setCurtain_animate = function (nowColor, color, time, callback) {
var per_time = 10, step = parseInt(time / per_time);
var animate = setInterval(function() {
nowColor = [
@ -2066,8 +2044,8 @@ control.prototype.screenFlash = function (color, time, times, callback) {
times = times || 1;
time = time / 3;
var nowColor = core.clone(core.status.curtainColor);
core.setFg(color, time, function() {
core.setFg(nowColor, time * 2, function() {
core.setCurtain(color, time, function() {
core.setCurtain(nowColor, time * 2, function() {
if (times > 1)
core.screenFlash(color, time * 3, times - 1, callback);
else {
@ -2203,8 +2181,8 @@ control.prototype.stopSound = function () {
for (var i in core.musicStatus.playingSounds) {
var source = core.musicStatus.playingSounds[i];
try {
if (source[i].stop) source[i].stop();
else if (source[i].noteOff) source[i].noteOff();
if (source.stop) source.stop();
else if (source.noteOff) source.noteOff();
}
catch (e) {
main.log(e);
@ -2353,10 +2331,11 @@ control.prototype.updateGlobalAttribute = function (name) {
core.dom.statusBar.style.borderTop = border;
core.dom.statusBar.style.borderLeft = border;
core.dom.statusBar.style.borderRight = core.domStyle.isVertical?border:'';
core.dom.statusBar.style.borderBottom = core.domStyle.isVertical?'':border;
core.dom.gameDraw.style.border = border;
core.dom.toolBar.style.borderBottom = border;
core.dom.toolBar.style.borderLeft = border;
core.dom.toolBar.style.borderRight = core.domStyle.isVertical?border:'';
core.dom.toolBar.style.borderBottom = core.domStyle.isVertical?border:'';
break;
}
case 'statusBarColor':
@ -2378,7 +2357,7 @@ control.prototype.updateGlobalAttribute = function (name) {
}
}
////// 改变工具栏为按钮1-7 //////
////// 改变工具栏为按钮1-8 //////
control.prototype.setToolbarButton = function (useButton) {
if (!core.domStyle.showStatusBar) {
// 隐藏状态栏时检查竖屏
@ -2544,7 +2523,7 @@ control.prototype._resize_gameGroup = function (obj) {
floorMsgGroup.style.color = obj.globalAttribute.floorChangingTextColor;
// musicBtn
if (core.domStyle.isVertical || core.domStyle.scale < 1) {
core.dom.musicBtn.style.right = core.dom.musicBtn.style.height = "3px";
core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px";
}
else {
core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px";
@ -2588,7 +2567,7 @@ control.prototype._resize_statusBar = function (obj) {
}
else {
statusBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + "px";
statusBar.style.height = obj.outerSize - 3 + "px";
statusBar.style.height = obj.outerSize + "px";
statusBar.style.background = obj.globalAttribute.statusLeftBackground;
// --- 计算文字大小
statusBar.style.fontSize = 16 * Math.min(1, (core.__HALF_SIZE__ + 3) / obj.count) * core.domStyle.scale + "px";
@ -2596,6 +2575,7 @@ control.prototype._resize_statusBar = function (obj) {
statusBar.style.display = 'block';
statusBar.style.borderTop = statusBar.style.borderLeft = obj.border;
statusBar.style.borderRight = core.domStyle.isVertical ? obj.border : '';
statusBar.style.borderBottom = core.domStyle.isVertical ? '' : obj.border;
// 自绘状态栏
if (core.domStyle.isVertical) {
core.dom.statusCanvas.style.width = obj.outerSize - 6 + "px";
@ -2651,8 +2631,8 @@ control.prototype._resize_toolBar = function (obj) {
toolBar.style.background = 'transparent';
}
toolBar.style.display = 'block';
toolBar.style.borderLeft = toolBar.style.borderBottom = obj.border;
toolBar.style.borderRight = core.domStyle.isVertical ? obj.border : '';
toolBar.style.borderLeft = obj.border;
toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical ? obj.border : '';
toolBar.style.fontSize = 16 * core.domStyle.scale + "px";
}

View File

@ -16,8 +16,7 @@ function core() {
'ground': null,
'items': {},
'enemys': {},
'icons': {},
'events': {}
'icons': {}
}
this.timeout = {
'tipTimeout': null,
@ -84,8 +83,8 @@ function core() {
this.domStyle = {
scale: 1.0,
isVertical: false,
toolbarBtn: false,
showStatusBar: true,
toolbarBtn: false,
}
this.bigmap = {
canvas: ["bg", "event", "event2", "fg", "damage"],
@ -220,6 +219,7 @@ core.prototype.init = function (coreData, callback) {
this._init_flags();
this._init_platform();
this._init_others();
this._initPlugins();
core.loader._load(function () {
core._afterLoadResources(callback);
@ -342,9 +342,10 @@ core.prototype._init_others = function () {
core.prototype._afterLoadResources = function (callback) {
// 初始化地图
core.initStatus.maps = core.maps.initMaps(core.floorIds);
core.initStatus.maps = core.maps._initMaps();
core.control._setRequestAnimationFrame();
core._initPlugins();
if (core.plugin._afterLoadResources)
core.plugin._afterLoadResources();
core.showStartAnimate();
if (callback) callback();
}

View File

@ -107,13 +107,13 @@ enemys.prototype.canBattle = function (enemy, x, y, floorId) {
////// 获得某个怪物的伤害 //////
enemys.prototype.getDamage = function (enemy, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
var damage = this.calDamage(enemy, null, x, y, floorId);
var damage = this._calDamage(enemy, null, x, y, floorId);
if (damage == null) return null;
return damage + this.getExtraDamage(enemy);
return damage + this.getExtraDamage(enemy, x, y, floorId);
}
////// 获得某个怪物的额外伤害 //////
enemys.prototype.getExtraDamage = function (enemy) {
enemys.prototype.getExtraDamage = function (enemy, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
var extra_damage = 0;
if (this.hasSpecial(enemy.special, 17)) { // 仇恨
@ -265,8 +265,8 @@ enemys.prototype._nextCriticals_useTurn = function (enemy, info, number, x, y, f
enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
k = k || 1;
var nowDamage = this.calDamage(enemy, null, x, y, floorId);
var nextDamage = this.calDamage(enemy, {"def": core.status.hero.def + k}, x, y, floorId);
var nowDamage = this._calDamage(enemy, null, x, y, floorId);
var nextDamage = this._calDamage(enemy, {"def": core.status.hero.def + k}, x, y, floorId);
if (nowDamage == null || nextDamage == null) return "???";
return nowDamage - nextDamage;
}
@ -284,7 +284,7 @@ enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) {
}
////// 获得在某个勇士属性下怪物伤害 //////
enemys.prototype.calDamage = function (enemy, hero, x, y, floorId) {
enemys.prototype._calDamage = function (enemy, hero, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
var info = this.getDamageInfo(enemy, hero, x, y, floorId);
@ -357,4 +357,8 @@ enemys.prototype._getCurrentEnemys_sort = function (enemys) {
}
return a.damage - b.damage;
});
}
enemys.prototype.hasEnemyLeft = function (floorId) {
return core.getCurrentEnemys(floorId).length > 0;
}

View File

@ -52,9 +52,6 @@ events.prototype._startGame_start = function (hard, seed, route, callback) {
}
else core.utils.__init_seed();
this.setInitData();
core.clearMap('all');
core.deleteAllCanvas();
core.clearStatusBar();
var todo = [];
@ -183,7 +180,7 @@ events.prototype._gameOver_doUpload = function (username, ending, norank) {
formData.append('money', core.status.hero.money);
formData.append('experience', core.status.hero.experience);
formData.append('steps', core.status.hero.steps);
formData.append('norank', norank || 0);
formData.append('norank', norank ? 1 : 0);
formData.append('seed', core.getFlag('__seed__'));
formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime / 1000));
formData.append('route', core.encodeRoute(core.status.route));
@ -234,6 +231,23 @@ events.prototype._gameOver_askRate = function (ending) {
});
}
////// 重新开始游戏;此函数将回到标题页面 //////
events.prototype.restart = function() {
core.showStartAnimate();
core.playBgm(main.startBgm);
}
////// 询问是否需要重新开始 //////
events.prototype.confirmRestart = function () {
core.status.event.selection = 1;
core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () {
core.ui.closePanel();
core.restart();
}, function () {
core.ui.closePanel();
});
}
// ------ 系统事件的处理 ------ //
////// 注册一个系统事件 //////
@ -279,10 +293,7 @@ events.prototype._trigger = function (x, y) {
if (trigger == 'changeFloor' && !noPass && this._trigger_ignoreChangeFloor(block))
return;
core.status.automaticRoute.moveDirectly = false;
this.doSystemEvent(trigger, block, function () {
if (trigger == 'openDoor' || trigger == 'changeFloor')
core.replay();
})
this.doSystemEvent(trigger, block);
}
}
@ -341,16 +352,20 @@ events.prototype.afterBattle = function (enemyId, x, y, callback) {
}
events.prototype._sys_openDoor = function (data, callback) {
this.openDoor(data.event.id, data.x, data.y, true, callback);
this.openDoor(data.x, data.y, true, function () {
core.replay();
if (callback) callback();
});
}
////// 开门 //////
events.prototype.openDoor = function (id, x, y, needKey, callback) {
id = id || core.getBlockId(x, y);
events.prototype.openDoor = function (x, y, needKey, callback) {
var id = core.getBlockId(x, y);
core.saveAndStopAutomaticRoute();
if (!this._openDoor_check(id, x, y, needKey)) {
var locked = core.status.lockControl;
core.waitHeroToStop(function () {
core.unLockControl();
if (!locked) core.unLockControl();
if (callback) callback();
});
return;
@ -362,7 +377,7 @@ events.prototype.openDoor = function (id, x, y, needKey, callback) {
events.prototype._openDoor_check = function (id, x, y, needKey) {
// 是否存在门或暗墙
if (!core.terrainExists(x, y, id) || !(id.endsWith("Door") || id.endsWith("Wall"))
|| !core.material.icons.animates[id]) {
|| core.material.icons.animates[id] == null) {
core.clearContinueAutomaticRoute();
return false;
}
@ -376,7 +391,7 @@ events.prototype._openDoor_check = function (id, x, y, needKey) {
core.clearContinueAutomaticRoute();
return false;
}
core.autosave(true);
if (!core.status.event.id) core.autosave(true);
core.removeItem(key);
}
return true;
@ -386,6 +401,7 @@ events.prototype._openDoor_animate = function (id, x, y, callback) {
var door = core.material.icons.animates[id];
var speed = id.endsWith("Door") ? 30 : 70;
var locked = core.status.lockControl;
core.lockControl();
core.status.replay.animate = true;
var state = 0;
@ -394,7 +410,7 @@ events.prototype._openDoor_animate = function (id, x, y, callback) {
if (state == 4) {
clearInterval(animate);
core.removeBlock(x, y);
core.unLockControl();
if (!locked) core.unLockControl();
core.status.replay.animate = false;
core.events.afterOpenDoor(id, x, y, callback);
return;
@ -414,29 +430,33 @@ events.prototype._sys_getItem = function (data, callback) {
}
////// 获得某个物品 //////
events.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) {
itemNum = itemNum || 1;
var itemCls = core.material.items[itemId].cls;
core.items.getItemEffect(itemId, itemNum);
core.removeBlock(itemX, itemY);
var text = '获得 ' + core.material.items[itemId].name;
if (itemNum > 1) text += "x" + itemNum;
if (itemCls === 'items') text += core.items.getItemEffectTip(itemId);
core.drawTip(text, core.material.icons.items[itemId]);
events.prototype.getItem = function (id, num, x, y, callback) {
num = num || 1;
var itemCls = core.material.items[id].cls;
core.items.getItemEffect(id, num);
core.removeBlock(x, y);
var text = '获得 ' + core.material.items[id].name;
if (num > 1) text += "x" + num;
if (itemCls === 'items') text += core.items.getItemEffectTip(id);
core.drawTip(text, id);
core.updateStatusBar();
this.eventdata.afterGetItem(itemId, itemX, itemY, callback);
this.afterGetItem(id, x, y, callback);
}
events.prototype.afterGetItem = function (id, x, y, callback) {
this.eventdata.afterGetItem(id, x, y, callback);
}
////// 获得面前的物品(轻按) //////
events.prototype.getNextItem = function () {
events.prototype.getNextItem = function (noRoute) {
if (core.isMoving() || !core.canMoveHero() || !core.flags.enableGentleClick) return false;
var nextX = core.nextX(), nextY = core.nextY();
var block = core.getBlock(nextX, nextY);
if (block == null) return false;
if (block.block.event.trigger == 'getItem') {
core.status.route.push("getNext");
if (!noRoute) core.status.route.push("getNext");
this.getItem(block.block.event.id, 1, nextX, nextY);
return true;
}
@ -449,7 +469,10 @@ events.prototype._sys_changeFloor = function (data, callback) {
if (data.loc) heroLoc = {'x': data.loc[0], 'y': data.loc[1]};
if (data.direction) heroLoc.direction = data.direction;
if (core.status.event.id != 'action') core.status.event.id = null;
core.changeFloor(data.floorId, data.stair, heroLoc, data.time, callback);
core.changeFloor(data.floorId, data.stair, heroLoc, data.time, function () {
core.replay();
if (callback) callback();
});
}
////// 楼层切换 //////
@ -461,6 +484,7 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback
}
info.fromLoad = fromLoad;
floorId = info.floorId;
info.locked = core.status.lockControl;
core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title;
core.lockControl();
@ -535,7 +559,7 @@ events.prototype._changeFloor_beforeChange = function (info, callback) {
if (info.time == 0)
core.events._changeFloor_changing(info, callback);
else
core.show(core.dom.floorMsgGroup, info.time / 2, function () {
core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () {
core.events._changeFloor_changing(info, callback);
});
}, 25)
@ -547,13 +571,13 @@ events.prototype._changeFloor_changing = function (info, callback) {
if (info.time == 0)
this._changeFloor_afterChange(info, callback);
else
core.hide(core.dom.floorMsgGroup, info.time / 4, function () {
core.hideWithAnimate(core.dom.floorMsgGroup, info.time / 4, function () {
core.events._changeFloor_afterChange(info, callback);
});
}
events.prototype._changeFloor_afterChange = function (info, callback) {
core.unLockControl();
if (!info.locked) core.unLockControl();
core.status.replay.animate = false;
core.events.afterChangeFloor(info.floorId, info.fromLoad);
@ -661,11 +685,6 @@ events.prototype._sys_changeLight = function (data, callback) {
events.prototype.changeLight = function (id, x, y) {
if (id != null && id != 'light') return;
core.setBlock(core.getNumberById('darkLight'), x, y);
this.afterChangeLight(x, y);
}
////// 改变亮灯之后,可以触发的事件 //////
events.prototype.afterChangeLight = function (x, y) {
return this.eventdata.afterChangeLight(x, y);
}
@ -724,8 +743,19 @@ events.prototype.doEvent = function (data, x, y, prefix) {
core.doAction();
}
events.prototype.setEvents = function (list, x, y, callback) {
var data = core.status.event.data || {};
if (list)
data.list = [{todo: core.clone(list), total: core.clone(list), condition: "false"}];
if (x != null) data.x = x;
if (y != null) data.y = y;
if (callback) data.callback = callback;
core.status.event.id = 'action';
core.status.event.data = data;
}
////// 开始执行一系列自定义事件 //////
events.prototype.doEvents = function (list, x, y, callback) {
events.prototype.startEvents = function (list, x, y, callback) {
if (!list) return;
if (!(list instanceof Array)) {
list = [list];
@ -738,24 +768,10 @@ events.prototype.doEvents = function (list, x, y, callback) {
});
}
events.prototype.setEvents = function (list, x, y, callback) {
var data = core.status.event.data || {};
if (list)
data.list = [{todo: core.clone(list), total: core.clone(list), condition: "false"}];
if (x != null) data.x = x;
if (y != null) data.y = y;
if (callback) data.callback = callback;
core.status.event.id = 'action';
core.status.event.data = data;
}
////// 执行当前自定义事件列表中的下一个事件 //////
events.prototype.doAction = function () {
// 清空boxAnimate和UI层
core.status.boxAnimateObjs = [];
clearInterval(core.status.event.interval);
core.status.event.interval = null;
core.clearSelector();
core.clearUI();
// 判定是否执行完毕
if (this._doAction_finishEvents()) return;
// 当前点坐标和前缀
@ -799,7 +815,7 @@ events.prototype._popEvents = function (current, prefix) {
return false;
}
////// 往当前事件列表之前添加一个或多个事件 //////
////// 往当前事件列表之前或之后添加一个或多个事件 //////
events.prototype.insertAction = function (action, x, y, callback, addToLast) {
if (core.hasFlag("__statistics__")) return;
if (core.status.gameOver) return;
@ -810,7 +826,7 @@ events.prototype.insertAction = function (action, x, y, callback, addToLast) {
if (!action) return;
if (core.status.event.id != 'action') {
this.doEvents(action, x, y, callback);
this.startEvents(action, x, y, callback);
}
else {
if (addToLast)
@ -1022,10 +1038,7 @@ events.prototype._action_jumpHero = function (data, x, y, prefix) {
events.prototype._action_changeFloor = function (data, x, y, prefix) {
var loc = this.__action_getHeroLoc(data.loc, prefix);
var heroLoc = {x: loc[0], y: loc[1], direction: data.direction};
core.changeFloor(data.floorId || core.status.floorId, null, heroLoc, data.time, function () {
core.lockControl();
core.doAction();
});
core.changeFloor(data.floorId || core.status.floorId, null, heroLoc, data.time, core.doAction);
}
events.prototype._action_changePos = function (data, x, y, prefix) {
@ -1068,13 +1081,17 @@ events.prototype._action_moveImage = function (data, x, y, prefix) {
}
events.prototype._action_setFg = function (data, x, y, prefix) {
return this._action_setCurtain(data, x, y, prefix);
}
events.prototype._action_setCurtain = function (data, x, y, prefix) {
if (data.async) {
core.setFg(data.color, data.time);
core.setCurtain(data.color, data.time);
core.setFlag('__color__', data.color || null);
core.doAction();
}
else {
core.setFg(data.color, data.time, function () {
core.setCurtain(data.color, data.time, function () {
core.setFlag('__color__', data.color || null);
core.doAction();
});
@ -1097,10 +1114,7 @@ events.prototype._action_openDoor = function (data, x, y, prefix) {
var loc = this.__action_getLoc(data.loc, x, y, prefix);
var floorId = data.floorId || core.status.floorId;
if (floorId == core.status.floorId) {
core.openDoor(null, loc[0], loc[1], data.needKey, function () {
core.lockControl();
core.doAction();
});
core.openDoor(loc[0], loc[1], data.needKey, core.doAction);
}
else {
core.removeBlock(loc[0], loc[1], floorId);
@ -1148,14 +1162,10 @@ events.prototype._action_trigger = function (data, x, y, prefix) {
if (block != null && block.block.event.trigger) {
block = block.block;
this.setEvents([], block.x, block.y);
var _callback = function () {
core.lockControl();
core.doAction();
}
if (block.event.trigger == 'action')
this.setEvents(block.event.data);
else {
core.doSystemEvent(block.event.trigger, block, _callback);
core.doSystemEvent(block.event.trigger, block, core.doAction);
return;
}
}
@ -1213,6 +1223,7 @@ events.prototype._action_freeBgm = function (data, x, y, prefix) {
}
events.prototype._action_playSound = function (data, x, y, prefix) {
if (data.stop) core.stopSound();
core.playSound(data.name);
core.doAction();
}
@ -1318,13 +1329,16 @@ events.prototype._action_if = function (data, x, y, prefix) {
events.prototype._action_switch = function (data, x, y, prefix) {
var key = core.calValue(data.condition, prefix)
var list = [];
for (var i = 0; i < data.caseList.length; i++) {
var condition = data.caseList[i]["case"];
if (condition == "default" || core.calValue(condition, prefix) == key) {
this.insertAction(data.caseList[i].action);
break;
core.push(list, data.caseList[i].action);
if (!data.caseList[i].nobreak)
break;
}
}
core.insertAction(list);
core.doAction();
}
@ -1352,6 +1366,35 @@ events.prototype._action_choices = function (data, x, y, prefix) {
core.ui.drawChoices(data.text, data.choices);
}
events.prototype._action_confirm = function (data, x, y, prefix) {
core.status.event.ui = {"text": data.text, "yes": data.yes, "no": data.no};
if (core.isReplaying()) {
var action = core.status.replay.toReplay.shift(), index;
// --- 忽略可能的turn事件
if (action == 'turn') action = core.status.replay.toReplay.shift();
if (action.indexOf("choices:") == 0 && ((index = parseInt(action.substring(8))) >= 0) && index < 2) {
core.status.event.selection = index;
setTimeout(function () {
core.status.route.push("choices:" + index);
if (index == 0) core.insertAction(data.yes);
else core.insertAction(data.no);
core.doAction();
}, 750 / Math.max(1, core.status.replay.speed))
}
else {
main.log("录像文件出错!当前需要一个 choices: 项,实际为 " + action);
core.stopReplay();
core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]);
core.doAction();
return;
}
}
else {
core.status.event.selection = data["default"] ? 0 : 1;
}
core.ui.drawConfirmBox(data.text);
}
events.prototype._action_while = function (data, x, y, prefix) {
if (core.calValue(data.condition, prefix)) {
core.unshift(core.status.event.data.list,
@ -1641,7 +1684,6 @@ events.prototype.load = function (fromUserAction) {
core.dom.startPanel.style.display = 'none';
core.clearStatus();
core.clearMap('all');
core.deleteAllCanvas();
core.status.event = {'id': 'load', 'data': null};
core.status.lockControl = true;
core.ui.drawSLPanel(10*page+offset);
@ -1698,24 +1740,6 @@ events.prototype.unfollow = function (name) {
core.drawHero();
}
////// 绘制或取消一张gif图片 //////
events.prototype.showGif = function (name, x, y) {
var image = core.material.images.images[name];
if (image) {
var gif = new Image();
gif.src = image.src;
gif.style.position = 'absolute';
gif.style.left = x * core.domStyle.scale + "px";
gif.style.top = y * core.domStyle.scale + "px";
gif.style.width = image.width * core.domStyle.scale + "px";
gif.style.height = image.height * core.domStyle.scale + "px";
core.dom.gif2.appendChild(gif);
}
else {
core.dom.gif2.innerHTML = "";
}
}
////// 数值操作 //////
events.prototype.setValue = function (name, value, prefix, add) {
var value = core.calValue(value, prefix);
@ -1919,6 +1943,24 @@ events.prototype._moveImage_moving = function (name, moveInfo, callback) {
core.animateFrame.asyncId[animate] = true;
}
////// 绘制或取消一张gif图片 //////
events.prototype.showGif = function (name, x, y) {
var image = core.material.images.images[name];
if (image) {
var gif = new Image();
gif.src = image.src;
gif.style.position = 'absolute';
gif.style.left = x * core.domStyle.scale + "px";
gif.style.top = y * core.domStyle.scale + "px";
gif.style.width = image.width * core.domStyle.scale + "px";
gif.style.height = image.height * core.domStyle.scale + "px";
core.dom.gif2.appendChild(gif);
}
else {
core.dom.gif2.innerHTML = "";
}
}
////// 淡入淡出音乐 //////
events.prototype.setVolume = function (value, time, callback) {
var set = function (value) {
@ -2040,7 +2082,7 @@ events.prototype.jumpHero = function (ex, ey, time, callback) {
var sx=core.status.hero.loc.x, sy=core.status.hero.loc.y;
if (!core.isset(ex)) ex=sx;
if (!core.isset(ey)) ey=sy;
core.maps.__playJumpSound();
core.playSound('jump.mp3');
var jumpInfo = core.maps.__generateJumpInfo(sx, sy, ex, ey, time || 500);
jumpInfo.icon = core.material.icons.hero[core.getHeroLoc('direction')];
jumpInfo.height = core.material.icons.hero.height;
@ -2209,7 +2251,7 @@ events.prototype.afterUseBomb = function () {
}
////// 上传当前数据 //////
events.prototype.uploadCurrent = function (username) {
events.prototype._uploadCurrent = function (username) {
var formData = new FormData();
formData.append('type', 'score');

View File

@ -196,20 +196,6 @@ items.prototype.setItem = function (itemId, itemNum) {
core.updateStatusBar();
}
////// 删除某个物品 //////
items.prototype.removeItem = function (itemId, itemNum) {
if (itemNum == null) itemNum = 1;
if (!core.hasItem(itemId)) return false;
var itemCls = core.material.items[itemId].cls;
core.status.hero.items[itemCls][itemId] -= itemNum;
if (core.status.hero.items[itemCls][itemId] <= 0) {
if (itemCls != 'keys') delete core.status.hero.items[itemCls][itemId];
else core.status.hero.items[itemCls][itemId] = 0;
}
core.updateStatusBar();
return true;
}
////// 增加某个物品的个数 //////
items.prototype.addItem = function (itemId, itemNum) {
if (itemNum == null) itemNum = 1;
@ -230,15 +216,22 @@ items.prototype.addItem = function (itemId, itemNum) {
core.updateStatusBar();
}
// ---------- 装备相关 ------------ //
items.prototype.getEquipTypeById = function (equipId) {
var type = core.material.items[equipId].equip.type;
if (typeof type == 'string')
type = this.getEquipTypeByName(type);
return type;
////// 删除某个物品 //////
items.prototype.removeItem = function (itemId, itemNum) {
if (itemNum == null) itemNum = 1;
if (!core.hasItem(itemId)) return false;
var itemCls = core.material.items[itemId].cls;
core.status.hero.items[itemCls][itemId] -= itemNum;
if (core.status.hero.items[itemCls][itemId] <= 0) {
if (itemCls != 'keys') delete core.status.hero.items[itemCls][itemId];
else core.status.hero.items[itemCls][itemId] = 0;
}
core.updateStatusBar();
return true;
}
// ---------- 装备相关 ------------ //
items.prototype.getEquipTypeByName = function (name) {
var names = core.status.globalAttribute.equipName;
for (var i = 0; i < names.length; ++i) {
@ -249,6 +242,13 @@ items.prototype.getEquipTypeByName = function (name) {
return -1;
}
items.prototype.getEquipTypeById = function (equipId) {
var type = core.material.items[equipId].equip.type;
if (typeof type == 'string')
type = this.getEquipTypeByName(type);
return type;
}
// 当前能否撞上某装备
items.prototype.canEquip = function (equipId, hint) {
// 装备是否合法
@ -365,8 +365,8 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) {
core.status.hero.equipment[type] = loadId || null;
// --- 提示
if (loadId) core.drawTip("已装备上" + loadEquip.name, core.material.icons.items[loadId]);
else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, core.material.icons.items[unloadId]);
if (loadId) core.drawTip("已装备上" + loadEquip.name, loadId);
else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, unloadId);
if (callback) callback();
}

View File

@ -155,7 +155,8 @@ maps.prototype._addEvent = function (block, x, y, event) {
}
////// 初始化所有地图 //////
maps.prototype.initMaps = function (floorIds) {
maps.prototype._initMaps = function () {
var floorIds = core.floorIds;
var maps = {};
for (var i = 0; i < floorIds.length; i++) {
var floorId = floorIds[i];
@ -242,7 +243,7 @@ maps.prototype.saveMap = function (floorId) {
return map;
}
var map = maps[floorId], floor = core.floors[floorId];
var blocks = this.getMapArray(map.blocks, floor.width, floor.height, true);
var blocks = this._getMapArrayFromBlocks(map.blocks, floor.width, floor.height, true);
if (main.mode == 'editor') return blocks;
var thisFloor = this._compressFloorData(map, floor);
@ -297,7 +298,13 @@ maps.prototype.resizeMap = function (floorId) {
}
////// 将当前地图重新变成二维数组形式 //////
maps.prototype.getMapArray = function (blockArray, width, height, checkDisable) {
maps.prototype.getMapArray = function (floorId, showDisable) {
floorId = floorId || core.status.floorId;
return this._getMapArrayFromBlocks(core.status.maps[floorId].blocks,
core.floors[floorId].width, core.floors[floorId].height, showDisable);
}
maps.prototype._getMapArrayFromBlocks = function (blockArray, width, height, showDisable) {
if (typeof blockArray == 'string') {
var floorId = blockArray;
blockArray = core.status.maps[floorId].blocks;
@ -313,11 +320,11 @@ maps.prototype.getMapArray = function (blockArray, width, height, checkDisable)
blockArray.forEach(function (block) {
var x = block.x, y = block.y;
if (block.disable) {
if (checkDisable) blocks[y][x] = block.id + ":f";
if (showDisable) blocks[y][x] = block.id + ":f";
}
else {
blocks[y][x] = block.id;
if (checkDisable && block.disable === false)
if (showDisable && block.disable === false)
blocks[y][x] = block.id + ":t";
}
});
@ -336,7 +343,7 @@ maps.prototype.getMapBlocksObj = function (floorId, showDisable) {
}
////// 将背景前景层变成二维数组的形式 //////
maps.prototype.getBgFgMapArray = function (name, floorId, noCache) {
maps.prototype._getBgFgMapArray = function (name, floorId, noCache) {
floorId = floorId || core.status.floorId;
if (!floorId) return [];
var width = core.floors[floorId].width;
@ -362,10 +369,26 @@ maps.prototype.getBgFgMapArray = function (name, floorId, noCache) {
return arr;
}
maps.prototype.getBgFgNumber = function (name, x, y, floorId, noCache) {
maps.prototype.getBgMapArray = function (floorId, noCache) {
return this._getBgFgMapArray('bg', floorId, noCache);
}
maps.prototype.getFgMapArray = function (floorId, noCache) {
return this._getBgFgMapArray('fg', floorId, noCache);
}
maps.prototype._getBgFgNumber = function (name, x, y, floorId, noCache) {
if (x == null) x = core.getHeroLoc('x');
if (y == null) y = core.getHeroLoc('y');
return this.getBgFgMapArray(name, floorId, noCache)[y][x];
return this._getBgFgMapArray(name, floorId, noCache)[y][x];
}
maps.prototype.getBgNumber = function (x, y, floorId, noCache) {
return this._getBgFgNumber('bg', x, y, floorId, noCache);
}
maps.prototype.getFgNumber = function (x, y, floorId, noCache) {
return this._getBgFgNumber('fg', x, y, floorId, noCache);
}
// ------ 当前能否朝某方向移动,能否瞬间移动 ------ //
@ -375,8 +398,8 @@ maps.prototype.generateMovableArray = function (floorId, x, y, direction) {
floorId = floorId || core.status.floorId;
if (!floorId) return null;
var width = core.floors[floorId].width, height = core.floors[floorId].height;
var bgArray = this.getBgFgMapArray('bg', floorId),
fgArray = this.getBgFgMapArray('fg', floorId),
var bgArray = this.getBgMapArray(floorId),
fgArray = this.getFgMapArray(floorId),
eventArray = this.getMapArray(floorId);
var generate = function (x, y, direction) {
@ -489,7 +512,7 @@ maps.prototype._canMoveDirectly_bfs = function (sx, sy, ex, ey) {
var canMoveArray = this.generateMovableArray();
var blocksObj = this.getMapBlocksObj(core.status.floorId);
// 滑冰
var bgMap = this.getBgFgMapArray('bg');
var bgMap = this.getBgMapArray();
var visited = [], queue = [];
visited[sx + "," + sy] = 0;
@ -685,7 +708,7 @@ maps.prototype._drawMap_drawAll = function (floorId) {
maps.prototype._drawMap_drawBlockInfo = function (ctx, block, blockInfo, arr, onMap) {
if (blockInfo == null) return;
if (blockInfo.cls == 'autotile') { // Autotile单独处理
this.drawAutotile(ctx, arr, block, 32, 0, 0);
this._drawAutotile(ctx, arr, block, 32, 0, 0);
if (onMap) this.addGlobalAnimate(block);
return;
}
@ -700,15 +723,16 @@ maps.prototype._drawMap_drawBlockInfo = function (ctx, block, blockInfo, arr, on
////// 绘制背景层 //////
maps.prototype.drawBg = function (floorId, ctx) {
floorId = floorId || core.status.floorId;
var onMap = ctx == null;
if (onMap) {
ctx = core.canvas.bg;
core.clearMap(ctx);
}
this._drawBg_drawBackground(floorId, ctx);
core.maps._drawBg_drawBackground(floorId, ctx);
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。
this._drawFloorImages(floorId, ctx, 'bg');
this._drawBgFgMap(floorId, ctx, 'bg', onMap);
core.maps._drawFloorImages(floorId, ctx, 'bg');
core.maps._drawBgFgMap(floorId, ctx, 'bg', onMap);
}
maps.prototype._drawBg_drawBackground = function (floorId, ctx) {
@ -728,7 +752,7 @@ maps.prototype._drawBg_drawBackground = function (floorId, ctx) {
maps.prototype.drawEvents = function (floorId, blocks, ctx) {
floorId = floorId || core.status.floorId;
if (!blocks) blocks = core.status.maps[floorId].blocks;
var arr = this.getMapArray(blocks, core.floors[floorId].width, core.floors[floorId].height);
var arr = this._getMapArrayFromBlocks(blocks, core.floors[floorId].width, core.floors[floorId].height);
var onMap = ctx == null;
if (onMap) ctx = core.canvas.event;
blocks.filter(function (block) {
@ -741,6 +765,7 @@ maps.prototype.drawEvents = function (floorId, blocks, ctx) {
////// 绘制前景层 //////
maps.prototype.drawFg = function (floorId, ctx) {
floorId = floorId || core.status.floorId;
var onMap = ctx == null;
if (onMap) ctx = core.canvas.fg;
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。
@ -758,7 +783,7 @@ maps.prototype._drawBgFgMap = function (floorId, ctx, name, onMap) {
if (!core.status[name + "maps"])
core.status[name + "maps"] = {};
var arr = this.getBgFgMapArray(name, floorId, true);
var arr = this._getBgFgMapArray(name, floorId, true);
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var block = this.initBlock(x, y, arr[y][x], true);
@ -850,7 +875,7 @@ maps.prototype._drawFloorImage = function (ctx, name, type, image, offsetX, widt
}
////// 绘制Autotile //////
maps.prototype.drawAutotile = function (ctx, mapArr, block, size, left, top, status) {
maps.prototype._drawAutotile = function (ctx, mapArr, block, size, left, top, status) {
var indexArrs = [ //16种组合的图块索引数组; // 将autotile分割成48块16*16的小块; 数组索引即对应各个小块
// +----+----+----+----+----+----+
[10, 9, 4, 3], //0 bin:0000 | 1 | 2 | 3 | 4 | 5 | 6 |
@ -1215,7 +1240,7 @@ maps.prototype.searchBlock = function (id, floorId, showDisable) {
for (var i = 0; i < core.status.maps[floorId].blocks.length; ++i) {
var block = core.status.maps[floorId].blocks[i];
if (block.event.id == id && (showDisable || !block.disable))
result.push({floorId: floorId, index: i, block: block});
result.push({floorId: floorId, index: i, block: block, x: block.x, y: block.y});
}
return result;
}
@ -1301,19 +1326,6 @@ maps.prototype.removeBlockById = function (index, floorId) {
}
}
////// 能否彻底从地图中删除一个图块 //////
maps.prototype.canRemoveBlock = function (block, floorId) {
var x = block.x, y = block.y;
// 检查该点是否存在事件
if (core.floors[floorId].events[x + "," + y] || core.floors[floorId].changeFloor[x + "," + y])
return false;
// 检查是否存在重生
if (block.event && block.event.cls.indexOf('enemy') == 0 && core.hasSpecial(block.event.id, 23))
return false;
return true;
}
////// 一次性删除多个block //////
maps.prototype.removeBlockByIds = function (floorId, ids) {
floorId = floorId || core.status.floorId;
@ -1325,24 +1337,17 @@ maps.prototype.removeBlockByIds = function (floorId, ids) {
});
}
////// 将地图中所有某个图块替换成另一个图块 //////
maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) {
floorId = floorId || core.status.floorId;
if (floorId instanceof Array) {
floorId.forEach(function (floorId) {
core.replaceBlock(fromNumber, toNumber, floorId);
});
return;
}
var toBlock = this.initBlock(0, 0, toNumber, true);
core.status.maps[floorId].blocks.forEach(function (block) {
if (block.id == fromNumber) {
block.id = toNumber;
for (var one in toBlock.event) {
block.event[one] = core.clone(toBlock.event[one]);
}
}
});
////// 能否彻底从地图中删除一个图块 //////
maps.prototype.canRemoveBlock = function (block, floorId) {
var x = block.x, y = block.y;
// 检查该点是否存在事件
if (core.floors[floorId].events[x + "," + y] || core.floors[floorId].changeFloor[x + "," + y])
return false;
// 检查是否存在重生
if (block.event && block.event.cls.indexOf('enemy') == 0 && core.hasSpecial(block.event.id, 23))
return false;
return true;
}
////// 显示前景/背景地图 //////
@ -1420,6 +1425,7 @@ maps.prototype.setBlock = function (number, x, y, floorId) {
floorId = floorId || core.status.floorId;
if (!floorId || number == null || x == null || y == null) return;
if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return;
if (typeof number == 'string') number = core.getNumberById(number);
var originBlock = core.getBlock(x, y, floorId, true);
var block = this.initBlock(x, y, number, true, core.floors[floorId]);
@ -1447,6 +1453,26 @@ maps.prototype.setBlock = function (number, x, y, floorId) {
}
}
////// 将地图中所有某个图块替换成另一个图块 //////
maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) {
floorId = floorId || core.status.floorId;
if (floorId instanceof Array) {
floorId.forEach(function (floorId) {
core.replaceBlock(fromNumber, toNumber, floorId);
});
return;
}
var toBlock = this.initBlock(0, 0, toNumber, true);
core.status.maps[floorId].blocks.forEach(function (block) {
if (block.id == fromNumber) {
block.id = toNumber;
for (var one in toBlock.event) {
block.event[one] = core.clone(toBlock.event[one]);
}
}
});
}
////// 改变前景背景的图块 //////
maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) {
floorId = floorId || core.status.floorId;
@ -1629,10 +1655,8 @@ maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, callback) {
var block = blockArr[0], blockInfo = blockArr[1];
var canvases = this._initDetachedBlock(blockInfo, sx, sy, block.event.animate !== false);
this._moveDetachedBlock(blockInfo, 32 * sx, 32 * sy, 1, canvases);
this.__playJumpSound();
var jumpInfo = ths.__generateJumpInfo(sx, sy, ex, ey, time);
core.playSound('jump.mp3');
var jumpInfo = this.__generateJumpInfo(sx, sy, ex, ey, time);
jumpInfo.keep = keep;
this._jumpBlock_doJump(blockInfo, canvases, jumpInfo, callback);
@ -1644,14 +1668,10 @@ maps.prototype.__generateJumpInfo = function (sx, sy, ex, ey, time) {
return {
x: sx, y: sy, ex: ex, ey: ey, px: 32 * sx, py: 32 * sy, opacity: 1,
jump_peak: jump_peak, jump_count: jump_count,
step: 0, per_time: time / 16 / core.status.replay.speed
step: 0, per_time: time / jump_count
};
}
maps.prototype.__playJumpSound = function () {
core.playSound('jump.mp3');
}
maps.prototype._jumpBlock_doJump = function (blockInfo, canvases, jumpInfo, callback) {
var animate = window.setInterval(function () {
if (jumpInfo.jump_count > 0)
@ -1766,16 +1786,16 @@ maps.prototype._animateBlock_drawList = function (list, opacity) {
// ------ 全局动画控制,动画绘制 ------ //
////// 添加一个全局动画 //////
maps.prototype.addGlobalAnimate = function (b) {
if (!b.event || b.event.animate == null) return;
if (b.event.cls == 'autotile') {
var id = b.event.id, img = core.material.images.autotile[id];
maps.prototype.addGlobalAnimate = function (block) {
if (!block.event || block.event.animate == null) return;
if (block.event.cls == 'autotile') {
var id = block.event.id, img = core.material.images.autotile[id];
if (!img || img.width == 96) return;
core.status.autotileAnimateObjs.blocks.push(b);
core.status.autotileAnimateObjs.blocks.push(block);
}
else {
if (!b.event.animate || b.event.animate == 1) return;
core.status.globalAnimateObjs.push(b);
if (!block.event.animate || block.event.animate == 1) return;
core.status.globalAnimateObjs.push(block);
}
}

View File

@ -23,12 +23,13 @@ ui.prototype._init = function () {
////////////////// 地图设置
ui.prototype.getContextByName = function (canvas) {
if (typeof canvas == 'string') {
if (core.canvas[canvas])
canvas = core.canvas[canvas];
else if (core.dymCanvas[canvas])
canvas = core.dymCanvas[canvas];
ui.prototype.getContextByName = function (name) {
var canvas = name;
if (typeof name == 'string') {
if (core.canvas[name])
canvas = core.canvas[name];
else if (core.dymCanvas[name])
canvas = core.dymCanvas[name];
}
if (canvas && canvas.canvas) {
return canvas;
@ -190,6 +191,36 @@ ui.prototype.calWidth = function (name, text, font) {
return 0;
}
////// 字符串自动换行的分割 //////
ui.prototype.splitLines = function (name, text, maxWidth, font) {
var ctx = this.getContextByName(name);
if (!ctx) return;
if (font) core.setFont(name, font);
var contents = [];
var last = 0;
for (var i = 0; i < text.length; i++) {
if (text.charAt(i) == '\n') {
contents.push(text.substring(last, i));
last = i + 1;
}
else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') {
contents.push(text.substring(last, i));
last = i + 2;
}
else {
var toAdd = text.substring(last, i + 1);
var width = core.calWidth(name, toAdd);
if (maxWidth && width > maxWidth) {
contents.push(text.substring(last, i));
last = i;
}
}
}
contents.push(text.substring(last));
return contents;
}
////// 绘制一张图片 //////
ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1) {
var ctx = this.getContextByName(name);
@ -233,25 +264,23 @@ ui.prototype.clearUI = function () {
clearInterval(core.status.event.interval);
core.status.event.interval = null;
core.status.boxAnimateObjs = [];
core.clearSelector();
core.clearMap('ui');
core.setAlpha('ui', 1);
}
////// 清除光标 //////
ui.prototype.clearSelector = function () {
if (core.dymCanvas._selector) core.deleteCanvas("_selector");
core.clearMap('ui');
core.setAlpha('ui', 1);
}
////// 左上角绘制一段提示 //////
ui.prototype.drawTip = function (text, itemIcon) {
ui.prototype.drawTip = function (text, id) {
var textX, textY, width, height;
clearInterval(core.interval.tipAnimate);
core.setFont('data', "16px Arial");
core.setTextAlign('data', 'left');
if (!itemIcon) {
if (id != null) {
var info = core.getBlockInfo(id);
if (info == null || !info.image || info.height != 32) id = null;
else id = info;
}
if (!id) {
textX = 16;
textY = 18;
width = textX + core.calWidth('data', text) + 16;
@ -263,10 +292,10 @@ ui.prototype.drawTip = function (text, itemIcon) {
width = textX + core.calWidth('data', text) + 8;
height = 42;
}
this._drawTip_animate(text, itemIcon, textX, textY, width, height);
this._drawTip_animate(text, id, textX, textY, width, height);
}
ui.prototype._drawTip_animate = function (text, itemIcon, textX, textY, width, height) {
ui.prototype._drawTip_animate = function (text, info, textX, textY, width, height) {
var alpha = 0, hide = false;
core.interval.tipAnimate = window.setInterval(function () {
if (hide) alpha -= 0.1;
@ -274,8 +303,8 @@ ui.prototype._drawTip_animate = function (text, itemIcon, textX, textY, width, h
core.clearMap('data', 5, 5, core.ui.PIXEL, height);
core.setAlpha('data', alpha);
core.fillRect('data', 5, 5, width, height, '#000');
if (itemIcon)
core.drawImage('data', core.material.images.items, 0, itemIcon * 32, 32, 32, 10, 8, 32, 32);
if (info)
core.drawImage('data', info.image, info.posX * 32, info.posY * 32, 32, 32, 10, 8, 32, 32);
core.fillText('data', text, textX + 5, textY + 15, '#fff');
core.setAlpha('data', 1);
if (alpha > 0.6 || alpha < 0) {
@ -507,13 +536,13 @@ ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) {
}
////// 计算有效文本框的宽度
ui.prototype.calTextBoxWidth = function (ctx, content, min_width, max_width, font) {
ui.prototype._calTextBoxWidth = function (ctx, content, min_width, max_width, font) {
// 无限长度自动换行
var allLines = core.splitLines(ctx, content, null, font);
// 如果不存在手动换行,尽量调成半行形式
if (allLines.length == 1) {
var w = core.calWidth(ctx, allLines[0]);
var w = core.calWidth(ctx, allLines[0]) + 10;
if (w<min_width*2.3) return core.clamp(w / 1.4, min_width, max_width);
if (w<max_width*2.2) return core.clamp(w / 2.4, min_width, max_width);
return core.clamp(w / 3.4, min_width, max_width);
@ -521,7 +550,7 @@ ui.prototype.calTextBoxWidth = function (ctx, content, min_width, max_width, fon
// 存在手动换行:以最长的为准
else {
return core.clamp(allLines.reduce(function (pre, curr) {
return Math.max(pre, core.calWidth(ctx, curr))
return Math.max(pre, core.calWidth(ctx, curr) + 10);
}, 0), min_width, max_width);
}
}
@ -794,7 +823,7 @@ ui.prototype._drawTextBox_getHorizontalPosition = function (content, titleInfo,
var min_width = 220 - paddingLeft, max_width = validWidth;
// 无行走图或头像则可以适当缩小min_width
if (titleInfo.image == null) min_width = 160;
validWidth = this.calTextBoxWidth('ui', realContent, min_width, max_width, this._buildFont());
validWidth = this._calTextBoxWidth('ui', realContent, min_width, max_width, this._buildFont());
width = validWidth + paddingLeft + paddingRight;
left = core.clamp(32 * posInfo.px + 16 - width / 2 - core.bigmap.offsetX, left, right - width);
right = left + width;
@ -1052,9 +1081,13 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) {
core.lockControl();
text = core.replaceText(text || "");
core.status.event.id = 'confirmBox';
core.status.event.data = {'yes': yesCallback, 'no': noCallback};
core.status.event.ui = text;
// 处理自定义事件
if (core.status.event.id != 'action') {
core.status.event.id = 'confirmBox';
core.status.event.ui = text;
core.status.event.data = {'yes': yesCallback, 'no': noCallback};
}
if (core.status.event.selection != 0) core.status.event.selection = 1;
this.clearUI();
@ -1090,6 +1123,20 @@ ui.prototype._drawConfirmBox_getRect = function (contents) {
return { top: top, left: left, bottom: bottom, right: right, width: right - left, height: bottom - top };
}
////// 绘制等待界面 //////
ui.prototype.drawWaiting = function(text) {
core.lockControl();
core.status.event.id = 'waiting';
core.clearUI();
text = core.replaceText(text || "");
var text_length = core.calWidth('ui', text, this._buildFont(19, true));
var width = Math.max(text_length + 80, 220), left = this.HPIXEL - parseInt(width / 2), right = left + width;
var top = this.HPIXEL - 48, height = 96, bottom = top + height;
this.drawBackground(left, top, right, bottom);
core.setTextAlign('ui', 'center');
core.fillText('ui', text, this.HPIXEL, top + 56, core.arrayToRGBA(core.status.textAttribute.text));
}
////// 绘制系统设置界面 //////
ui.prototype.drawSwitchs = function() {
core.status.event.id = 'switchs';
@ -1128,20 +1175,6 @@ ui.prototype.drawQuickShop = function () {
this.drawChoices(null, choices);
}
////// 绘制等待界面 //////
ui.prototype.drawWaiting = function(text) {
core.lockControl();
core.status.event.id = 'waiting';
core.clearSelector();
text = core.replaceText(text || "");
var text_length = core.calWidth('ui', text, this._buildFont(19, true));
var width = Math.max(text_length + 80, 220), left = this.HPIXEL - parseInt(width / 2), right = left + width;
var top = this.HPIXEL - 48, height = 96, bottom = top + height;
this.drawBackground(left, top, right, bottom);
core.setTextAlign('ui', 'center');
core.fillText('ui', text, this.HPIXEL, top + 56, core.arrayToRGBA(core.status.textAttribute.text));
}
////// 绘制存档同步界面 //////
ui.prototype.drawSyncSave = function () {
core.status.event.id = 'syncSave';
@ -1190,22 +1223,22 @@ ui.prototype.drawGameInfo = function () {
}
////// 绘制分页 //////
ui.prototype.drawPagination = function (page, totalPage, top) {
ui.prototype.drawPagination = function (page, totalPage, y) {
// if (totalPage<page) totalPage=page;
if (totalPage <= 1) return;
if (top == null) top = this.LAST;
if (y == null) y = this.LAST;
core.setFillStyle('ui', '#DDDDDD');
var length = core.calWidth('ui', page + " / " + page, this._buildFont(15, true));
core.setTextAlign('ui', 'left');
core.fillText('ui', page + " / " + totalPage, parseInt((this.PIXEL - length) / 2), top*32+19);
core.fillText('ui', page + " / " + totalPage, parseInt((this.PIXEL - length) / 2), y*32+19);
core.setTextAlign('ui', 'center');
if (page > 1)
core.fillText('ui', '上一页', this.HPIXEL - 80, top*32+19);
core.fillText('ui', '上一页', this.HPIXEL - 80, y*32+19);
if (page < totalPage)
core.fillText('ui', '下一页', this.HPIXEL + 80, top*32+19);
core.fillText('ui', '下一页', this.HPIXEL + 80, y*32+19);
}
////// 绘制键盘光标 //////
@ -1254,6 +1287,7 @@ ui.prototype.drawBook = function (index) {
core.drawBoxAnimate();
this.drawPagination(page, totalPage);
core.setTextAlign('ui', 'center');
core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD', this._buildFont(15, true));
}
@ -1632,7 +1666,7 @@ ui.prototype.drawMaps = function (index, x, y) {
var offsetX = 32 * (data.x - this.HSIZE), offsetY = 32 * (data.y - this.HSIZE);
var value = core.paint[data.floorId];
if (value) value = lzw_decode(value).split(",");
core.utils.decodeCanvas(value, 32 * data.mw, 32 * data.mh);
core.utils._decodeCanvas(value, 32 * data.mw, 32 * data.mh);
core.drawImage('ui', core.bigmap.tempCanvas.canvas, offsetX * 32, offsetY * 32,
this.PIXEL, this.PIXEL, 0, 0, this.PIXEL, this.PIXEL);
}
@ -2358,13 +2392,13 @@ ui.prototype._drawPaint_draw = function () {
core.status.event.id = 'paint';
core.status.event.data = {"x": null, "y": null, "erase": false};
core.clearSelector();
core.clearUI();
core.createCanvas('paint', -core.bigmap.offsetX, -core.bigmap.offsetY, 32*core.bigmap.width, 32*core.bigmap.height, 95);
// 将已有的内容绘制到route上
var value = core.paint[core.status.floorId];
if (core.isset(value)) value = lzw_decode(value).split(",");
core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height);
core.utils._decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height);
core.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0);
core.setLineWidth('paint', 3);
@ -2383,7 +2417,7 @@ ui.prototype._drawPaint_draw = function () {
////// 绘制帮助页面 //////
ui.prototype.drawHelp = function () {
core.clearSelector();
core.clearUI();
if (core.material.images.keyboard) {
core.status.event.id = 'help';
core.lockControl();

View File

@ -75,35 +75,6 @@ utils.prototype.calValue = function (value, prefix, need, times) {
return value;
}
////// 字符串自动换行的分割 //////
utils.prototype.splitLines = function (canvas, text, maxLength, font) {
if (font) core.setFont(canvas, font);
var contents = [];
var last = 0;
for (var i = 0; i < text.length; i++) {
if (text.charAt(i) == '\n') {
contents.push(text.substring(last, i));
last = i + 1;
}
else if (text.charAt(i) == '\\' && text.charAt(i + 1) == 'n') {
contents.push(text.substring(last, i));
last = i + 2;
}
else {
var toAdd = text.substring(last, i + 1);
var width = core.calWidth(canvas, toAdd);
if (maxLength && width > maxLength) {
contents.push(text.substring(last, i));
last = i;
}
}
}
contents.push(text.substring(last));
return contents;
}
////// 向某个数组前插入另一个数组或元素 //////
utils.prototype.unshift = function (a, b) {
if (!(a instanceof Array) || b == null) return;
@ -128,6 +99,28 @@ utils.prototype.push = function (a, b) {
return a;
}
utils.prototype.decompress = function (value) {
try {
var output = lzw_decode(value);
if (output) return JSON.parse(output);
}
catch (e) {
}
try {
var output = LZString.decompress(value);
if (output) return JSON.parse(output);
}
catch (e) {
}
try {
return JSON.parse(value);
}
catch (e) {
main.log(e);
}
return null;
}
////// 设置本地存储 //////
utils.prototype.setLocalStorage = function (key, value) {
try {
@ -163,28 +156,6 @@ utils.prototype.setLocalStorage = function (key, value) {
}
}
utils.prototype.decompress = function (value) {
try {
var output = lzw_decode(value);
if (output) return JSON.parse(output);
}
catch (e) {
}
try {
var output = LZString.decompress(value);
if (output) return JSON.parse(output);
}
catch (e) {
}
try {
return JSON.parse(value);
}
catch (e) {
main.log(e);
}
return null;
}
////// 获得本地存储 //////
utils.prototype.getLocalStorage = function (key, defaultValue) {
var res = this.decompress(localStorage.getItem(core.firstData.name + "_" + key));
@ -475,7 +446,7 @@ utils.prototype._encodeRoute_encodeOne = function (t) {
return 'K' + t.substring(4);
else if (t.indexOf('random:') == 0)
return 'X' + t.substring(7);
return '';
return '('+t+')';
}
////// 解密路线 //////
@ -485,8 +456,9 @@ utils.prototype.decodeRoute = function (route) {
// 解压缩
try {
var v = LZString.decompressFromBase64(route);
if (/^[a-zA-Z0-9+\/=:]*$/.test(v)) {
route = v;
if (v != null && /^[-_a-zA-Z0-9+\/=:()]*$/.test(v)) {
if (v != "" || route.length < 8)
route = v;
}
} catch (e) {
}
@ -525,6 +497,15 @@ utils.prototype._decodeRoute_number2id = function (number) {
}
utils.prototype._decodeRoute_decodeOne = function (decodeObj, c) {
// --- 特殊处理自定义项
if (c == '(') {
var idx = decodeObj.route.indexOf(')', decodeObj.index);
if (idx >= 0) {
decodeObj.ans.push(decodeObj.route.substring(decodeObj.index, idx));
decodeObj.index = idx + 1;
return;
}
}
var nxt = (c == 'I' || c == 'e' || c == 'F' || c == 'S' || c == 'Q' || c == 't') ?
this._decodeRoute_getString(decodeObj) : this._decodeRoute_getNumber(decodeObj);
@ -618,6 +599,7 @@ utils.prototype.getCookie = function (name) {
////// 设置statusBar的innerHTML会自动斜体和放缩也可以增加自定义css //////
utils.prototype.setStatusBarInnerHTML = function (name, value, css) {
if (!core.statusBar[name]) return;
if (typeof value == 'number') value = this.formatBigNumber(value);
// 判定是否斜体
var italic = /^[-a-zA-Z0-9`~!@#$%^&*()_=+\[{\]}\\|;:'",<.>\/?]*$/.test(value);
@ -921,7 +903,7 @@ utils.prototype.myprompt = function (hint, value, callback) {
}
////// 动画显示某对象 //////
utils.prototype.show = function (obj, speed, callback) {
utils.prototype.showWithAnimate = function (obj, speed, callback) {
obj.style.display = 'block';
if (!speed && main.mode != 'play') {
obj.style.opacity = 1;
@ -941,7 +923,7 @@ utils.prototype.show = function (obj, speed, callback) {
}
////// 动画使某对象消失 //////
utils.prototype.hide = function (obj, speed, callback) {
utils.prototype.hideWithAnimate = function (obj, speed, callback) {
if (!speed || main.mode != 'play') {
obj.style.display = 'none';
if (callback) callback();
@ -960,7 +942,7 @@ utils.prototype.hide = function (obj, speed, callback) {
}, speed);
}
utils.prototype.encodeCanvas = function (ctx) {
utils.prototype._encodeCanvas = function (ctx) {
var list = [];
var width = ctx.canvas.width, height = ctx.canvas.height;
ctx.mozImageSmoothingEnabled = false;
@ -987,7 +969,7 @@ utils.prototype.encodeCanvas = function (ctx) {
}
////// 解析arr数组并绘制到tempCanvas上 //////
utils.prototype.decodeCanvas = function (arr, width, height) {
utils.prototype._decodeCanvas = function (arr, width, height) {
// 清空tempCanvas
var tempCanvas = core.bigmap.tempCanvas;
tempCanvas.canvas.width = width;
@ -1070,7 +1052,7 @@ utils.prototype._export = function (floorIds) {
// map
var content = floorIds.length + "\n" + core.__SIZE__ + " " + core.__SIZE__ + "\n\n";
floorIds.forEach(function (floorId) {
var arr = core.maps.getMapArray(core.status.maps[floorId].blocks);
var arr = core.maps._getMapArrayFromBlocks(core.status.maps[floorId].blocks);
content += arr.map(function (x) {
// check monster
x.forEach(function (t) {

View File

@ -364,7 +364,7 @@ main.dom.body.onselectstart = function () {
main.dom.data.onmousedown = function (e) {
try {
e.stopPropagation();
var loc = main.core.getClickLoc(e.clientX, e.clientY);
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.ondown(loc);
} catch (ee) { main.log(ee); }
@ -374,7 +374,7 @@ main.dom.data.onmousedown = function (e) {
main.dom.data.onmousemove = function (e) {
try {
e.stopPropagation();
var loc = main.core.getClickLoc(e.clientX, e.clientY);
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.onmove(loc);
}catch (ee) { main.log(ee); }
@ -401,7 +401,7 @@ main.dom.data.onmousewheel = function(e) {
main.dom.data.ontouchstart = function (e) {
try {
e.preventDefault();
var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
if (loc == null) return;
main.core.ondown(loc);
}catch (ee) { main.log(ee); }
@ -411,7 +411,7 @@ main.dom.data.ontouchstart = function (e) {
main.dom.data.ontouchmove = function (e) {
try {
e.preventDefault();
var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
if (loc == null) return;
main.core.onmove(loc);
}catch (ee) { main.log(ee); }

View File

@ -78,6 +78,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"lv": 1,
"hpmax": 9999,
"hp": 1000,
"manamax": -1,
"mana": 0,
"atk": 100,
"def": 100,

View File

@ -223,7 +223,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 =
},
{
"type": "if",
"condition": "core.getBgFgNumber('bg') == 167",
"condition": "core.getBgNumber() == 167",
"true": [
{
"type": "function",

View File

@ -151,7 +151,7 @@ main.floors.sample1=
"type": "hide"
},
{
"type": "setFg",
"type": "setCurtain",
"color": [
0,
0,
@ -184,7 +184,7 @@ main.floors.sample1=
"2,11": [
"\t[杰克,thief]喂!醒醒!快醒醒!",
{
"type": "setFg",
"type": "setCurtain",
"time": 1500
},
"\t[hero]额,我这是在什么地方?",

View File

@ -6,6 +6,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
// hero勇士信息hard难度floorId当前楼层IDmaps地图信息values全局数值信息
// 清除游戏数据
// 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布
core.clearStatus();
// 初始化status
core.status = core.clone(core.initStatus);
@ -753,152 +754,156 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
// keyCode当前按键的keyCode每个键的keyCode自行百度
// altKeyAlt键是否被按下为true代表同时按下了Alt键
// 可以在这里任意增加或编辑每个按键的行为
// 如果处于正在行走状态,则不处理
if (!core.status.heroStop || core.status.heroMoving > 0)
if (core.isMoving())
return;
// Alt+0~9快捷换上套装
if (altKey && keyCode>=48 && keyCode<=57) {
core.items.quickLoadEquip(keyCode-48);
if (altKey && keyCode >= 48 && keyCode <= 57) {
core.items.quickLoadEquip(keyCode - 48);
return;
}
// 根据keyCode值来执行对应操作
switch (keyCode) {
case 27: // ESC打开菜单栏
core.openSettings(true);
break;
case 88: // X使用怪物手册
core.openBook(true);
break;
case 71: // G使用楼传器
core.useFly(true);
break;
case 65: // A读取自动存档回退
core.doSL("autoSave", "load");
break;
case 83: // S存档
core.save(true);
break;
case 68: // D读档
core.load(true);
break;
case 69: // E打开光标
core.ui.drawCursor();
break;
case 84: // T打开道具栏
core.openToolbox(true);
break;
case 81: // Q打开装备栏
core.openEquipbox(true);
break;
case 90: // Z转向
core.turnHero();
break;
case 75: case 86: // V打开快捷商店列表
core.openQuickShop(true);
break;
case 32: // SPACE轻按
core.getNextItem();
break;
case 82: // R回放录像
core.actions._clickSyncSave_replay();
break;
case 33: case 34: // PgUp/PgDn浏览地图
core.ui.drawMaps();
break;
case 77: // M绘图模式
core.ui.drawPaint();
break;
case 66: // B打开数据统计
core.ui.drawStatistics();
break;
case 72: // H打开帮助页面
core.ui.drawHelp();
break;
case 78: // N重新开始
core.confirmRestart();
break;
case 79: // O查看工程
core.actions._clickGameInfo_openProject();
break;
case 80: // P游戏主页
core.actions._clickGameInfo_openComments();
break;
case 49: // 快捷键1: 破
if (core.hasItem('pickaxe')) {
if (core.canUseItem('pickaxe')) {
core.useItem('pickaxe');
}
else {
core.drawTip('当前不能使用破墙镐');
}
case 27: // ESC打开菜单栏
core.openSettings(true);
break;
case 88: // X使用怪物手册
core.openBook(true);
break;
case 71: // G使用楼传器
core.useFly(true);
break;
case 65: // A读取自动存档回退
core.doSL("autoSave", "load");
break;
case 83: // S存档
core.save(true);
break;
case 68: // D读档
core.load(true);
break;
case 69: // E打开光标
core.ui.drawCursor();
break;
case 84: // T打开道具栏
core.openToolbox(true);
break;
case 81: // Q打开装备栏
core.openEquipbox(true);
break;
case 90: // Z转向
core.turnHero();
break;
case 75:
case 86: // V打开快捷商店列表
core.openQuickShop(true);
break;
case 32: // SPACE轻按
core.getNextItem();
break;
case 82: // R回放录像
core.actions._clickSyncSave_replay();
break;
case 33:
case 34: // PgUp/PgDn浏览地图
core.ui.drawMaps();
break;
case 77: // M绘图模式
core.ui.drawPaint();
break;
case 66: // B打开数据统计
core.ui.drawStatistics();
break;
case 72: // H打开帮助页面
core.ui.drawHelp();
break;
case 78: // N重新开始
core.confirmRestart();
break;
case 79: // O查看工程
core.actions._clickGameInfo_openProject();
break;
case 80: // P游戏主页
core.actions._clickGameInfo_openComments();
break;
case 49: // 快捷键1: 破
if (core.hasItem('pickaxe')) {
if (core.canUseItem('pickaxe')) {
core.status.route.push("key:49"); // 将按键记在录像中
core.useItem('pickaxe', true); // 第二个参数true代表该次使用道具是被按键触发的使用过程不计入录像
} else {
core.drawTip('当前不能使用破墙镐');
}
break;
case 50: // 快捷键2: 炸
if (core.hasItem('bomb')) {
if (core.canUseItem('bomb')) {
core.useItem('bomb');
}
else {
core.drawTip('当前不能使用炸弹');
}
}
break;
case 50: // 快捷键2: 炸
if (core.hasItem('bomb')) {
if (core.canUseItem('bomb')) {
core.status.route.push("key:50"); // 将按键记在录像中
core.useItem('bomb', true); // 第二个参数true代表该次使用道具是被按键触发的使用过程不计入录像
} else {
core.drawTip('当前不能使用炸弹');
}
else if (core.hasItem('hammer')) {
if (core.canUseItem('hammer')) {
core.useItem('hammer');
}
else {
core.drawTip('当前不能使用圣锤');
}
}
break;
case 51: // 快捷键3: 飞
if (core.hasItem('centerFly')) {
core.ui.drawCenterFly();
}
break;
case 52: // 快捷键4破冰/冰冻/地震/上下楼器/... 其他道具依次判断
{
var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"];
for (var i=0;i<list.length;i++) {
var itemId = list[i];
if (core.canUseItem(itemId)) {
core.useItem(itemId);
break;
}
}
}
break;
case 55: // 快捷键7绑定为轻按方便手机版操作
core.getNextItem();
break;
case 118: // F7开启debug模式
core.debug();
break;
case 87: // W开启技能“二倍斩”
// 检测是否拥有“二倍斩”这个技能道具
if (core.hasItem('skill1')) {
core.useItem('skill1');
}
break;
// 在这里可以任意新增或编辑已有的快捷键内容
/*
case 0: // 使用该按键的keyCode
// 还可以再判定altKey是否被按下即 if (altKey) { ...
// ... 在这里写你要执行脚本
// **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为**
if (core.hasItem('...')) {
core.useItem('...');
} else if (core.hasItem('hammer')) {
if (core.canUseItem('hammer')) {
core.status.route.push("key:50"); // 将按键记在录像中
core.useItem('hammer', true); // 第二个参数true代表该次使用道具是被按键触发的使用过程不计入录像
} else {
core.drawTip('当前不能使用圣锤');
}
break;
*/
}
}
break;
case 51: // 快捷键3: 飞
if (core.hasItem('centerFly')) {
core.ui.drawCenterFly();
}
break;
case 52: // 快捷键4破冰/冰冻/地震/上下楼器/... 其他道具依次判断
{
var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"];
for (var i = 0; i < list.length; i++) {
var itemId = list[i];
if (core.canUseItem(itemId)) {
core.status.route.push("key:52");
core.useItem(itemId, true);
break;
}
}
}
break;
case 55: // 快捷键7绑定为轻按方便手机版操作
core.getNextItem();
break;
case 118: // F7开启debug模式
core.debug();
break;
case 87: // W开启技能“二倍斩”
// 检测是否拥有“二倍斩”这个技能道具
if (core.hasItem('skill1')) {
core.status.route.push("key:87");
core.useItem('skill1', true);
}
break;
// 在这里可以任意新增或编辑已有的快捷键内容
/*
case 0: // 使用该按键的keyCode
// 还可以再判定altKey是否被按下即 if (altKey) { ...
// ... 在这里写你要执行脚本
// **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为**
if (core.hasItem('...')) {
core.status.route.push("key:0");
core.useItem('...', true); // 增加true代表该使用道具不计入录像
}
break;
*/
}
}
},
"control": {
@ -1006,9 +1011,14 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
// 设置魔力值
if (core.flags.enableMana) {
// 也可以使用flag:manaMax来表示最大魔力值详见文档-个性化-技能塔的支持
// core.status.hero.mana = Math.max(core.status.hero.mana, core.getFlag('manaMax', 10));
// core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.getFlag('manaMax', 10));
// status:manamax 只有在非负时才生效。
if (core.status.hero.manamax != null && core.status.hero.manamax >= 0) {
core.status.hero.mana = Math.min(core.status.hero.mana, core.status.hero.manamax);
core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.status.hero.manamax);
}
else {
core.setStatusBarInnerHTML("mana", core.status.hero.mana);
}
}
// 设置技能栏
if (core.flags.enableSkill) {

View File

@ -1,18 +1,28 @@
var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
{
"init": function () {
// 在这里写的代码,在所有模块加载完毕后,游戏开始前会被执行
console.log("插件编写测试");
// 可以写一些其他的被直接执行的代码
// 可以写一些直接执行的代码
// 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。
// 请勿在这里对包括bgm图片等资源进行操作。
this.test = function () {
console.log("插件函数执行测试");
console.log(this);
this._afterLoadResources = function () {
// 本函数将在所有资源加载完毕后,游戏开启前被执行
// 可以在这个函数里面对资源进行一些操作,比如切分图片等。
// 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。
// var arr = core.splitImage("assets.png", 32, 32);
// for (var i = 0; i < arr.length; i++) {
// core.material.images.images["asset"+i+".png"] = arr[i];
// }
}
// 可以在任何地方如afterXXX或自定义脚本事件调用函数方法为 core.plugin.xxx();
// 可以在任何地方如afterXXX或自定义脚本事件调用函数方法为 core.plugin.xxx();
// 从V2.6开始插件中用this.XXX方式定义的函数也会被转发到core中详见文档-脚本-函数的转发。
},
"drawLight": function () {