diff --git a/README.md b/README.md index 066ff6c4..b81aa04b 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,12 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! │ ├─ /sounds/ # 音效目录 │ ├─ data.js # 全局变量信息 │ ├─ enemys.js # 怪物属性数据 +│ ├─ events.js # 公共事件 │ ├─ functions.js # 可能会被修改的脚本代码 │ ├─ icons.js # 素材和ID的对应关系定义 │ ├─ items.js # 道具的定义,获得道具的效果 -│ └─ maps.js # 地图和数字的对应关系 +│ ├─ maps.js # 地图和数字的对应关系 +│ └─ plugins.js # 自定义插件 ├── /常用工具/ # 一些常用工具,可以辅助造塔;具体可参见下面的【相关工具】 ├── editor.html # 可视化地图编辑工具 ├── editor-mobile.html # 可视化地图编辑工具(手机版) diff --git a/_docs/_api.md b/_docs/_api.md new file mode 100644 index 00000000..64362e3b --- /dev/null +++ b/_docs/_api.md @@ -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为目标楼层ID,stair为到达的目标楼梯,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为目标点的ID,x和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为怪物的id,x和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为要绘制楼层的floorId,callback为绘制完毕后的回调函数。 + + +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为道具ID,noRoute如果为真则该道具的使用不计入录像。 +callback为成功或失败后的回调。 + + +core.canUseItem(itemId) +返回当前能否使用某个道具。 + + +core.loadEquip(itemId, callback) +装备上某个装备。itemId为装备的ID,callback为成功或失败后的回调。 + + +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为目标楼层ID,canvas为要绘制到的图层,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为失败后的回调。 + +``` diff --git a/_docs/_sidebar.md b/_docs/_sidebar.md index a7960592..914ee883 100644 --- a/_docs/_sidebar.md +++ b/_docs/_sidebar.md @@ -3,5 +3,5 @@ - [元件说明](element) - [事件](event) - [个性化](personalization) -- [V2.0版本介绍](V2.0) +- [脚本](script) - [附录:API列表](api) diff --git a/_docs/api.md b/_docs/api.md index 88697135..346f4bd6 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1,219 +1,1444 @@ -# 附录: API列表 +# 附录:API列表 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * -**这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** +这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 + +本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 如有任何疑问,请联系小艾寻求帮助。 -可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 +## core.js -**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。** +core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 -!> 最常用的新手向命令,强烈建议每个人了解 +但是,core中定义了很多游戏运行时的状态,这些状态很多都会被使用到。 ``` text - -core.status.floorId -获得当前层的floorId。 +core.__SIZE__, core.__PIXELS__ +游戏窗口大小;对于13x13的游戏而言这两个值分别是13和416,15x15来说分别是15和480。 -core.status.maps -获得所有楼层的地图信息。 +core.material +游戏中的所有资源列表,具体分为如下内容: +core.material.animates (动画) +core.material.bgms (背景音乐) +core.material.enemys (怪物信息,来自于 project/enemys.js) +core.material.icons (图标信息,来自于 project/icons.js) +core.material.images (图片素材,存放了各项素材图片如items.png等) + core.material.images.autotile (所有的自动元件图片) + core.material.images.tilesets (所有的额外素材图片) + core.material.images.images (用户引入的其他图片) +core.material.items (道具信息) +core.material.sounds (音效) -core.status.thisMap -获得当前楼层信息,其等价于core.status.maps[core.status.floorId]。 +core.animateFrame +主要是记录和requestAnimationFrame相关的一些数据,常用的如下: +core.animateFrame.totalTime (游戏总的运行时时间) +core.animateFrame.weather (当前的天气信息) +core.animateFrame.asyncId (当前的异步处理事件的内容) + + +core.musicStatus +主要是记录和音效相关的内容,常用的如下: +core.musicStatus.bgmStatus (音乐开启状态) +core.musicStatus.soundStatus (音效开启状态) +core.musicStatus.playingBgm (当前正在播放的BGM) +core.musicStatus.lastBgm (最近一次尝试播放的BGM) +core.musicStatus.volume (当前的音量) +core.musicStatus.cachedBgms (背景音乐的缓存内容) +core.musicStatus.cacheBgmCount (背景音乐的缓存数量,默认值是4) + + +core.platform +游戏平台相关信息,常见的几个如下: +core.platform.isPC (是否是电脑端) +core.platform.isAndroid (是否是安卓端) +core.platform.isIOS (是否是iOS端) +core.platform.useLocalForage (是否开启了新版存档) +core.platform.extendKeyBoard (是否开启了拓展键盘) + + +core.domStyle +游戏的界面信息,包含如下几个: +core.domStyle.scale (当前的放缩比) +core.domStyle.isVertical (当前是否是竖屏状态) +core.domStyle.showStatusBar (当前是否显示状态栏) +core.domStyle.toolbarBtn (当前是否显示工具栏) + + +core.bigmap +当前的地图的尺寸信息,主要包含如下几个 +core.bigmap.width (当前地图的宽度) +core.bigmap.height (当前地图的高度) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素x) +core.bigmap.offsetX (当前地图针对窗口左上角的偏移像素y) +core.bigmap.tempCanvas (一个临时画布,可以用来临时绘制很多东西) + + +core.saves +和存档相关的信息,包含如下几个: +core.saves.saveIndex (上次保存或读取的存档编号) +core.saves.ids (当前存在存档的编号列表) +core.saves.autosave (自动存档的信息) +core.saves.favorite (收藏的存档) +core.saves.favoriteNames (自定义存档的名称) + + +core.status +游戏的状态相关,是整个游戏中最重要的东西,其核心是如下几条: +请注意,每次重新开始、存档或读档时,core.status都会重新初始化。 +core.status.played (当前是否在游戏中) +core.status.gameOver (当前是否已经游戏结束,即win或lose) +core.status.hero (勇士信息;此项和全塔属性中的hero大体是对应的) + core.status.hero.name 勇士名 + core.status.hero.lv 当前等级 + core.status.hero.hpmax 当前生命上限 + core.status.hero.hp 当前生命值 + core.status.hero.manamax 当前魔力上限 + core.status.hero.mana 当前魔力值 + core.status.hero.atk 当前攻击力 + core.status.hero.def 当前防御力 + core.status.hero.mdef 当前魔防值 + core.status.hero.money 当前金币值 + core.status.hero.experience 当前经验值 + core.status.hero.loc 当前的位置信息 + core.status.hero.equipment 当前装上的装备 + core.status.hero.items 当前拥有的道具信息 + core.status.hero.flags 当前的各项flag信息 + core.status.hero.step 当前的步数值 + core.status.hero.statistics 当前的统计信息 +core.status.floorId (当前所在的楼层) +core.status.maps (所有的地图信息) +core.status.thisMap (当前的地图信息,等价于core.status.maps[core.status.floorId]) +core.status.bgmaps (所有背景层的信息) +core.status.fgmaps (所有的前景层的信息) +core.status.checkBlock (地图上的阻激夹域信息,也作为光环的缓存) +core.status.lockControl (当前是否是控制锁定状态) +core.status.automaticRoute (当前的自动寻路信息) +core.status.route (当前记录的录像) +core.status.replay (录像回放时要用到的信息) +core.status.shops (所有全局商店信息) +core.status.textAttribute (当前的文字属性,如颜色、背景等信息,和setText事件对应) +core.status.globalAttribute (当前的全局属性,如边框色、装备栏等) +core.status.curtainColor (当前色调层的颜色) +core.status.globalAnimateObjs (当前的全局帧动画效果) +core.status.floorAnimateObjs (当前的楼层贴图帧动画效果) +core.status.boxAnimateObjs (当前的盒子帧动画效果,例如怪物手册中的怪物) +core.status.autotileAnimateObjs (当前楼层的自动元件动画效果) +core.status.globalAnimateStatus (当前的帧动画的状态) +core.status.animateObjs (当前的播放动画信息) + + +core.floorIds +一个数组,表示所有的楼层ID,和全塔属性中的floorIds一致。 core.floors -获得所有楼层的信息。例如core.floors[core.status.floorId].events可获得本楼层的所有自定义事件。 +从楼层文件中读取全部的地图数据。 +和core.status.maps不同的是,后者在每次重新开始和读档时都会重置,也允许被修改(会存入存档)。 +而core.floors全程唯一,不允许被修改。 -core.status.hero -获得当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值。 +core.statusBar +状态栏信息,例如状态栏图片,图标,以及各个内容的DOM定义等。 +core.statusBar.images (所有的系统图标,和icons.png对应) +core.statusBar.icons (状态栏中绘制的图标内容) -core.material.enemys -获得所有怪物信息。例如core.material.enemys.greenSlime就是获得绿色史莱姆的属性数据。 +core.values +所有的全局数值信息,和全塔属性中的values一致。 +此项允许被直接修改,会存入存档。 -core.material.items -获得所有道具的信息。 +core.flags +所有的全塔开关,和全塔属性中的flags一致。 +此项不允许被直接修改,如有需要请使用“设置系统开关”事件,或者调用core.setGlobalFlag这个API。 -core.debug() -开启调试模式。此模式下可以按Ctrl键进行穿墙,并忽略一切事件。 -此模式下不可回放录像和上传成绩。 +core.plugin +定义的插件函数。 -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为目标楼层ID,stair为到达的目标楼梯,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 -录像回放的快捷键;这不是一个控制台命令,但是也把它放在这里供使用。 -录像回放在修改地图或新增数据后会很有用。 - +core.doFunc(func, _this) +执行一个函数,func为函数体或者插件中的函数名,_this为使用的this。 +如果func为一个字符串,则视为插件中的函数名,同时_this将被设置成core.plugin。 +此函数剩余参数将作为参数被传入func。 ``` -!> 一些相对高级的命令,针对有一定脚本经验的人 +## actions.js -``` text +actions.js主要是处理一些和用户交互相关的内容。 -========== 可直接从core中调用的,最常被使用的函数 ========== -core.js实际上是所有API的入口(路由),核心API的实现在其他几个文件中,core.js主要进行转发操作。 +```text +core.registerAction(action, name, func, priority) +注册一个用户交互行为。 +action:要注册的交互类型,如 ondown, onclick, keyDown 等等。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +priority:优先级;优先级高的被注册项将会被执行。此项可不填,默认为0。 +返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 -core.nextX(n) -获得勇士面向的第n个位置的x坐标,n可以省略默认为1(即正前方) +core.unregisterAction(action, name) +注销一个用户交互行为。 -core.nextY(n) -获得勇士面向的第n个位置的y坐标,n可以省略默认为1(即正前方) +core.doRegisteredAction(action) +执行一个用户交互行为。 +此函数将在该交互行为所注册的所有函数中,按照优先级从高到底依次执行。 +此函数剩余的参数将会作为参数传入该执行函数中。 +当某个执行函数返回true时将终止这一过程。 + + +core.onkeyDown(e) +当按下某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.onkeyUp(e) +当放开某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.pressKey(keyCode) +当按住某个键不动时的操作,目前只对方向键有效。 +如果需要添加对于其他键的长按,请复写_sys_onkeyDown和_sys_onkeyUp。 +请勿直接覆盖或调用此函数,如有需要请注册一个"pressKey"的交互函数。 + + +core.keyDown(keyCode) +当按下某个键时的操作,参数为该键的keyCode值。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDown"的交互函数。 + + +core.keyUp(keyCode, altKey, fromReplay) +当按下某个键时的操作,参数为该键的keyCode值。 +altKey标志了Alt键是否同时被按下,fromReplay表示是否是从录像回放中调用的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyUp"的交互函数。 + + +core.ondown(loc) +当点击屏幕时的操作。loc为点击的信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"ondown"的交互函数。 +注册的ondown交互函数需要接受x, y, px, py四个参数,代表点击的位置和像素坐标。 + + +core.onmove(loc) +当在屏幕上滑动时的操作。loc为当前的坐标信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmove"的交互函数。 +注册的onmove交互函数需要接受x, y, px, py四个参数,代表当前的的位置和像素坐标。 + + +core.onup() +当从屏幕上离开时的操作。请注意此函数是没有参数的。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onup"的交互函数。 + + +core.onclick(x, y) +当点击屏幕上的某点位置时执行的操作,请注意这里的x和y是位置坐标。 +一般而言,一个完整的ondown到onup将触发一个onclick事件。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onclick"的交互函数。 + + +core.onmousewheel(direct) +当滚动鼠标滑轮时执行的操作。direct为滑轮方向,上为1,下为-1。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmousewheel"的交互函数。 + + +core.keyDownCtrl() +当长按Ctrl键不动时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 + + +core.longClick() +当长按住屏幕时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 +注册的交互函数如果某一项返回true,则之后仍然会继续触发该长按, +如果全部返回false则将停止本次长按行为,直到手指离开屏幕并重新进行长按为止。 +``` + +## control.js + +control.js将负责整个游戏的核心控制系统,分为如下几个部分: +- requestAnimationFrame相关 +- 标题界面,开始和重新开始游戏 +- 自动寻路和人物行走相关 +- 画布、位置、阻激夹域、显伤等相关 +- 录像的回放相关 +- 存读档,自动存档,同步存档等相关 +- 人物属性和状态、位置、变量等相关 +- 天气、色调、音乐和音效的播放 +- 状态栏和工具栏相关 +- 界面resize相关 + +```text +// ------ requestAnimationFrame 相关 ------ // + +core.registerAnimationFrame(name, needPlaying, func) +注册一个animationFrame。它将在每次浏览器的帧刷新时(约16.6ms)被执行。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +needPlaying:如果此项为true,则仅在游戏开始后才会被执行(标题界面不执行) +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +func可以接受一个timestamp作为参数,表示从整个页面加载完毕到当前时刻所经过的毫秒数。 +如果func执行报错,将在控制台打出一条信息,并自动进行注销。 + + +core.unregisterAnimationFrame(name) +注销一个animationFrame,参数是你的上面的自定义名称。 + +// ------ 开始界面相关 ------ // + +core.showStartAnimate(noAnimate, callback) +重置所有内容并显示游戏标题界面。 +noAnimate如果为true则不会有淡入动画,callback为执行完毕的回调。 + + +core.hideStartAnimate(callback) +淡出隐藏游戏标题界面,callback为执行完毕的回调。 + + +core.isPlaying() +当前是否正在游戏中。 + + +core.clearStatus() +清除所有的游戏状态和数据,包括状态栏的显示。 + +// ------ 自动寻路、人物行走 ------ // + +core.stopAutomaticRoute() +停止自动寻路的操作 + + +core.saveAndStopAutomaticRoute() +保存剩下的寻路路线并停止自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.continueAutomaticRoute() +继续剩下的自动寻路操作。主要用于打怪开门后继续寻路使用。 + + +core.clearContinueAutomaticRoute() +清空剩下的自动寻路操作。 + + +core.setAutomaticRoute(destX, destY, stepPostfix) +尝试开始进行一个自动寻路。stepPostfix是鼠标拖动的路径。 +此函数将检测是否在寻路中(在则停止或双击瞬移),检测是否点击自己(转身或轻按), +检测是否能单击瞬移,最后找寻自动寻路路线并开始寻路。 + + +core.setAutoHeroMove(steps) +设置勇士的自动行走路线,并立刻开始行走。 + + +core.setHeroMoveInterval(callback) +设置勇士行走动画。callback是每一步行走完毕后的回调。 + + +core.moveOneStep(x, y) +每走完一步后执行的操作,被转发到了脚本编辑中。 + + +core.moveAction(callback) +尝试执行单步行走。callback是执行完毕的回调。 +如果勇士面对的方向是noPass的,将直接触发事件并执行回调。 + + +core.moveHero(direction, callback) +令勇士朝一个方向行走。如果设置了callback,则只会行走一步,并执行回调。 +否则,将一直朝该方向行走,直到core.status.heroStop为true为止。 + + +core.isMoving() +当前是否正在处于行走状态 + + +core.waitHeroToStop(callback) +停止勇士的行走,等待行动结束后,再异步执行回调。 + + +core.turnHero(direction) +转向。如果设置了direction则会转到该方向,否则会右转。该函数会自动计入录像。 + + +core.moveDirectly(destX, destY) +尝试瞬间移动到某点,被转发到了脚本编辑中。 +此函数返回非负值代表成功进行瞬移,返回值是省略的步数;如果返回-1则代表没有成功瞬移。 + + +core.tryMoveDirectly(destX, destY) +尝试单击瞬移到某点。 +如果该点可被直接瞬间移动到,则直接瞬移到该点;否则尝试瞬移到相邻的上下左右点并行走一步。 + + +core.drawHero(status, offset) +绘制勇士。 +status可选,为'stop','leftFoot'和'rightFoot'之一,不填或null默认是'stop'。 +offset可选,表示具体当前格子的偏移量。不填默认为0。 +此函数将重新计算地图的偏移量,调整窗口位置,绘制勇士和跟随者信息。 + +// ------ 画布、位置、阻激夹域、显伤 ------ // + +core.setGameCanvasTranslate(canvas, x, y) +设置某个画布的偏移量 + + +core.addGameCanvasTranslate(x, y) +加减所有系统画布(ui和data除外)的偏移量。主要是被“画面震动”所使用。 + + +core.updateViewport() +根据大地图的偏移量来更新窗口的视野范围。 + + +core.nextX(n) / core.nextY(m) +获得勇士面对的第n个位置的横纵坐标。n可不填,默认为1。 core.nearHero(x, y) -判断某个点是否和勇士的距离不超过1。 +判定某个点是否和勇士的距离不大于1。 -core.openDoor(id, x, y, needKey, callback) [异步] -尝试开门操作。id为目标点的ID,x和y为坐标,needKey表示是否需要使用钥匙,callback为开门完毕后的回调函数。 -id可为null代表使用地图上的值。 -例如:core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")}) -此函数返回true代表成功开门,并将执行callback回调;返回false代表无法开门,且不会执行回调函数。 +core.gatherFollowers() +聚集所有的跟随者到勇士的位置。 -core.battle(id, x, y, force, callback) [异步] -执行战斗事件。id为怪物的id,x和y为坐标,force为bool值表示是否是强制战斗,callback为战斗完毕后的回调函数。 -id可为null代表使用地图上的值。 -例如:core.battle('greenSlime', null, null, true) +core.updateFollowers() +更新跟随者们的坐标。 -core.trigger(x, y) [异步] -触发某个地点的事件。 +core.updateCheckBlock(floorId) +更新阻激夹域的信息,被转发到了脚本编辑中。 + + +core.checkBlock() +检查勇士坐标点的阻激夹域信息。 + + +core.updateDamage(floorId, ctx) +更新全地图的显伤。floorId可选,默认为当前楼层。 +ctx可选,为画布;如果不为空,则将会绘制到该画布上而不是damage层上。 + +// ------ 录像相关 ------ // + +core.chooseReplayFile() +弹出选择文件窗口,让用户选择录像文件。 + + +core.startReplay(list) +开始播放一段录像。list为录像的操作数组。 + + +core.triggerReplay() +播放或暂停录像,实际上是pauseReplay或resumeReplay之一。 + + +core.pauseReplay() / core.resumeReplay() +暂停和继续录像播放。 + + +core.speedUpReplay() / core.speedDownReplay() +加速和减速录像播放。 + + +core.setReplaySpeed(speed) +直接设置录像回放速度。 + + +core.stopReplay(force) +停止录像回放。如果force为true则强制停止。 + + +core.rewindReplay() +回退一个录像节点。 + + +core.saveReplay() / core.bookReplay() / core.viewMapReplay() +回放录像时的存档、查看怪物手册、浏览地图操作。 core.isReplaying() -当前是否正在录像播放中 +当前是否正在录像播放中。 -core.drawBlock(block) -重绘某个图块。block应为core.status.thisMap.blocks中的一项。 +core.registerReplayAction(name, func) +注册一个自定义的录像行为。 +name:自定义名称,可用户注销使用。 +func:具体执行录像的函数,是一个函数体或者插件中的函数名。 +func需要接受action参数,代表录像回放时的当前操作行为。 +如果func返回true,则代表成功处理了此次操作,返回false代表没有进行处理。 +自己添加的录像项只能由数字、大小写、下划线线、冒号等符号组成,否则无法正常压缩和解压缩。 +对于自定义内容(比如中文文本或数组)请使用JSON.stringify再core.encodeBase64处理。 +请注意回放录像时的二次记录问题(即回放时录像会重新记录路线)。 + + +core.unregisterReplayAction(name) +注销一个录像行为。此函数一般不应当被使用。 + +// ------ 存读档相关 ------ // + +core.autosave(remoreLast) +进行一个自动存档,实际上是加入到缓存之中。 +removeLast如果为true则会从路线中删除最后一项再存(打怪开门前的状态)。 +在事件处理中不允许调用本函数,如有需要请呼出存档页面。 + + +core.checkAutosave() +将缓存的自动存档写入存储中。平均每五秒钟,或在窗口失去焦点时被执行。 + + +core.doSL(id, type) +实际执行一个存读档事件。id为存档编号,自动存档为'autoSave'。 +type只能为'save', 'load', 'replayLoad'之一,代表存档、读档和从存档回放录像。 + + +core.syncSave(type) / core.syncLoad() +向服务器同步存档,从服务器加载存档。type如果为'all'则会向服务器同步所有存档。 + + +core.saveData() +获得要存档的内容,实际转发到了脚本编辑中。 + + +core.loadData(data, callback) +实际执行一次读档行为,data为读取到的数据,callback为执行完毕的回调。 +实际转发到了脚本编辑中。 + + +core.getSave(index, callback) +获得某个存档位的存档。index为存档编号,0代表自动存档。 + + +core.getSaves(ids, callback) +获得若干个存档位的存档。ids为一个存档编号数组,0代表自动存档。 + + +core.getAllSaves(callback) +获得全部的存档内容。目前仅被同步全部存档和下载全部存档所调用。 + + +core.getSaveIndexes(callback) +刷新全部的存档信息,将哪些档位有存档的记录到core.saves.ids中。 + + +core.hasSave(index) +判定某个存档位是否存在存档。index为存档编号,0代表自动存档。 + + +core.removeSave(index) +删除某个存档。index为存档编号,0代表自动存档。 + + +// ------ 属性、状态、位置、变量等 ------ // + +core.setStatus(name, value) +设置勇士当前的某个属性。 + + +core.addStatus(name, value) +加减勇士当前的某个属性。等价于 core.setStatus(name, core.getStatus(name) + value) + + +core.getStatus(name) +获得勇士的某个原始属性值。 + + +core.getStatusOrDefault(status, name) +尝试从status中获得某个原始属性值;如果status为null或不存在对应属性值则从勇士属性中获取。 +此项在伤害计算函数中使用较多,例如传递新的攻击和防御来计算临界和1防减伤。 + + +core.getRealStatus(name) +获得勇士的某个计算属性值。该属性值是在加成buff之后得到的。 +该函数等价于 core.getStatus(name) * core.getBuff(name) + + +core.getRealStatusOrDefault(status, name) +尝试从status中获得某个原始属性值再进行增幅,如果不存在则获取勇士本身的计算属性值。 + + +core.setBuff(name, value) +设置勇士的某个属性的增幅值。value为1代表无增幅。 + + +core.addBuff(name, value) +增减勇士的某个属性的增幅值。等价于 core.setBuff(name, core.getBuff(name) + value) + + +core.getBuff(name) +获得勇士的某个属性的增幅值。默认值是1。 + + +core.setHeroLoc(name, value, noGather) +设置勇士位置属性。name只能为'x', 'y'和'direction'之一。 +如果noGather为true,则不会聚集所有的跟随者。 + + +core.getHeroLoc(name) +获得勇士的某个位置属性。如果name为null则直接返回core.status.hero.loc。 + + +core.getLvName(lv) +获得某个等级对应的名称,其在全塔属性的levelUp中定义。如果不存在则返回原始数值。 + + +core.setFlag(name, value) +设置某个自定义变量或flag。如果value为null则会调用core.removeFlag进行删除。 + + +core.addFlag(name, value) +加减某个自定义的变量或flag。等价于 core.setFlag(name, core.getFlag(name, 0) + value) + + +core.getFlag(name, defaultValue) +获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue值。 + + +core.hasFlag(name) +判定是否拥有某个自定义变量或flag。等价于 !!core.getFlag(name, 0) + + +core.removeFlag(name) +删除一个自定义变量或flag。 + + +core.lockControl() / core.unlockControl() +锁定和解锁控制。常常应用于事件处理。 + + +core.debug() +开启调试模式。此模式下可以按住Ctrl进行穿墙。 + +// ------ 天气,色调,音乐和音效 ------ // + +core.setWeather(type, level) +设置当前的天气。type只能为'rain', 'snow'或'fog',level为1-10之间代表强度信息。 + + +core.setCurtain(color, time, callback) +更改画面色调。color为更改到的色调,是个三元或四元组;time为渐变时间,0代表立刻切换。 + + +core.screenFlash(color, time, times, callback) +画面闪烁。color为色调,三元或四元组;time为单次闪烁时间,times为总闪烁次数。 + + +core.playBgm(bgm, startTime) +播放一个bgm。startTime可以控制开始时间,不填默认为0。 +如果bgm不存在、不被支持,或当前不允许播放背景音乐,则会跳过。 + + +core.pauseBgm() / core.resumeBgm() +暂停和恢复当前bgm的播放。 + + +core.triggerBgm() +更改当前bgm的播放状态。 + + +core.playSound(sound) / core.stopSound() +播放一个音效,停止全部音效。 +如果sound不存在、不被支持,或当前不允许播放音效,则会忽略。 + + +core.checkBgm() +检查bgm的状态。 +有的时候,刚打开页面时,浏览器是不允许自动播放标题界面bgm的,一定要经过一次用户操作行为。 +这时候我们可以给开始按钮增加core.checkBgm(),如果之前没有成功播放则重新播放。 + +// ------ 状态栏和工具栏相关 ------ // + +core.clearStatusBar() +清空状态栏的数据。 + + +core.updateStatusBar() +更新状态栏,被转发到了脚本编辑中。此函数还会根据是否在回放来设置工具栏的图标。 + + +core.showStatusBar() / core.hideStatusBar(showToolbox) +显示和隐藏状态栏。 +如果showToolbox为true,则在竖屏模式下不隐藏工具栏,方便手机存读档操作。 + + +core.updateHeroIcon() +更新状态栏上的勇士图标。 + + +core.updateGlobalAttribute() +更新全局属性,例如状态栏的背景图等。 + + +core.setToolbarButton(useButtom) +设置工具栏是否是拓展键盘。 + +// ------ resize 相关 ------ // + +core.registerResize(name, func) +注册一个resize函数。 +name为自定义名称,可供注销使用。 +func可以是一个函数,或插件中的函数名,可以接受一个obj作为参数。 +具体详见resize函数。 + + +core.unregisterResize(name) +注销一个resize函数。 + + +core.resize() +屏幕分辨率改变后的重新自适应。 +此函数将根据当前的屏幕分辨率信息,生成一个obj,并传入各个注册好的resize函数中执行。 +``` + +## enemys.js + +enemys.js中定义了一系列和怪物相关的API函数。 + +```text +core.hasSpecial(special, test) +判断是否含有某个特殊属性。test为要检查的特殊属性编号。 +special为要测试的内容,允许接收如下类型参数: + - 一个数字:将直接和test进行判等。 + - 一个数组:将检查test是否在该数组之中存在。 + - 一个怪物信息:将检查test是否在该怪物的特殊属性中存在 + - 一个字符串:视为怪物ID,将检查该怪物的特殊属性 + + +core.getSpecials() +获得所有特殊属性的列表。实际上被转发到了脚本编辑中。 + + +core.getSpecialText(enemy) +获得某个怪物的全部特殊属性名称。enemy可以是怪物信息或怪物ID。 +将返回一个数组,每一项是该怪物所拥有的一个特殊属性的名称。 + + +core.getSpecialHint(enemy, special) +获得怪物的某个特殊属性的描述。enemy可以是怪物信息或怪物ID,special为该特殊属性编号。 + + +core.canBattle(enemy, x, y, floorId) +判定当前能否战胜某个怪物。 +enemy可以是怪物信息或怪物ID,x,y,floorId为当前坐标和楼层。(下同) +能战胜返回true,不能战胜返回false。 + + +core.getDamage(enemy, x, y, floorId) +获得某个怪物的全部伤害值。 +如果没有破防或无法战斗则返回null,否则返回具体的伤害值。 + + +core.getExtraDamage(enemy, x, y, floorId) +获得某个怪物的额外伤害值(不可被魔防减伤)。 +目前暂时只包含了仇恨和固伤两者,如有需要可复写该函数。 + + +core.getDamageString(enemy, x, y, floorId) +获得某个怪物伤害字符串和颜色信息,以便于在地图上绘制显伤。 + + +core.nextCriticals(enemy, number, x, y, floorId) +获得接下来的N个临界值和临界减伤。enemy可以是怪物信息或怪物ID,x,y,floorId为当前坐标和楼层。 +number为要计算的临界值数量,不填默认为1。 +如果全塔属性中的useLoop开关被开启,则将使用循环法或二分法计算临界,否则使用回合法计算临界。 +返回一个二维数组 [[x1,y1],[x2,y2],...] 表示接下来的每个临界值和减伤值。 + + +core.getDefDamage(enemy, k, x, y, floorId) +获得某个怪物的k防减伤值。k可不填默认为1。 + + +core.getEnemyInfo(enemy, hero, x, y, floorId) +获得某个怪物的实际计算时的属性。该函数实际被转发到了脚本编辑中。 +hero可为null或一个对象,具体将使用core.getRealStatusOrDefault(hero, "atk")来获得攻击力数值。 +该函数应当返回一个对象,记录了怪物的实际计算时的属性。 + + +core.getDamageInfo(enemy, hero, x, y, floorId) +获得某个怪物的战斗信息。该函数实际被转发到了脚本编辑中。 +hero可为null或一个对象,具体将使用core.getRealStatusOrDefault(hero, "atk")来获得攻击力数值。 +如果该函数返回null,则代表不可战斗(如没有破防,或无敌等)。 +否则,该函数应该返回一个对象,记录了战斗伤害信息,如战斗回合数等。 +从V2.5.5开始,该函数也允许直接返回一个数字,代表战斗伤害值,此时回合数将视为0。 + + +core.updateEnemys() +更新怪物数据。该函数实际被转发到了脚本编辑中。详见文档-事件-更新怪物数据。 + + +core.getCurrentEnemys(floorId) +获得某个楼层不重复的怪物信息,floorId不填默认为当前楼层。该函数会被怪物手册所调用。 +该函数将返回一个列表,每一项都是一个不同的怪物,按照伤害值从小到大排序。 +另外值得注意的是,如果设置了某个怪物的displayIdInBook,则会返回对应的怪物。 + + +core.hasEnemyLeft(floorId) +检查某个楼层是否还有剩余的怪物。等价于 core.getCurrentEnemys(floorId).length > 0 +``` + +## events.js + +events.js将处理所有和事件相关的操作,主要分为五个部分: +- 游戏的开始和结束 +- 系统事件的处理 +- 自定义事件的处理 +- 点击状态栏图标所进行的操作 +- 一些具体事件的执行内容 + + +```text +// ------ 游戏的开始和结束 ------ // + +core.resetGame(hero, hard, floorId, maps, values) +重置整个游戏。该函数实际被转发到了脚本编辑中。 + + +core.startGame(hard, seed, route, callback) +开始新游戏。 +hard为难度字符串,会被设置为core.status.hard。 +seed为开始时要设置的的种子,route为要开始播放的录像,callback为回调函数。 +该函数将重置整个游戏,调用setInitData,执行startText事件,上传游戏人数统计信息等。 + + +core.setInitData() +根据难度分歧来初始化难度,包括设置flag:hard,设置初始属性等。 +该函数实际被转发到了脚本编辑中。 + + +core.win(reason, norank) +游戏胜利,reason为结局名,norank如果为真则该结局不计入榜单。 +该函数实际被转发到了脚本编辑中。 + + +core.lose(reason) +游戏失败,reason为结局名。该函数实际被转发到了脚本编辑中。 + + +core.gameOver(ending, fromReplay, norank) +游戏结束。ending为获胜结局名,null代表失败;fromReplay标识是否是录像触发的。 +此函数将询问是否上传成绩(如果ending不是null),是否下载录像等,并重新开始。 + + +core.restart() +重新开始游戏。本质上就是播放标题界面的BGM并调用showStartAnimate。 + + +core.confirmRestart() +确认用户是否需要重新开始。 + +// ------ 系统事件处理 ------ // + +core.registerSystemEvent(type, func) +注册一个系统事件,即通过图块的默认触发器所触发的事件。 +type为一个要注册的事件类型,func为要执行的函数体或插件中的函数名。 +func需要接受(data, callback)作为参数,分别是触发点的图块信息,和执行完毕时的回调。 +如果注册一个已经存在的系统事件,比如openDoor,则会覆盖系统的默认函数。 + + +core.unregisterSystemEvent(type) +注销一个系统事件。type是上面你注册的事件类型。 + + +core.doSystemEvent(type, data, callback) +执行一个系统事件。type为事件类型,data为该事件点的图块信息,callback为执行完毕的回调。 + + +core.battle(id, x, y, force, callback) +和怪物进行战斗。 +id为怪物的ID,x和y为怪物坐标,force如果为真将强制战斗,callback为执行完毕的回调。 +如果填写了怪物坐标,则会删除对应点的图块并执行该点战后事件。 +如果是在事件流的执行过程中调用此函数,则不会进行自动存档,且会强制战斗。 + + +core.beforeBattle(enemyId, x, y) +战前事件。实际被转发到了脚本编辑中,可以在这里加上一些战前特效。 +此函数在“检测能否战斗和自动存档”【之后】执行。 +如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 +此函数返回true则将继续本次战斗,返回false将不再战斗。 + + +core.afterBattle(enemyId, x, y, callback) +战后事件,将执行扣血、加金币经验、特殊属性处理、战后事件处理等操作。 +实际被转发到了脚本编辑中。 + + +core.openDoor(x, y, needKey, callback) +尝试开一个门。x和y为门的坐标,needKey表示是否需要钥匙,callback为执行完毕的回调。 +如果不是一个有效的门,需要钥匙且未持有等,均会忽略此事件并直接执行callback。 + + +core.afterOpenDoor(doorId, x, y, callback) +开完一个门后执行的事件,实际被转发到了脚本编辑中。 + + +core.getItem(id, num, x, y, callback) +获得若干个道具。itemId为道具ID,itemNum为获得的道具个数,不填默认为1。 +x和y为道具点的坐标,如果设置则会擦除地图上的该点。 + + +core.afterGetItem(id, x, y, callback) +获得一个道具后执行的事件,实际被转发到了脚本编辑中。 + + +core.getNextItem(noRoute) +轻按,即获得面对的道具。如果noRoute为真则这个轻按行为不会计入录像。 + + +core.changeFloor(floorId, stair, heroLoc, time, callback, fromLoad) +楼层切换。floorId为目标楼层ID,stair为是什么楼梯,heroLoc为目标点坐标。 +time为切换时间,callback为切换完毕的回调,fromLoad标志是否是从读档造成的切换。 +floorId也可以填":before"和":next"表示前一层和后一层。 +heroLoc为{"x": 0, "y": 0, "direction": "up"}的形式。不存在则从勇士位置取。 +如果stair不为null,则会在该楼层中找对应的图块作为目标点的坐标并覆盖heroLoc。 +一般设置的是"upFloor"和"downFloor",但也可以用任何其他的图块ID。 + + +core.changingFloor(floorId, heroLoc, fromLoad) +正在执行楼层切换中执行的操作,实际被转发到了脚本编辑中。 + + +core.hasVisitedFloor(floorId) +是否曾经到达过某一层。 + + +core.visitFloor(floorId) +标记曾经到达了某一层。 + + +core.passNet(data) +执行一个路障处理。这里只有毒衰咒网的处理,血网被移动到了updateCheckBlock中。 + + +core.pushBox(data) +执行一个推箱子事件。 + + +core.afterPushBox() +推箱子之后触发的事件,实际被转发到了脚本编辑中。 + + +core.changeLight(id, x, y) +踩灯后的事件。 + +// ------ 自定义事件的处理 ------ // + +core.registerEvent(type, func) +注册一个自定义事件。type为事件名,func为执行事件的函数体或插件中的函数名。 +func可以接受(data, x, y, prefix)参数,其中data为事件内容,x和y为该点坐标,prefix为该点前缀。 +同名注册的事件将进行覆盖。 +请记得在自定义处理事件完毕后调用core.doAction()再继续执行下一个事件! + + +core.unregisterEvent(type) +注销一个自定义事件。 + + +core.doEvent(data, x, y, prefix) +执行一个自定义事件。data为事件内容,将根据data.type去注册的事件列表中查找对应的执行函数。 +x和y为该点坐标,prefix为该点前缀。执行事件时也会把(data, x, y, prefix)传入执行函数。 + + +core.setEvents(list, x, y, callback) +设置自定义事件的执行列表,坐标和回调函数。 + + +core.startEvents(list, x, y, callback) +开始执行一系列的自定义事件。list为事件列表,x和y为事件坐标,callback为执行完毕的回调。 +此函数将调用core.setEvents,然后停止勇士,再执行core.doAction()。 + + +core.doAction() +执行下一个自定义事件。 +此函数将检测事件列表是否全部执行完毕,如果是则执行回调函数。 +否则,将从事件列表中弹出下一个事件,并调用core.doEvent进行执行。 + + +core.insertAction(action, x, y, callback, addToLast) +向当前的事件列表中插入一个或多个事件并执行。 +如果当前并不是在事件执行流中,则会调用core.startEvents()开始执行事件,否则仅仅执行插入操作。 +action为要插入的事件,可以是一个单独的事件,或者是一个事件列表。 +x,y,callback如果设置了且不为null,则会覆盖当前的坐标和回调函数。 +addToLast如果为真,则会插入到事件执行列表的尾部,否则是插入到执行列表的头部。 + + +core.getCommonEvent(name) +根据名称获得某个公共事件内容。 + + +core.recoverEvents(data) +恢复事件现场。一般用于呼出怪物手册、呼出存读档页面等时,恢复事件执行流。 + +// ------ 点击状态栏图标时执行的一些操作 ------ // + +core.openBook(fromUserAction) +尝试打开怪物手册。fromUserAction标志是否是从用户的行为触发,如按键或点击状态栏。(下同) +不建议复写此函数,否则【呼出怪物手册】事件会出问题。 + + +core.useFly(fromUserAction) +尝试使用楼传器。可以安全的复写此函数,参见文档-个性化-覆盖楼传事件。 + + +core.flyTo(toId, callback) +尝试飞行到某个楼层,被转发到了脚本编辑中。 +如果此函数返回true代表成功进行了飞行,false代表不能进行飞行。 + + +core.openEquipbox(fromUserAction) / core.openToolbox(fromUserAction) +尝试打开道具栏和装备栏。可以安全复写这两个函数。 + + +core.openQuickShop(fromUserAction) / core.openKeyBoard(fromUserAction) +尝试打开快捷商店和虚拟键盘。可以安全复写这两个函数。 + + +core.save(fromUserAction) / core.load(fromUserAction) +尝试打开存读档页面。 +不建议复写这两个函数,否则【呼出存读档页面】事件会出问题。 + + +core.openSettings(fromUserAction) +尝试打开系统菜单。不建议复写此函数。 + + +// ------ 一些具体事件的执行内容 ------ // + +core.hasAsync() +当前是否存在未执行完毕的异步事件。请注意正在播放的动画也算异步事件。 + + +core.follow(name) / core.unfollow(name) +跟随勇士/取消跟随。name为行走图名称。 +在取消跟随时如果指定了name,则会从跟随列表中选取一个该行走图取消,否则取消所有跟随。 +跟随和取消跟随都会调用core.gatherFollowers()来聚集所有的跟随者。 + + +core.setValue(name, value, prefix) / core.addValue(name, value, prefix) +设置/增减某个数值。name可以是status:xxx,item:xxx或flag:xxx。 +value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用。 + + +core.doEffect(effect, need, times) +执行一个effect操作。该函数目前仅被全局商店的status:xxx+=yyy所调用。 + + +core.setFloorInfo(name, values, floorId, prefix) +设置某层楼的楼层属性。 + + +core.setGlobalAttribute(name, value) +设置一个全局属性,如边框颜色等。 + + +core.setGlobalFlag(name, value) +设置一个全局开关,如enableXXX等。 +如果需要设置一个全局数值如红宝石数值,可以直接简单的修改core.values,因此没有单独列出函数。 + + +core.closeDoor(x, y, id, callback) +执行一个关门事件。如果不是一个合法的门,或者该点不为空地,则会忽略本事件。 + + +core.showImage(code, image, sloc, loc, opacityVal, time, callback) +显示一张图片。code为图片编号,image为图片内容或图片名。 +sloc为[x,y,w,h]形式,表示在原始图片上裁剪的区域,也可直接设为null表示整张图片。 +loc为[x,y,w,h]形式,表示在界面上绘制的位置和大小,w和h可忽略表示使用绘制大小。 +opacityVal为绘制的不透明度,time为淡入时间。 +此函数将创建一个画布,其z-index是100+code,即图片编号为1则是101,编号50则是150。 +请注意,curtain层的z-index是125,UI层的z-index是140;因此可以通过图片编号来调整覆盖关系。 + + +core.hideImage(code, time, callback) +隐藏一张图片。code为图片编号,time为淡出时间。 + + +core.moveImage(code, to, opacityVal, time, callback) +移动一张图片。code为图片编号,to为[x,y]表示目标位置,opacityVal目标不透明度,time为移动时间。 + + +core.showGif(name, x, y) +绘制一张gif图片或取消所有绘制内容。如果name不设置则视为取消。x和y为左上角像素坐标。 + + +core.setVolume(value, time, callback) +设置音量。value为目标音量大小,在0到1之间。time为音量渐变的时间。 + + +core.vibrate(time, callback) +画面震动。time为震动时间。 +请注意,画面震动时间必须是500的倍数,系统也会自动把time调整为上整的500倍数值。 + + +core.eventMoveHero(steps, time, callback) +使用事件移动勇士。time为每步的移动时间。 +steps为移动数组,可以接受'up','down','left','right','forward'和'backward'项。 +使用事件移动勇士将不会触发任何地图上的事件。 + + +core.jumpHero(ex, ey, time, callback) +跳跃勇士。ex和ey为目标点的坐标,可以为null表示原地跳跃。time为总跳跃时间。 + + +core.openShop(shopId, needVisited) +打开一个全局商店。needVisited表示是否需要该商店原本就是启用状态。 +如果该商店对应的实际上是一个全局事件,则会直接插入并执行。 + + +core.disableQuickShop(shopId) +禁用一个全局商店,即把一个商店从启用变成禁用状态。 + + +core.canUseQuickShop(shopId) +当前能否使用某个全局商店,实际被转发到了脚本编辑中。 +如果此函数返回null则表示可以使用,返回一个字符串表示不可以,该字符串表示不可以的原因。 + + +core.setHeroIcon(name, noDraw) +设置勇士的行走图。 +name为行走图名称,noDraw如果为真则不会调用core.drawHero()函数进行刷新。 + + +core.checkLvUp() +检查升级事件。该函数将判定当前是否升级(或连续升级),然后执行升级事件。 + + +core.tryUseItem(itemId) +尝试使用一个道具。 +对于怪物手册和楼传器,将分别调用core.openBook()和core.useFly()函数。 +对于中心对称飞行器,则会调用core.drawCenterFly()函数。 +对于其他的道具,将检查是否拥有,能否使用,并且进行使用。 + + +core.afterUseBomb() +使用炸弹或圣锤后的事件。实际被转发到了脚本编辑中。 +``` + +## icons.js + +icons.js主要是负责素材相关信息,比如某个素材在对应的图片上的位置。 + +```text +core.getClsFromId(id) +根据某个素材的ID获得该素材的cls + + +core.getTilesetOffset(id) +根据某个素材来获得对应的tileset和坐标信息。 +如果该素材不是tileset,则返回null。 +``` + +## items.js + +items.js主要负责一切和道具相关的内容。 + +```text +core.getItemEffect(itemId, itemNum) +即捡即用类的道具获得时的效果。实际对应道具图块属性中的itemEffect框。 + + +core.getItemEffectTip(itemId) +即捡即用类的道具获得时的额外提示,比如“,攻击+100”。 +实际对应道具图块属性中的itemEffectTip框。 + + +core.useItem(itemId, noRoute, callback) +尝试使用一个道具。实际对应道具图块属性中的useItemEffect框。 +此函数也会调用一遍core.canUseItem(),如果无法使用将直接返回。 +noRoute如果为真,则这次使用道具的过程不会被计入录像。 +使用道具完毕后,对于消耗道具将自动扣除,永久道具不会扣除。 + + +core.canUseItem(itemId) +当前能否使用某个道具。 +有些系统道具如破炸和上下楼器等,会在计算出目标点的坐标后存入core.status.event.ui。 +使用道具时将直接从core.status.event.ui调用,不会重新计算。 + + +core.itemCount(itemId) +获得某个道具的个数。 + + +core.hasItem(itemId) +当前是否拥有某个道具。等价于 core.itemCount(itemId) > 0 +请注意,装备上的装备不视为拥有该道具,即core.hasEquip()和core.hasItem()是完全不同的。 + + +core.hasEquip(itemId) +当前是否装备上某个装备。 +请注意,装备上的装备不视为拥有该道具,即core.hasEquip()和core.hasItem()是完全不同的。 + + +core.getEquip(equipType) +获得某个装备位的当前装备。equipType为装备类型,从0开始。 +如果该装备位没有装备则返回null,否则返回当前装备的ID。 + + +core.setItem(itemId, itemNum) +设置某个道具的个数。 + + +core.addItem(itemId, itemNum) +增减某个道具的个数,itemNum可不填默认为1。 + + +core.getEquipTypeByName(name) +根据装备位名称来找到一个空的装备孔,适用于多重装备。 +如果没有一个装备孔是该装备名称,则返回-1。 + + +core.getEquipTypeById(equipId) +获得某个装备的装备类型。 +如果其type写的是装备名(多重装备),则调用core.getEquipTypeByName()函数。 + + +core.canEquip(equipId, hint) +当前能否穿上某个装备。如果hint为真,则不可装备时会气泡提示原因。 + + +core.loadEquip(equipId, callback) +穿上某个装备。 + + +core.unloadEquip(equipType, callback) +脱下某个装备孔的装备。 + + +core.compareEquipment(compareEquipId, beComparedEquipId) +比较两个套装的差异。 +此函数将对所有的勇士属性包括生命魔力攻防魔防金币等进行比较。 +如果存在差异的,将作为一个对象返回其差异内容。 + + +core.quickSaveEquip(index) +保存当前套装。index为保存的套装编号。 + + +core.quickLoadEquip() +读取当前套装。index为读取的套装编号。 +``` + +## loader.js + +loader.js主要负责资源加载相关的内容。 + +```text +core.loadImage(imgName, callback) +从 project/images/ 中加载一张图片。imgName为图片名。 +callback为执行完毕的回调函数,接收(imgName, image)即图片名和图片内容作为参数。 +如果图片不存在或加载失败则会在控制台打出一条错误日志,不会执行回调。 + + +core.loadImages(names, toSave, callback) +从 project/images/ 中加载若干张图片。 +names为一个图片名的列表,toSave为加载并存到的对象。 +callback为全部加载完毕执行的回调。 + + +core.loadOneMusic(name) +从 project/sounds/ 或第三方中加载一个音乐,并存入core.material.bgms中。name为音乐名。 + + +core.loadOneSound(name) +从 project/sounds/ 中加载一个音效,并存入core.material.sounds中。name为音效名。 + + +core.loadBgm(name) +预加载一个bgm并加入缓存列表core.musicStatus.cachedBgms。 +此函数将会检查bgm的缓存,预加载和静音播放。 +如果缓存列表溢出(core.musicStatus.cacheBgmCount)则通过LRU算法选择一个bgm并调用core.freeBgm()。 + + +core.freeBgm(name) +释放一个bgm的内存并移出缓存列表。如果该bgm正在播放则也会立刻停止。 +``` + +## map.js + +maps.js负责一切和地图相关的处理内容,包括如下几个方面: +- 地图的初始化,保存和读取,地图数组的生成 +- 是否可移动或瞬间移动的判定 +- 地图的绘制 +- 获得某个点的图块信息 +- 启用和禁用图块,改变图块 +- 移动/跳跃图块,淡入淡出图块 +- 全局动画控制,动画的绘制 + +```text +// ------ 地图的初始化,保存和读取,地图数组的生成 ------ // + +core.loadFloor(floorId, map) +从楼层或者存档中生成core.status.maps的内容。 +map为存档信息,如果某项在map中不存在则会从core.floors中读取。 + + +core.getNumberById(id) +给定一个图块ID,找到对应的数字。 + + +core.initBlock(x, y, id, addInfo, eventFloor) +给定一个数字,初始化一个图块信息。 +x和y为坐标,id为数字或者可以:t或:f结尾表示初始是启用还是禁用状态。 +addInfo如果为true则会填充上图块的默认信息,比如给怪物添加battle触发器。 +eventFloor如果设置为某个楼层信息,则会填充上该点的自定义或楼层切换事件。 + + +core.compressMap(mapArr, floorId) +压缩地图。mapArr为要压缩的二维数组,floorId为对应的楼层。 +此函数将把mapArr和对应的楼层中的数组进行比较,并只取差异值进行存储。 +通过这种压缩地图的方式,不仅节省了存档空间,还支持了任意修改地图的接档。 + + +core.decompressMap(mapArr, floorId) +解压缩地图。mapArr为压缩后的地图,floorId为对应的楼层。 +此函数返回解压后的二维数组。 + + +core.saveMap(floorId) +将某层楼的数据生成存档所保存的内容。在core.saveData()中被调用。 + + +core.loadMap(data, floorId) +从data中读取楼层数据,并调用core.loadFloor()进行初始化。 + + +core.resizeMap(floorId) +根据某层楼的地图大小来调整大地图的画布大小。floorId可为null表示当前层。 + + +core.getMapArray(floorId, showDisable) +生成某层楼的二维数组。floorId可不填代表当前楼层。 +showDisable若为真,则对于禁用的点会加上:f表示,否则视为0。 + + +core.getMapBlocksObj(floorId, showDisable) +以x,y的形式返回每个点的图块信息。floorId可不填表示当前楼层。 +此函数将返回 {"0,0": {...}, "0,1": {...}} 这样的结构,其中内部为对应点的block信息。 + + +core.getBgMapArray(floorId, noCache) +获得某层楼的背景层的二维数组。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 + + +core.getFgMapArray(floorId, noCache) +获得某层楼的前景层的二维数组。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 + + +core.getBgNumber(x, y, floorId, noCache) +获得某层楼的背景层中某个点的数字。floorId可不填表示当前楼层。 +如果noCache为真则重新从剧本中读取而不使用缓存数据。 +本函数实际等价于 core.getBgMapArray(floorId, noCache)[y][x] + + +core.getBgNumber(x, y, floorId, noCache) +获得某层楼的前景层中某个点的数字。参数和方法同上。 + +// ------ 是否可移动或瞬间移动的判定 ------ // + +core.generateMovableArray(floorId, x, y, direction) +生成全图或某个点的可通行方向数组。floorId为楼层Id,可不填默认为当前点。 +这里的可通行方向数组,指的是["up","down","left","right"]中的一个或多个组成的数组。 + - 如果不设置x和y,则会返回一个三维数组,其中每个点都是一个该点可通行方向的数组。 + - 如果设置了x和y但没有设置direction,则只会返回该点的可通行方向数组, + - 如果设置了x和y以及direction,则会判定direction是否在该点可通行方向数组中,并返回true或false。 +可以使用core.inArray()来判定某个方向是否在可通行方向数组中。 + + +core.canMoveHero(x, y, direction, floorId) +某个点是否可朝某个方向移动。x和y可选,不填或为null则默认为勇士当前点。 +direction可选,不填或为null则默认勇士当前朝向。floorId不填则默认为当前楼层。 +此函数将直接调用 core.generateMovableArray() 进行判定。 + + +core.canMoveDirectly(destX, destY) +当前能否瞬间移动到某个点。 +如果可以瞬移则返回非负数,其值为该次瞬移所少走的步数;如果不能瞬移则返回-1。 + + +core.automaticRoute(destX, destY) +找寻到目标点的一条自动寻路路径。 + +// ------ 绘制地图相关 ------ // + +core.drawBlock(block, animate) +重新绘制一个图块,block为图块信息。 +如果animate不为null则代表是通过全局动画的绘制,其值为当前的帧数。 + + +core.generateGroundPattern(floorId) +生成某个楼层的地板信息。floorId不填默认为当前楼层。 +该函数可被怪物手册、对话框帧动画等地方使用。 core.drawMap(floorId, callback) -重绘某一层的地图。floorId为要绘制楼层的floorId,callback为绘制完毕后的回调函数。 +绘制某层楼的地图。floorId为目标楼层ID,可不填表示当前楼层。 +此函数会将core.status.floorId设置为floorId,并设置core.status.thisMap。 +将依次调用core.drawBg(), core.drawEvents()和core.drawFg()函数,最后绘制勇士和更新地图显伤。 + + +core.drawBg(floorId, ctx) +绘制背景层。floorId为目标楼层ID,可不填表示当前楼层。 +如果ctx不为null,则背景层将绘制在该画布上而不是bg层上(drawThumbnail使用)。 +可以通过复写该函数,调整_drawFloorImages和_drawBgFgMap的顺序来调整背景图块和贴图的遮挡顺序。 + + +core.drawEvents(floorId, blocks, ctx) +绘制事件层。floorId为目标楼层ID,可不填表示当前楼层。 +block表示要绘制的图块列表,可不填使用当前楼层的图块列表。 +如果ctx不为null,则背景层将绘制在该画布上而不是event层上(drawThumbnail使用)。 + + +core.drawFg(floorId, ctx) +绘制前景层。floorId为目标楼层ID,可不填表示当前楼层。 +如果ctx不为null,则背景层将绘制在该画布上而不是fg层上(drawThumbnail使用)。 +可以通过复写该函数,调整_drawFloorImages和_drawBgFgMap的顺序来调整前景图块和贴图的遮挡顺序。 + + +core.drawThumbnail(floorId, blocks, options, toDraw) +绘制一个楼层的缩略图。floorId为目标楼层ID,可不填表示当前楼层。 +block表示要绘制的图块列表,可不填使用当前楼层的图块列表。 +options为绘制选项(可为null),包括: + heroLoc: 勇士位置;heroIcon:勇士图标(默认当前勇士);damage:是否绘制显伤; + flags:当前的flags(在存读档时使用) +toDraw为要绘制到的信息(可为null,或为一个画布名),包括: + ctx:要绘制到的画布(名);x,y:起点横纵坐标(默认0);size:绘制大小(默认416/480); + all:是否绘制全图(默认false);centerX,centerY:截取中心(默认为地图正中心) + +// ------ 获得某个点的图块信息 ------ // + +core.noPass(x, y, floorId) +判定某个点是否有noPass的图块。 + + +core.npcExists(x, y, floorId) +判定某个点是否有NPC的存在。 core.terrainExists(x, y, id, floorId) -检测某个点是否存在(指定的)地形。 -x和y为坐标;id为地形ID,可为null表示任意地形;floorId为楼层ID,可忽略表示当前楼层。 +判定某个点是否有(指定的)地形存在。 +如果id为null,则只要存在terrains即为真,否则还会判定对应点的ID。 + + +core.stairExists(x, y, floorId) +判定某个点是否存在楼梯。 + + +core.nearStair() +判定当前勇士是否在楼梯上或旁边(距离不超过1)。 core.enemyExists(x, y, id, floorId) -检测某个点是否存在(指定的)怪物。 -x和y为坐标;id为怪物ID,可为null表示任意怪物;floorId为楼层ID,可忽略表示当前楼层。 +判定某个点是否有(指定的)怪物存在。 +如果id为null,则只要存在怪物即为真,否则还会判定对应点的怪物ID。 +请注意,如果需要判定某个楼层是否存在怪物请使用core.hasEnemyLeft()函数。 core.getBlock(x, y, floorId, showDisable) -获得某个点的当前图块信息。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +获得某个点的当前图块信息。x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 showDisable如果为true,则对于禁用的点和事件也会进行返回。 如果该点不存在图块,则返回null。 否则,返回值如下: {"index": xxx, "block": xxx} @@ -221,25 +1446,38 @@ showDisable如果为true,则对于禁用的点和事件也会进行返回。 core.getBlockId(x, y, floorId, showDisable) -获得某个点的图块ID。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 -showDisable如果为true,则对于禁用的点和事件也会进行返回。 -如果该点不存在图块,则返回null,否则返回该点的图块ID。 +获得某个点的图块ID。如果该点不存在图块则返回null。 core.getBlockCls(x, y, floorId, showDisable) -获得某个点的图块cls。 -x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 -showDisable如果为true,则对于禁用的点和事件也会进行返回。 -如果该点不存在图块,则返回null,否则返回该点的图块cls。 +获得某个点的图块类型。如果该点不存在图块则返回null。 +core.getBlockInfo(block) +根据某个的图块信息获得其详细的素材信息。 +如果参数block为字符串,则视为图块ID;如果参数为数字,则视为图块的数字。 +此函数将返回一个非常详尽的素材信息,目前包括如下几项: +number:素材数字;id:素材id;cls:素材类型;image:素材所在的素材图片;animate:素材的帧数。 +posX, posY:素材在该素材图片上的位置;height:素材的高度;faceIds:NPC朝向记录。 + + +core.searchBlock(id, floorId, showDisable) +搜索一个图块出现过的所有位置。id为图块ID,也可以传入图块的数字。 +floorId为要搜索的楼层,可以是一个楼层ID,或者一个楼层数组。如果floorId不填则只搜索当前楼层。 +showDisable如果为真,则对于禁用的图块也会返回。 +此函数将返回一个数组,每一项为一个搜索到的结果: +{"floorId": ..., "index": ..., "block": {...}, "x": ..., "y": ...} +即包含该图块所在的楼层ID,在该楼层的blocks数组的索引,图块内容,和横纵坐标。 + + +// ------ 启用和禁用图块,改变图块 ------ // + core.showBlock(x, y, floorId) -将某个点从禁用变成启用状态。 +将某个点从禁用变成启用状态。floorId可不填或null表示当前楼层。 core.hideBlock(x, y, floorId) -将某个点从启用变成禁用状态,但不会对其进行删除。 +将某个点从启用变成禁用状态,但不会对其进行删除。floorId可不填或null表示当前楼层。 此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。 @@ -249,72 +1487,521 @@ core.removeBlock(x, y, floorId) 如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。 +core.removeBlockById(index, floorId) +根据索引从地图的block数组中尽可能删除一个图块。floorId可不填或null表示当前楼层。 + + +core.removeBlockByIds(floorId, ids) +根据索引数组从地图的block数组中尽可能删除一系列图块。floorId可不填或null表示当前楼层。 + + +core.canRemoveBlock(block, floorId) +判定当前能否完全删除某个图块。floorId可不填或null表示当前楼层。 +如果该点存在自定义事件,或者是重生怪,则不可进行删除。 + + +core.showBgFgMap(name, loc, floorId, callback) +显示某层楼中某个背景/前景层的图块。name只能为'bg'或'fg'表示背景或前景层。 +loc为该点坐标,floorId可不填默认为当前楼层。callback为执行完毕的回调。 + + +core.hideBgFgMap(name, loc, floorId, callback) +隐藏某层楼中某个背景/前景层的图块。name只能为'bg'或'fg'表示背景或前景层。 +loc为该点坐标,floorId可不填默认为当前楼层。callback为执行完毕的回调。 + + +core.showFloorImage(loc, floorId, callback) +显示某层楼中的某个楼层贴图。loc为该贴图的左上角坐标。floorId可省略表示当前楼层。 + + +core.hideFloorImage(loc, floorId, callback) +隐藏某层楼中的某个楼层贴图。loc为该贴图的左上角坐标。floorId可省略表示当前楼层。 + + core.setBlock(number, x, y, floorId) -改变图块。number为要改变到的图块数字,x和y为坐标,floorId为楼层ID,可忽略表示当前楼层。 +改变某个楼层的某个图块。 +number为要改变到的数字,也可以传入图块id(将调用core.getNumberById()来获得数字)。 +x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 -core.useItem(itemId, noRoute, callback) -尝试使用某个道具。itemId为道具ID,noRoute如果为真则该道具的使用不计入录像。 -callback为成功或失败后的回调。 +core.replaceBlock(fromNumber, toNumber, floorId) +将某个或某些楼层中的所有某个图块替换成另一个图块 +fromNumber和toNumber为要被替换和替换到的数字。 +floorId可为某个楼层ID,或者一个楼层数组;如果不填只视为当前楼层。 +值得注意的是,使用此函数转了的点上的自定义事件可能无法被执行。 +如有需要,再对那些存在事件的点执行core.setBlock()即可 -core.canUseItem(itemId) -返回当前能否使用某个道具。 +core.setBgFgBlock(name, number, x, y, floorId) +设置前景/背景层的某个图块。name只能为'bg'或'fg'表示前景或背景层。 +number为要设置到的图块数字,x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 -core.loadEquip(itemId, callback) -装备上某个装备。itemId为装备的ID,callback为成功或失败后的回调。 +core.resetMap(floorId) +重置某层或若干层的地图和楼层属性。 +floorId可为某个楼层ID,或者一个楼层数组(同时重置若干层);如果不填则只重置当前楼层。 + +// ------ 移动/跳跃图块,淡入淡出图块 ------ // + +core.moveBlock(x, y, steps, time, keep, callback) +移动一个图块,x和y为图块的坐标。 +steps为移动的数组,每一项只能是"up","down","left","right"之一。 +time为每一步的移动时间,不填默认为500ms。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 -core.unloadEquip(equipType, callback) -卸下某个部位的装备。equipType为装备类型,从0开始;callback为成功或失败后的回调。 +core.jumpBlock(sx, sy, ex, ey, time, keep, callback) +跳跃一个图块,sx和sy为图块的坐标,ex和ey为目标坐标。time为整个跳跃过程中的全程用时,不填默认500。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 -core.getNextItem() -轻按。 +core.animateBlock(loc, type, time, callback) +淡入/淡出一个或多个图块。 +loc为一个图块坐标,或者一个二维数组表示一系列图块坐标(将同时显示和隐藏)。 +type只能为'show'或'hide'表示是淡入但是淡出。time为动画时间,callback为执行完毕的回调。 + +// ------ 全局动画控制,动画的绘制 ------ // + +core.addGlobalAnimate(block) +添加一个全局帧动画。 -core.drawTip(text, itemIcon) -在左上角绘制一段提示信息,2秒后消失。itemIcon为道具图标的索引。 +core.removeGlobalAnimate(x, y, name) +删除一个或全部的全局帧动画。name可为'bg',null或'fg'表示某个图层。 +x和y如果为null,则会删除全部的全局帧动画,否则只会删除该点的该层的帧动画。 -core.drawText(contents, callback) [异步] -绘制一段文字。 -不推荐使用此函数,尽量使用core.insertAction(contents)来显示剧情文本。 +core.drawBoxAnimate() +绘制UI层的box动画,如怪物手册和对话框中的帧动画等。 +core.drawAnimate(name, x, y, callback) +绘制一个动画。name为动画名,x和y为绘制的基准坐标,callback为绘制完毕的回调函数。 +此函数将播放动画音效,并异步开始绘制该动画。 +此函数会返回一个动画id,可以通过core.stopAnimate()立刻停止该动画的播放。 + + +core.stopAnimate(id, doCallback) +立刻停止某个动画的播放。id为上面core.drawAnimate的返回值。 +如果doCallback为真,则会执行该动画所对应的回调函数。 +``` + +## ui.js + +ui.js负责一切UI界面的绘制。主要包括三个部分: +- 设置某个画布的属性的相关API +- 具体的某个UI界面的绘制 +- 动态创建画布相关的API + +```text +// ------ 设置某个画布的属性的相关API ------// +这系列函数的name一般都是画布名,可以是系统画布或动态创建的画布。 +但也同时也允许直接传画布的context本身,将返回自身。 + + +core.getContextByName(name) +根据画布名找到一个画布的context;支持系统画布和自定义画布。 +如果不存在画布此函数返回null。 +该参数也可以直接传画布的context自身,则返回自己。 + + +core.clearMap(name) +清空某个画布图层。 +该函数的name也可以是'all',若为'all'则为清空所有系统画布。 + + +core.fillText(name, text, x, y, style, font) +在某个画布上绘制一段文字。 +text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) +请注意textAlign和textBaseline将决定绘制的左右对齐和上下对齐方式。 +具体可详见core.setTextAlign()和core.setTextBaseline()函数。 + + +core.fillBoldText(name, text, x, y, style, font) +在某个画布上绘制一个描黑边的文字。 + + +core.fillRect(name, x, y, width, height, style) +绘制一个矩形。style可选为绘制样式。如果设置将调用core.setFillStyle()。(下同) + + +core.strokeRect(name, x, y, width, height, style, lineWidth) +绘制一个矩形的边框。style可选为绘制样式,如果设置将调用core.setStrokeStyle()。 +lineWidth如果设置将调用core.setLineWidth()。(下同) + + +core.drawLine(name, x1, y1, x2, y2, style, lineWidth) +绘制一条线。 + + +core.drawArrow(name, x1, y1, x2, y2, style, lineWidth) +绘制一个箭头。 + + +core.setFont(name, font) / core.setLineWidth(name, lineWidth) +设置一个画布的字体/线宽。 + + +core.setAlpha(name, font) / core.setOpacity(name, font) +设置一个画布的绘制不透明度和画布本身的不透明度。 +两者区别如下: + - setAlpha是设置"接下来绘制的内容的不透明度",不会对已经绘制的内容产生影响。 + > 比如setAlpha('ui', 0.5)则会在接下来的绘制中使用0.5的不透明度。 + - setOpacity是设置"画布本身的不透明度",已经绘制的内容也会产生影响。 + > 比如我已经在UI层绘制了一段文字,再setOpacity则也会让已经绘制的文字变得透明。 +尽量不要对系统画布使用setOpacity(因为会对已经绘制的内容产生影响),自定义创建的画布则不受此限制。 + + +core.setFillStyle(name, style) / core.setStrokeStyle(name, style) +设置一个画布的填充样式/描边样式。 + + +core.setTextAlign(name, align) +设置一个画布的文字横向对齐模式,这里的align只能为'left', 'right'和'center'。 +默认为'left'。 + + +core.setTextBaseline(name, baseline) +设置一个画布的纵向对齐模式。有关textBaseline的说明可参见如下资料: +http://www.runoob.com/tags/canvas-textbaseline.html +默认值是'alphabetic'。 +请注意,系统画布在绘制前都是没有设置过textBaseline的,都是按照alphabetic进行坐标绘制。 +因此,如果你修改了系统画布的textBaseline来绘图,记得再绘制完毕后修改回来。 + + +core.calWidth(name, text, font) +计算一段文字在某个画布上的绘制宽度,此函数其实是ctx.measureText()的包装。 +font可选,如果存在则会先设置该画布上的字体。 +此函数不会对文字进行换行,如需换行版本请使用core.splitLines()函数。 + + +core.splitLines(name, text, maxWidth, font) +计算一段文字在某画布上换行分割的结果。 +text为文字内容,maxWidth为最大宽度,可为null表示不自动换行。 +font可选,如果存在则会先设置该画布上的字体。 +此函数将返回一个换行完毕的文本列表。 + + +core.drawImage(name, image, x, y, w, h, x1, y1, w1, h1) +在一张画布上绘制一张图片,此函数其实是ctx.drawImage()的包装。 +可以查看下面的文档以了解各项参数的信息: +http://www.w3school.com.cn/html5/canvas_drawimage.asp +这里的image允许传一个图片,画布。也允许传递图片名,将从你导入的图片中获取图片内容。 + +// ------ 具体的某个UI界面的绘制 ------ // core.closePanel() -结束一切事件和绘制,关闭UI窗口,返回游戏进程。 +结束一切事件和UI绘制,关闭UI窗口,返回游戏。 +此函数将以此调用core.clearUI(),core.unlockControl(),并清空core.status.event里面的内容。 -core.replaceText(text) -将一段文字中的${}进行计算并替换。 +core.clearUI() +重置UI窗口。此函数将清掉所有的UI帧动画和光标,清空UI画布,并将alpha设为1。 -core.calValue(value, prefix, need, times) -计算表达式的实际值。这个函数可以传入status:atk等这样的参数。 +core.drawTip(text, id) +在左上角以气泡的形式绘制一段提示。 +text为文字内容,仅支持${}的表达式计算,不支持换行和变色。 +id可选,为同时绘制的图标ID,如果不为null则会同时绘制该图标(仅对32x32的素材有效)。 + + +core.drawText(content, callback) +绘制一段文字。contents为一个字符串或一个字符串数组,callback为全部绘制完毕的回调。 +支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +如果当前在事件处理中或录像回放中,则会自动转成core.insertAction处理。 +不建议使用该函数,如有绘制文字的需求请尽量使用core.insertAction()插入剧情文本事件。 + + +core.drawWindowSelector(background, x, y, w, h) +绘制一个WindowSkin的闪烁光标。background可为winskin的图片名或图片本身。 +x,y,w,h为绘制的左上角位置和宽高,都是像素为单位。 + + +core.drawWindowSkin(background, ctx, x, y, w, h, direction, px, py) +绘制一个WindowSkin。background可为winskin的图片名或图片本身。 +ctx为画布名或画布本身,x,y,w,h为左上角位置和宽高,都是像素为单位。 +direction, px, py可选,如果设置则会绘制一个对话框箭头。 + + +core.drawBackground(left, top, right, bottom, posInfo) +绘制一个背景图。此函数将使用你在【剧情文本设置】中设置的背景,即用纯色或WindowSkin来绘制。 +left, top, right, bottom为你要绘制的左上角和右下角的坐标。 +posInfo如果不为null则是一个含position, px和py的对象,表示一个对话框箭头的绘制。 + + +core.drawTextContent(ctx, content, config) +根据配置在某个画布上绘制一段文字。此函数会被core.drawTextBox()所调用。 +ctx为画布名或画布本身,如果不设置则会忽略该函数。 +content为要绘制的文字内容,支持所有的文字效果(如\n,${},\r,\\i等),但不支持支持\t和\b的语法。 +config为绘制的配置项,目前可以包括如下几项: + - left, top:在该画布上绘制的左上角像素位置,不设置默认为(0,0)。 + > 该函数绘制时会将textBaseline设置为'top',因此只需要考虑第一个字的左上角位置。 + - maxWidth:单行最大宽度,超过此宽度将自动换行,不设置不会自动换行。 + - color:默认颜色,为#XXXXXX形式。如果不设置则使用剧情文本设置中的正文颜色。 + - bold:是否粗体。如果不设置默认为false。 + - align:文字对齐方式,仅在maxWidth设置时有效,默认为'left'。 + - fontSize:字体大小,如果不设置则使用剧情文本设置中的正文字体大小。 + - lineHeight:绘制的行距值,如果不设置则使用fontSize*1.3(即1.3被行距)。 + - time:打字机效果。如果此项不为0,则会用打字机效果逐个字进行绘制,并设置core.status.event.interval定时器。 + + +core.drawTextBox(content, showAll) +绘制一个对话框。content为一个字符串或一个字符串数组。 +支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +该函数将使用用户在剧情文本设置中的配置项进行绘制。 +实际执行时,会计算文本框宽度并绘制背景,绘制标题和头像,再调用core.drawTextContent()绘制正文内容。 +showAll可选,如果为true则不会使用打字机效果而全部显示,主要用于打字机效果的点击显示全部。 + + +core.drawScrollText(content, time, lineHeight, callback) +绘制一个滚动字幕。content为绘制内容,time为总滚动时间(默认为5000),lineHeight为行距比例(默认为1.4)。 +滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i等),但不支持\t和\b效果。 +可以通过剧情文本设置中的align控制是否居中绘制,offset控制其距离左边的偏移量。 + + +core.textImage(content, lineHeight) +将文本图片化。content为绘制内容,lineHeight为行距比例(默认为1.4)。 +可以通过剧情文本设置中的align控制是否居中绘制。 +此函数将返回一个临时画布的canvas,供转绘到其他画布上使用。 + + +core.drawChoices(content, choices) +绘制一个选项框。 +content可选,为选项上方的提示文字,支持所有的文字效果(如\n,${},\r,\\i等),也支持\t效果。 +choices必选,为要绘制的选项内容,是一个列表。其中的每一项: + - 可以是一个字符串,表示选项文字,将使用剧情文本设置中的正文颜色来绘制,仅支持${}表达式计算。 + - 或者是一个包含text, color和icon的对象。 + > text必选,为要绘制的文字内容,仅支持${}的表达式计算, + > color为要绘制的选项颜色,可以是#XXXXXX的格式或RGBA数组。不设置则使用正文颜色。 + > icon为要绘制的图标ID,支持使用素材的ID,或者系统图标。 + + +core.drawConfirmBox(text, yesCallback, noCallback) +绘制一个确认框。text为确认文字,支持\n换行(但不支持自动换行)和${}表达式计算。 +yesCallback和noCallback分别为确定和取消的回调函数。 +可以在调用此函数前设置core.status.event.selection来控制默认的选中项(0确定1取消)。 +此函数和core.myconfirm的区别主要在于: + - 此函数会清掉UI层原有的绘制信息,core.myconfirm不会清除。 + - 此函数可以用键盘进行操作,core.myconfirm必须用鼠标点击。 +另外请注意:本函数和事件流的执行不兼容,选择也不会进录像。 +如果需要在事件流中调用请使用提供选择项。 + + +core.drawWaiting(text) +绘制一个等待界面,一般用于同步存档之类的操作。 +调用此函数后,需要再调用core.closePanel()才会恢复游戏。 + + +core.drawPagination(page, totalPage, y) +绘制一个分页。y如果不设置则绘制在最后一行。 + + +core.drawBook(index) / core.drawBookDetail(index) +绘制怪物手册,绘制怪物的详细信息。 + + +core.drawFly(page) / core.drawCenterFly() / core.drawShop(shopId) +绘制楼传页面,中心对称飞行器,绘制商店 + + +core.drawToolbox(index) / core.drawEquipbox(index) / core.drawKeyBoard() +绘制道具栏,绘制装备栏,绘制虚拟键盘。 + + +core.drawMaps(index, x, y) / core.drawSLPanel(index, refresh) +绘制浏览地图,绘制存读档界面。 + + +core.drawStatusBar() +自定义绘制状态栏,仅在状态栏canvas化开启时有效,实际被转发到了脚本编辑中。 + + +core.drawStatistics() +绘制数据统计。将从脚本编辑中获得要统计的数据列表,再遍历所有地图进行统计。 + + +core.drawAbout() / core.drawPaint() / core.drawHelp() +绘制关于界面,绘图模式,帮助界面。 + +// ------ 动态创建画布相关的API ------ // + + +core.ui.createCanvas(name, x, y, width, height, z) +动态创建一个自定义画布。name为要创建的画布名,如果已存在则会直接取用当前存在的。 +x,y为创建的画布相对窗口左上角的像素坐标,width,height为创建的长宽。 +z值为创建的纵向高度(关系到画布之间的覆盖),z值高的将覆盖z值低的;系统画布的z值可在个性化中查看。 +返回创建的画布的context,也可以通过core.dymCanvas[name]或core.getContextByName获得。 + + +core.ui.relocateCanvas(name, x, y) +重新定位一个自定义画布。x和y为画布的左上角坐标。 + + +core.ui.resizeCanvas(name, width, height) +重新设置一个自定义画布的大小。width和height为新设置的宽高。此操作会清空画布。 + + +core.ui.deleteCanvas(name) +删除一个自定义画布。 + + +core.ui.deleteAllCanvas() +删除所有的自定义画布。 +``` + +## utils.js + +utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 + +```text +core.replayText(text, need, times) +将一段文字中的${}(表达式)进行替换。need和time一般可以直接忽略。 + + +core.calValue(value, prefix, need, time) +计算一个表达式的值,支持status:xxx等的计算。 +prefix为前缀(switch:xxx的独立开关使用),need和time一般可以直接忽略。 + + +core.unshift(a, b) / core.push(a, b) +将b插入到列表a之前或之后。b可以是一个元素或者一个数组。 + + +core.decompress(value) +解压缩一个字符串并进行JSON解析。value可能会被LZString或LZW算法进行压缩。 +返回解压后的JSON格式内容,如果无法解压则返回null。 + + +core.setLocalStorage(key, value) +将一个键值对存入localStorage中,会立刻返回结果。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 +value为要存储的内容,存储前会被JSON转成字符串形式,并LZW算法进行压缩。 +如果value为null,则实际会调用core.removeLocalStorage()函数清除该key。 +localStorage为浏览器的默认存储,和存档无关,只有5M的空间大小。 +此函数立刻返回true或false,如果为false则代表存储失败,一般指的是空间满了。 core.getLocalStorage(key, defaultValue) -从localStorage中获得某个数据(已被parse);如果对应的key不存在则返回defaultValue。 +从localStorage中获得一个键的值,会立刻返回结果。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 +如果对应的键值不存在或为null,则会返回defaultValue。 +返回的结果会被调用core.decompress进行解压缩和JSON解析。 + + +core.removeLocalStorage(key) +从localStorage中删除一个键的值,会立刻返回。 +此函数会自动给key加上它的name作为前缀,以便于不同塔之间的区分。 + + +core.setLocalForage(key, value, successCallback, errorCallback) +将一个键值对存入localForage中,异步返回结果。 +如果用户没有开启【新版存档】开关,则仍然会使用core.setLocalStorage()。 +localForage为一个开源库,项目地址 https://github.com/localForage/localForage +一般存入的是indexedDB,这是一个浏览器的自带数据库,没有空间限制。 +successCallback和errorCallback均可选,表示该次存储成功或失败的回调, +此函数为异步的,只能通过回调函数来获得存储的成功或失败信息。 core.getLocalForage(key, defaultValue, successCallback, errorCallback) -从localForage中获得某个数据(已被parse),如果对应的key不存在则返回defaultValue。 -如果成功则通过successCallback回调,失败则通过errorCallback回调。 - - -core.hasSave(index) -判定当前某个存档位是否存在存档,返回true/false。 -index为存档编号,0代表自动存档,大于0则为正常的存档位。 +从localForage中获得一个键的值,异步返回结果。 +如果对应的键值不存在,则会使用defaultValue。 +如果获得成功,则会将core.decompress后的结果传入successCallback回调函数执行。 +errorCallback可选,如果失败,则会将错误信息传入errorCallback()。 +此函数是异步的,只能通过回调函数来获得读取的结果或错误信息。 core.clone(data) -深拷贝某个对象。 +深拷贝一个对象。有关浅拷贝,深拷贝,基本类型和引用类型等相关知识可参见: +https://zhuanlan.zhihu.com/p/26282765 -core.isset(x) -测试x是否不为null,不为undefined也不为NaN。 +core.splitImage(image, width, height) +等比例切分一张图片。width和height为每张子图片的宽高。 +请确保原始图片的宽度和高度都是是width和height的倍数。 +此函数将返回一个一维数组,每一项都是一张切分好的图片,横向再纵向排列。 +例如4x3的图片按1x1切分得到一个长度为12的数组,按如下方式进行排列: +0 1 2 3 +4 5 6 7 +8 9 10 11 +可以将很多需要的图片拼在一张大图上,然后在插件的_afterLoadResources中切分。 +切分好的图片再存入core.material.images.images中,这样可以少很多IO请求。 + + +core.formatDate(date) / core.formatDate2(date) / core.formatTime(time) +格式化日期和时间。 + + +core.setTwoDigits(x) +将x变成两位数。其实就是 parseInt(x) < 10 ? "0" + x : x + + +core.formatBigNumber(x, onMap) +大数据格式化。x为要格式化的内容,如果不合法会返回???。 +onMap标记是否在地图上调用,如果为真则尝试格式化成六位,否则五位。 + + +core.arrayToRGB(color) / core.arrayToRGBA(color) +将一个颜色数组,例如[255,0,0,1]转成#FF0000或rgba(255,0,0,1)的形式。 + + +core.encodeRoute(route) +录像压缩和解压缩。route为要压缩的路线数组。 +此函数将尽可能对录像进行压缩。对于无法识别的项目(比如自己添加的),将不压缩而原样放入。 +例如,["up","up","left","move:3:5","test:2333","getNext","item:bomb","down"] +将被压缩成"U2LM3:5(test:2333)GIbomb:D"。 +自己添加的录像项只能由数字、大小写、下划线线、冒号等符号组成,否则无法正常压缩和解压缩。 +对于自定义内容(比如中文文本或数组)请使用JSON.stringify再core.encodeBase64处理。 +压缩的结果将再次进行LZString.compressToBase64()的压缩以进一步节省空间。 + + +core.decodeRoute(route) +解压缩一个录像,返回解压完毕的路线数组。 + + +core.isset(v) +判定v是不是null, undefined或NaN。 +请尽量避免使用此函数,而是直接判定 v == null (请注意 null==undefined !) + + +core.subarray(a, b) +判定数组b是不是数组a的一个前缀子数组。 +如果是,则返回a中除去b后的剩余数组,否则返回null。 + + +core.inArray(array, element) +判定array是不是一个数组,以及element是否在该数组中。 + + +core.clamp(x, a, b) +将x限定在[a,b]区间内。 + + +core.getCookie(name) +获得一个cookie值,如果不存在该cookie则返回null。 + + +core.setStatusBarInnerHTML(name, value, css) +设置一个状态栏的innerHTML。此函数会自动设置状态栏的文字放缩和斜体等效果。 +name为状态栏的名称,如atk, def等。需要是core.statusBar中的一个合法项。 +value为要设置到的数值,如果是数字则会先core.formatBigNumber()进行格式化。 +css可选,为增添的额外css内容,比如可以设定颜色等。 + + +core.strlen(str) +计算某个字符串的实际长度。每个字符的长度,ASCII码视为1,中文等视为2。 + + +core.reverseDirection(direction) +翻转方向,即"up"转成"down", "left"转成"right"等。 + + +core.encodeBase64(str) / core.decodeBase64(str) +将字符串进行base64加密或解密。 + + +core.convertBase(str, fromBase, toBase) +任意进制转换。此函数可能执行的非常慢,慎用。 core.rand(num) @@ -328,362 +2015,70 @@ core.rand2(num) num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到2147483647之间的整数。 此函数使用了系统的Math.random()函数,支持SL大法。 但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 +对于需要大量生成随机数,但又想使用真随机支持SL大法的(例如随机生成地图等),可以采用如下方式: + var x = core.rand2(100); for (var i = 0; i < x; i++) core.rand() +即先生成一个真随机数,根据该数来推进伪随机的种子,这样就可以放心调用core.rand()啦。 -core.restart() [异步] -返回标题界面。 +core.readFile(success, error) +读取一个本地文件内容。success和error分别为读取成功或失败的回调函数。 +iOS平台暂不支持读取文件操作。 -========== core.actions.XXX 和游戏控制相关的函数 ========== -actions.js主要用来进行用户交互行为的处理。 -所有用户行为,比如按键、点击、滑动等等,都会被此文件接收并进行操作。 +core.readFileContent(content) +读取到的文件内容。此函数会被APP等调用,来传递文件的具体内容。 -========== core.control.XXX 和游戏控制相关的函数 ========== -control.js主要用来进行游戏控制,比如行走控制、自动寻路、存读档等等游戏核心内容。 +core.download(filename, content) +生成一个文件并下载。filename为文件名,content为具体的文件内容。 +iOS平台暂不支持下载文件操作。 -core.control.setGameCanvasTranslate(canvasId, x, y) -设置大地图的偏移量 +core.copy(data) +将一段内容拷贝到剪切板。 -core.control.updateViewport() -更新大地图的可见区域 +core.myconfirm(hint, yesCallback, noCallback) +弹窗绘制一段提示信息并让用户确认。hint为提示信息。 +yesCallback和noCallback分别为确定和取消的回调函数。 +此函数和core.drawConfirmBox的区别主要在于: + - drawConfirmBox会清掉UI层原有的绘制信息,此函数不会清除。 + - drawConfirmBox可以用键盘进行操作,此函数必须用鼠标点击。 +另外请注意:本函数的选择也不会进录像,一般用于全局的提示。 +如果需要在事件流中调用请使用显示确认框或显示选择项。 -core.control.gatherFollowers() -立刻聚集所有的跟随者 +core.myprompt(hint, value, callback) +弹窗让用户输入一段内容。hint为提示信息,value为框内的默认填写内容。 +callback为用户点击确认或取消后的回调。 +如果用户点击了确认,则会把框内的内容(可能是空串)传递给callback,否则把null传递给callback。 -core.control.replay() -回放下一个操作 +core.showWithAnimate(obj, speed, callback) / core.hideWithAnimate(obj, speed, callback) +动画淡入或淡出一个对象。 -========== core.enemys.XXX 和怪物相关的函数 ========== -enemys.js主要用来进行怪物相关的内容,比如怪物的特殊属性,伤害和临界计算等。 +core.consoleOpened() +检测当前的控制台是否处于开启状态。仅在全塔属性中的检查控制台开关开启时有效。 +此函数有可能会存在误伤行为,即没开过控制台仍认为开启过。 -core.enemys.hasSpecial(special, test) -测试怪物是否含有某个特殊属性。 -常见用法: core.enemys.hasSpecial(monster.special, 3) ## 测试是否拥有坚固 +core.hashCode(obj) +计算一个对象的哈希值。 -core.enemys.getSpecialText(enemyId) -返回一个列表,包含该怪物ID对应的所有特殊属性。 +core.same(a, b) +判定a和b是否相同,包括类型相同和值相同。 +如果a和b都是数组,则会递归依次比较数组中的值;如果都是对象亦然。 -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为目标楼层ID,canvas为要绘制到的图层,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) [异步] +core.utils.http(type, url, formData, success, error, mimeType, responseType) 发送一个异步HTTP请求。 type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 success为成功后的回调,error为失败后的回调。 +mimeType和responseType如果设置将会覆盖默认值。 + +lzw_encode(s) / lzw_decode(s) +LZW压缩算法,来自https://gist.github.com/revolunet/843889 ``` diff --git a/_docs/element.md b/_docs/element.md index 8d352fef..4fed9b92 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 @@ -56,6 +56,8 @@ type为该装备的类型,必填,和上面装备栏一一对应。例如,0 atk/def/mdef为该装备分别增加的攻防魔防数值(支持负数);如果不加也可省略不写。 +从V2.6开始,可以拓展到任何勇士的属性,如hpmax, atk, def, mdef, experience等等;自行添加的属性包括攻速speed也能使用。 + animate为该装备的攻击动画,仅对type为0时有效。具体可参见[动画和天气系统](#动画和天气系统)。 percentage为该装备是否按比例增加属性。 @@ -151,49 +153,13 @@ yellowWall, blueWall, whiteWall 怪物的特殊属性所对应的数字(special)在脚本编辑中的`getSpecials`中定义,请勿对已有的属性进行修改。 -``` js -function() { - // 获得怪物的特殊属性,每一行定义一个特殊属性。 - // 分为三项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 - // 可以直接写字符串,也可以写个function将怪物传进去 - return [ - [1, "先攻", "怪物首先攻击"], - [2, "魔攻", "怪物无视勇士的防御"], - [3, "坚固", "勇士每回合最多只能对怪物造成1点伤害"], - [4, "2连击", "怪物每回合攻击2次"], - [5, "3连击", "怪物每回合攻击3次"], - [6, function(enemy) {return (enemy.n||4)+"连击";}, function(enemy) {return "怪物每回合攻击"+(enemy.n||4)+"次";}], - [7, "破甲", "战斗前,怪物附加角色防御的"+Math.floor(100*core.values.breakArmor||0)+"%作为伤害"], - [8, "反击", "战斗时,怪物每回合附加角色攻击的"+Math.floor(100*core.values.counterAttack||0)+"%作为伤害,无视角色防御"], - [9, "净化", "战斗前,怪物附加勇士魔防的"+core.values.purify+"倍作为伤害"], - [10, "模仿", "怪物的攻防和勇士攻防相等"], - [11, "吸血", function (enemy) {return "战斗前,怪物首先吸取角色的"+Math.floor(100*enemy.value||0)+"%生命作为伤害"+(enemy.add?",并把伤害数值加到自身生命上":"");}], - [12, "中毒", "战斗后,勇士陷入中毒状态,每一步损失生命"+core.values.poisonDamage+"点"], - [13, "衰弱", "战斗后,勇士陷入衰弱状态,攻防暂时下降"+(core.values.weakValue>=1?core.values.weakValue+"点":parseInt(core.values.weakValue*100)+"%")], - [14, "诅咒", "战斗后,勇士陷入诅咒状态,战斗无法获得金币和经验"], - [15, "领域", function (enemy) {return "经过怪物周围"+(enemy.range||1)+"格时自动减生命"+(enemy.value||0)+"点";}], - [16, "夹击", "经过两只相同的怪物中间,勇士生命值变成一半"], - [17, "仇恨", "战斗前,怪物附加之前积累的仇恨值作为伤害"+(core.flags.hatredDecrease?";战斗后,释放一半的仇恨值":"")+"。(每杀死一个怪物获得"+(core.values.hatred||0)+"点仇恨值)"], - [18, "阻击", function (enemy) {return "经过怪物的十字领域时自动减生命"+(enemy.value||0)+"点,同时怪物后退一格";}], - [19, "自爆", "战斗后勇士的生命值变成1"], - [20, "无敌", "勇士无法打败怪物,除非拥有十字架"], - [21, "退化", function (enemy) {return "战斗后勇士永久下降"+(enemy.atkValue||0)+"点攻击和"+(enemy.defValue||0)+"点防御";}], - [22, "固伤", function (enemy) {return "战斗前,怪物对勇士造成"+(enemy.damage||0)+"点固定伤害,无视勇士魔防。";}], - [23, "重生", "怪物被击败后,角色转换楼层则怪物将再次出现"], - [24, "激光", function (enemy) {return "经过怪物同行或同列时自动减生命"+(enemy.value||0)+"点";}] - ]; -} -``` - 多属性可采用数组的写法,比如`'special': [1,3]`视为同时拥有先攻和坚固属性;`'special': [5,10,14,18]`视为拥有3连击、魔防、诅咒、阻击四个属性。 -怪物可以负伤,在`data.js`的全局变量`enableNegativeDamage`中指定。 - -下面的`getSpecialHint`函数则给定了每个特殊属性的详细描述。这个描述将在怪物手册中看到。 +怪物可以负伤,在全塔属性的全局变量`enableNegativeDamage`中指定。 打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。 -如果`data.js`中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 +如果全塔属性中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 拿到幸运金币后,打怪获得的金币将翻倍。 @@ -286,16 +252,12 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 ## 路障,楼梯,传送门 -血网的伤害数值、中毒后每步伤害数值、衰弱时暂时攻防下降的数值,都在 `data.js` 的values内定义。 +血网的伤害数值、中毒后每步伤害数值、衰弱时暂时攻防下降的数值,都在全塔属性的values内定义。 路障同样会尽量被自动寻路绕过。 有关楼梯和传送门,必须在该层样板的changeFloor里指定传送点的目标。 -![楼层转换](./img/changefloor.png) - -!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。如(6,0)代表最上面一行的正中间一列。** - floorId指定的是目标楼层的唯一标识符(ID)。 也可以写`"floorId": ":before"`和`"floorId": ":next"`表示上一楼和下一楼。 @@ -347,7 +309,7 @@ floorId指定的是目标楼层的唯一标识符(ID)。 从V2.4开始,H5魔塔开始支持大地图。 -大地图在创建时可以指定宽高,要求**宽和高都不得小于13,且宽高之积不超过1000**。 +大地图在创建时可以指定宽高,要求**宽和高都不得小于13(15x15版本则是不小于15),且宽高之积不超过1000**。 大地图一旦创建成功则不得修改宽高数值。 @@ -355,13 +317,12 @@ floorId指定的是目标楼层的唯一标识符(ID)。 现在我们的H5魔塔支持播放动画,也支持天气系统了。 -要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后再data.js中定义。 +要播放动画,你需要先使用“RM动画导出器”将动画导出,放在animates目录下,然后在全塔属性的animates中定义。 ``` js -"animates": [// 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 - // 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 - "hand", "sword", "zone", "yongchang", "thunder" // 根据需求自行添加 -] +// 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 +// 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 +"animates": ["hand", "sword", "zone", "yongchang", "thunder"] ``` !> 动画必须是animate格式,名称不能使用中文,不能带空格或特殊字符。 @@ -376,9 +337,9 @@ floorId指定的是目标楼层的唯一标识符(ID)。 !> 播放录像时,将默认忽略所有动画。 -目前天气系统只支持雨和雪两种天气。 +目前天气系统支持雨和雪和雾两种天气。 -在每层楼的剧本文件里存在一个weather选项,表示该层楼的默认天气。 +在每层楼的楼层属性中存在一个weather选项,表示该层楼的默认天气。 ``` js // 该层的默认天气。本项可忽略表示晴天,如果写则第一项为"rain","snow"或"fog"代表雨雪雾,第二项为1-10之间的数代表强度。 @@ -394,24 +355,18 @@ floorId指定的是目标楼层的唯一标识符(ID)。 要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在全塔属性中进行定义 ``` js -"bgms": [ // 在此存放所有的bgm,和文件名一致。 - // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'bgm.mp3' -]; -"sounds": [ // 在此存放所有的SE,和文件名一致 - // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3' -] +// 在此存放所有的bgm,和文件名一致。 +// 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 +"bgms": ["bgm.mp3"] + +// 在此存放所有的SE,和文件名一致 +"sounds": ["floor.mp3", "attack.mp3", "door.mp3", "item.mp3", "zone.mp3"] ``` !> 音频名不能使用中文,不能带空格或特殊字符。 -目前BGM支持主流的音乐格式,如mp3, ogg,格式等。不支持mid格式的播放。 - 定义完毕后,我们可以调用`playBgm`/`playSound`事件来播放对应的音乐/音效,有关事件的详细介绍请参见[事件](event)。 **另外,考虑到用户的流量问题,将遵循如下规则:** @@ -443,7 +398,7 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 录像的回放主要有两种方式: 1. 保存成的录像文件(.h5route文件):在标题界面点录像回放,再选择文件即可。 -2. 游戏过程中时的当前录像:随时按R可以进行回放;手机端则长按任何位置3秒以上调出虚拟键盘,再按R。 +2. 游戏过程中时的当前录像:随时按R可以进行回放;手机端则可调出虚拟键盘,再按R。 录像播放过程中,可以进行如下操作: @@ -474,6 +429,9 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 ## 操作说明 +![](img/keyboard.png) + +   diff --git a/_docs/event.md b/_docs/event.md index 86f93c9e..e5a2b7b9 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 @@ -20,11 +20,13 @@ ## 关于V2.0的重要说明 -在V2.0版本中,所有事件均可以使用blockly来进行块的可视化编辑。 +在V2.0以后版本中,所有事件均可以使用blockly来进行块的可视化编辑。 它能通过拖动、复制粘贴等方式帮助你快速生成事件列表,而不用手动打大量字符。 -但是,仍然强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 +下述所说的都是在事件编辑器右边所展示的,该事件的代码化写法。 + +强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 ## 自定义事件 @@ -33,22 +35,18 @@ 所有自定义的事件都是如下的写法: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - "trigger": "action", // 触发的trigger, action代表自定义事件 - "enable": true, // 该事件初始状态下是否处于启用状态 - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + "trigger": "action", // 触发的trigger, action代表自定义事件 + "enable": true, // 该事件初始状态下是否处于启用状态 + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` -这里的`"x,y"`代表该点的横坐标为`x`,纵坐标为`y`;即从左到右第`x`列,从上到下的第`y`行(从0开始计算)。 - 我们上面提到,有很多系统已经默认的事件(例如开门、打怪等,相当于公共事件)。如果我们需要自定义一个事件,则需要`"trigger": "action"`,它表示该点是一个自定义事件。 !> **如果系统本身存在事件(如一个怪物),且你指定了`"trigger": "action"`,则原事件会被覆盖。** @@ -58,17 +56,15 @@ 如果该点本身不存在系统事件,则`"trigger":"action"`可被省略不写: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - "enable": true, // 该事件初始状态下是否处于启用状态 - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + "enable": true, // 该事件初始状态下是否处于启用状态 + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -77,17 +73,15 @@ 默认情况下`enable`是`true`,所以如果`enable`为`true`,该项也可以省略不写: ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false + "noPass": true, // 该点是否不可通行。true代表不可通行,false代表可通行。 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -98,17 +92,15 @@ 因此,除非你想覆盖默认的可通行选项(比如将一个空地设为不可通行),否则该项可以忽略。 ``` js -"events": { // 该楼的所有可能事件列表 - "x,y": { - // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 - // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false - // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略 - "data": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] - } +{ + // 除非你要覆盖该点已存在的系统默认事件,否则"trigger": "action"可以省略 + // 该事件初始状态下是启用状态,则可以省略"enable": true;如果是禁用状态则必须加上"enable": false + // 除非你想覆盖系统默认的可通行状态,否则"noPass"项可以忽略 + "data": [ // 实际执行的事件列表 + // 事件1 + // 事件2 + // ... + ] } ``` @@ -117,14 +109,12 @@ 如果大括号里只有`"data"`,则可以省略大括号和`"data"`,直接写中括号数组,换句话说,上面和下面这种写法也是等价的,可以进行一下比较: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - // 事件1 - // 事件2 - // ... - ] -} +// 如果大括号里只有"data"项(没有"action", "enable"或"noPass"),则可以省略到只剩下中括号 +[ + // 事件1 + // 事件2 + // ... +] ``` 这种简写方式可以极大方便地造塔者进行造塔。 @@ -136,15 +126,13 @@ `"data"`中,是由一系列的自定义事件类型组成。每个元素类似于: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - {"type": "xxx", ...}, // 事件1 - {"type": "xxx", ...}, // 事件2 - // ... - // 按顺序写事件,直到结束 - ] -} +// 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 +[ + {"type": "xxx", ...}, // 事件1 + {"type": "xxx", ...}, // 事件2 + // ... + // 按顺序写事件,直到结束 +] ``` `"type"`为该自定义事件的类型;而后面的`...`则为具体的一些事件参数。 @@ -158,29 +146,23 @@ 使用`{"type": "text"}`可以显示一段文字。后面`"text"`可以指定文字内容。 ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] -} +[ + {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 +] ``` 该项可以简写成直接的字符串的形式,即下面这种方式也是可以的: ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 - "在界面上的一段文字",// 直接简写,和下面写法完全等价 - {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 - // ... - // 按顺序写事件,直到结束 - ] -} +[ + "在界面上的一段文字",// 直接简写,和下面写法完全等价 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 + // ... + // 按顺序写事件,直到结束 +] ``` 所有文字事件均可以进行简写,系统会自动转成`{"type": "text"}`的形式。 @@ -188,15 +170,12 @@ 值得注意的是,系统会自动对文字进行换行;不过我们也可以手动加入`\n`来换行。 ``` js -"events": { // 该楼的所有可能事件列表 - // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 - "x,y": [ // 实际执行的事件列表 +[ "这一段文字特别特别长,但是系统可以对它进行自动换行,因此我们无需手动换行", "这是第一行\n这是第二行\n这是第三行", // ... // 按顺序写事件,直到结束 - ] -} +] ``` 我们可以给文字加上标题或图标,只要以`\t[...]`开头就可以。 @@ -210,7 +189,7 @@ 从V2.5.2以后,新增了绘制大头像的功能。绘制大头像图的基本写法是`\t[1.png]`或者`\t[标题,1.png]`。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "一段普通文字", "\t[勇士,hero]这是一段勇士说的话", "\t[hero]如果使用勇士默认名称也可以直接简写hero", @@ -233,7 +212,7 @@ - `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数且在0到12之间。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\b[up]这段文字显示在当前点上方", "\b[down]这段文字显示在当前点上方", "\t[hero]\b[up,hero]这是一段勇士说的话,会显示在勇士上方", @@ -246,7 +225,7 @@ 还可以使用`\r[...]`来调整剧情文本的颜色。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "这句话是默认颜色,\r[red]将颜色变成红色,\r[blue]将颜色变成蓝色", "\r[#FF00FF]还可以使用RGB值来控制颜色,\r如果不加中括号则回到默认颜色", "\t[hero]\b[up,hero]啊啊啊,别过来,\r[red]别过来!!!\n\r你到底是什么东西!" @@ -255,15 +234,16 @@ 从V2.5.3以后,也可以使用`\f[...]`来同时绘制一张图片。 -其基本写法是`\f[图片名,起始x像素,起始y像素]`,或者`\f[图片名,起始x像素,起始y像素,绘制宽度,绘制高度]`。 +其基本写法是`\f[img,x,y]`,或者`\f[img,x,y,w,h]`,或者`\f[img,sx,sy,sw,sh,x,y,w,h]`。 需要注意的是,这个图片是绘制在UI层上的,下一个事件执行时即会擦除;同时如果使用了\t的图标动画效果,重叠的地方也会被图标动画给覆盖掉。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\t[勇士]\b[up,hero]\f[1.png,100,100]以(100,100)为左上角绘制1.png图片", "\t[hero]\f[1.png,100,100]\f[2.png,300,300]同时绘制了两张图片", - "\f[1.png,100,100,300,300]也可以填写宽高,这样会把图片强制进行放缩到指定的宽高值" + "\f[1.png,100,100,300,300]也可以填写宽高,这样会把图片强制进行放缩到指定的宽高值", + "\f[1.png,64,64,128,128,100,100,128,128]裁剪1.png上以(64,64)开始的128x128图片,并绘制到画布的(100,100)处" ] ``` @@ -272,7 +252,7 @@ 这里可以使用一个合法ID(32x48图块除外),或使用一个系统图标(`core.statusBar.icons`中的内容)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "\t[勇士]\b[up,hero]这是一个飞行器\\i[fly],这是一个破墙镐\\i[pickaxe]", "\t[hero]也可以使用系统图标,比如这是存档\\i[save],这是工具栏\\i[toolbox]", ] @@ -285,7 +265,7 @@ 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "1+2=${1+2}, 4*5+6=${4*5+6}", // 显示"1+2=3, 4*5+6=26" ] ``` @@ -293,7 +273,7 @@ 我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ "你当前的攻击力是${status:atk}, 防御是${status:def},坐标是(${status:x},${status:y})", "你的攻防和的十倍是${10*(status:atk+status:def)}", "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", @@ -305,18 +285,12 @@ - `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 - `flag:xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 -另外,有个小`trick`。是否想立刻知道显示效果? - -你可以用Chrome浏览器打开游戏,按Ctrl+Shift+I打开开发者工具,找到Console(控制台),并中输入`core.drawText("...")` 即可立刻看到文字显示的效果。适当调整文字,使得显示效果满意后,再复制粘贴到你的剧情文本中。 - -![调试](./img/eventdebug.png) - ### autoText:自动剧情文本 使用`{"type": "autoText"}`可以使用剧情文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "autoText", "text": "一段自动显示的剧情文字", "time": 5000} ] ``` @@ -336,8 +310,8 @@ time为可选项,代表该自动文本的时间。可以不指定,不指定 使用`{"type": "scrollText"}`可以使用滚动剧情文本,即将一段文字从屏幕最下方滚动到屏幕最上方。 ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "scrollText", "text": "第一排\n第二牌\n\n空行后的一排", "time": 5000, "async": true}, +[ + {"type": "scrollText", "text": "第一排\n第二牌\n\n空行后的一排", "time": 5000, "lineHeight": 1.4, "async": true}, ] ``` @@ -345,6 +319,8 @@ text为正文文本内容。可以使用`${ }`来计算表达式的值,且使 time为可选项,代表总的滚动时间。默认为5000毫秒。 +lineHeight为可选项,代表行距。默认为1.4。 + async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 可以使用下面的[设置剧情文本的属性](event#setText:设置剧情文本的属性)来对文字颜色、文字大小、粗体、距离左边的偏移量进行设置。 @@ -356,7 +332,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用`{"type": "setText"}`可以设置剧情文本的各项属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3], "time": 70}, {"type": "setText", "position": "up", "offset": 15, "bold": true, "titlefont": 26, "textfont": 17}, "这段话将显示在上方(距离顶端15像素),标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色,标题26px,正文17px,70毫秒速度打字机效果", @@ -389,7 +365,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 `{"type": "tip"}`可以在左上角显示一段提示文字。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "tip", "text": "这段话将在左上角以气泡形式显示"} ] ``` @@ -401,7 +377,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 使用`{"type": "comment"}`可以添加一段注释 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "comment", "text": "这是一段会被跳过的注释内容"} ] ``` @@ -415,7 +391,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "...", "value": "..."}, // 设置一个属性、道具或自定义Flag ] ``` @@ -427,7 +403,7 @@ name为你要修改的属性/道具/Flag,每次只能修改一个值。写法 value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`的写法表示勇士当前属性,道具个数和某个变量/Flag值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 {"type": "setValue", "name": "status:money", "value": "1000" } // 将金币数设为1000(不是+1000) {"type": "setValue", "name": "status:hp", "value": "status:hp*2" } // 生命值翻倍 @@ -440,20 +416,20 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 另外注意一点的是,如果hp被设置成了0或以下,将触发lose事件,直接死亡。 -### setValue2:增减勇士的某个属性、道具个数,或某个变量/Flag的值 +### addValue:增减勇士的某个属性、道具个数,或某个变量/Flag的值 和`{"type": "setValue"}`的写法完全相同,不过此项是可以直接将值加减到原始数值上。 即下面的写法是等价的: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setValue", "name": "status:atk", "value": "status:atk+10" } // 攻击提高10点 - {"type": "setValue2", "name": "status:atk", "value": "10" } // 和上面写法等价 + {"type": "addVakue", "name": "status:atk", "value": "10" } // 和上面写法等价 {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey-3" } // 黄钥匙个数-3 - {"type": "setValue2", "name": "item:yellowKey", "value": "-3" } // 和上面写法等价 + {"type": "addValue", "name": "item:yellowKey", "value": "-3" } // 和上面写法等价 {"type": "setValue", "name": "flag:door2", "value": "flag:door2+1" } // 将变量door值+1 - {"type": "setValue2", "name": "flag:door2", "value": "01" } // 和上面写法等价 + {"type": "addValue", "name": "flag:door2", "value": "01" } // 和上面写法等价 ] ``` @@ -462,7 +438,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 使用`{"type":"setFloor"}`可以设置某层楼的楼层属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setFloor", "name": "title", "value": "'主塔 0 层'" } // 设置当前楼层的中文名为主塔0层 {"type": "setFloor", "name": "canFlyTo", "floorId": "MT2", "value": "false" } // 设置MT2层不可飞行 {"type": "setFloor", "name": "cannotViewMap", "floorId": "MT0", "value": "true" } // 设置MT0层不可被浏览地图 @@ -473,8 +449,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam ] ``` -name为必填项,代表要修改的楼层属性。其和楼层属性中一一对应,目前只能为`"title", "name", "canFlyTo", "canUseQuickShop", "cannotViewMap", "cannotMoveDirectly", "color", "weather", -"defaultGround", "images", "item_ratio", "upFloor", "bgm", "downFloor", "underGround"`。 +name为必填项,代表要修改的楼层属性,和楼层属性中的一一对应。 floorId为可选项,代表要修改的楼层ID;可以省略代表当前楼层。 @@ -487,13 +462,12 @@ value为必填项,代表要修改到的数值。其应该和楼层属性中的 使用`{"type":"setGlobalAttribute"}`可以设置一个全局属性。 ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "setGlobalAttribute", "name": "font", "value": "Verdana"}, // 设置字体为Verdana +[ + {"type": "setGlobalAttribute", "name": "font", "value": "Verdana"}, // 设置字体为Verdana ] ``` -name必填项,代表要修改的全局属性。目前只能为`"font", "statusLeftBackground", "statusTopBackground", "toolsBackground", -"borderColor", "statusBarColor", "hardLabelColor", "floorChangingBackground", "floorChangingTextColor"`。 +name必填项,代表要修改的全局属性。 value为必填项,代表要修改到的结果。此项无需再手动加单引号。 @@ -502,14 +476,12 @@ value为必填项,代表要修改到的结果。此项无需再手动加单引 使用`{"type":"setGlobalValue"}`可以设置一个全局数值。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setGlobalValue", "name": "lavaDamage", "value": 200}, // 设置血网伤害为200 ] ``` -name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。目前只能为`"lavaDamage", "poisonDamage", "weakValue", "redJewel", -"blueJewel", "greenJewel", "redPotion", "bluePotion", "yellowPotion", "greenPotion", "breakArmor", "counterAttack", -"purify", "hatred", "moveSpeed", "animateSpeed"`。 +name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。 value为必填项,代表要修改到的结果。该项必须是个数值。 @@ -518,15 +490,12 @@ value为必填项,代表要修改到的结果。该项必须是个数值。 使用`{"type":"setGlobalFlag"}`可以设置一个系统开关。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setGlobalFlag", "name": "enableMDef", "value": false}, // 不在状态栏显示魔防值 ] ``` -name必填项,代表要修改的系统开关,其是全塔属性中的flags中的一部分。目前只能为`"enableFloor", "enableName", "enableLv", -"enableHPMax", "enableMana", "enableMDef", "enableMoney", "enableExperience", "enableLevelUp", "levelUpLeftMode", -"enableKeys", "enablePZF", "enableDebuff", "enableSkill", "flyNearStair", "enableAddPoint", "enableNegativeDamage", -"useLoop", "enableGentleClick", "canGoDeadZone", "enableMoveDirectly", "disableShopOnDamage"`。 +name必填项,代表要修改的系统开关,其是全塔属性中的flags中的一部分。 value为必填项,只能为true或false,代表要修改到的结果。 @@ -537,7 +506,7 @@ value为必填项,只能为true或false,代表要修改到的结果。 使用`{"type":"show"}`可以将一个本身禁用的事件启用。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "show", "loc": [3,6], "floorId": "MT1", "time": 500}, // 启用MT1层[3,6]位置事件,动画500ms {"type": "show", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 {"type": "show", "loc": [3,6]}, // 如果不指定动画时间,则立刻显示,否则动画效果逐渐显示,time为动画时间 @@ -573,7 +542,7 @@ loc同样可以简单的写[x,y]表示单个点,或二维数组[[x1,y1],[x2,y2 NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}`,可以不写loc选项代表当前事件,可以指定time使NPC动画消失。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hide", "loc": [3,6], "floorId": "MT1", "time": 500}, // 禁用MT1层[3,6]位置事件,动画500ms {"type": "hide", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 {"type": "hide", "loc": [3,6]}, // 如果不指定动画时间,则立刻消失,否则动画效果逐渐消失,time为动画时间 @@ -593,7 +562,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` 其基本写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "trigger", "loc": [3,6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 "执行trigger后,这段文字将不会再被显示" ] @@ -614,11 +583,12 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` 其基本写法如下: ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "insert", "name": "加点事件"}, // 插入公共事件:加点事件 - {"type": "insert", "name": "毒衰咒处理"}, // 插入公共事件:毒衰咒处理 +[ + {"type": "insert", "name": "加点事件", "args": [10] }, // 插入公共事件:加点事件,传入参数10 + {"type": "insert", "name": "毒衰咒处理", "args": [0]}, // 插入公共事件:毒衰咒处理,传入参数0 {"type": "insert", "loc": [3,6]}, // 插入[3,6]点的事件并执行 {"type": "insert", "loc": [10,10], "floorId": "MT1"}, // 插入MT1层[10,10]点的事件并执行 + {"type": "insert", "loc": [2,2], "args": [1,"flag:abc","status:atk+status:def"]}, // 传入三个参数 "上面的插入事件执行完毕后会接着继续执行后面的事件" ] ``` @@ -630,6 +600,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` - 否则,如果写了`"loc": [x,y]`,则会插入另一个地点的事件 - loc为另一个地点的坐标 - floorId可选,代表另一个地点所在的楼层;如果不写则默认为当前层。 + - 从V2.6开始,还可以传可选的which,可以为`afterBattle`/`afterGetItem`/`afterOpenDoor`,代表插入该点的战后/获得道具后/开门后事件。 和`type:trigger`不同的是,**`type:trigger`是立刻将当前事件结束(剩下所有内容都忽略),然后重新启动另一个地点的action事件。** @@ -637,12 +608,22 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` **这个过程中,当前事件不会被结束,当前的楼层和事件坐标不会发生改变。** 插入的事件执行完毕后,会继续执行接下来的内容。 +从V2.6开始,插入事件允许传参。如果需要传参,则需要增加一个`args`数组。 + +例如: `"args": [1,"flag:abc","status:atk+status:def"]` 传入了三个参数。 + +系统会自动把`flag:arg1`设置为第一个参数数值,`flag:arg2`设置为第二个参数数值,等等。 + +(`flag:arg0`则会被置为公共事件名称,或者插入的点的坐标) + +即可在事件中直接取用`flag:arg1`等等来获得各项参数值!。 + ### revisit:立即重启当前事件 revisit和trigger完全相同,只不过是立刻触发的还是本地点的事件 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "revisit"}, // 立即触发本事件,等价于 {"type": "trigger", "loc": [x,y]} "执行revisit后,这段文字将不会再被显示" ] @@ -661,7 +642,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 例如玩家点击商人的"离开"选项,则可以调用exit返回游戏。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "exit" }, // 立即结束事件并恢复游戏,一切列表中的事件都将不再被执行 "执行exit后,这段文字将不会再被显示" ] @@ -672,7 +653,7 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setBlock", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层的(3,3)点变成数字233 {"type": "setBlock", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBlock", "number": 57}, // loc也可省略,默认为当前点 @@ -700,7 +681,7 @@ number为**要更改到的数字**,有关“数字”的定义详见参见[素 有关贴图说明请参见[使用自己的图片作为某层楼的背景/前景素材](personalization#使用自己的图片作为某层楼的背景前景素材)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideFloorImg", "loc": [3,6], "floorId": "MT1"}, // 隐藏[3,6]的贴图 {"type": "hideFloorImg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 {"type": "hideFloorImg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 @@ -720,7 +701,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 其做法和参数,和隐藏贴图是完全一致的。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showFloorImg", "loc": [3,6], "floorId": "MT1"}, // 显示[3,6]的贴图 ] ``` @@ -732,7 +713,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 从V2.4.1开始,允许绘制三层图层(背景层,事件层和前景层)。使用`hideBgFgMap`可以隐藏背景或前景层中的图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 隐藏MT1层[3,6]的背景层图块 {"type": "hideBgFgMap", "name": "bg", "loc": [3,6]}, // 如果是当前层,则可以省略floorId项 {"type": "hideBgFgMap", "name": "fg", "loc": [[3,6],[2,9],[1,2]]} // 我们也可以同时隐藏多个贴图。 @@ -752,7 +733,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 其做法和参数,和隐藏贴图是完全一致的。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showBgFgMap", "name": "bg", "loc": [3,6], "floorId": "MT1"}, // 显示MT1层[3,6]的前景层图块 ] ``` @@ -762,7 +743,7 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 我们可以采用 `{"type": "setBgFgBlock"}` 来改变某个背景或前景层地图块。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setBgFgBlock", "name": "bg", "floorId": "MT1", "loc": [3,3], "number": 233}, // 将MT1层背景层的(3,3)点变成数字233 {"type": "setBgFgBlock", "name": "bg", "loc": [2,1], "number": 121}, // 省略floorId则默认为本层 {"type": "setBgFgBlock", "name": "fg", "number": 57}, // loc也可省略,默认为当前点 @@ -782,7 +763,7 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 使用`{"type": "setHeroIcon"}`可以更改角色行走图。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setHeroIcon", "name": "hero2.png"}, // 将勇士行走图改成hero2.png;必须在全塔属性的images中被定义过。 {"type": "setHeroIcon"}, // 如果不加name则恢复最初默认状态 {"type": "setValue", "name": "status:name", "value": "'可绒'"}, // 修改勇士名;请注意value必须加单引号。 @@ -824,7 +805,7 @@ name是可选的,代表目标行走图的文件名。 基本写法:`{"type": "sleep", "time": xxx}` ,其中xxx为指定的毫秒数。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "sleep", "time": 1000}, // 等待1000ms "等待1000ms后才开始执行这个事件", {"type": "sleep", "time": 2000, "noSkip": true}, // 等待2000毫秒,且不可被跳过 @@ -839,12 +820,10 @@ name是可选的,代表目标行走图的文件名。 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 -例如,《宿命的旋律》中,一区有个骷髅队长,当你拿了它周围三个物品时,就会立刻触发强制战斗事件。这时候就可以用`{"type": "battle"}` 实现。 - 其基本写法是: `{"type": "battle", "id": xxx}`,其中xxx为怪物ID。 ``` js -"10,4": [ // 开门后走进去的事件:强制战斗 +[ "\t[blackKing]你终于还是来了。", "\t[hero]放开我们的公主!", "\t[blackKing]如果我不愿意呢?", @@ -862,8 +841,6 @@ name是可选的,代表目标行走图的文件名。 如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。 -打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](#加点事件)。 - 强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。 ### openDoor:开门 @@ -871,7 +848,7 @@ name是可选的,代表目标行走图的文件名。 调用`{"type":"openDoor"}`可以打开一扇门。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId {"type": "openDoor", "loc": [3,6], "needKey": true} // 打开此门需要钥匙 @@ -888,6 +865,30 @@ needKey是可选的,如果设置为true则需要钥匙才能打开此门。如 !> needKey仅对当前楼层开门有效!跨楼层的门仍然不需要钥匙即可打开,如有需求请自行判定。 +### closeDoor:关门 + +从V2.6开始提供了关门事件`{"type": "closeDoor"}`,拥有关门动画和对应的音效。 + +``` js +[ + {"type": "closeDoor", "id": "yellowDoor", "loc": [3,6]}, // 给(3,6)点关上黄门 + {"type": "closeDoor", "id": "specialDoor"}, // 不写loc则视为当前点 +] +``` + +id为你要关门的ID,需要是一个合法的门,系统默认只支持如下几种: + +``` +yellowDoor, blueDoor, redDoor, greenDoor, specialDoor, steelDoor, +yellowWall, blueWall, whiteWall +``` + +如果需要自己添加门,请参考[新增门和对应的钥匙](personalization#新增门和对应的钥匙)。 + +loc可选,为要关的位置,不填默认为当前点。 + +关门事件需要保证该点是空地,否则将无视此事件。 + ### changeFloor:楼层切换 在事件中也可以对楼层进行切换。一个比较典型的例子就是TSW中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。 @@ -895,7 +896,7 @@ needKey是可选的,如果设置为true则需要钥匙才能打开此门。如 changeFloor的事件写法大致如下。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changeFloor", "floorId": "sample0","loc": [10, 10], "direction": "left", "time": 1000 }, //后面几项依次为楼层id,楼层位置(这两项为必填);勇士方向可选,切换时间也是可选。 ] @@ -911,14 +912,14 @@ time为可选的,指定的话将作为楼层切换动画的时间。 **如果time指定为小于100,则视为没有楼层切换动画。** -### changePos:当前位置切换/勇士转向 +### changePos:当前位置切换/set勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 这时候可以用changePos。其参数和changeFloor类似,但少了floorId和time两个选项。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changePos", "loc": [10,10], "direction": "left"}, // 直接切换勇士的坐标,loc为目标地点,后面勇士换位后方向 {"type": "changePos", "loc", [10,10]}, // 如无需指定方向则direction可省略 {"type": "changePos", "direction": "left"} // loc也可省略,只指定direction;此时等价于当前勇士转向到某个方向。 @@ -930,7 +931,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 调用`{"type": "useItem"}`可以使用一个道具。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "changePos", "id": "pickaxe"}, // 尝试使用破 {"type": "changePos", "id": "bomb"}, // 尝试使用炸 {"type": "changePos", "id": "centerFly"} // 尝试使用飞 @@ -956,7 +957,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 使用 `{"type": "follow"}` 可以让一个npc加入跟随。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "follow", "name": "npc.png"}, // 将 npc.png 这个行走图加入跟随 {"type": "follow", "name": "hero.png"}, // 再将另一个行走图加入跟随 ] @@ -971,7 +972,7 @@ name所指定的图片必须存在,在全塔属性中的images中被定义过 使用 `{"type": "unfollow"}` 来取消一个跟随。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "unfollow", "name": "npc.png"}, // 将 npc.png 这个行走图取消跟随 {"type": "follow"}, // 取消所有跟随 ] @@ -998,7 +999,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 有关动画的详细介绍可参见[动画和天气系统](element#动画和天气系统)。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "animate", "name": "yongchang", "loc": [1,3]}, // 在(1,3)显示“咏唱魔法”动画 {"type": "animate", "name": "zone", "loc": "hero"}, // 在勇士位置显示“领域”动画 {"type": "animate", "name": "hand"}, // 可以不指定loc,则默认为当前事件点 @@ -1021,10 +1022,11 @@ loc可忽略,如果忽略则显示为事件当前点。 我们可以使用 `{"type": "showImage"}` 来显示一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "showImage", "code": 1, "image": "bg.jpg", "loc": [231,297], "dw": 100, "dy" : 100, "opacity": 1, "time" : 0}, // 在(231,297)显示bg.jpg - {"type": "showImage", "code": 12, "image": "1.png", "loc": [209,267], "dw": 100, "dy" : 100, "opacity": 0.5, "time" : 1000}, // 在(209,267)渐变显示1.png,渐变时间为1000毫秒,完成时不透明度为0.5,这张图片将遮盖上一张 - {"type": "showImage", "code": 8, "image": "hero.png", "loc": [349,367], "dw": 50, "dy" : 50, "opacity": 1, "time" : 500, "async": true}, // 在(209,267)渐变显示hero.png,大小为原图片的一半,渐变时间为500毫秒,异步执行;这张图片将被上一张遮盖 +[ + {"type": "showImage", "code": 1, "image": "bg.jpg", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示bg.jpg + {"type": "showImage", "code": 12, "image": "1.png", "loc": [209,267], "opacity": 0.5, "time" : 1000}, // 在(209,267)渐变显示1.png,渐变时间为1000毫秒,完成时不透明度为0.5,这张图片将遮盖上一张 + {"type": "showImage", "code": 8, "image": "hero.png", "loc": [349,367], "opacity": 1, "time" : 500, "async": true}, // 在(209,267)渐变显示hero.png,渐变时间为500毫秒,异步执行;这张图片将被上一张遮盖 + {"type": "showImage", "code": 10, "image": "hero.png", "sloc": [100,100,100,100], "loc": [0,0,100,100], "opacity": 1, "time": 0} // 截取原图的一部分绘制到画布上的一部分。 ] ``` @@ -1032,9 +1034,9 @@ code为图片编号,如果两张图片重叠,编号较大会覆盖编号较 image为图片名。**请确保图片在全塔属性中的images中被定义过。** -loc为图片左上角坐标,以像素为单位进行计算。 +sloc为可选项;如果设置了则是个2或4元组,代表裁剪原始图片的左上角像素位置和宽高。 -dw和dh为图片的横向、纵向放大率,默认值为100,即不进行缩放。 +loc为2或4元组,代表要绘制的画布上的左上角像素位置和宽高。 opacity为图片不透明度,在0~1之间,默认值为1,即不透明。 @@ -1047,7 +1049,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "showTextImage"}` 以图片的方式显示文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showTextImage", "code": 1, "text": "第一排\n第二排\n\n空行后的一排", "loc": [231,297], "opacity": 1, "time" : 0}, // 在(231,297)显示"第一排\n第二排\n\n空行后的一排" ] ``` @@ -1060,6 +1062,8 @@ loc为图片左上角坐标,以像素为单位进行计算。 opacity为图片不透明度,在0~1之间,默认值为1,即不透明。 +lineHeight为可选项,代表行距。默认为1.4。 + time为渐变时间,默认值为0,即不渐变直接显示。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 @@ -1071,7 +1075,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "hideImage"}` 来清除一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "hideImage", "code": 1, "time" : 0}, // 使1号图片消失 {"type": "hideImage", "code": 12, "time" : 1000}, // 使12号图片渐变消失,时间为1000毫秒 ] @@ -1088,7 +1092,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "showGif"}` 来显示一张图片。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "showGif", "name": "timg.gif", "loc": [231,297]}, // 在(231,297)显示一张动图 {"type": "showGif"} // 如果不指定name则清除所有动图。 ] @@ -1105,7 +1109,7 @@ loc为动图左上角坐标,以像素为单位进行计算。 我们可以使用 `{"type": "moveImage"}` 来造成图片移动,淡入淡出等效果。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "moveImage", "code": 1, "to": [22,333], "opacity": 1, "time": 1000}, // 将1号图片移动到(22,333),动画时间为1000ms {"type": "moveImage", "code": 12, "opacity": 0.5, "time": 500}, // 将二号图片的透明度变为0.5,动画时间500ms {"type": "moveImage", "code": 1, "to": [109,167], "opacity": 0, "time": 300, "async": true}, // 将1号图片移动到(109,167),透明度设为0(不可见),动画时间300ms,异步执行 @@ -1122,15 +1126,15 @@ time为总移动的时间。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 -### setFg:更改画面色调 +### setCurtain:更改画面色调 -我们可以使用 `{"type": "setFg"}` 来更改画面色调。 +我们可以使用 `{"type": "setCurtain"}` 来更改画面色调。 ``` js -"x,y": [ // 实际执行的事件列表 - {"type": "setFg", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 - {"type": "setFg", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 - {"type": "setFg"} // 如果不指定color则恢复原样。 +[ + {"type": "setCurtain", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒 + {"type": "setCurtain", "color": [0,0,0], "async": true}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间),且异步执行 + {"type": "setCurtain"} // 如果不指定color则恢复原样。 ] ``` @@ -1149,7 +1153,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "screenFlash"}` 来进行画面闪烁。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "screenFlash", "color": [255,255,255,0.6], "time": 500, "times": 1}, // 闪光为白色,不透明度0.6,动画时间1000毫秒 {"type": "screenFlash", "color": [255,0,0,1], "time": 100, "times": 2, "async": true}, // 闪光为红色,强度最大,动画时间100毫秒,闪烁两次且异步执行 ] @@ -1170,7 +1174,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 我们可以使用 `{"type": "setWeather"}` 来更改天气。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "setWeather", "name": "rain", "level": 6}, // 更改为雨天,强度为6级 {"type": "setWeather", "name": "snow", "level": 3}, // 更改为雪天,强度为3级 {"type": "setWeather"} // 更改回晴天 @@ -1194,10 +1198,9 @@ level为天气的强度等级,在1-10之间。1级为最弱,10级为最强 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),loc为位置可选,steps为移动数组 - {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 - "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 + "right", "right", "down" // 向右两格,向下一格 ], "keep": true, "async":true }, // keep可选,如果为true则不消失,否则渐变消失;async可选,如果为true则异步执行。 ] ``` @@ -1206,9 +1209,7 @@ time选项必须指定,为每移动一步所需要用到的时间。 loc为需要移动的事件位置。可以省略,如果省略则移动本事件。 -steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。 - -如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。 +steps为一个数组,其每一项是`up, down, left, right`之一,表示这一步应该朝哪个方向走。 keep为一个可选项,代表该事件移动完毕后是否消失。如果该项指定了并为true,则移动完毕后将不消失,否则以动画效果消失。 @@ -1223,7 +1224,7 @@ keep为一个可选项,代表该事件移动完毕后是否消失。如果该 ``` js "4,3": [ // [4,3]是一个NPC,比如小偷 {"type": "move", "time": 750, "steps": [ // 向上移动两格,每步750毫秒 - {"direction": "up", "value": 2}, + "up", "up" ], "keep": true}, // 移动完毕后不消失 ], "4,1": { // [4,1]为目标地点 @@ -1247,13 +1248,14 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ``` js "x,y": [ // 实际执行的事件列表 {"type": "moveHero", "time": 750, "async": true, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组 - {"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失 - "down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1 + "down", "right", "forward", "backward" // 向下、右、前、后各走一步 ]}, ] ``` -可以看到,和上面的move事件几乎完全相同,除了不能指定loc,且少了immediateHide选项。 +可以看到,和上面的move事件几乎完全相同,除了不能指定loc,且少了keep选项。 + +勇士的steps也允许`forward`和`backward`,即前进和后退。 不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。 @@ -1266,7 +1268,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "keep": true, "async": true}, ] ``` @@ -1290,7 +1292,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 下面是该事件常见的写法: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "jump", "loc": [3,6], "time": 750, "async": true}, ] ``` @@ -1309,7 +1311,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 值得注意的是,额外添加进文件的背景音乐,需在main.js中this.bgms里加载它。 -目前支持mp3/ogg/wav/mid等多种格式的音乐播放。 +目前支持mp3/ogg/wav等多种格式的音乐播放。 有关BGM播放的详细说明参见[背景音乐](element#背景音乐) @@ -1343,6 +1345,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 +从V2.6开始,也可以加`"stop": true`来停止之前正在播放的音效。 + ### stopSound:停止所有音效 使用`{"type": "stopSound"}`可以停止所有音效。 @@ -1367,6 +1371,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 该事件会显示获胜页面,并重新游戏。 +可以增加`"norank": 1`来表示该结局不计入榜单。 + !> 如果`reason`不为空,则会以reason作为获胜的结局! ### lose:游戏失败 @@ -1398,7 +1404,7 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用`{"type": "input"}`可以接受用户的输入的数字。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "input", "text": "请输入一个数"}, // 显示一个弹窗让用户输入数字 "你刚刚输入的数是${flag:input}" // 输入结果将被赋值为flag:input ] @@ -1417,7 +1423,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 类似于input事件,使用`{"type": "input2"}`可以接受用户的输入的文本。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "input2", "text": "请输入你的ID"}, // 显示一个弹窗让用户输入文本 "你好,${flag:input},欢迎来到本塔" // 输入结果将被赋值为flag:input ] @@ -1438,7 +1444,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "if", "condition": "...", // 测试某个条件 "true": [ // 条件成立则执行true里面的事件 @@ -1459,7 +1465,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 例如下面这个例子,每次将检查你的攻击力是否大于500,不是的场合将给你的攻击力加100点。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "if", "condition": "status:atk>500", // 判断攻击力是否大于500 "true": [ // 条件成立则执行true里面的事件 "你的攻击力已经大于500了!", @@ -1467,7 +1473,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 ], "false": [ // 条件不成立则执行false里的事件 "你当前攻击力为${status:atk}, 不足500!\n给你增加100点攻击力!", - {"type": "setValue", "name": "status:atk", "value": "status:atk+100"}, // 攻击力加100, 接着会执行revisit事件 + {"type": "addValue", "name": "status:atk", "value": "100"}, // 攻击力加100, 接着会执行revisit事件 ] }, {"type", "revisit"}, // 立刻重启本事件, 直到攻击力大于500后结束 @@ -1477,7 +1483,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 需要额外注意的几点: - 给定的表达式(condition)一般需要返回true或false。 -- `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为false。而JS中,`false==0`这个判断是成立的,因此我们可以简单使用 `"flag:npc_times==0"` 来判断某个NPC是否被访问过。 +- `flag:xxx` 可取用一个自定义变量或flag。如果从未设置过该flag,则其值默认为0。 - 即使成功失败的场合不执行事件,对应的true或false数组也需要存在,不过简单的留空就好。 - if可以不断进行嵌套,一层套一层;如成立的场合再进行另一个if判断等。 - if语句内的内容执行完毕后将接着其后面的语句继续执行。 @@ -1490,13 +1496,13 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "switch", "condition": "...", // 计算某个表达式 "caseList": [ {"case": "a", "action": [// 若表达式的值等于a则执行该处事件 ], - {"case": "b", "action": [// 若表达式的值等于b则执行该处事件 + {"case": "b", "nobreak": true, "action": [// 若表达式的值等于b则执行该处事件,不跳出 ], {"case": "default", "action": [ // 没有条件成立则执行该处里的事件 @@ -1513,10 +1519,12 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 如果没有符合的值,则将执行`default`中的列表事件内容。 +nobreak是可选的,如果设置,则在当前条件满足并插入事件后,不跳出多重分歧,而是继续判定下一个条件。 + 例如下面这个例子,将检查当前游戏难度并赠送不同属性。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "switch", "condition": "flag:hard", // 判断当前游戏难度 "caseList": [ {"case": "0", "action": [// 若表达式的值等于0则执行该处事件 @@ -1539,12 +1547,32 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 需要额外注意的几点: -- 各个条件分支的判断是顺序执行的,因此若多个分支的条件都满足,将只执行最靠前的分支,同理,请不要在`default`分支后添加分支,这些分支将不可能被执行。 +- 各个条件分支的判断是顺序执行的,因此若多个分支的条件都满足,将只执行最靠前的分支。 + - 同理,请不要在`default`分支后添加分支,这些分支将不可能被执行。 - `default`分支并不是必要的,如果删除,则在没有满足条件的分支时将不执行任何事件。 - 即使某个场合不执行事件,对应的action数组也需要存在,不过简单的留空就好。 -- switch可以不断进行嵌套,一层套一层;如某条件成立的场合再进行另一个switch判断等。 - switch语句内的内容执行完毕后将接着其后面的语句继续执行。 +另外由于`case`中的内容是会被计算的,因此如下写法也是合法的 + +```js +[ + {"type": "switch", "condition": "true", // 条件:某一项为真时 + "caseList": [ + {"case": "flag:a==1", "action": [ // 如果 flag:a == 1 + "走进了 flag:a==1 分支!" + ], + {"case": "flag:b>=3", "action": [ // 如果 flag:b >= 3 + "走进了 flag:b>=3 分支!" + ], + {"case": "default", "action": [ // 上述两条均布成立 + "上述两条均不成立" + ] + ] + }, +] +``` + ### choices:给用户提供选项 @@ -1557,17 +1585,16 @@ choices是一个很麻烦的事件,它将弹出一个列表供用户进行选 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "choices", "text": "...", // 提示文字 - "color": [255,0,0,1], // 颜色 "choices": [ {"text": "选项1文字", "action": [ // 选项1执行的事件 ]}, - {"text": "选项2文字", "action": [ + {"text": "选项2文字", "color": [255,0,0,1], "action": [ // 选项2执行的事件 ]}, - {"text": "选项3文字", "action": [ + {"text": "选项3文字", "icon": "fly", "action": [ // 选项3执行的事件 ]}, ] @@ -1585,67 +1612,36 @@ action为当用户选择了该选项时将执行的事件。 color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数组([255,0,0,1])。 +icon是可选的,如果设置则会在选项前绘制图标,其可以是一个有效的ID,或者`core.statusBar.icons`中的系统图标。 + 选项可以有任意多个,但一般不要超过6个,否则屏幕可能塞不下。 -下面是一个卖钥匙的事件,是一个比较复杂却也较为典型的if和choices合并使用的样例。 +### confirm:显示确认框 -``` js -"10,11": [ // 商人事件,if语句和choices语句的写法 - // 这部分逻辑相对比较长,细心看,很容易看懂的。 - {"type": "if", "condition": "flag:woman_times==0", // 条件判断:是否从未访问过此商人。 - "true": [ // 如果从未访问过该商人,显示一段文字 - "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", - "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", - "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" - // 第一次访问结束 +`{"type": "confirm"}`将提供一个确认框供用户选择,其基本写法如下: + +```js +[ + {"type": "confirm", "text": "...", // 提示文字 + "default": false, // 是否默认选中【确定】 + "yes": [ + // 点击确定时执行的事件 ], - "false": [ // 如果已经访问过该商人 - {"type": "if", "condition": "flag:woman_times==8", // 条件判断:是否已经出售七把钥匙 - "true": [ // 如果已经出售过七把钥匙,则直接结束 - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", - {"type": "setValue", "name": "item:bigKey", "value": "item:bigKey+1"}, // 获得一把大黄门钥匙 - "\t[老人,woman]我先走了,拜拜~", - {"type":"hide", "time": 500}, // 消失 - {"type":"exit"} // 立刻结束当前事件。下面的 setValue 和 revisit 都不会再执行。 - ], - "false": [ // 否则,显示选择页面 - {"type": "choices", "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", // 显示一个卖钥匙的选择页面 - "choices": [ // 提供四个选项:黄钥匙、蓝钥匙、红钥匙、离开。前三个选项显示需要的金额 - {"text": "黄钥匙(${9+flag:woman_times}金币)", "color": [255,255,0,1], "action": [ // 第一个选项,黄钥匙 - // 选择该选项的执行内容 - {"type": "if", "condition": "status:money>=9+flag:woman_times", // 条件判断:钱够不够 - "true": [ - {"type": "setValue", "name": "status:money", "value": "status:money-(9+flag:woman_times)"}, // 扣减金钱 - {"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+1"}, // 增加黄钥匙 - // 然后会继续执行下面的setValue来增加商人访问次数 - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - {"type": "revisit"} // 直接重新访问;不执行下面的setValue来增加访问次数 - ] - } - ]}, - {"text": "蓝钥匙(${18+2*flag:woman_times}金币)", "color": [0,0,255,1], "action": [ // 第二个选项:蓝钥匙 - // 逻辑和上面黄钥匙完全相同,略 - ]}, - {"text": "红钥匙(${36+4*flag:woman_times}金币)", "color": [255,0,0,1], "action": [ // 第三个选项:红钥匙 - // 逻辑和上面黄钥匙完全相同,略 - ]}, - {"text": "离开", "action": [ // 第四个选项:离开 - {"type": "exit"} // 立刻结束当前事件 - ]} - ] - } - ] - } + "no": [ + // 点击取消时执行的事件 ] }, - {"type": "setValue", "name": "flag:woman_times", "value": "flag:woman_times+1"}, // 增加该商人的访问次数。 - {"type": "revisit"} // 立即重新开始这个事件 -], +] ``` +text为必填项,代表提示的文字,支持${}的表达式计算和\n的手动换行。 + +text暂时不支持自动换行、变色\r、图标绘制\i等效果。如有需求请使用choices事件。 + +default可选,如果为true则显示选择项时默认选中【确定】,否则默认选中【取消】。 + +yes和no均为必填项,即用户点击确认或取消后执行的事件。 + ### while:循环处理 从2.2.1样板开始,我们提供了循环处理(while事件)。 @@ -1653,7 +1649,7 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 其大致写法如下: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "while", "condition": "...", // 循环测试某个条件 "data": [ // 条件成立则执行data里面的事件 @@ -1671,10 +1667,10 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 下面是一个输出1到10之间的数字,每隔1秒显示一个的例子。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type":"while", "condition": "flag:i<=10", // 循环处理;注意flag未设置则默认为0 "data":[ - {"type": "setValue", "name": "flag:i", "value": "flag:i+1"}, // 递增i + {"type": "addValue", "name": "flag:i", "value": "1"}, // 递增i "${flag:i}", // 输出i {"type": "sleep","time":1000}, // 等待1秒 ] @@ -1695,10 +1691,10 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 上面的输出例子也可以这么写: ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type":"while", "condition": "true", // 循环处理;永远为真 "data":[ - {"type": "setValue", "name": "flag:i", "value": "flag:i+1"}, // 递增i + {"type": "addValue", "name": "flag:i", "value": "1"}, // 递增i "${flag:i}", // 输出i {"type": "sleep","time":1000}, // 等待1秒 {"type": "if", "condition": "flag:i<10", // 测试i是否小于10 @@ -1718,15 +1714,14 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 使用 `{"type": "wait"}` 可以等待用户进行操作(如点击、按键等)。 当用户执行操作后: -- 如果是键盘的按键操作,则会将flag:type置为0,并且把flag:keycode置为刚刚按键的keycode。 -- 如果是屏幕的点击操作,则会将flag:type置为1,并且设置flag:x和flag:y为刚刚的点击坐标(0-12之间),flag:px和flag:py置为刚刚的像素坐标(0-415之间)。 +- 如果是键盘的按键操作,则会将flag:type置为0,并且把flag:keycode置为按键的keycode。 +- 如果是屏幕的点击操作,则会将flag:type置为1,并且设置flag:x和flag:y为点击的位置坐标,flag:px和flag:py为点击的像素坐标。 下面是一个while事件和wait合并使用的例子,这个例子将不断接收用户的点击或按键行为,并输出该信息。 如果用户按下了ESC或者点击了屏幕正中心,则退出循环。 - ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "while", "condition": "true", // 永久循环 "data": [ {"type": "wait"}, // 等待用户操作 @@ -1749,7 +1744,6 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 ] } ] - ``` ### waitAsync:等待所有异步事件执行完毕 @@ -1770,7 +1764,7 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 ``` js -"x,y": [ // 实际执行的事件列表 +[ {"type": "function", "function": function(){ // 执行一段js脚本 // 这里写js代码 alert(core.getStatus("atk")); // 弹窗显示勇士的攻击力 @@ -1780,11 +1774,11 @@ color为可选的,可以是一个字符串(#FF0000),或者一个RGBA数 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统常见可能会被造塔所用到的的API都在[附录:API列表](api)中给出,请进行参照。 +系统常见可能会被造塔所用到的的API都在[API列表](api)中给出,请进行参照。 **警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** -[附录:API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 +[API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 @@ -1887,7 +1881,6 @@ core.insertAction([ } ``` - 总之,记住如下两点: - 可以使用setBlock来更改一个图块。 @@ -1971,31 +1964,13 @@ if (core.getFlag("door",0)==2) { 如果要对某个怪物进行加点操作,则首先需要修改该怪物的`point`数值,代表怪物本身的加点数值。 -然后在脚本编辑中找到加点事件,双击进行修改。它将返回一个choices事件。修改此函数为我们需要的加点项即可。 +从V2.5.5开始,加点事件移动到了[公共事件](personalization#公共事件)之中,会通过传参的形式来传递怪物的加点值。 -``` js -////// 加点事件 ////// -"addPoint" : function (enemy) { - // 加点事件 - var point = enemy.point; - if (!core.flags.enableAddPoint || !core.isset(point) || point<=0) return []; - - // 加点,返回一个choices事件 - return [ - {"type": "choices", - "choices": [ // 提供三个选项:对于每一点,攻击+1/防御+2/生命+200 - {"text": "攻击+"+(1*point), "action": [ - {"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)} - ]}, - {"text": "防御+"+(2*point), "action": [ - {"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)} - ]}, - {"text": "生命+"+(200*point), "action": [ - {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)} - ]}, - ] - } - ]; +```js +// 如果有加点 +var point = core.material.enemys[enemyId].point; +if (core.flags.enableAddPoint && point > 0) { + core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); } ``` @@ -2018,42 +1993,14 @@ if (core.getFlag("door",0)==2) { "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 "use": "money", // 商店所要使用的。只能是"money"或"experience"。 "commonTimes": true, // 是否使用全局次数 - "mustEnable": true, // 如果未开启则不显示在状态栏中 - "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times作为参数计算。 - // 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。 - // 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。 - // 例如: "need": "25" 就是恒定需要25金币的商店; "need": "20+2*times" 就是第一次访问要20金币,以后每次递增2金币的商店。 - // 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。 - "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字,需手动加换行符。可以使用${need}表示上面的need值。 + "mustEnable": false, // 如果未开启则不显示在状态栏中 + "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times(访问次数)作为参数计算。 + "text": "勇敢的武士啊,给我${need}金币就可以:", // 显示的文字。可以使用${need}表示上面的need值。 "choices": [ // 商店的选项 - {"text": "生命+800", "effect": "status:hp+=800"}, - // 如果有多个effect以分号分开,参见下面的经验商店 - {"text": "攻击+4", "effect": "status:atk+=4"}, - {"text": "防御+4", "effect": "status:def+=4"}, - {"text": "魔防+10", "effect": "status:mdef+=10"} - // effect可以对status,item和flag进行操作。 - // 必须是X+=Y的形式,其中Y可以是一个表达式,以status:xxx, item:xxx或flag:xxx为参数 - // 其他effect样例: - // "item:yellowKey+=1" 黄钥匙+1 - // "item:pickaxe+=3" 破墙镐+3 - // "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍 - ] - }, - { - "id": "expShop1", // 商店唯一ID - "name": "经验之神", - "icon": "pinkShop", - "textInList": "1F经验商店", - "use": "experience", // 该商店使用的是经验进行计算 - "need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1,然后下面选项里给定具体数值 - "text": "勇敢的武士啊,给我若干经验就可以:", - "choices": [ - // 在choices中写need,可以针对每个选项都有不同的需求。 - // 这里的need同样可以以times作为参数,比如 "need": "100+20*times" - {"text": "等级+1", "need": "100", "effect": "status:lv+=1;status:hp+=1000;status:atk+=7;status:def+=7"}, - // 多个effect直接以分号分开即可。如上面的意思是生命+1000,攻击+7,防御+7。 - {"text": "攻击+5", "need": "30", "effect": "status:atk+=5"}, - {"text": "防御+5", "need": "30", "effect": "status:def+=5"}, + // effect可以对status,item和flag进行操作;必须是X+=Y的形式,其中Y可以是一个表达式 + {"text": "生命+800", "effect": "status:hp+=800"}, // 生命+800 + {"text": "攻击+4", "need": 30, "effect": "status:atk+=4"}, // 规定具体的数值 + {"text": "防御+2,魔防+4", "effect": "status:def+=2;status:mdef+=4"}, // 多个效果用分号分开 ] } ], @@ -2075,14 +2022,15 @@ if (core.getFlag("door",0)==2) { - choices 为商店的各个选项,是一个list,每一项是一个选项 - text为显示文字。请注意这里不支持 ${} 的表达式计算。 - effect 为该选项的效果;effect必须是 `status:xxx+=yyy`, `item:xxx+=yyy`或`flag:xxx+=yyy`的形式。即中间必须是+=符号。 - - 如有多个effect(例如升级全属性提升),使用分号分开,参见经验商店的写法。 + - 如有多个effect(例如升级全属性提升),使用分号分开。 像这样定义了全局商店后,即可在快捷栏中看到。 请注意,快捷商店默认是不可被使用的。直到至少调用一次自定义事件中的 `{"type": "openShop"}` 打开商店后,才能真正在快捷栏中被使用。 ``` js -"1,0": [ // 金币商店 +// 事件列表 +[ // 打开商店前,你也可以添加自己的剧情 // 例如,通过if来事件来判断是不是第一次访问商店,是的则显示一段文字(类似宿命的华音那样) {"type": "openShop", "id": "moneyShop1"}, // 这里的id要和data.js中你定义的商店ID完全一致 @@ -2099,6 +2047,27 @@ if (core.getFlag("door",0)==2) { 另外需要注意的一点就是,每层楼都有一个 canUseQuickShop 选项。如果该选项置为false则无法在该层使用快捷商店。 +**从V2.6开始,也提出了“公共事件化的全局商店”,即打开使用全局商店实际上是执行一个公共事件。** + +```js +"shops": [ + // 定义公共事件化的全局商店 + { + "id": "keyShop1", // 商店唯一ID + "textInList": "回收钥匙商店", // 在快捷商店栏中显示的名称 + "mustEnable": false, // 如果未开启则不显示在状态栏中 + "commonEvent": "回收钥匙商店", // 公共事件名 + "args": [], // 向该公共事件传递的参数 + } +] +``` + +`id`, `textInList`, `mustEnable`和上述完全相同。 + +`commonEvent`为公共事件名,即选择此项时要执行的公共事件。 + +`args`为向该公共事件传递的参数,参见[type:insert](#insert:插入公共事件或另一个地点的事件并执行)的说明。 + ## 系统引发的自定义事件 我们知道,所有自定义事件都是需要定义在`"x,y"`处,并且得让用户经过或撞上才能触发的。 @@ -2137,17 +2106,13 @@ if (core.getFlag("door",0)==2) { !> 多个机关门请分别设置开门变量如door1, door2等等。请勿存在两个机关门用相同的变量! -同样,为了实现类似于RMXP中,到达某一层后自动触发某段事件的效果,样板中还存在`firstArrive`事件。 - -当且仅当勇士第一次到达某层时,将会触发此事件。可以利用此事件来显示一些剧情,或再让它调用 `{"type": "trigger"}` 来继续调用其他的事件。 +除此以外,每层楼还提供了`firstArrive`和`eachArrive`事件,分别为首次到达该楼层和每次到达该楼层时执行的事件。 ## 使用炸弹后的事件 上面的afterBattle事件只对和怪物进行战斗后才有会被处理。 -如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在`functions.js`里面的`afterUseBomb`函数进行处理: - -!> V2.0版本可以直接在“脚本编辑 - 使用炸弹后的事件”中双击进行修改! +如果我们想在使用炸弹后也能触发一些事件(如开门),则可以在脚本编辑里面的`afterUseBomb`函数进行处理: ``` js ////// 使用炸弹/圣锤后的事件 ////// @@ -2163,47 +2128,42 @@ if (core.getFlag("door",0)==2) { } ``` -## 滑冰和推箱子事件 +## 滑冰事件 -最新的样板还支持滑冰和推箱子事件。 +从V2.6开始,滑冰事件被重写。现在的滑冰由公共事件执行。 -滑冰事件的数字是167,trigger为ski。 +在新版本中,冰面应该放在背景层上,上面可以放置道具、怪物、门等图块。 -当角色走上冰面时,将触发ski事件,并会一直向前滑行,直到撞上不可通行的块会触发事件(比如撞上怪物会触发battle,撞上门会触发openDoor等等),或者离开冰面为止。 +角色走上冰面后,将一直向前滑行,直到撞上不可通行的图块,或触发事件为止。 -!> 由于H5魔塔只有事件一层,因此滑冰的冰面上将无法摆放任何东西(如怪物,门或道具等);不过可以在战后/开门后/道具后的事件写转变图块成167,从而继续滑冰。 +如果撞上怪物将自动进行战斗,此战斗是强制的,打不过将直接死亡。 -!> 撞上怪物将触发battle进行战斗,该战斗是强制战斗,打不过将直接死亡。 +默认情况下,拾取冰面上道具后将停止滑冰行为。如果要继续滑冰,请在`afterGetItem`中插入公共事件:滑冰事件。打怪和开门同理。 + +!> 滑冰图块的数字是167,请勿修改此数字! + +## 推箱子事件 关于推箱子,存在三种状态:花(168),箱子(169)和已经推到花的箱子(170)。 !> 推箱子的前方不允许存在任何事件(花除外),包括已经禁用的自定义事件。 -推完箱子后将触发functions.js中的afterPushBox事件,你可以在这里进行开门判断。 +推完箱子后将触发脚本编辑中的afterPushBox函数,你可以在这里进行开门判断。 ``` js ////// 推箱子后的事件 ////// "afterPushBox" = function () { - - var noBoxLeft = function () { - // 地图上是否还存在未推到的箱子,如果不存在则返回true,存在则返回false - for (var i=0;i 如果reason不为空,则将会作为结局名! - -当失败(`{"type": "lose"}`,或者被怪强制战斗打死、被领域怪扣血死、中毒导致扣血死,路障导致扣血死等等)事件发生时,将调用`events.js`中的`lose`事件。其直接显示一段文字,并重新开始游戏。 - -``` js -////// 游戏失败事件 ////// -"lose": function(reason) { - core.ui.closePanel(); - var replaying = core.isReplaying(); - core.stopReplay(); - core.waitHeroToStop(function() { - core.drawText([ - "\t[结局1]你死了。\n如题。" - ], function () { - core.events.gameOver(null, replaying); - }); - }) -} -``` - -其参数reason为失败原因。你可以在这里修改失败界面时显示的文字。 - ========================================================================================== [继续阅读下一章:个性化](personalization) diff --git a/_docs/img/console.jpg b/_docs/img/console.jpg new file mode 100644 index 00000000..76d59378 Binary files /dev/null and b/_docs/img/console.jpg differ diff --git a/_docs/img/console1.jpg b/_docs/img/console1.jpg new file mode 100644 index 00000000..46d1f5ba Binary files /dev/null and b/_docs/img/console1.jpg differ diff --git a/_docs/img/elements.jpg b/_docs/img/elements.jpg new file mode 100644 index 00000000..d32ebaf2 Binary files /dev/null and b/_docs/img/elements.jpg differ diff --git a/_docs/img/keyboard.png b/_docs/img/keyboard.png new file mode 100644 index 00000000..c2b9d241 Binary files /dev/null and b/_docs/img/keyboard.png differ diff --git a/_docs/img/plugin.jpg b/_docs/img/plugin.jpg new file mode 100644 index 00000000..d0d69bef Binary files /dev/null and b/_docs/img/plugin.jpg differ diff --git a/_docs/img/sources.jpg b/_docs/img/sources.jpg new file mode 100644 index 00000000..7fce53b3 Binary files /dev/null and b/_docs/img/sources.jpg differ diff --git a/_docs/index.md b/_docs/index.md index cb6a896c..d2f290e2 100644 --- a/_docs/index.md +++ b/_docs/index.md @@ -1,11 +1,10 @@ # HTML5 魔塔样板说明文档 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 但是,现在我们有了HTML5。 HTML5的画布(canvas)以及它被Android/iOS内置浏览器所支持的特性,可以让我们做出真正意义上的全平台覆盖的魔塔。 -事实上,在贴吧的试水发布也证明了,H5魔塔确实是可以成功的。两部即使是复刻的魔塔也受到了不少人的追捧,其流畅的手感和全平台支持的特性,也让很多没办法打开电脑的人爱不释手。 然而,一般而言使用非RMXP制作魔塔往往需要一定的编程技术,HTML5魔塔自然也不例外。但是,为了能让大家更加注重于“做塔”本身,而不用考虑做塔以外的各种脚本问题,我特意制作了这样一部HTML5的魔塔样板。 diff --git a/_docs/personalization.md b/_docs/personalization.md index 51ca82d2..c8f517e6 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -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的在色调层之上) -- ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 160) +- image1\~50**[D]**:图片层;用来绘制图片等操作。(z-index: 100+code, 101~150) +- ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140) - data:数据层;用来绘制一些顶层的或更新比较快的数据,如左上角的提示,战斗界面中数据的变化等等。 (z-index: 170) +请注意:显示图片事件将自动创建一个图片层,z-index是100+图片编号。 + +而,色调层的z-index是25,ui层的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为一个数组,代表当前层所有作为背景素材的图片信息。每一项为一个五元组,分别为该背景素材的x,y,图片名,遮挡方式和帧数。 @@ -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. 看到成功的提示后刷新编辑器即可。 - - - -### 地图生成器使用自定义素材 - -地图生成器是直接从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,107 +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.status.heroStop) { - core.drawTip("请先停止勇士行动"); - 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`改成空字符串`""`,然后再双击进行编辑。 - +除了覆盖楼传事件外,对于快捷商店、虚拟键盘等等也可以进行覆盖,只不过是仿照上述代码重写对应的函数(`openQuickShop`,`openKeyBoard`)即可。 ## 自定义怪物属性 -如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑-getSpecials。 +如果你对现有的怪物不满意,想自行添加怪物属性也是可以的。具体参见脚本编辑的getSpecials。 你需自己指定一个special数字,修改属性名和属性提示文字。后两者可以直接写字符串,或写个函数传入怪物。 @@ -550,7 +379,8 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 // ... 在这里写你要执行脚本 // **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为** if (core.hasItem('...')) { - core.useItem('...'); + core.status.route.push("key:0"); // 记录按键到录像中 + core.useItem('...', true); // 第二个参数true代表该次使用道具是被按键触发的,使用过程不计入录像 } break; @@ -558,6 +388,10 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 强烈建议所有新增的自定义快捷键均给个对应的永久道具可点击,以方便手机端的行为。 +使用`core.status.route.push("key:"+keyCode)`可以将这次按键记录在录像中。 + +!> 如果记录了按键,且使用道具的话,需要将useItem的第二个参数设为true,避免重复记录! + 可以使用altKey来判断Alt键是否被同时按下。 ## 公共事件 @@ -572,13 +406,9 @@ case 89: // 使用该按键的keyCode,比如Y键就是89 ## 插件系统 -在H5中,提供了“插件”系统。具体参见“脚本编辑 - 插件编写”。 +在H5中,提供了“插件”系统。在V2.6中提供了一个插件下拉框,用户可以自行创建和写插件。 -![插件编写](./img/plugin.png) - -当我们在这上面定义了自己需要的函数(插件后),就可以通过任何方式进行调用。 - -在这个插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 +在插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 下面是一个很简单的例子,我编写一个插件函数,其效果是让勇士生命值变成原来的x倍,并令面前的图块消失。 @@ -597,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可以决定是否开启标题界面事件化。 然后就可以使用“事件流”的形式来绘制标题界面、提供选项等等。 @@ -617,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

``` -3. 在editor.html中的statusBar(323行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。 -4. 使用便捷PS工具,打开icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 +3. 在editor.html中的statusBar(383行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。 +4. 使用便捷PS工具,打开project/icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 5. 在main.js的this.statusBar中增加图片、图标和内容的定义。 ``` js this.statusBar = { @@ -672,17 +538,15 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); ## 技能塔的支持 -其实,在HTML5上制作技能塔是完全可行的。 - 要支持技能塔,可能需要如下几个方面: +从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 + - 魔力(和上限)的添加;技能的定义 - 状态栏的显示 - 技能的触发(按键与录像问题) - 技能的效果 -从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 - ### 魔力的定义添加;技能的定义 从V2.5开始,提供了status:mana选项,可以直接代表当前魔力值。 @@ -695,6 +559,8 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); 如果flag:skill不为0,则代表当前处于某个技能开启状态,且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。 +!> 关于魔力上限:样板中默认没有提供status:manamax + ### 状态栏的显示 从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。 @@ -704,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) { @@ -759,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键时,判断技能的道具是否存在,如果存在再使用它。 -!> 1,2,3这三个键被默认绑定到了破炸飞;如果想用的话也是一样,只不过是把已有的实现进行替换。 +!> 由于现在手机端存在拓展键盘,也强烈建议直接覆盖1-8的使用效果,这样手机端使用也非常方便。 ### 技能的效果 @@ -814,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)。 @@ -970,11 +707,11 @@ this.getAchievements = function () { - **`flag:__color__`**, **`flag:__weather__`**, **`flag:__volume__`**: 当前的画面色调、天气和音量。 - **`flag:__events__`**: 当前保存的事件列表,读档时会恢复(适用于在事件中存档) - **`flag:textAttribute`**, **`flag:globalAttribute`**, **`flag:globalFlags`**: 当前的剧情文本属性,当前的全局属性,当前的全局开关。 -- **`flag:cannotMoveDirectly`**, **`flag:clickMove`**: 当前是否不允许瞬间移动,当前用户是否开启了单击瞬移。 +- **`flag:cannotMoveDirectly`**, **`flag:__noClickMove__`**: 当前是否不允许瞬间移动,当前用户是否开启了单击瞬移。 - **`flag:hideStatusBar`**, **`flag:showToolbox`**: 是否隐藏状态栏,是否显示工具栏。 -- **`flag:debug`**, **`flag:consoleOpened`**: 当前是否开启了调试模式,是否开启了控制台。 +- **`flag:debug`**, **`flag:__consoleOpened__`**: 当前是否开启了调试模式,是否开启了控制台。 - **`flag:__seed__`**, **`flag:__rand__`**: 伪随机数生成种子和当前的状态 ========================================================================================== -[继续阅读附录:所有API列表](api) +[继续阅读脚本](script) diff --git a/_docs/script.md b/_docs/script.md new file mode 100644 index 00000000..817a68a6 --- /dev/null +++ b/_docs/script.md @@ -0,0 +1,313 @@ +# 脚本 + +?> 目前版本**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下的文件就会出现各种问题: +- 不容易随着新样板接档进行迁移 +- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 +- …… + +好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 + +如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` + +下面是几个例子,从简单到复杂。 + +### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 + +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + +```js +// 重写ui.js中的_drawBook_drawBackground函数 +core.ui._drawBook_drawBackground = function () { + // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); +} +``` + +### 重写点击楼传事件 + +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如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..."); // 控制台打出一条信息 + drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps +} +``` + +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 + +========================================================================================== + +[继续阅读下一章:API列表](api) + + diff --git a/_docs/start.md b/_docs/start.md index a68ac244..004ea21f 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! @@ -28,6 +28,7 @@ * “地图编辑器”允许你以可视化的方式进行编辑地图。 * “便捷PS工具”能让你很方便的对自定义素材进行添加。参见[自定义素材](personalization#自定义素材)。 * “地图生成器”能让你从已有的截图(如RMXP项目)中立刻生成可被本样板识别的地图数据。 +* “怪物数据导出”能让你从RMXP中导出怪物数据而被H5魔塔使用。 * “RM动画导出器”能让你从RMXP中导出动画而被H5魔塔使用。 * “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。 * “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。 @@ -54,6 +55,8 @@ ### 从RMXP导入已有的地图 +!> 注:现在已经不推荐此方法,如需从RM刻塔请使用 [RM转H5刻塔器使用教程](https://www.bilibili.com/video/av43125840) 进行操作。 + 如果我们想复刻一个现有的,已经被RMXP所制作的塔,也有很便捷的方式,那就是用到我们的“地图生成器”。 首先,我们打开RMXP和对应的项目,可以看到它的地图。 @@ -176,7 +179,7 @@ 之后刷新编辑器即可。 -对于怪物和道具,我们也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 +也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 @@ -230,6 +233,18 @@ HTML5的塔都是可以进行控制台调试的。 更多API和详细参数介绍可参见[API列表](api)。 +## 编辑器的基本操作 + +- **Alt+0~9, Ctrl+0~9** 保存和读取当前选中图块 +- **W/A/S/D** 移动大地图 +- **Ctrl+Z** 撤销上次绘图 +- **Ctrl+Y** 重做上次绘图 +- **PgUp/PgDn** 切换楼层 +- **Ctrl+S** 保存事件编辑器/脚本编辑器 +- **地图上单击** 选中该点 +- **地图上双击** 选中该点图块 +- **地图上右键** 弹出菜单栏,包括选中、复制、清除等操作 +- **事件编辑器中Ctrl+C, Ctrl+X, 右键等** 执行相应操作 ## 报错处理 diff --git a/_server/blockly/MotaAction.g4 b/_server/MotaAction.g4 similarity index 88% rename from _server/blockly/MotaAction.g4 rename to _server/MotaAction.g4 index df4cf03e..a43d0c9a 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -65,6 +65,7 @@ return code; shoplist : shopsub + | shopcommonevent | emptyshop ; @@ -77,6 +78,36 @@ var code = ' \n'; return code; */; +shopcommonevent + : '商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? '执行的公共事件 id' EvalString '参数列表' EvalString? + +/* shopcommonevent +tooltip : 全局商店, 执行一个公共事件 +helpUrl : https://h5mota.com/games/template/docs/#/ +default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +if (EvalString_2) { + if (EvalString_2.indexOf('"')>=0) + throw new Error('请勿在此处使用双引号!尝试使用单引号吧~'); + // 检查是不是数组 + try { + EvalString_2 = JSON.parse(EvalString_2.replace(/'/g, '"')); + if (!(EvalString_2 instanceof Array)) throw new Error(); + } + catch (e) { + throw new Error('参数列表必须是个有效的数组!'); + } +} +var code = { + 'id': IdString_0, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'commonEvent': EvalString_1 +} +if (EvalString_2) code.args = EvalString_2; +code=JSON.stringify(code,null,2)+',\n'; +return code; +*/; + shopsub : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '未开启状态则不显示在列表中' Bool BGNL? NewLine '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND @@ -200,7 +231,7 @@ var loc = ', "loc": ['+Number_0+', '+Number_1+']'; if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); Int_0 = (Int_0!=='') ?(', "time": '+Int_0):''; -Bool_0 = Bool_0 ?'':(', "portalWithoutTrigger": false'); +Bool_0 = Bool_0 ?'':(', "ignoreChangeFloor": false'); var code = '{"floorId": "'+toFloorId+'"'+loc+DirectionEx_List_0+Int_0+Bool_0+' }\n'; return code; */; @@ -228,7 +259,7 @@ action | setText_s | tip_s | setValue_s - | setValue2_s + | addValue_s | setFloor_s | setGlobalAttribute_s | setGlobalValue_s @@ -256,6 +287,7 @@ action | waitAsync_s | battle_s | openDoor_s + | closeDoor_s | changeFloor_s | changePos_0_s | changePos_1_s @@ -267,13 +299,14 @@ action | animate_s | vibrate_s | showImage_s + | showImage_1_s | hideImage_s | showTextImage_s | 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 @@ -298,9 +331,11 @@ action | input_s | input2_s | choices_s + | confirm_s | callBook_s | callSave_s | callLoad_s + | unknown_s | function_s | pass_s ; @@ -379,27 +414,28 @@ return code; */; scrollText_s - : '滚动剧情文本:' '时间' Int '不等待执行完毕' Bool? BGNL? EvalString Newline + : '滚动剧情文本:' '时间' Int '行距' Number '不等待执行完毕' Bool? BGNL? EvalString Newline /* scrollText_s tooltip : scrollText:滚动剧情文本,将从下到上进行滚动显示。 helpUrl : https://h5mota.com/games/template/docs/#/event?id=scrollText%ef%bc%9a%e6%bb%9a%e5%8a%a8%e5%89%a7%e6%83%85%e6%96%87%e6%9c%ac -default : [5000,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] +default : [5000,1.4,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] Bool_0 = Bool_0?', "async": true':''; -var code = '{"type": "scrollText", "text": "'+EvalString_0+'"'+Bool_0+', "time" :'+Int_0+'},\n'; +var code = '{"type": "scrollText", "text": "'+EvalString_0+'"'+Bool_0+', "time" :'+Int_0+', "lineHeight": '+Number_0+'},\n'; return code; */; setText_s - : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' EvalString? BGNL? '标题颜色' EvalString? Colour '正文颜色' EvalString? Colour '背景色' EvalString? Colour BGNL? '粗体' B_1_List '标题字体大小' EvalString? '正文字体大小' EvalString? '打字间隔' EvalString? Newline + : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' EvalString? '对齐' SetTextAlign_List? BGNL? '标题颜色' EvalString? Colour '正文颜色' EvalString? Colour '背景色' EvalString? Colour BGNL? '粗体' B_1_List '标题字体大小' EvalString? '正文字体大小' EvalString? '打字间隔' EvalString? Newline /* setText_s tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填 helpUrl : https://h5mota.com/games/template/docs/#/event?id=settext%EF%BC%9A%E8%AE%BE%E7%BD%AE%E5%89%A7%E6%83%85%E6%96%87%E6%9C%AC%E7%9A%84%E5%B1%9E%E6%80%A7 -default : [null,"","",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',null,"","",""] +default : [null,"",null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',null,"","",""] SetTextPosition_List_0 =SetTextPosition_List_0==='null'?'': ', "position": "'+SetTextPosition_List_0+'"'; +SetTextAlign_List_0 =SetTextAlign_List_0==='null'?'': ', "align": "'+SetTextAlign_List_0+'"'; 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 (EvalString_0) { if (!/^\d+$/.test(EvalString_0))throw new Error('像素偏移量必须是整数或不填'); @@ -437,7 +473,7 @@ if (EvalString_6) { EvalString_6 = ', "time": '+EvalString_6; } B_1_List_0 = B_1_List_0==='null'?'':', "bold": '+B_1_List_0; -var code = '{"type": "setText"'+SetTextPosition_List_0+EvalString_0+EvalString_1+EvalString_2+B_1_List_0+EvalString_3+EvalString_4+EvalString_5+EvalString_6+'},\n'; +var code = '{"type": "setText"'+SetTextPosition_List_0+EvalString_0+SetTextAlign_List_0+EvalString_1+EvalString_2+B_1_List_0+EvalString_3+EvalString_4+EvalString_5+EvalString_6+'},\n'; return code; */; @@ -465,15 +501,15 @@ var code = '{"type": "setValue", "name": "'+idString_e_0+'", "value": "'+express return code; */; -setValue2_s +addValue_s : '数值增减' ':' '名称' idString_e '+=' expression Newline -/* setValue2_s -tooltip : setValue2:增减勇士的某个属性、道具个数, 或某个变量/Flag的值 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setValue2%ef%bc%9a%e5%a2%9e%e5%87%8f%e5%8b%87%e5%a3%ab%e7%9a%84%e6%9f%90%e4%b8%aa%e5%b1%9e%e6%80%a7%e3%80%81%e9%81%93%e5%85%b7%e4%b8%aa%e6%95%b0%ef%bc%8c%e6%88%96%e6%9f%90%e4%b8%aa%e5%8f%98%e9%87%8f%2fFlag%e7%9a%84%e5%80%bc +/* addValue_s +tooltip : addValue:增减勇士的某个属性、道具个数, 或某个变量/Flag的值 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=addValue%ef%bc%9a%e5%a2%9e%e5%87%8f%e5%8b%87%e5%a3%ab%e7%9a%84%e6%9f%90%e4%b8%aa%e5%b1%9e%e6%80%a7%e3%80%81%e9%81%93%e5%85%b7%e4%b8%aa%e6%95%b0%ef%bc%8c%e6%88%96%e6%9f%90%e4%b8%aa%e5%8f%98%e9%87%8f%2fFlag%e7%9a%84%e5%80%bc colour : this.dataColor -var code = '{"type": "setValue2", "name": "'+idString_e_0+'", "value": "'+expression_0+'"},\n'; +var code = '{"type": "addValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"},\n'; return code; */; @@ -614,29 +650,55 @@ return code; */; insert_1_s - : '插入公共事件' EvalString Newline + : '插入公共事件' EvalString '参数列表' EvalString? Newline /* insert_1_s tooltip : insert: 插入公共事件并执行 helpUrl : https://h5mota.com/games/template/docs/#/event?id=insert%ef%bc%9a%e6%8f%92%e5%85%a5%e5%85%ac%e5%85%b1%e4%ba%8b%e4%bb%b6%e6%88%96%e5%8f%a6%e4%b8%80%e4%b8%aa%e5%9c%b0%e7%82%b9%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%b9%b6%e6%89%a7%e8%a1%8c -default : ["加点事件"] +default : ["加点事件", ""] colour : this.eventColor -var code = '{"type": "insert", "name": "'+EvalString_0+'"},\n'; +if (EvalString_1) { + if (EvalString_1.indexOf('"')>=0) + throw new Error('请勿在此处使用双引号!尝试使用单引号吧~'); + // 检查是不是数组 + try { + if (!(JSON.parse(EvalString_1.replace(/'/g, '"')) instanceof Array)) throw new Error(); + } + catch (e) { + throw new Error('参数列表必须是个有效的数组!'); + } + EvalString_1 = ', "args": ' +EvalString_1; +} +var code = '{"type": "insert", "name": "'+EvalString_0+'"'+EvalString_1+'},\n'; return code; */; insert_2_s - : '插入事件' 'x' PosString ',' 'y' PosString '楼层' IdString? Newline + : '插入事件' 'x' PosString ',' 'y' PosString Event_List? '楼层' IdString? '参数列表' EvalString? ENewline /* insert_2_s tooltip : insert: 立即插入另一个地点的事件执行,当前事件不会中断,事件坐标不会改变 helpUrl : https://h5mota.com/games/template/docs/#/event?id=insert%ef%bc%9a%e6%8f%92%e5%85%a5%e5%85%ac%e5%85%b1%e4%ba%8b%e4%bb%b6%e6%88%96%e5%8f%a6%e4%b8%80%e4%b8%aa%e5%9c%b0%e7%82%b9%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%b9%b6%e6%89%a7%e8%a1%8c -default : ["0","0",""] +default : ["0","0",null,"",""] colour : this.eventColor IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); -var code = '{"type": "insert", "loc": ['+PosString_0+','+PosString_1+']'+IdString_0+'},\n'; +if (EvalString_0) { + if (EvalString_0.indexOf('"')>=0) + throw new Error('请勿在此处使用双引号!尝试使用单引号吧~'); + try { + if (!(JSON.parse(EvalString_0.replace(/'/g, '"')) instanceof Array)) throw new Error(); + } + catch (e) { + throw new Error('参数列表必须是个有效的数组!'); + } + EvalString_0 = ', "args": ' +EvalString_0; +} +if (Event_List_0 && Event_List_0 !=='null') + Event_List_0 = ', "which": "'+Event_List_0+'"'; +else Event_List_0 = ''; +var code = '{"type": "insert", "loc": ['+PosString_0+','+PosString_1+']'+Event_List_0+IdString_0+EvalString_0+'},\n'; return code; */; @@ -935,6 +997,23 @@ var code = '{"type": "openDoor"'+floorstr+IdString_0+Bool_0+'},\n'; return code; */; +closeDoor_s + : '关门' 'x' PosString? ',' 'y' PosString? 'ID' IdString Newline + + +/* closeDoor_s +tooltip : closeDoor: 关门事件,需要该点本身无事件 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=opendoor%EF%BC%9A%E5%BC%80%E9%97%A8 +default : ["","","yellowDoor"] +colour : this.dataColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "closeDoor", "id": "'+IdString_0+'"'+floorstr+'},\n'; +return code; +*/; + changeFloor_s : '楼层切换' IdString? 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List '动画时间' Int? Newline @@ -1088,34 +1167,54 @@ return code; */; showImage_s - : '显示图片' '图片编号' Int '图片' EvalString '起点像素位置' 'x' PosString 'y' PosString BGNL? - '放大率 : x' Int '% y' Int '% 不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + : '显示图片' '图片编号' Int '图片' EvalString BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline /* showImage_s tooltip : showImage:显示图片 helpUrl : https://h5mota.com/games/template/docs/#/event?id=showImage%ef%bc%9a%e6%98%be%e7%a4%ba%e5%9b%be%e7%89%87 -default : [1,"bg.jpg","0","0",100,100,1,0,false] +default : [1,"bg.jpg","0","0",1,0,false] colour : this.printColor if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); var async = Bool_0?', "async": true':''; -var code = '{"type": "showImage", "code": '+Int_0+', "image": "'+EvalString_0+'", "loc": ['+PosString_0+','+PosString_1+'], "dw": '+Int_1+', "dh": '+Int_2+', "opacity": '+Number_0+', "time": '+Int_3+async+'},\n'; +var code = '{"type": "showImage", "code": '+Int_0+', "image": "'+EvalString_0+'", "loc": ['+PosString_0+','+PosString_1+'], "opacity": '+Number_0+', "time": '+Int_1+async+'},\n'; +return code; +*/; + +showImage_1_s + : '显示图片' '图片编号' Int '图片' EvalString BGNL? + '裁剪的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '不透明度' Number BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '时间' Int '不等待执行完毕' Bool Newline + + +/* showImage_1_s +tooltip : showImage_1:显示图片 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=showImage%ef%bc%9a%e6%98%be%e7%a4%ba%e5%9b%be%e7%89%87 +default : [1,"bg.jpg","0","0","","",1,"0","0","","",0,false] +colour : this.printColor +if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); +var async = Bool_0?', "async": true':''; +var code = '{"type": "showImage", "code": '+Int_0+', "image": "'+EvalString_0+'", '+ + '"sloc": ['+PosString_0+','+PosString_1+','+PosString_2+','+PosString_3+'], '+ + '"loc": ['+PosString_4+','+PosString_5+','+PosString_6+','+PosString_7+'], '+ + '"opacity": '+Number_0+', "time": '+Int_1+async+'},\n'; return code; */; showTextImage_s : '显示图片化文本' '文本内容' EvalString BGNL? - '图片编号' Int '起点像素位置' 'x' PosString 'y' PosString '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + '图片编号' Int '起点像素' 'x' PosString 'y' PosString '行距' Number '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline /* showTextImage_s tooltip : showTextImage:显示图片化文本 helpUrl : https://h5mota.com/games/template/docs/#/event?id=showTextImage%ef%bc%9a%e6%98%be%e7%a4%ba%e6%96%87%e6%9c%ac%e5%8c%96%e5%9b%be%e7%89%87 colour : this.printColor -default : ["可以使用setText事件来控制字体、颜色、大小、偏移量等",1,"0","0",1,0,false] +default : ["可以使用setText事件来控制字体、颜色、大小、偏移量等",1,"0","0",1.4,1,0,false] if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); var async = Bool_0?', "async": true':''; -var code = '{"type": "showTextImage", "code": '+Int_0+', "text": "'+EvalString_0+'", "loc": ['+PosString_0+','+PosString_1+'], "opacity": '+Number_0+', "time": '+Int_1+async+'},\n'; +var code = '{"type": "showTextImage", "code": '+Int_0+', "text": "'+EvalString_0+'", "loc": ['+PosString_0+','+PosString_1+'], "lineHeight": '+Number_0+', "opacity": '+Number_1+', "time": '+Int_1+async+'},\n'; return code; */; @@ -1179,35 +1278,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; */; @@ -1383,15 +1482,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; */; @@ -1504,14 +1604,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; */; @@ -1560,6 +1662,21 @@ var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\ return code; */; +confirm_s + : '显示确认框' ':' EvalString BGNL? '确定的场合' ':' '(默认选中' Bool ')' BGNL? Newline action+ '取消的场合' ':' BGNL? Newline action+ BEND Newline + +/* confirm_s +tooltip : 弹出确认框 +helpUrl : https://h5mota.com/games/template/docs/#/ +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(''); +return code; +*/; + while_s : '循环处理' ':' '当' expression '时' BGNL? Newline action+ BEND Newline @@ -1660,6 +1777,21 @@ var code = '{"type": "callLoad"},\n'; return code; */; +unknown_s + : '自定义事件' BGNL? RawEvalString + +/* unknown_s +tooltip : 通过脚本自定义的事件类型, 以及编辑器不识别的事件类型 +helpUrl : https://h5mota.com/games/template/docs/#/ +default : ['{"type":"test", "data": "这是自定义的参数"}'] +colour : this.dataColor +try { + var tempobj = JSON.parse(RawEvalString_0); +} catch (e) {throw new Error("不合法的JSON格式!");} +if (!tempobj.type) throw new Error("自定义事件需要一个type:xxx"); +var code = JSON.stringify(tempobj) +',\n'; +return code; +*/; function_s : '自定义JS脚本' '不自动执行下一个事件' Bool BGNL? Newline RawEvalString Newline BEND Newline @@ -1828,6 +1960,10 @@ SetTextPosition_List : '不改变'|'距离顶部'|'居中'|'距离底部' /*SetTextPosition_List ['null','up','center','down']*/; +SetTextAlign_List + : '不改变'|'左对齐'|'左右居中'|'右对齐' + /*SetTextAlign_List ['null','left','center','right']*/; + ShopUse_List : '金币' | '经验' /*ShopUse_List ['money','experience']*/; @@ -1852,6 +1988,10 @@ Bg_Fg_List : '背景层'|'前景层' /*Bg_Fg_List ['bg','fg']*/; +Event_List + : '事件'|'战后事件'|'道具后事件'|'开门后事件' + /*Event_List ['null','afterBattle','afterGetItem','afterOpenDoor']*/; + Floor_Meta_List : '楼层中文名'|'状态栏名称'|'能否使用楼传'|'能否打开快捷商店'|'是否不可浏览地图'|'是否不可瞬间移动'|'默认地面ID'|'楼层贴图'|'宝石血瓶效果'|'上楼点坐标'|'下楼点坐标'|'背景音乐'|'画面色调'|'天气和强度'|'是否地下层' /*Floor_Meta_List ['title','name','canFlyTo', 'canUseQuickShop', 'cannotViewMap', 'cannotMoveDirectly', 'defaultGround', 'images', 'item_ratio', 'upFloor', 'downFloor', 'bgm', 'color', 'weather', 'underGround']*/; @@ -2006,7 +2146,7 @@ ActionParser.prototype.parse = function (obj,type) { if (!this.isset(obj.time)) obj.time=500; return MotaActionBlocks['changeFloor_m'].xmlText([ obj.floorType||'floorId',obj.floorId,obj.stair||'loc',obj.loc[0],obj.loc[1],obj.direction, - obj.time,!this.isset(obj.portalWithoutTrigger) + obj.time,!this.isset(obj.ignoreChangeFloor) ]); case 'level': @@ -2040,10 +2180,26 @@ ActionParser.prototype.parse = function (obj,type) { obj.id,obj.name,obj.icon,obj.textInList,obj.commonTimes,obj.mustEnable,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next ]); } + var buildcommentevent = function(obj,parser,next){ + if (obj.args instanceof Array) { + try { obj.args = JSON.stringify(obj.args).replace(/"/g, "'"); } + catch (e) {obj.args = '';} + } + else obj.args = null; + return MotaActionBlocks['shopcommonevent'].xmlText([ + obj.id,parser.EvalString(obj.textInList),obj.mustEnable,parser.EvalString(obj.commonEvent),obj.args,next + ]); + } var next=null; if(!obj)obj=[]; while(obj.length){ - next=buildsub(obj.pop(),this,next); + var shopobj=obj.pop() + if(shopobj.choices) + next=buildsub(shopobj,this,next); + else if(shopobj.commonEvent) + next=buildcommentevent(shopobj,this,next); + else + throw new Error("[警告]出错啦!\n"+shopobj.id+" 无效的商店"); } return MotaActionBlocks['shop_m'].xmlText([next]); @@ -2103,7 +2259,7 @@ ActionParser.prototype.parseAction = function() { break; case "scrollText": this.next = MotaActionBlocks['scrollText_s'].xmlText([ - data.time, data.async||false, this.EvalString(data.text), this.next]); + data.time, data.lineHeight||1.4, data.async||false, this.EvalString(data.text), this.next]); break; case "comment": // 注释 this.next = MotaActionBlocks['comment_s'].xmlText([this.EvalString(data.text),this.next],null,data.text); @@ -2115,7 +2271,7 @@ ActionParser.prototype.parseAction = function() { if (!/^\w+\.png$/.test(data.background)) data.background=setTextfunc(data.background); this.next = MotaActionBlocks['setText_s'].xmlText([ - data.position,data.offset,data.title,'rgba('+data.title+')', + data.position,data.offset,data.align,data.title,'rgba('+data.title+')', data.text,'rgba('+data.text+')',data.background,'rgba('+data.background+')', data.bold,data.titlefont,data.textfont,data.time,this.next]); break; @@ -2260,8 +2416,16 @@ ActionParser.prototype.parseAction = function() { break; case "showImage": // 显示图片 data.loc=data.loc||['',''] - this.next = MotaActionBlocks['showImage_s'].xmlText([ - data.code,data.image||data.name,data.loc[0],data.loc[1],data.dw,data.dh,data.opacity,data.time||0,data.async||false,this.next]); + if (data.sloc) { + this.next = MotaActionBlocks['showImage_1_s'].xmlText([ + data.code,data.image||data.name,data.sloc[0],data.sloc[1],data.sloc[2],data.sloc[3],data.opacity, + data.loc[0],data.loc[1],data.loc[2],data.loc[3],data.time||0,data.async||false,this.next + ]); + } + else { + this.next = MotaActionBlocks['showImage_s'].xmlText([ + data.code,data.image||data.name,data.loc[0],data.loc[1],data.opacity,data.time||0,data.async||false,this.next]); + } break; case "hideImage": // 清除图片 this.next = MotaActionBlocks['hideImage_s'].xmlText([ @@ -2270,7 +2434,7 @@ ActionParser.prototype.parseAction = function() { case "showTextImage": // 显示图片化文本 data.loc=data.loc||['',''] this.next = MotaActionBlocks['showTextImage_s'].xmlText([ - this.EvalString(data.text),data.code,data.loc[0],data.loc[1],data.opacity,data.time||0,data.async||false,this.next]); + this.EvalString(data.text),data.code,data.loc[0],data.loc[1],data.lineHeight||1.4,data.opacity,data.time||0,data.async||false,this.next]); break; case "moveImage": // 移动图片 data.to=data.to||['',''] @@ -2287,11 +2451,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; @@ -2308,6 +2473,11 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['openDoor_s'].xmlText([ data.loc[0],data.loc[1],data.floorId||'',data.needKey||false,this.next]); break; + case "closeDoor": // 关一个门,需要该点无事件 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['closeDoor_s'].xmlText([ + data.loc[0],data.loc[1],data.id,this.next]); + break; case "useItem": // 使用道具 this.next = MotaActionBlocks['useItem_s'].xmlText([ data.id,this.next]); @@ -2329,18 +2499,23 @@ ActionParser.prototype.parseAction = function() { data.loc[0],data.loc[1],this.next]); break; case "insert": // 强制插入另一个点的事件在当前事件列表执行,当前坐标和楼层不会改变 + if (data.args instanceof Array) { + try { data.args = JSON.stringify(data.args).replace(/"/g, "'"); } + catch (e) {data.args = '';} + } + else data.args = null; if (this.isset(data.name)) { this.next = MotaActionBlocks['insert_1_s'].xmlText([ - data.name, this.next]); + data.name, data.args||"", this.next]); } else { this.next = MotaActionBlocks['insert_2_s'].xmlText([ - data.loc[0],data.loc[1],data.floorId||'',this.next]); + data.loc[0],data.loc[1],data.which,data.floorId||'',data.args||"",this.next]); } 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([ @@ -2372,14 +2547,13 @@ ActionParser.prototype.parseAction = function() { break case "setValue": this.next = MotaActionBlocks['setValue_s'].xmlText([ - // MotaActionBlocks['idString_e'].xmlText([data.name]), this.tryToUseEvFlag_e('idString_e', [data.name]), MotaActionBlocks['evalString_e'].xmlText([data.value]), this.next]); break; case "setValue2": - this.next = MotaActionBlocks['setValue2_s'].xmlText([ - // MotaActionBlocks['idString_e'].xmlText([data.name]), + case "addValue": + this.next = MotaActionBlocks['addValue_s'].xmlText([ this.tryToUseEvFlag_e('idString_e', [data.name]), MotaActionBlocks['evalString_e'].xmlText([data.value]), this.next]); @@ -2416,11 +2590,18 @@ ActionParser.prototype.parseAction = function() { this.insertActionList(data["false"]), this.next]); break; + case "confirm": // 显示确认框 + this.next = MotaActionBlocks['confirm_s'].xmlText([ + this.EvalString(data.text), data["default"], + this.insertActionList(data["yes"]), + this.insertActionList(data["no"]), + this.next]); + break; case "switch": // 多重条件分歧 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]), @@ -2516,7 +2697,8 @@ ActionParser.prototype.parseAction = function() { case "animateImage": // 兼容 animateImage break; default: - throw new Error("[警告]出错啦!\n"+data.type+" 事件不被支持..."); + this.next = MotaActionBlocks['unknown_s'].xmlText([ + JSON.stringify(data),this.next]); } this.parseAction(); return; diff --git a/_server/README.md b/_server/README.md index 521ce1cc..d23bfb43 100644 --- a/_server/README.md +++ b/_server/README.md @@ -1,5 +1,7 @@ # editor +[重构](refactoring.md) + >! 以下均是v2.0时的说明, 未及时改动 本目录下所有文件,以及`../editor.html`和`../启动服务.exe`([源码](http://github.com/ckcz123/mota-js-server/))是地图编辑器的所有组件. @@ -31,7 +33,7 @@ ``` js editor.mapInit();//清空地图 editor.changeFloor('MT2')//切换地图 -editor.guid()//产生一个可以作为id的长随机字符串 +editor.util.guid()//产生一个可以作为id的长随机字符串 ``` `editor.updateMap`中画未定义快的报错 diff --git a/_server/comment.js b/_server/comment.js index cdf576e4..d4c1ac53 100644 --- a/_server/comment.js +++ b/_server/comment.js @@ -227,8 +227,8 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "openDoor", "passNet", "changeLight", - "ski", - "pushBox" + "pushBox", + "custom" ] }, "_data": "该图块的默认触发器" diff --git a/_server/css/editor.css b/_server/css/editor.css index 8126b33c..dd9c0e18 100644 --- a/_server/css/editor.css +++ b/_server/css/editor.css @@ -54,7 +54,7 @@ body { height: 630px; } -#editArea { +#mapEditArea { position: absolute; width: 100%; height: 400px; @@ -107,7 +107,7 @@ body { top: 530px; } -#editArea p { +#mapEditArea p { margin: 10px; display: block; width: 70%; @@ -148,7 +148,6 @@ body { height: 180px; left: 0; bottom: 0; - border-top: 1px solid #ccc; padding: 10px 5px; margin-left: 8px;; box-sizing: border-box; diff --git a/_server/css/editor_mobile.css b/_server/css/editor_mobile.css index 7bdf8953..1120c9fd 100644 --- a/_server/css/editor_mobile.css +++ b/_server/css/editor_mobile.css @@ -37,7 +37,7 @@ body { position: absolute; } -#editArea { +#mapEditArea { position: absolute; width: 100%; height: 70%; @@ -64,7 +64,7 @@ body { overflow: auto; } -#editArea p { +#mapEditArea p { margin: 10px; display: block; width: 70%; diff --git a/_server/data.comment.js b/_server/data.comment.js index 4b6d6156..c4b8b951 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -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", @@ -253,12 +258,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = } } }, - "flyRange": { - "_leaf": true, - "_type": "textarea", - "_range": "thiseval instanceof Array", - "_data": "初始可飞的楼层;一般留空数组即可" - }, "loc": { "_type": "object", @@ -636,7 +635,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "寻路算法是否经过血瓶;如果该项为false,则寻路算法会自动尽量绕过血瓶" }, - "portalWithoutTrigger": { + "ignoreChangeFloor": { "_leaf": true, "_type": "checkbox", "_bool": "bool", diff --git a/_server/editor.js b/_server/editor.js index f77895f1..3d50fa1f 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -41,15 +41,34 @@ editor.info /////////// 数据相关 /////////// editor.prototype.init = function (callback) { + + editor_util_wrapper(editor); + editor_game_wrapper(editor, main, core); + editor_table_wrapper(editor); + + var afterMainInit = function () { + editor.game.fixFunctionInGameData(); + editor.main = main; + editor.core = core; + editor.fs = fs; + editor_file = editor_file(editor, function () { + editor.file = editor_file; + editor_mode = editor_mode(editor); + editor.mode = editor_mode; + core.resetGame(core.firstData.hero, null, core.firstData.floorId, core.initStatus.maps); + core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function () { + afterCoreReset(); + }, true); + core.events.setInitData(null); + }); + } + var afterCoreReset = function () { - - main.editor.disableGlobalAnimate = false;//允许GlobalAnimate - // core.setHeroMoveTriggerInterval(); - - editor.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 + + editor.game.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 editor.drawInitData(core.icons.icons); // 初始化绘图 - editor.fetchMapFromCore(); + editor.game.fetchMapFromCore(); editor.updateMap(); editor.buildMark(); editor.drawEventBlock(); @@ -70,102 +89,9 @@ editor.prototype.init = function (callback) { } - var afterMainInit = function () { - core.floors = JSON.parse(JSON.stringify(core.floors, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); - core.data = JSON.parse(JSON.stringify(core.data, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); - data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, function (k, v) { - if (v instanceof Function) { - return v.toString() - } else return v - })); - editor.main = main; - editor.core = core; - editor.fs = fs; - editor_file = editor_file(editor, function () { - editor.file = editor_file; - editor_mode = editor_mode(editor); - editor.mode = editor_mode; - core.resetStatus(core.firstData.hero, null, core.firstData.floorId, null, core.initStatus.maps); - core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function () { - afterCoreReset(); - }, true); - core.events.setInitData(null); - }); - } afterMainInit(); } -editor.prototype.idsInit = function (maps, icons) { - editor.ids = [0]; - editor.indexs = []; - var MAX_NUM = 0; - var keys=Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); - for(var ii=0;iiMAX_NUM && v 32*32*3000){ - alert(imgName+'上的图块数量超过了3000,请修改后刷新页面'); - } - for (var id=startOffset; id'; colNum += tpl; } arrColMark.innerHTML = '' + colNum + ''; mapColMark.innerHTML = '' + colNum + ''; var rowNum = ' '; - for (var i = 0; i < 13; i++) { + for (var i = 0; i < core.__SIZE__; i++) { var tpl = '' + (i+offsetY) + '
'; rowNum += tpl; } @@ -527,29 +403,29 @@ editor.prototype.buildMark = function(){ } var buildMark_mobile = function (offsetX,offsetY) { var colNum = ' '; - for (var i = 0; i < 13; i++) { - var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; colNum += tpl; } arrColMark.innerHTML = '' + colNum + ''; //mapColMark.innerHTML = '' + colNum + ''; var rowNum = ' '; - for (var i = 0; i < 13; i++) { - var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; rowNum += tpl; } arrRowMark.innerHTML = rowNum; //mapRowMark.innerHTML = rowNum; //===== var colNum = ' '; - for (var i = 0; i < 13; i++) { - var tpl = '
' + (' '+(i+offsetX)).slice(-2).replace(' ',' ') + '
'; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
' + (' '+(i+offsetX)).slice(-2).replace(' ',' ') + '
'; colNum += tpl; } mapColMark.innerHTML = '
' + colNum + '
'; var rowNum = ' '; - for (var i = 0; i < 13; i++) { - var tpl = '
' + (' '+(i+offsetY)).slice(-2).replace(' ',' ') + '
'; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
' + (' '+(i+offsetY)).slice(-2).replace(' ',' ') + '
'; rowNum += tpl; } mapRowMark.innerHTML = rowNum; @@ -578,9 +454,9 @@ editor.prototype.setSelectBoxFromInfo=function(thisevent){ dataSelection.style.left = pos.x * 32 + 'px'; dataSelection.style.top = pos.y * ysize + 'px'; dataSelection.style.height = ysize - 6 + 'px'; - setTimeout(function(){selectBox.isSelected = true;}); + setTimeout(function(){selectBox.isSelected(true);}); editor.info = JSON.parse(JSON.stringify(thisevent)); - tip.infos = JSON.parse(JSON.stringify(thisevent)); + tip.infos(JSON.parse(JSON.stringify(thisevent))); editor.pos=pos; editor_mode.onmode('nextChange'); editor_mode.onmode('enemyitem'); @@ -622,7 +498,7 @@ editor.prototype.listen = function () { } if (unselect) { if (clickpath.indexOf('eui') === -1) { - if (selectBox.isSelected) { + if (selectBox.isSelected()) { editor_mode.onmode(''); editor.file.saveFloorFile(function (err) { if (err) { @@ -632,7 +508,7 @@ editor.prototype.listen = function () { ;printf('地图保存成功'); }); } - selectBox.isSelected = false; + selectBox.isSelected(false); editor.info = {}; } } @@ -662,7 +538,7 @@ editor.prototype.listen = function () { editor.loc = { 'x': scrollLeft + xx - mid.offsetLeft - mapEdit.offsetLeft, 'y': scrollTop + yy - mid.offsetTop - mapEdit.offsetTop, - 'size': editor.isMobile?(32*innerWidth*0.96/416):32 + 'size': editor.isMobile?(32*innerWidth*0.96/core.__PIXELS__):32 }; return editor.loc; }//返回可用的组件内坐标 @@ -690,7 +566,7 @@ editor.prototype.listen = function () { } holdingPath = 0; stepPostfix = []; - uc.clearRect(0, 0, 416, 416); + uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); }//用于鼠标移出canvas时的自动清除状态 eui.oncontextmenu=function(e){e.preventDefault()} @@ -710,13 +586,13 @@ editor.prototype.listen = function () { editor.showMidMenu(e.clientX,e.clientY); return; } - if (!selectBox.isSelected) { + if (!selectBox.isSelected()) { var loc = eToLoc(e); var pos = locToPos(loc,true); editor_mode.onmode('nextChange'); editor_mode.onmode('loc'); //editor_mode.loc(); - //tip.whichShow = 1; + //tip.whichShow(1); if(editor.isMobile)editor.showMidMenu(e.clientX,e.clientY); return; } @@ -726,7 +602,7 @@ editor.prototype.listen = function () { mouseOutCheck = 2; setTimeout(clear1); e.stopPropagation(); - uc.clearRect(0, 0, 416, 416); + uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); var loc = eToLoc(e); var pos = locToPos(loc,true); stepPostfix = []; @@ -735,8 +611,8 @@ editor.prototype.listen = function () { } eui.onmousemove = function (e) { - if (!selectBox.isSelected) { - //tip.whichShow = 1; + if (!selectBox.isSelected()) { + //tip.whichShow(1); return; } @@ -766,8 +642,8 @@ editor.prototype.listen = function () { } eui.onmouseup = function (e) { - if (!selectBox.isSelected) { - //tip.whichShow = 1; + if (!selectBox.isSelected()) { + //tip.whichShow(1); return; } holdingPath = 0; @@ -812,7 +688,7 @@ editor.prototype.listen = function () { editor.updateMap(); holdingPath = 0; stepPostfix = []; - uc.clearRect(0, 0, 416, 416); + uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); } } @@ -897,7 +773,7 @@ editor.prototype.listen = function () { if (e.altKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) e.preventDefault(); //Ctrl+z 撤销上一步undo - if (e.keyCode == 90 && e.ctrlKey && editor.preMapData && currDrawData.pos.length && selectBox.isSelected) { + if (e.keyCode == 90 && e.ctrlKey && editor.preMapData && currDrawData.pos.length && selectBox.isSelected()) { editor.map = JSON.parse(JSON.stringify(editor.preMapData.map)); editor.fgmap = JSON.parse(JSON.stringify(editor.preMapData.fgmap)); editor.bgmap = JSON.parse(JSON.stringify(editor.preMapData.bgmap)); @@ -907,7 +783,7 @@ editor.prototype.listen = function () { editor.preMapData = null; } //Ctrl+y 重做一步redo - if (e.keyCode == 89 && e.ctrlKey && reDo && reDo.pos.length && selectBox.isSelected) { + if (e.keyCode == 89 && e.ctrlKey && reDo && reDo.pos.length && selectBox.isSelected()) { editor.preMapData = JSON.parse(JSON.stringify({map:editor.map,fgmap:editor.fgmap,bgmap:editor.bgmap})); for (var j = 0; j < reDo.pos.length; j++) editor.map[reDo.pos[j].y][reDo.pos[j].x] = JSON.parse(JSON.stringify(reDo.info)); @@ -1029,7 +905,7 @@ editor.prototype.listen = function () { } else if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 1; - selectBox.isSelected = true; + selectBox.isSelected(true); // console.log(pos,core.material.images[pos.images].height) dataSelection.style.left = pos.x * 32 + 'px'; dataSelection.style.top = pos.y * ysize + 'px'; @@ -1060,7 +936,7 @@ editor.prototype.listen = function () { } } } - tip.infos = JSON.parse(JSON.stringify(editor.info)); + tip.infos(JSON.parse(JSON.stringify(editor.info))); editor_mode.onmode('nextChange'); editor_mode.onmode('enemyitem'); //editor_mode.enemyitem(); @@ -1132,12 +1008,12 @@ editor.prototype.listen = function () { chooseThis.onmousedown = function(e){ editor.hideMidMenu(); e.stopPropagation(); - selectBox.isSelected = false; + selectBox.isSelected(false); editor_mode.onmode('nextChange'); editor_mode.onmode('loc'); //editor_mode.loc(); - //tip.whichShow = 1; + //tip.whichShow(1); if(editor.isMobile)editor.showdataarea(false); } diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index e1db7827..1c81e65c 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -37,6 +37,11 @@ editor_blockly = function () { {"text": "防御+4", "effect": "status:def+=4"}, {"text": "魔防+10", "effect": "status:mdef+=10"} ] + },{ + "id": "keyShop1", + "textInList": "回收钥匙商店", + "commonEvent": "回收钥匙商店", + "args": "" }],'shop'), MotaActionBlocks['afterBattle_m'].xmlText(), MotaActionBlocks['afterGetItem_m'].xmlText(), @@ -54,6 +59,7 @@ editor_blockly = function () { MotaActionBlocks['scrollText_s'].xmlText(), MotaActionBlocks['setText_s'].xmlText(), MotaActionBlocks['showImage_s'].xmlText(), + MotaActionBlocks['showImage_1_s'].xmlText(), MotaActionBlocks['hideImage_s'].xmlText(), MotaActionBlocks['showTextImage_s'].xmlText(), MotaActionBlocks['moveImage_s'].xmlText(), @@ -70,12 +76,13 @@ editor_blockly = function () { ]) ]) ]), + MotaActionBlocks['confirm_s'].xmlText(), ], '数据相关':[ MotaActionBlocks['setValue_s'].xmlText([ MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) ]), - MotaActionBlocks['setValue2_s'].xmlText([ + MotaActionBlocks['addValue_s'].xmlText([ MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) ]), MotaActionBlocks['setFloor_s'].xmlText(), @@ -93,6 +100,7 @@ editor_blockly = function () { MotaActionBlocks['changePos_1_s'].xmlText(), MotaActionBlocks['battle_s'].xmlText(), MotaActionBlocks['openDoor_s'].xmlText(), + MotaActionBlocks['closeDoor_s'].xmlText(), MotaActionBlocks['useItem_s'].xmlText(), MotaActionBlocks['openShop_s'].xmlText(), MotaActionBlocks['setBlock_s'].xmlText(), @@ -105,7 +113,7 @@ editor_blockly = function () { MotaActionBlocks['if_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(), @@ -134,8 +142,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(), @@ -152,12 +160,13 @@ editor_blockly = function () { ], '原生脚本':[ MotaActionBlocks['function_s'].xmlText(), + MotaActionBlocks['unknown_s'].xmlText(), ], '值块':[ MotaActionBlocks['setValue_s'].xmlText([ MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) ]), - MotaActionBlocks['setValue2_s'].xmlText([ + MotaActionBlocks['addValue_s'].xmlText([ MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) ]), MotaActionBlocks['expression_arithmetic_0'].xmlText(), @@ -184,8 +193,8 @@ editor_blockly = function () { {"text": "黄钥匙(\${9+flag:shop_times}金币)", "color": [255,255,0,1], "action": [ {"type": "if", "condition": "status:money>=9+flag:shop_times", "true": [ - {"type": "setValue2", "name": "status:money", "value": "-(9+flag:shop_times)"}, - {"type": "setValue2", "name": "item:yellowKey", "value": "1"}, + {"type": "addValue", "name": "status:money", "value": "-(9+flag:shop_times)"}, + {"type": "addValue", "name": "item:yellowKey", "value": "1"}, ], "false": [ "\t[老人,man]你的金钱不足!", @@ -200,7 +209,7 @@ editor_blockly = function () { ]} ] }, - {"type": "setValue2", "name": "flag:shop_times", "value": "1"}, + {"type": "addValue", "name": "flag:shop_times", "value": "1"}, {"type": "revisit"} ], 'event'), '', @@ -223,7 +232,7 @@ editor_blockly = function () { ],'afterBattle'), '', MotaActionFunctions.actionParser.parse([ - {"type": "setValue2", "name": "flag:__door__", "value": "1"}, + {"type": "addValue", "name": "flag:__door__", "value": "1"}, {"type": "if", "condition": "flag:__door__==2", "true": [ {"type": "openDoor", "loc": [10,5]} @@ -312,7 +321,8 @@ document.getElementById('blocklyDiv').onmousewheel = function(e){ //console.log(e); e.preventDefault(); var hvScroll = e.shiftKey?'hScroll':'vScroll'; - workspace.scrollbar[hvScroll].handlePosition_+=( ((e.deltaY||0)+(e.detail||0)) >0?20:-20); + var mousewheelOffsetValue=20/380*workspace.scrollbar[hvScroll].handleLength_*3; + workspace.scrollbar[hvScroll].handlePosition_+=( ((e.deltaY||0)+(e.detail||0)) >0?mousewheelOffsetValue:-mousewheelOffsetValue); workspace.scrollbar[hvScroll].onScroll_(); workspace.setScale(workspace.scale); } @@ -442,13 +452,13 @@ function omitedcheckUpdateFunction(event) { xhr.onreadystatechange = function () { if (xhr.readyState != 4) return; if (xhr.status != 200) { - alert("无法在file://下加载"); + alert("图块描述文件加载失败, 请在'启动服务.exe'中打开编辑器"); return; } input_ = xhr.responseText; editor_blockly.runOne(); } - xhr.open('GET', '_server/blockly/MotaAction.g4', true); + xhr.open('GET', '_server/MotaAction.g4', true); xhr.send(null); codeAreaHL = CodeMirror.fromTextArea(document.getElementById("codeArea"), { @@ -512,7 +522,7 @@ function omitedcheckUpdateFunction(event) { var blocklyWidgetDiv = document.getElementsByClassName('blocklyWidgetDiv'); editor_blockly.show = function () { - if (typeof(selectBox) !== typeof(undefined)) selectBox.isSelected = false; + if (typeof(selectBox) !== typeof(undefined)) selectBox.isSelected(false); document.getElementById('left6').style = ''; for (var ii = 0, node; node = blocklyWidgetDiv[ii]; ii++) { node.style.zIndex = 201; @@ -580,6 +590,7 @@ function omitedcheckUpdateFunction(event) { 'showTextImage_s': 'EvalString_0', 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', + 'confirm_s': 'EvalString_0', } var f = b ? textStringDict[b.type] : null; if (f) { diff --git a/_server/editor_file.js b/_server/editor_file.js index 6094be7a..89b7f6c5 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -8,6 +8,7 @@ editor_file = function (editor, callback) { 'data.comment': 'dataComment', 'functions.comment': 'functionsComment', 'events.comment': 'eventsComment', + 'plugins.comment': 'pluginsComment', } for (var key in commentjs) { (function (key) { @@ -93,7 +94,7 @@ editor_file = function (editor, callback) { } // format 更改实现方式以支持undefined删除 var tempJsonObj=Object.assign({},editor.currentFloorData); - var tempMap=[['map',editor.guid()],['bgmap',editor.guid()],['fgmap',editor.guid()]]; + var tempMap=[['map',editor.util.guid()],['bgmap',editor.util.guid()],['fgmap',editor.util.guid()]]; tempMap.forEach(function(v){ v[2]=tempJsonObj[v[0]]; tempJsonObj[v[0]]=v[1]; @@ -154,7 +155,7 @@ editor_file = function (editor, callback) { cannotMove: {} }; Object.keys(editor.currentFloorData).forEach(function (t) { - if (!core.isset(editor.currentFloorData[t])) + if (editor.currentFloorData[t] == null) delete editor.currentFloorData[t]; }) editor.currentFloorData.map = "new"; @@ -216,7 +217,7 @@ editor_file = function (editor, callback) { cannotMove: {} }; Object.keys(data).forEach(function (t) { - if (!core.isset(data[t])) + if (data[t] == null) delete data[t]; else { if (t=='map') { @@ -278,7 +279,7 @@ editor_file = function (editor, callback) { // get id num var id = c+idnum; - if (image=='terrains' && core.isset(terrainsId[y])) { + if (image=='terrains' && terrainsId[y] != null) { id=terrainsId[y]; } else { @@ -763,7 +764,7 @@ editor_file = function (editor, callback) { var fmap = {}; var fjson = JSON.stringify(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a, function (k, v) { if (v instanceof Function) { - var id_ = editor.guid(); + var id_ = editor.util.guid(); fmap[id_] = v.toString(); return id_; } else return v @@ -849,6 +850,65 @@ editor_file = function (editor, callback) { //////////////////////////////////////////////////////////////////// + var plmap = {}; + var pljson = JSON.stringify(plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + plmap[id_] = v.toString(); + return id_; + } else if(v===null){ + var id_ = editor.util.guid(); + plmap[id_] = 'null'; + return id_; + } return v + }, 4); + var plobj = JSON.parse(pljson); + editor_file.pluginsMap = plmap; + editor_file.pluginsObj = plobj; + var buildpllocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildpllocobj(locObj[key]); + else locObj[key] = plmap[locObj[key]]; + } + }; + + editor_file.editPlugins = function (actionList, callback) { + /*actionList:[ + ["change","['test']","function(x,y){console.log(x,y)}"], + ] + 为[]时只查询不修改 + */ + if (!isset(callback)) { + printe('未设置callback'); + throw('未设置callback') + } + ; + if (isset(actionList) && actionList.length > 0) { + saveSetting('plugins', actionList, function (err) { + callback([ + (function () { + var locObj = JSON.parse(JSON.stringify(plobj)); + buildpllocobj(locObj); + return locObj; + })(), + editor_file.pluginsComment, + err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(JSON.stringify(plobj)); + buildpllocobj(locObj); + return locObj; + })(), + editor_file.pluginsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + var isset = function (val) { if (val == undefined || val == null) { return false; @@ -920,7 +980,7 @@ editor_file = function (editor, callback) { var emap = {}; var estr = JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e, function (k, v) { if (v.id != null) { - var id_ = editor.guid(); + var id_ = editor.util.guid(); emap[id_] = JSON.stringify(v); return id_; } else return v @@ -954,7 +1014,7 @@ editor_file = function (editor, callback) { var emap = {}; var estr = JSON.stringify(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80, function (k, v) { if (v.hp != null) { - var id_ = editor.guid(); + var id_ = editor.util.guid(); emap[id_] = JSON.stringify(v); return id_; } else return v @@ -999,8 +1059,10 @@ editor_file = function (editor, callback) { if (file == 'floorloc') { actionList.forEach(function (value) { // 检测null/undefined - if (!core.isset(value[2]))value[2]=undefined; - eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + if (value[2]==null) + eval("delete editor.currentFloorData" + value[1]); + else + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); }); editor_file.saveFloorFile(callback); return; @@ -1023,6 +1085,25 @@ editor_file = function (editor, callback) { }); return; } + if (file == 'plugins') { + actionList.forEach(function (value) { + if(value[0]==='add'){ + eval("plobj" + value[1] + '=' + JSON.stringify(value[2])); + } else { + eval("plmap[plobj" + value[1] + ']=' + JSON.stringify(value[2])); + } + }); + var plraw = JSON.stringify(plobj,null,4); + for (var id_ in plmap) { + plraw = plraw.replace('"' + id_ + '"', plmap[id_]) + } + var datastr = 'var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = \n'; + datastr += plraw; + fs.writeFile('project/plugins.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } callback('出错了,要设置的文件名不识别'); } diff --git a/_server/editor_game.js b/_server/editor_game.js new file mode 100644 index 00000000..cb5a5b23 --- /dev/null +++ b/_server/editor_game.js @@ -0,0 +1,124 @@ +editor_game_wrapper = function (editor, main, core) { + + editor_game = function () { + + } + + editor_game.prototype.fixFunctionInGameData = function () { + core.floors = JSON.parse(JSON.stringify(core.floors, function (_k, v) { + if (v instanceof Function) { + return v.toString() + } else return v + })); + core.data = JSON.parse(JSON.stringify(core.data, function (_k, v) { + if (v instanceof Function) { + return v.toString() + } else return v + })); + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, function (_k, v) { + if (v instanceof Function) { + return v.toString() + } else return v + })); + } + + editor_game.prototype.idsInit = function (maps, icons) { + editor.ids = [0]; + editor.indexs = []; + var MAX_NUM = 0; + var keys=Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); + for(var ii=0;iiMAX_NUM && v 32*32*3000){ + alert(imgName+'上的图块数量超过了3000,请修改后刷新页面'); + } + for (var id=startOffset; id结构如下 - * tr>td[title=field] - * >td[title=comment,cobj=cobj:json] - * >td>div>input[value=thiseval] - * 返回结果 - * 返回一个对象, 假设被命名为tableinfo - * 在把一个 table 的 innerHTML 赋值为 tableinfo.HTML 后 - * 再调 tableinfo.listen(tableinfo.guids) 进行绑定事件 - * @param {Object} obj - * @param {Object} commentObj - * @returns {{"HTML":String,"guids":String[],"listen":Function}} - */ - editor_mode.prototype.objToTable_ = function (obj, commentObj) { - // 表格抬头 - var outstr = ["\n条目注释值\n"]; - var guids = []; - var defaultcobj = { - // 默认是文本域 - _type: 'textarea', - _data: '', - _string: function (args) {//object~[field,cfield,vobj,cobj] - var thiseval = args.vobj; - return (typeof(thiseval) === typeof('')) && thiseval[0] === '"'; - }, - // 默认情况下 非对象和数组的视为叶节点 - _leaf: function (args) {//object~[field,cfield,vobj,cobj] - var thiseval = args.vobj; - if (thiseval == null || thiseval == undefined) return true;//null,undefined - if (typeof(thiseval) === typeof('')) return true;//字符串 - if (Object.keys(thiseval).length === 0) return true;//数字,true,false,空数组,空对象 - return false; - }, - } - /** - * 深度优先遍历, p*即为父节点的四个属性 - * @param {String} pfield - * @param {String} pcfield - * @param {Object} pvobj - * @param {Object} pcobj - */ - var recursionParse = function (pfield, pcfield, pvobj, pcobj) { - var keysForTableOrder={}; - var voidMark={}; - // 1. 按照pcobj排序生成 - if (pcobj && pcobj['_data']){ - for (var ii in pcobj['_data']) keysForTableOrder[ii]=voidMark; - } - // 2. 对每个pvobj且不在pcobj的,再添加到最后 - keysForTableOrder=Object.assign(keysForTableOrder,pvobj) - for (var ii in keysForTableOrder) { - // 3. 对于pcobj有但是pvobj中没有的, 弹出提示, (正常情况下editor_file会补全成null) - // 事实上能执行到这一步工程没崩掉打不开,就继续吧.. - if(keysForTableOrder[ii]===voidMark){ - if(typeof id_815975ad_ee6f_4684_aac7_397b7e392702==="undefined"){ - alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') - console.error('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') - id_815975ad_ee6f_4684_aac7_397b7e392702=1; - } - pvobj[ii]=null; - } - var field = pfield + "['" + ii + "']"; - var cfield = pcfield + "['_data']['" + ii + "']"; - var vobj = pvobj[ii]; - var cobj = null; - if (pcobj && pcobj['_data'] && pcobj['_data'][ii]) { - // cobj存在时直接取 - cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]); - } else { - // 当其函数时代入参数算出cobj, 不存在时只取defaultcobj - if (pcobj && (pcobj['_data'] instanceof Function)) cobj = Object.assign({}, defaultcobj, pcobj['_data'](ii)); - else cobj = Object.assign({}, defaultcobj); - } - var args = {field: field, cfield: cfield, vobj: vobj, cobj: cobj} - // 当cobj的参数为函数时,代入args算出值 - for (var key in cobj) { - if (key === '_data') continue; - if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); - } - // 标记为_hide的属性不展示 - if (cobj._hide)continue; - if (!cobj._leaf) { - // 不是叶节点时, 插入展开的标记并继续遍历, 此处可以改成按钮用来添加新项或折叠等 - outstr.push(["--------", field, "\n"].join('')); - recursionParse(field, cfield, vobj, cobj); - } else { - // 是叶节点时, 调objToTr_渲染 - var leafnode = editor_mode.objToTr_(obj, commentObj, field, cfield, vobj, cobj); - outstr.push(leafnode[0]); - guids.push(leafnode[1]); - } - } - } - // 开始遍历 - recursionParse("", "", obj, commentObj); - var checkRange = function (cobj, thiseval) { - if (cobj._range) { - return eval(cobj._range); - } - if (cobj._select) { - return cobj._select.values.indexOf(thiseval)!==-1; - } - if (cobj._bool) { - return [true,false].indexOf(thiseval)!==-1; - } - return true; - } - var listen = function (guids) { - // 每个叶节点的事件绑定 - guids.forEach(function (guid) { - // tr>td[title=field] - // >td[title=comment,cobj=cobj:json] - // >td>div>input[value=thiseval] - var thisTr = document.getElementById(guid); - var input = thisTr.children[2].children[0].children[0]; - var field = thisTr.children[0].getAttribute('title'); - var cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); - var modeNode = thisTr.parentNode; - while (!editor_mode._ids.hasOwnProperty(modeNode.getAttribute('id'))) { - modeNode = modeNode.parentNode; - } - input.onchange = function () { - editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); - var thiseval = null; - if (input.checked != null) input.value = input.checked; - try { - thiseval = JSON.parse(input.value); - } catch (ee) { - printe(field + ' : ' + ee); - throw ee; - } - if (checkRange(cobj, thiseval)) { - editor_mode.addAction(['change', field, thiseval]); - editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 - } else { - printe(field + ' : 输入的值不合要求,请鼠标放置在注释上查看说明'); - } - } - // 双击表格时 - // 正常编辑: 尝试用事件编辑器或多行文本编辑器打开 - // 添加: 在该项的同一级创建一个内容为null新的项, 刷新后生效并可以继续编辑 - // 删除: 删除该项, 刷新后生效 - // 在点击按钮 添加/删除 后,下一次双击将被视为 添加/删除 - var dblclickfunc=function () { - if(editor_mode.doubleClickMode==='change'){ - if (cobj._type === 'event') editor_blockly.import(guid, {type: cobj._event}); - if (cobj._type === 'textarea') editor_multi.import(guid, {lint: cobj._lint, string: cobj._string}); - } - if(editor_mode.doubleClickMode==='add'){ - editor_mode.doubleClickMode='change'; - addfunc() - } - if(editor_mode.doubleClickMode==='delete'){ - editor_mode.doubleClickMode='change'; - deletefunc() - } - } - input.ondblclick = dblclickfunc - var doubleClickCheck=[0]; - thisTr.onclick = function(){ - var newClick = new Date().getTime(); - var lastClick = doubleClickCheck.shift(); - doubleClickCheck.push(newClick); - if(newClick-lastClick<500){ - dblclickfunc() - } - } - var deletefunc=function(){ - editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); - if (checkRange(cobj, null)) { - editor_mode.addAction(['delete', field, undefined]); - editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 - } else { - printe(field + ' : 该值不允许为null,无法删除'); - } - } - var addfunc=function(){ - editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); - - var mode = document.getElementById('editModeSelect').value; - - // 1.输入id - var newid=prompt('请输入新项的ID(仅公共事件支持中文ID)'); - if (newid == null || newid.length==0) { - return; - } - - // 检查commentEvents - if (mode !== 'commonevent') { - // 2.检查id是否符合规范或与已有id重复 - if (!/^[a-zA-Z0-9_]+$/.test(newid)){ - printe('id不符合规范, 请使用大小写字母数字下划线来构成'); - return; - } - } - - var conflict=true; - var basefield=field.replace(/\[[^\[]*\]$/,''); - if (basefield==="['main']"){ - printe("全塔属性 ~ ['main'] 不允许添加新值"); - return; - } - try { - var baseobj=eval('obj'+basefield); - conflict=newid in baseobj; - } catch (ee) { - // 理论上这里不会发生错误 - printe(ee); - throw ee; - } - if (conflict){ - printe('id已存在, 请直接修改该项的值'); - return; - } - // 3.添加 - editor_mode.addAction(['add',basefield+"['"+newid+"']",null]); - editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 - } - }); - } - return {"HTML": outstr.join(''), "guids": guids, "listen": listen}; - } - - /** - * 返回叶节点形如 - * tr>td[title=field] - * >td[title=comment,cobj=cobj:json] - * >td>div>input[value=thiseval] - * 参数意义在 objToTable_ 中已解释 - * @param {Object} obj - * @param {Object} commentObj - * @param {String} field - * @param {String} cfield - * @param {Object} vobj - * @param {Object} cobj - */ - editor_mode.prototype.objToTr_ = function (obj, commentObj, field, cfield, vobj, cobj) { - var guid = editor.guid(); - var thiseval = vobj; - var comment = String(cobj._data); - - var charlength = 10; - // "['a']['b']" => "b" - var shortField = field.split("']").slice(-2)[0].split("['").slice(-1)[0]; - // 把长度超过 charlength 的字符改成 固定长度+...的形式 - shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); - - // 完整的内容转义后供悬停查看 - var commentHTMLescape = editor.HTMLescape(comment); - // 把长度超过 charlength 的字符改成 固定长度+...的形式 - var shortCommentHTMLescape = (comment.length < charlength ? commentHTMLescape : editor.HTMLescape(comment.slice(0, charlength)) + '...'); - - var cobjstr = Object.assign({}, cobj); - delete cobjstr._data; - // 把cobj塞到第二个td的[cobj]中, 方便绑定事件时取 - cobjstr = editor.HTMLescape(JSON.stringify(cobjstr)); - - var outstr = ['', shortField, '', - '', shortCommentHTMLescape, '', - '
', editor_mode.objToTd_(obj, commentObj, field, cfield, vobj, cobj), '
\n', - ]; - return [outstr.join(''), guid]; - } - - editor_mode.prototype.objToTd_ = function (obj, commentObj, field, cfield, vobj, cobj) { - var thiseval = vobj; - if (cobj._select) { - var values = cobj._select.values; - var outstr = [''); - return outstr.join(''); - } else if (cobj._input) { - return ["\n"].join(''); - } else if (cobj._bool) { - return ["\n"].join(''); - } else { - var num = 0;//editor_mode.indent(field); - return ["\n'].join(''); - } - } - editor_mode.prototype.indent = function (field) { var num = '\t'; if (field.indexOf("['main']") === 0) return 0; - if (field.indexOf("['flyRange']") !== -1) return 0; if (field === "['special']") return 0; return num; } @@ -386,6 +87,9 @@ editor_mode = function (editor) { case 'commonevent': editor.file.editCommonEvent(actionList, cb); break; + case 'plugins': + editor.file.editPlugins(actionList, cb); + break; default: break; } @@ -395,7 +99,7 @@ editor_mode = function (editor) { if (editor_mode.mode != mode) { if (mode === 'save') editor_mode.doActionList(editor_mode.mode, editor_mode.actionList); if (editor_mode.mode === 'nextChange' && mode) editor_mode.showMode(mode); - editor_mode.mode = mode; + if (mode !== 'save') editor_mode.mode = mode; editor_mode.actionList = []; } } @@ -411,7 +115,7 @@ editor_mode = function (editor) { if (editor_mode[mode]) editor_mode[mode](); document.getElementById('editModeSelect').value = mode; var tips = tip_in_showMode; - if (!selectBox.isSelected) printf('tips: ' + tips[~~(tips.length * Math.random())]); + if (!selectBox.isSelected()) printf('tips: ' + tips[~~(tips.length * Math.random())]); } editor_mode.prototype.loc = function (callback) { @@ -426,7 +130,7 @@ editor_mode = function (editor) { //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_3d846fc4_7644_44d1_aa04_433d266a73df').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); editor.drawPosSelection(); @@ -469,7 +173,7 @@ editor_mode = function (editor) { }); } //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); @@ -483,7 +187,7 @@ editor_mode = function (editor) { //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_4a3b1b09_b2fb_4bdf_b9ab_9f4cdac14c74').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); @@ -496,7 +200,7 @@ editor_mode = function (editor) { //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_b6a03e4c_5968_4633_ac40_0dfdd2c9cde5').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); @@ -509,7 +213,7 @@ editor_mode = function (editor) { //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_e260a2be_5690_476a_b04e_dacddede78b3').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); @@ -522,11 +226,24 @@ editor_mode = function (editor) { //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 - var tableinfo = editor_mode.objToTable_(objs[0], objs[1]); + var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById('table_b7bf0124_99fd_4af8_ae2f_0017f04a7c7d').innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); } + + editor_mode.prototype.plugins = function (callback) { + var objs = []; + editor.file.editPlugins([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_e2c034ec_47c6_48ae_8db8_4f8f32fea2d6').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } ///////////////////////////////////////////////////////////////////////////// @@ -609,8 +326,8 @@ editor_mode = function (editor) { } var width = parseInt(document.getElementById('newMapWidth').value); var height = parseInt(document.getElementById('newMapHeight').value); - if (!core.isset(width) || !core.isset(height) || width<13 || height<13 || width*height>1000) { - printe("新建地图的宽高都不得小于13,且宽高之积不能超过1000"); + if (!core.isset(width) || !core.isset(height) || width1000) { + printe("新建地图的宽高都不得小于"+core.__SIZE__+",且宽高之积不能超过1000"); return; } @@ -674,8 +391,8 @@ editor_mode = function (editor) { var width = parseInt(document.getElementById('newMapsWidth').value); var height = parseInt(document.getElementById('newMapsHeight').value); - if (!core.isset(width) || !core.isset(height) || width<13 || height<13 || width*height>1000) { - printe("新建地图的宽高都不得小于13,且宽高之积不能超过1000"); + if (!core.isset(width) || !core.isset(height) || width1000) { + printe("新建地图的宽高都不得小于"+core.__SIZE__+",且宽高之积不能超过1000"); return; } editor_mode.onmode(''); @@ -757,21 +474,8 @@ editor_mode = function (editor) { } selectAppend.onchange(); - var getPixel=function(imgData, x, y) { - var offset = (x + y * imgData.width) * 4; - var r = imgData.data[offset+0]; - var g = imgData.data[offset+1]; - var b = imgData.data[offset+2]; - var a = imgData.data[offset+3]; - return [r,g,b,a]; - } - var setPixel=function(imgData, x, y, rgba) { - var offset = (x + y * imgData.width) * 4; - imgData.data[offset+0]=rgba[0]; - imgData.data[offset+1]=rgba[1]; - imgData.data[offset+2]=rgba[2]; - imgData.data[offset+3]=rgba[3]; - } + var getPixel=editor.util.getPixel + var setPixel=editor.util.setPixel var autoAdjust = function (image, callback) { var changed = false; @@ -792,7 +496,7 @@ editor_mode = function (editor) { var pixel = getPixel(imgData, i, j); if (pixel[3]==0) trans++; if (pixel[0]==255 && pixel[1]==255 && pixel[2]==255 && pixel[3]==255) white++; - if (pixel[0]==0 && pixel[1]==0 && pixel[2]==0 && pixel[3]==255) black++; + // if (pixel[0]==0 && pixel[1]==0 && pixel[2]==0 && pixel[3]==255) black++; } } if (white>black && white>trans*10 && confirm("看起来这张图片是以纯白为底色,是否自动调整为透明底色?")) { @@ -808,6 +512,7 @@ editor_mode = function (editor) { tempCanvas.putImageData(imgData, 0, 0); changed = true; } + /* if (black>white && black>trans*10 && confirm("看起来这张图片是以纯黑为底色,是否自动调整为透明底色?")) { for (var i=0;i 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - - h /= 6; - } - - //CARLOS - h = round(h * 360); - s = round(s * 100); - l = round(l * 100); - - return [h, s, l]; - } - // - var hue2rgb = function (p, q, t) { - if (t < 0) { t += 1; } - if (t > 1) { t -= 1; } - if (t < 1 / 6) { return p + (q - p) * 6 * t; } - if (t < 1 / 2) { return q; } - if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } - return p; - } - var hslToRgb = function (hsl) { - var arg, r, g, b, h, s, l, q, p; - - arg = hsl; - - if (typeof arg[0] === 'number') { - h = arg[0] / 360; - s = arg[1] / 100; - l = arg[2] / 100; - } else { - h = arg[0][0] / 360; - s = arg[0][1] / 100; - l = arg[0][2] / 100; - } - - if (s === 0) { - r = g = b = l; // achromatic - } else { - - q = l < 0.5 ? l * (1 + s) : l + s - l * s; - p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - return [round(r * 255), round(g * 255), round(b * 255)]; - } + var rgbToHsl = editor.util.rgbToHsl + var hue2rgb = editor.util.hue2rgb + var hslToRgb = editor.util.hslToRgb // var hsl=rgbToHsl(rgba) hsl[0]=(hsl[0]+delta)%360 diff --git a/_server/editor_multi.js b/_server/editor_multi.js index 705f0ab4..9ccd00c9 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -39,7 +39,7 @@ editor_multi = function () { editor_multi.lintAutocomplete = false; editor_multi.show = function () { - if (typeof(selectBox) !== typeof(undefined)) selectBox.isSelected = false; + if (typeof(selectBox) !== typeof(undefined)) selectBox.isSelected(false); var valueNow = codeEditor.getValue(); //try{eval('function _asdygakufyg_() { return '+valueNow+'\n}');editor_multi.lintAutocomplete=true;}catch(ee){} if (valueNow.slice(0, 8) === 'function') editor_multi.lintAutocomplete = true; @@ -103,7 +103,7 @@ editor_multi = function () { var tmap = {}; var tstr = JSON.stringify(tobj, function (k, v) { if (typeof(v) === typeof('') && v.slice(0, 8) === 'function') { - var id_ = editor.guid(); + var id_ = editor.util.guid(); tmap[id_] = v.toString(); return id_; } else return v @@ -146,7 +146,7 @@ editor_multi = function () { var tmap = {}; var tstr = JSON.stringify(tobj, function (k, v) { if (v instanceof Function) { - var id_ = editor.guid(); + var id_ = editor.util.guid(); tmap[id_] = v.toString(); return id_; } else return v diff --git a/_server/editor_table.js b/_server/editor_table.js new file mode 100644 index 00000000..33a1465f --- /dev/null +++ b/_server/editor_table.js @@ -0,0 +1,383 @@ +editor_table_wrapper = function (editor) { + + editor_table = function () { + + } + + ///////////////////////////////////////////////////////////////////////////// + // HTML模板 + + editor_table.prototype.select = function (value, values) { + let content = editor.table.option(value) + + values.map(function (v) { + return editor.table.option(v) + }).join('') + return `\n` + } + editor_table.prototype.option = function (value) { + return `\n` + } + editor_table.prototype.text = function (value) { + return `\n` + } + editor_table.prototype.checkbox = function (value) { + return `\n` + } + editor_table.prototype.textarea = function (value, indent) { + return `\n` + } + + editor_table.prototype.title = function () { + return `\n条目注释值\n` + } + + editor_table.prototype.gap = function (field) { + return `--------${field}\n` + } + + editor_table.prototype.tr = function (guid, field, shortField, commentHTMLescape, cobjstr, shortCommentHTMLescape, tdstr) { + return ` + ${shortField} + ${shortCommentHTMLescape} +
${tdstr}
+ \n` + } + + + ///////////////////////////////////////////////////////////////////////////// + // 表格生成的控制 + + /** + * 注释对象的默认值 + */ + editor_table.prototype.defaultcobj = { + // 默认是文本域 + _type: 'textarea', + _data: '', + _string: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + return (typeof (thiseval) === typeof ('')) && thiseval[0] === '"'; + }, + // 默认情况下 非对象和数组的视为叶节点 + _leaf: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + if (thiseval == null || thiseval == undefined) return true;//null,undefined + if (typeof (thiseval) === typeof ('')) return true;//字符串 + if (Object.keys(thiseval).length === 0) return true;//数字,true,false,空数组,空对象 + return false; + }, + } + + /** + * 把来自数据文件的obj和来自*comment.js的commentObj组装成表格 + * commentObj在无视['_data']的意义下与obj同形 + * 即: commentObj['_data']['a']['_data']['b'] 与 obj['a']['b'] 是对应的 + * 在此意义下, 两者的结构是一致的 + * 在commentObj没有被定义的obj的分支, 会取defaultcobj作为默认值 + * 因此在深度优先遍历时,维护 + * field="['a']['b']" + * cfield="['_data']['a']['_data']['b']" + * vobj=obj['a']['b'] + * cobj=commentObj['_data']['a']['_data']['b'] + * cobj + * cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]) + * 每一项若未定义,就从defaultcobj中取 + * 当其是函数不是具体值时,把args = {field: field, cfield: cfield, vobj: vobj, cobj: cobj}代入算出该值 + * 得到的叶节点的结构如下 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 返回结果 + * 返回一个对象, 假设被命名为tableinfo + * 在把一个 table 的 innerHTML 赋值为 tableinfo.HTML 后 + * 再调 tableinfo.listen(tableinfo.guids) 进行绑定事件 + * @param {Object} obj + * @param {Object} commentObj + * @returns {{"HTML":String,"guids":String[],"listen":Function}} + */ + editor_table.prototype.objToTable = function (obj, commentObj) { + // 表格抬头 + var outstr = [editor.table.title()]; + var guids = []; + var defaultcobj = this.defaultcobj + /** + * 深度优先遍历, p*即为父节点的四个属性 + * @param {String} pfield + * @param {String} pcfield + * @param {Object} pvobj + * @param {Object} pcobj + */ + var recursionParse = function (pfield, pcfield, pvobj, pcobj) { + var keysForTableOrder = {}; + var voidMark = {}; + // 1. 按照pcobj排序生成 + if (pcobj && pcobj['_data']) { + for (var ii in pcobj['_data']) keysForTableOrder[ii] = voidMark; + } + // 2. 对每个pvobj且不在pcobj的,再添加到最后 + keysForTableOrder = Object.assign(keysForTableOrder, pvobj) + for (var ii in keysForTableOrder) { + // 3. 对于pcobj有但是pvobj中没有的, 弹出提示, (正常情况下editor_file会补全成null) + // 事实上能执行到这一步工程没崩掉打不开,就继续吧.. + if (keysForTableOrder[ii] === voidMark) { + if (typeof id_815975ad_ee6f_4684_aac7_397b7e392702 === "undefined") { + alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + console.error('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + id_815975ad_ee6f_4684_aac7_397b7e392702 = 1; + } + pvobj[ii] = null; + } + var field = pfield + "['" + ii + "']"; + var cfield = pcfield + "['_data']['" + ii + "']"; + var vobj = pvobj[ii]; + var cobj = null; + if (pcobj && pcobj['_data'] && pcobj['_data'][ii]) { + // cobj存在时直接取 + cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]); + } else { + // 当其函数时代入参数算出cobj, 不存在时只取defaultcobj + if (pcobj && (pcobj['_data'] instanceof Function)) cobj = Object.assign({}, defaultcobj, pcobj['_data'](ii)); + else cobj = Object.assign({}, defaultcobj); + } + var args = { field: field, cfield: cfield, vobj: vobj, cobj: cobj } + // 当cobj的参数为函数时,代入args算出值 + for (var key in cobj) { + if (key === '_data') continue; + if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); + } + // 标记为_hide的属性不展示 + if (cobj._hide) continue; + if (!cobj._leaf) { + // 不是叶节点时, 插入展开的标记并继续遍历, 此处可以改成按钮用来添加新项或折叠等 + outstr.push(editor.table.gap(field)); + recursionParse(field, cfield, vobj, cobj); + } else { + // 是叶节点时, 调objToTr_渲染 + var leafnode = editor.table.objToTr(obj, commentObj, field, cfield, vobj, cobj); + outstr.push(leafnode[0]); + guids.push(leafnode[1]); + } + } + } + // 开始遍历 + recursionParse("", "", obj, commentObj); + + var listen = function (guids) { + // 每个叶节点的事件绑定 + guids.forEach(function (guid) { + editor.table.guidListen(guid, obj, commentObj) + }); + } + return { "HTML": outstr.join(''), "guids": guids, "listen": listen }; + } + + /** + * 返回叶节点形如 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 参数意义在 objToTable 中已解释 + * @param {Object} obj + * @param {Object} commentObj + * @param {String} field + * @param {String} cfield + * @param {Object} vobj + * @param {Object} cobj + */ + editor_table.prototype.objToTr = function (obj, commentObj, field, cfield, vobj, cobj) { + var guid = editor.util.guid(); + var thiseval = vobj; + var comment = String(cobj._data); + + var charlength = 10; + // "['a']['b']" => "b" + var shortField = field.split("']").slice(-2)[0].split("['").slice(-1)[0]; + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); + + // 完整的内容转义后供悬停查看 + var commentHTMLescape = editor.util.HTMLescape(comment); + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + var shortCommentHTMLescape = (comment.length < charlength ? commentHTMLescape : editor.util.HTMLescape(comment.slice(0, charlength)) + '...'); + + var cobjstr = Object.assign({}, cobj); + delete cobjstr._data; + // 把cobj塞到第二个td的[cobj]中, 方便绑定事件时取 + cobjstr = editor.util.HTMLescape(JSON.stringify(cobjstr)); + + var tdstr = editor.table.objToTd(obj, commentObj, field, cfield, vobj, cobj) + var outstr = editor.table.tr(guid, field, shortField, commentHTMLescape, cobjstr, shortCommentHTMLescape, tdstr) + return [outstr, guid]; + } + + editor_table.prototype.objToTd = function (obj, commentObj, field, cfield, vobj, cobj) { + var thiseval = vobj; + if (cobj._select) { + var values = cobj._select.values; + return editor.table.select(thiseval, values); + } else if (cobj._input) { + return editor.table.text(thiseval); + } else if (cobj._bool) { + return editor.table.checkbox(thiseval); + } else { + var indent = 0; + return editor.table.textarea(thiseval, indent); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // 表格的用户交互 + + /** + * 检查一个值是否允许被设置为当前输入 + * @param {Object} cobj + * @param {*} thiseval + */ + editor_table.prototype.checkRange = function (cobj, thiseval) { + if (cobj._range) { + return eval(cobj._range); + } + if (cobj._select) { + return cobj._select.values.indexOf(thiseval) !== -1; + } + if (cobj._bool) { + return [true, false].indexOf(thiseval) !== -1; + } + return true; + } + + /** + * 监听一个guid对应的表格项 + * @param {String} guid + */ + editor_table.prototype.guidListen = function (guid, obj, commentObj) { + // tr>td[title=field] + // >td[title=comment,cobj=cobj:json] + // >td>div>input[value=thiseval] + var thisTr = document.getElementById(guid); + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + var modeNode = thisTr.parentNode; + while (!editor_mode._ids.hasOwnProperty(modeNode.getAttribute('id'))) { + modeNode = modeNode.parentNode; + } + input.onchange = function () { + editor.table.onchange(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + // 用检测两次单击的方式来实现双击(以支持手机端的双击) + var doubleClickCheck = [0]; + thisTr.onclick = function () { + var newClick = new Date().getTime(); + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if (newClick - lastClick < 500) { + editor.table.dblclickfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + } + + /** + * 表格的值变化时 + */ + editor_table.prototype.onchange = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + var thiseval = null; + if (input.checked != null) input.value = input.checked; + try { + thiseval = JSON.parse(input.value); + } catch (ee) { + printe(field + ' : ' + ee); + throw ee; + } + if (editor.table.checkRange(cobj, thiseval)) { + editor_mode.addAction(['change', field, thiseval]); + editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 + } else { + printe(field + ' : 输入的值不合要求,请鼠标放置在注释上查看说明'); + } + } + + /** + * 双击表格时 + * 正常编辑: 尝试用事件编辑器或多行文本编辑器打开 + * 添加: 在该项的同一级创建一个内容为null新的项, 刷新后生效并可以继续编辑 + * 删除: 删除该项, 刷新后生效 + * 在点击按钮 添加/删除 后,下一次双击将被视为 添加/删除 + */ + editor_table.prototype.dblclickfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + if (editor_mode.doubleClickMode === 'change') { + if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); + if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string }); + } else if (editor_mode.doubleClickMode === 'add') { + editor_mode.doubleClickMode = 'change'; + editor.table.addfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } else if (editor_mode.doubleClickMode === 'delete') { + editor_mode.doubleClickMode = 'change'; + editor.table.deletefunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + + /** + * 删除表格项 + */ + editor_table.prototype.deletefunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + if (editor.table.checkRange(cobj, null)) { + editor_mode.addAction(['delete', field, undefined]); + editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 + } else { + printe(field + ' : 该值不允许为null,无法删除'); + } + } + + /** + * 添加表格项 + */ + editor_table.prototype.addfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + + var mode = document.getElementById('editModeSelect').value; + + // 1.输入id + var newid = prompt('请输入新项的ID(仅公共事件支持中文ID)'); + if (newid == null || newid.length == 0) { + return; + } + + // 检查commentEvents + if (mode !== 'commonevent') { + // 2.检查id是否符合规范或与已有id重复 + if (!/^[a-zA-Z0-9_]+$/.test(newid)) { + printe('id不符合规范, 请使用大小写字母数字下划线来构成'); + return; + } + } + + var conflict = true; + var basefield = field.replace(/\[[^\[]*\]$/, ''); + if (basefield === "['main']") { + printe("全塔属性 ~ ['main'] 不允许添加新值"); + return; + } + try { + var baseobj = eval('obj' + basefield); + conflict = newid in baseobj; + } catch (ee) { + // 理论上这里不会发生错误 + printe(ee); + throw ee; + } + if (conflict) { + printe('id已存在, 请直接修改该项的值'); + return; + } + // 3.添加 + editor_mode.addAction(['add', basefield + "['" + newid + "']", null]); + editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 + } + + ///////////////////////////////////////////////////////////////////////////// + editor.constructor.prototype.table = new editor_table(); +} +//editor_table_wrapper(editor); \ No newline at end of file diff --git a/_server/editor_util.js b/_server/editor_util.js new file mode 100644 index 00000000..bab29837 --- /dev/null +++ b/_server/editor_util.js @@ -0,0 +1,150 @@ +editor_util_wrapper = function (editor) { + + editor_util = function () { + + } + + editor_util.prototype.guid = function () { + return 'id_' + 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + editor_util.prototype.HTMLescape = function (str_) { + return String(str_).split('').map(function (v) { + return '&#' + v.charCodeAt(0) + ';' + }).join(''); + } + + editor_util.prototype.getPixel = function (imgData, x, y) { + var offset = (x + y * imgData.width) * 4; + var r = imgData.data[offset + 0]; + var g = imgData.data[offset + 1]; + var b = imgData.data[offset + 2]; + var a = imgData.data[offset + 3]; + return [r, g, b, a]; + } + + editor_util.prototype.setPixel = function (imgData, x, y, rgba) { + var offset = (x + y * imgData.width) * 4; + imgData.data[offset + 0] = rgba[0]; + imgData.data[offset + 1] = rgba[1]; + imgData.data[offset + 2] = rgba[2]; + imgData.data[offset + 3] = rgba[3]; + } + + // rgbToHsl hue2rgb hslToRgb from https://github.com/carloscabo/colz.git + //-------------------------------------------- + // The MIT License (MIT) + // + // Copyright (c) 2014 Carlos Cabo + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. + //-------------------------------------------- + // https://github.com/carloscabo/colz/blob/master/public/js/colz.class.js + var round=Math.round; + var rgbToHsl = function (rgba) { + var arg, r, g, b, h, s, l, d, max, min; + + arg = rgba; + + if (typeof arg[0] === 'number') { + r = arg[0]; + g = arg[1]; + b = arg[2]; + } else { + r = arg[0][0]; + g = arg[0][1]; + b = arg[0][2]; + } + + r /= 255; + g /= 255; + b /= 255; + + max = Math.max(r, g, b); + min = Math.min(r, g, b); + l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + //CARLOS + h = round(h * 360); + s = round(s * 100); + l = round(l * 100); + + return [h, s, l]; + } + // + var hue2rgb = function (p, q, t) { + if (t < 0) { t += 1; } + if (t > 1) { t -= 1; } + if (t < 1 / 6) { return p + (q - p) * 6 * t; } + if (t < 1 / 2) { return q; } + if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } + return p; + } + var hslToRgb = function (hsl) { + var arg, r, g, b, h, s, l, q, p; + + arg = hsl; + + if (typeof arg[0] === 'number') { + h = arg[0] / 360; + s = arg[1] / 100; + l = arg[2] / 100; + } else { + h = arg[0][0] / 360; + s = arg[0][1] / 100; + l = arg[0][2] / 100; + } + + if (s === 0) { + r = g = b = l; // achromatic + } else { + + q = l < 0.5 ? l * (1 + s) : l + s - l * s; + p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + return [round(r * 255), round(g * 255), round(b * 255)]; + } + editor_util.prototype.rgbToHsl=rgbToHsl + editor_util.prototype.hue2rgb=hue2rgb + editor_util.prototype.hslToRgb=hslToRgb + + editor.constructor.prototype.util = new editor_util(); +} +//editor_util_wrapper(editor); \ No newline at end of file diff --git a/_server/events.comment.js b/_server/events.comment.js index 62fdbfef..a96301d7 100644 --- a/_server/events.comment.js +++ b/_server/events.comment.js @@ -13,14 +13,21 @@ var events_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "event", "_range": "thiseval instanceof Array", "_event": "commonEvent", - "_data": "打败怪物后进行加点" + "_data": "打败怪物后加点" }, "毒衰咒处理": { "_leaf": true, "_type": "event", "_range": "thiseval instanceof Array", "_event": "commonEvent", - "_data": "对毒衰咒效果进行的处理" + "_data": "毒衰咒效果处理" + }, + "滑冰事件": { + "_leaf": true, + "_type": "event", + "_range": "thiseval instanceof Array", + "_event": "commonEvent", + "_data": "滑冰事件" }, } if (obj[key]) return obj[key]; diff --git a/_server/functions.comment.js b/_server/functions.comment.js index 89181b22..1d295260 100644 --- a/_server/functions.comment.js +++ b/_server/functions.comment.js @@ -4,20 +4,19 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "object", "_data": { "events": { - "_type": "object", "_data": { - "initGame": { + "resetGame": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "游戏开始前的一些初始化操作" + "_data": "重置整个游戏" }, "setInitData": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "不同难度分别设置初始属性" + "_data": "设置初始属性" }, "win": { "_leaf": true, @@ -35,127 +34,124 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "正在切换楼层过程中的操作\n此函数的执行时间是在切换楼层过程中屏幕完全变黑的一刻" + "_data": "切换楼层中" }, "afterChangeFloor": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "转换楼层结束的事件\n此函数会在整个楼层切换完全结束后执行" + "_data": "切换楼层后" }, - "addPoint": { + "flyTo": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "加点事件" + "_data": "楼层飞行" + }, + "beforeBattle": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战前事件" }, "afterBattle": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "战斗结束后触发的事件" + "_data": "战后事件" }, "afterOpenDoor": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "开一个门后触发的事件" + "_data": "开门后事件" }, "afterGetItem": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "获得一个道具后触发的事件" + "_data": "获得道具后事件" }, "afterChangeLight": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "改变亮灯之后,可以触发的事件" + "_data": "改变亮灯事件" }, "afterPushBox": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "推箱子后的事件" + "_data": "推箱子事件" }, "afterUseBomb": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "使用炸弹/圣锤后的事件" - }, - "beforeSaveData": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "即将存档前可以执行的操作" - }, - "afterLoadData": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "读档事件后,载入事件前,可以执行的操作" + "_data": "炸弹事件" }, "canUseQuickShop": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "当前能否使用快捷商店" + "_data": "能否用快捷商店" } } }, "enemys": { - "_type": "object", "_data": { "getSpecials": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "怪物特殊属性的定义(获得怪物的特殊属性)" + "_data": "怪物特殊属性定义" }, "getEnemyInfo": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "获得某个怪物的当前属性数据\n该函数主要是会被伤害计算和怪物手册等使用" + "_data": "获得怪物真实属性" }, "getDamageInfo": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "获得战斗伤害信息(实际伤害计算函数)" + "_data": "获得战斗伤害信息" }, "updateEnemys": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "更新怪物数据,可以在这里对怪物属性和数据进行动态更新" + "_data": "更新怪物数据" } } }, "actions": { - "_type": "object", "_data": { "onKeyUp": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "按键处理;可以在这里自定义快捷键,详见文档-个性化-自定义快捷键" + "_data": "按键处理" } } }, "control": { - "_type": "object", "_data": { - "flyTo": { + "saveData": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "使用楼层传送器飞到某层" + "_data": "存档操作" + }, + "loadData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "读档操作" }, "updateStatusBar": { "_leaf": true, @@ -167,55 +163,48 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "阻激夹域的伤害值计算" + "_data": "阻激夹域伤害" }, "moveOneStep": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "勇士每走一步的操作" + "_data": "每一步后的操作" + }, + "moveDirectly": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "瞬间移动处理" + }, + "parallelDo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "并行事件处理" } } }, "ui": { - "_type": "object", "_data": { "drawStatusBar": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "自定义绘制状态栏。\n当状态栏canvas化开启时,可以在这里对状态栏进行自定义绘制。\n仅当statusCanvas开启时有效。" + "_data": "自绘状态栏" }, "drawStatistics": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "地图数据统计项的注册" + "_data": "地图数据统计" }, "drawAbout": { "_leaf": true, "_type": "textarea", "_lint": true, - "_data": "绘制“关于”界面" - } - } - }, - "plugins": { - - "_type": "object", - "_data": { - "parallelDo": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "并行事件处理" - }, - "plugin": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "自定义插件编写" + "_data": "绘制关于界面" } } } diff --git a/_server/plugins.comment.js b/_server/plugins.comment.js new file mode 100644 index 00000000..e708c576 --- /dev/null +++ b/_server/plugins.comment.js @@ -0,0 +1,27 @@ +var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = +{ + "_type": "object", + "_data": function (key) { + var obj = { + "init": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string'", + "_data": "自定义插件" + }, + "drawLight": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "绘制灯光效果" + }, + } + if (obj[key]) return obj[key]; + return { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "自定义插件" + } + } +} \ No newline at end of file diff --git a/_server/refactoring.md b/_server/refactoring.md new file mode 100644 index 00000000..55b02bfe --- /dev/null +++ b/_server/refactoring.md @@ -0,0 +1,101 @@ +# 重构 + ++ 按功能拆分文件 ++ 左侧页面模块化, 方便添加 ++ 不同的模式的文件操作尽可能模块化 + + +## 文件结构 + ++ [ ] editor_blockly 图块化事件编辑器 ++ [ ] editor_multi 多行文本编辑器 ++ [x] editor_table 处理表格的生成, 及其响应的事件, 从原editor\_mode中分离 ++ [ ] editor_file 调用fs.js编辑文件, 把原editor\_file模块化 ++ [ ] editor_game 处理来自core的数据, 导入为editor的数据, 从原editor中分离 ++ [x] editor_util 生成guid等函数, 从editor分离 ++ [ ] editor 执行初始化流程加组合各组件 ++ [ ] 原editor_mode 移除 ++ [ ] 原vm 移除 ++ [ ] \*comment.js 表格注释与结构, 移至comment/\*comment.js + +## 对象结构 + +``` +editor: { + __proto__: { + fs + util + file + table + multi + blockly + game + } + config: 编辑器配置 + mode: 当前的模式(左侧的选择) + map: 当前编辑层的地图 + isMobile: 编辑器是否是手机端 + currentFloorData: 当前编辑的楼层数据 + ... +} +``` + +--- + +## 某些注意到的点&准备修改的内容 + ++ 插入公共事件的参数的转义处理, .g4中添加ObjectString, 要求其中的值可以JSON.parse, 生成的code中也是作为对象而不是字符串出现 + ++ 转义改由editor.blockly处理,editor.multi原样接受和返回 + ++ 地图的编辑与其他(如全塔属性和楼层属性), 现在的文件操作的模式是完全不同的 + 楼层文件的储存与其他不同 + ++ functions和plugins的借助JSON.stringify的replacer特殊处理 + ++ 目前editor.map中储存的是info\, 准备改为和core一致只储存数字 + ++ editor.file在修改是不再返回obj和commentobj,只在查询时返回 + +## 功能改进 + ++ [ ] 大地图 + 在切换时, 每次都回到最左上->每个楼层记录一个位置 + 四个箭头支持长按 + ? 滚动条 + ++ [ ] ? 表格折叠 + 变为四栏, 可以折叠展开 + ++ [x] blockly对于无法识别的图块原样返回 + ++ [ ] ? 简洁的事件方块注册 + `editor.registerEvent('log',[['test','Int','测试',0],['floorId','Idstring','楼层','MT0']])` + ++ [ ] 一个显示所有快捷键的文本 + ++ [ ] 更多快捷键 + 【全塔属性】、【楼层属性】等常用的编辑栏切换 + ++ [ ] ? 地图编辑优化 + 常用的地图编辑快捷键/命令:复制ctrl+c、粘贴ctrl+v、(复制可绑定为现在的“选中xx位置事件” 粘贴为复制xx事件到此处),撤回ctrl+z、取消撤回ctrl+y + 可以按住拖动图块与事件。 + ++ [ ] ? 自由建立快捷键到命令的注册表。 + ++ [ ] 画地图也自动保存 + ++ [ ] 修改系统的触发器(下拉菜单增加新项) + 在编辑器修改`comment.js`:现场发readFile请求读文件,然后开脚本编辑器进行编辑 + ++ [ ] ? 删除注册项/修改图块ID + ++ [ ] ? 怪物和道具也能像其他类型那样查看“图块信息”(而不只是具体的怪物属性) + +## 左侧页面模式 + +标题? 保存按钮? 添加按钮? 删除按钮? + +自定义内容? + +表格? diff --git a/_server/vm.js b/_server/vm.js index 6707dea0..be273557 100644 --- a/_server/vm.js +++ b/_server/vm.js @@ -1,226 +1,205 @@ -// vue 相关处理 -var exportMap = new Vue({ - el: '#exportMap', - data: { - isExport: false, - }, - methods: { - exportMap: function () { - editor.updateMap(); - var sx=editor.map.length-1,sy=editor.map[0].length-1; +var exportMap = document.getElementById('exportMap') +exportMap.isExport=false +exportMap.onclick=function(){ + editor.updateMap(); + var sx=editor.map.length-1,sy=editor.map[0].length-1; - var filestr = ''; - for (var yy = 0; yy <= sy; yy++) { - filestr += '[' - for (var xx = 0; xx <= sx; xx++) { - var mapxy = editor.map[yy][xx]; - if (typeof(mapxy) == typeof({})) { - if ('idnum' in mapxy) mapxy = mapxy.idnum; - else { - // mapxy='!!?'; - tip.whichShow = 3; - return; - } - } else if (typeof(mapxy) == 'undefined') { - tip.whichShow = 3; - return; - } - mapxy = String(mapxy); - mapxy = Array(Math.max(4 - mapxy.length, 0)).join(' ') + mapxy; - filestr += mapxy + (xx == sx ? '' : ',') + var filestr = ''; + for (var yy = 0; yy <= sy; yy++) { + filestr += '[' + for (var xx = 0; xx <= sx; xx++) { + var mapxy = editor.map[yy][xx]; + if (typeof(mapxy) == typeof({})) { + if ('idnum' in mapxy) mapxy = mapxy.idnum; + else { + // mapxy='!!?'; + tip.whichShow(3); + return; } - - filestr += ']' + (yy == sy ? '' : ',\n'); - } - pout.value = filestr; - editArea.mapArr = filestr; - this.isExport = true; - editArea.error = 0; - tip.whichShow = 2; - } - } -}) -var editArea = new Vue({ - el: '#editArea', - data: { - mapArr: '', - errors: [ // 编号1,2,3,4 - "格式错误!请使用正确格式(请使用地图生成器进行生成,且需要和本地图宽高完全一致)", - "当前有未定义ID(在地图区域显示红块),请修改ID或者到icons.js和maps.js中进行定义!", - "ID越界(在地图区域显示红块),当前编辑器暂时支持编号小于400,请修改编号!", - // "发生错误!", - ], - error: 0, - formatTimer: null, - }, - watch: { - mapArr: function (val, oldval) { - var that = this; - if (val == '') return; - if (exportMap.isExport) { - exportMap.isExport = false; + } else if (typeof(mapxy) == 'undefined') { + tip.whichShow(3); return; } - if (that.formatArr()) { - that.error = 0; - - setTimeout(function () { - that.mapArr = that.formatArr(); - that.drawMap(); - tip.whichShow = 8 - }, 1000); - that.formatTimer = setTimeout(function () { - pout.value = that.formatArr(); - }, 5000); //5s后再格式化,不然光标跳到最后很烦 - } else { - that.error = 1; - } - }, - error: function () { - // console.log(editArea.mapArr); - if (this.error>0) - printe(this.errors[this.error-1]) + mapxy = String(mapxy); + mapxy = Array(Math.max(4 - mapxy.length, 0)).join(' ') + mapxy; + filestr += mapxy + (xx == sx ? '' : ',') } - }, - methods: { - drawMap: function () { - var that = this; - // var mapArray = that.mapArr.split(/\D+/).join(' ').trim().split(' '); - var mapArray = JSON.parse('[' + that.mapArr + ']'); - var sy=editor.map.length,sx=editor.map[0].length; - for (var y = 0; y < sy; y++) - for (var x = 0; x < sx; x++) { - var num = mapArray[y][x]; - if (num == 0) - editor.map[y][x] = 0; - else if (typeof(editor.indexs[num][0]) == 'undefined') { - that.error = 2; - editor.map[y][x] = undefined; - } else editor.map[y][x] = editor.ids[[editor.indexs[num][0]]]; - } - - editor.updateMap(); - - }, - formatArr: function () { - var formatArrStr = ''; - var that = this; - clearTimeout(that.formatTimer); - var si=editor.map.length,sk=editor.map[0].length; - if (this.mapArr.split(/\D+/).join(' ').trim().split(' ').length != si*sk) return false; - var arr = this.mapArr.replace(/\s+/g, '').split('],['); - - if (arr.length != si) return; - for (var i = 0; i < si; i++) { - var a = []; - formatArrStr += '['; - if (i == 0 || i == si-1) a = arr[i].split(/\D+/).join(' ').trim().split(' '); - else a = arr[i].split(/\D+/); - if (a.length != sk) { - formatArrStr = ''; - return; - } - - for (var k = 0; k < sk; k++) { - var num = parseInt(a[k]); - formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk-1 ? '' : ','); - } - formatArrStr += ']' + (i == si-1 ? '' : ',\n'); - } - return formatArrStr; - } + filestr += ']' + (yy == sy ? '' : ',\n'); } -}); -var copyMap = new Vue({ - el: '#copyMap', - data: { - err: '' - }, - methods: { - copyMap: function () { + pout.value = filestr; + mapEditArea.mapArr(filestr); + exportMap.isExport = true; + mapEditArea.error(0); + tip.whichShow(2); +} +var mapEditArea = document.getElementById('mapEditArea') +mapEditArea.errors=[ // 编号1,2 + "格式错误!请使用正确格式(请使用地图生成器进行生成,且需要和本地图宽高完全一致)", + "当前有未定义ID(在地图区域显示红块),请修改ID或者到icons.js和maps.js中进行定义!" +] +mapEditArea.formatTimer=null +mapEditArea._mapArr='' +mapEditArea.mapArr=function(value){ + if(value!=null){ + var val=value + var oldval=mapEditArea._mapArr + if (val==oldval) return; - tip.whichShow = 0; - if (pout.value.trim() != '') { - if (editArea.error) { - this.err = editArea.errors[editArea.error - 1]; - tip.whichShow = 5 - return; - } - try { - pout.focus(); - pout.setSelectionRange(0, pout.value.length); - document.execCommand("Copy"); - tip.whichShow = 6; - } catch (e) { - this.err = e; - tip.whichShow = 5; - } - } else { - tip.whichShow = 7; - } + if (exportMap.isExport) { + exportMap.isExport = false; + return; } - }, -}) -var clearMap = new Vue({ - el: '#clearMap', + if (mapEditArea.formatArr()) { + mapEditArea.error(0); - methods: { - clearMap: function () { - editor.mapInit(); - editor_mode.onmode(''); - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('地图清除成功'); - }); - editor.updateMap(); - clearTimeout(editArea.formatTimer); - clearTimeout(tip.timer); - pout.value = ''; - editArea.mapArr = ''; - tip.whichShow = 4; - editArea.error = 0; + setTimeout(function () { + if (mapEditArea.formatArr())mapEditArea.mapArr(mapEditArea.formatArr()); + mapEditArea.drawMap(); + tip.whichShow(8) + }, 1000); + clearTimeout(mapEditArea.formatTimer); + mapEditArea.formatTimer = setTimeout(function () { + pout.value = mapEditArea.formatArr(); + }, 5000); //5s后再格式化,不然光标跳到最后很烦 + } else { + mapEditArea.error(1); } + + mapEditArea._mapArr=value } -}) -var deleteMap = new Vue({ - el: '#deleteMap', - methods: { - deleteMap: function () { - editor_mode.onmode(''); - var index = core.floorIds.indexOf(editor.currentFloorId); - if (index>=0) { - core.floorIds.splice(index,1); - editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); - if (objs_.slice(-1)[0] != null) { - printe(objs_.slice(-1)[0]); - throw(objs_.slice(-1)[0]) - } - ;printe('删除成功,请F5刷新编辑器生效'); - }); - } - else printe('删除成功,请F5刷新编辑器生效'); + return mapEditArea._mapArr +} +pout.oninput=function(){ + mapEditArea.mapArr(pout.value) +} +mapEditArea._error=0 +mapEditArea.error=function(value){ + if(value!=null){ + mapEditArea._error=value + if (value>0) + printe(mapEditArea.errors[value-1]) + } + return mapEditArea._error +} +mapEditArea.drawMap= function () { + // var mapArray = mapEditArea.mapArr().split(/\D+/).join(' ').trim().split(' '); + var mapArray = JSON.parse('[' + mapEditArea.mapArr() + ']'); + var sy=editor.map.length,sx=editor.map[0].length; + for (var y = 0; y < sy; y++) + for (var x = 0; x < sx; x++) { + var num = mapArray[y][x]; + if (num == 0) + editor.map[y][x] = 0; + else if (typeof(editor.indexs[num][0]) == 'undefined') { + mapEditArea.error(2); + editor.map[y][x] = undefined; + } else editor.map[y][x] = editor.ids[[editor.indexs[num][0]]]; } + + editor.updateMap(); + +} +mapEditArea.formatArr= function () { + var formatArrStr = ''; + console.log(1) + + var si=editor.map.length,sk=editor.map[0].length; + if (mapEditArea.mapArr().split(/\D+/).join(' ').trim().split(' ').length != si*sk) return false; + var arr = mapEditArea.mapArr().replace(/\s+/g, '').split('],['); + + if (arr.length != si) return; + for (var i = 0; i < si; i++) { + var a = []; + formatArrStr += '['; + if (i == 0 || i == si-1) a = arr[i].split(/\D+/).join(' ').trim().split(' '); + else a = arr[i].split(/\D+/); + if (a.length != sk) { + formatArrStr = ''; + return; + } + + for (var k = 0; k < sk; k++) { + var num = parseInt(a[k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk-1 ? '' : ','); + } + formatArrStr += ']' + (i == si-1 ? '' : ',\n'); } -}) -printf = function (str_, type) { - selectBox.isSelected = false; - if (!type) { - tip.whichShow = 11; + return formatArrStr; +} +var copyMap=document.getElementById('copyMap') +copyMap.err='' +copyMap.onclick=function(){ + tip.whichShow(0); + if (pout.value.trim() != '') { + if (mapEditArea.error()) { + copyMap.err = mapEditArea.errors[mapEditArea.error() - 1]; + tip.whichShow(5) + return; + } + try { + pout.focus(); + pout.setSelectionRange(0, pout.value.length); + document.execCommand("Copy"); + tip.whichShow(6); + } catch (e) { + copyMap.err = e; + tip.whichShow(5); + } } else { - tip.whichShow = 12; + tip.whichShow(7); + } +} +var clearMapButton=document.getElementById('clearMapButton') +clearMapButton.onclick=function () { + editor.mapInit(); + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('地图清除成功'); + }); + editor.updateMap(); + clearTimeout(mapEditArea.formatTimer); + clearTimeout(tip.timer); + pout.value = ''; + mapEditArea.mapArr(''); + tip.whichShow(4); + mapEditArea.error(0); +} +var deleteMap=document.getElementById('deleteMap') +deleteMap.onclick=function () { + editor_mode.onmode(''); + var index = core.floorIds.indexOf(editor.currentFloorId); + if (index>=0) { + core.floorIds.splice(index,1); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw(objs_.slice(-1)[0]) + } + ;printe('删除成功,请F5刷新编辑器生效'); + }); + } + else printe('删除成功,请F5刷新编辑器生效'); +} +printf = function (str_, type) { + selectBox.isSelected(false); + if (!type) { + tip.whichShow(11); + } else { + tip.whichShow(12); } setTimeout(function () { if (!type) { tip.msgs[11] = String(str_); - tip.whichShow = 12; + tip.whichShow(12); } else { tip.msgs[10] = String(str_); - tip.whichShow = 11; + tip.whichShow(11); } }, 1); } @@ -233,87 +212,158 @@ tip_in_showMode = [ '事件编辑器中的显示文本和自定义脚本的方块也可以双击', "画出的地图要点击\"保存地图\"才会写入到文件中", ]; -var tip = new Vue({ - el: '#tip', - data: { - infos: {}, - hasId: true, - isAutotile: false, - isSelectedBlock: false, - isClearBlock: false, - isAirwall: false, - geneMapSuccess: false, - timer: null, - msgs: [ //分别编号1,2,3,4,5,6,7,8,9,10;奇数警告,偶数成功 - "当前未选择任何图块,请先在右边选择要画的图块!", - "生成地图成功!可点击复制按钮复制地图数组到剪切板", - "生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!", - "地图清除成功!", - "复制失败!", - "复制成功!可直接粘贴到楼层文件的地图数组中。", - "复制失败!当前还没有数据", - "修改成功!可点击复制按钮复制地图数组到剪切板", - "选择背景图片失败!文件名格式错误或图片不存在!", - "更新背景图片成功!", - "11:警告", - "12:成功" - ], - mapMsg: '', - whichShow: 0, - }, - watch: { - infos: { - handler: function (val, oldval) { - this.isClearBlock = false; - this.isAirwall = false; - if (typeof(val) != 'undefined') { - if (val == 0) { - this.isClearBlock = true; - return; - } - if ('id' in val) { - if (val.idnum == 17) { - this.isAirwall = true; - return; - } - this.hasId = true; - } else { - this.hasId = false; - } - this.isAutotile = false; - if (val.images == "autotile" && this.hasId) this.isAutotile = true; - } - }, - deep: true - }, +var tip=document.getElementById('tip') +tip._infos= {} +tip.infos=function(value){ + if(value!=null){ + var val=value + var oldval=tip._infos - whichShow: function () { - var that = this; - that.mapMsg = ''; - that.msgs[4] = "复制失败!" + editTip.err; - clearTimeout(that.timer); - if (that.whichShow) { - that.mapMsg = that.msgs[that.whichShow - 1]; - that.timer = setTimeout(function () { - if (!(that.whichShow % 2)) - that.whichShow = 0; - }, 5000); //5秒后自动清除success,warn不清除 + tip.isClearBlock(false); + tip.isAirwall(false); + if (typeof(val) != 'undefined') { + if (val == 0) { + tip.isClearBlock(true); + return; } + if ('id' in val) { + if (val.idnum == 17) { + tip.isAirwall(true); + return; + } + tip.hasId = true; + } else { + tip.hasId = false; + } + tip.isAutotile = false; + if (val.images == "autotile" && tip.hasId) tip.isAutotile = true; + document.getElementById('isAirwall-else').innerHTML=(tip.hasId?`

图块编号:${ value['idnum'] }

+

图块ID:${ value['id'] }

`:` +

该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!

`)+` +

图块所在素材:${ value['images'] + (tip.isAutotile ? '( '+value['id']+' )' : '') } +

+

图块索引:${ value['y'] }

` } - } -}) -var selectBox = new Vue({ - el: '#selectBox', - data: { - isSelected: false - }, - watch: { - isSelected: function () { - tip.isSelectedBlock = this.isSelected; - tip.whichShow = 0; - clearTimeout(tip.timer); + tip._infos=value + } + return tip._infos +} +tip.hasId= true +tip.isAutotile= false +tip._isSelectedBlock= false +tip.isSelectedBlock=function(value){ + if(value!=null){ + var dshow=document.getElementById('isSelectedBlock-if') + var dhide=document.getElementById('isSelectedBlock-else') + if(!value){ + var dtemp=dshow + dshow=dhide + dhide=dtemp } + dshow.style.display='' + dhide.style.display='none' + tip._isSelectedBlock=value } -}) + return tip._isSelectedBlock +} +tip._isClearBlock= false +tip.isClearBlock=function(value){ + if(value!=null){ + var dshow=document.getElementById('isClearBlock-if') + var dhide=document.getElementById('isClearBlock-else') + if(!value){ + var dtemp=dshow + dshow=dhide + dhide=dtemp + } + dshow.style.display='' + dhide.style.display='none' + tip._isClearBlock=value + } + return tip._isClearBlock +} +tip._isAirwall= false +tip.isAirwall=function(value){ + if(value!=null){ + var dshow=document.getElementById('isAirwall-if') + var dhide=document.getElementById('isAirwall-else') + if(!value){ + var dtemp=dshow + dshow=dhide + dhide=dtemp + } + dshow.style.display='' + dhide.style.display='none' + tip._isAirwall=value + } + return tip._isAirwall +} +tip.geneMapSuccess= false +tip.timer= null +tip.msgs= [ //分别编号1,2,3,4,5,6,7,8,9,10;奇数警告,偶数成功 + "当前未选择任何图块,请先在右边选择要画的图块!", + "生成地图成功!可点击复制按钮复制地图数组到剪切板", + "生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!", + "地图清除成功!", + "复制失败!", + "复制成功!可直接粘贴到楼层文件的地图数组中。", + "复制失败!当前还没有数据", + "修改成功!可点击复制按钮复制地图数组到剪切板", + "选择背景图片失败!文件名格式错误或图片不存在!", + "更新背景图片成功!", + "11:警告", + "12:成功" +] +tip._mapMsg= '' +tip.mapMsg=function(value){ + if(value!=null){ + document.getElementById('whichShow-if').innerText=value + tip._mapMsg=value + } + return tip._mapMsg +} +tip._whichShow= 0 +tip.whichShow=function(value){ + if(value!=null){ + + var dshow=document.getElementById('whichShow-if') + var dhide=null + if(!value){ + var dtemp=dshow + dshow=dhide + dhide=dtemp + } + if(dshow)dshow.style.display='' + if(dhide)dhide.style.display='none' + + if(dshow)dshow.setAttribute('class',(value%2) ? 'warnText' : 'successText') + + tip.mapMsg(''); + tip.msgs[4] = "复制失败!" + editTip.err; + clearTimeout(tip.timer); + if (value) { + tip.mapMsg(tip.msgs[value - 1]); + tip.timer = setTimeout(function () { + if (!(value % 2)) + value = 0; + }, 5000); //5秒后自动清除success,warn不清除 + } + tip._whichShow=value + } + return tip._whichShow +} +var selectBox=document.getElementById('selectBox') +var dataSelection=document.getElementById('dataSelection') +selectBox._isSelected=false +selectBox.isSelected=function(value){ + if(value!=null){ + selectBox._isSelected=value; + tip.isSelectedBlock(value); + tip.whichShow(0); + clearTimeout(tip.timer); + dataSelection.style.display=value?'':'none' + } + return selectBox._isSelected +} diff --git a/editor-mobile.html b/editor-mobile.html index 681feb8f..ebcc334c 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -18,9 +18,8 @@
-
- - +
+
@@ -34,10 +33,10 @@
- - - - + + + +
+
+

插件编写       +

+
+
+ + + + + + + + +
条目注释
+
+
+
@@ -277,28 +293,23 @@
-
+
-
-
-

当前选择为清除块,可擦除地图上块

-

当前选择为空气墙, 在编辑器中可视, 在游戏中隐藏的墙, 用来配合前景/背景的贴图

-
-

图块编号:{{ infos['idnum'] }}

-

图块ID:{{ infos['id'] }}

-

该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!

-

图块所在素材:{{ infos['images'] + (isAutotile ? '( '+infos['id']+' )' : '') }} -

-

图块索引:{{ infos['y'] }}

-
+
+ -
-

{{ mapMsg }}

+
+
@@ -329,6 +340,7 @@ + + + +
+
@@ -505,7 +525,10 @@ if (location.protocol.indexOf("http")!=0) { + + + @@ -514,8 +537,8 @@ if (location.protocol.indexOf("http")!=0) { --> @@ -488,7 +508,10 @@ if (location.protocol.indexOf("http")!=0) { + + + @@ -497,8 +520,8 @@ if (location.protocol.indexOf("http")!=0) { - + \ No newline at end of file diff --git a/libs/actions.js b/libs/actions.js index 87bedddf..bb0cce1f 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -7,6 +7,11 @@ actions.js:用户交互的事件的处理 function actions() { this._init(); + this.SIZE = core.__SIZE__; + this.HSIZE = core.__HALF_SIZE__; + this.LAST = this.SIZE - 1; + this.CHOICES_LEFT = 5; // choices + this.CHOICES_RIGHT = this.LAST - this.CHOICES_LEFT; } actions.prototype._init = function () { @@ -84,30 +89,32 @@ actions.prototype.registerAction = function (action, name, func, priority) { ////// 注销一个用户交互行为 ////// actions.prototype.unregisterAction = function (action, name) { if (!this.actions[action]) return; - this.actions[action] = this.actions[action].filter(function (x) { return x.name != name; }); + this.actions[action] = this.actions[action].filter(function (x) { + return x.name != name; + }); } ////// 执行一个用户交互行为 ////// actions.prototype.doRegisteredAction = function (action) { var actions = this.actions[action]; - if (!core.isset(actions)) return false; + if (!actions) return false; for (var i = 0; i < actions.length; ++i) { - if (actions[i].func.apply(this, Array.prototype.slice.call(arguments, 1))) + if (core.doFunc.apply(core, [actions[i].func, this].concat(Array.prototype.slice.call(arguments, 1)))) return true; } return false; } -actions.prototype.checkReplaying = function () { - if (core.isReplaying()&&core.status.event.id!='save' - &&(core.status.event.id||"").indexOf('book')!=0&&core.status.event.id!='viewMaps') +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; return false; } ////// 检查是否在录像播放中,如果是,则停止交互 actions.prototype._sys_checkReplay = function () { - if (this.checkReplaying()) return true; + if (this._checkReplaying()) return true; } ////// 按下某个键时 ////// @@ -116,70 +123,70 @@ actions.prototype.onkeyDown = function (e) { } actions.prototype._sys_onkeyDown = function (e) { - if (!core.isset(core.status.holdingKeys))core.status.holdingKeys=[]; - var isArrow={37:true,38:true,39:true,40:true}[e.keyCode] - if(isArrow && !core.status.lockControl){ - for(var ii =0;ii=49 && e.keyCode<=51) - core.setReplaySpeed(e.keyCode-48); - else if (e.keyCode==52) + else if (e.keyCode >= 49 && e.keyCode <= 51) + core.setReplaySpeed(e.keyCode - 48); + else if (e.keyCode == 52) core.setReplaySpeed(6); - else if (e.keyCode==53) + else if (e.keyCode == 53) core.setReplaySpeed(12); - else if (e.keyCode==54) + else if (e.keyCode == 54) core.setReplaySpeed(24); return true; } } actions.prototype._sys_onkeyUp = function (e) { - var isArrow={37:true,38:true,39:true,40:true}[e.keyCode] - if(isArrow && !core.status.lockControl){ - for(var ii =0;ii0) { - var pos={'x':x,'y':y}; - var pos0=core.status.stepPostfix[core.status.stepPostfix.length-1]; - var directionDistance=[pos.y-pos0.y,pos0.x-pos.x,pos0.y-pos.y,pos.x-pos0.x]; - var max=0,index=4; - for(var ii=0;ii<4;ii++){ - if(directionDistance[ii]>max){ - index=ii; - max=directionDistance[ii]; + if ((core.status.stepPostfix || []).length > 0) { + var pos = {'x': x, 'y': y}; + var pos0 = core.status.stepPostfix[core.status.stepPostfix.length - 1]; + var directionDistance = [pos.y - pos0.y, pos0.x - pos.x, pos0.y - pos.y, pos.x - pos0.x]; + var max = 0, index = 4; + for (var ii = 0; ii < 4; ii++) { + if (directionDistance[ii] > max) { + index = ii; + max = directionDistance[ii]; } } - pos=[{'x':0,'y':1},{'x':-1,'y':0},{'x':0,'y':-1},{'x':1,'y':0},false][index] - if(pos){ - pos.x+=pos0.x; - pos.y+=pos0.y; + pos = [{'x': 0, 'y': 1}, {'x': -1, 'y': 0}, {'x': 0, 'y': -1}, {'x': 1, 'y': 0}, false][index] + if (pos) { + pos.x += pos0.x; + pos.y += pos0.y; core.status.stepPostfix.push(pos); core.fillPosWithPoint(pos); } @@ -488,7 +496,7 @@ actions.prototype.onup = function () { actions.prototype._sys_onup_paint = function () { // 画板 if (core.status.played && (core.status.event || {}).id == 'paint') { - this.onupPaint(); + this._onupPaint(); return true; } } @@ -499,36 +507,40 @@ actions.prototype._sys_onup = function () { clearInterval(core.interval.onDownInterval); core.interval.onDownInterval = null; - if ((core.status.stepPostfix||[]).length == 0) return false; + if ((core.status.stepPostfix || []).length == 0) return false; var stepPostfix = []; - var direction={'0':{'1':'down','-1':'up'},'-1':{'0':'left'},'1':{'0':'right'}}; - for(var ii=1;ii=1000) { - this.longClick(posx, posy); + if (!core.status.lockControl && stepPostfix.length == 0 && core.status.downTime != null && new Date() - core.status.downTime >= 1000) { + core.actions.longClick(posx, posy); } else { //posx,posy是寻路的目标点,stepPostfix是后续的移动 - this.onclick(posx,posy,stepPostfix); + core.actions.onclick(posx, posy, stepPostfix); } - core.status.downTime=null; + core.status.downTime = null; 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; @@ -545,7 +557,7 @@ actions.prototype.getClickLoc = function (x, y) { var left = core.dom.gameGroup.offsetLeft + statusBar.x; var top = core.dom.gameGroup.offsetTop + statusBar.y; - var loc={'x': x - left, 'y': y - top, 'size': size}; + var loc = {'x': x - left, 'y': y - top, 'size': size}; return loc; } @@ -562,72 +574,72 @@ actions.prototype._sys_onclick_lockControl = function (x, y) { this._clickCenterFly(x, y); break; case 'book': - this._clickBook(x,y); + this._clickBook(x, y); break; case 'book-detail': - this._clickBookDetail(x,y); + this._clickBookDetail(x, y); break; case 'fly': - this._clickFly(x,y); + this._clickFly(x, y); break; case 'viewMaps': - this._clickViewMaps(x,y); + this._clickViewMaps(x, y); break; case 'switchs': - this._clickSwitchs(x,y); + this._clickSwitchs(x, y); break; case 'settings': - this._clickSettings(x,y); + this._clickSettings(x, y); break; case 'shop': - this._clickShop(x,y); + this._clickShop(x, y); break; case 'selectShop': - this._clickQuickShop(x,y); + this._clickQuickShop(x, y); break; case 'equipbox': - this._clickEquipbox(x,y); + this._clickEquipbox(x, y); break; case 'toolbox': - this._clickToolbox(x,y); + this._clickToolbox(x, y); break; case 'save': case 'load': case 'replayLoad': - this._clickSL(x,y); + this._clickSL(x, y); break; case 'confirmBox': - this._clickConfirmBox(x,y); + this._clickConfirmBox(x, y); break; case 'keyBoard': - this._clickKeyBoard(x,y); + this._clickKeyBoard(x, y); break; case 'action': - this._clickAction(x,y); + this._clickAction(x, y); break; case 'text': core.drawText(); break; case 'syncSave': - this._clickSyncSave(x,y); + this._clickSyncSave(x, y); break; case 'syncSelect': - this._clickSyncSelect(x,y); + this._clickSyncSelect(x, y); break; case 'localSaveSelect': - this._clickLocalSaveSelect(x,y); + this._clickLocalSaveSelect(x, y); break; case 'storageRemove': - this._clickStorageRemove(x,y); + this._clickStorageRemove(x, y); break; case 'cursor': - this._clickCursor(x,y); + this._clickCursor(x, y); break; case 'replay': - this._clickReplay(x,y); + this._clickReplay(x, y); break; case 'gameInfo': - this._clickGameInfo(x,y); + this._clickGameInfo(x, y); break; case 'about': case 'help': @@ -639,7 +651,7 @@ actions.prototype._sys_onclick_lockControl = function (x, y) { actions.prototype._sys_onclick = function (x, y, stepPostfix) { // 寻路 - core.setAutomaticRoute(x+parseInt(core.bigmap.offsetX/32), y+parseInt(core.bigmap.offsetY/32), stepPostfix); + core.setAutomaticRoute(x + parseInt(core.bigmap.offsetX / 32), y + parseInt(core.bigmap.offsetY / 32), stepPostfix); return true; } @@ -651,38 +663,39 @@ 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(); + if (direct == 1) core.speedUpReplay(); + if (direct == -1) core.speedDownReplay(); return; } // 楼层飞行器 if (core.status.lockControl && core.status.event.id == 'fly') { - if (direct==1) core.ui.drawFly(core.status.event.data+1); - if (direct==-1) core.ui.drawFly(core.status.event.data-1); + if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1)); + if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1)); return; } // 怪物手册 if (core.status.lockControl && core.status.event.id == 'book') { - if (direct==1) core.ui.drawBook(core.status.event.data - 6); - if (direct==-1) core.ui.drawBook(core.status.event.data + 6); + if (direct == 1) core.ui.drawBook(core.status.event.data - this.HSIZE); + if (direct == -1) core.ui.drawBook(core.status.event.data + this.HSIZE); return; } // 存读档 if (core.status.lockControl && (core.status.event.id == 'save' || core.status.event.id == 'load')) { - if (direct==1) core.ui.drawSLPanel(core.status.event.data - 10); - if (direct==-1) core.ui.drawSLPanel(core.status.event.data + 10); + var index = core.status.event.data.page*10+core.status.event.data.offset; + if (direct == 1) core.ui.drawSLPanel(index - 10); + if (direct == -1) core.ui.drawSLPanel(index + 10); return; } // 浏览地图 if (core.status.lockControl && core.status.event.id == 'viewMaps') { - if (direct==1) this._clickViewMaps(6,3); - if (direct==-1) this._clickViewMaps(6,9); + if (direct == 1) this._clickViewMaps(this.HSIZE, this.HSIZE - 3); + if (direct == -1) this._clickViewMaps(this.HSIZE, this.HSIZE + 3); return; } @@ -694,20 +707,20 @@ actions.prototype.keyDownCtrl = function () { } actions.prototype._sys_keyDownCtrl = function () { - if (core.status.event.id=='text') { + if (core.status.event.id == 'text') { core.drawText(); return true; } - if (core.status.event.id=='action' && core.status.event.data.type=='text') { + if (core.status.event.id == 'action' && core.status.event.data.type == 'text') { core.doAction(); return true; } - if (core.status.event.id=='action' && core.status.event.data.type=='sleep' + if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { - if (core.isset(core.timeout.sleepTimeout) && Object.keys(core.animateFrame.asyncId).length==0) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { clearTimeout(core.timeout.sleepTimeout); core.timeout.sleepTimeout = null; - core.events.doAction(); + core.doAction(); } return true; } @@ -739,10 +752,10 @@ actions.prototype._sys_longClick_lockControl = function (x, y) { // 长按可以跳过等待事件 if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { - if (core.isset(core.timeout.sleepTimeout) && Object.keys(core.animateFrame.asyncId).length == 0) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { clearTimeout(core.timeout.sleepTimeout); core.timeout.sleepTimeout = null; - core.events.doAction(); + core.doAction(); return true; } } @@ -764,35 +777,35 @@ actions.prototype._sys_longClick = function (x, y, fromEvent) { // 数字键快速选择选项 actions.prototype._selectChoices = function (length, keycode, callback) { - var topIndex = 6 - parseInt((length - 1) / 2); - if (keycode==13 || keycode==32 || keycode==67) { - callback(6, topIndex+core.status.event.selection); + var topIndex = this.HSIZE - parseInt((length - 1) / 2); + if (keycode == 13 || keycode == 32 || keycode == 67) { + callback.apply(this, [this.HSIZE, topIndex + core.status.event.selection]); } - if (keycode>=49 && keycode<=57) { - var index = keycode-49; + if (keycode >= 49 && keycode <= 57) { + var index = keycode - 49; if (index < length) { - callback(6, topIndex+index); + callback.apply(this, [this.HSIZE, topIndex + index]); } } } // 上下键调整选项 actions.prototype._keyDownChoices = function (keycode) { - if (keycode==38) { + if (keycode == 38) { core.status.event.selection--; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } - if (keycode==40) { + if (keycode == 40) { core.status.event.selection++; core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); } } ////// 点击中心对称飞行器时 -actions.prototype._clickCenterFly = function(x, y) { +actions.prototype._clickCenterFly = function (x, y) { var posX = core.status.event.data.posX, posY = core.status.event.data.posY; core.ui.closePanel(); - if (x==posX&& y==posY) { + if (x == posX && y == posY) { if (core.canUseItem('centerFly')) { core.useItem('centerFly'); } @@ -804,7 +817,7 @@ actions.prototype._clickCenterFly = function(x, y) { actions.prototype._keyUpCenterFly = function (keycode) { core.ui.closePanel(); - if (keycode==51 || keycode==13 || keycode==32 || keycode==67) { + if (keycode == 51 || keycode == 13 || keycode == 32 || keycode == 67) { if (core.canUseItem('centerFly')) { core.useItem('centerFly'); } @@ -815,35 +828,35 @@ actions.prototype._keyUpCenterFly = function (keycode) { } ////// 点击确认框时 ////// -actions.prototype._clickConfirmBox = function (x,y) { - if ((x == 4 || x == 5) && y == 7 && core.isset(core.status.event.data.yes)) +actions.prototype._clickConfirmBox = function (x, y) { + if ((x == this.HSIZE-2 || x == this.HSIZE-1) && y == this.HSIZE+1 && core.status.event.data.yes) core.status.event.data.yes(); - if ((x == 7 || x == 8) && y == 7 && core.isset(core.status.event.data.no)) + if ((x == this.HSIZE+2 || x == this.HSIZE+1) && y == this.HSIZE+1 && core.status.event.data.no) core.status.event.data.no(); } ////// 键盘操作确认框时 ////// actions.prototype._keyUpConfirmBox = function (keycode) { - if (keycode==37) { - core.status.event.selection=0; + if (keycode == 37) { + core.status.event.selection = 0; core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); return; } - if (keycode==39) { - core.status.event.selection=1; + if (keycode == 39) { + core.status.event.selection = 1; core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); return; } - if (keycode==13 || keycode==32 || keycode==67) { - if (core.status.event.selection==0 && core.isset(core.status.event.data.yes)) { - core.status.event.selection=null; + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (core.status.event.selection == 0 && core.status.event.data.yes) { + core.status.event.selection = null; core.status.event.data.yes(); return; } - if (core.status.event.selection==1 && core.isset(core.status.event.data.no)) { - core.status.event.selection=null; + if (core.status.event.selection == 1 && core.status.event.data.no) { + core.status.event.selection = null; core.status.event.data.no(); return; } @@ -851,11 +864,11 @@ actions.prototype._keyUpConfirmBox = function (keycode) { } ////// 自定义事件时的点击操作 ////// -actions.prototype._clickAction = function (x,y) { - if (core.status.event.data.type=='text') { +actions.prototype._clickAction = function (x, y) { + if (core.status.event.data.type == 'text') { // 打字机效果显示全部文字 - if (core.status.event.interval!=null) { + if (core.status.event.interval != null) { core.insertAction({"type": "text", "text": core.status.event.ui, "showAll": true}); } @@ -864,70 +877,96 @@ actions.prototype._clickAction = function (x,y) { return; } - if (core.status.event.data.type=='choices') { + if (core.status.event.data.type == 'choices') { // 选项 var data = core.status.event.data.current; var choices = data.choices; - if (choices.length==0) return; - if (x >= 5 && x <= 7) { - var topIndex = 6 - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2); + if (y >= topIndex && y < topIndex + choices.length) { // 选择 - core.status.route.push("choices:"+(y-topIndex)); - core.insertAction(choices[y-topIndex].action); + core.status.route.push("choices:" + (y - topIndex)); + core.insertAction(choices[y - topIndex].action); core.doAction(); } } } + + 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(); + } + } } ////// 自定义事件时,按下某个键的操作 ////// actions.prototype._keyDownAction = function (keycode) { - if (core.status.event.data.type=='choices') { + if (core.status.event.data.type == 'choices') { this._keyDownChoices(keycode); } + 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); + } } ////// 自定义事件时,放开某个键的操作 ////// actions.prototype._keyUpAction = function (keycode) { - if (core.status.event.data.type=='text' && (keycode==13 || keycode==32 || keycode==67)) { + if (core.status.event.data.type == 'text' && (keycode == 13 || keycode == 32 || keycode == 67)) { // 打字机效果显示全部文字 - if (core.status.event.interval!=null) { + if (core.status.event.interval != null) { core.insertAction({"type": "text", "text": core.status.event.ui, "showAll": true}); } core.doAction(); return; } - if (core.status.event.data.type=='wait') { + if (core.status.event.data.type == 'wait') { core.setFlag('type', 0); core.setFlag('keycode', keycode); - core.status.route.push("input:"+keycode); + core.status.route.push("input:" + keycode); core.doAction(); return; } - if (core.status.event.data.type=='choices') { + if (core.status.event.data.type == 'choices') { var data = core.status.event.data.current; var choices = data.choices; - if (choices.length>0) { + 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; } } ////// 怪物手册界面的点击操作 ////// -actions.prototype._clickBook = function(x,y) { +actions.prototype._clickBook = function (x, y) { // 上一页 - if ((x == 3 || x == 4) && y == 12) { - core.ui.drawBook(core.status.event.data - 6); + if ((x == this.HSIZE-2 || x == this.HSIZE-3) && y == this.LAST) { + core.ui.drawBook(core.status.event.data - this.HSIZE); return; } // 下一页 - if ((x == 8 || x == 9) && y == 12) { - core.ui.drawBook(core.status.event.data + 6); + if ((x == this.HSIZE+2 || x == this.HSIZE+3) && y == this.LAST) { + core.ui.drawBook(core.status.event.data + this.HSIZE); return; } // 返回 - if (x>=10 && x<=12 && y==12) { + if (x >= this.LAST-2 && y == this.LAST) { if (core.events.recoverEvents(core.status.event.interval)) { return; } @@ -940,9 +979,9 @@ actions.prototype._clickBook = function(x,y) { } // 怪物信息 var data = core.status.event.data; - if (core.isset(data) && y<12) { - var page=parseInt(data/6); - var index=6*page+parseInt(y/2); + if (data != null && y < this.LAST) { + var page = parseInt(data / this.HSIZE); + var index = this.HSIZE * page + parseInt(y / 2); core.ui.drawBook(index); core.ui.drawBookDetail(index); } @@ -951,18 +990,18 @@ actions.prototype._clickBook = function(x,y) { ////// 怪物手册界面时,按下某个键的操作 ////// actions.prototype._keyDownBook = function (keycode) { - if (keycode==37) core.ui.drawBook(core.status.event.data-6); - if (keycode==38) core.ui.drawBook(core.status.event.data-1); - if (keycode==39) core.ui.drawBook(core.status.event.data+6); - if (keycode==40) core.ui.drawBook(core.status.event.data+1); - if (keycode==33) core.ui.drawBook(core.status.event.data-6); - if (keycode==34) core.ui.drawBook(core.status.event.data+6); + if (keycode == 37) core.ui.drawBook(core.status.event.data - this.HSIZE); + if (keycode == 38) core.ui.drawBook(core.status.event.data - 1); + if (keycode == 39) core.ui.drawBook(core.status.event.data + this.HSIZE); + if (keycode == 40) core.ui.drawBook(core.status.event.data + 1); + if (keycode == 33) core.ui.drawBook(core.status.event.data - this.HSIZE); + if (keycode == 34) core.ui.drawBook(core.status.event.data + this.HSIZE); return; } ////// 怪物手册界面时,放开某个键的操作 ////// actions.prototype._keyUpBook = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { if (core.events.recoverEvents(core.status.event.interval)) { return; } @@ -973,10 +1012,10 @@ actions.prototype._keyUpBook = function (keycode) { else core.ui.closePanel(); return; } - if (keycode==13 || keycode==32 || keycode==67) { - var data=core.status.event.data; - if (core.isset(data)) { - this._clickBook(6, 2*(data%6)); + if (keycode == 13 || keycode == 32 || keycode == 67) { + var data = core.status.event.data; + if (data != null) { + this._clickBook(this.HSIZE, 2 * (data % this.HSIZE)); } return; } @@ -989,95 +1028,114 @@ actions.prototype._clickBookDetail = function () { } ////// 楼层传送器界面时的点击操作 ////// -actions.prototype._clickFly = function(x,y) { - if ((x==10 || x==11) && y==9) core.ui.drawFly(core.status.event.data-1); - if ((x==10 || x==11) && y==5) core.ui.drawFly(core.status.event.data+1); - if ((x==10 || x==11) && y==10) core.ui.drawFly(core.status.event.data-10); - if ((x==10 || x==11) && y==4) core.ui.drawFly(core.status.event.data+10); - if (x>=5 && x<=7 && y==12) core.ui.closePanel(); - if (x>=0 && x<=9 && y>=3 && y<=11) - core.control.flyTo(core.status.hero.flyRange[core.status.event.data]); +actions.prototype._clickFly = function (x, y) { + if ((x == this.SIZE-2 || x == this.SIZE-3) && y == this.HSIZE+3) core.ui.drawFly(this._getNextFlyFloor(-1)); + if ((x == this.SIZE-2 || x == this.SIZE-3) && y == this.HSIZE-1) core.ui.drawFly(this._getNextFlyFloor(1)); + if ((x == this.SIZE-2 || x == this.SIZE-3) && y == this.HSIZE+4) core.ui.drawFly(this._getNextFlyFloor(-10)); + if ((x == this.SIZE-2 || x == this.SIZE-3) && y == this.HSIZE-2) core.ui.drawFly(this._getNextFlyFloor(10)); + if (x >= this.HSIZE-1 && x <= this.HSIZE+1 && y == this.LAST) core.ui.closePanel(); + if (x >= 0 && x <= this.HSIZE+3 && y >= 3 && y <= this.LAST - 1) + core.flyTo(core.floorIds[core.status.event.data]); return; } ////// 楼层传送器界面时,按下某个键的操作 ////// actions.prototype._keyDownFly = function (keycode) { - if (keycode==37) core.ui.drawFly(core.status.event.data-10); - else if (keycode==38) core.ui.drawFly(core.status.event.data+1); - else if (keycode==39) core.ui.drawFly(core.status.event.data+10); - else if (keycode==40) core.ui.drawFly(core.status.event.data-1); + if (keycode == 37) core.ui.drawFly(this._getNextFlyFloor(-10)); + else if (keycode == 38) core.ui.drawFly(this._getNextFlyFloor(1)); + else if (keycode == 39) core.ui.drawFly(this._getNextFlyFloor(10)); + else if (keycode == 40) core.ui.drawFly(this._getNextFlyFloor(-1)); return; } +actions.prototype._getNextFlyFloor = function (delta, index) { + if (index == null) index = core.status.event.data; + if (delta == 0) return index; + var sign = Math.sign(delta); + delta = Math.abs(delta); + var ans = index; + while (true) { + index += sign; + if (index < 0 || index >= core.floorIds.length) break; + var floorId = core.floorIds[index]; + if (core.status.maps[floorId].canFlyTo && core.hasVisitedFloor(floorId)) { + delta--; + ans = index; + } + if (delta == 0) break; + } + return ans; +} + ////// 楼层传送器界面时,放开某个键的操作 ////// actions.prototype._keyUpFly = function (keycode) { - if (keycode==71 || keycode==27 || keycode==88) + if (keycode == 71 || keycode == 27 || keycode == 88) core.ui.closePanel(); - if (keycode==13 || keycode==32 || keycode==67) - this._clickFly(5,5); + if (keycode == 13 || keycode == 32 || keycode == 67) + this._clickFly(this.HSIZE-1, this.HSIZE-1); return; } ////// 查看地图界面时的点击操作 ////// -actions.prototype._clickViewMaps = function (x,y) { - if (!core.isset(core.status.event.data)) { +actions.prototype._clickViewMaps = function (x, y) { + if (core.status.event.data == null) { core.ui.drawMaps(core.floorIds.indexOf(core.status.floorId)); return; } - var now = core.floorIds.indexOf(core.status.floorId); var index = core.status.event.data.index; var cx = core.status.event.data.x, cy = core.status.event.data.y; var floorId = core.floorIds[index], mw = core.floors[floorId].width, mh = core.floors[floorId].height; + var per = this.HSIZE - 4; - if (x==0 && y==0) { + if (x <= per - 2 && y <= per - 2) { core.status.event.data.damage = !core.status.event.data.damage; core.ui.drawMaps(index, cx, cy); return; } - if (x==0 && y==12) { + if (x <= per - 2 && y >= this.SIZE + 1 - per) { core.status.event.data.paint = !core.status.event.data.paint; core.ui.drawMaps(index, cx, cy); return; } - if (x==12 && y==0) { + if (x >= this.SIZE + 1 - per && y <= per - 2) { core.status.event.data.all = !core.status.event.data.all; core.ui.drawMaps(index, cx, cy); return; } - if (x>=2 && x<=10 && y<=1 && mh>13) { - core.ui.drawMaps(index, cx, cy-1); + if (x >= per && x <= this.LAST - per && y <= per - 1 && mh > this.SIZE) { + core.ui.drawMaps(index, cx, cy - 1); return; } - if (x>=2 && x<=10 && y>=11 && mh>13) { - core.ui.drawMaps(index, cx, cy+1); + if (x >= per && x <= this.LAST - per && y >= this.SIZE - per && mh > this.SIZE) { + core.ui.drawMaps(index, cx, cy + 1); return; } - if (x<=1 && y>=2 && y<=10) { - core.ui.drawMaps(index, cx-1, cy); + if (x <= per - 1 && y >= per && y <= this.LAST - per) { + core.ui.drawMaps(index, cx - 1, cy); return; } - if (x>=11 && y>=2 && y<=10) { - core.ui.drawMaps(index, cx+1, cy); + if (x >= this.SIZE - per && y >= per && y <= this.LAST - per) { + core.ui.drawMaps(index, cx + 1, cy); return; } - if(y<=4 && (mh==13 || (x>=2 && x<=10))) { + if (y <= this.HSIZE - 2 && (mh == this.SIZE || (x >= per && x <= this.LAST - per))) { index++; - while (index=8 && (mh==13 || (x>=2 && x<=10))) { + else 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) + while (index >= 0 && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) index--; - if (index>=0) + if (index >= 0) core.ui.drawMaps(index); } - else if (x>=2 && x<=10 && y>=5 && y<=7) { + else if (x >= per && x <= this.LAST - per && y >= this.HSIZE - 1 && y <= this.HSIZE + 1) { core.clearMap('data'); core.ui.closePanel(); } @@ -1085,47 +1143,47 @@ actions.prototype._clickViewMaps = function (x,y) { ////// 查看地图界面时,按下某个键的操作 ////// actions.prototype._keyDownViewMaps = function (keycode) { - if (!core.isset(core.status.event.data)) return; + if (core.status.event.data == null) return; var floorId = core.floorIds[core.status.event.data.index], mh = core.floors[floorId].height; - if (keycode==38||keycode==33) this._clickViewMaps(6, 3); - if (keycode==40||keycode==34) this._clickViewMaps(6, 9); - if (keycode==87 && mh>13) this._clickViewMaps(6,0); - if (keycode==65) this._clickViewMaps(0,6); - if (keycode==83 && mh>13) this._clickViewMaps(6,12); - if (keycode==68) this._clickViewMaps(12,6); + if (keycode == 38 || keycode == 33) this._clickViewMaps(this.HSIZE, this.HSIZE - 3); + if (keycode == 40 || keycode == 34) this._clickViewMaps(this.HSIZE, this.HSIZE + 3); + if (keycode == 87 && mh > this.SIZE) this._clickViewMaps(this.HSIZE, 0); + if (keycode == 65) this._clickViewMaps(0, this.HSIZE); + if (keycode == 83 && mh > this.SIZE) this._clickViewMaps(this.HSIZE, this.LAST); + if (keycode == 68) this._clickViewMaps(this.LAST, this.HSIZE); return; } ////// 查看地图界面时,放开某个键的操作 ////// actions.prototype._keyUpViewMaps = function (keycode) { - if (!core.isset(core.status.event.data)) { + if (core.status.event.data == null) { core.ui.drawMaps(core.floorIds.indexOf(core.status.floorId)); return; } - if (keycode==27 || keycode==13 || keycode==32 || (!core.isReplaying() && keycode==67)) { + if (keycode == 27 || keycode == 13 || keycode == 32 || (!core.isReplaying() && keycode == 67)) { core.clearMap('data'); core.ui.closePanel(); return; } - if (keycode==86) { + if (keycode == 86) { core.status.event.data.damage = !core.status.event.data.damage; core.ui.drawMaps(core.status.event.data); return; } - if (keycode==90) { + if (keycode == 90) { core.status.event.data.all = !core.status.event.data.all; core.ui.drawMaps(core.status.event.data); return; } - if (keycode==77) { + if (keycode == 77) { core.status.event.data.paint = !core.status.event.data.paint; core.ui.drawMaps(core.status.event.data); return; } - if (keycode==88 || (core.isReplaying() && keycode==67)) { + if (keycode == 88 || (core.isReplaying() && keycode == 67)) { if (core.isReplaying()) { core.bookReplay(); } else { @@ -1137,16 +1195,16 @@ actions.prototype._keyUpViewMaps = function (keycode) { } ////// 商店界面时的点击操作 ////// -actions.prototype._clickShop = function(x,y) { +actions.prototype._clickShop = function (x, y) { var shop = core.status.event.data.shop; var choices = shop.choices; - if (x >= 5 && x <= 7) { - var topIndex = 6 - parseInt(choices.length / 2); - if (y>=topIndex && y= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { + var topIndex = this.HSIZE - parseInt(choices.length / 2); + if (y >= topIndex && y < topIndex + choices.length) { + return core.events._useShop(shop, y - topIndex); } // 离开 - else if (y==topIndex+choices.length) { + else if (y == topIndex + choices.length) { core.events._exitShop(); } else return false; @@ -1156,111 +1214,115 @@ actions.prototype._clickShop = function(x,y) { ////// 商店界面时,放开某个键的操作 ////// actions.prototype._keyUpShop = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { core.events._exitShop(); return; } - this._selectChoices(core.status.event.data.shop.choices.length+1, keycode, this._clickShop); + this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); return; } ////// 快捷商店界面时的点击操作 ////// -actions.prototype._clickQuickShop = function(x, y) { - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) {return shopList[shopId].visited || !shopList[shopId].mustEnable}); - if (x >= 5 && x <= 7) { - var topIndex = 6 - parseInt(keys.length / 2); - if (y>=topIndex && y= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { + var topIndex = this.HSIZE - parseInt(keys.length / 2); + if (y >= topIndex && y < topIndex + keys.length) { var reason = core.events.canUseQuickShop(keys[y - topIndex]); - if (!core.flags.enableDisabledShop && core.isset(reason)) { + if (!core.flags.enableDisabledShop && reason) { core.drawText(reason); return; } core.events.openShop(keys[y - topIndex], true); - if (core.status.event.id=='shop') + if (core.status.event.id == 'shop') core.status.event.data.fromList = true; } // 离开 - else if (y==topIndex+keys.length) + else if (y == topIndex + keys.length) core.ui.closePanel(); } } ////// 快捷商店界面时,放开某个键的操作 ////// actions.prototype._keyUpQuickShop = function (keycode) { - if (keycode==27 || keycode==75 || keycode==88 || keycode==86) { + if (keycode == 27 || keycode == 75 || keycode == 88 || keycode == 86) { core.ui.closePanel(); return; } - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) {return shopList[shopId].visited || !shopList[shopId].mustEnable}); - this._selectChoices(keys.length+1, keycode, this._clickQuickShop); + var keys = Object.keys(core.status.shops).filter(function (shopId) { + return core.status.shops[shopId].visited || !core.status.shops[shopId].mustEnable + }); + this._selectChoices(keys.length + 1, keycode, this._clickQuickShop); return; } ////// 工具栏界面时的点击操作 ////// -actions.prototype._clickToolbox = function(x,y) { +actions.prototype._clickToolbox = function (x, y) { // 装备栏 - if (x>=10 && x<=12 && y==0) { + if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); core.openEquipbox(); return; } - // 返回 - if (x>=10 && x<=12 && y==12) { + if (x >= this.LAST - 2 && y == this.LAST) { core.ui.closePanel(); return; } + var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; // 上一页 - if (x == 3 || x == 4) { - if (y == 7 && toolsPage>1) { + if (x == this.HSIZE-2 || x == this.HSIZE-3) { + if (y == this.LAST - 5 && toolsPage > 1) { core.status.event.data.toolsPage--; core.ui.drawToolbox(core.status.event.selection); } - if (y == 12 && constantsPage>1) { - core.status.event.data.toolsPage--; + if (y == this.LAST && constantsPage > 1) { + core.status.event.data.constantsPage--; core.ui.drawToolbox(core.status.event.selection); } } // 下一页 - if (x == 8 || x == 9) { - if (y == 7 && toolsPage=0) + var index = parseInt(x / 2); + if (y == this.LAST - 8) index += 0; + else if (y == this.LAST - 6) index += this.HSIZE; + else if (y == this.LAST - 3) index += this.LAST; + else if (y == this.LAST - 1) index += this.LAST + this.HSIZE; + else index = -1; + if (index >= 0) this._clickToolboxIndex(index); } ////// 选择工具栏界面中某个Index后的操作 ////// -actions.prototype._clickToolboxIndex = function(index) { +actions.prototype._clickToolboxIndex = function (index) { var items = null; var select; - if (index<12) { - select = index + 12 * (core.status.event.data.toolsPage-1); + if (index < this.LAST) { + select = index + this.LAST * (core.status.event.data.toolsPage - 1); items = Object.keys(core.status.hero.items.tools).sort(); } else { - select = index%12 + 12 * (core.status.event.data.constantsPage-1); + select = index % this.LAST + this.LAST * (core.status.event.data.constantsPage - 1); items = Object.keys(core.status.hero.items.constants).sort(); } - if (items==null) return; - if (select>=items.length) return; - var itemId=items[select]; - if (itemId==core.status.event.data.selectId) { + if (items == null) return; + if (select >= items.length) return; + var itemId = items[select]; + if (itemId == core.status.event.data.selectId) { core.events.tryUseItem(itemId); } else { @@ -1270,86 +1332,89 @@ actions.prototype._clickToolboxIndex = function(index) { ////// 工具栏界面时,按下某个键的操作 ////// actions.prototype._keyDownToolbox = function (keycode) { - if (!core.isset(core.status.event.data)) return; + if (core.status.event.data == null) return; + + var last_index = this.LAST - 1; var tools = Object.keys(core.status.hero.items.tools).sort(); var constants = Object.keys(core.status.hero.items.constants).sort(); var index = core.status.event.selection; var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; - var toolsTotalPage = Math.ceil(tools.length/12); - var constantsTotalPage = Math.ceil(constants.length/12); - var toolsLastIndex = toolsPage 1) { core.status.event.data.toolsPage--; - index = 11; + index = last_index; } else return; // 第一页不向前翻 } - else if (index==12) { + else if (index == this.LAST) { if (constantsPage == 1) { - if (toolsTotalPage==0) return; + if (toolsTotalPage == 0) return; core.status.event.data.toolsPage = toolsTotalPage; - index = (tools.length+11)%12; + index = (tools.length + last_index) % this.LAST; } else { core.status.event.data.constantsPage--; - index = 23; + index = 2 * this.LAST - 1; } } - else index -= 1 ; + else index -= 1; this._clickToolboxIndex(index); return; } - if (keycode==38) { // up - if (index>=12&&index<=17) { // 进入tools - if (toolsTotalPage==0) return; - if (toolsLastIndex>=6) index = Math.min(toolsLastIndex, index-6); - else index = Math.min(toolsLastIndex, index-12); + if (keycode == 38) { // up + if (index >= this.LAST && index < this.LAST + this.HSIZE) { // 进入tools + if (toolsTotalPage == 0) return; + if (toolsLastIndex >= this.HSIZE) index = Math.min(toolsLastIndex, index - this.HSIZE); + else index = Math.min(toolsLastIndex, index - this.LAST); } - else if (index<6) return; // 第一行没有向上 - else index -= 6; + else if (index < this.HSIZE) return; // 第一行没有向上 + else index -= this.HSIZE; this._clickToolboxIndex(index); return; } - if (keycode==39) { // right - if (toolsPage 5) nextIndex = Math.min(toolsLastIndex, index + 6); - else index+=6; + if (index < this.HSIZE) { + if (toolsLastIndex >= this.HSIZE) nextIndex = Math.min(toolsLastIndex, index + this.HSIZE); + else index += this.HSIZE; } - if (nextIndex==null && index<=11) { + if (nextIndex == null && index < this.LAST) { if (constantsTotalPage == 0) return; - nextIndex = Math.min(index+6, constantsLastIndex); + nextIndex = Math.min(index + this.HSIZE, constantsLastIndex); } - if (nextIndex==null && index<=17) { - if (constantsLastIndex > 17) nextIndex = Math.min(constantsLastIndex, index+6); + if (nextIndex == null && index < this.LAST + this.HSIZE) { + if (constantsLastIndex >= this.LAST + this.HSIZE) + nextIndex = Math.min(constantsLastIndex, index + this.HSIZE); } - if (nextIndex!=null) { + if (nextIndex != null) { this._clickToolboxIndex(nextIndex); } return; @@ -1358,108 +1423,111 @@ actions.prototype._keyDownToolbox = function (keycode) { ////// 工具栏界面时,放开某个键的操作 ////// actions.prototype._keyUpToolbox = function (keycode) { - if (keycode==81){ + if (keycode == 81) { core.ui.closePanel(); core.openEquipbox(); return; } - if (keycode==84 || keycode==27 || keycode==88) { + if (keycode == 84 || keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } - if (!core.isset(core.status.event.data)) return; + if (core.status.event.data == null) return; - if (keycode==13 || keycode==32 || keycode==67) { + if (keycode == 13 || keycode == 32 || keycode == 67) { this._clickToolboxIndex(core.status.event.selection); return; } } ////// 装备栏界面时的点击操作 ////// -actions.prototype._clickEquipbox = function(x,y) { +actions.prototype._clickEquipbox = function (x, y) { // 道具栏 - if (x>=10 && x<=12 && y==0) { + if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); core.openToolbox(); return; } // 返回 - if (x>=10 && x<=12 && y==12) { + if (x >= this.LAST - 2 && y == this.LAST) { core.ui.closePanel(); return; } - // 当前页面 - var page = core.status.event.data.page; - // 上一页 - if ((x == 3 || x == 4) && y == 12) { - if (page>1) { + if ((x == this.HSIZE-2 || x == this.HSIZE-3) && y == this.LAST) { + if (core.status.event.data.page > 1) { core.status.event.data.page--; core.ui.drawEquipbox(core.status.event.selection); } return; } // 下一页 - if ((x == 8 || x == 9) && y == 12) { - var lastPage = Math.ceil(Object.keys(core.status.hero.items.equips).length/12); - if (page=0) { - if (index<12) index = parseInt(index/2); - this._clickEquipboxIndex(index); + var per_page = this.HSIZE - 3, v = this.SIZE / per_page; + if (y == this.LAST - 8) { + for (var i = 0; i < per_page; ++i) + if (x >= i * v && x <= (i + 1) * v) + return this._clickEquipboxIndex(i); } + else if (y == this.LAST - 6) { + for (var i = 0; i < per_page; ++i) + if (x >= i * v && x <= (i + 1) * v) + return this._clickEquipboxIndex(per_page + i); + } + else if (y == this.LAST - 3) + this._clickEquipboxIndex(this.LAST + parseInt(x / 2)) + else if (y == this.LAST - 1) + this._clickEquipboxIndex(this.LAST + this.HSIZE + parseInt(x / 2)); } ////// 选择装备栏界面中某个Index后的操作 ////// -actions.prototype._clickEquipboxIndex = function(index) { - if (index<6) { - if (index>=core.status.globalAttribute.equipName.length) return; - if (index==core.status.event.selection && core.isset(core.status.hero.equipment[index])) { +actions.prototype._clickEquipboxIndex = function (index) { + if (index < this.LAST) { + if (index >= core.status.globalAttribute.equipName.length) return; + if (index == core.status.event.selection && core.status.hero.equipment[index]) { core.unloadEquip(index); - core.status.route.push("unEquip:"+index); + core.status.route.push("unEquip:" + index); } } - else if (index>=12) { - var equips = Object.keys(core.status.hero.items.equips||{}).sort(); - if (index==core.status.event.selection) { - var equipId = equips[index-12 + (core.status.event.data.page-1)*12]; + else { + var equips = Object.keys(core.status.hero.items.equips || {}).sort(); + if (index == core.status.event.selection) { + var equipId = equips[index - this.LAST + (core.status.event.data.page - 1) * this.LAST]; core.loadEquip(equipId); - core.status.route.push("equip:"+equipId); + core.status.route.push("equip:" + equipId); } - } + } core.ui.drawEquipbox(index); } ////// 装备栏界面时,按下某个键的操作 ////// actions.prototype._keyDownEquipbox = function (keycode) { - if (!core.isset(core.status.event.data)) return; + if (core.status.event.data == null) return; + var last_index = this.LAST - 1; + var per_line = this.HSIZE - 3; var equipCapacity = core.status.globalAttribute.equipName.length; var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); var index = core.status.event.selection; var page = core.status.event.data.page; - var totalPage = Math.ceil(ownEquipment.length/12); - var totalLastIndex = 12+(page 1) { core.status.event.data.page--; - index = 23; + index = this.LAST + last_index; } else if (page == 1) index = equipCapacity - 1; @@ -1469,47 +1537,47 @@ actions.prototype._keyDownEquipbox = function (keycode) { this._clickEquipboxIndex(index); return; } - if (keycode==38) { // up - if (index<3) return; - else if (index<6) index -= 3; - else if (index < 18) { - index = parseInt((index-12)/2); - if (equipCapacity>3) index = Math.min(equipCapacity-1, index + 3); - else index = Math.min(equipCapacity-1, index); + if (keycode == 38) { // up + if (index < per_line) return; + else if (index < 2 * per_line) index -= per_line; + else if (index < this.LAST + this.HSIZE) { + index = parseInt((index - this.LAST) / 2); + if (equipCapacity > per_line) index = Math.min(equipCapacity - 1, index + per_line); + else index = Math.min(equipCapacity - 1, index); } - else index -= 6; + else index -= this.HSIZE; this._clickEquipboxIndex(index); return; } - if (keycode==39) { // right - if (page3) index = Math.min(index+3, equipCapacity-1); + if (keycode == 40) { // down + if (index < per_line) { + if (equipCapacity > per_line) index = Math.min(index + per_line, equipCapacity - 1); else { if (totalPage == 0) return; - index = Math.min(2*index+1+12, totalLastIndex); + index = Math.min(2 * index + 1 + this.LAST, totalLastIndex); } } - else if (index < 6) { + else if (index < 2 * per_line) { if (totalPage == 0) return; - index = Math.min(2*(index-3)+1+12, totalLastIndex); + index = Math.min(2 * (index - per_line) + 1 + this.LAST, totalLastIndex); } - else if (index < 18) - index = Math.min(index+6, totalLastIndex); + else if (index < this.LAST + this.HSIZE) + index = Math.min(index + this.HSIZE, totalLastIndex); else return; this._clickEquipboxIndex(index); return; @@ -1518,314 +1586,367 @@ actions.prototype._keyDownEquipbox = function (keycode) { ////// 装备栏界面时,放开某个键的操作 ////// actions.prototype._keyUpEquipbox = function (keycode, altKey) { - if (altKey && keycode>=48 && keycode<=57) { - core.items.quickSaveEquip(keycode-48); + if (altKey && keycode >= 48 && keycode <= 57) { + core.items.quickSaveEquip(keycode - 48); return; } - if (keycode==84){ + if (keycode == 84) { core.ui.closePanel(); core.openToolbox(); return; } - if (keycode==81 || keycode==27 || keycode==88) { + if (keycode == 81 || keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } - if (!core.isset(core.status.event.data.selectId)) return; + if (!core.status.event.data.selectId) return; - if (keycode==13 || keycode==32 || keycode==67) { + if (keycode == 13 || keycode == 32 || keycode == 67) { this._clickEquipboxIndex(core.status.event.selection); return; } } ////// 存读档界面时的点击操作 ////// -actions.prototype._clickSL = function(x,y) { - - var index=core.status.event.data; - var page = parseInt(index/10), offset=index%10; +actions.prototype._clickSL = function (x, y) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; // 上一页 - if ((x == 3 || x == 4) && y == 12) { - core.ui.drawSLPanel(10*(page-1)+offset); + if ((x == this.HSIZE-2 || x == this.HSIZE-3) && y == this.LAST) { + core.ui.drawSLPanel(10 * (page - 1) + offset); return; } // 下一页 - if ((x == 8 || x == 9) && y == 12) { - core.ui.drawSLPanel(10*(page+1)+offset); + if ((x == this.HSIZE+2 || x == this.HSIZE+3) && y == this.LAST) { + core.ui.drawSLPanel(10 * (page + 1) + offset); return; } // 返回 - if (x>=10 && x<=12 && y==12) { - if (core.events.recoverEvents(core.status.event.interval)) { + if (x >= this.LAST-2 && y == this.LAST) { + if (core.events.recoverEvents(core.status.event.interval)) return; - } core.ui.closePanel(); - if (!core.isPlaying()) { + if (!core.isPlaying()) core.showStartAnimate(true); - } return; } // 删除 - if (x>=0 && x<=2 && y==12) { - - if (core.status.event.id=='save') { - core.status.event.selection=!core.status.event.selection; + if (x >= 0 && x <= 2 && y == this.LAST) { + if (core.status.event.id == 'save') { + core.status.event.selection = !core.status.event.selection; core.ui.drawSLPanel(index); } - else { - var index = parseInt(prompt("请输入读档编号"))||0; - if (index>0) { - core.doSL(index, core.status.event.id); + else { // 显示收藏 + core.status.event.data.mode = core.status.event.data.mode == 'all'?'fav':'all'; + if (core.status.event.data.mode == 'fav') + core.ui.drawSLPanel(1, true); + else { + page = parseInt((core.saves.saveIndex-1)/5); + offset = core.saves.saveIndex-5*page; + core.ui.drawSLPanel(10*page + offset, true); } } return; } + // 点存档名 + var xLeft = parseInt(this.SIZE/3), xRight = parseInt(this.SIZE*2/3); + var topY1 = 0, topY2 = this.HSIZE; + if(y >= topY1 && y <= topY1 + 1) { + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 1); + if (x >= xRight) return this._clickSL_favorite(page, 2); + } + if(y >= topY2 && y <= topY2 + 1) { + if (x < xLeft) return this._clickSL_favorite(page, 3); + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 4); + if (x >= xRight) return this._clickSL_favorite(page, 5); + } - var id=null; - if (y>=1 && y<=4) { - if (x>=1 && x<=3) id = "autoSave"; - if (x>=5 && x<=7) id = 5*page+1; - if (x>=9 && x<=11) id = 5*page+2; + var id = null; + if (y >= topY1 + 2 && y < this.HSIZE - 1) { + if (x < xLeft) id = "autoSave"; + if (x >= xLeft && x < xRight) id = 5 * page + 1; + if (x >= xRight) id = 5 * page + 2; } - if (y>=7 && y<=10) { - if (x>=1 && x<=3) id = 5*page+3; - if (x>=5 && x<=7) id = 5*page+4; - if (x>=9 && x<=11) id = 5*page+5; + if (y >= topY2 + 2 && y < this.SIZE - 1) { + if (x < xLeft) id = 5 * page + 3; + if (x >= xLeft && x < xRight) id = 5 * page + 4; + if (x >= xRight) id = 5 * page + 5; } - if (id!=null) { - if (core.status.event.selection) { - if (id == 'autoSave') { + if (id != null) { + if (core.status.event.selection) { + if (id == 'autoSave') core.drawTip("无法删除自动存档!"); - } else { - // core.removeLocalStorage("save"+id); - core.removeLocalForage("save"+id, function() { + core.removeSave(id, function () { core.ui.drawSLPanel(index, true); - }, function() { - core.drawTip("无法删除存档!"); - }) + }); } } else { + if(core.status.event.data.mode == 'fav' && id != 'autoSave') + id = core.saves.favorite[id - 1]; core.doSL(id, core.status.event.id); } } } +actions.prototype._clickSL_favorite = function (page, offset) { + if (offset == 0) return; + var index = 5 * page + offset; + if (core.status.event.data.mode == 'fav') { // 收藏模式下点击的下标直接对应favorite + index = core.saves.favorite[index - 1]; + core.myprompt("请输入想要显示的存档名(长度不超过5字符)", null, function (value) { + if(value && value.length <= 5){ + core.saves.favoriteName[index] = value; + core.control._updateFavoriteSaves(); + core.drawSLPanel(10 * page + offset); + } else if (value) { + alert("无效的输入!"); + } + }); + } else { + var v = core.saves.favorite.indexOf(index); + if (v >= 0) { // 已经处于收藏状态:取消收藏 + core.saves.favorite.splice(v, 1); + delete core.saves.favoriteName[index]; + } + else if (core.hasSave(index)) { // 存在存档则进行收藏 + core.saves.favorite.push(index); + core.saves.favorite = core.saves.favorite.sort(function (a,b) {return a-b;}); // 保证有序 + core.drawTip("收藏成功!"); + } + core.control._updateFavoriteSaves(); + core.ui.drawSLPanel(10 * page + offset); + } +} + ////// 存读档界面时,按下某个键的操作 ////// -actions.prototype._keyDownSL = function(keycode) { +actions.prototype._keyDownSL = function (keycode) { - var index=core.status.event.data; - var page = parseInt(index/10), offset=index%10; +// var index = core.status.event.data; + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page*10 + offset; - if (keycode==37) { // left - if (offset==0) { - core.ui.drawSLPanel(10*(page-1) + 5); + if (keycode == 37) { // left + if (offset == 0) { + core.ui.drawSLPanel(10 * (page - 1) + 5); } else { core.ui.drawSLPanel(index - 1); } return; } - if (keycode==38) { // up - if (offset<3) { - core.ui.drawSLPanel(10*(page-1) + offset + 3); + if (keycode == 38) { // up + if (offset < 3) { + core.ui.drawSLPanel(10 * (page - 1) + offset + 3); } else { core.ui.drawSLPanel(index - 3); } return; } - if (keycode==39) { // right - if (offset==5) { - core.ui.drawSLPanel(10*(page+1)+1); + if (keycode == 39) { // right + if (offset == 5) { + core.ui.drawSLPanel(10 * (page + 1) + 1); } else { core.ui.drawSLPanel(index + 1); } return; } - if (keycode==40) { // down - if (offset>=3) { - core.ui.drawSLPanel(10*(page+1) + offset - 3); + if (keycode == 40) { // down + if (offset >= 3) { + core.ui.drawSLPanel(10 * (page + 1) + offset - 3); } else { core.ui.drawSLPanel(index + 3); } return; } - if (keycode==33) { // PAGEUP - core.ui.drawSLPanel(10*(page-1) + offset); + if (keycode == 33) { // PAGEUP + core.ui.drawSLPanel(10 * (page - 1) + offset); return; } - if (keycode==34) { // PAGEDOWN - core.ui.drawSLPanel(10*(page+1) + offset); + if (keycode == 34) { // PAGEDOWN + core.ui.drawSLPanel(10 * (page + 1) + offset); return; } } ////// 存读档界面时,放开某个键的操作 ////// actions.prototype._keyUpSL = function (keycode) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; - var index=core.status.event.data; - var page = parseInt(index/10), offset=index%10; - - if (keycode==27 || keycode==88 || (core.status.event.id == 'save' && keycode==83) || (core.status.event.id == 'load' && keycode==68)) { - if (core.events.recoverEvents(core.status.event.interval)) { - return; - } - core.ui.closePanel(); - if (!core.isPlaying()) { - core.showStartAnimate(true); - } + if (keycode == 27 || keycode == 88 || (core.status.event.id == 'save' && keycode == 83) + || (core.status.event.id == 'load' && keycode == 68)) { + this._clickSL(this.LAST, this.LAST); return; } - if (keycode==13 || keycode==32 || keycode==67) { - if (offset==0) { + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (offset == 0) core.doSL("autoSave", core.status.event.id); - } else { - core.doSL(5*page+offset, core.status.event.id); + var id = 5 * page + offset; + if(core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.doSL(id, core.status.event.id); } return; } - if (keycode==69 && core.status.event.id == 'load') { // E - var index = parseInt(prompt("请输入读档编号"))||0; - if (index>0) { - core.doSL(index, core.status.event.id); - } + if (keycode == 69 && core.status.event.id != 'save') { // E 收藏切换 + this._clickSL(0, this.LAST); return; } - if (keycode==46) { - if (offset==0) { + if (keycode == 46) { + if (offset == 0) { core.drawTip("无法删除自动存档!"); } else { - // core.removeLocalStorage("save"+(5*page+offset)); - // core.ui.drawSLPanel(index); - core.removeLocalForage("save"+(5*page+offset), function() { + var id = 5 * page + offset; + if(core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.removeSave(id, function () { core.ui.drawSLPanel(index, true); - }, function() { - core.drawTip("无法删除存档!"); - }) + }); } } + if (keycode == 70 && core.status.event.data.mode == 'all') { // F + this._clickSL_favorite(page, offset); + } } + ////// 系统设置界面时的点击操作 ////// -actions.prototype._clickSwitchs = function (x,y) { - if (x<5 || x>7) return; +actions.prototype._clickSwitchs = function (x, y) { + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { case 0: - core.triggerBgm(); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_bgm(); case 1: - core.musicStatus.soundStatus = !core.musicStatus.soundStatus; - core.setLocalStorage('soundStatus', core.musicStatus.soundStatus); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_sound(); case 2: - core.flags.displayEnemyDamage=!core.flags.displayEnemyDamage; - core.updateDamage(); - core.setLocalStorage('enemyDamage', core.flags.displayEnemyDamage); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_displayEnemyDamage(); case 3: - core.flags.displayCritical=!core.flags.displayCritical; - core.updateDamage(); - core.setLocalStorage('critical', core.flags.displayCritical); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_displayCritical(); case 4: - core.flags.displayExtraDamage=!core.flags.displayExtraDamage; - core.updateDamage(); - core.setLocalStorage('extraDamage', core.flags.displayExtraDamage); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_displayExtraDamage(); case 5: - core.platform.useLocalForage=!core.platform.useLocalForage; - core.setLocalStorage('useLocalForage', core.platform.useLocalForage); - core.control.getSaveIndexes(function (indexes) { - core.saves.ids = indexes; - }); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_localForage(); case 6: - core.setFlag('clickMove', !core.getFlag('clickMove', true)); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_clickMove(); case 7: - core.platform.extendKeyboard = !core.platform.extendKeyboard; - core.setLocalStorage('extendKeyboard', core.platform.extendKeyboard); - core.updateStatusBar(); - core.ui.drawSwitchs(); - break; + return this._clickSwitchs_ExtendKeyboard(); case 8: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawSettings(); break; } } } +actions.prototype._clickSwitchs_bgm = function () { + core.triggerBgm(); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_sound = function () { + core.musicStatus.soundStatus = !core.musicStatus.soundStatus; + core.setLocalStorage('soundStatus', core.musicStatus.soundStatus); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_displayEnemyDamage = function () { + core.flags.displayEnemyDamage = !core.flags.displayEnemyDamage; + core.updateDamage(); + core.setLocalStorage('enemyDamage', core.flags.displayEnemyDamage); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_displayCritical = function () { + core.flags.displayCritical = !core.flags.displayCritical; + core.updateDamage(); + core.setLocalStorage('critical', core.flags.displayCritical); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_displayExtraDamage = function () { + core.flags.displayExtraDamage = !core.flags.displayExtraDamage; + core.updateDamage(); + core.setLocalStorage('extraDamage', core.flags.displayExtraDamage); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_localForage = function () { + core.platform.useLocalForage = !core.platform.useLocalForage; + core.setLocalStorage('useLocalForage', core.platform.useLocalForage); + core.control.getSaveIndexes(function (indexes) { + core.saves.ids = indexes; + }); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_clickMove = function () { + if (core.hasFlag('__noClickMove__')) core.removeFlag('__noClickMove__'); + else core.setFlag('__noClickMove__', true); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_ExtendKeyboard = function () { + core.platform.extendKeyboard = !core.platform.extendKeyboard; + core.setLocalStorage('extendKeyboard', core.platform.extendKeyboard); + core.updateStatusBar(); + core.ui.drawSwitchs(); +} + ////// 系统设置界面时,放开某个键的操作 ////// actions.prototype._keyUpSwitchs = function (keycode) { - if (keycode==27 || keycode==88) { - core.status.event.selection=0; + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; core.ui.drawSettings(); return; } this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs); } -////// 系统菜单栏界面时的点击事件 ////// -actions.prototype._clickSettings = function (x,y) { - if (x<5 || x>7) return; +////// 系统菜单栏界面时的点击操作 ////// +actions.prototype._clickSettings = function (x, y) { + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { case 0: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawSwitchs(); break; case 1: core.ui.drawKeyBoard(); break; case 2: - core.clearLastEvent(); + core.clearUI(); core.ui.drawMaps(); break; case 3: - core.clearLastEvent(); + core.clearUI(); core.ui.drawPaint(); break; case 4: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawSyncSave(); break; case 5: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawGameInfo(); break; case 6: - core.status.event.selection=1; - core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { - core.ui.closePanel(); - core.restart(); - }, function () { - core.status.event.selection=3; - core.ui.drawSettings(); - }); - break; + return core.confirmRestart(); case 7: core.ui.closePanel(); break; @@ -1836,7 +1957,7 @@ actions.prototype._clickSettings = function (x,y) { ////// 系统菜单栏界面时,放开某个键的操作 ////// actions.prototype._keyUpSettings = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } @@ -1844,91 +1965,37 @@ actions.prototype._keyUpSettings = function (keycode) { } ////// 同步存档界面时的点击操作 ////// -actions.prototype._clickSyncSave = function (x,y) { - if (x<5 || x>7) return; +actions.prototype._clickSyncSave = function (x, y) { + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { case 0: - // core.syncSave("save"); - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawSyncSelect(); break; case 1: core.syncLoad(); break; case 2: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawLocalSaveSelect(); break; case 3: - core.readFile(function (obj) { - if (obj.name!=core.firstData.name) { - alert("存档和游戏不一致!"); - return; - } - if (obj.version!=core.firstData.version) { - alert("游戏版本不一致!"); - return; - } - if (!core.isset(obj.data)) { - alert("无效的存档!"); - return; - } - var data=obj.data; - - if (data instanceof Array) { - core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { - for (var i=1;i<=5*(main.savePages||30);i++) { - if (i<=data.length) { - core.setLocalForage("save"+i, data[i-1]); - } - else { - if (core.saves.ids[i]) - core.removeLocalForage("save"+i); - } - } - core.ui.closePanel(); - core.drawText("读取成功!\n你的本地所有存档均已被覆盖。"); - }, function () { - core.status.event.selection=0; - core.ui.drawSyncSave(); - }) - } - else { - // core.setLocalStorage("save"+core.saves.saveIndex, data); - core.setLocalForage("save"+core.saves.saveIndex, data, function() { - core.drawText("同步成功!\n单存档已覆盖至存档"+core.saves.saveIndex); - }) - } - }, function () { - - }); - break; + return this._clickSyncSave_readFile(); case 4: - core.status.event.selection=0; - core.ui.drawReplay(); - break; + return this._clickSyncSave_replay(); case 5: - if (core.hasFlag('debug')) { - core.drawText("\t[系统提示]调试模式下无法下载录像"); - break; - } - core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify({ - 'name': core.firstData.name, - 'hard': core.status.hard, - 'seed': core.getFlag('__seed__'), - 'route': core.encodeRoute(core.status.route) - })); - break; + return this._clickSyncSave_download(); case 6: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawStorageRemove(); break; case 7: - core.status.event.selection=4; + core.status.event.selection = 4; core.ui.drawSettings(); break; @@ -1937,10 +2004,42 @@ actions.prototype._clickSyncSave = function (x,y) { return; } +actions.prototype._clickSyncSave_readFile = function () { + core.readFile(function (obj) { + if (obj.name != core.firstData.name) return alert("存档和游戏不一致!"); + if (obj.version != core.firstData.version) return alert("游戏版本不一致!"); + if (!obj.data) return alert("无效的存档!"); + core.control._syncLoad_write(obj.data); + }); +} + +actions.prototype._clickSyncSave_replay = function () { + if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法回放录像"); + } + else { + core.status.event.selection = 0; + core.ui.drawReplay(); + } +} + +actions.prototype._clickSyncSave_download = function () { + if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法下载录像"); + return; + } + core.download(core.firstData.name + "_" + core.formatDate2() + ".h5route", JSON.stringify({ + 'name': core.firstData.name, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + })); +} + ////// 同步存档界面时,放开某个键的操作 ////// actions.prototype._keyUpSyncSave = function (keycode) { - if (keycode==27 || keycode==88) { - core.status.event.selection=2; + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 2; core.ui.drawSettings(); return; } @@ -1949,12 +2048,13 @@ actions.prototype._keyUpSyncSave = function (keycode) { ////// 同步存档选择界面时的点击操作 ////// actions.prototype._clickSyncSelect = function (x, y) { - if (x<5 || x>7) return; + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { case 0: core.syncSave('all'); @@ -1963,7 +2063,7 @@ actions.prototype._clickSyncSelect = function (x, y) { core.syncSave(); break; case 2: - core.status.event.selection=0; + core.status.event.selection = 0; core.ui.drawSyncSave(); break; } @@ -1972,8 +2072,8 @@ actions.prototype._clickSyncSelect = function (x, y) { ////// 同步存档选择界面时,放开某个键的操作 ////// actions.prototype._keyUpSyncSelect = function (keycode) { - if (keycode==27 || keycode==88) { - core.status.event.selection=0; + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; core.ui.drawSettings(); return; } @@ -1981,36 +2081,39 @@ actions.prototype._keyUpSyncSelect = function (keycode) { } ////// 存档下载界面时的点击操作 ////// -actions.prototype._clickLocalSaveSelect = function (x,y) { - if (x<5 || x>7) return; +actions.prototype._clickLocalSaveSelect = function (x, y) { + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { var selection = y - topIndex; - if (selection<2) { - core.control.getSaves(selection==0?null:core.saves.saveIndex, function(saves) { - if (core.isset(saves)) { + core.status.event.selection = selection; + if (selection < 2) { + var callback = function (saves) { + if (saves) { var content = { "name": core.firstData.name, "version": core.firstData.version, "data": saves } - core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5save", JSON.stringify(content)); + 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; - core.ui.drawSyncSave(); + core.status.event.selection = 2; + core.ui.drawSyncSave(); + } } ////// 存档下载界面时,放开某个键的操作 ////// actions.prototype._keyUpLocalSaveSelect = function (keycode) { - if (keycode==27 || keycode==88) { - core.status.event.selection=0; + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; core.ui.drawSettings(); return; } @@ -2019,72 +2122,87 @@ actions.prototype._keyUpLocalSaveSelect = function (keycode) { ////// 存档删除界面时的点击操作 ////// actions.prototype._clickStorageRemove = function (x, y) { - if (x<5 || x>7) return; + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { case 0: - if (core.platform.useLocalForage) { - core.ui.drawWaiting("正在清空,请稍后..."); - localforage.clear(function () { - core.ui.closePanel(); - core.drawText("\t[操作成功]你的所有存档已被清空。"); - core.saves.saveIndex = 1; - core.removeLocalStorage('saveIndex'); - }); - } - else { - localStorage.clear(); - core.drawText("\t[操作成功]你的所有存档已被清空。"); - core.saves.saveIndex = 1; - core.removeLocalStorage('saveIndex'); - } - break; + return this._clickStorageRemove_all(); case 1: - if (core.platform.useLocalForage) { - core.ui.drawWaiting("正在清空,请稍后..."); - Object.keys(core.saves.ids).forEach(function (v) { - if (v!=0) - core.removeLocalForage("save"+v); - }); - core.removeLocalForage("autoSave", function() { - core.saves.autosave.data = null; - core.saves.autosave.updated = false; - core.ui.closePanel(); - core.drawText("\t[操作成功]当前塔的存档已被清空。"); - core.saves.saveIndex = 1; - core.removeLocalStorage('saveIndex'); - }); - } - else { - Object.keys(core.saves.ids).forEach(function (v) { - if (v!=0) - core.removeLocalStorage("save"+v); - }); - core.removeLocalStorage("autoSave"); - core.saves.autosave.data = null; - core.saves.autosave.updated = false; - core.drawText("\t[操作成功]当前塔的存档已被清空。"); - core.saves.saveIndex = 1; - core.removeLocalStorage('saveIndex'); - } - break; + return this._clickStorageRemove_current(); case 2: - core.status.event.selection=6; + core.status.event.selection = 6; core.ui.drawSyncSave(); break; } } } +actions.prototype._clickStorageRemove_all = function () { + core.myconfirm("你确定要清除【全部塔】的所有本地存档?\n此行为不可逆!!!", function () { + var done = function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]你的所有存档已被清空。"); + }; + if (core.platform.useLocalForage) { + core.ui.drawWaiting("正在清空,请稍后..."); + localforage.clear(done); + } + else { + localStorage.clear(); + done(); + } + }); +} + +actions.prototype._clickStorageRemove_current = function () { + core.myconfirm("你确定要清除本塔的所有本地存档?\n此行为不可逆!!!", function () { + var done = function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]当前塔的存档已被清空。"); + } + if (core.platform.useLocalForage) { + core.ui.drawWaiting("正在清空,请稍后..."); + Object.keys(core.saves.ids).forEach(function (v) { + core.removeLocalForage("save" + v); + }); + core.removeLocalForage("autoSave", done); + } + else { + Object.keys(core.saves.ids).forEach(function (v) { + core.removeLocalStorage("save" + v); + }); + core.removeLocalStorage("autoSave"); + done(); + } + }); +} + ////// 存档删除界面时,放开某个键的操作 ////// actions.prototype._keyUpStorageRemove = function (keycode) { - if (keycode==27 || keycode==88) { - core.status.event.selection=5; + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 5; core.ui.drawSyncSave(); return; } @@ -2093,56 +2211,52 @@ actions.prototype._keyUpStorageRemove = function (keycode) { ////// 回放选择界面时的点击操作 ////// actions.prototype._clickReplay = function (x, y) { - if (x<5 || x>7) return; + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { - case 0: - { - core.ui.closePanel(); - core.startGame(core.status.hard, core.getFlag('__seed__'), core.clone(core.status.route)); - break; - } - case 1: - { - core.status.event.id = 'replayLoad'; - core.status.event.selection = null; - core.ui.clearLastEvent(); - var saveIndex = core.saves.saveIndex; - var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; - core.ui.drawSLPanel(10*page+offset); - break; - } - case 2: - core.chooseReplayFile(); - break; - case 3: - if (core.hasFlag('debug')) { - core.drawText("\t[系统提示]调试模式下无法下载录像"); - break; - } - core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify({ - 'name': core.firstData.name, - 'hard': core.status.hard, - 'seed': core.getFlag('__seed__'), - 'route': core.encodeRoute(core.status.route) - })); - break; - break; - case 4: - core.ui.closePanel(); - break; + case 0: return this._clickReplay_fromBeginning(); + case 1: return this._clickReplay_fromLoad(); + case 2: return core.chooseReplayFile(); + case 3: return this._clickReplay_download(); + case 4: return core.ui.closePanel(); } } } +actions.prototype._clickReplay_fromBeginning = function () { + core.ui.closePanel(); + core.startGame(core.status.hard, core.getFlag('__seed__'), core.clone(core.status.route)); +} + +actions.prototype._clickReplay_fromLoad = function () { + core.status.event.id = 'replayLoad'; + core.status.event.selection = null; + core.clearUI(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui.drawSLPanel(10 * page + offset); +} + +actions.prototype._clickReplay_download = function () { + if (core.hasFlag('debug')) return core.drawText("\t[系统提示]调试模式下无法下载录像"); + core.download(core.firstData.name + "_" + core.formatDate2() + ".h5route", JSON.stringify({ + 'name': core.firstData.name, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + })); + +} + ////// 回放选择界面时,放开某个键的操作 ////// actions.prototype._keyUpReplay = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } @@ -2151,57 +2265,60 @@ actions.prototype._keyUpReplay = function (keycode) { ////// 游戏信息界面时的点击操作 ////// actions.prototype._clickGameInfo = function (x, y) { - if (x<5 || x>7) return; + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; var choices = core.status.event.ui.choices; - var topIndex = 6 - parseInt((choices.length - 1) / 2); + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2); - if (y>=topIndex && y= topIndex && y < topIndex + choices.length) { var selection = y - topIndex; + core.status.event.selection = selection; switch (selection) { - case 0: - core.ui.drawStatistics(); - break; - case 1: - if (core.platform.isPC) - window.open("editor.html", "_blank"); - else if (confirm("即将离开本塔,跳转至本塔工程页面,确认?")) { - window.location.href = "editor-mobile.html"; - } - break; - case 2: - if (core.platform.isPC) { - window.open("/score.php?name="+core.firstData.name+"&num=10", "_blank"); - } - else { - if (confirm("即将离开本塔,跳转至本塔评论页面,确认?")) { - window.location.href = "/score.php?name="+core.firstData.name+"&num=10"; - } - } - break; - case 3: - core.ui.drawHelp(); - break; - case 4: - core.ui.drawAbout(); - break; - case 5: - if (core.platform.isPC) - window.open(core.firstData.name+".zip"); - else - window.location.href = core.firstData.name+".zip"; - break; + case 0: return core.ui.drawStatistics(); + case 1: return this._clickGameInfo_openProject(); + case 2: return this._clickGameInfo_openComments(); + case 3: return core.ui.drawHelp(); + case 4: return core.ui.drawAbout(); + case 5: return this._clickGameInfo_download(); case 6: - core.status.event.selection=5; + core.status.event.selection = 5; core.ui.drawSettings(); break; } } } +actions.prototype._clickGameInfo_openProject = function () { + if (core.platform.isPC) + window.open("editor.html", "_blank"); + else { + core.myconfirm("即将离开本塔,跳转至本塔工程页面,确认?", function () { + window.location.href = "editor-mobile.html"; + }); + } +} + +actions.prototype._clickGameInfo_openComments = function () { + if (core.platform.isPC) { + window.open("/score.php?name=" + core.firstData.name + "&num=10", "_blank"); + } + else { + core.myconfirm("即将离开本塔,跳转至本塔评论页面,确认?", function () { + window.location.href = "/score.php?name=" + core.firstData.name + "&num=10"; + }); + } +} + +actions.prototype._clickGameInfo_download = function () { + if (core.platform.isPC) + window.open(core.firstData.name + ".zip"); + else + window.location.href = core.firstData.name + ".zip"; +} + ////// 游戏信息界面时,放开某个键的操作 ////// actions.prototype._keyUpGameInfo = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } @@ -2210,102 +2327,104 @@ actions.prototype._keyUpGameInfo = function (keycode) { ////// “虚拟键盘”界面时的点击操作 ////// actions.prototype._clickKeyBoard = function (x, y) { - if (y==3 && x>=1 && x<=11) { + var m = this.HSIZE; + if (y == m - 3 && x >= m - 5 && x <= m + 5) { core.ui.closePanel(); - core.keyUp(112+x-1); // F1-F12: 112-122 + core.keyUp(112 + x + 5 - m); } - if (y==3 && x==12) { + if (y == m - 3 && x == m + 6) { var val = prompt(); - if (val!=null) { + if (val != null) { try { eval(val); } - catch (e) {} + catch (e) { + } } } - if (y==4 && x>=1 && x<=10) { + if (y == m - 2 && x >= m - 5 && x <= m + 4) { core.ui.closePanel(); - core.keyUp(x==10?48:48+x); // 1-9: 49-57; 0: 48 + core.keyUp(x == m + 4 ? 48 : 49 + x + 5 - m); // 1-9: 49-57; 0: 48 } // 字母 var lines = [ - ["Q","W","E","R","T","Y","U","I","O","P"], - ["A","S","D","F","G","H","J","K","L"], - ["Z","X","C","V","B","N","M"], + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M"], ]; - if (y==5 && x>=1 && x<=10) { + if (y == m - 1 && x >= m - 5 && x <= m + 4) { core.ui.closePanel(); - core.keyUp(lines[0][x-1].charCodeAt(0)); + core.keyUp(lines[0][x + 5 - m].charCodeAt(0)); } - if (y==6 && x>=1 && x<=9) { + if (y == m && x >= m - 5 && x <= m + 3) { core.ui.closePanel(); - core.keyUp(lines[1][x-1].charCodeAt(0)); + core.keyUp(lines[1][x + 5 - m].charCodeAt(0)); } - if (y==7 && x>=1 && x<=7) { + if (y == m + 1 && x >= m - 5 && x <= m + 1) { core.ui.closePanel(); - core.keyUp(lines[2][x-1].charCodeAt(0)); + core.keyUp(lines[2][x + 5 - m].charCodeAt(0)); } - if (y==8 && x>=1 && x<=11) { + if (y == m + 2 && x >= m - 5 && x <= m + 5) { core.ui.closePanel(); - if (x==1) core.keyUp(189); // - - if (x==2) core.keyUp(187); // = - if (x==3) core.keyUp(219); // [ - if (x==4) core.keyUp(221); // ] - if (x==5) core.keyUp(220); // \ - if (x==6) core.keyUp(186); // ; - if (x==7) core.keyUp(222); // ' - if (x==8) core.keyUp(188); // , - if (x==9) core.keyUp(190); // . - if (x==10) core.keyUp(191); // / - if (x==11) core.keyUp(192); // ` + if (x == m - 5) core.keyUp(189); // - + if (x == m - 4) core.keyUp(187); // = + if (x == m - 3) core.keyUp(219); // [ + if (x == m - 2) core.keyUp(221); // ] + if (x == m - 1) core.keyUp(220); // \ + if (x == m) core.keyUp(186); // ; + if (x == m + 1) core.keyUp(222); // ' + if (x == m + 2) core.keyUp(188); // , + if (x == m + 3) core.keyUp(190); // . + if (x == m + 4) core.keyUp(191); // / + if (x == m + 5) core.keyUp(192); // ` } - if (y==9 && x>=1 && x<=10) { + if (y == m + 3 && x >= m - 5 && x <= m + 4) { core.ui.closePanel(); - if (x==1) core.keyUp(27); // ESC - if (x==2) core.keyUp(9); // TAB - if (x==3) core.keyUp(20); // CAPS - if (x==4) core.keyUp(16); // SHIFT - if (x==5) core.keyUp(17); // CTRL - if (x==6) core.keyUp(18); // ALT - if (x==7) core.keyUp(32); // SPACE - if (x==8) core.keyUp(8); // BACKSPACE - if (x==9) core.keyUp(13); // ENTER - if (x==10) core.keyUp(46); // DEL + if (x == m - 5) core.keyUp(27); // ESC + if (x == m - 4) core.keyUp(9); // TAB + if (x == m - 3) core.keyUp(20); // CAPS + if (x == m - 2) core.keyUp(16); // SHIFT + if (x == m - 1) core.keyUp(17); // CTRL + if (x == m) core.keyUp(18); // ALT + if (x == m + 1) core.keyUp(32); // SPACE + if (x == m + 2) core.keyUp(8); // BACKSPACE + if (x == m + 3) core.keyUp(13); // ENTER + if (x == m + 4) core.keyUp(46); // DEL } - if (y==10 && x>=9 && x<=11) + if (y == m + 4 && x >= m + 3 && x <= m + 5) core.ui.closePanel(); } ////// 光标界面时的点击操作 ////// -actions.prototype._clickCursor = function (x,y) { - if (x==core.status.automaticRoute.cursorX && y==core.status.automaticRoute.cursorY) { +actions.prototype._clickCursor = function (x, y) { + if (x == core.status.automaticRoute.cursorX && y == core.status.automaticRoute.cursorY) { core.ui.closePanel(); - core.onclick(x,y,[]); + core.onclick(x, y, []); return; } - core.status.automaticRoute.cursorX=x; - core.status.automaticRoute.cursorY=y; + core.status.automaticRoute.cursorX = x; + core.status.automaticRoute.cursorY = y; core.ui.drawCursor(); } ////// 光标界面时,按下某个键的操作 ////// actions.prototype._keyDownCursor = function (keycode) { - if (keycode==37) { // left + if (keycode == 37) { // left core.status.automaticRoute.cursorX--; core.ui.drawCursor(); return; } - if (keycode==38) { // up + if (keycode == 38) { // up core.status.automaticRoute.cursorY--; core.ui.drawCursor(); return; } - if (keycode==39) { // right + if (keycode == 39) { // right core.status.automaticRoute.cursorX++; core.ui.drawCursor(); return; } - if (keycode==40) { // down + if (keycode == 40) { // down core.status.automaticRoute.cursorY++; core.ui.drawCursor(); return; @@ -2314,11 +2433,11 @@ actions.prototype._keyDownCursor = function (keycode) { ////// 光标界面时,放开某个键的操作 ////// actions.prototype._keyUpCursor = function (keycode) { - if (keycode==27 || keycode==88) { + if (keycode == 27 || keycode == 88) { core.ui.closePanel(); return; } - if (keycode==13 || keycode==32 || keycode==67 || keycode==69) { + if (keycode == 13 || keycode == 32 || keycode == 67 || keycode == 69) { core.ui.closePanel(); core.onclick(core.status.automaticRoute.cursorX, core.status.automaticRoute.cursorY, []); return; @@ -2327,9 +2446,9 @@ actions.prototype._keyUpCursor = function (keycode) { ////// 绘图相关 ////// -actions.prototype.ondownPaint = function (x, y) { - x+=core.bigmap.offsetX; - y+=core.bigmap.offsetY; +actions.prototype._ondownPaint = function (x, y) { + x += core.bigmap.offsetX; + y += core.bigmap.offsetY; if (!core.status.event.data.erase) { core.dymCanvas.paint.beginPath(); core.dymCanvas.paint.moveTo(x, y); @@ -2338,26 +2457,26 @@ actions.prototype.ondownPaint = function (x, y) { core.status.event.data.y = y; } -actions.prototype.onmovePaint = function (x, y) { - if (core.status.event.data.x==null) return; - x+=core.bigmap.offsetX; - y+=core.bigmap.offsetY; +actions.prototype._onmovePaint = function (x, y) { + if (core.status.event.data.x == null) return; + x += core.bigmap.offsetX; + y += core.bigmap.offsetY; if (core.status.event.data.erase) { - core.clearMap('paint', x-10, y-10, 20, 20); + core.clearMap('paint', x - 10, y - 10, 20, 20); return; } - var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; + var midx = (core.status.event.data.x + x) / 2, midy = (core.status.event.data.y + y) / 2; core.dymCanvas.paint.quadraticCurveTo(midx, midy, x, y); core.dymCanvas.paint.stroke(); core.status.event.data.x = x; core.status.event.data.y = y; } -actions.prototype.onupPaint = function () { +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) { @@ -2365,7 +2484,7 @@ actions.prototype.setPaintMode = function (mode) { else if (mode == 'erase') core.status.event.data.erase = true; else return; - core.drawTip("进入"+(core.status.event.data.erase?"擦除":"绘图")+"模式"); + core.drawTip("进入" + (core.status.event.data.erase ? "擦除" : "绘图") + "模式"); } actions.prototype.clearPaint = function () { @@ -2377,10 +2496,10 @@ actions.prototype.clearPaint = function () { actions.prototype.savePaint = function () { var data = {}; for (var floorId in core.paint) { - if (core.isset(core.paint[floorId])) + if (core.paint[floorId]) data[floorId] = lzw_decode(core.paint[floorId]); } - core.download(core.firstData.name+".h5paint", JSON.stringify({ + core.download(core.firstData.name + ".h5paint", JSON.stringify({ 'name': core.firstData.name, 'paint': data })); @@ -2388,24 +2507,24 @@ actions.prototype.savePaint = function () { actions.prototype.loadPaint = function () { core.readFile(function (obj) { - if (obj.name!=core.firstData.name) { + if (obj.name != core.firstData.name) { alert("绘图文件和游戏不一致!"); return; } - if (!core.isset(obj.paint)) { + if (!obj.paint) { alert("无效的绘图文件!"); return; } core.paint = {}; for (var floorId in obj.paint) { - if (core.isset(obj.paint[floorId])) + if (obj.paint[floorId]) core.paint[floorId] = lzw_encode(obj.paint[floorId]); } core.clearMap('paint'); 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); + if (value) value = lzw_decode(value).split(","); + core.utils._decodeCanvas(value, 32 * core.bigmap.width, 32 * core.bigmap.height); core.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0); core.drawTip("读取绘图文件成功"); @@ -2422,7 +2541,7 @@ actions.prototype.exitPaint = function () { } actions.prototype._keyUpPaint = function (keycode) { - if (keycode==27 || keycode==88 || keycode==77 || keycode==13 || keycode==32 || keycode==67) { + if (keycode == 27 || keycode == 88 || keycode == 77 || keycode == 13 || keycode == 32 || keycode == 67) { this.exitPaint(); return; } diff --git a/libs/control.js b/libs/control.js index a9296e4a..f0de8ee1 100644 --- a/libs/control.js +++ b/libs/control.js @@ -12,11 +12,78 @@ function control() { control.prototype._init = function () { this.controldata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.control; + this.renderFrameFuncs = []; + this.replayActions = []; + this.resizes = []; + // --- 注册系统的animationFrame + this.registerAnimationFrame("totalTime", false, this._animationFrame_totalTime); + this.registerAnimationFrame("autoSave", true, this._animationFrame_autoSave); + this.registerAnimationFrame("globalAnimate", true, this._animationFrame_globalAnimate); + this.registerAnimationFrame("selector", false, this._animationFrame_selector); + this.registerAnimationFrame("animate", true, this._animationFrame_animate); + this.registerAnimationFrame("heroMoving", true, this._animationFrame_heroMoving); + this.registerAnimationFrame("weather", true, this._animationFrame_weather); + this.registerAnimationFrame("parallelDo", false, this._animationFrame_parallelDo); + this.registerAnimationFrame("checkConsoleOpened", true, this._animationFrame_checkConsoleOpened); + // --- 注册系统的replay + this.registerReplayAction("move", this._replayAction_move); + this.registerReplayAction("item", this._replayAction_item); + this.registerReplayAction("equip", this._replayAction_equip); + this.registerReplayAction("unEquip", this._replayAction_unEquip); + this.registerReplayAction("fly", this._replayAction_fly); + this.registerReplayAction("shop", this._replayAction_shop); + this.registerReplayAction("turn", this._replayAction_turn); + this.registerReplayAction("getNext", this._replayAction_getNext); + this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly); + this.registerReplayAction("key", this._replayAction_key); + // --- 注册系统的resize + this.registerResize("gameGroup", this._resize_gameGroup); + this.registerResize("canvas", this._resize_canvas); + this.registerResize("statusBar", this._resize_statusBar); + this.registerResize("status", this._resize_status); + this.registerResize("toolBar", this._resize_toolBar); + this.registerResize("tools", this._resize_tools); +} + +// ------ requestAnimationFrame 相关 ------ // + +////// 注册一个 animationFrame ////// +// name:名称,可用来作为注销使用;needPlaying:是否只在游戏运行时才执行(在标题界面不执行) +// func:要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数 +control.prototype.registerAnimationFrame = function (name, needPlaying, func) { + this.unregisterAnimationFrame(name); + this.renderFrameFuncs.push({name: name, needPlaying: needPlaying, func: func}); +} + +////// 注销一个 animationFrame ////// +control.prototype.unregisterAnimationFrame = function (name) { + this.renderFrameFuncs = this.renderFrameFuncs.filter(function (x) { return x.name!=name; }); } ////// 设置requestAnimationFrame ////// -control.prototype.setRequestAnimationFrame = function () { +control.prototype._setRequestAnimationFrame = function () { + this._checkRequestAnimationFrame(); + core.animateFrame.totalTime = Math.max(core.animateFrame.totalTime, core.getLocalStorage('totalTime', 0)); + var loop = function (timestamp) { + core.control.renderFrameFuncs.forEach(function (b) { + if (b.func) { + try { + if (core.isPlaying() || !b.needPlaying) + core.doFunc(b.func, core.control, timestamp); + } + catch (e) { + main.log(e); + main.log("ERROR in requestAnimationFrame["+b.name+"]:已自动注销该项。"); + core.unregisterAnimationFrame(b.name); + } + } + }) + window.requestAnimationFrame(loop); + } + window.requestAnimationFrame(loop); +} +control.prototype._checkRequestAnimationFrame = function () { (function() { var lastTime = 0; var vendors = ['webkit', 'moz']; @@ -43,270 +110,210 @@ control.prototype.setRequestAnimationFrame = function () { }; } }()); - - core.animateFrame.totalTime = Math.max(core.animateFrame.totalTime, core.getLocalStorage('totalTime', 0)); - - var draw = function(timestamp) { - - core.animateFrame.totalTime += timestamp - core.animateFrame.totalTimeStart; - core.animateFrame.totalTimeStart = timestamp; - - // move time - if (core.isPlaying() && core.isset(core.status) && core.isset(core.status.hero) - && core.isset(core.status.hero.statistics)) { - core.status.hero.statistics.totalTime = core.animateFrame.totalTime; - core.status.hero.statistics.currTime += timestamp-(core.status.hero.statistics.start||timestamp); - core.status.hero.statistics.start=timestamp; - } - - // Global Animate - if (timestamp - core.animateFrame.globalTime > core.values.animateSpeed && core.isPlaying()) { - - core.status.globalAnimateStatus++; - - if (core.animateFrame.globalAnimate && core.isset(core.status.floorId)) { - // Global Animate - core.status.globalAnimateObjs.forEach(function (block) { - core.drawBlock(block, core.status.globalAnimateStatus % (block.event.animate||1)); - }); - - // Global floor images - core.maps.drawFloorImages(core.status.floorId, core.canvas.bg, 'bg', core.status.floorAnimateObjs||[], core.status.globalAnimateStatus); - core.maps.drawFloorImages(core.status.floorId, core.canvas.fg, 'fg', core.status.floorAnimateObjs||[], core.status.globalAnimateStatus); - - // Global Autotile Animate - core.status.autotileAnimateObjs.blocks.forEach(function (block) { - // ------ 界面外的动画不绘制 - if (block.x * 32 < core.bigmap.offsetX - 64 || block.x * 32 > core.bigmap.offsetX + 416 + 32 - || block.y * 32 < core.bigmap.offsetY - 64 || block.y * 32 > core.bigmap.offsetY + 416 + 32 + 16) { - return; - } - - var cv = core.isset(block.name)?core.canvas[block.name]:core.canvas.event; - cv.clearRect(block.x * 32, block.y * 32, 32, 32); - if (core.isset(block.name)) { - if (block.name == 'bg') { - core.drawImage('bg', core.material.groundCanvas.canvas, block.x * 32, block.y * 32); - } - core.drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, core.status.globalAnimateStatus); - } - else { - core.drawAutotile(cv, core.status.autotileAnimateObjs.map, block, 32, 0, 0, core.status.globalAnimateStatus); - } - }); - } - - // Box animate - core.drawBoxAnimate(); - core.animateFrame.globalTime = timestamp; - } - - // AutosaveTime - if (timestamp - core.saves.autosave.time > 5000 && core.isPlaying()) { - core.control.checkAutosave(); - core.saves.autosave.time = timestamp; - } - - // selectorTime - if (timestamp-core.animateFrame.selectorTime>20 && core.isset(core.dymCanvas.selector)) { - var opacity = parseFloat(core.dymCanvas.selector.canvas.style.opacity); - if (core.animateFrame.selectorUp) - opacity += 0.02; - else - opacity -= 0.02; - if (opacity > 0.95 || opacity < 0.55) - core.animateFrame.selectorUp = !core.animateFrame.selectorUp; - core.setOpacity("selector", opacity); - core.animateFrame.selectorTime = timestamp; - } - - // Animate - if (timestamp-core.animateFrame.animateTime>50 && core.isset(core.status.animateObjs) && core.status.animateObjs.length>0) { - core.clearMap('animate'); - // 更新帧 - var animateObjs = []; - for (var i=0;i0) { - var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); - - // 200ms换腿? - if (timestamp - core.animateFrame.moveTime > (core.values.moveSpeed||100)) { - core.animateFrame.leftLeg = !core.animateFrame.leftLeg; - core.animateFrame.moveTime = timestamp; - } - core.drawHero(direction, x, y, core.animateFrame.leftLeg?'leftFoot':'rightFoot', 4*core.status.heroMoving); - /* - if (core.status.heroMoving<=4) { - core.drawHero(direction, x, y, 'leftFoot', 4*core.status.heroMoving); - } - else if (core.status.heroMoving<=8) { - core.drawHero(direction, x, y, 'rightFoot', 4*core.status.heroMoving); - } - */ - } - - // weather - if (core.isPlaying() && timestamp-core.animateFrame.weather.time>30 && core.isset(core.dymCanvas.weather)) { - var ctx = core.dymCanvas.weather; - - var ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; - - if (core.animateFrame.weather.type == 'rain' && core.animateFrame.weather.level > 0) { - core.clearMap('weather'); - ctx.strokeStyle = 'rgba(174,194,224,0.8)'; - ctx.lineWidth = 1; - ctx.lineCap = 'round'; - - core.animateFrame.weather.nodes.forEach(function (p) { - ctx.beginPath(); - ctx.moveTo(p.x-ox, p.y-oy); - ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy); - ctx.stroke(); - - p.x += p.xs; - p.y += p.ys; - if (p.x > core.bigmap.width*32 || p.y > core.bigmap.height*32) { - p.x = Math.random() * core.bigmap.width*32; - p.y = -10; - } - - }) - - ctx.fill(); - - } - else if (core.animateFrame.weather.type == 'snow' && core.animateFrame.weather.level > 0) { - - core.clearMap('weather'); - ctx.fillStyle = "rgba(255, 255, 255, 0.8)"; - ctx.beginPath(); - - if (!core.isset(core.animateFrame.weather.data)) - core.animateFrame.weather.data = 0; - core.animateFrame.weather.data += 0.01; - - var angle = core.animateFrame.weather.data; - core.animateFrame.weather.nodes.forEach(function (p) { - ctx.moveTo(p.x - ox, p.y - oy); - ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true); - - // update - p.x += Math.sin(angle) * 2; - p.y += Math.cos(angle + p.d) + 1 + p.r / 2; - - if (p.x > core.bigmap.width*32 + 5 || p.x < -5 || p.y > core.bigmap.height*32) { - if (Math.random() > 1 / 3) { - p.x = Math.random() * core.bigmap.width*32; - p.y = -10; - } - else { - if (Math.sin(angle) > 0) { - p.x = -5; - p.y = Math.random() * core.bigmap.height*32; - } - else { - p.x = core.bigmap.width*32 + 5; - p.y = Math.random() * core.bigmap.height*32; - } - } - } - - }) - - ctx.fill(); - - } - else if (core.animateFrame.weather.type == 'fog' && core.animateFrame.weather.level > 0) { - core.clearMap('weather'); - if (core.animateFrame.weather.fog) { - var w = 416, h = 416; - core.setAlpha('weather', 0.5); - core.animateFrame.weather.nodes.forEach(function (p) { - ctx.drawImage(core.animateFrame.weather.fog, p.x - ox, p.y - oy, w, h); - - p.x += p.xs; - p.y += p.ys; - if (p.x > core.bigmap.width*32 - w/2) { - p.x = core.bigmap.width*32 - w/2 - 1; - p.xs = -p.xs; - } - if (p.x < -w/2) { - p.x = -w/2+1; - p.xs = -p.xs; - } - if (p.y > core.bigmap.height*32 - h/2) { - p.y = core.bigmap.height*32 - h/2 - 1; - p.ys = -p.ys; - } - if (p.y < -h/2) { - p.y = -h/2+1; - p.ys = -p.ys; - } - }) - core.setAlpha('weather',1); - } - - } - core.animateFrame.weather.time = timestamp; - - } - - // 执行用户的并行事件处理内容 - functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.plugins.parallelDo(timestamp); - - if (core.isPlaying()) { - // 执行插件中的每帧函数 - var renderFrameFuncs = core.plugin.__renderFrameFuncs || []; - renderFrameFuncs.forEach(function (t) { - try { - if (t instanceof Function) { - t(timestamp); - } - else if (typeof t == 'string') { - if (core.plugin[t]) - core.plugin[t](timestamp); - } - } - catch (e) { - main.log(e); - } - }); - } - - // 检查控制台状态 - if (core.utils.consoleOpened()) { - core.setFlag('consoleOpened', true); - } - - window.requestAnimationFrame(draw); - } - window.requestAnimationFrame(draw); } +control.prototype._animationFrame_totalTime = function (timestamp) { + core.animateFrame.totalTime += timestamp - core.animateFrame.totalTimeStart; + core.animateFrame.totalTimeStart = timestamp; + if (core.isPlaying()) { + core.status.hero.statistics.totalTime = core.animateFrame.totalTime; + core.status.hero.statistics.currTime += timestamp-(core.status.hero.statistics.start||timestamp); + core.status.hero.statistics.start = timestamp; + } +} + +control.prototype._animationFrame_autoSave = function (timestamp) { + if (timestamp - core.saves.autosave.time <= 5000) return; + core.control.checkAutosave(); + core.saves.autosave.time = timestamp; +} + +control.prototype._animationFrame_globalAnimate = function (timestamp) { + if (timestamp - core.animateFrame.globalTime <= core.values.animateSpeed) return; + core.status.globalAnimateStatus++; + if (core.status.floorId) { + // Global Animate + core.status.globalAnimateObjs.forEach(function (block) { + core.drawBlock(block, core.status.globalAnimateStatus % (block.event.animate||1)); + }); + + // Global floor images + core.maps._drawFloorImages(core.status.floorId, core.canvas.bg, 'bg', core.status.floorAnimateObjs||[], core.status.globalAnimateStatus); + core.maps._drawFloorImages(core.status.floorId, core.canvas.fg, 'fg', core.status.floorAnimateObjs||[], core.status.globalAnimateStatus); + + // Global Autotile Animate + core.status.autotileAnimateObjs.blocks.forEach(function (block) { + core.maps._drawAutotileAnimate(block, core.status.globalAnimateStatus); + }); + } + // Box animate + core.drawBoxAnimate(); + core.animateFrame.globalTime = timestamp; +} + +control.prototype._animationFrame_selector = function (timestamp) { + if (timestamp - core.animateFrame.selectorTime <= 20 || !core.dymCanvas._selector) return; + var opacity = parseFloat(core.dymCanvas._selector.canvas.style.opacity); + if (core.animateFrame.selectorUp) + opacity += 0.02; + else + opacity -= 0.02; + if (opacity > 0.95 || opacity < 0.55) + core.animateFrame.selectorUp = !core.animateFrame.selectorUp; + core.setOpacity("_selector", opacity); + core.animateFrame.selectorTime = timestamp; +} + +control.prototype._animationFrame_animate = function (timestamp) { + if (timestamp - core.animateFrame.animateTime < 50 || !core.status.animateObjs || core.status.animateObjs.length == 0) return; + core.clearMap('animate'); + // 更新帧 + for (var i = 0; i < core.status.animateObjs.length; i++) { + var obj = core.status.animateObjs[i]; + if (obj.index == obj.animate.frames.length) { + (function (callback) { + setTimeout(function () { + if (callback) callback(); + }); + })(obj.callback); + } + } + core.status.animateObjs = core.status.animateObjs.filter(function (obj) { + return obj.index < obj.animate.frames.length; + }); + core.status.animateObjs.forEach(function (obj) { + core.maps._drawAnimateFrame(obj.animate, obj.centerX, obj.centerY, obj.index++); + }); + core.animateFrame.animateTime = timestamp; +} + +control.prototype._animationFrame_heroMoving = function (timestamp) { + if (core.status.heroMoving <= 0) return; + var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); + // 换腿 + if (timestamp - core.animateFrame.moveTime > (core.values.moveSpeed||100)) { + core.animateFrame.leftLeg = !core.animateFrame.leftLeg; + core.animateFrame.moveTime = timestamp; + } + core.drawHero(core.animateFrame.leftLeg?'leftFoot':'rightFoot', 4*core.status.heroMoving); +} + +control.prototype._animationFrame_weather = function (timestamp) { + var weather = core.animateFrame.weather; + if (timestamp - weather.time <= 30 || !core.dymCanvas.weather) return; + core.control["_animationFrame_weather_"+weather.type](); + weather.time = timestamp; +} + +control.prototype._animationFrame_weather_rain = function () { + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.strokeStyle = 'rgba(174,194,224,0.8)'; + ctx.lineWidth = 1; + ctx.lineCap = 'round'; + + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.beginPath(); + ctx.moveTo(p.x-ox, p.y-oy); + ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy); + ctx.stroke(); + + p.x += p.xs; + p.y += p.ys; + if (p.x > core.bigmap.width * 32 || p.y > core.bigmap.height * 32) { + p.x = Math.random() * core.bigmap.width * 32; + p.y = -10; + } + + }); + + ctx.fill(); +} + +control.prototype._animationFrame_weather_snow = function () { + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.fillStyle = "rgba(255, 255, 255, 0.8)"; + ctx.beginPath(); + core.animateFrame.weather.data = core.animateFrame.weather.data || 0; + core.animateFrame.weather.data += 0.01; + + var angle = core.animateFrame.weather.data; + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.moveTo(p.x - ox, p.y - oy); + ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true); + // update + p.x += Math.sin(angle) * 2; + p.y += Math.cos(angle + p.d) + 1 + p.r / 2; + if (p.x > core.bigmap.width*32 + 5 || p.x < -5 || p.y > core.bigmap.height*32) { + if (Math.random() > 1 / 3) { + p.x = Math.random() * core.bigmap.width*32; + p.y = -10; + } + else { + if (Math.sin(angle) > 0) + p.x = -5; + else + p.x = core.bigmap.width*32 + 5; + p.y = Math.random() * core.bigmap.height*32; + } + } + }); + ctx.fill(); +} + +control.prototype._animationFrame_weather_fog = function () { + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + if (core.animateFrame.weather.fog) { + var w = core.__PIXELS__, h = core.__PIXELS__; + core.setAlpha('weather', 0.5); + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.drawImage(core.animateFrame.weather.fog, p.x - ox, p.y - oy, w, h); + p.x += p.xs; + p.y += p.ys; + if (p.x > core.bigmap.width*32 - w/2) { + p.x = core.bigmap.width*32 - w/2 - 1; + p.xs = -p.xs; + } + if (p.x < -w/2) { + p.x = -w/2+1; + p.xs = -p.xs; + } + if (p.y > core.bigmap.height*32 - h/2) { + p.y = core.bigmap.height*32 - h/2 - 1; + p.ys = -p.ys; + } + if (p.y < -h/2) { + p.y = -h/2+1; + p.ys = -p.ys; + } + }); + core.setAlpha('weather',1); + } +} + +control.prototype._animationFrame_parallelDo = function (timestamp) { + core.control.controldata.parallelDo(timestamp); +} + +control.prototype._animationFrame_checkConsoleOpened = function (timestamp) { + if (core.consoleOpened()) core.setFlag('__consoleOpened__', true); +} + +// ------ 标题界面的处理 ------ // + ////// 显示游戏开始界面 ////// control.prototype.showStartAnimate = function (noAnimate, callback) { + this._showStartAnimate_resetDom(); + if (core.flags.startUsingCanvas || noAnimate) + return this._showStartAnimate_finished(core.flags.startUsingCanvas, callback); + core.hideWithAnimate(core.dom.startTop, 20, function () { + core.control._showStartAnimate_finished(false, callback); + }); +} + +control.prototype._showStartAnimate_resetDom = function () { core.dom.startPanel.style.opacity=1; core.dom.startPanel.style.display="block"; core.dom.startTop.style.opacity=1; @@ -316,62 +323,28 @@ control.prototype.showStartAnimate = function (noAnimate, callback) { core.dom.levelChooseButtons.style.display = 'none'; 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(); +} - if (core.flags.startUsingCanvas) { - core.dom.startTop.style.display = 'none'; - core.dom.startButtonGroup.style.display = 'block'; - core.events.startGame(''); - if (core.isset(callback)) callback(); - return; - } - - if(noAnimate) { - core.dom.startTop.style.display = 'none'; - // core.playGame(); - core.dom.startButtonGroup.style.display = 'block'; - if (core.isset(callback)) callback(); - } - else { - var opacityVal = 1; - var startAnimate = window.setInterval(function () { - opacityVal -= 0.03; - if (opacityVal < 0) { - clearInterval(startAnimate); - core.dom.startTop.style.display = 'none'; - // core.playGame(); - core.dom.startButtonGroup.style.display = 'block'; - if (core.isset(callback)) callback(); - } - core.dom.startTop.style.opacity = opacityVal; - }, 20); - } - +control.prototype._showStartAnimate_finished = function (start, callback) { + core.dom.startTop.style.display = 'none'; + core.dom.startButtonGroup.style.display = 'block'; + if (start) core.startGame(); + if (callback) callback(); } ////// 隐藏游戏开始界面 ////// control.prototype.hideStartAnimate = function (callback) { - var opacityVal = 1; - var startAnimate = window.setInterval(function () { - opacityVal -= 0.03; - if (opacityVal < 0) { - clearInterval(startAnimate); - core.dom.startPanel.style.display = 'none'; - if (core.isset(callback)) callback(); - } - core.dom.startPanel.style.opacity = opacityVal; - }, 20); + core.hideWithAnimate(core.dom.startPanel, 20, callback); } ////// 游戏是否已经开始 ////// control.prototype.isPlaying = function() { - return core.isset(core.status.played) && core.status.played; + return core.status.played; } ////// 清除游戏状态和数据 ////// @@ -387,33 +360,12 @@ control.prototype.clearStatus = function() { } core.status = {}; core.clearStatusBar(); + core.clearMap('all'); core.deleteAllCanvas(); core.status.played = false; - core.events.setHeroIcon('hero.png', true); } -////// 重置游戏状态和初始数据 ////// -control.prototype.resetStatus = function(hero, hard, floorId, route, maps, values) { - - var totalTime = core.animateFrame.totalTime; - - // 清除游戏数据 - core.clearStatus(); - - // 初始化status - core.status = core.clone(core.initStatus); - core.status.played = true; - // 初始化maps - core.status.floorId = floorId; - core.status.maps = core.clone(maps); - // 初始化怪物 - core.material.enemys = core.enemys.getEnemys(); - core.material.items = core.items.getItems(); - // 初始化人物属性 - core.status.hero = core.clone(hero); - // 初始化人物图标 - core.events.setHeroIcon(core.getFlag('heroIcon', 'hero.png'), true); - // 统计数据 +control.prototype._initStatistics = function (totalTime) { if (!core.isset(core.status.hero.statistics)) core.status.hero.statistics = { 'totalTime': totalTime, @@ -428,51 +380,18 @@ control.prototype.resetStatus = function(hero, hard, floorId, route, maps, value 'moveDirectly': 0, 'ignoreSteps': 0, } - core.status.hero.statistics.totalTime = core.animateFrame.totalTime = - Math.max(core.status.hero.statistics.totalTime, core.animateFrame.totalTime); - core.status.hero.statistics.start = null; - - core.status.hard = hard; - // 初始化路线 - if (core.isset(route)) - core.status.route = route; - - if (core.isset(values)) - core.values = core.clone(values); - else core.values = core.clone(core.data.values); - - core.flags = core.clone(core.data.flags); - var systemFlags = core.getFlag("globalFlags", {}); - for (var key in systemFlags) - core.flags[key] = systemFlags[key]; - - core.events.initGame(); - core.resize(); - this.updateGlobalAttribute(Object.keys(core.status.globalAttribute)); - this.triggerStatusBar(core.getFlag('hideStatusBar', false)?'hide':'show', core.getFlag("showToolbox")); - core.dom.musicBtn.style.display = 'none'; } -////// 重新开始游戏;此函数将回到标题页面 ////// -control.prototype.restart = function(noAnimate) { - this.showStartAnimate(noAnimate); - core.playBgm(main.startBgm); -} - - -/////////////////////// 寻路算法 & 人物行走控制 /////////////////////// +// ------ 自动寻路,人物行走 ------ // ////// 清除自动寻路路线 ////// control.prototype.clearAutomaticRouteNode = function (x, y) { - if (core.status.event.id==null) - core.clearMap('route', x * 32 + 5 - core.status.automaticRoute.offsetX, y * 32 + 5 - core.status.automaticRoute.offsetY, 27, 27); + core.clearMap('route', x * 32 + 5 - core.status.automaticRoute.offsetX, y * 32 + 5 - core.status.automaticRoute.offsetY, 27, 27); } ////// 停止自动寻路操作 ////// control.prototype.stopAutomaticRoute = function () { - if (!core.status.played) { - return; - } + if (!core.status.played) return; core.status.automaticRoute.autoHeroMove = false; core.status.automaticRoute.autoStep = 0; core.status.automaticRoute.destStep = 0; @@ -481,11 +400,22 @@ 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'); } +////// 保存剩下的寻路,并停止 ////// +control.prototype.saveAndStopAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + if (automaticRoute.moveStepBeforeStop.length == 0) { + automaticRoute.moveStepBeforeStop = automaticRoute.autoStepRoutes.slice(automaticRoute.autoStep - 1); + if (automaticRoute.moveStepBeforeStop.length>=1) + automaticRoute.moveStepBeforeStop[0].step -= automaticRoute.movedStep; + } + this.stopAutomaticRoute(); +} + ////// 继续剩下的自动寻路操作 ////// control.prototype.continueAutomaticRoute = function () { // 此函数只应由events.afterOpenDoor和events.afterBattle调用 @@ -500,57 +430,41 @@ control.prototype.continueAutomaticRoute = function () { } ////// 清空剩下的自动寻路列表 ////// -control.prototype.clearContinueAutomaticRoute = function () { +control.prototype.clearContinueAutomaticRoute = function (callback) { core.deleteCanvas('route'); core.status.automaticRoute.moveStepBeforeStop=[]; + if (callback) callback(); } -////// 瞬间移动 ////// -control.prototype.moveDirectly = function (destX, destY) { - var ignoreSteps = core.canMoveDirectly(destX, destY); - if (ignoreSteps>=0) { - core.clearMap('hero'); - var lastDirection = core.status.route[core.status.route.length-1]; - if (['left', 'right', 'up', 'down'].indexOf(lastDirection)>=0) - core.setHeroLoc('direction', lastDirection); - core.setHeroLoc('x', destX); - core.setHeroLoc('y', destY); - core.drawHero(); - core.status.route.push("move:"+destX+":"+destY); - core.status.hero.statistics.moveDirectly++; - core.status.hero.statistics.ignoreSteps+=ignoreSteps; - return true; - } - return false; -} - -////// 尝试瞬间移动 ////// -control.prototype.tryMoveDirectly = function (destX, destY) { - if (Math.abs(core.getHeroLoc('x')-destX)+Math.abs(core.getHeroLoc('y')-destY)<=1) - return false; - var canMoveArray = core.maps._canMoveHero_generateArray(); - var testMove = function (dx, dy, dir) { - if (dx<0 || dx>=core.bigmap.width|| dy<0 || dy>=core.bigmap.height) return false; - if (core.isset(dir) && !core.inArray(canMoveArray[dx][dy],dir)) return false; - if (core.control.moveDirectly(dx, dy)) { - if (core.isset(dir)) core.moveHero(dir, function() {}); - return true; - } - return false; - } - return testMove(destX,destY) || testMove(destX-1, destY, "right") || testMove(destX,destY-1,"down") - || testMove(destX,destY+1,"up") || testMove(destX+1,destY,"left"); +////// 显示离散的寻路点 ////// +control.prototype.fillPosWithPoint = function (pos) { + core.fillRect('ui', pos.x*32+12,pos.y*32+12,8,8, '#bfbfbf'); } ////// 设置自动寻路路线 ////// control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { - if (!core.status.played || core.status.lockControl) { + if (!core.status.played || core.status.lockControl) return; + if (this._setAutomaticRoute_isMoving(destX, destY)) return; + if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return; + if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return; + // 找寻自动寻路路线 + 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); + this._setAutomaticRoute_setAutoSteps(moveStep); + // 立刻移动 + core.setAutoHeroMove(); +} + +control.prototype._setAutomaticRoute_isMoving = function (destX, destY) { if (core.status.automaticRoute.autoHeroMove) { var lastX = core.status.automaticRoute.destX, lastY=core.status.automaticRoute.destY; core.stopAutomaticRoute(); + // 双击瞬移 if (lastX==destX && lastY==destY) { core.status.automaticRoute.moveDirectly = true; setTimeout(function () { @@ -560,8 +474,12 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { core.status.automaticRoute.moveDirectly = false; }, core.values.moveSpeed || 100); } - return; + return true; } + return false; +} + +control.prototype._setAutomaticRoute_isTurning = function (destX, destY, stepPostfix) { if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y && stepPostfix.length==0) { if (core.timeout.turnHeroTimeout==null) { core.timeout.turnHeroTimeout = setTimeout(function() { @@ -575,27 +493,23 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { core.timeout.turnHeroTimeout = null; core.getNextItem(); } - return; + return true; } + if (core.timeout.turnHeroTimeout!=null) return true; + return false; +} - if (core.timeout.turnHeroTimeout!=null) return; - +control.prototype._setAutomaticRoute_clickMoveDirectly = function (destX, destY, stepPostfix) { // 单击瞬间移动 if (core.status.heroStop && core.status.heroMoving==0) { - if (stepPostfix.length<=1 && core.getFlag('clickMove', true) && core.control.tryMoveDirectly(destX, destY)) - return; + if (stepPostfix.length<=1 && !core.hasFlag('__noClickMove__') && core.control.tryMoveDirectly(destX, destY)) + return true; } + return false; +} - var moveStep = core.automaticRoute(destX, destY).concat(stepPostfix); - if (moveStep.length == 0) { - core.deleteCanvas('route'); - return; - } - - core.status.automaticRoute.destX=destX; - core.status.automaticRoute.destY=destY; - - // 计算绘制区域的宽高 +control.prototype._setAutomaticRoute_drawRoute = function (moveStep) { + // 计算绘制区域的宽高,并尽可能小的创建route层 var sx = core.bigmap.width * 32, sy = core.bigmap.height * 32, dx = 0, dy = 0; moveStep.forEach(function (t) { sx = Math.min(sx, t.x * 32); dx = Math.max(dx, t.x * 32); @@ -621,7 +535,9 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { ctx.stroke(); } } +} +control.prototype._setAutomaticRoute_setAutoSteps = function (moveStep) { // 路线转autoStepRoutes var step = 0, currStep = null; moveStep.forEach(function (t) { @@ -630,106 +546,17 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { step++; else { core.status.automaticRoute.autoStepRoutes.push({'direction': currStep, 'step': step}); - step = 1; + step = 1; } currStep = dir; }); core.status.automaticRoute.autoStepRoutes.push({'direction': currStep, 'step': step}); - - // 立刻移动 - core.setAutoHeroMove(); -} - -////// 自动寻路算法,找寻最优路径 ////// -control.prototype.automaticRoute = function (destX, destY) { - var fw = core.bigmap.width, fh = core.bigmap.height; - var startX = core.getHeroLoc('x'); - var startY = core.getHeroLoc('y'); - if (destX == startX && destY == startY) return []; - - var route = []; - var queue = new PriorityQueue({comparator: function (a,b) { - return a.depth - b.depth; - }}); - var ans = []; - - var canMoveArray = core.maps._canMoveHero_generateArray(); - - route[startX + fw * startY] = ''; - queue.queue({depth: 0, x: startX, y: startY}); - while (queue.length!=0) { - var curr = queue.dequeue(); - var deep = curr.depth, nowX = curr.x, nowY = curr.y; - - for (var direction in core.utils.scan) { - if (!core.inArray(canMoveArray[nowX][nowY], direction)) - continue; - - var nx = nowX + core.utils.scan[direction].x; - var ny = nowY + core.utils.scan[direction].y; - if (nx<0 || nx>=fw || ny<0 || ny>=fh) continue; - - var nid = nx + fw * ny; - - if (core.isset(route[nid])) continue; - - var deepAdd=1; - var nextId, nextBlock = core.getBlock(nx,ny); - if (nextBlock!=null){ - nextId = nextBlock.block.event.id; - // 绕过亮灯(因为只有一次通行机会很宝贵) - if(nextId == "light") deepAdd=100; - // 绕过路障 - // if (nextId.substring(nextId.length-3)=="Net") deepAdd=core.values.lavaDamage*10; - // 绕过血瓶 - if (!core.flags.potionWhileRouting && nextId.substring(nextId.length-6)=="Potion") deepAdd+=20; - // 绕过传送点 - if (nextBlock.block.event.trigger == 'changeFloor') deepAdd+=10; - } - deepAdd+=core.status.checkBlock.damage[nid]*10; - // 绕过捕捉 - if ((core.status.checkBlock.ambush||[])[nid]) - deepAdd += 10000; - - if (nx == destX && ny == destY) { - route[nid] = direction; - break; - } - if (core.noPassExists(nx, ny)) - continue; - - route[nid] = direction; - queue.queue({depth: deep+deepAdd, x: nx, y: ny}); - } - if (core.isset(route[destX + fw * destY])) break; - } - if (!core.isset(route[destX + fw * destY])) { - return []; - } - - var nowX = destX, nowY = destY; - while (nowX != startX || nowY != startY) { - var dir = route[nowX + fw * nowY]; - ans.push({'direction': dir, 'x': nowX, 'y': nowY}); - nowX -= core.utils.scan[dir].x; - nowY -= core.utils.scan[dir].y; - } - - ans.reverse(); - return ans; -} - -////// 显示离散的寻路点 ////// -control.prototype.fillPosWithPoint = function (pos) { - core.fillRect('ui', pos.x*32+12,pos.y*32+12,8,8, '#bfbfbf'); } ////// 设置勇士的自动行走路线 ////// control.prototype.setAutoHeroMove = function (steps) { steps=steps||core.status.automaticRoute.autoStepRoutes; - if (steps.length == 0) { - return; - } + if (steps.length == 0) return; core.status.automaticRoute.autoStepRoutes=steps; core.status.automaticRoute.autoHeroMove = true; core.status.automaticRoute.autoStep = 1; @@ -738,114 +565,105 @@ control.prototype.setAutoHeroMove = function (steps) { } ////// 设置行走的效果动画 ////// -control.prototype.setHeroMoveInterval = function (direction, x, y, callback) { - if (core.status.heroMoving>0) { - return; - } +control.prototype.setHeroMoveInterval = function (callback) { + if (core.status.heroMoving > 0) return; core.status.heroMoving=1; var toAdd = 1; - if (core.status.replay.speed>3) - toAdd = 2; - if (core.status.replay.speed>6) - toAdd = 4; - if (core.status.replay.speed>12) - toAdd = 8; + if (core.status.replay.speed>3) toAdd = 2; + if (core.status.replay.speed>6) toAdd = 4; + if (core.status.replay.speed>12) toAdd = 8; core.interval.heroMoveInterval = window.setInterval(function () { core.status.heroMoving+=toAdd; if (core.status.heroMoving>=8) { - core.setHeroLoc('x', x+core.utils.scan[direction].x, true); - core.setHeroLoc('y', y+core.utils.scan[direction].y, true); - core.control.updateFollowers(); - core.moveOneStep(); - core.clearMap('hero'); - core.drawHero(direction); clearInterval(core.interval.heroMoveInterval); core.status.heroMoving = 0; - if (core.isset(callback)) callback(); + core.moveOneStep(core.nextX(), core.nextY()); + if (callback) callback(); } }, (core.values.moveSpeed||100) / 8 * toAdd / core.status.replay.speed); } +////// 每移动一格后执行的事件 ////// +control.prototype.moveOneStep = function(x, y) { + return this.controldata.moveOneStep(x, y); +} + ////// 实际每一步的行走过程 ////// control.prototype.moveAction = function (callback) { if (core.status.heroMoving>0) return; - var direction = core.getHeroLoc('direction'); - var x = core.getHeroLoc('x'); - var y = core.getHeroLoc('y'); - var noPass = core.noPass(x + core.utils.scan[direction].x, y + core.utils.scan[direction].y), canMove = core.canMoveHero(); - if (noPass || !canMove) { - if (core.status.event.id!='ski') - core.status.route.push(direction); - core.status.automaticRoute.moveStepBeforeStop = []; - core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); - if (canMove) // 非箭头:触发 - core.trigger(x + core.utils.scan[direction].x, y + core.utils.scan[direction].y); - core.drawHero(direction, x, y); - - if (core.status.automaticRoute.moveStepBeforeStop.length==0) { - core.clearContinueAutomaticRoute(); - core.stopAutomaticRoute(); - } - if (core.isset(callback)) - callback(); - } - else { - core.setHeroMoveInterval(direction, x, y, function () { - if (core.status.automaticRoute.autoHeroMove) { - core.status.automaticRoute.movedStep++; - core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); - if (core.status.automaticRoute.destStep == core.status.automaticRoute.movedStep) { - if (core.status.automaticRoute.autoStep == core.status.automaticRoute.autoStepRoutes.length) { - core.clearContinueAutomaticRoute(); - core.stopAutomaticRoute(); - } - else { - core.status.automaticRoute.movedStep = 0; - core.status.automaticRoute.destStep = core.status.automaticRoute.autoStepRoutes[core.status.automaticRoute.autoStep].step; - core.setHeroLoc('direction', core.status.automaticRoute.autoStepRoutes[core.status.automaticRoute.autoStep].direction); - core.status.automaticRoute.autoStep++; - } - } - } - else if (core.status.heroStop) { - core.drawHero(); - } - if (core.status.event.id!='ski') - core.status.route.push(direction); - - // 检查是不是无事件的道具 - var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); - var block = core.getBlock(nowx,nowy); - var hasTrigger = false; - if (block!=null && block.block.event.trigger=='getItem' && - !core.isset(core.floors[core.status.floorId].afterGetItem[nowx+","+nowy])) { - hasTrigger = true; - core.trigger(nowx, nowy); - } - core.checkBlock(); - if (!hasTrigger && !core.status.gameOver) - core.trigger(nowx, nowy); - if (core.isset(callback)) callback(); - }); - } + var noPass = core.noPass(core.nextX(), core.nextY()), canMove = core.canMoveHero(); + // 下一个点如果不能走 + if (noPass || !canMove) return this._moveAction_noPass(canMove, callback); + this._moveAction_moving(callback); } -////// 转向 ////// -control.prototype.turnHero = function(direction) { - if (core.isset(direction)) { - core.setHeroLoc('direction', direction); - core.drawHero(); - core.status.route.push("turn:"+direction); - return; - } - if (core.status.hero.loc.direction == 'up') core.status.hero.loc.direction = 'right'; - else if (core.status.hero.loc.direction == 'right') core.status.hero.loc.direction = 'down'; - else if (core.status.hero.loc.direction == 'down') core.status.hero.loc.direction = 'left'; - else if (core.status.hero.loc.direction == 'left') core.status.hero.loc.direction = 'up'; +control.prototype._moveAction_noPass = function (canMove, callback) { + core.status.route.push(core.getHeroLoc('direction')); + core.status.automaticRoute.moveStepBeforeStop = []; + core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (canMove) core.events._trigger(core.nextX(), core.nextY()); core.drawHero(); - core.status.route.push("turn"); + + if (core.status.automaticRoute.moveStepBeforeStop.length==0) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + if (callback) callback(); +} + +control.prototype._moveAction_moving = function (callback) { + core.setHeroMoveInterval(function () { + var direction = core.getHeroLoc('direction'); + core.control._moveAction_popAutomaticRoute(); + core.status.route.push(direction); + + // 无事件的道具(如血瓶)需要优先于阻激夹域判定 + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + var block = core.getBlock(nowx,nowy); + var hasTrigger = false; + if (block!=null && block.block.event.trigger=='getItem' && + !core.floors[core.status.floorId].afterGetItem[nowx+","+nowy]) { + hasTrigger = true; + core.events._trigger(nowx, nowy); + } + // 执行该点的阻激夹域事件 + core.checkBlock(); + + // 执行该点事件 + if (!hasTrigger) + core.events._trigger(nowx, nowy); + core.updateStatusBar(); + + // 检查该点是否是滑冰 + if (core.getBgNumber() == 167) { + core.insertAction("滑冰事件", null, null, null, true); + } + + if (callback) callback(); + }); +} + +control.prototype._moveAction_popAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + // 检查自动寻路是否被弹出 + if (automaticRoute.autoHeroMove) { + automaticRoute.movedStep++; + automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (automaticRoute.destStep == automaticRoute.movedStep) { + if (automaticRoute.autoStep == automaticRoute.autoStepRoutes.length) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + else { + automaticRoute.movedStep = 0; + automaticRoute.destStep = automaticRoute.autoStepRoutes[automaticRoute.autoStep].step; + core.setHeroLoc('direction', automaticRoute.autoStepRoutes[automaticRoute.autoStep].direction); + core.status.automaticRoute.autoStep++; + } + } + } } ////// 让勇士开始移动 ////// @@ -854,173 +672,40 @@ control.prototype.moveHero = function (direction, callback) { if (core.status.heroMoving!=0) return; if (core.isset(direction)) core.setHeroLoc('direction', direction); - if (!core.isset(callback)) { // 如果不存在回调函数,则使用heroMoveTrigger - core.status.heroStop = false; - core.status.automaticRoute.moveDirectly = false; - var doAction = function () { - if (!core.status.heroStop) { - if (core.hasFlag('debug') && core.status.ctrlDown) { - if (core.status.heroMoving!=0) return; - // 检测是否穿出去 - direction = core.getHeroLoc('direction'); - var nx = core.getHeroLoc('x') + core.utils.scan[direction].x, ny=core.getHeroLoc('y') + core.utils.scan[direction].y; - if (nx<0 || nx>=core.bigmap.width || ny<0 || ny>=core.bigmap.height) return; + if (callback) return this.moveAction(callback); + this._moveHero_moving(); +} - core.status.heroMoving=-1; - core.eventMoveHero([direction], 100, function () { - core.status.heroMoving=0; - doAction(); - }); - } - else { - core.moveAction(); - setTimeout(doAction, 50); - } +control.prototype._moveHero_moving = function () { + // ------ 我已经看不懂这个函数了,反正好用就行23333333 + core.status.heroStop = false; + core.status.automaticRoute.moveDirectly = false; + var move = function () { + if (!core.status.heroStop) { + if (core.hasFlag('debug') && core.status.ctrlDown) { + if (core.status.heroMoving!=0) return; + // 检测是否穿出去 + var nx = core.nextX(), ny = core.nextY(); + if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height) return; + core.status.heroMoving=-1; + core.eventMoveHero([core.getHeroLoc('direction')], core.values.moveSpeed, function () { + core.status.heroMoving=0; + move(); + }); } else { - core.stopHero(); + core.moveAction(); + setTimeout(move, 50); } } - doAction(); - } - else { // 否则,只向某个方向移动一步,然后调用callback - core.moveAction(function () { - callback(); - }) } + move(); } -/////// 使用事件让勇士移动。这个函数将不会触发任何事件 ////// -control.prototype.eventMoveHero = function(steps, time, callback) { - time = time || core.values.moveSpeed || 100; - - // 要运行的轨迹:将steps展开 - var moveSteps=[]; - steps.forEach(function (e) { - if (typeof e=="string") { - moveSteps.push(e); - } - else { - if (!core.isset(e.value)) { - moveSteps.push(e.direction) - } - else { - for (var i=0;i=0;}); - - var step=0; - - var animate=window.setInterval(function() { - var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'); - if (moveSteps.length==0) { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.drawHero(null, x, y); - if (core.isset(callback)) callback(); - } - else { - var direction = moveSteps[0]; - - // ------ 前进/后退 - var o = direction == 'backward' ? -1 : 1; - if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction'); - - core.setHeroLoc('direction', direction); - step++; - if (step <= 4) { - core.drawHero(direction, x, y, 'leftFoot', 4 * o * step); - } - else if (step <= 8) { - core.drawHero(direction, x, y, 'rightFoot', 4 * o * step); - } - if (step == 8) { - step = 0; - core.setHeroLoc('x', x + o * core.utils.scan[direction].x, true); - core.setHeroLoc('y', y + o * core.utils.scan[direction].y, true); - core.control.updateFollowers(); - moveSteps.shift(); - } - } - }, time / 8 / core.status.replay.speed); - - core.animateFrame.asyncId[animate] = true; -} - -////// 勇士跳跃事件 ////// -control.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; - - time = time || 500; - - core.playSound('jump.mp3'); - - var dx = ex-sx, dy=ey-sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); - var jump_peak = 6 + distance, jump_count = jump_peak * 2; - var currx = sx, curry = sy; - - var heroIcon = core.material.icons.hero[core.getHeroLoc('direction')]; - var status = 'stop'; - var height = core.material.icons.hero.height; - - var drawX = function() { - return currx * 32; - } - var drawY = function() { - var ret = curry * 32; - if(jump_count >= jump_peak){ - var n = jump_count - jump_peak; - }else{ - var n = jump_peak - jump_count; - } - return ret - (jump_peak * jump_peak - n * n) / 2; - } - var updateJump = function() { - jump_count--; - currx = (currx * jump_count + ex) / (jump_count + 1.0); - curry = (curry * jump_count + ey) / (jump_count + 1.0); - } - - if (core.isset(core.status.hero.followers) && core.status.hero.followers.length>0) - core.clearMap('hero'); - - var animate=window.setInterval(function() { - - if (jump_count>0) { - core.clearMap('hero', drawX()-core.bigmap.offsetX, drawY()-height+32-core.bigmap.offsetY, 32, height); - updateJump(); - var nowx = drawX(), nowy = drawY(); - core.bigmap.offsetX = core.clamp(nowx - 32*6, 0, 32*core.bigmap.width-416); - core.bigmap.offsetY = core.clamp(nowy - 32*6, 0, 32*core.bigmap.height-416); - core.control.updateViewport(); - core.drawImage('hero', core.material.images.hero, heroIcon[status] * 32, heroIcon.loc * height, 32, height, - nowx - core.bigmap.offsetX, nowy + 32-height - core.bigmap.offsetY, 32, height); - } - else { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.setHeroLoc('x', ex); - core.setHeroLoc('y', ey); - core.drawHero(); - if (core.isset(callback)) callback(); - } - - }, time / 16 / core.status.replay.speed); - - core.animateFrame.asyncId[animate] = true; -} - -////// 每移动一格后执行的事件 ////// -control.prototype.moveOneStep = function() { - return this.controldata.moveOneStep(); +////// 当前是否正在移动 ////// +control.prototype.isMoving = function () { + return !core.status.heroStop || core.status.heroMoving > 0; } ////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback ////// @@ -1028,7 +713,7 @@ control.prototype.waitHeroToStop = function(callback) { var lastDirection = core.status.automaticRoute.lastDirection; core.stopAutomaticRoute(); core.clearContinueAutomaticRoute(); - if (core.isset(callback)) { + if (callback) { core.status.replay.animate=true; core.lockControl(); core.status.automaticRoute.moveDirectly = false; @@ -1042,11 +727,93 @@ control.prototype.waitHeroToStop = function(callback) { } } -////// 停止勇士的移动状态 ////// -control.prototype.stopHero = function () { - core.status.heroStop = true; +////// 转向 ////// +control.prototype.turnHero = function(direction) { + if (direction) { + core.setHeroLoc('direction', direction); + core.drawHero(); + core.status.route.push("turn:"+direction); + return; + } + var dirs = {'up':'right','right':'down','down':'left','left':'up'}; + core.setHeroLoc('direction', dirs[core.getHeroLoc('direction')]); + core.drawHero(); + core.status.route.push("turn"); } +////// 瞬间移动 ////// +control.prototype.moveDirectly = function (destX, destY) { + return this.controldata.moveDirectly(destX, destY); +} + +////// 尝试瞬间移动 ////// +control.prototype.tryMoveDirectly = function (destX, destY) { + if (this.nearHero(destX, destY)) return false; + var canMoveArray = core.maps.generateMovableArray(); + var testMove = function (dx, dy, dir) { + if (dx<0 || dx>=core.bigmap.width|| dy<0 || dy>=core.bigmap.height) return false; + if (dir && !core.inArray(canMoveArray[dx][dy],dir)) return false; + if (core.control.moveDirectly(dx, dy)) { + if (dir) core.moveHero(dir, function() {}); + return true; + } + return false; + } + return testMove(destX,destY) || testMove(destX-1, destY, "right") || testMove(destX,destY-1,"down") + || testMove(destX,destY+1,"up") || testMove(destX+1,destY,"left"); +} + +////// 绘制勇士 ////// +control.prototype.drawHero = function (status, offset) { + if (!core.isPlaying() || !core.status.floorId || core.status.gameOver) return; + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); + status = status || 'stop'; + offset = offset || 0; + var way = core.utils.scan[direction]; + var dx = way.x, dy = way.y, offsetX = dx * offset, offsetY = dy * offset; + core.bigmap.offsetX = core.clamp((x - core.__HALF_SIZE__) * 32 + offsetX, 0, 32*core.bigmap.width-core.__PIXELS__); + core.bigmap.offsetY = core.clamp((y - core.__HALF_SIZE__) * 32 + offsetY, 0, 32*core.bigmap.height-core.__PIXELS__); + core.clearAutomaticRouteNode(x+dx, y+dy); + core.clearMap('hero'); + + this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (block) { + core.drawImage('hero', block.img, block.heroIcon[block.status]*32, + block.heroIcon.loc * block.height, 32, block.height, + block.posx, block.posy+32-block.height, 32, block.height); + }); + + core.control.updateViewport(); +} + +control.prototype._drawHero_getDrawObjs = function (direction, x, y, status, offset) { + var heroIconArr = core.material.icons.hero, drawObjs = [], index = 0; + drawObjs.push({ + "img": core.material.images.hero, + "height": core.material.icons.hero.height, + "heroIcon": heroIconArr[direction], + "posx": x * 32 - core.bigmap.offsetX + core.utils.scan[direction].x * offset, + "posy": y * 32 - core.bigmap.offsetY + core.utils.scan[direction].y * offset, + "status": status, + "index": index++, + }); + (core.status.hero.followers||[]).forEach(function (t) { + drawObjs.push({ + "img": t.img, + "height": t.img.height/4, + "heroIcon": heroIconArr[t.direction], + "posx": 32*t.x - core.bigmap.offsetX + (t.stop?0:core.utils.scan[t.direction].x*offset), + "posy": 32*t.y - core.bigmap.offsetY + (t.stop?0:core.utils.scan[t.direction].y*offset), + "status": t.stop?"stop":status, + "index": index++ + }); + }); + return drawObjs.sort(function(a, b) { + return a.posy==b.posy?b.index-a.index:a.posy-b.posy; + }); +} + +// ------ 画布、位置、阻激夹域,显伤 ------ // + ////// 设置画布偏移 control.prototype.setGameCanvasTranslate = function(canvas,x,y){ var c=core.dom.gameCanvas[canvas]; @@ -1057,13 +824,27 @@ control.prototype.setGameCanvasTranslate = function(canvas,x,y){ c.style.OTransform='translate('+x+'px,'+y+'px)'; c.style.MozTransform='translate('+x+'px,'+y+'px)'; if(main.mode==='editor' && editor.isMobile){ - c.style.transform='translate('+(x/416*96)+'vw,'+(y/416*96)+'vw)'; - c.style.webkitTransform='translate('+(x/416*96)+'vw,'+(y/416*96)+'vw)'; - c.style.OTransform='translate('+(x/416*96)+'vw,'+(y/416*96)+'vw)'; - c.style.MozTransform='translate('+(x/416*96)+'vw,'+(y/416*96)+'vw)'; + c.style.transform='translate('+(x/core.__PIXELS__*96)+'vw,'+(y/core.__PIXELS__*96)+'vw)'; + c.style.webkitTransform='translate('+(x/core.__PIXELS__*96)+'vw,'+(y/core.__PIXELS__*96)+'vw)'; + c.style.OTransform='translate('+(x/core.__PIXELS__*96)+'vw,'+(y/core.__PIXELS__*96)+'vw)'; + c.style.MozTransform='translate('+(x/core.__PIXELS__*96)+'vw,'+(y/core.__PIXELS__*96)+'vw)'; } }; +////// 加减画布偏移 +control.prototype.addGameCanvasTranslate = function (x, y) { + for(var ii=0,canvas;canvas=core.dom.gameCanvas[ii];ii++){ + var id = canvas.getAttribute('id'); + if (id=='ui' || id=='data') continue; // UI层和data层不移动 + var offsetX = x, offsetY = y; + if (core.bigmap.canvas.indexOf(id)>=0) { + offsetX -= core.bigmap.offsetX; + offsetY -= core.bigmap.offsetY; + } + core.control.setGameCanvasTranslate(id, offsetX, offsetY); + } +} + ////// 更新视野范围 ////// control.prototype.updateViewport = function() { core.bigmap.canvas.forEach(function(cn){ @@ -1073,87 +854,6 @@ control.prototype.updateViewport = function() { core.relocateCanvas('route', core.status.automaticRoute.offsetX - core.bigmap.offsetX, core.status.automaticRoute.offsetY - core.bigmap.offsetY); } -////// 绘制勇士 ////// -control.prototype.drawHero = function (direction, x, y, status, offset) { - - if (!core.isPlaying()) return; - - if (!core.isset(x)) x = core.getHeroLoc('x'); - if (!core.isset(y)) y = core.getHeroLoc('y'); - status = status || 'stop'; - direction = direction || core.getHeroLoc('direction'); - offset = offset || 0; - var way = core.utils.scan[direction]; - var offsetX = way.x*offset; - var offsetY = way.y*offset; - var dx=offsetX==0?0:offsetX/Math.abs(offsetX), dy=offsetY==0?0:offsetY/Math.abs(offsetY); - - core.bigmap.offsetX = core.clamp((x - 6) * 32 + offsetX, 0, 32*core.bigmap.width-416); - core.bigmap.offsetY = core.clamp((y - 6) * 32 + offsetY, 0, 32*core.bigmap.height-416); - - core.clearAutomaticRouteNode(x+dx, y+dy); - - core.clearMap('hero', x * 32 - core.bigmap.offsetX - 32, y * 32 - core.bigmap.offsetY - 32, 96, 96); - - var heroIconArr = core.material.icons.hero; - var drawObjs = []; - // add hero - drawObjs.push({ - "img": core.material.images.hero, - "height": core.material.icons.hero.height, - "heroIcon": heroIconArr[direction], - "posx": x * 32 - core.bigmap.offsetX + offsetX, - "posy": y * 32 - core.bigmap.offsetY + offsetY, - "status": status, - "index": 0, - }); - - // Add other followers - if (core.isset(core.status.hero.followers)) { - var index=1; - core.status.hero.followers.forEach(function (t) { - core.clearMap('hero', 32*t.x-core.bigmap.offsetX-32, 32*t.y-core.bigmap.offsetY-32, 96, 96); - if (core.isset(core.material.images.images[t.img])) { - drawObjs.push({ - "img": core.material.images.images[t.img], - "height": core.material.images.images[t.img].height/4, - "heroIcon": heroIconArr[t.direction], - "posx": 32*t.x - core.bigmap.offsetX + (t.stop?0:core.utils.scan[t.direction].x*offset), - "posy": 32*t.y - core.bigmap.offsetY + (t.stop?0:core.utils.scan[t.direction].y*offset), - "status": t.stop?"stop":status, - "index": index++ - }); - } - }); - } - - drawObjs.sort(function (a, b) { - return a.posy==b.posy?b.index-a.index:a.posy-b.posy; - }); - - drawObjs.forEach(function (block) { - core.drawImage('hero', block.img, block.heroIcon[block.status]*32, - block.heroIcon.loc * block.height, 32, block.height, - block.posx, block.posy+32-block.height, 32, block.height); - }); - - core.control.updateViewport(); -} - -////// 设置勇士的位置 ////// -control.prototype.setHeroLoc = function (itemName, itemVal, noGather) { - core.status.hero.loc[itemName] = itemVal; - if ((itemName=='x' || itemName=='y') && !noGather) { - this.gatherFollowers(); - } -} - -////// 获得勇士的位置 ////// -control.prototype.getHeroLoc = function (itemName) { - if (!core.isset(itemName)) return core.status.hero.loc; - return core.status.hero.loc[itemName]; -} - ////// 获得勇士面对位置的x坐标 ////// control.prototype.nextX = function(n) { return core.getHeroLoc('x')+core.utils.scan[core.getHeroLoc('direction')].x*(n||1); @@ -1171,9 +871,8 @@ control.prototype.nearHero = function (x, y) { ////// 聚集跟随者 ////// control.prototype.gatherFollowers = function () { - if (!core.isset(core.status.hero.followers) || core.status.hero.followers.length==0) return; var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'), dir=core.getHeroLoc('direction'); - core.status.hero.followers.forEach(function (t) { + (core.status.hero.followers||[]).forEach(function (t) { t.x = x; t.y = y; t.stop = true; @@ -1183,8 +882,7 @@ control.prototype.gatherFollowers = function () { ////// 更新跟随者坐标 ////// control.prototype.updateFollowers = function () { - if (!core.isset(core.status.hero.followers) || core.status.hero.followers.length==0) return; - core.status.hero.followers.forEach(function (t) { + (core.status.hero.followers||[]).forEach(function (t) { if (!t.stop) { t.x += core.utils.scan[t.direction].x; t.y += core.utils.scan[t.direction].y; @@ -1192,404 +890,152 @@ control.prototype.updateFollowers = function () { }) var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); - core.status.hero.followers.forEach(function (t) { - if (t.x == nowx && t.y == nowy) { - t.stop = true; - } - else { - var dx = nowx-t.x, dy = nowy-t.y; - if (dx==-1) { - t.stop=false; - t.direction='left'; - } - else if (dx==1) { - t.stop=false; - t.direction='right'; - } - else if (dy==-1) { - t.stop=false; - t.direction='up'; - } - else if (dy==1) { - t.stop=false; - t.direction='down'; + (core.status.hero.followers||[]).forEach(function (t) { + t.stop = true; + var dx = nowx - t.x, dy = nowy - t.y; + for (var dir in core.utils.scan) { + if (core.utils.scan[dir].x == dx && core.utils.scan[dir].y == dy) { + t.stop = false; + t.direction = dir; } } - nowx=t.x; nowy=t.y; + nowx = t.x; nowy = t.y; }) } ////// 更新领域、夹击、阻击的伤害地图 ////// -control.prototype.updateCheckBlock = function() { - return this.controldata.updateCheckBlock(); +control.prototype.updateCheckBlock = function(floorId) { + return this.controldata.updateCheckBlock(floorId); } ////// 检查并执行领域、夹击、阻击事件 ////// control.prototype.checkBlock = function () { - var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'); - var damage = core.status.checkBlock.damage[x+core.bigmap.width*y]; - if (damage>0) { + var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'), loc = x+","+y; + var damage = core.status.checkBlock.damage[loc]; + if (damage) { core.status.hero.hp -= damage; - - // 检查阻击事件 - var snipe = []; - if (!core.hasFlag("no_snipe")) { - for (var direction in core.utils.scan) { - var nx = x+core.utils.scan[direction].x, ny=y+core.utils.scan[direction].y; - if (nx<0 || nx>=core.bigmap.width || ny<0 || ny>=core.bigmap.height) continue; - var id=core.status.checkBlock.map[nx+core.bigmap.width*ny]; - if (core.isset(id)) { - var enemy = core.material.enemys[id]; - if (core.isset(enemy) && core.enemys.hasSpecial(enemy.special, 18)) { - snipe.push({'direction': direction, 'x': nx, 'y': ny}); - } - } - } - } - - if (core.status.checkBlock.betweenAttack[x+core.bigmap.width*y] && damage>0) { - core.drawTip('受到夹击,生命变成一半'); - } - else if (core.status.checkBlock.map[x+core.bigmap.width*y]=='lavaNet') { - core.drawTip('受到血网伤害'+damage+'点'); - } - // 阻击 - else if (snipe.length>0 && damage>0) { - core.drawTip('受到阻击伤害'+damage+'点'); - } - else if (damage>0) { - core.drawTip('受到领域或激光伤害'+damage+'点'); - } - - if (damage>0) { - core.playSound('zone.mp3'); - core.drawAnimate("zone", x, y); - - // 禁用快捷商店 - if (core.flags.disableShopOnDamage) { - for (var shopId in core.status.shops) { - core.status.shops[shopId].visited = false; - } - } - - } - core.status.hero.statistics.extraDamage += damage; - - if (core.status.hero.hp<=0) { + core.drawTip("受到"+(core.status.checkBlock.type[loc]||"伤害")+damage+"点"); + this._checkBlock_soundAndAnimate(x, y); + if (core.status.hero.hp <= 0) { core.status.hero.hp=0; core.updateStatusBar(); core.events.lose(); return; } - snipe = snipe.filter(function (t) { - var x=t.x, y=t.y, direction = t.direction; - var nx = x+core.utils.scan[direction].x, ny=y+core.utils.scan[direction].y; - - return nx>=0 && nx=0 && ny0) - core.snipe(snipe); - } - // 检查捕捉 - var ambush = (core.status.checkBlock.ambush||[])[x+core.bigmap.width*y]; - if (core.isset(ambush)) { - // 捕捉效果 - var actions = []; - ambush.forEach(function (t) { - actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 500, "keep": false, "async":true}); - }); - actions.push({"type": "waitAsync"}); - // 强制战斗 - ambush.forEach(function (t) { - actions.push({"type": "battle", "id": t[2]}); - }) - core.insertAction(actions); } + this._checkBlock_snipe(core.status.checkBlock.snipe[loc]); + this._checkBlock_ambush(core.status.checkBlock.ambush[loc]); } -////// 阻击事件(动画效果) ////// -control.prototype.snipe = function (snipes) { - // 阻击改成moveBlock事件完成 +control.prototype._checkBlock_soundAndAnimate = function (x,y) { + core.playSound('zone.mp3'); + core.drawAnimate("zone", x, y); +} + +////// 阻击 ////// +control.prototype._checkBlock_snipe = function (snipe) { + if (!snipe || snipe.length == 0) return; var actions = []; - snipes.forEach(function (t) { - actions.push({"type": "move", "loc": [t.x, t.y], "steps": [t.direction], "time": 500, "keep": true, "async": true}); + snipe.forEach(function (t) { + actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 500, "keep": true, "async": true}); }); actions.push({"type": "waitAsync"}); core.insertAction(actions); } -////// 更改天气效果 ////// -control.prototype.setWeather = function (type, level) { - - // 非雨雪 - if (type!='rain' && type!='snow' && type!='fog') { - // core.clearMap('weather'); - core.deleteCanvas('weather') - core.animateFrame.weather.type = null; - core.animateFrame.weather.level = 0; - core.animateFrame.weather.nodes = []; - return; - } - - level = parseInt(level); - - // 当前天气:则忽略 - if (type==core.animateFrame.weather.type && !core.isset(level)) { - return; - } - - if (!core.isset(level)) level=5; - if (level<1) level=1; if (level>10) level=10; - level *= parseInt(20*core.bigmap.width*core.bigmap.height/169); - // 计算当前的宽高 - - core.createCanvas('weather', 0, 0, 416, 416, 80); - core.animateFrame.weather.type = type; - core.animateFrame.weather.level = level; - core.animateFrame.weather.nodes = []; - - if (type == 'rain') { - for (var a=0;a 1) - core.screenFlash(color, time * 3, times - 1, callback); - else { - if (core.isset(callback)) callback(); - } - }); +////// 捕捉 ////// +control.prototype._checkBlock_ambush = function (ambush) { + if (!ambush || ambush.length == 0) return; + // 捕捉效果 + var actions = []; + ambush.forEach(function (t) { + actions.push({"type": "move", "loc": [t[0],t[1]], "steps": [t[3]], "time": 500, "keep": false, "async":true}); }); + actions.push({"type": "waitAsync"}); + // 强制战斗 + ambush.forEach(function (t) { + actions.push({"type": "battle", "id": t[2]}); + }); + core.insertAction(actions); } ////// 更新全地图显伤 ////// -control.prototype.updateDamage = function (floorId, canvas) { +control.prototype.updateDamage = function (floorId, ctx) { floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; + if (!core.isset(floorId) || core.status.gameOver) return; if (core.status.gameOver) return; - if (!core.isset(canvas)) { - canvas = core.canvas.damage; + var refreshCheckBlock = true; + if (!core.isset(ctx)) { + ctx = core.canvas.damage; core.clearMap('damage'); + refreshCheckBlock = false; } - // 更新显伤 - var mapBlocks = core.status.maps[floorId].blocks; // 没有怪物手册 if (!core.hasItem('book')) return; - canvas.font = "bold 11px Arial"; + core.setFont(ctx, "bold 11px Arial"); + this._updateDamage_damage(floorId, ctx); + this._updateDamage_extraDamage(floorId, ctx, refreshCheckBlock); +} - if (core.flags.displayEnemyDamage || core.flags.displayCritical) { - canvas.textAlign = 'left'; - - for (var b = 0; b < mapBlocks.length; b++) { - var x = mapBlocks[b].x, y = mapBlocks[b].y; - if (core.isset(mapBlocks[b].event) && mapBlocks[b].event.cls.indexOf('enemy')==0 - && !mapBlocks[b].disable) { - - // 判定是否显伤 - if (mapBlocks[b].event.displayDamage === false) - continue; - - var id = mapBlocks[b].event.id; - - if (core.flags.displayEnemyDamage) { - var damageString = core.enemys.getDamageString(id, x, y, floorId); - var damage = damageString.damage, color = damageString.color; - core.fillBoldText(canvas, damage, 32*x+1, 32*(y+1)-1, color); - } - - // 临界显伤 - if (core.flags.displayCritical) { - var critical = core.enemys.nextCriticals(id, 1, x, y, floorId); - if (critical.length>0) critical=critical[0]; - critical = core.formatBigNumber(critical[0], true); - if (critical == '???') critical = '?'; - core.fillBoldText(canvas, critical, 32*x+1, 32*(y+1)-11, '#FFFFFF'); - } - - } +control.prototype._updateDamage_damage = function (floorId, ctx) { + core.setTextAlign(ctx, 'left'); + core.status.maps[floorId].blocks.forEach(function (block) { + var x = block.x, y = block.y; + if (!block.disable && block.event.cls.indexOf('enemy') == 0 && block.event.displayDamage !== false) { + if (core.flags.displayEnemyDamage) { + var damageString = core.enemys.getDamageString(block.event.id, x, y, floorId); + var damage = damageString.damage, color = damageString.color; + core.fillBoldText(ctx, damage, 32*x+1, 32*(y+1)-1, color); + } + if (core.flags.displayCritical) { + var critical = core.enemys.nextCriticals(block.event.id, 1, x, y, floorId); + critical = core.formatBigNumber((critical[0]||[])[0], true); + if (critical == '???') critical = '?'; + core.fillBoldText(ctx, critical, 32*x+1, 32*(y+1)-11, '#FFFFFF'); + } } - } - // 如果是领域&夹击 - if (core.flags.displayExtraDamage && core.isset((core.status.checkBlock||{}).damage)) { - canvas.textAlign = 'center'; + }); +} - // 临时改变 - var tempCheckBlock = null; - if (floorId != core.status.floorId) { - tempCheckBlock = core.clone(core.status.checkBlock); - core.status.thisMap = core.status.maps[floorId]; - core.bigmap.width = core.floors[floorId].width; - core.bigmap.height = core.floors[floorId].height; - core.updateCheckBlock(); - } - - for (var x=0;x0) { // 该点伤害 damage = core.formatBigNumber(damage, true); - core.fillBoldText(canvas, damage, 32*x+16, 32*(y+1)-14, '#FF7F00'); + core.fillBoldText(ctx, damage, 32*x+16, 32*(y+1)-14, '#FF7F00'); } else { // 检查捕捉 - if ((core.status.checkBlock.ambush||[])[x+core.bigmap.width*y]) { - core.fillBoldText(canvas, '!', 32*x+16, 32*(y+1)-14, '#FF7F00'); + if (core.status.checkBlock.ambush[x+","+y]) { + core.fillBoldText(ctx, '!', 32*x+16, 32*(y+1)-14, '#FF7F00'); } } } } - - if (floorId!=core.status.floorId) { - core.status.thisMap = core.status.maps[core.status.floorId]; - core.status.checkBlock = tempCheckBlock; - core.bigmap.width = core.floors[core.status.floorId].width; - core.bigmap.height = core.floors[core.status.floorId].height; - } } } -////// 执行一个表达式的effect操作 ////// -control.prototype.doEffect = function (effect, need, times) { - effect.split(";").forEach(function (expression) { - var arr = expression.split("+="); - if (arr.length!=2) return; - var name=arr[0], value=core.calValue(arr[1], null, need, times); - if (name.indexOf("status:")==0) { - var status=name.substring(7); - core.setStatus(status, core.getStatus(status)+value); - } - else if (name.indexOf("item:")==0) { - var itemId=name.substring(5); - core.setItem(itemId, core.itemCount(itemId)+value); - } - else if (name.indexOf("flag:")==0) { - var flag=name.substring(5); - core.setFlag(flag, core.getFlag(flag, 0)+value); - } - }); -} - -////// 开启debug模式 ////// -control.prototype.debug = function() { - core.setFlag('debug', true); - core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n同时,录像将失效,也无法上传成绩。"); -} +// ------ 录像相关 ------ // ////// 选择录像文件 ////// control.prototype.chooseReplayFile = function () { core.readFile(function (obj) { - if (obj.name!=core.firstData.name) { - alert("存档和游戏不一致!"); + if (obj.name!=core.firstData.name) return alert("存档和游戏不一致!"); + if (!obj.route) return alert("无效的录像!"); + var _replay = function () { + core.startGame(core.flags.startUsingCanvas?'':obj.hard||'', obj.seed, core.decodeRoute(obj.route)); + } + if (obj.version && obj.version!=core.firstData.version) { + core.myconfirm("游戏版本不一致!\n你仍然想播放录像吗?", _replay); return; } - if (core.isset(obj.version) && obj.version!=core.firstData.version) { - // alert("游戏版本不一致!"); - if (!confirm("游戏版本不一致!\n你仍然想播放录像吗?")) { - return; - } - } - if (!core.isset(obj.route)) { - alert("无效的录像!"); - return; - } - - if (core.flags.startUsingCanvas) { - core.startGame('', obj.seed, core.decodeRoute(obj.route)); - } - else { - core.startGame(obj.hard, obj.seed, core.decodeRoute(obj.route)); - } - }, function () { - - }) + _replay(); + }); } ////// 开始播放 ////// @@ -1605,22 +1051,17 @@ control.prototype.startReplay = function (list) { core.updateStatusBar(); core.drawTip("开始播放"); this.replay(); - return; } ////// 更改播放状态 ////// control.prototype.triggerReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; if (core.status.replay.pausing) this.resumeReplay(); else this.pauseReplay(); } ////// 暂停播放 ////// control.prototype.pauseReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; + if (!core.isPlaying() || !core.isReplaying()) return; core.status.replay.pausing = true; core.updateStatusBar(); core.drawTip("暂停播放"); @@ -1628,9 +1069,9 @@ control.prototype.pauseReplay = function () { ////// 恢复播放 ////// control.prototype.resumeReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; + if (!core.isPlaying() || !core.isReplaying()) return; + if (core.isMoving() || core.status.replay.animate || core.status.event.id) + return core.drawTip("请等待当前事件的处理结束"); core.status.replay.pausing = false; core.updateStatusBar(); core.drawTip("恢复播放"); @@ -1639,12 +1080,10 @@ control.prototype.resumeReplay = function () { ////// 加速播放 ////// control.prototype.speedUpReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; - if (core.status.replay.speed==12) core.status.replay.speed=24.0; - else if (core.status.replay.speed==6) core.status.replay.speed=12.0; - else if (core.status.replay.speed==3) core.status.replay.speed=6.0; + if (!core.isPlaying() || !core.isReplaying()) return; + if (core.status.replay.speed==12) core.status.replay.speed=24; + else if (core.status.replay.speed==6) core.status.replay.speed=12; + else if (core.status.replay.speed==3) core.status.replay.speed=6; else if (core.status.replay.speed<3) { var toAdd = core.status.replay.speed>=2?2:1; core.status.replay.speed = parseInt(10*core.status.replay.speed + toAdd)/10; @@ -1654,9 +1093,7 @@ control.prototype.speedUpReplay = function () { ////// 减速播放 ////// control.prototype.speedDownReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; + if (!core.isPlaying() || !core.isReplaying()) return; if (core.status.replay.speed==24) core.status.replay.speed=12.0; else if (core.status.replay.speed==12) core.status.replay.speed=6.0; else if (core.status.replay.speed==6) core.status.replay.speed=3.0; @@ -1670,18 +1107,15 @@ control.prototype.speedDownReplay = function () { ////// 设置播放速度 ////// control.prototype.setReplaySpeed = function (speed) { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; + if (!core.isPlaying() || !core.isReplaying()) return; core.status.replay.speed = speed; core.drawTip("x"+core.status.replay.speed+"倍"); } ////// 停止播放 ////// -control.prototype.stopReplay = function () { +control.prototype.stopReplay = function (force) { if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - // if (!core.isReplaying()) return; + if (!core.isReplaying() && !force) return; core.status.replay.toReplay = []; core.status.replay.totalList = []; core.status.replay.replaying=false; @@ -1695,24 +1129,13 @@ control.prototype.stopReplay = function () { ////// 回退 ////// control.prototype.rewindReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; - if (!core.status.replay.pausing) { - core.drawTip("请先暂停录像"); - return; - } - if (core.status.replay.animate) { - core.drawTip("请等待当前事件的处理结束"); - return; - } - if (core.status.replay.save.length==0) { - core.drawTip("无法再回到上一个节点"); - return; - } - - var save = core.status.replay.save; - var data = save.pop(); + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); + if (core.isMoving() || core.status.replay.animate || core.status.event.id) + return core.drawTip("请等待当前事件的处理结束"); + if (core.status.replay.save.length==0) + return core.drawTip("无法再回到上一个节点"); + var save = core.status.replay.save, data = save.pop(); core.loadData(data.data, function () { core.status.replay = { "replaying": true, @@ -1726,22 +1149,15 @@ control.prototype.rewindReplay = function () { } core.updateStatusBar(); core.drawTip("成功回退到上一个节点"); - }) + }); } ////// 回放时存档 ////// control.prototype.saveReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - if (!core.isReplaying()) return; - if (!core.status.replay.pausing) { - core.drawTip("请先暂停录像"); - return; - } - if (core.status.replay.animate || core.isset(core.status.event.id)) { - core.drawTip("请等待当前事件的处理结束"); - return; - } + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); + if (core.isMoving() || core.status.replay.animate || core.status.event.id) + return core.drawTip("请等待当前事件的处理结束"); core.lockControl(); core.status.event.id='save'; @@ -1753,24 +1169,15 @@ control.prototype.saveReplay = function () { ////// 回放时查看怪物手册 ////// control.prototype.bookReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0) return; - if (!core.isReplaying()) return; - if (!core.status.replay.pausing) { - core.drawTip("请先暂停录像"); - return; - } + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); + if (core.isMoving() || core.status.replay.animate + || (core.status.event.id && core.status.event.id != 'viewMaps')) + return core.drawTip("请等待当前事件的处理结束"); // 从“浏览地图”页面打开 - if (core.status.event.id=='viewMaps') { + if (core.status.event.id=='viewMaps') core.status.event.selection = core.status.event.data; - core.status.event.id=null; - } - - if (core.status.replay.animate || core.isset(core.status.event.id)) { - core.drawTip("请等待当前事件的处理结束"); - return; - } core.lockControl(); core.status.event.id='book'; @@ -1779,38 +1186,81 @@ control.prototype.bookReplay = function () { ////// 回放录像时浏览地图 ////// control.prototype.viewMapReplay = function () { - if (!core.isPlaying()) return; - if (core.status.event.id=='save' || (core.status.event.id||"").indexOf('book')==0 || core.status.event.id=='viewMaps') return; - - if (!core.isReplaying()) return; - if (!core.status.replay.pausing) { - core.drawTip("请先暂停录像"); - return; - } - if (core.status.replay.animate || core.isset(core.status.event.id)) { - core.drawTip("请等待当前事件的处理结束"); - return; - } + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); + if (core.isMoving() || core.status.replay.animate || core.status.event.id) + return core.drawTip("请等待当前事件的处理结束"); core.lockControl(); core.status.event.id='viewMaps'; core.ui.drawMaps(); } +////// 是否正在播放录像 ////// +control.prototype.isReplaying = function () { + return (core.status.replay||{}).replaying; +} + ////// 回放 ////// control.prototype.replay = function () { - if (!core.isPlaying()) return; + if (!core.isPlaying() || !core.isReplaying() + || core.status.replay.pausing || core.status.replay.animate || core.status.event.id) return; + if (core.status.replay.toReplay.length==0) + return this._replay_finished(); + this._replay_save(); + var action=core.status.replay.toReplay.shift(); + if (this._doReplayAction(action)) return; + this._replay_error(action); +} - if (!core.isReplaying()) return; // 没有回放 - if (core.status.replay.pausing) return; // 暂停状态 - if (core.status.replay.animate) return; // 正在某段动画中 +////// 注册一个录像行为 ////// +// name:自定义名称,可用于注销使用 +// func:具体执行录像的函数,可为一个函数或插件中的函数名; +// 需要接受一个action参数,代表录像回放时的下一个操作 +// func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 +control.prototype.registerReplayAction = function (name, func) { + this.unregisterReplayAction(name); + this.replayActions.push({name: name, func: func}); +} - if (core.status.replay.toReplay.length==0) { // 回放完毕 - core.stopReplay(); - core.insertAction("录像回放完毕!"); - return; +////// 注销一个录像行为 ////// +control.prototype.unregisterReplayAction = function (name) { + this.replayActions = this.replayActions.filter(function (b) { return b.name != name; }); +} + +////// 执行录像行为,会在注册的函数中依次执行直到得到true为止 ////// +control.prototype._doReplayAction = function (action) { + for (var i in this.replayActions) { + try { + if (core.doFunc(this.replayActions[i].func, this, action)) return true; + } catch (e) { + main.log(e); + main.log("ERROR in replayActions["+this.replayActions[i].name+"]:已自动注销该项。"); + core.unregisterReplayAction(this.replayActions[i].name); + } } + return false; +} +control.prototype._replay_finished = function () { + core.status.replay.replaying = false; + core.status.event.selection = 0; + 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 () { + core.status.replay.replaying = true; + core.ui.closePanel(); + core.pauseReplay(); + }); +} + +control.prototype._replay_save = function () { core.status.replay.steps++; if (core.status.replay.steps%50==0) { if (core.status.replay.save.length == 30) @@ -1822,184 +1272,16 @@ control.prototype.replay = function () { "steps": core.status.replay.steps }}); } +} - var action=core.status.replay.toReplay.shift(); - - if (action == 'input2:===') { - core.replay(); - return; - } - - if (action=='up' || action=='down' || action=='left' || action=='right') { - core.moveHero(action, function () { - setTimeout(function() { - core.replay(); - }); - }); - return; - } - else if (action.indexOf("item:")==0) { - var itemId = action.substring(5); - if (core.canUseItem(itemId)) { - // 是否绘制道具栏 - if (core.material.items[itemId].hideInReplay) { - core.useItem(itemId, false, function () { - core.replay(); - }); - return; - } - else { - var tools = Object.keys(core.status.hero.items.tools).sort(); - var constants = Object.keys(core.status.hero.items.constants).sort(); - var index=-1; - if ((index=tools.indexOf(itemId))>=0) { - core.status.event.data = {"toolsPage":Math.floor(index/12)+1, "constantsPage":1, "selectId":null}; - index = index%12; - } - else if ((index=constants.indexOf(itemId))>=0) { - core.status.event.data = {"toolsPage":1, "constantsPage":Math.floor(index/12)+1, "selectId":null}; - index = index%12+12; - } - if (index>=0) { - core.ui.drawToolbox(index); - setTimeout(function () { - core.ui.closePanel(); - core.useItem(itemId, false, function () { - core.replay(); - }); - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - } - } - else if (action.indexOf("unEquip:")==0) { - var equipType = parseInt(action.substring(8)); - if (core.isset(equipType)) { - core.ui.drawEquipbox(equipType); - core.status.route.push(action); - setTimeout(function () { - core.ui.closePanel(); - core.unloadEquip(equipType, function () { - core.replay(); - }); - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - else if (action.indexOf("equip:")==0) { - var equipId = action.substring(6); - var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); - var index = ownEquipment.indexOf(equipId); - if (index>=0) { - core.status.route.push(action); - core.status.event.data = {"page":Math.floor(index/12)+1, "selectId":null}; - index = index%12+12; - core.ui.drawEquipbox(index); - setTimeout(function () { - core.ui.closePanel(); - core.loadEquip(equipId, function () { - core.replay(); - }); - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - else if (action.indexOf("fly:")==0) { - var floorId=action.substring(4); - var toIndex=core.status.hero.flyRange.indexOf(floorId); - var nowIndex=core.status.hero.flyRange.indexOf(core.status.floorId); - if (core.hasItem('fly') && toIndex>=0 && nowIndex>=0) { - core.ui.drawFly(toIndex); - setTimeout(function () { - if (!core.control.flyTo(floorId, function () {core.replay()})) { - core.stopReplay(); - core.insertAction("录像文件出错"); - } - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - else if (action.indexOf("shop:")==0) { - var sps=action.substring(5).split(":"); - var shopId=sps[0], selections=sps[1].split(""); - - if (selections.length>0) { - var shop=core.status.shops[shopId]; - if (core.isset(shop) && shop.visited) { // 商店可用 - var choices = shop.choices; - var topIndex = 6 - parseInt(choices.length / 2); - - core.status.event.selection = parseInt(selections.shift()); - - core.events.openShop(shopId, false); - var shopInterval = setInterval(function () { - if (!core.actions._clickShop(6, topIndex+core.status.event.selection)) { - clearInterval(shopInterval); - core.stopReplay(); - core.drawTip("录像文件出错"); - return; - } - if (selections.length==0) { - clearInterval(shopInterval); - core.actions._clickShop(6, topIndex+choices.length); - core.replay(); - return; - } - core.status.event.selection = parseInt(selections.shift()); - core.events.openShop(shopId, false); - - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - } - else if (action=='turn') { - core.turnHero(); - core.replay(); - return; - } - else if (action.indexOf("turn:")==0) { - core.turnHero(action.substring(5)); - core.replay(); - return; - } - else if (action=='getNext') { - if (core.getNextItem()) { - core.replay(); - return; - } - } - else if (action.indexOf('move:')==0) { - while (core.status.replay.toReplay.length>0 && - core.status.replay.toReplay[0].indexOf('move:')==0) { - action = core.status.replay.toReplay.shift(); - } - - var pos=action.substring(5).split(":"); - var x=parseInt(pos[0]), y=parseInt(pos[1]); - var nowx=core.getHeroLoc('x'), nowy=core.getHeroLoc('y'); - if (core.control.moveDirectly(x,y)) { - core.ui.drawArrow('ui', 32*nowx+16-core.bigmap.offsetX, 32*nowy+16-core.bigmap.offsetY, 32*x+16-core.bigmap.offsetX, 32*y+16-core.bigmap.offsetY, '#FF0000', 3); - setTimeout(function () { - core.clearMap('ui'); - core.replay(); - }, 750 / Math.max(1, core.status.replay.speed)); - return; - } - } - else if (action.indexOf('key:')==0) { - core.actions.keyUp(parseInt(action.substring(4)), false, true); - core.replay(); - return; - } - - // core.stopReplay(); - // core.insertAction("录像文件出错"); - +control.prototype._replay_error = function (action) { core.status.replay.replaying = false; - main.log("录像文件出错,当前操作:"+action+ - "\n接下来10个操作是:"+core.status.replay.toReplay.slice(0, 10).toString()); + var len = core.status.replay.toReplay.length; + var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1); + var nextList = core.status.replay.toReplay.slice(0, 10); + main.log("录像文件出错,当前操作:" + action); + main.log("之前的10个操作是:\n" + prevList.toString()); + main.log("接下来10个操作是:\n" + nextList.toString()); core.ui.drawConfirmBox("录像文件出错,你想回到上个节点吗?", function () { core.ui.closePanel(); if (core.status.replay.save.length > 0) { @@ -2009,204 +1291,196 @@ control.prototype.replay = function () { } else { core.drawTip("无法回到上一个节点"); - core.stopReplay(); + core.stopReplay(true); } }, function () { core.ui.closePanel(); - core.stopReplay(); + core.stopReplay(true); }); - } -control.prototype.isReplaying = function () { - return (core.status.replay||{}).replaying; +control.prototype.__replay_getTimeout = function () { + return 750 / Math.max(1, core.status.replay.speed); } -////// 判断当前能否进入某个事件 ////// -control.prototype.checkStatus = function (name, need, item) { - if (need && core.status.event.id == name) { - core.ui.closePanel(); - return false; - } - - if (need && core.status.lockControl) return false; - if (core.isset(item) && item && !core.hasItem(name)) { - core.drawTip("你没有" + core.material.items[name].name); - return false; - } - if (!core.status.heroStop) { - core.drawTip("请先停止勇士行动"); - return false; - } - - core.lockControl(); - core.status.event.id = name; +control.prototype._replayAction_move = function (action) { + if (["up","down","left","right"].indexOf(action)<0) return false; + core.moveHero(action, function () { + setTimeout(core.replay); + }); return true; } -////// 点击怪物手册时的打开操作 ////// -control.prototype.openBook = function (need) { - if (core.isReplaying()) return; +control.prototype._replayAction_item = function (action) { + if (action.indexOf("item:")!=0) return false; + var itemId = action.substring(5); + if (!core.canUseItem(itemId)) return false; + if (core.material.items[itemId].hideInReplay) { + core.useItem(itemId, false, core.replay); + return true; + } + var tools = Object.keys(core.status.hero.items.tools).sort(); + var constants = Object.keys(core.status.hero.items.constants).sort(); + var index, per = core.__SIZE__-1; + if ((index=tools.indexOf(itemId))>=0) { + core.status.event.data = {"toolsPage": Math.floor(index/per)+1, "constantsPage":1}; + index = index%per; + } + else if ((index=constants.indexOf(itemId))>=0) { + core.status.event.data = {"toolsPage": 1, "constantsPage": Math.floor(index/per)+1}; + index = index%per+per; + } + if (index<0) return false; + core.ui.drawToolbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.useItem(itemId, false, core.replay); + }, core.control.__replay_getTimeout()); + return true; +} - if (core.status.event.id == 'book' && core.events.recoverEvents(core.status.event.interval)) { - return; +control.prototype._replayAction_equip = function (action) { + if (action.indexOf("equip:")!=0) return false; + var equipId = action.substring(6); + var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); + var index = ownEquipment.indexOf(equipId), per = core.__SIZE__-1; + if (index<0) return false; + core.status.route.push(action); + core.status.event.data = {"page":Math.floor(index/per)+1, "selectId":null}; + index = index%per+per; + core.ui.drawEquipbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.loadEquip(equipId, core.replay); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_unEquip = function (action) { + if (action.indexOf("unEquip:")!=0) return false; + var equipType = parseInt(action.substring(8)); + if (!core.isset(equipType)) return false; + core.ui.drawEquipbox(equipType); + core.status.route.push(action); + setTimeout(function () { + core.ui.closePanel(); + core.unloadEquip(equipType, core.replay); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_fly = function (action) { + if (action.indexOf("fly:")!=0) return false; + var floorId=action.substring(4); + var toIndex=core.floorIds.indexOf(floorId); + if (!core.canUseItem('fly')) return false; + core.ui.drawFly(toIndex); + setTimeout(function () { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_shop = function (action) { + if (action.indexOf("shop:")!=0) return false; + var sps=action.substring(5).split(":"); + var shopId=sps[0], selections=sps[1].split(""); + if (selections.length == 0) return false; + var shop=core.status.shops[shopId]; + if (!shop || !shop.visited) return false; + // --- 判定commonEvent + if (shop.commonEvent) { + core.openShop(shopId, false); + setTimeout(core.replay); + return true; + } + var choices = shop.choices; + var topIndex = core.__HALF_SIZE__ - parseInt(choices.length / 2); + core.status.event.selection = parseInt(selections.shift()); + core.events.openShop(shopId, false); + var shopInterval = setInterval(function () { + if (!core.actions._clickShop(core.__HALF_SIZE__, topIndex+core.status.event.selection)) { + clearInterval(shopInterval); + core.stopReplay(); + core.drawTip("录像文件出错"); + return; + } + if (selections.length==0) { + clearInterval(shopInterval); + core.actions._clickShop(core.__HALF_SIZE__, topIndex+choices.length); + core.replay(); + return; + } + core.status.event.selection = parseInt(selections.shift()); + core.events.openShop(shopId, false); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_turn = function (action) { + if (action != 'turn' && action.indexOf('turn:') != 0) return false; + if (action == 'turn') core.turnHero(); + else core.turnHero(action.substring(5)); + setTimeout(core.replay); + return true; +} + +control.prototype._replayAction_getNext = function (action) { + if (action != "getNext") return false; + if (!core.getNextItem()) return false; + setTimeout(core.replay); + return true; +} + +control.prototype._replayAction_moveDirectly = function (action) { + if (action.indexOf("move:")!=0) return false; + // 忽略连续的瞬移事件 + 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(); } - // 当前是book,且从“浏览地图”打开 - if (core.status.event.id == 'book' && core.isset(core.status.event.ui)) { - core.status.boxAnimateObjs = []; - core.ui.drawMaps(core.status.event.ui); - return; - } - - // 从“浏览地图”页面打开 - if (core.status.event.id=='viewMaps') { - need=false; - core.status.event.ui = core.status.event.data; - } - - if (!core.checkStatus('book', need, true)) - return; - core.useItem('book', true); + var pos=action.substring(5).split(":"); + var x=parseInt(pos[0]), y=parseInt(pos[1]); + var nowx=core.getHeroLoc('x'), nowy=core.getHeroLoc('y'); + if (!core.moveDirectly(x, y)) return false; + core.ui.drawArrow('ui', 32*nowx+16-core.bigmap.offsetX, 32*nowy+16-core.bigmap.offsetY, + 32*x+16-core.bigmap.offsetX, 32*y+16-core.bigmap.offsetY, '#FF0000', 3); + setTimeout(function () { + core.clearMap('ui'); + core.replay(); + }, core.control.__replay_getTimeout()); + return true; } -////// 点击楼层传送器时的打开操作 ////// -control.prototype.useFly = function (need) { - if (core.isReplaying()) return; - if (!core.checkStatus('fly', need, true)) - return; - if (core.flags.flyNearStair && !core.nearStair()) { - core.drawTip("只有在楼梯边才能使用传送器"); - core.unLockControl(); - core.status.event.data = null; - core.status.event.id = null; - return; - } - if (!core.canUseItem('fly')) { - core.drawTip("楼层传送器好像失效了"); - core.unLockControl(); - core.status.event.data = null; - core.status.event.id = null; - return; - } - core.useItem('fly', true); - return; +control.prototype._replayAction_key = function (action) { + if (action.indexOf("key:") != 0) return false; + core.actions.keyUp(parseInt(action.substring(4)), false, true); + setTimeout(core.replay); + return true; } -control.prototype.flyTo = function (toId, callback) { - return this.controldata.flyTo(toId, callback); -} - -////// 点击装备栏时的打开操作 ////// -control.prototype.openEquipbox = function (need) { - if (core.isReplaying()) return; - if (!core.checkStatus('equipbox', need)) - return; - core.ui.drawEquipbox(); -} - -////// 点击工具栏时的打开操作 ////// -control.prototype.openToolbox = function (need) { - if (core.isReplaying()) return; - if (!core.checkStatus('toolbox', need)) - return; - core.ui.drawToolbox(); -} - -////// 点击快捷商店按钮时的打开操作 ////// -control.prototype.openQuickShop = function (need) { - if (core.isReplaying()) return; - if (!core.checkStatus('selectShop', need)) - return; - core.ui.drawQuickShop(); -} - -control.prototype.openKeyBoard = function (need) { - if (core.isReplaying()) return; - - if (!core.checkStatus('keyBoard', need)) - return; - core.ui.drawKeyBoard(); -} - -////// 点击保存按钮时的打开操作 ////// -control.prototype.save = function(need) { - if (core.isReplaying()) return; - - if (core.status.event.id == 'save' && core.events.recoverEvents(core.status.event.interval)) { - return; - } - - if (!core.checkStatus('save', need)) - return; - - var saveIndex = core.saves.saveIndex; - var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; - - core.ui.drawSLPanel(10*page+offset); -} - -////// 点击读取按钮时的打开操作 ////// -control.prototype.load = function (need) { - if (core.isReplaying()) return; - - var saveIndex = core.saves.saveIndex; - var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; - - // 游戏开始前读档 - if (!core.isPlaying()) { - 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); - return; - } - - if (core.status.event.id == 'load' && core.events.recoverEvents(core.status.event.interval)) { - return; - } - - if (!core.checkStatus('load', need)) - return; - core.ui.drawSLPanel(10*page+offset); -} - -////// 点击设置按钮时的操作 ////// -control.prototype.openSettings = function (need) { - if (core.isReplaying()) return; - if (!core.checkStatus('settings', need)) - return; - core.ui.drawSettings(); -} +// ------ 存读档相关 ------ // ////// 自动存档 ////// control.prototype.autosave = function (removeLast) { if (core.status.event.id!=null) return; var x=null; - if (removeLast) - x=core.status.route.pop(); + if (removeLast) x=core.status.route.pop(); core.status.route.push("turn:"+core.getHeroLoc('direction')); - // core.setLocalForage("autoSave", core.saveData()); - // ----- Add to autosaveData core.saves.autosave.data = core.saveData(); core.saves.autosave.updated = true; core.saves.ids[0] = true; - // ----- Updated every 5s core.status.route.pop(); - if (removeLast && core.isset(x)) - core.status.route.push(x); + if (x) core.status.route.push(x); } /////// 实际进行自动存档 ////// control.prototype.checkAutosave = function () { - if (!core.animateFrame || !core.saves || !core.saves.autosave) return; - core.setLocalStorage('totalTime', core.animateFrame.totalTime); - if (core.saves.autosave.data == null || !core.saves.autosave.updated) return; core.saves.autosave.updated = false; core.setLocalForage("autoSave", core.saves.autosave.data); @@ -2214,168 +1488,154 @@ control.prototype.checkAutosave = function () { ////// 实际进行存读档事件 ////// control.prototype.doSL = function (id, type) { - if (type=='save') { - if (id=='autoSave') { - core.drawTip('不能覆盖自动存档!'); - return; - } - // 事件中的存档 - if (core.status.event.interval != null) { - core.setFlag("__events__", core.status.event.interval); - } - core.setLocalForage("save"+id, core.saveData(), function() { - if (id!="autoSave") { - core.saves.saveIndex=id; - core.setLocalStorage('saveIndex', core.saves.saveIndex); - } - if (core.events.recoverEvents(core.status.event.interval)) { - core.drawTip("存档成功!"); - return; - } + switch (type) { + case 'save': this._doSL_save(id); break; + case 'load': this._doSL_load(id); break; + case 'replayLoad': this._doSL_replayLoad(id); break; + } +} + +control.prototype._doSL_save = function (id) { + if (id=='autoSave') return core.drawTip('不能覆盖自动存档!'); + // 在事件中的存档 + if (core.status.event.interval != null) + core.setFlag("__events__", core.status.event.interval); + core.setLocalForage("save"+id, core.saveData(), function() { + core.saves.saveIndex=id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); + // 恢复事件 + if (!core.events.recoverEvents(core.status.event.interval)) core.ui.closePanel(); - core.drawTip('存档成功!'); + core.drawTip('存档成功!'); + }, function(err) { + main.log(err); + if (core.platform.useLocalForage) { + alert("存档失败,错误信息:\n"+err); + } + else { + alert("存档失败,错误信息:\n"+err+"\n建议使用垃圾存档清理工具进行清理!"); + } + }); + core.removeFlag("__events__"); + return; +} + +control.prototype._doSL_load = function (id) { + if (id == 'autoSave' && core.saves.autosave.data != null) { + this._doSL_load_afterGet(id, core.clone(core.saves.autosave.data)); + } + else { + core.getLocalForage(id=='autoSave'?id:"save"+id, null, function(data) { + if (id == 'autoSave') core.saves.autosave.data = core.clone(data); + core.control._doSL_load_afterGet(id, data); }, function(err) { - console.info(err); - if (core.platform.useLocalForage) { - alert("存档失败,错误信息:\n"+err); - } - else { - alert("存档失败,错误信息:\n"+err+"\n建议使用垃圾存档清理工具进行清理!"); - } - }); - core.removeFlag("__events__"); + main.log(err); + alert("无效的存档"); + }) + } + return; +} + +control.prototype._doSL_load_afterGet = function (id, data) { + if (!data) return alert("无效的存档"); + var _replay = function () { + core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); + }; + if (core.flags.checkConsole && data.hashCode != null && data.hashCode != core.hashCode(data.hero)) { + core.myconfirm("存档校验失败,请勿修改存档文件!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。", _replay); return; } - else if (type=='load') { - var afterGet = function (data) { - if (!core.isset(data)) { - alert("无效的存档"); - return; - } - if (core.flags.checkConsole && core.isset(data.hashCode) && data.hashCode != core.utils.hashCode(data.hero)) { - if (confirm("存档校验失败,请勿修改存档文件!\n你想回放此存档的录像吗?")) { - core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); - } - return; - } - if (data.version != core.firstData.version) { - // core.drawTip("存档版本不匹配"); - if (confirm("存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。")) { - core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); - } - return; - } - core.ui.closePanel(); - core.loadData(data, function() { - core.drawTip("读档成功"); - if (id!="autoSave") { - core.saves.saveIndex=id; - core.setLocalStorage('saveIndex', core.saves.saveIndex); - } - }); - } - if (id == 'autoSave' && core.saves.autosave.data != null) { - afterGet(core.clone(core.saves.autosave.data)); - } - else { - core.getLocalForage(id=='autoSave'?id:"save"+id, null, function(data) { - if (id == 'autoSave') core.saves.autosave.data = core.clone(data); - afterGet(data); - }, function(err) { - main.log(err); - alert("无效的存档"); - }) - } + if (data.version != core.firstData.version) { + core.myconfirm("存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。", _replay); return; } - else if (type == 'replayLoad') { - var afterGet = function (data) { - if (!core.isset(data)) { - core.drawTip("无效的存档"); - return; - } - if (data.version != core.firstData.version) { - core.drawTip("存档版本不匹配"); - return; - } - if (data.hard != core.status.hard) { - core.drawTip("游戏难度不匹配!"); - return; - } - if (core.isset(data.hashCode) && data.hashCode != core.utils.hashCode(data.hero)) { - alert("存档校验失败,请勿修改存档文件!"); - return; - } - var route = core.subarray(core.status.route, core.decodeRoute(data.route)); - if (!core.isset(route) || data.hero.flags.__seed__!=core.getFlag('__seed__')) { - core.drawTip("无法从此存档回放录像"); - return; - } - core.loadData(data, function () { - core.startReplay(route); - core.drawTip("回退到存档节点"); - }); - } - if (id == 'autoSave' && core.saves.autosave.data != null) { - afterGet(core.clone(core.saves.autosave.data)); - } - else { - core.getLocalForage(id=='autoSave'?id:"save"+id, null, function(data) { - if (id == 'autoSave') core.saves.autosave.data = core.clone(data); - afterGet(data); - }, function(err) { - main.log(err); - alert("无效的存档"); - }) + core.ui.closePanel(); + core.loadData(data, function() { + core.drawTip("读档成功"); + if (id!="autoSave") { + core.saves.saveIndex=id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); } + }); + +} + +control.prototype._doSL_replayLoad = function (id) { + if (id == 'autoSave' && core.saves.autosave.data != null) { + this._doSL_replayLoad_afterGet(core.clone(core.saves.autosave.data)); } + else{ + core.getLocalForage(id=='autoSave'?id:"save"+id, null, function(data) { + if (id == 'autoSave') core.saves.autosave.data = core.clone(data); + core.control._doSL_replayLoad_afterGet(id, data); + }, function(err) { + main.log(err); + alert("无效的存档"); + }) + } +} + +control.prototype._doSL_replayLoad_afterGet = function (id, data) { + if (!data) return core.drawTip("无效的存档"); + if (data.version != core.firstData.version) return core.drawTip("存档版本不匹配"); + if (data.hard != core.status.hard) core.drawTip("游戏难度不匹配!"); + if (data.hashCode != null && data.hashCode != core.utils.hashCode(data.hero)) + return alert("存档校验失败,请勿修改存档文件!"); + var route = core.subarray(core.status.route, core.decodeRoute(data.route)); + if (route == null || data.hero.flags.__seed__ != core.getFlag('__seed__')) + return core.drawTip("无法从此存档回放录像"); + core.loadData(data, function () { + core.startReplay(route); + core.drawTip("回退到存档节点"); + }); + } ////// 同步存档到服务器 ////// control.prototype.syncSave = function (type) { core.ui.drawWaiting("正在同步,请稍后..."); - core.control.getSaves(type=='all'?null:core.saves.saveIndex, function (saves) { - if (!core.isset(saves)) { - core.drawText("没有要同步的存档"); - return; + 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); + formData.append('data', JSON.stringify(saves)); + + core.http("POST", "/games/sync.php", formData, function (data) { + var response = JSON.parse(data); + if (response.code<0) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+response.msg); } - - var formData = new FormData(); - formData.append('type', 'save'); - formData.append('name', core.firstData.name); - var save_text = JSON.stringify(saves); - formData.append('data', save_text); - - core.http("POST", "/games/sync.php", formData, function (data) { - var response = JSON.parse(data); - if (response.code<0) { - core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+response.msg); - } - else { - core.drawText((type=='all'?"所有存档":"存档"+core.saves.saveIndex)+"同步成功!\n\n您的存档编号: " - +response.code+"\n您的存档密码: "+response.msg - +"\n\n请牢记以上两个信息(如截图等),在从服务器\n同步存档时使用。") - } - }, function (e) { - core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+e); - }) + else { + core.drawText((type=='all'?"所有存档":"存档"+core.saves.saveIndex)+"同步成功!\n\n您的存档编号: " + +response.code+"\n您的存档密码: "+response.msg + +"\n\n请牢记以上两个信息(如截图等),在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r") + } + }, function (e) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:"+e); }) } ////// 从服务器加载存档 ////// control.prototype.syncLoad = function () { - core.interval.onDownInterval = 'tmp'; - var id = prompt("请输入存档编号:"); - if (id==null || id=="") { - core.ui.drawSyncSave(); return; - } - core.interval.onDownInterval = 'tmp'; - var password = prompt("请输入存档密码:"); - if (password==null || password=="") { - core.ui.drawSyncSave(); return; - } - core.ui.drawWaiting("正在同步,请稍后..."); + core.myprompt("请输入存档编号", null, function (id) { + if (!id) return core.ui.drawSyncSave(); + core.myprompt("请输入存档密码:", null, function (password) { + if (!password) return core.ui.drawSyncSave(); + core.ui.drawWaiting("正在同步,请稍后..."); + core.control._syncLoad_http(id, password); + }); + }); +} +control.prototype._syncLoad_http = function (id, password) { var formData = new FormData(); formData.append('type', 'load'); formData.append('name', core.firstData.name); @@ -2384,257 +1644,277 @@ control.prototype.syncLoad = function () { core.http("POST", "/games/sync.php", formData, function (data) { var response = JSON.parse(data); - switch (response.code) { - case 0: - // 成功 - var data=JSON.parse(response.msg); - if (data instanceof Array) { - core.status.event.selection=1; - core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { - for (var i=1;i<=5*(main.savePages||30);i++) { - if (i<=data.length) { - // core.setLocalStorage("save"+i, data[i-1]); - core.setLocalForage("save"+i, data[i-1]); - } - else { - if (core.saves.ids[i]) - core.removeLocalForage("save"+i); - } - } - core.ui.closePanel(); - core.drawText("同步成功!\n你的本地所有存档均已被覆盖。"); - }, function () { - core.status.event.selection=0; - core.ui.drawSyncSave(); - }) - } - else { - // 只覆盖单存档 - core.setLocalForage("save"+core.saves.saveIndex, data, function() { - core.drawText("同步成功!\n单存档已覆盖至存档"+core.saves.saveIndex); - }); - } - break; - case -1: - core.drawText("出错啦!\n存档编号"+id+"不存在!"); - break; - case -2: - core.drawText("出错啦!\n存档密码错误!"); - break; - default: - core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+response.msg); - break; + if (response.code == 0) { + core.control._syncLoad_write(JSON.parse(response.msg)); + } + else { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+response.msg); } }, function (e) { core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:"+e); }); } +control.prototype._syncLoad_write = function (data) { + if (data instanceof Array) { + core.status.event.selection=1; + core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { + for (var i=1;i<=5*(main.savePages||30);i++) { + if (i<=data.length) + core.setLocalForage("save"+i, data[i-1]); + else if (core.saves.ids[i]) + core.removeLocalForage("save"+i); + } + core.ui.closePanel(); + core.drawText("同步成功!\n你的本地所有存档均已被覆盖。"); + }, function () { + core.status.event.selection=0; + core.ui.drawSyncSave(); + }); + } + else { + // 只覆盖单存档 + core.setLocalForage("save"+core.saves.saveIndex, data, function() { + core.drawText("同步成功!\n单存档已覆盖至存档"+core.saves.saveIndex); + }); + } +} + ////// 存档到本地 ////// control.prototype.saveData = function() { - var hero = core.clone(core.status.hero); - var hashCode = core.utils.hashCode(hero); - - var data = { - 'floorId': core.status.floorId, - 'hero': hero, - 'hard': core.status.hard, - 'maps': core.maps.saveMap(), - 'route': core.encodeRoute(core.status.route), - 'values': core.clone(core.values), - 'shops': {}, - 'version': core.firstData.version, - "time": new Date().getTime(), - "hashCode": hashCode - }; - // set shop times - for (var shop in core.status.shops) { - data.shops[shop]={ - 'times': core.status.shops[shop].times || 0, - 'visited': core.status.shops[shop].visited || false - } - } - core.events.beforeSaveData(data); - - return data; + return this.controldata.saveData(); } ////// 从本地读档 ////// control.prototype.loadData = function (data, callback) { - - core.resetStatus(data.hero, data.hard, data.floorId, core.decodeRoute(data.route), core.maps.loadMap(data.maps), - data.values); - - // load shop times - for (var shop in core.status.shops) { - if (core.isset(data.shops[shop])) { - core.status.shops[shop].times = data.shops[shop].times; - core.status.shops[shop].visited = data.shops[shop].visited; - } - } - - core.status.textAttribute = core.getFlag('textAttribute', core.status.textAttribute); - var toAttribute = core.getFlag('globalAttribute', core.status.globalAttribute); - // if (core.utils.hashCode(toAttribute) != core.utils.hashCode(core.status.globalAttribute)) { - if (!core.same(toAttribute, core.status.globalAttribute)) { - core.status.globalAttribute = toAttribute; - core.control.updateGlobalAttribute(Object.keys(toAttribute)); - } - - // 重置音量 - core.events.setVolume(core.getFlag("__volume__", 1), 0); - - // load icons - var icon = core.getFlag("heroIcon", "hero.png"); - if (core.isset(core.material.images.images[icon])) { - core.material.images.hero.src = core.material.images.images[icon].src; - core.material.icons.hero.height = core.material.images.images[icon].height/4; - } - - core.events.afterLoadData(data); - - core.changeFloor(data.floorId, null, data.hero.loc, 0, function() { - if (core.isset(callback)) callback(); - }, true); + return this.controldata.loadData(data, callback); } -control.prototype.getSaves = function (index, callback) { - if (core.isset(index)) { - core.getLocalForage("save"+index, null, function(data) { - if (core.isset(callback)) callback(data); - }, function(err) { - main.log(err); - if (core.isset(callback)) +control.prototype.getSave = function (index, callback) { + if (index == 0) { + // --- 自动存档先从缓存中获取 + if (core.saves.autosave.data != null) + callback(core.clone(core.saves.autosave.data)); + else { + core.getLocalForage("autoSave", null, function(data) { + callback(data); + }, function(err) { + main.log(err); callback(null); - }) + }); + } return; } + core.getLocalForage("save"+index, null, function(data) { + if (callback) callback(data); + }, function(err) { + main.log(err); + if (callback) callback(null); + }); +} - var ids = Object.keys(core.saves.ids).filter(function(x){return x!=0;}) - .sort(function(a,b) {return a-b;}), number = ids.length; - - // 不计0 - var saves = []; - - var load = function (index, callback) { - if (index > number) { - if (core.isset(callback)) callback(saves); - return; - } - core.getLocalForage("save"+ids[index], null, function (data) { - saves.push(data); - load(index+1, callback); - }, function(err) { - main.log(err); - load(index+1, callback); - }) +control.prototype.getSaves = function (ids, callback) { + if (!(ids instanceof Array)) return this.getSave(ids, callback); + var count = ids.length, data = {}; + for (var i = 0; i < ids.length; ++i) { + (function (i) { + core.getSave(ids[i], function (result) { + data[i] = result; + if (Object.keys(data).length == count) + callback(data); + }) + })(i); } - load(0, 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) { + for (var i = 0; i < ids.length; ++i) { + if (data[i] != null) + saves.push(data[i]); + } + callback(saves); + }); } ////// 获得所有存在存档的存档位 ////// control.prototype.getSaveIndexes = function (callback) { var indexes = {}; - - var getIndex = function (name) { - var e = new RegExp('^'+core.firstData.name+"_(save\\d+|autoSave)$").exec(name); - if (e!=null) { - if (e[1]=='autoSave') indexes[0]=true; - else indexes[parseInt(e[1].substring(4))] = true; - } - }; - - if (!core.platform.useLocalForage) { + if (core.platform.useLocalForage) { + localforage.iterate(function (value, key, n) { + core.control._getSaveIndexes_getIndex(indexes, key); + }, function () { + callback(indexes); + }); + } + else { Object.keys(localStorage).forEach(function (key) { - getIndex(key); + core.control._getSaveIndexes_getIndex(indexes, key); }); callback(indexes); } - else { - localforage.iterate(function (value, key, n) { - getIndex(key) - }, function () { - callback(indexes); - }) +} + +control.prototype._getSaveIndexes_getIndex = function (indexes, name) { + var e = new RegExp('^'+core.firstData.name+"_(save\\d+|autoSave)$").exec(name); + if (e) { + if (e[1]=='autoSave') indexes[0]=true; + else indexes[parseInt(e[1].substring(4))] = true; } } ////// 判断某个存档位是否存在存档 ////// control.prototype.hasSave = function (index) { - return core.saves.ids[index]||false; + return core.saves.ids[index] || false; } +////// 删除某个存档 +control.prototype.removeSave = function (index, callback) { + if (index == 0 || index == "autoSave") { + index = "autoSave"; + core.removeLocalForage(index, function () { + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + if (callback) callback(); + }); + return; + } + core.removeLocalForage("save" + index, function () { + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + delete core.saves.favoriteName[index]; + core.control._updateFavoriteSaves(); + if (callback) callback(); + }, function () { + core.drawTip("无法删除存档!"); + if (callback) callback(); + }); +} + +////// 读取收藏信息 +control.prototype._loadFavoriteSaves = function () { + core.saves.favorite = core.getLocalStorage("favorite", []); + // --- 移除不存在的收藏 + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + core.saves.favoriteName = core.getLocalStorage("favoriteName", {}); +} + +control.prototype._updateFavoriteSaves = function () { + core.setLocalStorage("favorite", core.saves.favorite); + core.setLocalStorage("favoriteName", core.saves.favoriteName); +} + +// ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // + ////// 设置勇士属性 ////// -control.prototype.setStatus = function (statusName, statusVal) { - if (!core.isset(core.status.hero)) return; - if (statusName == 'exp') statusName = 'experience'; - if (core.isset(core.status.hero.loc[statusName])) - core.status.hero.loc[statusName] = statusVal; +control.prototype.setStatus = function (name, value) { + if (!core.status.hero) return; + if (name == 'exp') name = 'experience'; + if (name == 'x' || name == 'y' || name == 'direction') + this.setHeroLoc(name, value); else - core.status.hero[statusName] = statusVal; + core.status.hero[name] = value; +} + +////// 增减勇士属性 ////// +control.prototype.addStatus = function (name, value) { + this.setStatus(name, this.getStatus(name) + value); } ////// 获得勇士属性 ////// control.prototype.getStatus = function (name) { - if (!core.isset(core.status.hero)) return null; - // support status:x - if (core.isset(core.status.hero.loc[name])) - return core.status.hero.loc[name]; + if (!core.status.hero) return null; + if (name == 'x' || name == 'y' || name == 'direction') + return this.getHeroLoc(name); if (name == 'exp') name = 'experience'; return core.status.hero[name]; } +////// 从status中获得属性,如果不存在则从勇士属性中获取 ////// control.prototype.getStatusOrDefault = function (status, name) { - if (core.isset(status) && name in status) + if (status && name in status) return status[name]; return this.getStatus(name); } +////// 获得勇士实际属性(增幅后的) ////// control.prototype.getRealStatus = function (name) { return this.getRealStatusOrDefault(null, name); } +////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 ////// control.prototype.getRealStatusOrDefault = function (status, name) { - return this.getStatusOrDefault(status, name) * core.getFlag('__'+name+'_buff__', 1); + return this.getStatusOrDefault(status, name) * this.getBuff(name); +} + +////// 设置某个属性的增幅值 ////// +control.prototype.setBuff = function (name, value) { + this.setFlag('__'+name+'_buff__', value); +} + +////// 加减某个属性的增幅值 ////// +control.prototype.addBuff = function (name, value) { + this.setFlag('__'+name+'_buff__', this.getBuff(name) + value); +} + +////// 获得某个属性的增幅值 ////// +control.prototype.getBuff = function (name) { + return core.getFlag('__'+name+'_buff__', 1); +} + +////// 设置勇士的位置 ////// +control.prototype.setHeroLoc = function (name, value, noGather) { + if (!core.status.hero) return; + core.status.hero.loc[name] = value; + if ((name=='x' || name=='y') && !noGather) { + this.gatherFollowers(); + } +} + +////// 获得勇士的位置 ////// +control.prototype.getHeroLoc = function (name) { + if (!core.status.hero) return; + if (name == null) return core.status.hero.loc; + return core.status.hero.loc[name]; } ////// 获得某个等级的名称 ////// -control.prototype.getLvName = function () { - if (!core.isset(core.status.hero)) return null; - return ((core.firstData.levelUp||[])[core.status.hero.lv-1]||{}).title || core.status.hero.lv; +control.prototype.getLvName = function (lv) { + if (!core.status.hero) return null; + if (lv == null) lv = core.status.hero.lv; + return ((core.firstData.levelUp||[])[lv-1]||{}).title || lv; } ////// 设置某个自定义变量或flag ////// -control.prototype.setFlag = function(flag, value) { - if (!core.isset(value)) return this.removeFlag(flag); - if (!core.isset(core.status.hero)) return; - core.status.hero.flags[flag]=value; -} - -////// 获得某个自定义变量或flag ////// -control.prototype.getFlag = function(flag, defaultValue) { - if (!core.isset(core.status.hero)) return defaultValue; - var value = core.status.hero.flags[flag]; - if (core.isset(value)) return value; - return defaultValue; -} - -////// 是否存在某个自定义变量或flag,且值为true ////// -control.prototype.hasFlag = function(flag) { - if (core.getFlag(flag)) return true; - return false; -} - -////// 删除某个自定义变量或flag ////// -control.prototype.removeFlag = function(flag) { - if (!core.isset(core.status.hero)) return; - delete core.status.hero.flags[flag]; +control.prototype.setFlag = function(name, value) { + if (value == null) return this.removeFlag(name); + if (!core.status.hero) return; + core.status.hero.flags[name]=value; } ////// 增加某个flag数值 ////// -control.prototype.addFlag = function(flag, delta) { - if (!core.isset(core.status.hero)) return; - core.setFlag(flag, core.getFlag(flag, 0) + delta); +control.prototype.addFlag = function(name, value) { + if (!core.status.hero) return; + core.setFlag(name, core.getFlag(name, 0) + value); +} + +////// 获得某个自定义变量或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; +} + +////// 是否存在某个自定义变量或flag,且值为true ////// +control.prototype.hasFlag = function(name) { + return !!core.getFlag(name); +} + +////// 删除某个自定义变量或flag ////// +control.prototype.removeFlag = function(name) { + if (!core.status.hero) return; + delete core.status.hero.flags[name]; } ////// 锁定状态栏,常常用于事件处理 ////// @@ -2647,11 +1927,137 @@ control.prototype.unLockControl = function () { core.status.lockControl = false; } +////// 开启debug模式 ////// +control.prototype.debug = function() { + core.setFlag('debug', true); + core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n同时,录像将失效,也无法上传成绩。"); +} + +// ------ 天气,色调,BGM ------ // + +////// 更改天气效果 ////// +control.prototype.setWeather = function (type, level) { + // 非雨雪 + if (type == null) { + core.deleteCanvas('weather') + core.animateFrame.weather.type = null; + core.animateFrame.weather.nodes = []; + return; + } + // 当前天气:则忽略 + if (type==core.animateFrame.weather.type && level == null) return; + level = core.clamp(parseInt(level) || 5, 1, 10); + level *= parseInt(20*core.bigmap.width*core.bigmap.height/(core.__SIZE__*core.__SIZE__)); + + // 计算当前的宽高 + core.createCanvas('weather', 0, 0, core.__PIXELS__, core.__PIXELS__, 80); + core.animateFrame.weather.type = type; + core.animateFrame.weather.nodes = []; + this._setWeather_createNodes(type, level); +} + +control.prototype._setWeather_createNodes = function (type, level) { + switch (type) { + case 'rain': + for (var a=0;a 1) + core.screenFlash(color, time * 3, times - 1, callback); + else { + if (callback) callback(); + } + }); + }); +} + ////// 播放背景音乐 ////// control.prototype.playBgm = function (bgm, startTime) { - if (main.mode!='play')return; - // 音频不存在 - if (!core.isset(core.material.bgms[bgm])) return; + if (main.mode!='play' || !core.material.bgms[bgm]) return; // 如果不允许播放 if (!core.musicStatus.bgmStatus) { try { @@ -2667,22 +2073,7 @@ control.prototype.playBgm = function (bgm, startTime) { this.setMusicBtn(); try { - // 缓存BGM - core.loader.loadBgm(bgm); - // 如果当前正在播放,且和本BGM相同,直接忽略 - if (core.musicStatus.playingBgm == bgm && !core.material.bgms[core.musicStatus.playingBgm].paused) { - return; - } - // 如果正在播放中,暂停 - if (core.isset(core.musicStatus.playingBgm)) { - core.material.bgms[core.musicStatus.playingBgm].pause(); - } - // 播放当前BGM - core.material.bgms[bgm].volume = core.musicStatus.volume; - core.material.bgms[bgm].currentTime = startTime || 0; - core.material.bgms[bgm].play(); - core.musicStatus.playingBgm = bgm; - core.musicStatus.lastBgm = bgm; + this._playBgm_play(bgm, startTime); } catch (e) { console.log("无法播放BGM "+bgm); @@ -2691,11 +2082,30 @@ control.prototype.playBgm = function (bgm, startTime) { } } +control.prototype._playBgm_play = function (bgm, startTime) { + // 缓存BGM + core.loader.loadBgm(bgm); + // 如果当前正在播放,且和本BGM相同,直接忽略 + if (core.musicStatus.playingBgm == bgm && !core.material.bgms[core.musicStatus.playingBgm].paused) { + return; + } + // 如果正在播放中,暂停 + if (core.musicStatus.playingBgm) { + core.material.bgms[core.musicStatus.playingBgm].pause(); + } + // 播放当前BGM + core.material.bgms[bgm].volume = core.musicStatus.volume; + core.material.bgms[bgm].currentTime = startTime || 0; + core.material.bgms[bgm].play(); + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; +} + ////// 暂停背景音乐的播放 ////// control.prototype.pauseBgm = function () { - // 直接暂停播放 + if (main.mode!='play')return; try { - if (core.isset(core.musicStatus.playingBgm)) { + if (core.musicStatus.playingBgm) { core.material.bgms[core.musicStatus.playingBgm].pause(); core.musicStatus.playingBgm = null; } @@ -2710,8 +2120,6 @@ control.prototype.pauseBgm = function () { ////// 恢复背景音乐的播放 ////// control.prototype.resumeBgm = function () { if (main.mode!='play')return; - - // 恢复BGM try { core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm); } @@ -2731,46 +2139,30 @@ control.prototype.setMusicBtn = function () { ////// 更改背景音乐的播放 ////// control.prototype.triggerBgm = function () { - if (main.mode!='play')return; + if (main.mode!='play') return; core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; if (core.musicStatus.bgmStatus) this.resumeBgm(); - else { + else this.pauseBgm(); - } core.setLocalStorage('bgmStatus', core.musicStatus.bgmStatus); } ////// 播放音频 ////// control.prototype.playSound = function (sound) { - if (main.mode!='play')return; - // 如果不允许播放 - if (!core.musicStatus.soundStatus) return; - // 音频不存在 - if (!core.isset(core.material.sounds[sound])) return; - + if (main.mode!='play' || !core.musicStatus.soundStatus || !core.material.sounds[sound]) return; try { if (core.musicStatus.audioContext != null) { var source = core.musicStatus.audioContext.createBufferSource(); source.buffer = core.material.sounds[sound]; source.connect(core.musicStatus.gainNode); - var id = parseInt(Math.random()*10000000); + var id = setTimeout(null); source.onended = function () { delete core.musicStatus.playingSounds[id]; } - try { - source.start(0); - } - catch (e) { - try { - source.noteOn(0); - } - catch (ee) { - main.log(ee); - return; - } - } + if (source.start) source.start(0); + else if (source.noteOn) source.noteOn(0); core.musicStatus.playingSounds[id] = source; } else { @@ -2778,76 +2170,68 @@ control.prototype.playSound = function (sound) { core.material.sounds[sound].play(); } } - catch (eee) { + catch (e) { console.log("无法播放SE "+sound); - main.log(eee); + main.log(e); } } +////// 停止所有音频 ////// control.prototype.stopSound = function () { for (var i in core.musicStatus.playingSounds) { var source = core.musicStatus.playingSounds[i]; try { - source.stop(); + if (source.stop) source.stop(); + else if (source.noteOff) source.noteOff(); } catch (e) { - try { - source.noteOff(0); - } - catch (e) { - main.log(e); - } + main.log(e); } } + core.musicStatus.playingSounds = {}; } +////// 检查bgm状态 ////// control.prototype.checkBgm = function() { core.playBgm(core.musicStatus.playingBgm || main.startBgm); } +// ------ 状态栏,工具栏等相关 ------ // + ////// 清空状态栏 ////// control.prototype.clearStatusBar = function() { Object.keys(core.statusBar).forEach(function (e) { - if (core.isset(core.statusBar[e].innerHTML)) + if (core.statusBar[e].innerHTML != null) core.statusBar[e].innerHTML = " "; }) core.statusBar.image.book.style.opacity = 0.3; - if (!core.flags.equipboxButton) { + if (!core.flags.equipboxButton) core.statusBar.image.fly.style.opacity = 0.3; - } } ////// 更新状态栏 ////// control.prototype.updateStatusBar = function () { + if (!core.isPlaying()) return; + this.controldata.updateStatusBar(); + this._updateStatusBar_setToolboxIcon(); +} - if (core.isPlaying()) - this.controldata.updateStatusBar(); - - // 回放 +control.prototype._updateStatusBar_setToolboxIcon = function () { if (core.isReplaying()) { core.statusBar.image.book.src = core.status.replay.pausing ? core.statusBar.icons.play.src : core.statusBar.icons.pause.src; core.statusBar.image.book.style.opacity = 1; - core.statusBar.image.fly.src = core.statusBar.icons.stop.src; core.statusBar.image.fly.style.opacity = 1; - core.statusBar.image.toolbox.src = core.statusBar.icons.rewind.src; - core.statusBar.image.keyboard.src = core.statusBar.icons.book.src; - core.statusBar.image.shop.src = core.statusBar.icons.floor.src; - core.statusBar.image.save.src = core.statusBar.icons.speedDown.src; - core.statusBar.image.load.src = core.statusBar.icons.speedUp.src; - core.statusBar.image.settings.src = core.statusBar.icons.save.src; - } else { core.statusBar.image.book.src = core.statusBar.icons.book.src; core.statusBar.image.book.style.opacity = core.hasItem('book') ? 1 : 0.3; - if (!core.flags.equipboxButton) { core.statusBar.image.fly.src = core.statusBar.icons.fly.src; core.statusBar.image.fly.style.opacity = core.hasItem('fly') ? 1 : 0.3; @@ -2856,52 +2240,46 @@ control.prototype.updateStatusBar = function () { core.statusBar.image.fly.src = core.statusBar.icons.equipbox.src; core.statusBar.image.fly.style.opacity = 1; } - core.statusBar.image.toolbox.src = core.statusBar.icons.toolbox.src; - core.statusBar.image.keyboard.src = core.statusBar.icons.keyboard.src; - core.statusBar.image.shop.src = core.statusBar.icons.shop.src; - core.statusBar.image.save.src = core.statusBar.icons.save.src; - core.statusBar.image.load.src = core.statusBar.icons.load.src; - core.statusBar.image.settings.src = core.statusBar.icons.settings.src; } } -control.prototype.triggerStatusBar = function (name, showToolbox) { - if (name!='hide') name='show'; - - // 如果是隐藏 -> 显示工具栏,则先显示 - if (name == 'hide' && !core.domStyle.showStatusBar) { - this.triggerStatusBar("show"); - this.triggerStatusBar("hide", showToolbox); - return; - } - +control.prototype.showStatusBar = function () { + if (core.domStyle.showStatusBar) return; var statusItems = core.dom.status; - var toolItems = core.dom.tools; - core.domStyle.showStatusBar = name == 'show'; - core.setFlag('hideStatusBar', core.domStyle.showStatusBar?null:true); - core.setFlag('showToolbox', showToolbox?true:null); - if (!core.domStyle.showStatusBar) { - for (var i = 0; i < statusItems.length; ++i) - statusItems[i].style.opacity = 0; - if (!core.domStyle.isVertical || !showToolbox) { - for (var i = 0; i < toolItems.length; ++i) - toolItems[i].style.display = 'none'; - } - } - else { - for (var i = 0; i < statusItems.length; ++i) - statusItems[i].style.opacity = 1; - this.setToolbarButton(false); - core.dom.tools.hard.style.display = 'block'; + core.domStyle.showStatusBar = true; + core.removeFlag('hideStatusBar'); + // 显示 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 1; + this.setToolbarButton(false); + core.dom.tools.hard.style.display = 'block'; +} + +control.prototype.hideStatusBar = function (showToolbox) { + // 如果原本就是隐藏的,则先显示 + if (!core.domStyle.showStatusBar) + this.showStatusBar(); + + var statusItems = core.dom.status, toolItems = core.dom.tools; + core.domStyle.showStatusBar = false; + core.setFlag('hideStatusBar', true); + core.setFlag('showToolbox', showToolbox || null); + // 隐藏 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 0; + if (!core.domStyle.isVertical || !showToolbox) { + for (var i = 0; i < toolItems.length; ++i) + toolItems[i].style.display = 'none'; } } +////// 更新状态栏的勇士图标 ////// control.prototype.updateHeroIcon = function (name) { name = name || "hero.png"; if (core.statusBar.icons.name == name) return; @@ -2919,11 +2297,10 @@ control.prototype.updateHeroIcon = function (name) { context.drawImage(image, 0, 0, 32, height, left, 0, width, 32); core.statusBar.image.name.src = canvas.toDataURL("image/png"); - } control.prototype.updateGlobalAttribute = function (name) { - if (!core.isset(name)) return; + if (name == null) name = Object.keys(core.status.globalAttribute); if (name instanceof Array) { name.forEach(function (t) { core.control.updateGlobalAttribute(t); @@ -2931,7 +2308,7 @@ control.prototype.updateGlobalAttribute = function (name) { return; } var attribute = core.status.globalAttribute || core.initStatus.globalAttribute; - if (!core.isset(attribute)) return; + if (attribute == null) return; switch (name) { case 'statusLeftBackground': if (!core.domStyle.isVertical) { @@ -2954,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': @@ -2979,7 +2357,7 @@ control.prototype.updateGlobalAttribute = function (name) { } } -////// 改变工具栏为按钮1-7 ////// +////// 改变工具栏为按钮1-8 ////// control.prototype.setToolbarButton = function (useButton) { if (!core.domStyle.showStatusBar) { // 隐藏状态栏时检查竖屏 @@ -2992,11 +2370,10 @@ control.prototype.setToolbarButton = function (useButton) { else core.dom.tools.hard.style.display = 'block'; } - if (!core.isset(useButton)) useButton = core.domStyle.toolbarBtn; - if (!core.domStyle.isVertical) useButton = false; - if (!core.platform.extendKeyboard) useButton = false; - + if (useButton == null) useButton = core.domStyle.toolbarBtn; + if (!core.domStyle.isVertical || !core.platform.extendKeyboard) useButton = false; core.domStyle.toolbarBtn = useButton; + if (useButton) { ["book","fly","toolbox","keyboard","shop","save","load","settings"].forEach(function (t) { core.statusBar.image[t].style.display = 'none'; @@ -3018,14 +2395,16 @@ control.prototype.setToolbarButton = function (useButton) { } } -control.prototype.needDraw = function(id) { - if (!core.isset(id)) { +////// ------ resize处理 ------ // + +control.prototype._shouldDisplayStatus = function(id) { + if (id == null) { var toDraw = [], status = core.dom.status; for (var i = 0; i clientHeight && clientHeight < ADAPT_WIDTH){ - isHorizontal = true; - width = clientHeight; - } - // 各元素大小的变量声明 - var gameGroupWidth, gameGroupHeight, borderRight, - canvasWidth, canvasTop, // canvasLeft, - statusBarWidth, statusBarHeight, statusBarBorder, - statusWidth, statusHeight, statusMaxWidth,statusLabelsLH, - toolBarWidth, toolBarHeight, toolBarTop, toolBarBorder, - toolsWidth, toolsHeight,toolsMargin,toolsPMaxwidth, - fontSize, toolbarFontSize, margin, statusBackground, toolsBackground, - statusCanvasWidth, statusCanvasHeight, musicBtnBottom, musicBtnRight; - - var toDraw = this.needDraw(); - var count = toDraw.length; - var statusCanvas = core.flags.statusCanvas, statusCanvasRows = core.flags.statusCanvasRowsOnMobile || 3; - - if (!statusCanvas && count>12) alert("当前状态栏数目("+count+")大于12,请调整到不超过12以避免手机端出现显示问题。"); - var col = Math.ceil(count / 3); - if (statusCanvas) col = statusCanvasRows; - - var statusLineHeight = BASE_LINEHEIGHT * 9 / count; - var statusLineFontSize = DEFAULT_FONT_SIZE; - if (count>9) statusLineFontSize = statusLineFontSize * 9 / count; - - var borderColor = (core.status.globalAttribute||core.initStatus.globalAttribute).borderColor; - - statusBarBorder = '3px '+borderColor+' solid'; - toolBarBorder = '3px '+borderColor+' solid'; - var zoom = (ADAPT_WIDTH - width) / 4.22; - var aScale = 1 - zoom / 100; - - core.domStyle.toolbarBtn = false; - - // 移动端 - if (width < CHANGE_WIDTH) { - if(width < ADAPT_WIDTH){ - - core.domStyle.scale = aScale; - canvasWidth = width; - }else{ - canvasWidth = DEFAULT_CANVAS_WIDTH; - core.domStyle.scale = 1; - } - - var scale = core.domStyle.scale - var tempWidth = DEFAULT_CANVAS_WIDTH * scale; - if(!isHorizontal){ //竖屏 - core.domStyle.screenMode = 'vertical'; - core.domStyle.isVertical = true; - - var tempTopBarH = scale * (BASE_LINEHEIGHT * col + SPACE * 2) + 6; - var tempBotBarH = scale * (BASE_LINEHEIGHT + SPACE * 4) + 6; - - gameGroupHeight = tempWidth + tempTopBarH + tempBotBarH; - - gameGroupWidth = tempWidth - statusCanvasWidth = canvasWidth; - statusCanvasHeight = tempTopBarH; - canvasTop = tempTopBarH; - // canvasLeft = 0; - toolBarWidth = statusBarWidth = canvasWidth; - statusBarHeight = tempTopBarH; - statusBarBorder = '3px '+borderColor+' solid'; - - statusHeight = scale*BASE_LINEHEIGHT * .8; - statusLabelsLH = .8 * BASE_LINEHEIGHT *scale; - statusMaxWidth = scale * DEFAULT_BAR_WIDTH * .95; - statusBackground = (core.status.globalAttribute||core.initStatus.globalAttribute).statusTopBackground; - toolBarHeight = tempBotBarH; - - toolBarTop = statusBarHeight + canvasWidth; - toolBarBorder = '3px '+borderColor+' solid'; - toolsHeight = scale * BASE_LINEHEIGHT * 0.95; - toolsPMaxwidth = scale * DEFAULT_BAR_WIDTH * .4; - toolsBackground = (core.status.globalAttribute||core.initStatus.globalAttribute).toolsBackground; - borderRight = '3px '+borderColor+' solid'; - - margin = scale * SPACE * 2; - toolsMargin = scale * SPACE * 3; - fontSize = DEFAULT_FONT_SIZE * scale; - toolbarFontSize = DEFAULT_FONT_SIZE * scale; - musicBtnRight = 3; - musicBtnBottom = 3; - }else { //横屏 - core.domStyle.screenMode = 'horizontal'; - core.domStyle.isVertical = false; - gameGroupWidth = tempWidth + DEFAULT_BAR_WIDTH * scale; - gameGroupHeight = tempWidth; - canvasTop = 0; - // canvasLeft = DEFAULT_BAR_WIDTH * scale; - toolBarWidth = statusBarWidth = DEFAULT_BAR_WIDTH * scale; - statusBarHeight = gameGroupHeight - SPACE; - statusBarBorder = '3px '+borderColor+' solid'; - statusCanvasWidth = toolBarWidth + SPACE; - statusCanvasHeight = statusBarHeight; - statusBackground = (core.status.globalAttribute||core.initStatus.globalAttribute).statusLeftBackground; - - statusHeight = scale*statusLineHeight * .8; - statusLabelsLH = .8 * statusLineHeight *scale; - toolBarTop = scale*statusLineHeight * count + SPACE * 2; - toolBarHeight = canvasWidth - toolBarTop; - toolBarBorder = '3px '+borderColor+' solid'; - toolsHeight = scale * BASE_LINEHEIGHT; - toolsBackground = 'transparent'; - fontSize = statusLineFontSize * scale; - toolbarFontSize = DEFAULT_FONT_SIZE * scale; - borderRight = ''; - statusMaxWidth = scale * DEFAULT_BAR_WIDTH; - toolsPMaxwidth = scale * DEFAULT_BAR_WIDTH; - - margin = scale * SPACE * 2; - toolsMargin = 2 * SPACE * scale; - musicBtnRight = 3; - musicBtnBottom = 3; - } - - }else { //大屏设备 pc端 - core.domStyle.scale = 1; - core.domStyle.screenMode = 'bigScreen'; - core.domStyle.isVertical = false; - - gameGroupWidth = DEFAULT_CANVAS_WIDTH + DEFAULT_BAR_WIDTH; - gameGroupHeight = DEFAULT_CANVAS_WIDTH; - canvasWidth = DEFAULT_CANVAS_WIDTH; - canvasTop = 0; - // canvasLeft = DEFAULT_BAR_WIDTH; - - toolBarWidth = statusBarWidth = DEFAULT_BAR_WIDTH; - // statusBarHeight = statusLineHeight * count + SPACE * 2; //一共有9行 - statusBackground = (core.status.globalAttribute||core.initStatus.globalAttribute).statusLeftBackground; - statusBarHeight = gameGroupHeight - SPACE; - statusCanvasWidth = toolBarWidth + SPACE; - statusCanvasHeight = statusBarHeight; - - statusHeight = statusLineHeight * .8; - statusLabelsLH = .8 * statusLineHeight; - toolBarTop = statusLineHeight * count + SPACE * 2; - toolBarHeight = DEFAULT_CANVAS_WIDTH - toolBarTop; - toolsBackground = 'transparent'; - - toolsHeight = BASE_LINEHEIGHT; - borderRight = ''; - fontSize = statusLineFontSize; - toolbarFontSize = DEFAULT_FONT_SIZE; - statusMaxWidth = DEFAULT_BAR_WIDTH; - toolsPMaxwidth = DEFAULT_BAR_WIDTH * .9; - margin = SPACE * 2; - toolsMargin = 2 * SPACE; - - musicBtnRight = (clientWidth-gameGroupWidth)/2; - musicBtnBottom = (clientHeight-gameGroupHeight)/2 - 27; - } - - var unit = 'px' - core.domStyle.styles = [ - { - id: 'gameGroup', - rules:{ - width: gameGroupWidth + unit, - height: gameGroupHeight + unit, - top: (clientHeight-gameGroupHeight)/2 + unit, - left: (clientWidth-gameGroupWidth)/2 + unit, - } - }, - { - className: 'gameCanvas', - rules:{ - width: (canvasWidth - SPACE*2) + unit, - height: (canvasWidth - SPACE*2) + unit, - } - }, - { - id: 'statusCanvas', - rules: { - width: (statusCanvasWidth - SPACE*2) + unit, - height: (statusCanvasHeight - SPACE) + unit, - display: statusCanvas?'block':'none' - } - }, - { - id: 'gif', - rules: { - width: (canvasWidth - SPACE*2) + unit, - height:(canvasWidth - SPACE*2) + unit, - } - }, - { - id: 'gif2', - rules: { - width: (canvasWidth - SPACE*2) + unit, - height:(canvasWidth - SPACE*2) + unit, - } - }, - { - id: 'gameDraw', - rules: { - width: (canvasWidth - SPACE*2) + unit, - height:(canvasWidth - SPACE*2) + unit, - top: canvasTop + unit, - right: 0, - border: statusBarBorder - } - }, - { - id: 'floorMsgGroup', - rules:{ - width: (canvasWidth - SPACE*2) + unit, - height: (gameGroupHeight - SPACE*2) + unit, - top: SPACE + unit, - right: SPACE + unit, - background: (core.status.globalAttribute||core.initStatus.globalAttribute).floorChangingBackground, - color: (core.status.globalAttribute||core.initStatus.globalAttribute).floorChangingTextColor - } - }, - { - id: 'statusBar', - rules:{ - width: statusBarWidth + unit, - height: statusBarHeight + unit, - top: 0, - left: 0, - padding: SPACE + unit, - - borderTop: statusBarBorder, - borderLeft: statusBarBorder, - borderRight: borderRight, - fontSize: fontSize + unit, - background: statusBackground - } - }, - { - className: 'status', - rules:{ - width: '100%', - maxWidth: statusMaxWidth + unit, - height: statusHeight + unit, - margin: margin/2 + unit, - display: !statusCanvas?'block':'none' - } - }, - { - className: 'statusLabels', - rules:{ - marginLeft: margin + unit, - lineHeight: statusLabelsLH + unit - } - }, - { - className: 'statusTexts', - rules: { - color: (core.status.globalAttribute||core.initStatus.globalAttribute).statusBarColor - } - }, - { - id: 'toolBar', - rules:{ - width: toolBarWidth + unit, - height: toolBarHeight + unit, - top: toolBarTop +unit, - left: 0, - padding: SPACE + unit, - borderBottom: toolBarBorder, - borderLeft: toolBarBorder, - borderRight: borderRight, - fontSize: toolbarFontSize + unit, - background: toolsBackground, - } - }, - { - className: 'tools', - rules:{ - height: toolsHeight + unit, - maxWidth: toolsPMaxwidth + unit, - marginLeft: toolsMargin + unit, - marginTop: margin + unit, - } - }, - { - imgId: 'keyboard', - rules:{ - display: core.domStyle.isVertical && core.domStyle.showStatusBar - } - }, - { - id: 'hard', - rules: { - lineHeight: toolsHeight + unit, - color: (core.status.globalAttribute||core.initStatus.globalAttribute).hardLabelColor - } - }, - { - id: 'musicBtn', - rules: { - display: 'block', - right: musicBtnRight + unit, - bottom: musicBtnBottom + unit - } - } - ] - for (var i = 0; i < core.dom.status.length; ++i) { - var id = core.dom.status[i].id; - core.domStyle.styles.push({ - id: id, - rules: { - display: toDraw.indexOf(id.substring(0, id.length-3))>=0 && !statusCanvas ? "block": "none" - } - }); - } - - core.domRenderer(); - this.setToolbarButton(); - - if (core.domStyle.isVertical) { - core.dom.statusCanvas.width = 416; - core.dom.statusCanvas.height = col * BASE_LINEHEIGHT + SPACE + 6; - } - else { - core.dom.statusCanvas.width = 129; - core.dom.statusCanvas.height = 416; - } - this.setMusicBtn(); - if (core.isPlaying()) - core.updateStatusBar(); +////// 注册一个resize函数 ////// +// name为名称,可供注销使用 +// func可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。 +control.prototype.registerResize = function (name, func) { + this.unregisterResize(name); + this.resizes.push({ name: name, func: func }); } -////// 渲染DOM ////// -control.prototype.domRenderer = function(){ +////// 注销一个resize函数 ////// +control.prototype.unregisterResize = function (name) { + this.resizes = this.resizes.filter(function (b) { return b.name != name; }); +} - core.dom.statusBar.style.display = 'block'; - core.dom.toolBar.style.display = 'block'; - - var styles = core.domStyle.styles; - - for(var i=0; i= CANVAS_WIDTH + BAR_WIDTH || (clientWidth > clientHeight && clientHeight < CANVAS_WIDTH)) { + // 横屏 + core.domStyle.isVertical = false; + core.domStyle.scale = Math.min(1, clientHeight / CANVAS_WIDTH); } - // resize map - if (core.isPlaying() && core.isset(core.status) && core.isset(core.bigmap)) { - core.bigmap.canvas.forEach(function(cn){ - core.canvas[cn].canvas.style.width = core.bigmap.width*32*core.domStyle.scale + "px"; - core.canvas[cn].canvas.style.height = core.bigmap.height*32*core.domStyle.scale + "px"; - }); + else { + // 竖屏 + core.domStyle.isVertical = true; + core.domStyle.scale = Math.min(1, clientWidth / CANVAS_WIDTH); } - // 动态canvas + + var statusDisplayArr = this._shouldDisplayStatus(), count = statusDisplayArr.length; + var statusCanvas = core.flags.statusCanvas, statusCanvasRows = core.flags.statusCanvasRowsOnMobile || 3; + var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3); + if (col > 4) { + if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过4!"); + else alert("当前状态栏数目("+count+")大于12,请调整到不超过12以避免手机端出现显示问题。"); + } + var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + + var obj = { + clientWidth: clientWidth, + clientHeight: clientHeight, + CANVAS_WIDTH: CANVAS_WIDTH, + BAR_WIDTH: BAR_WIDTH, + outerSize: CANVAS_WIDTH * core.domStyle.scale, + globalAttribute: globalAttribute, + border: '3px ' + globalAttribute.borderColor + ' solid', + statusDisplayArr: statusDisplayArr, + count: count, + col: col, + statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 6 : 0, + toolbarHeightInVertical: core.domStyle.isVertical ? 44 * core.domStyle.scale + 6 : 0, + is15x15: core.__SIZE__ == 15 + }; + + this._doResize(obj); + this.setToolbarButton(); + core.updateStatusBar(); +} + +control.prototype._resize_gameGroup = function (obj) { + var gameGroup = core.dom.gameGroup; + var totalWidth, totalHeight; + if (core.domStyle.isVertical) { + totalWidth = obj.outerSize; + totalHeight = obj.outerSize + obj.statusBarHeightInVertical + obj.toolbarHeightInVertical + } + else { + totalWidth = (obj.CANVAS_WIDTH + obj.BAR_WIDTH) * core.domStyle.scale; + totalHeight = obj.outerSize; + } + gameGroup.style.width = totalWidth + "px"; + gameGroup.style.height = totalHeight + "px"; + gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + "px"; + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + floorMsgGroup.style.width = obj.outerSize - 6 + "px"; + floorMsgGroup.style.height = totalHeight - 6 + "px"; + floorMsgGroup.style.background = obj.globalAttribute.floorChangingBackground; + floorMsgGroup.style.color = obj.globalAttribute.floorChangingTextColor; + // musicBtn + if (core.domStyle.isVertical || core.domStyle.scale < 1) { + core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px"; + } + else { + core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = (obj.clientHeight - totalHeight) / 2 - 27 + "px"; + } +} + +control.prototype._resize_canvas = function (obj) { + var innerSize = (obj.outerSize - 6) + "px"; + for (var i = 0; i < core.dom.gameCanvas.length; ++i) + core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[i].style.height = innerSize; + core.dom.gif.style.width = core.dom.gif.style.height = innerSize; + core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; + core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize; + core.dom.gameDraw.style.top = obj.statusBarHeightInVertical + "px"; + core.dom.gameDraw.style.right = 0; + core.dom.gameDraw.style.border = obj.border; + // resize bigmap + core.bigmap.canvas.forEach(function(cn){ + core.canvas[cn].canvas.style.width = core.bigmap.width * 32 * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = core.bigmap.height * 32 * core.domStyle.scale + "px"; + }); + // resize dynamic canvas for (var name in core.dymCanvas) { var ctx = core.dymCanvas[name], canvas = ctx.canvas; canvas.style.width = canvas.width * core.domStyle.scale + "px"; @@ -3446,4 +2554,108 @@ control.prototype.domRenderer = function(){ canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; } -} \ No newline at end of file +} + +control.prototype._resize_statusBar = function (obj) { + // statusBar + var statusBar = core.dom.statusBar; + if (core.domStyle.isVertical) { + statusBar.style.width = obj.outerSize + "px"; + statusBar.style.height = obj.statusBarHeightInVertical + "px"; + statusBar.style.background = obj.globalAttribute.statusTopBackground; + statusBar.style.fontSize = 16 * core.domStyle.scale + "px"; + } + else { + statusBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + "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"; + } + 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"; + core.dom.statusCanvas.width = core.__PIXELS__; + core.dom.statusCanvas.style.height = obj.statusBarHeightInVertical - 3 + "px"; + core.dom.statusCanvas.height = obj.col * 32 + 9; + } + else { + core.dom.statusCanvas.style.width = obj.BAR_WIDTH * core.domStyle.scale - 3 + "px"; + core.dom.statusCanvas.width = obj.BAR_WIDTH - 3; + core.dom.statusCanvas.style.height = obj.outerSize - 6 + "px"; + core.dom.statusCanvas.height = core.__PIXELS__; + } + core.dom.statusCanvas.style.display = core.flags.statusCanvas ? "block" : "none"; +} + +control.prototype._resize_status = function (obj) { + var statusHeight = (core.domStyle.isVertical ? 1 : (core.__HALF_SIZE__ + 3) / obj.count) * 32 * core.domStyle.scale * 0.8; + // status + for (var i = 0; i < core.dom.status.length; ++i) { + var id = core.dom.status[i].id, style = core.dom.status[i].style; + if (id.endsWith("Col")) id = id.substring(0, id.length-3); + style.display = core.flags.statusCanvas || obj.statusDisplayArr.indexOf(id) < 0 ? 'none': 'block'; + style.margin = 3 * core.domStyle.scale + "px"; + style.height = statusHeight + "px"; + style.maxWidth = obj.BAR_WIDTH * core.domStyle.scale * (core.domStyle.isVertical ? 0.95 : 1) + "px"; + if (obj.is15x15 && !core.domStyle.isVertical) + style.marginLeft = 11 * core.domStyle.scale + "px"; + } + // statusLabels, statusTexts + for (var i = 0; i < core.dom.statusLabels.length; ++i) { + core.dom.statusLabels[i].style.lineHeight = statusHeight + "px"; + core.dom.statusLabels[i].style.marginLeft = 6 * core.domStyle.scale + "px"; + } + for (var i = 0; i < core.dom.statusTexts.length; ++i) { + core.dom.statusTexts[i].style.color = obj.globalAttribute.statusBarColor; + } +} + +control.prototype._resize_toolBar = function (obj) { + // toolBar + var toolBar = core.dom.toolBar; + if (core.domStyle.isVertical) { + toolBar.style.width = obj.outerSize + "px"; + toolBar.style.top = obj.statusBarHeightInVertical + obj.outerSize + "px"; + toolBar.style.height = obj.toolbarHeightInVertical + "px"; + toolBar.style.background = obj.globalAttribute.toolsBackground; + } + else { + toolBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + "px"; + toolBar.style.top = 0.718 * obj.outerSize + "px"; + toolBar.style.height = 0.281 * obj.outerSize + "px"; + toolBar.style.background = 'transparent'; + } + toolBar.style.display = 'block'; + toolBar.style.borderLeft = obj.border; + toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical ? obj.border : ''; + toolBar.style.fontSize = 16 * core.domStyle.scale + "px"; +} + +control.prototype._resize_tools = function (obj) { + var toolsHeight = 32 * core.domStyle.scale * (core.domStyle.isVertical ? 0.95 : 1); + var toolsMarginLeft; + if (core.domStyle.isVertical) + toolsMarginLeft = (core.__HALF_SIZE__ - 3) * 3 * core.domStyle.scale; + else + toolsMarginLeft = (obj.BAR_WIDTH * core.domStyle.scale - 9 - toolsHeight * 3) / 4; + for (var i = 0; i < core.dom.tools.length; ++i) { + var style = core.dom.tools[i].style; + style.height = toolsHeight + "px"; + style.marginLeft = toolsMarginLeft + "px"; + style.marginTop = 6 * core.domStyle.scale + "px" + } + core.dom.hard.style.lineHeight = toolsHeight + "px"; + core.dom.hard.style.color = obj.globalAttribute.hardLabelColor; + if (core.domStyle.isVertical) { + core.dom.hard.style.width = obj.outerSize - 9 * toolsMarginLeft - 8.5 * toolsHeight - 12 + "px"; + } + else { + core.dom.hard.style.width = obj.BAR_WIDTH * core.domStyle.scale - 9 - 2 * toolsMarginLeft + "px"; + if (!obj.is15x15) core.dom.hard.style.marginTop = 0; + } +} diff --git a/libs/core.js b/libs/core.js index c35abc7f..3f1165b6 100644 --- a/libs/core.js +++ b/libs/core.js @@ -5,6 +5,9 @@ "use strict"; function core() { + this.__SIZE__ = 13; + this.__PIXELS__ = this.__SIZE__ * 32; + this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2); this.material = { 'animates': {}, 'images': {}, @@ -13,11 +16,10 @@ function core() { 'ground': null, 'items': {}, 'enemys': {}, - 'icons': {}, - 'events': {} + 'icons': {} } this.timeout = { - 'getItemTipTimeout': null, + 'tipTimeout': null, 'turnHeroTimeout': null, 'onDownTimeout': null, 'sleepTimeout': null, @@ -41,7 +43,6 @@ function core() { 'weather': { 'time': 0, 'type': null, - 'level': 0, 'nodes': [], 'data': null, 'fog': null, @@ -65,6 +66,7 @@ function core() { 'isPC': true, // 是否是PC 'isAndroid': false, // 是否是Android 'isIOS': false, // 是否是iOS + 'string': 'PC', 'isWeChat': false, // 是否是微信 'isQQ': false, // 是否是QQ 'isChrome': false, // 是否是Chrome @@ -79,19 +81,17 @@ function core() { } // 样式 this.domStyle = { - styles: [], scale: 1.0, - screenMode: null, isVertical: false, - toolbarBtn: false, showStatusBar: true, + toolbarBtn: false, } this.bigmap = { canvas: ["bg", "event", "event2", "fg", "damage"], offsetX: 0, // in pixel offsetY: 0, - width: 13, // map width and height - height: 13, + width: this.__SIZE__, // map width and height + height: this.__SIZE__, tempCanvas: null, // A temp canvas for drawing } this.paint = {}; @@ -102,7 +102,9 @@ function core() { "data": null, "time": 0, "updated": false, - } + }, + "favorite": [], + "favoriteName": {} } this.initStatus = { 'played': false, @@ -172,9 +174,9 @@ function core() { 'textAttribute': { 'position': "center", "offset": 0, - "title": [255,215,0,1], - "background": [0,0,0,0.85], - "text": [255,255,255,1], + "title": [255, 215, 0, 1], + "background": [0, 0, 0, 0.85], + "text": [255, 255, 255, 1], "titlefont": 22, "textfont": 16, "bold": false, @@ -194,7 +196,6 @@ function core() { }, 'curtainColor': null, 'openingDoor': null, - 'isSkiing': false, // 动画 'globalAnimateObjs': [], @@ -213,201 +214,197 @@ function core() { ////// 初始化 ////// core.prototype.init = function (coreData, callback) { this._forwardFuncs(); - - for (var key in coreData) { + for (var key in coreData) core[key] = coreData[key]; - } + this._init_flags(); + this._init_platform(); + this._init_others(); + this._initPlugins(); + + core.loader._load(function () { + core._afterLoadResources(callback); + }); +} + +core.prototype._init_flags = function () { core.flags = core.clone(core.data.flags); core.values = core.clone(core.data.values); - core.firstData = core.data.getFirstData(); - - if (!core.flags.enableExperience) - core.flags.enableLevelUp = false; - if (!core.flags.enableLevelUp) - core.flags.levelUpLeftMode = false; - - if (core.isset(core.firstData.shops)) { - core.firstData.shops.forEach(function (t) { - core.initStatus.shops[t.id] = t; - }) - } - - core.maps._setFloorSize(); + core.firstData = core.clone(core.data.firstData); + this._init_sys_flags(); core.dom.versionLabel.innerHTML = core.firstData.version; core.dom.logoLabel.innerHTML = core.firstData.title; document.title = core.firstData.title + " - HTML5魔塔"; document.getElementById("startLogo").innerHTML = core.firstData.title; - core.material.items = core.items.getItems(); + (core.firstData.shops||[]).forEach(function (t) { core.initStatus.shops[t.id] = t; }); + core.maps._setFloorSize(); + // 初始化怪物、道具等 core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + core.items._resetItems(); core.material.icons = core.icons.getIcons(); - core.material.events = core.events.getEvents(); +} - core.platform.isOnline = location.protocol.indexOf("http")==0; - if (core.platform.isOnline) { - window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; - try { - core.musicStatus.audioContext = new window.AudioContext(); - core.musicStatus.gainNode = core.musicStatus.audioContext.createGain(); - core.musicStatus.gainNode.connect(core.musicStatus.audioContext.destination); - } catch (e) { - console.log("该浏览器不支持AudioContext"); - core.musicStatus.audioContext = null; - } +core.prototype._init_sys_flags = function () { + if (!core.flags.enableExperience) core.flags.enableLevelUp = false; + if (!core.flags.enableLevelUp) core.flags.levelUpLeftMode = false; + if (core.flags.equipboxButton) core.flags.equipment = true; + core.flags.displayEnemyDamage = core.getLocalStorage('enemyDamage', core.flags.displayEnemyDamage); + core.flags.displayCritical = core.getLocalStorage('critical', core.flags.displayCritical); + core.flags.displayExtraDamage = core.getLocalStorage('extraDamage', core.flags.displayExtraDamage); +} + +core.prototype._init_platform = function () { + core.platform.isOnline = location.protocol.indexOf("http") == 0; + if (!core.platform.isOnline) alert("请勿直接打开html文件!使用启动服务或者APP进行离线游戏。"); + window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; + try { + core.musicStatus.audioContext = new window.AudioContext(); + core.musicStatus.gainNode = core.musicStatus.audioContext.createGain(); + core.musicStatus.gainNode.connect(core.musicStatus.audioContext.destination); + } catch (e) { + console.log("该浏览器不支持AudioContext"); + core.musicStatus.audioContext = null; } - + core.musicStatus.bgmStatus = core.getLocalStorage('bgmStatus', true); + core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"].forEach(function (t) { - if (navigator.userAgent.indexOf(t)>=0) { - if (t=='iPhone' || t=='iPad' || t=='iPod') core.platform.isIOS = true; - if (t=='Android') core.platform.isAndroid=true; - core.platform.isPC=false; + if (navigator.userAgent.indexOf(t) >= 0) { + if (t == 'iPhone' || t == 'iPad' || t == 'iPod') core.platform.isIOS = true; + if (t == 'Android') core.platform.isAndroid = true; + core.platform.isPC = false; } }); - - try { - core.platform.supportCopy = document.queryCommandSupported("copy"); - } - catch (e) { - core.platform.supportCopy = false; - } - - var chrome=/Chrome\/(\d+)\./i.exec(navigator.userAgent); - if (core.isset(chrome) && parseInt(chrome[1])>=50) - core.platform.isChrome = true; + core.platform.string = core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""; + core.platform.supportCopy = document.queryCommandSupported || document.queryCommandSupported("copy"); + var chrome = /Chrome\/(\d+)\./i.exec(navigator.userAgent); + if (chrome && parseInt(chrome[1]) >= 50) core.platform.isChrome = true; core.platform.isSafari = /Safari/i.test(navigator.userAgent) && !/Chrome/i.test(navigator.userAgent); core.platform.isQQ = /QQ/i.test(navigator.userAgent); core.platform.isWeChat = /MicroMessenger/i.test(navigator.userAgent); - core.platform.useLocalForage = core.getLocalStorage('useLocalForage', !core.platform.isIOS); - if (core.platform.useLocalForage) { - try { - core.setLocalForage("__test__", lzw_encode("__test__"), function() { - try { - core.getLocalForage("__test__", null, function(data) { - try { - if (lzw_decode(data)!="__test__") { - console.log("localForage unsupported!"); - core.platform.useLocalForage=false; - } - else { - console.log("localForage supported!") - core.removeLocalForage("__test__"); - } - } - catch (e) {main.log(e); core.platform.useLocalForage=false;} - }, function(e) {main.log(e); core.platform.useLocalForage=false;}) - } - catch (e) {main.log(e); core.platform.useLocalForage=false;} - }, function(e) {main.log(e); core.platform.useLocalForage=false;}) - } - catch (e) {main.log(e); core.platform.useLocalForage=false;} - } - + this._init_checkLocalForage(); core.platform.extendKeyboard = core.getLocalStorage("extendKeyboard", false); - if (window.FileReader) { core.platform.fileReader = new FileReader(); core.platform.fileReader.onload = function () { core.readFileContent(core.platform.fileReader.result); }; core.platform.fileReader.onerror = function () { - if (core.isset(core.platform.errorCallback)) + if (core.platform.errorCallback) core.platform.errorCallback(); } } +} - // 先从存储中读取BGM状态 - core.musicStatus.bgmStatus = core.getLocalStorage('bgmStatus', true); - if (!core.platform.isPC && (navigator.connection||{}).type!='wifi') - core.musicStatus.bgmStatus = false; - core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); - - // switchs - core.flags.displayEnemyDamage = core.getLocalStorage('enemyDamage', core.flags.displayEnemyDamage); - core.flags.displayCritical = core.getLocalStorage('critical', core.flags.displayCritical); - core.flags.displayExtraDamage = core.getLocalStorage('extraDamage', core.flags.displayExtraDamage); +core.prototype._init_checkLocalForage = function () { + core.platform.useLocalForage = core.getLocalStorage('useLocalForage', true); + var _error = function (e) { + main.log(e); + core.platform.useLocalForage = false; + }; + if (core.platform.useLocalForage) { + try { + core.setLocalForage("__test__", lzw_encode("__test__"), function () { + try { + core.getLocalForage("__test__", null, function (data) { + try { + if (lzw_decode(data) != "__test__") { + console.log("localForage unsupported!"); + core.platform.useLocalForage = false; + } + else { + console.log("localForage supported!"); + core.removeLocalForage("__test__"); + } + } + catch (e) {_error(e);} + }, _error) + } + catch (e) {_error(e);} + }, _error) + } + catch (e) {_error(e);} + } +} +core.prototype._init_others = function () { + // 一些额外的东西 core.material.groundCanvas = document.createElement('canvas').getContext('2d'); core.material.groundCanvas.canvas.width = core.material.groundCanvas.canvas.height = 32; core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); - - core.animateFrame.weather.fog = new Image(); - core.animateFrame.weather.fog.onerror = function () { - core.animateFrame.weather.fog = null; - } - core.animateFrame.weather.fog.src = "project/images/fog.png"; - - core.material.images.keyboard = new Image(); - core.material.images.keyboard.onerror = function () { - core.material.images.keyboard = null; - } - core.material.images.keyboard.src = "project/images/keyboard.png"; - core.bigmap.tempCanvas = document.createElement('canvas').getContext('2d'); - - ////// 记录所有的存档编号!!! ////// + core.loadImage('fog', function (name, img) { core.animateFrame.weather.fog = img; }); + core.loadImage('keyboard', function (name, img) {core.material.images.keyboard = img; }); + // 记录存档编号 core.saves.saveIndex = core.getLocalStorage('saveIndex', 1); - core.control.getSaveIndexes(function (indexes) { - core.saves.ids = indexes; - }); - - core.loader._load(function () { - console.log(core.material); - // 设置勇士高度 - core.material.icons.hero.height = core.material.images.hero.height/4; - // 行走图 - core.control.updateHeroIcon(); - - core.initStatus.maps = core.maps.initMaps(core.floorIds); - core.setRequestAnimationFrame(); - - if (main.mode=='play') - core.events.initGame(); - - if (core.isset(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.plugins)) { - core.plugin = new function () { - this.__renderFrameFuncs = []; - }; - core.plugin.__init__ = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.plugins.plugin; - core.plugin.__init__(); - } - - core.showStartAnimate(); - - if (core.isset(callback)) callback(); - - }); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); } -core.prototype._forwardFuncs = function () { - var list = {}; - for (var i = 0; i < main.loadList.length; ++i) { - var name = main.loadList[i]; - if (name == 'core') continue; - for (var funcname in core[name]) { - if (funcname.charAt(0) != "_" && core[name][funcname] instanceof Function) { - if (list[funcname]) { - main.log("Error forward: "+name+"."+funcname); - } - else { - list[funcname] = name; - } +core.prototype._afterLoadResources = function (callback) { + // 初始化地图 + core.initStatus.maps = core.maps._initMaps(); + core.control._setRequestAnimationFrame(); + if (core.plugin._afterLoadResources) + core.plugin._afterLoadResources(); + core.showStartAnimate(); + if (callback) callback(); +} + +core.prototype._initPlugins = function () { + core.plugin = new function () {}; + + for (var name in plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1) { + if (plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name] instanceof Function) { + try { + plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name].apply(core.plugin); + } + catch (e) { + main.log(e); + main.log("无法初始化插件"+name); } } } - for (var funcname in list) { - this._forwardFunc(list[funcname], funcname); + + core._forwardFunc("plugin"); +} + +core.prototype._forwardFuncs = function () { + for (var i = 0; i < main.loadList.length; ++i) { + var name = main.loadList[i]; + if (name == 'core') continue; + this._forwardFunc(name); } } core.prototype._forwardFunc = function (name, funcname) { + if (funcname == null) { + for (funcname in core[name]) { + if (funcname.charAt(0) != "_" && core[name][funcname] instanceof Function) { + this._forwardFunc(name, funcname); + } + } + return; + } + if (core[funcname]) { - main.log("Error in forwarding "+funcname+" from "+name+"!"); + console.error("ERROR: 无法转发 "+name+" 中的函数 "+funcname+" 到 core 中!同名函数已存在。"); return; } var parameterInfo = /^\s*function\s*[\w_$]*\(([\w_,$\s]*)\)\s*\{/.exec(core[name][funcname].toString()); - var parameters = (parameterInfo==null?"":parameterInfo[1]).replace(/\s*/g, '').replace(/,/g, ', '); + var parameters = (parameterInfo == null ? "" : parameterInfo[1]).replace(/\s*/g, '').replace(/,/g, ', '); // core[funcname] = new Function(parameters, "return core."+name+"."+funcname+"("+parameters+");"); - eval("core."+funcname+" = function ("+parameters+") {\n\treturn core."+name+"."+funcname+"("+parameters+");\n}"); + eval("core." + funcname + " = function (" + parameters + ") {\n\treturn core." + name + "." + funcname + "(" + parameters + ");\n}"); + if (name == 'plugin') { + main.log("插件函数转发:core."+funcname+" = core.plugin."+funcname); + } +} + +core.prototype.doFunc = function (func, _this) { + if (typeof func == 'string') { + func = core.plugin[func]; + _this = core.plugin; + } + return func.apply(_this, Array.prototype.slice.call(arguments, 2)); } /** diff --git a/libs/data.js b/libs/data.js index b11f91cf..684e71cb 100644 --- a/libs/data.js +++ b/libs/data.js @@ -4,13 +4,9 @@ function data() { this._init(); } -data.prototype._init = function() { +data.prototype._init = function () { this.firstData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData; this.values = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.values; this.flags = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.flags; //delete(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d); -} - -data.prototype.getFirstData = function() { - return core.clone(this.firstData); } \ No newline at end of file diff --git a/libs/enemys.js b/libs/enemys.js index f0fc4c7a..2b7e551d 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -8,8 +8,10 @@ function enemys() { enemys.prototype._init = function () { this.enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys; - if (main.mode=='play') { - this.enemydata.hasSpecial = function (a, b) {return core.enemys.hasSpecial(a, b)}; + if (main.mode == 'play') { + this.enemydata.hasSpecial = function (a, b) { + return core.enemys.hasSpecial(a, b) + }; for (var enemyId in this.enemys) { this.enemys[enemyId].id = enemyId; } @@ -22,21 +24,21 @@ enemys.prototype.getEnemys = function () { ////// 判断是否含有某特殊属性 ////// enemys.prototype.hasSpecial = function (special, test) { - if (!core.isset(special)) return false; + if (special == null) return false; if (special instanceof Array) { - return special.indexOf(test)>=0; + return special.indexOf(test) >= 0; } if (typeof special == 'number') { - return special!=0 && (special%100==test||this.hasSpecial(parseInt(special/100), test)); + return special === test; } if (typeof special == 'string') { return this.hasSpecial(core.material.enemys[special], test); } - if (core.isset(special.special)) { + if (special.special != null) { return this.hasSpecial(special.special, test); } @@ -47,25 +49,16 @@ enemys.prototype.getSpecials = function () { return this.enemydata.getSpecials(); } -enemys.prototype._calSpecialContent = function (enemy, content) { - if (typeof content == 'string') return content; - if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; - if (content instanceof Function) { - return content(enemy); - } - return ""; -} - ////// 获得所有特殊属性的名称 ////// enemys.prototype.getSpecialText = function (enemy) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; - if (!core.isset(enemy)) return []; + if (!enemy) return []; var special = enemy.special; var text = []; - var specials=this.getSpecials(); - if (core.isset(specials)) { - for (var i=0;inextInfo.damage) { - pre = nextInfo.damage; - list.push([atk-hero_atk, info.damage-nextInfo.damage]); - if (nextInfo.damage<=0 && !core.flags.enableNegativeDamage) break; - if (list.length>=number) break; - } - } - if (list.length==0) list.push([0,0]); - return list; -} - -enemys.prototype._nextCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) { - var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, pre = info.damage; - var list = []; - var calNext = function (currAtk, maxAtk) { - var start = Math.floor(currAtk), end = Math.floor(maxAtk); - if (start>end) return null; - - while (startnextInfo.damage) end = mid; - else start = mid+1; - } - var nextInfo = core.enemys.getDamageInfo(enemy, {"atk": start}, x, y, floorId); - return nextInfo==null||(typeof nextInfo == 'number')||nextInfo.damage>=pre?null:[start,nextInfo.damage]; - } - var currAtk = hero_atk; - while (true) { - var next = calNext(currAtk+1, mon_hp+mon_def, pre); - if (next == null) break; - currAtk = next[0]; - pre = next[1]; - list.push([currAtk-hero_atk, info.damage-pre]); - if (pre<=0 && !core.flags.enableNegativeDamage) break; - if (list.length>=number) break; - } - if (list.length==0) list.push([0,0]); - return list; -} - -enemys.prototype._nextCriticals_useTurn = function (enemy, info, number, x, y, floorId) { - var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, turn = info.turn; - var list = [], pre = null; - for (var t = turn-1;t>=1;t--) { - var nextAtk = Math.ceil(mon_hp/t) + mon_def; - // 装备提升比例的计算临界 - nextAtk = Math.ceil(nextAtk / core.getFlag('__atk_buff__', 1)); - if (nextAtk<=hero_atk) break; - if (nextAtk!=pre) { - var nextInfo = this.getDamageInfo(enemy, {"atk": nextAtk}, x, y, floorId); - if (nextInfo==null || (typeof nextInfo == 'number')) break; - list.push([nextAtk-hero_atk,Math.floor(info.damage-nextInfo.damage)]); - if (nextInfo.damage<=0 && !core.flags.enableNegativeDamage) break; - pre = nextAtk; - } - if (list.length>=number) - break; - } - if (list.length==0) list.push([0,0]); - return list; -} - ////// 接下来N个临界值和临界减伤计算 ////// enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId) { if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; - number = number||1; + number = number || 1; if (this.hasSpecial(enemy.special, 10)) return []; // 模仿怪物临界 var info = this.getDamageInfo(enemy, null, x, y, floorId); if (info == null || this.hasSpecial(enemy.special, 3)) { // 未破防,或是坚固怪 info = this.getEnemyInfo(enemy, null, x, y, floorId); - if (core.status.hero.atk<=info.def) { - return [[info.def+1-core.status.hero.atk,'?']]; + if (core.status.hero.atk <= info.def) { + return [[info.def + 1 - core.status.hero.atk, '?']]; } return []; } // getDamageInfo直接返回数字;0伤且无负伤 - if (typeof info == 'number' || (info.damage<=0 && !core.flags.enableNegativeDamage)) { - return [[0,0]]; + if (typeof info == 'number' || (info.damage <= 0 && !core.flags.enableNegativeDamage)) { + return [[0, 0]]; } if (core.flags.useLoop) { @@ -259,13 +191,83 @@ enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId) { } } +enemys.prototype._nextCriticals_useLoop = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, pre = info.damage; + var list = []; + for (var atk = hero_atk + 1; atk <= mon_hp + mon_def; atk++) { + var nextInfo = this.getDamageInfo(enemy, {"atk": atk}, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) break; + if (pre > nextInfo.damage) { + pre = nextInfo.damage; + list.push([atk - hero_atk, info.damage - nextInfo.damage]); + if (nextInfo.damage <= 0 && !core.flags.enableNegativeDamage) break; + if (list.length >= number) break; + } + } + if (list.length == 0) list.push([0, 0]); + return list; +} + +enemys.prototype._nextCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, pre = info.damage; + var list = []; + var calNext = function (currAtk, maxAtk) { + var start = Math.floor(currAtk), end = Math.floor(maxAtk); + if (start > end) return null; + + while (start < end) { + var mid = Math.floor((start + end) / 2); + var nextInfo = core.enemys.getDamageInfo(enemy, {"atk": mid}, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) return null; + if (pre > nextInfo.damage) end = mid; + else start = mid + 1; + } + var nextInfo = core.enemys.getDamageInfo(enemy, {"atk": start}, x, y, floorId); + return nextInfo == null || (typeof nextInfo == 'number') || nextInfo.damage >= pre ? null : [start, nextInfo.damage]; + } + var currAtk = hero_atk; + while (true) { + var next = calNext(currAtk + 1, mon_hp + mon_def, pre); + if (next == null) break; + currAtk = next[0]; + pre = next[1]; + list.push([currAtk - hero_atk, info.damage - pre]); + if (pre <= 0 && !core.flags.enableNegativeDamage) break; + if (list.length >= number) break; + } + if (list.length == 0) list.push([0, 0]); + return list; +} + +enemys.prototype._nextCriticals_useTurn = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, turn = info.turn; + var list = [], pre = null; + for (var t = turn - 1; t >= 1; t--) { + var nextAtk = Math.ceil(mon_hp / t) + mon_def; + // 装备提升比例的计算临界 + nextAtk = Math.ceil(nextAtk / core.getBuff('atk')); + if (nextAtk <= hero_atk) break; + if (nextAtk != pre) { + var nextInfo = this.getDamageInfo(enemy, {"atk": nextAtk}, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) break; + list.push([nextAtk - hero_atk, Math.floor(info.damage - nextInfo.damage)]); + if (nextInfo.damage <= 0 && !core.flags.enableNegativeDamage) break; + pre = nextAtk; + } + if (list.length >= number) + break; + } + if (list.length == 0) list.push([0, 0]); + return list; +} + ////// N防减伤计算 ////// 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); - if (nowDamage == null || nextDamage ==null) return "???"; + 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; } @@ -275,14 +277,14 @@ enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) { } ////// 获得战斗伤害信息(实际伤害计算函数) ////// -enemys.prototype.getDamageInfo = function(enemy, hero, x, y, floorId) { +enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) { // 移动到了脚本编辑 - getDamageInfo中 if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; return this.enemydata.getDamageInfo(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); @@ -298,12 +300,11 @@ enemys.prototype.updateEnemys = function () { ////// 获得当前楼层的怪物列表 ////// enemys.prototype.getCurrentEnemys = function (floorId) { - floorId=floorId||core.status.floorId; + floorId = floorId || core.status.floorId; var enemys = [], used = {}; var mapBlocks = core.status.maps[floorId].blocks; for (var b = 0; b < mapBlocks.length; b++) { - if (core.isset(mapBlocks[b].event) && !mapBlocks[b].disable - && mapBlocks[b].event.cls.indexOf('enemy')==0) { + if (!mapBlocks[b].disable && mapBlocks[b].event.cls.indexOf('enemy') == 0) { this._getCurrentEnemys_addEnemy(mapBlocks[b].event.id, enemys, used, floorId); } } @@ -312,27 +313,23 @@ enemys.prototype.getCurrentEnemys = function (floorId) { enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) { var enemy = core.material.enemys[enemyId]; - if (!core.isset(enemy)) return null; + if (!enemy) return null; // 检查displayIdInBook - var tmpId = enemy.displayIdInBook; - if (core.isset(core.material.enemys[tmpId])) { - enemy = core.material.enemys[tmpId]; - } - return enemy; + return core.material.enemys[enemy.displayIdInBook] || enemy; } enemys.prototype._getCurrentEnemys_addEnemy = function (enemyId, enemys, used, floorId) { var enemy = this._getCurrentEnemys_getEnemy(enemyId); - if (enemy==null || used[enemy.id]) return; + if (enemy == null || used[enemy.id]) return; var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId); var specialText = core.enemys.getSpecialText(enemy); - if (specialText.length>=3) specialText = "多属性..."; + if (specialText.length >= 3) specialText = "多属性..."; else specialText = specialText.join(" "); var critical = this.nextCriticals(enemy, 1, null, null, floorId); - if (critical.length>0) critical=critical[0]; + if (critical.length > 0) critical = critical[0]; var e = core.clone(enemy); for (var x in enemyInfo) { @@ -342,7 +339,7 @@ enemys.prototype._getCurrentEnemys_addEnemy = function (enemyId, enemys, used, f e.damage = this.getDamage(enemy, null, null, floorId); e.critical = critical[0]; e.criticalDamage = critical[1]; - e.defDamage = this.getDefDamage(enemy,1,null,null,floorId); + e.defDamage = this.getDefDamage(enemy, 1, null, null, floorId); enemys.push(e); used[enemy.id] = true; } @@ -360,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; } \ No newline at end of file diff --git a/libs/events.js b/libs/events.js index 5ed17252..b5b1170f 100644 --- a/libs/events.js +++ b/libs/events.js @@ -8,178 +8,95 @@ function events() { events.prototype._init = function () { this.eventdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.events; this.commonEvent = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; - this.events = { - 'battle': function (data, callback) { - core.battle(data.event.id, data.x, data.y); - if (core.isset(callback)) - callback(); - }, - 'getItem': function (data, callback) { - core.getItem(data.event.id, 1, data.x, data.y); - if (core.isset(callback)) - callback(); - }, - 'openDoor': function (data, callback) { - core.openDoor(data.event.id, data.x, data.y, true, function () { - if (core.isset(callback)) callback(); - }); - }, - 'changeFloor': function (data, callback) { - var heroLoc = {}; - if (core.isset(data.event.data.loc)) - heroLoc = {'x': data.event.data.loc[0], 'y': data.event.data.loc[1]}; - if (core.isset(data.event.data.direction)) - heroLoc.direction = data.event.data.direction; - if (core.status.event.id!='action') core.status.event.id=null; - core.changeFloor(data.event.data.floorId, data.event.data.stair, - heroLoc, data.event.data.time, function () { - if (core.isset(callback)) callback(); - }); - }, - 'passNet': function (data, callback) { - core.events.passNet(data); - if (core.isset(callback)) - callback(); - }, - "changeLight": function (data, callback) { - core.events.changeLight(data.x, data.y); - if (core.isset(callback)) - callback(); - }, - "ski": function (data, callback) { - core.events.ski(); - if (core.isset(callback)) - callback(); - }, - "pushBox": function (data, callback) { - core.events.pushBox(data); - if (core.isset(callback)) - callback(); - }, - 'action': function (data, callback) { - var ev = core.clone(data.event.data), ex = data.x, ey = data.y; - // 检查是否需要改变朝向 - if (ex == core.nextX() && ey == core.nextY()) { - var dir = {"up":"down","down":"up","left":"right","right":"left"}[core.getHeroLoc('direction')]; - var id = data.event.id, toId = (data.event.faceIds||{})[dir]; - if (core.isset(toId) && id!=toId) { - var number = core.maps.getNumberById(toId); - if (number>0) - core.setBlock(number, ex, ey); - } - } - core.events.insertAction(ev, ex, ey, callback); - } - } + this.systemEvents = {}; + this.actions = {}; } -////// 获得一个或所有系统事件类型 ////// -events.prototype.getEvents = function (eventName) { - if (!core.isset(eventName)) { - return this.events; - } - return this.events[eventName]; -} +// ------ 初始化,开始和结束 ------ // -events.prototype.initGame = function () { - return this.eventdata.initGame(); +/// 初始化游戏 +events.prototype.resetGame = function (hero, hard, floorId, maps, values) { + this.eventdata.resetGame(hero, hard, floorId, maps, values); } ////// 游戏开始事件 ////// events.prototype.startGame = function (hard, seed, route, callback) { + main.dom.levelChooseButtons.style.display = 'none'; + main.dom.startButtonGroup.style.display = 'none'; + hard = hard || ""; - main.dom.levelChooseButtons.style.display='none'; - main.dom.startButtonGroup.style.display='none'; + if (main.mode != 'play') return; - var start = function () { - console.log('开始游戏'); - core.resetStatus(core.firstData.hero, hard, null, null, core.initStatus.maps); - var nowLoc = core.clone(core.getHeroLoc()); - core.setHeroLoc('x', -1); - core.setHeroLoc('y', -1); - - if (core.isset(seed)) { - core.setFlag('__seed__', seed); - core.setFlag('__rand__', seed); - } - else core.utils.__init_seed(); - - core.clearMap('all'); - core.deleteAllCanvas(); - core.clearStatusBar(); - - var post_start = function () { - core.ui.closePanel(); - - core.control.triggerStatusBar('show'); - core.dom.musicBtn.style.display = 'none'; - - core.changeFloor(core.firstData.floorId, null, nowLoc, null, function() { - if (core.isset(callback)) callback(); - }); - - setTimeout(function () { - // Upload - var formData = new FormData(); - formData.append('type', 'people'); - formData.append('name', core.firstData.name); - formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); - formData.append('hard', core.encodeBase64(core.status.hard)); - formData.append('hardCode', core.getFlag('hard', 0)); - formData.append('base64', 1); - - core.utils.http("POST", "/games/upload.php", formData); - }) - } - - var real_start = function () { - core.insertAction(core.clone(core.firstData.startText), null, null, function() { - post_start(); - }); - } - - if (core.flags.startUsingCanvas) { - core.control.triggerStatusBar('hide'); - core.dom.musicBtn.style.display = 'block'; - - core.insertAction(core.clone(core.firstData.startCanvas), null, null, function() { - real_start(); - }); - } - else { - core.events.setInitData(hard); - real_start(); - } - - if (core.isset(route)) { - core.startReplay(route); - } - - } - - if (main.mode!='play') return; - - if (core.flags.startUsingCanvas) { + // 无动画的开始游戏 + if (core.flags.startUsingCanvas || route != null) { core.dom.startPanel.style.display = 'none'; - start(); - return; - } - - if (core.isset(route)) { - core.dom.startPanel.style.display = 'none'; - start(); + this._startGame_start(hard, seed, route, callback); } else { - core.hideStartAnimate(function() { - start(); - }) + core.hideStartAnimate(function () { + core.events._startGame_start(hard, seed, route, callback); + }); } } +events.prototype._startGame_start = function (hard, seed, route, callback) { + console.log('开始游戏'); + core.resetGame(core.firstData.hero, hard, null, core.initStatus.maps); + var nowLoc = core.clone(core.getHeroLoc()); + core.setHeroLoc('x', -1); + core.setHeroLoc('y', -1); + + if (seed != null) { + core.setFlag('__seed__', seed); + core.setFlag('__rand__', seed); + } + else core.utils.__init_seed(); + this.setInitData(); + core.clearStatusBar(); + + var todo = []; + if (core.flags.startUsingCanvas) { + core.hideStatusBar(); + core.dom.musicBtn.style.display = 'block'; + core.push(todo, core.firstData.startCanvas); + } + core.push(todo, core.firstData.startText); + this.insertAction(todo, null, null, function () { + core.events._startGame_afterStart(nowLoc, callback); + }); + + if (route != null) core.startReplay(route); +} + +events.prototype._startGame_afterStart = function (nowLoc, callback) { + core.ui.closePanel(); + core.showStatusBar(); + core.dom.musicBtn.style.display = 'none'; + core.changeFloor(core.firstData.floorId, null, nowLoc, null, function () { + // 插入一个空事件避免直接回放录像出错 + core.insertAction([]); + if (callback) callback(); + }); + this._startGame_upload(); +} + +events.prototype._startGame_upload = function () { + // Upload + var formData = new FormData(); + formData.append('type', 'people'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.string); + formData.append('hard', core.encodeBase64(core.status.hard)); + formData.append('hardCode', core.getFlag('hard', 0)); + formData.append('base64', 1); + + core.utils.http("POST", "/games/upload.php", formData); +} + ////// 不同难度分别设置初始属性 ////// -events.prototype.setInitData = function (hard) { - return this.eventdata.setInitData(hard); +events.prototype.setInitData = function () { + return this.eventdata.setInitData(); } ////// 游戏获胜事件 ////// @@ -196,1168 +113,739 @@ events.prototype.lose = function (reason) { ////// 游戏结束 ////// events.prototype.gameOver = function (ending, fromReplay, norank) { - core.clearMap('all'); core.deleteAllCanvas(); core.dom.gif2.innerHTML = ""; core.setWeather(); core.ui.closePanel(); - if (main.isCompetition && core.isset(ending)) { + if (main.isCompetition && ending != null) { if (ending == "") ending = "恭喜通关"; ending += "[比赛]"; } - var askRate = function () { - if (!core.isset(ending)) { - core.restart(); - return; - } + var reason = null; + if (fromReplay) reason = "录像回放完毕!"; + else if (core.hasFlag("debug")) reason = "\t[系统提示]调试模式下无法上传成绩"; + else if (core.hasFlag("__consoleOpened__")) reason = "\t[系统提示]本存档开启过控制台,无法上传成绩"; - core.ui.closePanel(); - core.ui.drawConfirmBox("恭喜通关本塔,你想进行评分吗?", function () { - if (core.platform.isPC) { - window.open("/score.php?name="+core.firstData.name+"&num=10", "_blank"); - core.restart(); - } - else { - window.location.href = "/score.php?name="+core.firstData.name+"&num=10"; - } - }, function () { - core.restart(); - }); + if (reason != null) + core.drawText(reason, core.restart); + else + this._gameOver_confirmUpload(ending, norank); +} +events.prototype._gameOver_confirmUpload = function (ending, norank) { + core.ui.closePanel(); + + if (ending == null) { + this._gameOver_confirmDownload(ending); + return; } - - // 下载录像 - var confirmDownload = function () { - - core.ui.closePanel(); - core.ui.drawConfirmBox("你想下载录像吗?", function () { - var obj = { - 'name': core.firstData.name, - 'version': core.firstData.version, - 'hard': core.status.hard, - 'seed': core.getFlag('__seed__'), - 'route': core.encodeRoute(core.status.route) - } - core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify(obj)); - // core.restart(); - askRate(); - }, function () { - // core.restart(); - askRate(); - }) - - } - - // 上传成绩 - var confirmUpload = function () { - - core.ui.closePanel(); - - if (!core.isset(ending)) { - confirmDownload(); - return; + core.ui.drawConfirmBox("你想记录你的ID和成绩吗?", function () { + if (main.isCompetition) { + core.events._gameOver_doUpload("", ending, norank); } - - var doUpload = function(username) { - var hp = core.status.hero.hp; - if (username==undefined) hp = 1; - core.ui.closePanel(); - // upload - var formData = new FormData(); - formData.append('type', 'score'); - formData.append('name', core.firstData.name); - formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); - formData.append('hard', core.encodeBase64(core.status.hard)); - formData.append('username', core.encodeBase64(username||"")); - formData.append('ending', core.encodeBase64(ending)); - formData.append('lv', core.status.hero.lv); - formData.append('hp', Math.min(hp, Math.pow(2, 63))); - formData.append('atk', core.status.hero.atk); - formData.append('def', core.status.hero.def); - formData.append('mdef', core.status.hero.mdef); - 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('seed', core.getFlag('__seed__')); - formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime/1000)); - formData.append('route', core.encodeRoute(core.status.route)); - formData.append('base64', 1); - - if (main.isCompetition) - core.http("POST", "/games/competition/upload.php", formData); - else - core.http("POST", "/games/upload.php", formData); - - setTimeout(function() { - confirmDownload(); - }, 150); + else { + core.myprompt("请输入你的ID:", core.getCookie('id') || "", function (username) { + core.events._gameOver_doUpload(username, ending, norank); + }); } + }, function () { + if (main.isCompetition) + core.events._gameOver_confirmDownload(ending); + else + core.events._gameOver_doUpload(null, ending, norank); + }) +} - core.ui.drawConfirmBox("你想记录你的ID和成绩吗?", function () { - if (main.isCompetition) { - doUpload(""); - } - else { - doUpload(prompt("请输入你的ID:", core.getCookie('id')||"")); - } - }, function () { - if (main.isCompetition) - confirmDownload(); - else - doUpload(undefined); - }) +events.prototype._gameOver_doUpload = function (username, ending, norank) { + var hp = core.status.hero.hp; + if (username == null) hp = 1; + core.ui.closePanel(); + // upload + var formData = new FormData(); + formData.append('type', 'score'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.string); + formData.append('hard', core.encodeBase64(core.status.hard)); + formData.append('username', core.encodeBase64(username || "")); + formData.append('ending', core.encodeBase64(ending)); + formData.append('lv', core.status.hero.lv); + formData.append('hp', Math.min(hp, Math.pow(2, 63))); + formData.append('atk', core.status.hero.atk); + formData.append('def', core.status.hero.def); + formData.append('mdef', core.status.hero.mdef); + 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 ? 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)); + formData.append('base64', 1); + if (main.isCompetition) + core.http("POST", "/games/competition/upload.php", formData); + else + core.http("POST", "/games/upload.php", formData); + + core.events._gameOver_confirmDownload(ending); +} + +events.prototype._gameOver_confirmDownload = function (ending) { + core.ui.closePanel(); + core.ui.drawConfirmBox("你想下载录像吗?", function () { + var obj = { + 'name': core.firstData.name, + 'version': core.firstData.version, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + } + core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5route", JSON.stringify(obj)); + core.events._gameOver_askRate(ending); + }, function () { + core.events._gameOver_askRate(ending); + }); +} + +events.prototype._gameOver_askRate = function (ending) { + if (ending == null) { + core.restart(); return; } - if (fromReplay) { - core.drawText("录像回放完毕!", function () { + core.ui.closePanel(); + core.ui.drawConfirmBox("恭喜通关本塔,你想进行评分吗?", function () { + if (core.platform.isPC) { + window.open("/score.php?name=" + core.firstData.name + "&num=10", "_blank"); core.restart(); - }); - } - else { - - if (core.hasFlag('debug')) { - core.drawText("\t[系统提示]调试模式下无法上传成绩", function () { - core.restart(); - }) - } - else if (core.hasFlag('consoleOpened')) { - core.drawText("\t[系统提示]本存档开启过控制台,无法上传成绩", function () { - core.restart(); - }) } else { - confirmUpload(); + window.location.href = "/score.php?name=" + core.firstData.name + "&num=10"; } + }, function () { + core.restart(); + }); +} +////// 重新开始游戏;此函数将回到标题页面 ////// +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(); + }); +} + +// ------ 系统事件的处理 ------ // + +////// 注册一个系统事件 ////// +// type为事件名,func为事件的处理函数,可接受(data, callback)参数 +events.prototype.registerSystemEvent = function (type, func) { + this.systemEvents[type] = func; +} + +////// 注销一个系统事件 ////// +events.prototype.unregisterSystemEvent = function (type) { + delete this.systemEvents[type]; +} + +////// 执行一个系统事件 ////// +events.prototype.doSystemEvent = function (type, data, callback) { + if (this.systemEvents[type]) { + try { + return core.doFunc(this.systemEvents[type], this, data, callback); + } + catch (e) { + main.log(e); + main.log("ERROR in systemEvents["+type+"]"); + } + } + if (this["_sys_" + type]) return this["_sys_" + type](data, callback); + main.log("未知的系统事件: " + type + "!"); + if (callback) callback(); +} + +////// 触发(x,y)点的事件 ////// +events.prototype._trigger = function (x, y) { + // 如果已经死亡,或正处于某事件中,则忽略 + if (core.status.gameOver || core.status.event.id) return; + + var block = core.getBlock(x, y); + if (block == null) return; + block = block.block; + if (block.event.trigger) { + var noPass = block.event.noPass, trigger = block.event.trigger; + if (noPass) core.clearAutomaticRouteNode(x, y); + + // 转换楼层能否穿透 + if (trigger == 'changeFloor' && !noPass && this._trigger_ignoreChangeFloor(block)) + return; + core.status.automaticRoute.moveDirectly = false; + this.doSystemEvent(trigger, block); + } +} + +events.prototype._trigger_ignoreChangeFloor = function (block) { + var able = core.flags.ignoreChangeFloor; + if (block.event.data && block.event.data.ignoreChangeFloor != null) + able = block.event.data.ignoreChangeFloor; + if (able) { + if (core.isReplaying()) { + if (core.status.replay.toReplay[0] == 'no') { + core.status.replay.toReplay.shift(); + core.status.route.push("no"); + return true; + } + } + else if (core.status.automaticRoute.autoHeroMove + || core.status.automaticRoute.autoStep < core.status.automaticRoute.autoStepRoutes.length) { + core.status.route.push("no"); + return true; + } + } + return false; +} + +events.prototype._sys_battle = function (data, callback) { + this.battle(data.event.id, data.x, data.y, false, callback); +} + +////// 战斗 ////// +events.prototype.battle = function (id, x, y, force, callback) { + core.saveAndStopAutomaticRoute(); + id = id || core.getBlockId(x, y); + if (!id) return core.clearContinueAutomaticRoute(callback); + // 非强制战斗 + if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) { + core.drawTip("你打不过此怪物!"); + return core.clearContinueAutomaticRoute(callback); + } + // 自动存档 + if (!core.status.event.id) core.autosave(true); + // 战前事件 + if (!this.beforeBattle(id, x, y)) + return core.clearContinueAutomaticRoute(callback); + // 战后事件 + this.afterBattle(id, x, y, callback); +} + +////// 战斗前触发的事件 ////// +events.prototype.beforeBattle = function (enemyId, x, y) { + return this.eventdata.beforeBattle(enemyId, x, y) +} + +////// 战斗结束后触发的事件 ////// +events.prototype.afterBattle = function (enemyId, x, y, callback) { + return this.eventdata.afterBattle(enemyId, x, y, callback); +} + +events.prototype._sys_openDoor = function (data, callback) { + this.openDoor(data.x, data.y, true, function () { + core.replay(); + if (callback) callback(); + }); +} + +////// 开门 ////// +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 () { + if (!locked) core.unLockControl(); + if (callback) callback(); + }); + return; + } + core.playSound("door.mp3"); + this._openDoor_animate(id, x, y, 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.clearContinueAutomaticRoute(); + return false; } + if (needKey && id.endsWith("Door")) { + var key = id.replace("Door", "Key"); + if (!core.hasItem(key)) { + if (key != "specialKey") + core.drawTip("你没有" + ((core.material.items[key] || {}).name || "钥匙")); + else core.drawTip("无法开启此门"); + core.clearContinueAutomaticRoute(); + return false; + } + if (!core.status.event.id) core.autosave(true); + core.removeItem(key); + } + return true; +} + +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; + var animate = window.setInterval(function () { + state++; + if (state == 4) { + clearInterval(animate); + core.removeBlock(x, y); + if (!locked) core.unLockControl(); + core.status.replay.animate = false; + core.events.afterOpenDoor(id, x, y, callback); + return; + } + core.clearMap('event', 32 * x, 32 * y, 32, 32); + core.drawImage('event', core.material.images.animates, 32 * state, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); + }, speed / core.status.replay.speed); +} + +////// 开一个门后触发的事件 ////// +events.prototype.afterOpenDoor = function (doorId, x, y, callback) { + return this.eventdata.afterOpenDoor(doorId, x, y, callback); +} + +events.prototype._sys_getItem = function (data, callback) { + this.getItem(data.event.id, 1, data.x, data.y, callback); +} + +////// 获得某个物品 ////// +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.afterGetItem(id, x, y, callback); +} + +events.prototype.afterGetItem = function (id, x, y, callback) { + this.eventdata.afterGetItem(id, x, y, callback); +} + +////// 获得面前的物品(轻按) ////// +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') { + if (!noRoute) core.status.route.push("getNext"); + this.getItem(block.block.event.id, 1, nextX, nextY); + return true; + } + return false; +} + +events.prototype._sys_changeFloor = function (data, callback) { + data = data.event.data; + var heroLoc = {}; + 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, function () { + core.replay(); + if (callback) callback(); + }); +} + +////// 楼层切换 ////// +events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback, fromLoad) { + var info = this._changeFloor_getInfo(floorId, stair, heroLoc, time); + if (info == null) { + if (callback) callback(); + return; + } + info.fromLoad = fromLoad; + floorId = info.floorId; + info.locked = core.status.lockControl; + + core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title; + core.lockControl(); + core.stopAutomaticRoute(); + core.clearContinueAutomaticRoute(); + core.status.replay.animate = true; + + this._changeFloor_beforeChange(info, callback); +} + +events.prototype._changeFloor_getInfo = function (floorId, stair, heroLoc, time) { + floorId = floorId || core.status.floorId; + if (floorId == ':before') { + var index = core.floorIds.indexOf(core.status.floorId); + if (index > 0) floorId = core.floorIds[index - 1]; + else floorId = core.status.floorId; + } + else if (floorId == ':next') { + var index = core.floorIds.indexOf(core.status.floorId); + if (index < core.floorIds.length - 1) floorId = core.floorIds[index + 1]; + else floorId = core.status.floorId; + } + if (!core.status.maps[floorId]) { + main.log("不存在的楼层:" + floorId); + return null; + } + + if (main.mode != 'play' || core.isReplaying()) time = 0; + if (time == null) time = core.values.floorChangeTime; + if (time == null) time = 800; + if (time < 100) time = 0; + time /= 20; + + return { + floorId: floorId, + time: time, + heroLoc: core.clone(this._changeFloor_getHeroLoc(floorId, stair, heroLoc)) + }; +} + +events.prototype._changeFloor_getHeroLoc = function (floorId, stair, heroLoc) { + if (!heroLoc) + heroLoc = core.clone(core.status.hero.loc); + if (stair) { + // 检查该层地图的 upFloor & downFloor + if (core.status.maps[floorId][stair]) { + heroLoc.x = core.status.maps[floorId][stair][0]; + heroLoc.y = core.status.maps[floorId][stair][1]; + } + else { + var blocks = core.status.maps[floorId].blocks; + for (var i in blocks) { + if (!blocks[i].disable && blocks[i].event.id === stair) { + heroLoc.x = blocks[i].x; + heroLoc.y = blocks[i].y; + break; + } + } + } + } + ['x', 'y', 'direction'].forEach(function (name) { + if (heroLoc[name] == null) + heroLoc[name] = core.getHeroLoc(name); + }); + return heroLoc; +} + +events.prototype._changeFloor_beforeChange = function (info, callback) { + core.playSound('floor.mp3'); + // 需要 setTimeout 执行,不然会出错 + 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.prototype._changeFloor_changing = function (info, callback) { + this.changingFloor(info.floorId, info.heroLoc, info.fromLoad); + + if (info.time == 0) + this._changeFloor_afterChange(info, callback); + else + core.hideWithAnimate(core.dom.floorMsgGroup, info.time / 4, function () { + core.events._changeFloor_afterChange(info, callback); + }); +} + +events.prototype._changeFloor_afterChange = function (info, callback) { + if (!info.locked) core.unLockControl(); + core.status.replay.animate = false; + core.events.afterChangeFloor(info.floorId, info.fromLoad); + + if (callback) callback(); +} + +events.prototype.changingFloor = function (floorId, heroLoc, fromLoad) { + this.eventdata.changingFloor(floorId, heroLoc, fromLoad); } ////// 转换楼层结束的事件 ////// events.prototype.afterChangeFloor = function (floorId, fromLoad) { - if (main.mode!='play') return; + if (main.mode != 'play') return; return this.eventdata.afterChangeFloor(floorId, fromLoad); } +////// 是否到达过某个楼层 ////// +events.prototype.hasVisitedFloor = function (floorId) { + return core.getFlag("__visited__")[floorId] || false; +} + +////// 到达某楼层 ////// +events.prototype.visitFloor = function (floorId) { + core.getFlag("__visited__")[floorId] = true; +} + +events.prototype._sys_passNet = function (data, callback) { + this.passNet(data); + if (callback) callback(); +} + +////// 经过一个路障 ////// +events.prototype.passNet = function (data) { + if (core.hasItem('shoes')) return; + // 血网 lavaNet 移动到 checkBlock 中处理 + if (data.event.id == 'poisonNet') { // 毒网 + core.insertAction({"type":"insert","name":"毒衰咒处理","args":[0]}); + } + else if (data.event.id == 'weakNet') { // 衰网 + core.insertAction({"type":"insert","name":"毒衰咒处理","args":[1]}); + } + else if (data.event.id == 'curseNet') { // 咒网 + core.insertAction({"type":"insert","name":"毒衰咒处理","args":[2]}); + } + core.updateStatusBar(); +} + +events.prototype._sys_pushBox = function (data, callback) { + this.pushBox(data); + if (callback) callback(); +} + +////// 推箱子 ////// +events.prototype.pushBox = function (data) { + if (data.event.id != 'box' && data.event.id != 'boxed') return; + + // 判断还能否前进,看看是否存在事件 + var direction = core.getHeroLoc('direction'), + nx = data.x + core.utils.scan[direction].x, ny = data.y + core.utils.scan[direction].y; + + // 检测能否推上去 + if (!core.canMoveHero() || !core.canMoveHero(data.x, data.y, direction)) return; + var nextId = core.getBlockId(nx, ny); + if (nextId != null && nextId != 'flower') return; + + core.setBlock(nextId == null ? 169 : 170, nx, ny); + + if (data.event.id == 'box') + core.removeBlock(data.x, data.y); + else + core.setBlock(168, data.x, data.y); + + core.updateStatusBar(); + this._pushBox_moveHero(direction); +} + +events.prototype._pushBox_moveHero = function (direction) { + core.status.replay.animate = true; + core.lockControl(); + setTimeout(function () { + core.moveHero(direction, function () { + core.status.replay.animate = false; + core.status.route.pop(); + core.events.afterPushBox(); + // 可能有阻击... + if (core.status.event.id == null) { + core.unLockControl(); + core.replay(); + } + }); + }); +} + +////// 推箱子后的事件 ////// +events.prototype.afterPushBox = function () { + return this.eventdata.afterPushBox(); +} + +events.prototype._sys_changeLight = function (data, callback) { + core.events.changeLight(data.event.id, data.x, data.y); + if (callback) callback(); +} + +////// 改变亮灯(感叹号)的事件 ////// +events.prototype.changeLight = function (id, x, y) { + if (id != null && id != 'light') return; + core.setBlock(core.getNumberById('darkLight'), x, y); + return this.eventdata.afterChangeLight(x, y); +} + +events.prototype._sys_ski = function (data, callback) { + core.insertAction(["V2.6后,请将滑冰放在背景层!"], data.x, data.y, callback); +} + +events.prototype._sys_action = function (data, callback) { + var ev = core.clone(data.event.data), ex = data.x, ey = data.y; + // 检查是否需要改变朝向 + if (ex == core.nextX() && ey == core.nextY()) { + var dir = core.reverseDirection(); + var id = data.event.id, toId = (data.event.faceIds || {})[dir]; + if (toId && id != toId) { + var number = core.icons.getNumberById(toId); + if (number > 0) + core.setBlock(number, ex, ey); + } + } + this.insertAction(ev, ex, ey, callback); +} + +events.prototype._sys_custom = function (data, callback) { + core.insertAction(["请使用\r[yellow]core.registerSystemEvent('custom', func)\r来处理自己添加的系统触发器!"], + data.x, data.y, callback); +} + +// ------ 自定义事件的处理 ------ // + +////// 注册一个自定义事件 ////// +// type为事件名,func为事件的处理函数,可接受(data, x, y, prefix)参数 +// data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀 +events.prototype.registerEvent = function (type, func) { + this.actions[type] = func; +} + +////// 注销一个自定义事件 +events.prototype.unregisterEvent = function (type) { + delete this.actions[type]; +} + +////// 执行一个自定义事件 +events.prototype.doEvent = function (data, x, y, prefix) { + var type = data.type; + if (this.actions[type]) { + try { + return core.doFunc(this.actions[type], this, data, x, y, prefix); + } + catch (e) { + main.log(e); + main.log("ERROR in actions["+type+"]"); + } + } + if (this["_action_" + type]) return this["_action_" + type](data, x, y, prefix); + core.insertAction("未知的自定义事件: " + type + "!"); + 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) { - if (!core.isset(list)) return; +events.prototype.startEvents = function (list, x, y, callback) { + if (!list) return; if (!(list instanceof Array)) { list = [list]; } - - core.status.event = {'id': 'action', 'data': { - 'list': [ - {"todo": core.clone(list), "total": core.clone(list), "condition": "false"} - ], 'x': x, 'y': y, 'callback': callback - }} - + this.setEvents(list, x, y, callback); // 停止勇士 - core.waitHeroToStop(function() { + core.waitHeroToStop(function () { core.lockControl(); - core.events.doAction(); + core.doAction(); }); } ////// 执行当前自定义事件列表中的下一个事件 ////// -events.prototype.doAction = function() { +events.prototype.doAction = function () { // 清空boxAnimate和UI层 - core.status.boxAnimateObjs = []; - clearInterval(core.status.event.interval); - core.status.event.interval = null; - - core.clearLastEvent(); + core.clearUI(); + // 判定是否执行完毕 + if (this._doAction_finishEvents()) return; + // 当前点坐标和前缀 + var x = core.status.event.data.x, y = core.status.event.data.y; + var prefix = [core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + var current = core.status.event.data.list[0]; + if (this._popEvents(current, prefix)) return; + // 当前要执行的事件 + var data = current.todo.shift(); + core.status.event.data.current = data; + if (typeof data == "string") + data = {"type": "text", "text": data}; + core.status.event.data.type = data.type; + this.doEvent(data, x, y, prefix); + return; +} +events.prototype._doAction_finishEvents = function () { // 事件处理完毕 - if (core.status.event.data.list.length==0) { + if (core.status.event.data.list.length == 0) { var callback = core.status.event.data.callback; core.ui.closePanel(); - if (core.isset(callback)) - callback(); + if (callback) callback(); core.replay(); - return; + return true; } + return false; +} - var x=core.status.event.data.x, y=core.status.event.data.y; - var prefix = [core.status.floorId||"f", core.isset(x)?x:"x", core.isset(y)?y:"y"].join("@"); - - var current = core.status.event.data.list[0]; +events.prototype._popEvents = function (current, prefix) { if (current.todo.length == 0) { // current list is empty if (core.calValue(current.condition, prefix)) { // check condition current.todo = core.clone(current.total); } else { - core.status.event.data.list.shift(); // remove stackc + core.status.event.data.list.shift(); // remove stack } - this.doAction(); - return; + core.doAction(); + return true; } - var data = current.todo.shift(); - core.status.event.data.current = data; - - // 不同种类的事件 - - // 如果是文字:显示 - if (typeof data == "string") { - core.status.event.data.type='text'; - // 如果是正在回放中,不显示 - if (core.isReplaying()) - core.events.doAction(); - else - core.ui.drawTextBox(data); - return; - } - core.status.event.data.type=data.type; - switch (data.type) { - case "text": // 文字/对话 - if (core.isReplaying()) - core.events.doAction(); - else - core.ui.drawTextBox(data.text, data.showAll); - break; - case "autoText": - if (core.isReplaying()) - core.events.doAction(); - else { - core.ui.drawTextBox(data.text); - setTimeout(function () { - core.events.doAction(); - }, data.time || 3000); - } - break; - case "scrollText": // 滚动剧情文本 - if (core.isReplaying()) - core.events.doAction(); - else { - var content = core.replaceText(data.text); - var time = data.time || 5000; - if (data.async) { - core.ui.drawScrollText(content, time); - core.events.doAction(); - } - else { - core.ui.drawScrollText(content, time, function() { - core.events.doAction(); - }); - } - } - break; - case "comment": - this.doAction(); - break; - case "setText": // 设置文本状态 - ["position", "offset", "bold", "titlefont", "textfont", "time"].forEach(function (t) { - if (core.isset(data[t])) core.status.textAttribute[t]=data[t]; - }); - ["background", "title", "text"].forEach(function (t) { - if (core.isset(data[t]) && (data[t] instanceof Array) && data[t].length>=3) { - if (data[t].length==3) data[t].push(1); - core.status.textAttribute[t]=data[t]; - } - if (t=='background' && core.isset(data[t])) { - var img = core.material.images.images[data[t]]; - if (core.isset(img) && img.width==192 && img.height==128) { - core.status.textAttribute[t]=data[t]; - } - } - }); - core.setFlag('textAttribute', core.status.textAttribute); - core.events.doAction(); - break; - case "tip": - core.drawTip(core.replaceText(data.text)); - core.events.doAction(); - break; - case "show": // 显示 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { - if (data.async) { - core.animateBlock(data.loc, 'show', data.time); - this.doAction(); - } - else { - core.animateBlock(data.loc,'show', data.time, function () { - core.events.doAction(); - }); - } - } - else { - data.loc.forEach(function (t) { - core.showBlock(t[0],t[1],data.floorId); - }) - this.doAction(); - } - break; - case "hide": // 消失 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { - data.loc.forEach(function (t) { - core.hideBlock(t[0],t[1],data.floorId); - }); - if (data.async) { - core.animateBlock(data.loc, 'hide', data.time); - this.doAction(); - } - else { - core.animateBlock(data.loc,'hide', data.time, function () { - core.events.doAction(); - }); - } - } - else { - data.loc.forEach(function (t) { - core.removeBlock(t[0],t[1],data.floorId) - }) - this.doAction(); - } - break; - case "setBlock": // 设置某图块 - { - if (core.isset(data.loc)) { - x=core.calValue(data.loc[0], prefix); - y=core.calValue(data.loc[1], prefix); - } - core.setBlock(data.number, x, y, data.floorId); - this.doAction(); - break; - } - case "showFloorImg": // 显示贴图 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - core.maps.setFloorImage("show", data.loc, data.floorId, function() { - core.events.doAction(); - }) - break; - case "hideFloorImg": // 隐藏贴图 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - core.maps.setFloorImage("hide", data.loc, data.floorId, function() { - core.events.doAction(); - }) - break; - case "showBgFgMap": // 显示图层块 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - core.maps.setBgFgMap("show", data.name, data.loc, data.floorId, function() { - core.events.doAction(); - }) - break; - case "hideBgFgMap": // 隐藏图层块 - if (!core.isset(data.loc)) - data.loc = [x,y]; - if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') - && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) - data.loc = [[core.calValue(data.loc[0], prefix), core.calValue(data.loc[1], prefix)]]; - core.maps.setBgFgMap("hide", data.name, data.loc, data.floorId, function() { - core.events.doAction(); - }) - break; - case "setBgFgBlock": // 设置图层块 - { - if (core.isset(data.loc)) { - x=core.calValue(data.loc[0], prefix); - y=core.calValue(data.loc[1], prefix); - } - core.setBgFgBlock(data.name, data.number, x, y, data.floorId); - this.doAction(); - break; - } - case "follow": // 跟随 - if (core.isset(core.material.images.images[data.name]) - && core.material.images.images[data.name].width==128) { - if (!core.isset(core.status.hero.followers)) - core.status.hero.followers = []; - core.status.hero.followers.push({"img": data.name}); - core.control.gatherFollowers(); - core.clearMap('hero'); - core.drawHero(); - } - this.doAction(); - break; - case "unfollow": // 取消跟随 - if (core.isset(core.status.hero.followers)) { - var remove = false; - if (!core.isset(data.name) && core.status.hero.followers.length>0) { - core.status.hero.followers = []; - remove=true; - } - if (core.isset(data.name)) { - for (var i=0;icore.itemCount(itemId)) // 效果 - core.getItem(itemId,value-core.itemCount(itemId)); - else core.setItem(itemId, value); - } - // flag - if (data.name.indexOf("flag:")==0) { - core.setFlag(data.name.substring(5), value); - } - // switch - if (data.name.indexOf("switch:")==0) { - core.setFlag((prefix||"global")+"@"+data.name.substring(7), value); - } - } - catch (e) {main.log(e)} - core.updateStatusBar(); - this.doAction(); - break; - case "setFloor": - core.status.maps[data.floorId||core.status.floorId][data.name] = core.calValue(data.value, prefix); - core.updateStatusBar(); - this.doAction(); - break; - case "setGlobalAttribute": - if (typeof data.value == 'string') { - if ((data.value.charAt(0)=='"' && data.value.charAt(data.value.length-1)=='"') - || (data.value.charAt(0)=="'" && data.value.charAt(data.value.length-1)=="'")) - data.value = data.value.substring(1, data.value.length-1); - // --- 检查 [] - if (data.value.charAt(0) == '[' && data.value.charAt(data.value.length-1)==']') - data.value = eval(data.value); - } - core.status.globalAttribute[data.name] = data.value; - core.control.updateGlobalAttribute(data.name); - core.setFlag('globalAttribute', core.status.globalAttribute); - this.doAction(); - break; - case "setGlobalValue": - core.values[data.name] = data.value; - this.doAction(); - break; - case "setGlobalFlag": - { - var flags = core.getFlag("globalFlags", {}); - flags[data.name] = data.value; - core.flags[data.name] = data.value; - core.setFlag("globalFlags", flags); - core.resize(); - this.doAction(); - break; - } - case "setHeroIcon": - { - this.setHeroIcon(data.name); - this.doAction(); - break; - } - case "input": - { - var value; - if (core.isReplaying()) { - var action = core.status.replay.toReplay.shift(); - if (action.indexOf("input:")==0 ) { - value=parseInt(action.substring(6)); - } - else { - core.stopReplay(); - core.drawTip("录像文件出错"); - return; - } - } - else { - core.interval.onDownInterval = 'tmp'; - value = prompt(core.replaceText(data.text)); - } - value = Math.abs(parseInt(value)||0); - core.status.route.push("input:"+value); - core.setFlag("input", value); - this.doAction(); - } - break; - case "input2": - { - var value; - if (core.isReplaying()) { - var action = core.status.replay.toReplay.shift(); - try { - if (action.indexOf("input2:")!=0) throw new Error("Input2 Error. Current action: "+action); - value = core.decodeBase64(action.substring(7)); - } - catch (e) { - main.log(e); - core.stopReplay(); - core.drawTip("录像文件出错"); - return; - } - } - else { - core.interval.onDownInterval = 'tmp'; - value = prompt(core.replaceText(data.text)); - if (!core.isset(value)) value=""; - } - core.status.route.push("input2:"+core.encodeBase64(value)); - core.setFlag("input", value); - this.doAction(); - } - break; - case "if": // 条件判断 - if (core.calValue(data.condition, prefix)) - core.events.insertAction(data["true"]) - else - core.events.insertAction(data["false"]) - this.doAction(); - break; - case "switch": // 条件选择 - var key = core.calValue(data.condition, prefix) - for (var i = 0; i < data.caseList.length; i++) { - if (data.caseList[i]["case"]=="default" || core.calValue(data.caseList[i]["case"], prefix) == key) { - core.events.insertAction(data.caseList[i].action); - break; - } - } - this.doAction(); - break; - case "choices": // 提供选项 - if (core.isReplaying()) { - if (core.status.replay.toReplay.length==0) { // 回放完毕 - core.status.replay.replaying=false; - core.drawTip("录像回放完毕"); - } - else { - var action = core.status.replay.toReplay.shift(), index; - if (action == 'turn') action = core.status.replay.toReplay.shift(); - if (action.indexOf("choices:")==0 && ((index=parseInt(action.substring(8)))>=0) && index=1000000) { - core.setFlag('type', 1); - var px = parseInt((value-1000000)/1000), py = value%1000; - core.setFlag('px', px); - core.setFlag('py', py); - core.setFlag('x', parseInt(px/32)); - core.setFlag('y', parseInt(py/32)); - } - else if (value>=10000) { - core.setFlag('type', 1); - var x = parseInt((value-10000)/100), y = value%100; - core.setFlag('px', 32*x+16); - core.setFlag('py', 32*y+16); - core.setFlag('x', x); - core.setFlag('y', y); - } - else { - core.setFlag('type', 0); - core.setFlag('keycode', value); - } - core.events.doAction(); - } - else { - core.stopReplay(); - core.drawTip("录像文件出错"); - } - } - break; - case "waitAsync": // 等待所有异步事件执行完毕 - { - var test = window.setInterval(function () { - if (Object.keys(core.animateFrame.asyncId).length==0) { - clearInterval(test); - core.events.doAction(); - } - }, 50); - break; - } - case "revisit": // 立刻重新执行该事件 - { - var block=core.getBlock(x,y); // 重新获得事件 - if (block!=null) { - block = block.block; - if (core.isset(block.event) && block.event.trigger=='action') { - core.status.event.data.list = [ - {"todo": core.clone(block.event.data), "total": core.clone(block.event.data), "condition": "false"} - ]; - } - } - this.doAction(); - break; - } - case "callBook": // 呼出怪物手册 - if (core.isReplaying() || !core.hasItem('book')) { - this.doAction(); - } - else { - var e = core.clone(core.status.event.data); - core.ui.closePanel(); - core.openBook(); - core.status.event.interval = e; - } - break; - case "callSave": // 呼出存档页面 - if (core.isReplaying() || core.hasFlag("__events__")) { - core.removeFlag("__events__"); - this.doAction(); - } - else { - var e = core.clone(core.status.event.data); - core.ui.closePanel(); - core.save(); - core.status.event.interval = e; - } - break; - case "callLoad": // 呼出读档页面 - if (core.isReplaying()) { - this.doAction(); - } - else { - var e = core.clone(core.status.event.data); - core.ui.closePanel(); - core.load(); - core.status.event.interval = e; - } - break; - case "exit": // 立刻结束事件 - core.status.event.data.list = []; - core.events.doAction(); - break; - default: - core.status.event.data.type='text'; - core.ui.drawTextBox("\t[警告]出错啦!\n"+data.type+" 事件不被支持..."); - } - return; + return false; } -////// 往当前事件列表之前添加一个或多个事件 ////// -events.prototype.insertAction = function (action, x, y, callback) { +////// 往当前事件列表之前或之后添加一个或多个事件 ////// +events.prototype.insertAction = function (action, x, y, callback, addToLast) { if (core.hasFlag("__statistics__")) return; + if (core.status.gameOver) return; // ------ 判定commonEvent var commonEvent = this.getCommonEvent(action); - if (core.isset(commonEvent) && commonEvent instanceof Array) { - action = commonEvent; - } - if (!core.isset(action)) return; + if (commonEvent instanceof Array) action = commonEvent; + if (!action) return; if (core.status.event.id != 'action') { - this.doEvents(action, x, y, callback); + this.startEvents(action, x, y, callback); } else { - core.unshift(core.status.event.data.list[0].todo, action) - if (core.isset(x)) core.status.event.data.x=x; - if (core.isset(y)) core.status.event.data.y=y; - if (core.isset(callback)) core.status.event.data.callback=callback; + if (addToLast) + core.push(core.status.event.data.list[0].todo, action) + else + core.unshift(core.status.event.data.list[0].todo, action); + this.setEvents(null, x, y, callback); } } +////// 获得一个公共事件 ////// +events.prototype.getCommonEvent = function (name) { + if (!name || typeof name !== 'string') return null; + return this.commonEvent[name] || null; +} + ////// 恢复一个事件 ////// events.prototype.recoverEvents = function (data) { - if (core.isset(data)) { + if (data) { core.ui.closePanel(); core.lockControl(); core.status.event.id = 'action'; @@ -1370,572 +858,1277 @@ events.prototype.recoverEvents = function (data) { return false; } -////// 获得一个公共事件 ////// -events.prototype.getCommonEvent = function (name) { - if (!core.isset(name) || !(typeof name === 'string')) return null; - return this.commonEvent[name] || null; -} +// ------ 样板提供的的自定义事件 ------ // -////// 获得面前的物品(轻按) ////// -events.prototype.getNextItem = function() { - if (!core.status.heroStop || !core.flags.enableGentleClick) return false; - - if (!core.canMoveHero()) 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.getItem(block.block.event.id, 1, nextX, nextY); - core.status.route.push("getNext"); +events.prototype.__action_checkReplaying = function () { + if (core.isReplaying()) { + core.doAction(); return true; } return false; } -////// 获得某个物品 ////// -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]); - core.updateStatusBar(); - - this.eventdata.afterGetItem(itemId, itemX, itemY, callback); +events.prototype.__action_getLoc = function (loc, x, y, prefix) { + if (loc) { + x = core.calValue(loc[0], prefix); + y = core.calValue(loc[1], prefix); + } + return [x, y]; } -////// 开门 ////// -events.prototype.openDoor = function (id, x, y, needKey, callback) { - if (!core.isset(id)) id = core.getBlockId(x, y); +events.prototype.__action_getHeroLoc = function (loc, prefix) { + return this.__action_getLoc(loc, core.getHeroLoc('x'), core.getHeroLoc('y'), prefix); +} - // 是否存在门 - if (!core.terrainExists(x, y, id) || !(id.endsWith("Door") || id.endsWith("Wall")) - || !core.isset(core.material.icons.animates[id])) { +events.prototype.__action_getLoc2D = function (loc, x, y, prefix) { + if (!(loc && loc[0] instanceof Array)) + loc = [this.__action_getLoc(loc, x, y, prefix)]; + return loc; +} + +events.prototype.__action_doAsyncFunc = function (isAsync, func) { + var parameters = Array.prototype.slice.call(arguments, 2); + if (isAsync) { + func.apply(this, parameters); + core.doAction(); + } + else { + func.apply(this, parameters.concat(core.doAction)); + } +} + +events.prototype._action_text = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + core.ui.drawTextBox(data.text, data.showAll); +} + +events.prototype._action_autoText = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + core.ui.drawTextBox(data.text); + setTimeout(core.doAction, data.time || 3000); +} + +events.prototype._action_scrollText = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, core.ui.drawScrollText, data.text, data.lineHeight || 1.4, data.time || 5000); +} + +events.prototype._action_comment = function (data, x, y, prefix) { + core.doAction(); +} + +events.prototype._action_setText = function (data, x, y, prefix) { + ["position", "offset", "align", "bold", "titlefont", "textfont", "time"].forEach(function (t) { + if (data[t] != null) core.status.textAttribute[t] = data[t]; + }); + ["background", "title", "text"].forEach(function (t) { + if ((data[t] instanceof Array) && data[t].length >= 3) { + if (data[t].length == 3) data[t].push(1); + core.status.textAttribute[t] = data[t]; + } + if (t == 'background') { + var img = core.material.images.images[data[t]]; + if (img && img.width == 192 && img.height == 128) { + core.status.textAttribute[t] = data[t]; + } + } + }); + core.setFlag('textAttribute', core.status.textAttribute); + core.doAction(); +} + +events.prototype._action_tip = function (data, x, y, prefix) { + core.drawTip(core.replaceText(data.text)); + core.doAction(); +} + +events.prototype._action_show = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + if (data.time > 0 && !(data.floorId && data.floorId != core.status.floorId)) { + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, 'show', data.time); + } + else { + data.loc.forEach(function (t) { + core.showBlock(t[0], t[1], data.floorId); + }); + core.doAction(); + } +} + +events.prototype._action_hide = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + if (data.time > 0 && !(data.floorId && data.floorId != core.status.floorId)) { + data.loc.forEach(function (t) { + core.hideBlock(t[0], t[1], data.floorId); + }); + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, 'hide', data.time); + } + else { + data.loc.forEach(function (t) { + core.removeBlock(t[0], t[1], data.floorId) + }); + core.doAction(); + } +} + +events.prototype._action_setBlock = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.setBlock(data.number, loc[0], loc[1], data.floorId); + core.doAction(); +} + +events.prototype._action_showFloorImg = function (data, x, y, prefix) { + core.maps.showFloorImage(this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_hideFloorImg = function (data, x, y, prefix) { + core.maps.hideFloorImage(this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_showBgFgMap = function (data, x, y, prefix) { + core.maps.showBgFgMap(data.name, this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction) +} + +events.prototype._action_hideBgFgMap = function (data, x, y, prefix) { + core.maps.hideBgFgMap(data.name, this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_setBgFgBlock = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.setBgFgBlock(data.name, data.number, loc[0], loc[1], data.floorId); + core.doAction(); +} + +events.prototype._action_follow = function (data, x, y, prefix) { + this.follow(data.name); + core.doAction(); +} + +events.prototype._action_unfollow = function (data, x, y, prefix) { + this.unfollow(data.name); + core.doAction(); +} + +events.prototype._action_animate = function (data, x, y, prefix) { + if (data.loc == 'hero') data.loc = [core.getHeroLoc('x'), core.getHeroLoc('y')]; + else data.loc = this.__action_getLoc(data.loc, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.drawAnimate, data.name, data.loc[0], data.loc[1]); +} + +events.prototype._action_move = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.moveBlock, loc[0], loc[1], data.steps, data.time, data.keep); +} + +events.prototype._action_moveHero = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.eventMoveHero, data.steps, data.time); +} + +events.prototype._action_jump = function (data, x, y, prefix) { + var from = this.__action_getLoc(data.from, x, y, prefix), + to = this.__action_getLoc(data.to, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.jumpBlock, from[0], from[1], to[0], to[1], data.time, data.keep); +} + +events.prototype._action_jumpHero = function (data, x, y, prefix) { + var loc = this.__action_getHeroLoc(data.loc, prefix); + this.__action_doAsyncFunc(data.async, core.jumpHero, loc[0], loc[1], data.time); +} + +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, core.doAction); +} + +events.prototype._action_changePos = function (data, x, y, prefix) { + core.clearMap('hero'); + var loc = this.__action_getHeroLoc(data.loc, prefix); + core.setHeroLoc('x', loc[0]); + core.setHeroLoc('y', loc[1]); + if (data.direction) core.setHeroLoc('direction', data.direction); + core.drawHero(); + core.doAction(); +} + +events.prototype._action_showImage = function (data, x, y, prefix) { + if (core.isReplaying()) data.time = 0; + this.__action_doAsyncFunc(data.async || data.time == 0, this.showImage, + data.code, data.image, data.sloc, data.loc, data.opacity, data.time); +} + +events.prototype._action_showTextImage = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, 0, 0, prefix); + if (core.isReplaying()) data.time = 0; + this.__action_doAsyncFunc(data.async || data.time == 0, this.showImage, + data.code, core.ui.textImage(data.text), loc[0], loc[1], 100, 100, data.opacity, data.time); +} + +events.prototype._action_hideImage = function (data, x, y, prefix) { + if (core.isReplaying()) data.time = 0; + this.__action_doAsyncFunc(data.async || data.time == 0, this.hideImage, data.code, data.time); +} + +events.prototype._action_showGif = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, 0, 0, prefix); + this.showGif(data.name, loc[0], loc[1]); + core.doAction(); +} + +events.prototype._action_moveImage = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, this.moveImage, data.code, data.to, data.opacity, data.time); +} + +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.setCurtain(data.color, data.time); + core.setFlag('__color__', data.color || null); + core.doAction(); + } + else { + core.setCurtain(data.color, data.time, function () { + core.setFlag('__color__', data.color || null); + core.doAction(); + }); + } +} + +events.prototype._action_screenFlash = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.screenFlash, data.color, data.time, data.times); +} + +events.prototype._action_setWeather = function (data, x, y, prefix) { + core.setWeather(data.name, data.level); + if (data.name == 'rain' || data.name == 'snow' || data.name == 'fog') + core.setFlag('__weather__', [data.name, data.level]); + else core.removeFlag('__weather__'); + core.doAction(); +} + +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(loc[0], loc[1], data.needKey, core.doAction); + } + else { + core.removeBlock(loc[0], loc[1], floorId); + core.doAction(); + } +} + +events.prototype._action_closeDoor = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.closeDoor(loc[0], loc[1], data.id, core.doAction); +} + +events.prototype._action_useItem = function (data, x, y, prefix) { + // 考虑到可能覆盖楼传事件的问题,这里不对fly进行检查。 + if (data.id != 'book' && core.canUseItem(data.id)) { + core.useItem(data.id, true, core.doAction); + } + else { + core.drawTip("当前无法使用" + ((core.material.items[data.id] || {}).name || "未知道具")); + core.doAction(); + } +} + +events.prototype._action_openShop = function (data, x, y, prefix) { + core.status.shops[data.id].visited = true; + this.setEvents([]); + if (!core.isReplaying()) + this.openShop(data.id); + if (core.status.event.id == 'action') + core.doAction(); +} + +events.prototype._action_disableShop = function (data, x, y, prefix) { + this.disableQuickShop(data.id); + core.doAction(); +} + +events.prototype._action_battle = function (data, x, y, prefix) { + this.battle(data.id, null, null, true, core.doAction); +} + +events.prototype._action_trigger = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + var block = core.getBlock(loc[0], loc[1]); + if (block != null && block.block.event.trigger) { + block = block.block; + this.setEvents([], block.x, block.y); + if (block.event.trigger == 'action') + this.setEvents(block.event.data); + else { + core.doSystemEvent(block.event.trigger, block, core.doAction); + return; + } + } + core.doAction(); +} + +events.prototype._action_insert = function (data, x, y, prefix) { + // 设置参数 + if (data.args instanceof Array) { + for (var i = 0; i < data.args.length; ++i) { + try { + if (data.args[i] != null) + core.setFlag('arg'+(i+1), data.args[i]); + } catch (e) { main.log(e); } + } + } + if (data.name) { // 公共事件 + core.setFlag('arg0', data.name); + core.insertAction(data.name); + } + else { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.setFlag('arg0', loc); + var floorId = data.floorId || core.status.floorId; + var which = data.which || "events"; + var event = (core.floors[floorId][which]||[])[loc[0] + "," + loc[1]]; + if (event) this.insertAction(event.data || event); + } + core.doAction(); +} + +events.prototype._action_playBgm = function (data, x, y, prefix) { + core.playBgm(data.name); + core.doAction(); +} + +events.prototype._action_pauseBgm = function (data, x, y, prefix) { + core.pauseBgm(); + core.doAction(); +} + +events.prototype._action_resumeBgm = function (data, x, y, prefix) { + core.resumeBgm(); + core.doAction(); +} + +events.prototype._action_loadBgm = function (data, x, y, prefix) { + core.loadBgm(data.name); + core.doAction(); +} + +events.prototype._action_freeBgm = function (data, x, y, prefix) { + core.freeBgm(data.name); + core.doAction(); +} + +events.prototype._action_playSound = function (data, x, y, prefix) { + if (data.stop) core.stopSound(); + core.playSound(data.name); + core.doAction(); +} + +events.prototype._action_stopSound = function (data, x, y, prefix) { + core.stopSound(); + core.doAction(); +} + +events.prototype._action_setVolume = function (data, x, y, prefix) { + data.value = core.clamp(parseInt(data.value) / 100, 0, 1); + core.setFlag("__volume__", data.value); + this.__action_doAsyncFunc(data.async, this.setVolume, data.value, data.time || 0); +} + +events.prototype._action_setValue = function (data, x, y, prefix) { + this.setValue(data.name, data.value, prefix); + core.doAction(); +} + +events.prototype._action_setValue2 = function (data, x, y, prefix) { + this._action_addValue(data, x, y, prefix); +} + +events.prototype._action_addValue = function (data, x, y, prefix) { + this.addValue(data.name, data.value, prefix); + core.doAction(); +} + +events.prototype._action_setFloor = function (data, x, y, prefix) { + this.setFloorInfo(data.name, data.value, data.floorId, prefix); + core.doAction(); +} + +events.prototype._action_setGlobalAttribute = function (data, x, y, prefix) { + this.setGlobalAttribute(data.name, data.value); + core.doAction(); +} + +events.prototype._action_setGlobalValue = function (data, x, y, prefix) { + core.values[data.name] = data.value; + core.doAction(); +} + +events.prototype._action_setGlobalFlag = function (data, x, y, prefix) { + this.setGlobalFlag(data.name, data.value); + core.doAction(); +} + +events.prototype._action_setHeroIcon = function (data, x, y, prefix) { + this.setHeroIcon(data.name); + core.doAction(); +} + +events.prototype._action_input = function (data, x, y, prefix) { + this.__action_getInput(data.text, false, function (value) { + value = Math.abs(parseInt(value) || 0); + core.status.route.push("input:" + value); + core.setFlag("input", value); + core.doAction(); + }); +} + +events.prototype._action_input2 = function (data, x, y, prefix) { + this.__action_getInput(data.text, true, function (value) { + value = value || ""; + core.status.route.push("input2:" + core.encodeBase64(value)); + core.setFlag("input", value); + core.doAction(); + }); +} + +events.prototype.__action_getInput = function (hint, isText, callback) { + var value, prefix = isText ? "input2:" : "input:"; + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(); + try { + if (action.indexOf(prefix) != 0) + throw new Error("录像文件出错!当前需要一个 " + prefix + " 项,实际为 " + action); + if (isText) value = core.decodeBase64(action.substring(7)); + else value = parseInt(action.substring(6)); + callback(value); + } + catch (e) { + main.log(e); + core.stopReplay(); + core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); + core.doAction(); + } + } + else { + core.myprompt(core.replaceText(hint), null, callback); + } +} + +events.prototype._action_if = function (data, x, y, prefix) { + if (core.calValue(data.condition, prefix)) + core.events.insertAction(data["true"]) + else + core.events.insertAction(data["false"]) + core.doAction(); +} + +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) { + core.push(list, data.caseList[i].action); + if (!data.caseList[i].nobreak) + break; + } + } + core.insertAction(list); + core.doAction(); +} + +events.prototype._action_choices = function (data, x, y, prefix) { + 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 < data.choices.length) { + core.status.event.selection = index; + setTimeout(function () { + core.status.route.push("choices:" + index); + core.insertAction(data.choices[index].action); + core.doAction(); + }, 750 / Math.max(1, core.status.replay.speed)) + } + else { + main.log("录像文件出错!当前需要一个 choices: 项,实际为 " + action); + core.stopReplay(); + core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); + core.doAction(); + return; + } + } + 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, + {"todo": core.clone(data.data), "total": core.clone(data.data), "condition": data.condition} + ); + } + core.doAction(); +} + +events.prototype._action_break = function (data, x, y, prefix) { + core.status.event.data.list.shift(); + core.doAction(); +} + +events.prototype._action_continue = function (data, x, y, prefix) { + if (core.calValue(core.status.event.data.list[0].condition, prefix)) { + core.status.event.data.list[0].todo = core.clone(core.status.event.data.list[0].total); + } + else { + core.status.event.data.list.shift(); + } + core.doAction(); +} + +events.prototype._action_win = function (data, x, y, prefix) { + this.win(data.reason, data.norank); +} + +events.prototype._action_lose = function (data, x, y, prefix) { + this.lose(data.reason); +} + +events.prototype._action_function = function (data, x, y, prefix) { + var func = data["function"]; + try { + if (typeof func == "string" && func.indexOf("function") == 0) { + eval('(' + func + ')()'); + } + } catch (e) { + main.log(e); + } + if (!data.async) + core.doAction(); +} + +events.prototype._action_update = function (data, x, y, prefix) { + core.updateStatusBar(); + core.doAction(); +} + +events.prototype._action_showStatusBar = function (data, x, y, prefix) { + core.showStatusBar(); + core.doAction(); +} + +events.prototype._action_hideStatusBar = function (data, x, y, prefix) { + core.hideStatusBar(data.toolbox); + core.doAction(); +} + +events.prototype._action_updateEnemys = function (data, x, y, prefix) { + core.enemys.updateEnemys(); + core.updateStatusBar(); + core.doAction(); +} + +events.prototype._action_vibrate = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, this.vibrate, data.time); +} + +events.prototype._action_sleep = function (data, x, y, prefix) { + core.timeout.sleepTimeout = setTimeout(function () { + core.timeout.sleepTimeout = null; + core.doAction(); + }, core.isReplaying() ? Math.min(data.time, 20) : data.time); +} + +events.prototype._action_wait = function (data, x, y, prefix) { + if (core.isReplaying()) { + var code = core.status.replay.toReplay.shift(); + if (code.indexOf("input:") == 0) { + var value = parseInt(code.substring(6)); + core.status.route.push("input:" + value); + this.__action_wait_getValue(value); + } + else { + main.log("录像文件出错!当前需要一个 input: 项,实际为 " + code); + core.stopReplay(); + core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); + } + core.doAction(); + return; + } +} + +events.prototype.__action_wait_getValue = function (value) { + if (value >= 1000000) { + core.setFlag('type', 1); + var px = parseInt((value - 1000000) / 1000), py = value % 1000; + core.setFlag('px', px); + core.setFlag('py', py); + core.setFlag('x', parseInt(px / 32)); + core.setFlag('y', parseInt(py / 32)); + } + else if (value >= 10000) { + core.setFlag('type', 1); + var x = parseInt((value - 10000) / 100), y = value % 100; + core.setFlag('px', 32 * x + 16); + core.setFlag('py', 32 * y + 16); + core.setFlag('x', x); + core.setFlag('y', y); + } + else if (value > 0) { + core.setFlag('type', 0); + core.setFlag('keycode', value); + } +} + +events.prototype._action_waitAsync = function (data, x, y, prefix) { + var test = window.setInterval(function () { + if (!core.hasAsync()) { + clearInterval(test); + core.doAction(); + } + }, 50); +} + +events.prototype._action_revisit = function (data, x, y, prefix) { + var block = core.getBlock(x, y); + if (block != null && block.block.event.trigger == 'action') + this.setEvents(block.block.event.data); + core.doAction(); +} + +events.prototype._action_callBook = function (data, x, y, prefix) { + if (core.isReplaying() || !core.hasItem('book')) { + core.doAction(); + } + else { + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + core.openBook(); + core.status.event.interval = e; + } +} + +events.prototype._action_callSave = function (data, x, y, prefix) { + if (core.isReplaying() || core.hasFlag("__events__")) { + core.removeFlag("__events__"); + core.doAction(); + } + else { + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + core.save(); + core.status.event.interval = e; + } +} + +events.prototype._action_callLoad = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + core.load(); + core.status.event.interval = e; +} + +events.prototype._action_exit = function (data, x, y, prefix) { + this.setEvents([]); + core.doAction(); +} + +// ------ 点击状态栏图标所进行的一些操作 ------ // + +////// 判断当前能否进入某个事件 ////// +events.prototype._checkStatus = function (name, fromUserAction, checkItem) { + if (fromUserAction && core.status.event.id == name) { + core.ui.closePanel(); return false; } - if (core.status.automaticRoute.moveStepBeforeStop.length==0) { - core.status.automaticRoute.moveStepBeforeStop=core.status.automaticRoute.autoStepRoutes.slice(core.status.automaticRoute.autoStep-1,core.status.automaticRoute.autoStepRoutes.length); - if (core.status.automaticRoute.moveStepBeforeStop.length>=1)core.status.automaticRoute.moveStepBeforeStop[0].step-=core.status.automaticRoute.movedStep; + if (fromUserAction && core.status.lockControl) return false; + if (checkItem && !core.hasItem(name)) { + core.drawTip("你没有" + core.material.items[name].name); + return false; } - - var speed = id.endsWith("Wall")?70:30; - - if (needKey && id.endsWith("Door")) { - var key = id.replace("Door", "Key"); - if (!core.hasItem(key)) { - if (key != "specialKey") - core.drawTip("你没有" + ((core.material.items[key]||{}).name||"钥匙")); - else core.drawTip("无法开启此门"); - core.clearContinueAutomaticRoute(); - return false; - } - core.autosave(true); - core.removeItem(key); + if (core.isMoving()) { + core.drawTip("请先停止勇士行动"); + return false; } - - // open - core.playSound("door.mp3"); - var state = 0; - var door = core.material.icons.animates[id]; - core.lockControl(); - core.stopHero(); - core.stopAutomaticRoute(); - core.status.replay.animate=true; - core.removeGlobalAnimate(x,y); + core.status.event.id = name; + return true; +} + +////// 点击怪物手册时的打开操作 ////// +events.prototype.openBook = function (fromUserAction) { + if (core.isReplaying()) return; + // 如果能恢复事件(从callBook事件触发) + if (core.status.event.id == 'book' && core.events.recoverEvents(core.status.event.interval)) + return; + // 当前是book,且从“浏览地图”打开 + if (core.status.event.id == 'book' && core.status.event.ui) { + core.status.boxAnimateObjs = []; + core.ui.drawMaps(core.status.event.ui); + return; + } + // 从“浏览地图”页面打开 + if (core.status.event.id == 'viewMaps') { + fromUserAction = false; + core.status.event.ui = core.status.event.data; + } + if (!this._checkStatus('book', fromUserAction, true)) return; + core.useItem('book', true); +} + +////// 点击楼层传送器时的打开操作 ////// +events.prototype.useFly = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('fly', fromUserAction, true)) return; + if (core.flags.flyNearStair && !core.nearStair()) { + core.drawTip("只有在楼梯边才能使用传送器"); + core.unLockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + if (!core.canUseItem('fly')) { + core.drawTip("楼层传送器好像失效了"); + core.unLockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + core.useItem('fly', true); + return; +} + +events.prototype.flyTo = function (toId, callback) { + return this.eventdata.flyTo(toId, callback); +} + +////// 点击装备栏时的打开操作 ////// +events.prototype.openEquipbox = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('equipbox', fromUserAction)) return; + core.ui.drawEquipbox(); +} + +////// 点击工具栏时的打开操作 ////// +events.prototype.openToolbox = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('toolbox', fromUserAction)) return; + core.ui.drawToolbox(); +} + +////// 点击快捷商店按钮时的打开操作 ////// +events.prototype.openQuickShop = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('selectShop', fromUserAction)) return; + core.ui.drawQuickShop(); +} + +events.prototype.openKeyBoard = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('keyBoard', fromUserAction)) return; + core.ui.drawKeyBoard(); +} + +////// 点击保存按钮时的打开操作 ////// +events.prototype.save = function (fromUserAction) { + if (core.isReplaying()) return; + if (core.status.event.id == 'save' && core.events.recoverEvents(core.status.event.interval)) + return; + if (!this._checkStatus('save', fromUserAction)) return; + var saveIndex = core.saves.saveIndex; + var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; + core.ui.drawSLPanel(10*page+offset); +} + +////// 点击读取按钮时的打开操作 ////// +events.prototype.load = function (fromUserAction) { + if (core.isReplaying()) return; + var saveIndex = core.saves.saveIndex; + var page=parseInt((saveIndex-1)/5), offset=saveIndex-5*page; + // 游戏开始前读档 + if (!core.isPlaying()) { + core.dom.startPanel.style.display = 'none'; + core.clearStatus(); + core.status.event = {'id': 'load', 'data': null}; + core.status.lockControl = true; + core.ui.drawSLPanel(10*page+offset); + return; + } + if (core.status.event.id == 'load' && core.events.recoverEvents(core.status.event.interval)) + return; + if (!this._checkStatus('load', fromUserAction)) return; + core.ui.drawSLPanel(10*page+offset); +} + +////// 点击设置按钮时的操作 ////// +events.prototype.openSettings = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('settings', fromUserAction)) + return; + core.ui.drawSettings(); +} + +// ------ 一些事件的具体执行过程 ------ // + +events.prototype.hasAsync = function () { + return Object.keys(core.animateFrame.asyncId).length > 0 || (core.status.animateObjs || []).length > 0; +} + +////// 跟随 ////// +events.prototype.follow = function (name) { + core.status.hero.followers = core.status.hero.followers || []; + if (core.material.images.images[name] + && core.material.images.images[name].width == 128) { + core.status.hero.followers.push({"name": name, "img": core.material.images.images[name]}); + core.gatherFollowers(); + core.clearMap('hero'); + core.drawHero(); + } +} + +////// 取消跟随 ////// +events.prototype.unfollow = function (name) { + core.status.hero.followers = core.status.hero.followers || []; + if (!name) { + core.status.hero.followers = []; + } + else { + for (var i = 0; i < core.status.hero.followers.length; i++) { + if (core.status.hero.followers[i].name == name) { + core.status.hero.followers.splice(i, 1); + break; + } + } + } + core.gatherFollowers(); + core.clearMap('hero'); + core.drawHero(); +} + +////// 数值操作 ////// +events.prototype.setValue = function (name, value, prefix, add) { + var value = core.calValue(value, prefix); + if (add) value += core.calValue(name, prefix); + this._setValue_setStatus(name, value); + this._setValue_setItem(name, value); + this._setValue_setFlag(name, value); + this._setValue_setSwitch(name, value, prefix); + core.updateStatusBar(); +} + +events.prototype._setValue_setStatus = function (name, value) { + if (name.indexOf("status:") !== 0) return; + core.setStatus(name.substring(7), value); + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + } +} + +events.prototype._setValue_setItem = function (name, value) { + if (name.indexOf("item:") !== 0) return; + var itemId = name.substring(5), count = core.itemCount(itemId); + if (value > count) core.getItem(itemId, value - count); + else core.setItem(itemId, value); +} + +events.prototype._setValue_setFlag = function (name, value) { + if (name.indexOf("flag:") !== 0) return; + core.setFlag(name.substring(5), value); +} + +events.prototype._setValue_setSwitch = function (name, value, prefix) { + if (name.indexOf("switch:") !== 0) return; + core.setFlag((prefix || ":f@x@y") + "@" + name.substring(7), value); +} + +////// 数值增减 ////// +events.prototype.addValue = function (name, value, prefix) { + this.setValue(name, value, prefix, true); +} + +////// 执行一个表达式的effect操作 ////// +events.prototype.doEffect = function (effect, need, times) { + effect.split(";").forEach(function (expression) { + var arr = expression.split("+="); + if (arr.length != 2) return; + var name=arr[0], value=core.calValue(arr[1], null, need, times); + core.addValue(name, value); + }); +} + +////// 设置楼层属性 ////// +events.prototype.setFloorInfo = function (name, value, floorId, prefix) { + floorId = floorId || data.floorId; + core.status.maps[floorId][name] = core.calValue(value, prefix); + core.updateStatusBar(); +} + +////// 设置全塔属性 ////// +events.prototype.setGlobalAttribute = function (name, value) { + if (typeof value == 'string') { + if ((value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') + || (value.charAt(0) == "'" && value.charAt(value.length - 1) == "'")) + value = value.substring(1, value.length - 1); + // --- 检查 [] + if (value.charAt(0) == '[' && value.charAt(value.length - 1) == ']') + value = eval(value); + } + core.status.globalAttribute[name] = value; + core.updateGlobalAttribute(name); + core.setFlag('globalAttribute', core.status.globalAttribute); +} + +////// 设置全局开关 ////// +events.prototype.setGlobalFlag = function (name, value) { + var flags = core.getFlag("globalFlags", {}); + flags[name] = value; + core.flags[name] = value; + core.setFlag("globalFlags", flags); + core.resize(); +} + +events.prototype.closeDoor = function (x, y, id, callback) { + id = id || ""; + if (!(id.endsWith("Door") || id.endsWith("Wall")) + || !core.material.icons.animates[id] || core.getBlock(x, y) != null) { + if (callback) callback(); + return; + } + // 关门动画 + core.playSound('door.mp3'); + var door = core.material.icons.animates[id]; + var speed = id.endsWith("Door") ? 30 : 70, state = 0; var animate = window.setInterval(function () { state++; if (state == 4) { clearInterval(animate); - core.removeBlock(x, y); - core.unLockControl(); - core.status.replay.animate=false; - core.events.afterOpenDoor(id,x,y,callback); + delete core.animateFrame.asyncId[animate]; + core.setBlock(core.getNumberById(id), x, y); + if (callback) callback(); return; } core.clearMap('event', 32 * x, 32 * y, 32, 32); - core.drawImage('event', core.material.images.animates, 32 * state, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); + core.drawImage('event', core.material.images.animates, 32 * (4-state), 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); }, speed / core.status.replay.speed); - - return true; -} - -////// 战斗 ////// -events.prototype.battle = function (id, x, y, force, callback) { - if (core.status.automaticRoute.moveStepBeforeStop.length==0) { - core.status.automaticRoute.moveStepBeforeStop=core.status.automaticRoute.autoStepRoutes.slice(core.status.automaticRoute.autoStep-1,core.status.automaticRoute.autoStepRoutes.length); - if (core.status.automaticRoute.moveStepBeforeStop.length>=1)core.status.automaticRoute.moveStepBeforeStop[0].step-=core.status.automaticRoute.movedStep; - } - core.stopHero(); - core.stopAutomaticRoute(); - - if (!core.isset(id)) id = core.getBlockId(x, y); - if (!core.isset(id)) { - if (core.isset(callback)) callback(); - return; - } - - // 非强制战斗 - if (!core.enemys.canBattle(id, x, y) && !force && !core.isset(core.status.event.id)) { - core.drawTip("你打不过此怪物!"); - core.clearContinueAutomaticRoute(); - if (core.isset(callback)) callback(); - return; - } - - if (!core.isset(core.status.event.id)) // 自动存档 - core.autosave(true); - - // ------ 支援技能 ------// - if (core.isset(x) && core.isset(y)) { - var index = x + "," + y, cache = (core.status.checkBlock.cache || {})[index] || {}, - guards = cache.guards || []; - if (guards.length>0) { - core.setFlag("__guards__"+x+"_"+y, guards); - var actions = []; - guards.forEach(function (g) { - core.push(actions, {"type": "jump", "from": [g[0],g[1]], "to": [x, y], - "time": 300, "keep": false, "async": true}); - }) - core.push(actions, [ - {"type": "waitAsync"}, - {"type": "trigger", "loc": [x,y]} - ]); - core.insertAction(actions); - return; - } - } - - core.events.afterBattle(id, x, y, callback); -} - -////// 触发(x,y)点的事件 ////// -events.prototype.trigger = function (x, y) { - - if (core.status.event.id == 'action') return; - - core.status.isSkiing = false; - var block = core.getBlock(x, y); - if (block != null) { - block = block.block; - if (core.isset(block.event) && core.isset(block.event.trigger)) { - var noPass = block.event.noPass, trigger = block.event.trigger; - if (noPass) { - core.clearAutomaticRouteNode(x, y); - } - if (trigger == 'ski') core.status.isSkiing = true; - - // 转换楼层能否穿透 - if (trigger=='changeFloor' && !noPass) { - var canCross = core.flags.portalWithoutTrigger; - if (core.isset(block.event.data) && core.isset(block.event.data.portalWithoutTrigger)) - canCross=block.event.data.portalWithoutTrigger; - if (canCross) { - if (core.isReplaying()) { - if (core.status.replay.toReplay[0]=='no') { - core.status.replay.toReplay.shift(); - core.status.route.push("no"); - return; - } - } - else if (core.status.automaticRoute.autoHeroMove || core.status.automaticRoute.autoStep0) floorId = core.floorIds[index-1]; - else floorId=core.status.floorId; - } - else if (floorId == ':next') { - var index=core.floorIds.indexOf(core.status.floorId); - if (index=100 && !core.isReplaying(); - - time /= 20; - core.lockControl(); - core.stopHero(); - core.stopAutomaticRoute(); - core.clearContinueAutomaticRoute(); - core.status.replay.animate=true; - core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title; - if (!core.isset(stair) && !core.isset(heroLoc)) - heroLoc = core.clone(core.status.hero.loc); - if (core.isset(stair)) { - if (!core.isset(heroLoc)) heroLoc={}; - - if (core.isset(core.status.maps[floorId][stair])) { - heroLoc.x = core.status.maps[floorId][stair][0]; - heroLoc.y = core.status.maps[floorId][stair][1]; - } - else { - var blocks = core.status.maps[floorId].blocks; - for (var i in blocks) { - if (core.isset(blocks[i].event) && !blocks[i].disable && blocks[i].event.id === stair) { - heroLoc.x = blocks[i].x; - heroLoc.y = blocks[i].y; - break; - } - } - } - if (!core.isset(heroLoc.x)) { - heroLoc.x=core.status.hero.loc.x; - heroLoc.y=core.status.hero.loc.y; - } - } - if (!core.isset(heroLoc.direction)) heroLoc.direction = core.status.hero.loc.direction; - - if (core.status.maps[floorId].canFlyTo && core.status.hero.flyRange.indexOf(floorId)<0) { - core.status.hero.flyRange.push(floorId); - core.status.hero.flyRange.sort(function (a, b) { - return core.floorIds.indexOf(a) - core.floorIds.indexOf(b); - }) - } - - window.setTimeout(function () { - - var changing = function () { - - core.events.eventdata.changingFloor(floorId, heroLoc, fromLoad); - - var changed = function () { - core.unLockControl(); - core.status.replay.animate=false; - core.events.afterChangeFloor(floorId, fromLoad); - if (core.isset(callback)) callback(); - } - if (displayAnimate) { - core.hide(core.dom.floorMsgGroup, time/4, function () { - changed(); - }); - } - else { - changed(); - } - - } - core.playSound('floor.mp3'); - if (displayAnimate) { - core.show(core.dom.floorMsgGroup, time/2, function () { - changing(); - }); - } - else { - changing(); - } - }, 25); + core.animateFrame.asyncId[animate] = true; } ////// 显示图片 ////// -events.prototype.showImage = function (code, image, x, y, dw, dh, opacityVal, time, callback) { - dw /= 100; - dh /= 100; - x = core.calValue(x) || 0; - y = core.calValue(y) || 0; +events.prototype.showImage = function (code, image, sloc, loc, opacityVal, time, callback) { + if (typeof image == 'string') image = core.material.images.images[image]; + if (!image) { + if (callback) callback(); + return; + } + sloc = sloc || []; + var sx = core.calValue(sloc[0]) || 0, sy = core.calValue(sloc[1]) || 0; + var sw = core.calValue(sloc[2]), sh = core.calValue(sloc[3]); + if (sw == null) sw = image.width; + if (sh == null) sh = image.height; + loc = loc || []; + var x = core.calValue(loc[0]) || 0, y = core.calValue(loc[1]) || 0; + var w = core.calValue(loc[2]), h = core.calValue(loc[3]); + if (w == null) w = sw; + if (h == null) h = sh; var zIndex = code + 100; time = time || 0; - var name = "image"+ zIndex; - var ctx = core.createCanvas(name, x, y, image.width * dw, image.height * dh, zIndex); - - ctx.drawImage(image, 0, 0, image.width * dw, image.height * dh); - if (time == 0) + var name = "image" + zIndex; + var ctx = core.createCanvas(name, x, y, w, h, zIndex); + ctx.drawImage(image, sx, sy, sw, sh, 0, 0, w, h); + if (time == 0) { core.setOpacity(name, opacityVal); - else { - var per_time = 10, steps = parseInt(time / per_time), per_add = opacityVal / steps; - var opacity = 0; - core.setOpacity(name, 0); - var animate = setInterval(function () { - opacity += per_add; - core.setOpacity(name, opacity); - if (opacity >= opacityVal) { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.setOpacity(name, opacityVal); - if (core.isset(callback)) callback(); - } - }, per_time); - - core.animateFrame.asyncId[animate] = true; + if (callback) callback(); + return; } + core.setOpacity(name, 0); + this.moveImage(code, null, opacityVal, time, callback); } ////// 隐藏图片 ////// events.prototype.hideImage = function (code, time, callback) { time = time || 0; - var name = "image"+ (code+100); - if (!core.isset(core.dymCanvas[name])) { - if (core.isset(callback)) callback(); + var name = "image" + (code + 100); + if (time == 0 || !core.dymCanvas[name]) { + core.deleteCanvas(name); + if (callback) callback(); return; } - if (time == 0) + this.moveImage(code, null, 0, time, function () { core.deleteCanvas(name); - else { - var opacityVal = parseFloat(core.dymCanvas[name].canvas.style.opacity); - var per_time = 10, steps = parseInt(time / per_time), per_add = opacityVal / steps; - var animate = setInterval(function () { - opacityVal -= per_add; - core.setOpacity(name, opacityVal); - if (opacityVal < 0) { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.deleteCanvas(name); - if (core.isset(callback)) callback(); - } - }, per_time); - - core.animateFrame.asyncId[animate] = true; - } -} - -////// 文本图片化 ////// -events.prototype.textImage = function (content) { - content = content || ""; - - // 获得颜色的盒子等信息 - var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; - var textfont = textAttribute.textfont || 16; - var offset = textAttribute.offset || 15; - var textColor = core.arrayToRGBA(textAttribute.text); - - var font = textfont+"px "+core.status.globalAttribute.font; - if (textAttribute.bold) font = "bold "+font; - var contents = core.splitLines('ui', content), lines = contents.length; - - // 计算总高度,按1.4倍行距计算 - var width = 416, height = textfont * 1.4 * lines; - var tempCanvas = core.bigmap.tempCanvas; - tempCanvas.canvas.width = width; - tempCanvas.canvas.height = height; - tempCanvas.clearRect(0, 0, width, height); - tempCanvas.font = font; - tempCanvas.fillStyle = textColor; - - // 全部绘制 - var currH = textfont; - for (var i = 0; i < lines; ++i) { - var text = contents[i]; - tempCanvas.fillText(text, offset, currH); - currH += 1.4 * textfont; - } - - return tempCanvas.canvas; + if (callback) callback(); + }); } ////// 移动图片 ////// events.prototype.moveImage = function (code, to, opacityVal, time, callback) { time = time || 1000; - - var name = "image"+ (code+100); - if (!core.isset(core.dymCanvas[name])) { - if (core.isset(callback)) callback(); + to = to || []; + var name = "image" + (code + 100); + if (!core.dymCanvas[name]) { + if (callback) callback(); return; } - var fromX = parseFloat(core.dymCanvas[name].canvas.getAttribute("_left")), - fromY = parseFloat(core.dymCanvas[name].canvas.getAttribute("_top")), - preX = fromX, preY = fromY, toX = fromX, toY = fromY; + var getOrDefault = function (a, b) { + a = core.calValue(a); + return a != null ? a : b; + } + var canvas = core.dymCanvas[name].canvas; + var fromX = parseFloat(canvas.getAttribute("_left")), + fromY = parseFloat(canvas.getAttribute("_top")), + toX = getOrDefault(to[0], fromX), toY = getOrDefault(to[1], fromY); - if (core.isset(to)) { - toX = core.calValue(to[0]); - toY = core.calValue(to[1]); - if (!core.isset(toX)) toX = fromX; - if (!core.isset(toY)) toY = fromY; - } + var opacity = parseFloat(canvas.style.opacity), toOpacity = getOrDefault(opacityVal, opacity); - var step = 0; - var per_time = 10, steps = parseInt(time / per_time); - var preOpac = parseFloat(core.dymCanvas[name].canvas.style.opacity), opacStep = 0; - if (core.isset(opacityVal)) { - opacityVal = core.calValue(opacityVal); - opacStep = (opacityVal - preOpac) / steps; - } - - var moveStep = function () { - preOpac += opacStep; - core.setOpacity(name, preOpac); - preX = parseInt(fromX + (toX-fromX)*step/steps); - preY = parseInt(fromY + (toY-fromY)*step/steps); - core.relocateCanvas(name, preX, preY); - } + this._moveImage_moving(name, { + fromX: fromX, fromY: fromY, toX: toX, toY: toY, opacity: opacity, toOpacity: toOpacity, time: time + }, callback) +} + +events.prototype._moveImage_moving = function (name, moveInfo, callback) { + var per_time = 10, step = 0, steps = parseInt(moveInfo.time / 10); + var fromX = moveInfo.fromX, fromY = moveInfo.fromY, toX = moveInfo.toX, toY = moveInfo.toY, + opacity = moveInfo.opacity, toOpacity = moveInfo.toOpacity; + var currX = fromX, currY = fromY, currOpacity = opacity; var animate = setInterval(function () { step++; - moveStep(); + currOpacity = opacity + (toOpacity - opacity) * step / steps; + currX = parseInt(fromX + (toX - fromX) * step / steps); + currY = parseInt(fromY + (toY - fromY) * step / steps); + core.setOpacity(name, currOpacity); + core.relocateCanvas(name, currX, currY); if (step == steps) { - if (core.isset(opacityVal)) - core.setOpacity(name, opacityVal); + core.setOpacity(name, toOpacity); delete core.animateFrame.asyncId[animate]; clearInterval(animate); - if (core.isset(callback)) callback(); + if (callback) callback(); } }, per_time); - 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) { core.musicStatus.volume = value; - if (core.isset(core.musicStatus.playingBgm)) { + if (core.musicStatus.playingBgm) core.material.bgms[core.musicStatus.playingBgm].volume = value; - } - // core.musicStatus.gainNode.gain.value = value; } - - if (!core.isset(time) || time<100) { + if (!time || time < 100) { set(value); - if (core.isset(callback)) callback(); + if (callback) callback(); return; } - var currVolume = core.musicStatus.volume; var per_time = 10, step = 0, steps = parseInt(time / per_time); var fade = setInterval(function () { step++; - var nowVolume = currVolume+(value-currVolume)*step/steps; - set(nowVolume); - if (step>=steps) { + set(currVolume + (value - currVolume) * step / steps); + if (step >= steps) { delete core.animateFrame.asyncId[fade]; clearInterval(fade); - if (core.isset(callback)) - callback(); + if (callback) callback(); } }, per_time); - core.animateFrame.asyncId[fade] = true; } ////// 画面震动 ////// -events.prototype.vibrate = function(time, callback) { - +events.prototype.vibrate = function (time, callback) { if (core.isReplaying()) { - if (core.isset(callback)) callback(); + if (callback) callback(); return; } - - // core.status.replay.animate=true; - - var addGameCanvasTranslate=function(x,y){ - for(var ii=0,canvas;canvas=core.dom.gameCanvas[ii];ii++){ - var id = canvas.getAttribute('id'); - if (id=='ui' || id=='data') continue; - var offsetX = x, offsetY = y; - if (core.bigmap.canvas.indexOf(id)>=0) { - offsetX-=core.bigmap.offsetX; - offsetY-=core.bigmap.offsetY; - } - core.control.setGameCanvasTranslate(id, offsetX, offsetY); - } - } - - if (!core.isset(time) || time<1000) time=1000; - - var shake_duration = time*3/50; - var shake_speed = 5; - var shake_power = 5; - var shake_direction = 1; - var shake = 0; - - var update = function() { - if(shake_duration >= 1 || shake != 0){ - var delta = (shake_power * shake_speed * shake_direction) / 10.0; - if(shake_duration <= 1 && shake * (shake + delta) < 0){ - shake = 0; - }else{ - shake += delta; - } - if(shake > shake_power * 2){ - shake_direction = -1; - } - if(shake < - shake_power * 2){ - shake_direction = 1; - } - if(shake_duration >= 1){ - shake_duration -= 1 - } - } - } - - var animate=setInterval(function(){ - update(); - addGameCanvasTranslate(shake, 0); - if(shake_duration===0) { + if (!time || time < 1000) time = 1000; + // --- 将time调整为500的倍数(上整),不然会出错 + time = Math.ceil(time / 500) * 500; + var shakeInfo = {duration: time * 3 / 50, speed: 5, power: 5, direction: 1, shake: 0}; + var animate = setInterval(function () { + core.events._vibrate_update(shakeInfo); + core.control.addGameCanvasTranslate(shakeInfo.shake, 0); + if (shakeInfo.duration === 0) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); - // core.status.replay.animate=false; - if (core.isset(callback)) callback(); + if (callback) callback(); } - }, 50/3); + }, 50 / 3); core.animateFrame.asyncId[animate] = true; } +events.prototype._vibrate_update = function (shakeInfo) { + if (shakeInfo.duration >= 1 || shakeInfo.shake != 0) { + var delta = (shakeInfo.power * shakeInfo.speed * shakeInfo.direction) / 10.0; + if (shakeInfo.duration <= 1 && shakeInfo.shake * (shakeInfo.shake + delta) < 0) { + shakeInfo.shake = 0; + } else { + shakeInfo.shake += delta; + } + if (shakeInfo.shake > shakeInfo.power * 2) { + shakeInfo.direction = -1; + } + if (shakeInfo.shake < -shakeInfo.power * 2) { + shakeInfo.direction = 1; + } + if (shakeInfo.duration >= 1) { + shakeInfo.duration -= 1 + } + } +} + +/////// 使用事件让勇士移动。这个函数将不会触发任何事件 ////// +events.prototype.eventMoveHero = function(steps, time, callback) { + time = time || core.values.moveSpeed || 100; + var step = 0, moveSteps = (steps||[]).filter(function (t) { + return ['up','down','left','right','forward','backward'].indexOf(t)>=0; + }); + var animate=window.setInterval(function() { + if (moveSteps.length==0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + core.drawHero(); + if (callback) callback(); + } + else { + if (core.events._eventMoveHero_moving(++step, moveSteps)) + step = 0; + } + }, time / 8 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; +} + +events.prototype._eventMoveHero_moving = function (step, moveSteps) { + var direction = moveSteps[0], x = core.getHeroLoc('x'), y = core.getHeroLoc('y'); + // ------ 前进/后退 + var o = direction == 'backward' ? -1 : 1; + if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction'); + core.setHeroLoc('direction', direction); + if (step <= 4) { + core.drawHero('leftFoot', 4 * o * step); + } + else if (step <= 8) { + core.drawHero('rightFoot', 4 * o * step); + } + if (step == 8) { + core.setHeroLoc('x', x + o * core.utils.scan[direction].x, true); + core.setHeroLoc('y', y + o * core.utils.scan[direction].y, true); + core.updateFollowers(); + moveSteps.shift(); + return true; + } + return false; +} + +////// 勇士跳跃事件 ////// +events.prototype.jumpHero = function (ex, ey, time, callback) { + var sx = core.getHeroLoc('x'), sy = core.getHeroLoc('y'); + if (ex == null) ex = sx; + if (ey == null) ey = sy; + 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.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; + + this._jumpHero_doJump(jumpInfo, callback); +} + +events.prototype._jumpHero_doJump = function (jumpInfo, callback) { + var animate = window.setInterval(function () { + if (jumpInfo.jump_count > 0) + core.events._jumpHero_jumping(jumpInfo) + else + core.events._jumpHero_finished(animate, jumpInfo.ex, jumpInfo.ey, callback); + }, jumpInfo.per_time); + + core.animateFrame.asyncId[animate] = true; +} + +events.prototype._jumpHero_jumping = function (jumpInfo) { + core.clearMap('hero'); + core.maps.__updateJumpInfo(jumpInfo); + var nowx = jumpInfo.px, nowy = jumpInfo.py, height = jumpInfo.height; + core.bigmap.offsetX = core.clamp(nowx - 32*core.__HALF_SIZE__, 0, 32*core.bigmap.width-core.__PIXELS__); + core.bigmap.offsetY = core.clamp(nowy - 32*core.__HALF_SIZE__, 0, 32*core.bigmap.height-core.__PIXELS__); + core.control.updateViewport(); + core.drawImage('hero', core.material.images.hero, jumpInfo.icon.stop, jumpInfo.icon.loc * height, 32, height, + nowx - core.bigmap.offsetX, nowy + 32-height - core.bigmap.offsetY, 32, height); +} + +events.prototype._jumpHero_finished = function (animate, ex, ey, callback) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + core.setHeroLoc('x', ex); + core.setHeroLoc('y', ey); + core.drawHero(); + if (callback) callback(); +} + ////// 打开一个全局商店 ////// -events.prototype.openShop = function(shopId, needVisited) { +events.prototype.openShop = function (shopId, needVisited) { var shop = core.status.shops[shopId]; shop.times = shop.times || 0; - if (shop.commonTimes) - shop.times = core.getFlag('commonTimes', 0); - shop.visited = shop.visited || false; - + if (shop.commonTimes) shop.times = core.getFlag('commonTimes', 0); if (needVisited && !shop.visited) { - if (!core.flags.enableDisabledShop) { - if (shop.times==0) core.drawTip("该商店尚未开启"); - else core.drawTip("该商店已失效"); + if (!core.flags.enableDisabledShop || shop.commonEvent) { + if (shop.times == 0) core.drawTip("该项尚未开启"); + else core.drawTip("该项已失效"); return; } else { @@ -1944,90 +2137,42 @@ events.prototype.openShop = function(shopId, needVisited) { } else shop.visited = true; - var selection = core.status.event.selection; - var actions = []; - if (core.isset(core.status.event.data) && core.isset(core.status.event.data.actions)) - actions=core.status.event.data.actions; - var fromList; - if (core.isset(core.status.event.data) && core.isset(core.status.event.data.fromList)) - fromList = core.status.event.data.fromList; - - core.ui.closePanel(); - core.lockControl(); - // core.status.event = {'id': 'shop', 'data': {'id': shopId, 'shop': shop}}; - core.status.event.id = 'shop'; - core.status.event.data = {'id': shopId, 'shop': shop, 'actions': actions, 'fromList': fromList}; - core.status.event.selection = selection; - - // 拼词 - var content = "\t["+shop.name+","+shop.icon+"]"; - var times = shop.times, need=core.calValue(shop.need, null, null, times); - - content += core.replaceText(shop.text, need, times); - - var use = shop.use=='experience'?'经验':'金币'; - - var choices = []; - for (var i=0;i core.getStatus(use)) { + core.drawTip("你的" + (use == 'money' ? "金币" : "经验") + "不足"); return false; } - - var money = core.getStatus('money'), experience = core.getStatus('experience'); - var times = shop.times, need = core.calValue(shop.need, null, null, times); - var use = shop.use; - var use_text = use=='money'?"金币":"经验"; - - var choice = shop.choices[index]; - if (core.isset(choice.need)) - need = core.calValue(choice.need, null, null, times); - - if (need > eval(use)) { - core.drawTip("你的"+use_text+"不足"); - return false; - } - core.status.event.selection = index; core.status.event.data.actions.push(index); - - eval(use+'-='+need); - core.setStatus('money', money); - core.setStatus('experience', experience); - - // 更新属性 - choice.effect.split(";").forEach(function (t) { - core.doEffect(t, need, times); - }); + core.setStatus(use, core.getStatus(use) - need); + core.doEffect(choice.effect, need, times); core.updateStatusBar(); shop.times++; - if (shop.commonTimes) - core.setFlag('commonTimes', shop.times); - core.events.openShop(shop.id); + if (shop.commonTimes) core.setFlag('commonTimes', shop.times); + this.openShop(shop.id); return true; } events.prototype._exitShop = function () { - if (core.status.event.data.actions.length>0) { - core.status.route.push("shop:"+core.status.event.data.id+":"+core.status.event.data.actions.join("")); + if (core.status.event.data.actions.length > 0) { + core.status.route.push("shop:" + core.status.event.data.id + ":" + core.status.event.data.actions.join("")); } core.status.event.data.actions = []; core.status.boxAnimateObjs = []; @@ -2043,220 +2188,60 @@ events.prototype.disableQuickShop = function (shopId) { } ////// 能否使用快捷商店 ////// -events.prototype.canUseQuickShop = function(shopId) { +events.prototype.canUseQuickShop = function (shopId) { return this.eventdata.canUseQuickShop(shopId); } ////// 设置角色行走图 ////// events.prototype.setHeroIcon = function (name, noDraw) { - if (core.isset(core.material.images.images[name]) && core.material.images.images[name].width==128) { - core.setFlag("heroIcon", name); - core.material.images.hero.onload = function () { - core.material.icons.hero.height = core.material.images.images[name].height/4; - core.control.updateHeroIcon(name); - if (!noDraw) core.drawHero(); - } - core.material.images.hero.src = core.material.images.images[name].src; + var img = core.material.images.images[name]; + if (!img || img.width != 128) return; + core.setFlag("heroIcon", name); + core.material.images.hero.onload = function () { + core.material.icons.hero.height = img.height / 4; + core.control.updateHeroIcon(name); + if (!noDraw) core.drawHero(); } + core.material.images.hero.src = img.src; } ////// 检查升级事件 ////// events.prototype.checkLvUp = function () { - var check = function () { - if (!core.flags.enableLevelUp || !core.isset(core.firstData.levelUp) - || core.status.hero.lv>=core.firstData.levelUp.length) return null; - // 计算下一个所需要的数值 - var next = (core.firstData.levelUp[core.status.hero.lv]||{}); - var need = core.calValue(next.need); - if (!core.isset(need)) return null; - if (core.status.hero.experience>=need) { - // 升级 - core.status.hero.lv++; - if (next.clear) core.status.hero.experience -= need; - return next.action||[]; - } - return null; - } var actions = []; while (true) { - var next = check(); + var next = this._checkLvUp_check(); if (next == null) break; actions = actions.concat(next); } - if (actions.length>0) core.insertAction(actions); + if (actions.length > 0) core.insertAction(actions); +} + +events.prototype._checkLvUp_check = function () { + if (!core.flags.enableLevelUp || !core.firstData.levelUp + || core.status.hero.lv >= core.firstData.levelUp.length) return null; + // 计算下一个所需要的数值 + var next = (core.firstData.levelUp[core.status.hero.lv] || {}); + var need = core.calValue(next.need); + if (need == null) return null; + if (core.status.hero.experience >= need) { + // 升级 + core.status.hero.lv++; + if (next.clear) core.status.hero.experience -= need; + return next.action || []; + } + return null; } ////// 尝试使用道具 ////// -events.prototype.tryUseItem = function(itemId) { +events.prototype.tryUseItem = function (itemId) { core.ui.closePanel(); - if (itemId=='book') { - core.openBook(false); - return; - } - if (itemId=='fly') { - core.useFly(false); - return; - } - if (itemId=='centerFly') { - core.lockControl(); - core.status.event.id = 'centerFly'; - var fillstyle = 'rgba(255,0,0,0.5)'; - if (core.canUseItem('centerFly')) fillstyle = 'rgba(0,255,0,0.5)'; - var toX = core.bigmap.width-1 - core.getHeroLoc('x'), toY = core.bigmap.height-1-core.getHeroLoc('y'); - core.ui.drawThumbnail(core.status.floorId, 'ui', core.status.thisMap.blocks, 0, 0, 416, toX, toY, core.status.hero.loc, core.getFlag('heroIcon', "hero.png")); - var offsetX = core.clamp(toX-6, 0, core.bigmap.width-13), offsetY = core.clamp(toY-6, 0, core.bigmap.height-13); - core.fillRect('ui',(toX-offsetX)*32,(toY-offsetY)*32,32,32,fillstyle); - core.status.event.data = {"x": toX, "y": toY, "posX": toX-offsetX, "posY": toY-offsetY}; - core.drawTip("请确认当前中心对称飞行器的位置"); - return; - } + if (itemId == 'book') return core.openBook(false); + if (itemId == 'fly') return core.useFly(false); + if (itemId == 'centerFly') return core.ui.drawCenterFly(); - if (core.canUseItem(itemId))core.useItem(itemId); - else core.drawTip("当前无法使用"+core.material.items[itemId].name); -} - -////// 加点事件 ////// -events.prototype.addPoint = function (enemy) { - return this.eventdata.addPoint(enemy); -} - -////// 战斗结束后触发的事件 ////// -events.prototype.afterBattle = function (enemyId,x,y,callback) { - return this.eventdata.afterBattle(enemyId,x,y,callback); -} - -////// 开一个门后触发的事件 ////// -events.prototype.afterOpenDoor = function (doorId,x,y,callback) { - return this.eventdata.afterOpenDoor(doorId,x,y,callback); -} - -////// 经过一个路障 ////// -events.prototype.passNet = function (data) { - // 有鞋子 - if (core.hasItem('shoes')) return; - if (data.event.id=='lavaNet') { // 血网 - // 在checkBlock中进行处理 - /* - core.status.hero.hp -= core.values.lavaDamage; - if (core.status.hero.hp<=0) { - core.status.hero.hp=0; - core.updateStatusBar(); - core.events.lose(); - return; - } - */ - // core.drawTip('经过血网,生命-'+core.values.lavaDamage); - } - if (data.event.id=='poisonNet') { // 毒网 - core.setFlag('debuff', 'poison'); - core.insertAction('毒衰咒处理'); - } - else if (data.event.id=='weakNet') { // 衰网 - core.setFlag('debuff', 'weak'); - core.insertAction('毒衰咒处理'); - } - else if (data.event.id=='curseNet') { // 咒网 - core.setFlag('debuff', 'curse'); - core.insertAction('毒衰咒处理'); - } - core.updateStatusBar(); -} - -////// 改变亮灯(感叹号)的事件 ////// -events.prototype.changeLight = function(x, y) { - var block = core.getBlock(x, y); - if (block==null) return; - var index = block.index; - block = block.block; - if (block.event.id != 'light') return; - // 改变为dark - block.id = 166; - block.event = {'cls': 'terrains', 'id': 'darkLight', 'noPass': true}; - core.drawBlock(block); - this.afterChangeLight(x,y); -} - -////// 改变亮灯之后,可以触发的事件 ////// -events.prototype.afterChangeLight = function (x,y) { - return this.eventdata.afterChangeLight(x,y); -} - -////// 滑冰 ////// -events.prototype.ski = function (direction) { - if (!core.isset(direction)) - direction = core.status.automaticRoute.lastDirection || core.getHeroLoc('direction'); - if (core.status.event.id!='ski') { - core.waitHeroToStop(function () { - core.status.event.id='ski'; - core.events.ski(direction); - }); - } - else { - core.moveHero(direction, function () { - if (core.status.event.id=='ski' && !core.status.isSkiing) { - core.status.event.id=null; - core.unLockControl(); - core.replay(); - } - }) - } -} - -////// 推箱子 ////// -events.prototype.pushBox = function (data) { - if (data.event.id!='box' && data.event.id!='boxed') return; - - // 判断还能否前进,看看是否存在事件 - var direction = core.getHeroLoc('direction'), nx=data.x+core.utils.scan[direction].x, ny=data.y+core.utils.scan[direction].y; - - if (nx<0||nx>=core.bigmap.width||ny<0||ny>=core.bigmap.height) return; - - var block = core.getBlock(nx, ny, null, true); - if (block!=null && !(core.isset(block.block.event) && block.block.event.id=='flower')) - return; - - if (block==null) { - core.status.thisMap.blocks.push(core.maps.initBlock(nx, ny, 169)); - block = core.getBlock(nx, ny); - } - else { - block.block.id=170; - block.block.event=core.maps.initBlock(null,null,170).event; - } - core.drawBlock(block.block); - - if (data.event.id=='box') { - core.removeBlock(data.x, data.y); - } - else { - data.id=168; - data.event=core.maps.initBlock(null,null,168).event; - core.drawBlock(data); - } - - core.updateStatusBar(); - - core.status.replay.animate = true; - core.lockControl(); - setTimeout(function () { - core.moveHero(direction, function() { - core.status.replay.animate = false; - core.status.route.pop(); - core.events.afterPushBox(); - // 可能有阻击... - if (core.status.event.id == null) { - core.unLockControl(); - core.replay(); - } - }); - }) - -} - -////// 推箱子后的事件 ////// -events.prototype.afterPushBox = function () { - return this.eventdata.afterPushBox(); + if (core.canUseItem(itemId)) core.useItem(itemId); + else core.drawTip("当前无法使用" + core.material.items[itemId].name); } ////// 使用炸弹/圣锤后的事件 ////// @@ -2264,26 +2249,16 @@ events.prototype.afterUseBomb = function () { return this.eventdata.afterUseBomb(); } -////// 即将存档前可以执行的操作 ////// -events.prototype.beforeSaveData = function (data) { - return this.eventdata.beforeSaveData(data); -} - -////// 读档事件后,载入事件前,可以执行的操作 ////// -events.prototype.afterLoadData = function (data) { - return this.eventdata.afterLoadData(data); -} - ////// 上传当前数据 ////// -events.prototype.uploadCurrent = function (username) { +events.prototype._uploadCurrent = function (username) { var formData = new FormData(); formData.append('type', 'score'); formData.append('name', core.firstData.name); formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); + formData.append('platform', core.platform.string); formData.append('hard', core.encodeBase64(core.status.hard)); - formData.append('username', core.encodeBase64(username||"current")); + formData.append('username', core.encodeBase64(username || "current")); formData.append('lv', core.status.hero.lv); formData.append('hp', Math.min(core.status.hero.hp, Math.pow(2, 63))); formData.append('atk', core.status.hero.atk); @@ -2293,7 +2268,7 @@ events.prototype.uploadCurrent = function (username) { formData.append('experience', core.status.hero.experience); formData.append('steps', core.status.hero.steps); formData.append('seed', core.getFlag('__seed__')); - formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime/1000)); + formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime / 1000)); formData.append('route', core.encodeRoute(core.status.route)); formData.append('deler', 'current'); formData.append('base64', 1); diff --git a/libs/icons.js b/libs/icons.js index db33f828..12b78828 100644 --- a/libs/icons.js +++ b/libs/icons.js @@ -26,14 +26,14 @@ icons.prototype.getClsFromId = function (id) { } icons.prototype._getAnimateFrames = function (cls, useOriginValue) { - if (cls=='enemys' || cls=='npcs') { + if (cls == 'enemys' || cls == 'npcs') { return 2; } if (cls == 'animates' || cls == 'enemy48') { return 4; } if (cls == 'npc48') { - return useOriginValue? 4 : 1; + return useOriginValue ? 4 : 1; } return 1; } @@ -55,9 +55,9 @@ icons.prototype.getTilesetOffset = function (id) { for (var i in core.tilesets) { var imgName = core.tilesets[i]; var img = core.material.images.tilesets[imgName]; - var width = Math.floor(img.width/32), height = Math.floor(img.height/32); - if (id>=startOffset && id= startOffset && id < startOffset + width * height) { + var x = (id - startOffset) % width, y = parseInt((id - startOffset) / width); return {"image": imgName, "x": x, "y": y}; } startOffset += this.tilesetStartOffset; diff --git a/libs/items.js b/libs/items.js index 1834cdb3..6c13e824 100644 --- a/libs/items.js +++ b/libs/items.js @@ -21,8 +21,37 @@ items.prototype.getItems = function () { return core.clone(this.items); } +items.prototype._resetItems = function () { + // 只有运行时才能执行此函数! + if (main.mode != 'play') return; + + // 根据flag来对道具进行修改 + if (core.flags.bigKeyIsBox) { + core.material.items.bigKey.cls = 'items'; + core.material.items.bigKey.name = '钥匙盒'; + } + if (core.flags.pickaxeFourDirections) + core.material.items.pickaxe.text = "可以破坏勇士四周的墙"; + if (core.flags.bombFourDirections) + core.material.items.bomb.text = "可以炸掉勇士四周的怪物"; + if (core.flags.snowFourDirections) + core.material.items.bomb.text = "可以将四周的熔岩变成平地"; + if (core.flags.equipment) { + core.material.items.sword1.cls = 'equips'; + core.material.items.sword2.cls = 'equips'; + core.material.items.sword3.cls = 'equips'; + core.material.items.sword4.cls = 'equips'; + core.material.items.sword5.cls = 'equips'; + core.material.items.shield1.cls = 'equips'; + core.material.items.shield2.cls = 'equips'; + core.material.items.shield3.cls = 'equips'; + core.material.items.shield4.cls = 'equips'; + core.material.items.shield5.cls = 'equips'; + } +} + ////// “即捡即用类”道具的使用效果 ////// -items.prototype.getItemEffect = function(itemId, itemNum) { +items.prototype.getItemEffect = function (itemId, itemNum) { var itemCls = core.material.items[itemId].cls; // 消耗品 if (itemCls === 'items') { @@ -44,14 +73,14 @@ items.prototype.getItemEffect = function(itemId, itemNum) { } ////// “即捡即用类”道具的文字提示 ////// -items.prototype.getItemEffectTip = function(itemId) { +items.prototype.getItemEffectTip = function (itemId) { var itemCls = core.material.items[itemId].cls; // 消耗品 if (itemCls === 'items') { var ratio = parseInt(core.status.thisMap.item_ratio) || 1; if (itemId in this.itemEffectTip) { try { - return eval(this.itemEffectTip[itemId])||""; + return eval(this.itemEffectTip[itemId]) || ""; } catch (e) { main.log(e); return ""; @@ -61,6 +90,21 @@ items.prototype.getItemEffectTip = function(itemId) { return ""; } +////// 使用道具 ////// +items.prototype.useItem = function (itemId, noRoute, callback) { + if (!this.canUseItem(itemId)) { + if (callback) callback(); + return; + } + // 执行道具效果 + this._useItemEffect(itemId); + // 执行完毕 + this._afterUseItem(itemId); + // 记录路线 + if (!noRoute) core.status.route.push("item:" + itemId); + if (callback) callback(); +} + items.prototype._useItemEffect = function (itemId) { if (itemId in this.useItemEffect) { try { @@ -76,28 +120,16 @@ items.prototype._useItemEffect = function (itemId) { items.prototype._afterUseItem = function (itemId) { // 道具使用完毕:删除 var itemCls = core.material.items[itemId].cls; - if (itemCls=='tools') + if (itemCls == 'tools') core.status.hero.items[itemCls][itemId]--; - if (core.status.hero.items[itemCls][itemId]<=0) + if (core.status.hero.items[itemCls][itemId] <= 0) delete core.status.hero.items[itemCls][itemId]; - core.status.event.ui = null; - core.updateStatusBar(); -} - -////// 使用道具 ////// -items.prototype.useItem = function (itemId, noRoute, callback) { - if (!this.canUseItem(itemId)) { - if (core.isset(callback)) callback(); - return; + if (!core.status.event.id) { + core.status.event.data = null; + core.status.event.ui = null; } - // 执行道具效果 - this._useItemEffect(itemId); - // 执行完毕 - this._afterUseItem(itemId); - // 记录路线 - if (!noRoute) core.status.route.push("item:"+itemId); - if (core.isset(callback)) callback(); + core.updateStatusBar(); } ////// 当前能否使用道具 ////// @@ -114,18 +146,20 @@ items.prototype.canUseItem = function (itemId) { main.log(e); } } - if (!able) core.status.event.ui = null; + if (!able) { + core.status.event.data = null; + core.status.event.ui = null; + } return able; } ////// 获得某个物品的个数 ////// items.prototype.itemCount = function (itemId) { - if (!core.isset(core.status.hero)) return 0; - if (!core.isset(itemId) || !core.isset(core.material.items[itemId])) return 0; + if (!core.material.items[itemId] || !core.isPlaying()) return 0; var itemCls = core.material.items[itemId].cls; - if (itemCls=="items") return 0; - return core.status.hero.items[itemCls][itemId]||0; + if (itemCls == "items") return 0; + return core.status.hero.items[itemCls][itemId] || 0; } ////// 是否存在某个物品 ////// @@ -135,12 +169,9 @@ items.prototype.hasItem = function (itemId) { ////// 是否装备某件装备 ////// items.prototype.hasEquip = function (itemId) { - if (!core.isset(core.status.hero)) return null; + if (!(core.material.items[itemId] || {}).equip || !core.isPlaying()) return null; - if (!core.isset(itemId)) return null; - if (!core.isset((core.material.items[itemId]||{}).equip)) return null; - - for (var i in core.status.hero.equipment||[]) + for (var i in core.status.hero.equipment) if (core.status.hero.equipment[i] == itemId) return true; return false @@ -148,69 +179,69 @@ items.prototype.hasEquip = function (itemId) { ////// 获得某个装备类型的当前装备 ////// items.prototype.getEquip = function (equipType) { - if (!core.isset(core.status.hero)) return null; - return (core.status.hero.equipment||[])[equipType]||null; + return core.status.hero.equipment[equipType] || null; } ////// 设置某个物品的个数 ////// items.prototype.setItem = function (itemId, itemNum) { - if (!core.isset(core.status.hero)) return null; itemNum = itemNum || 0; var itemCls = core.material.items[itemId].cls; if (itemCls == 'items') return; - if (!core.isset(core.status.hero.items[itemCls])) { - core.status.hero.items[itemCls] = {}; - } + 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]; + if (itemCls != 'keys') delete core.status.hero.items[itemCls][itemId]; else core.status.hero.items[itemCls][itemId] = 0; } core.updateStatusBar(); } +////// 增加某个物品的个数 ////// +items.prototype.addItem = function (itemId, itemNum) { + if (itemNum == null) itemNum = 1; + var itemData = core.material.items[itemId]; + var itemCls = itemData.cls; + if (itemCls == 'items') return; + if (core.status.hero.items[itemCls][itemId] == null) { + core.status.hero.items[itemCls][itemId] = 0; + } + 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; + } + // 永久道具只能有一个 + if (itemCls == 'constants' && core.status.hero.items[itemCls][itemId] > 1) + core.status.hero.items[itemCls][itemId] = 1; + core.updateStatusBar(); +} + ////// 删除某个物品 ////// items.prototype.removeItem = function (itemId, itemNum) { - if (!core.isset(core.status.hero)) return null; - if (!core.isset(itemNum)) itemNum = 1; + 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; + 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]; + 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 (!core.isset(core.status.hero)) return null; - if (!core.isset(itemNum)) itemNum = 1; - var itemData = core.material.items[itemId]; - var itemCls = itemData.cls; - if (itemCls == 'items') return; - if (!core.isset(core.status.hero.items[itemCls])) { - core.status.hero.items[itemCls] = {}; - core.status.hero.items[itemCls][itemId] = 0; - } - else if (!core.isset(core.status.hero.items[itemCls][itemId])) { - core.status.hero.items[itemCls][itemId] = 0; - } - 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; - } - // 永久道具只能有一个 - if (itemCls == 'constants' && core.status.hero.items[itemCls][itemId]>1) - core.status.hero.items[itemCls][itemId] = 1; - core.updateStatusBar(); -} - // ---------- 装备相关 ------------ // +items.prototype.getEquipTypeByName = function (name) { + var names = core.status.globalAttribute.equipName; + for (var i = 0; i < names.length; ++i) { + if (names[i] === name && !core.status.hero.equipment[i]) { + return i; + } + } + return -1; +} + items.prototype.getEquipTypeById = function (equipId) { var type = core.material.items[equipId].equip.type; if (typeof type == 'string') @@ -218,37 +249,27 @@ items.prototype.getEquipTypeById = function (equipId) { return type; } -items.prototype.getEquipTypeByName = function (name) { - var names = core.status.globalAttribute.equipName; - for (var i = 0; i < names.length; ++i) { - if (names[i] === name && !core.isset((core.status.hero.equipment||[])[i])) { - return i; - } - } - return -1; -} - // 当前能否撞上某装备 items.prototype.canEquip = function (equipId, hint) { // 装备是否合法 - var equip = core.material.items[equipId]||{}; - if (!core.isset(equip.equip)) { + var equip = core.material.items[equipId] || {}; + if (!equip.equip) { if (hint) core.drawTip("不合法的装备!"); return false; } // 是否拥有该装备 if (!core.hasItem(equipId) && !core.hasEquip(equipId)) { - if (hint) core.drawTip("你当前没有"+equip.name+",无法换装"); + if (hint) core.drawTip("你当前没有" + equip.name + ",无法换装"); return false; } // 可装备条件 var condition = this.equipCondition[equipId]; - if (core.isset(condition) && condition.length>0) { + if (condition) { try { if (!eval(condition)) { - if (hint) core.drawTip("当前不可换上"+equip.name); + if (hint) core.drawTip("当前不可换上" + equip.name); return false; } } @@ -260,66 +281,18 @@ items.prototype.canEquip = function (equipId, hint) { return true; } -////// 实际换装的效果 ////// -items.prototype._loadEquipEffect = function (equipId, unloadEquipId, isPercentage) { - // 比较能力值 - var result = core.compareEquipment(equipId, unloadEquipId); - - if (isPercentage) { - for (var v in result) - core.addFlag('__'+v+'_buff__', result[v]/100); - } - else { - for (var v in result) - core.status.hero[v] += result[v]; - } -} - -items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { - var loadEquip = core.material.items[loadId] || {}, unloadEquip = core.material.items[unloadId] || {}; - if (!core.isset(loadEquip.equip)) loadEquip.equip = {}; - if (!core.isset(unloadEquip.equip)) unloadEquip.equip = {}; - - var loadPercentage = loadEquip.equip.percentage, unloadPercentage = unloadEquip.equip.percentage; - - if (loadPercentage != null && unloadPercentage != null && loadPercentage != unloadPercentage) { - this.unloadEquip(type); - this.loadEquip(loadId); - if (core.isset(callback)) callback(); - return; - } - - // --- 音效 - core.playSound('equip.mp3'); - - // --- 实际换装 - this._loadEquipEffect(loadId, unloadId, loadPercentage==null?unloadPercentage:loadPercentage); - - // --- 加减 - if (loadId) core.removeItem(loadId); - if (unloadId) core.addItem(unloadId); - 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 (core.isset(callback)) callback(); -} - ////// 换上 ////// items.prototype.loadEquip = function (equipId, callback) { - if (!core.isset(core.status.hero)) return null; - if (!core.isset(core.status.hero.equipment)) core.status.hero.equipment = []; if (!this.canEquip(equipId, true)) { - if (core.isset(callback)) callback(); + if (callback) callback(); return; } var loadEquip = core.material.items[equipId] || {}; var type = this.getEquipTypeById(equipId); if (type < 0) { - core.drawTip("当前没有"+loadEquip.equip.type+"的空位!"); + core.drawTip("当前没有" + loadEquip.equip.type + "的空位!"); + if (callback) callback(); return; } @@ -328,12 +301,9 @@ items.prototype.loadEquip = function (equipId, callback) { ////// 卸下 ////// items.prototype.unloadEquip = function (equipType, callback) { - if (!core.isset(core.status.hero)) return null; - if (!core.isset(core.status.hero.equipment)) core.status.hero.equipment = []; - var unloadEquipId = core.status.hero.equipment[equipType]; - if (!core.isset(unloadEquipId)) { - if (core.isset(callback)) callback(); + if (!unloadEquipId) { + if (callback) callback(); return; } @@ -341,60 +311,102 @@ items.prototype.unloadEquip = function (equipType, callback) { } items.prototype.compareEquipment = function (compareEquipId, beComparedEquipId) { - var compareAtk = 0, compareDef = 0, compareMdef = 0; - if (core.isset(compareEquipId)) { - var compareEquip = core.material.items[compareEquipId]; - compareAtk += (compareEquip.equip||{}).atk || 0; - compareDef += (compareEquip.equip||{}).def || 0; - compareMdef += (compareEquip.equip||{}).mdef || 0; + var result = {}; + var first = core.material.items[compareEquipId], second = core.material.items[beComparedEquipId]; + for (var name in core.status.hero) { + if (typeof core.status.hero[name] == 'number') { + var ans = 0; + if (first) ans += (first.equip || {})[name] || 0; + if (second) ans -= (second.equip || {})[name] || 0; + if (ans != 0) result[name] = ans; + } } - if (core.isset(beComparedEquipId)) { - var beComparedEquip = core.material.items[beComparedEquipId]; - compareAtk -= (beComparedEquip.equip||{}).atk || 0; - compareDef -= (beComparedEquip.equip||{}).def || 0; - compareMdef -= (beComparedEquip.equip||{}).mdef || 0; + return result; +} + +////// 实际换装的效果 ////// +items.prototype._loadEquipEffect = function (equipId, unloadEquipId, isPercentage) { + // 比较能力值 + var result = core.compareEquipment(equipId, unloadEquipId); + + if (isPercentage) { + for (var name in result) + core.addBuff(name, result[name] / 100); } - return {"atk":compareAtk,"def":compareDef,"mdef":compareMdef}; + else { + for (var name in result) + core.status.hero[name] += result[name]; + } +} + +items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { + var loadEquip = core.material.items[loadId] || {}, unloadEquip = core.material.items[unloadId] || {}; + loadEquip.equip = loadEquip.equip || {}; + unloadEquip.equip = unloadEquip.equip || {} + + var loadPercentage = loadEquip.equip.percentage, unloadPercentage = unloadEquip.equip.percentage; + + if (loadId && unloadId && (loadPercentage || false) != (unloadPercentage || false)) { + this.unloadEquip(type); + this.loadEquip(loadId); + if (callback) callback(); + return; + } + + // --- 音效 + core.playSound('equip.mp3'); + + // --- 实际换装 + this._loadEquipEffect(loadId, unloadId, loadPercentage == null ? unloadPercentage : loadPercentage); + + // --- 加减 + if (loadId) core.removeItem(loadId); + if (unloadId) core.addItem(unloadId); + core.status.hero.equipment[type] = loadId || null; + + // --- 提示 + if (loadId) core.drawTip("已装备上" + loadEquip.name, loadId); + else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, unloadId); + + if (callback) callback(); } ////// 保存装备 ////// items.prototype.quickSaveEquip = function (index) { - if (!core.isset(core.status.hero.equipment)) core.status.hero.equipment = []; var saveEquips = core.getFlag("saveEquips", []); saveEquips[index] = core.clone(core.status.hero.equipment); core.setFlag("saveEquips", saveEquips); - core.drawTip("已保存"+index+"号套装"); + core.drawTip("已保存" + index + "号套装"); } ////// 读取装备 ////// items.prototype.quickLoadEquip = function (index) { var current = core.getFlag("saveEquips", [])[index]; - if (!core.isset(current)) { - core.drawTip(index+"号套装不存在"); + if (!current) { + core.drawTip(index + "号套装不存在"); return; } // 检查所有的装备 var equipSize = core.status.globalAttribute.equipName.length; - for (var i=0;i 32*32*3000) { - console.warn("警告!"+imgName+"上的图块素材个数大于3000!"); + if (img.width * img.height > 32 * 32 * 3000) { + console.warn("警告!" + imgName + "上的图块素材个数大于3000!"); } } callback(); @@ -99,19 +99,19 @@ loader.prototype._loadTilesets = function (callback) { } loader.prototype.loadImages = function (names, toSave, callback) { - if (!core.isset(names) || names.length==0) { - if (core.isset(callback)) callback(); + if (!names || names.length == 0) { + if (callback) callback(); return; } var items = 0; - for (var i=0;i=0) { + if (index >= 0) { core.musicStatus.cachedBgms.splice(index, 1); } else { @@ -279,4 +257,28 @@ loader.prototype.loadBgm = function (name) { } // 移动到缓存最前方 core.musicStatus.cachedBgms.unshift(name); +} + +loader.prototype._preloadBgm = function (bgm) { + bgm.volume = 0; + bgm.play(); +} + +loader.prototype.freeBgm = function (name) { + if (!core.material.bgms[name]) return; + // 从cachedBgms中删除 + core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (t) { + return t != name; + }); + // 清掉缓存 + core.material.bgms[name].removeAttribute("src"); + core.material.bgms[name].load(); + core.material.bgms[name] = null; + if (name == core.musicStatus.playingBgm) { + core.musicStatus.playingBgm = null; + } + // 三秒后重新加载 + setTimeout(function () { + core.loader.loadOneMusic(name); + }, 3000); } \ No newline at end of file diff --git a/libs/maps.js b/libs/maps.js index c1fdd3b8..f03bfe60 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -2,57 +2,59 @@ function maps() { this._init(); - this.DEFAULT_WIDTH = 13; - this.DEFAULT_HEIGHT = 13; - this.DEFAULT_PIXEL_WIDTH = this.DEFAULT_WIDTH * 32; - this.DEFAULT_PIXEL_HEIGHT = this.DEFAULT_HEIGHT * 32; } -maps.prototype._init = function() { +maps.prototype._init = function () { this.blocksInfo = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; //delete(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); } maps.prototype._setFloorSize = function (floorId) { - if (!core.isset(floorId)) { + if (!floorId) { core.floorIds.forEach(function (floorId) { core.maps._setFloorSize(floorId); }); return; } - core.floors[floorId].width = core.floors[floorId].width || this.DEFAULT_WIDTH; - core.floors[floorId].height = core.floors[floorId].height || this.DEFAULT_HEIGHT; + core.floors[floorId].width = core.floors[floorId].width || core.__SIZE__; + core.floors[floorId].height = core.floors[floorId].height || core.__SIZE__; } -// ------ 加载与存档读档 ------ // +// ------ 加载地图与地图的存档读档(压缩与解压缩) ------ // ////// 加载某个楼层(从剧本或存档中) ////// maps.prototype.loadFloor = function (floorId, map) { var floor = core.floors[floorId]; - if (!core.isset(map)) map = floor.map; + if (!map) map = floor.map; if (map instanceof Array) { map = {"map": map}; } var content = {}; - ["floorId", "title", "name", "canFlyTo", "canUseQuickShop", "cannotViewMap", "cannotMoveDirectly", "color", "weather", - "defaultGround", "images", "item_ratio", "upFloor", "bgm", "downFloor", "underGround"].forEach(function (e) { - if (core.isset(map[e])) content[e] = core.clone(map[e]); - else content[e] = core.clone(floor[e]); - }); - map=this.decompressMap(map.map, floorId); + var notCopy = ["firstArrive", "eachArrive", "parallelDo", "map", "bgmap", "fgmap", + "events", "changeFloor", "afterBattle", "afterGetItem", "afterOpenDoor", "cannotMove"]; + for (var name in floor) { + if (notCopy.indexOf(name) == -1 && floor[name] != null) + content[name] = core.clone(floor[name]); + } + for (var name in map) { + if (notCopy.indexOf(name) == -1 && map[name] != null) + content[name] = core.clone(map[name]); + } + map = this.decompressMap(map.map, floorId); // 事件处理 - content['blocks'] = this._mapIntoBlocks(map,floor,floorId); + content['blocks'] = this._mapIntoBlocks(map, floor, floorId); return content; } -maps.prototype._mapIntoBlocks = function (map,floor,floorId){ +maps.prototype._mapIntoBlocks = function (map, floor, floorId) { var blocks = []; var mw = core.floors[floorId].width; var mh = core.floors[floorId].height; for (var i = 0; i < mh; i++) { for (var j = 0; j < mw; j++) { - var block = this.initBlock(j, i, (map[i]||[])[j], true, floor); - if (core.isset(block.event)) blocks.push(block); + var block = this.initBlock(j, i, (map[i] || [])[j], true, floor); + if (block.id != 0 || block.event.trigger) + blocks.push(block); } } return blocks; @@ -61,8 +63,8 @@ maps.prototype._mapIntoBlocks = function (map,floor,floorId){ ////// 从ID获得数字 ////// maps.prototype.getNumberById = function (id) { for (var number in this.blocksInfo) { - if ((this.blocksInfo[number]||{}).id == id) - return parseInt(number)||0; + if ((this.blocksInfo[number] || {}).id == id) + return parseInt(number) || 0; } // tilesets if (/^X\d+$/.test(id)) { @@ -76,22 +78,23 @@ maps.prototype.getNumberById = function (id) { ////// 数字和ID的对应关系 ////// maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) { - var disable=null; - id = ""+(id||0); + var disable = null; + id = "" + (id || 0); if (id.endsWith(":f")) disable = true; if (id.endsWith(":t")) disable = false; - id=parseInt(id); + id = parseInt(id); var block = {'x': x, 'y': y, 'id': id}; - if (disable!=null) block.disable = disable; + if (disable != null) block.disable = disable; - if (id==17) block.event = {"cls": "terrains", "id": "airwall", "noPass": true}; + if (id == 17) block.event = {"cls": "terrains", "id": "airwall", "noPass": true}; else if (id in this.blocksInfo) block.event = JSON.parse(JSON.stringify(this.blocksInfo[id])); - else if (core.icons.getTilesetOffset(id)) block.event = {"cls": "tileset", "id": "X"+id, "noPass": true}; + else if (core.icons.getTilesetOffset(id)) block.event = {"cls": "tileset", "id": "X" + id, "noPass": true}; + else block.event = {'cls': 'terrains', 'id': 'none', 'noPass': false}; if (addInfo) this._addInfo(block); if (eventFloor) { - this._addEvent(block, x, y, (eventFloor.events||{})[x+","+y]); - var changeFloor = (eventFloor.changeFloor||{})[x+","+y]; + this._addEvent(block, x, y, (eventFloor.events || {})[x + "," + y]); + var changeFloor = (eventFloor.changeFloor || {})[x + "," + y]; if (changeFloor) this._addEvent(block, x, y, {"trigger": "changeFloor", "data": changeFloor}); } if (main.mode == 'editor') delete block.disable; @@ -100,33 +103,28 @@ maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) { ////// 添加一些信息到block上 ////// maps.prototype._addInfo = function (block) { - if (core.isset(block.event)) { - if (block.event.cls.indexOf("enemy")==0 && !core.isset(block.event.trigger)) { - block.event.trigger = 'battle'; - } - if (block.event.cls == 'items' && !core.isset(block.event.trigger)) { - block.event.trigger = 'getItem'; - } - if (!core.isset(block.event.noPass)) { - if (block.event.cls != 'items') { - block.event.noPass = true; - } - } - if (!core.isset(block.event.animate)) { - block.event.animate = core.icons._getAnimateFrames(block.event.cls, false); - } - block.event.height = 32; - if (block.event.cls == 'enemy48' || block.event.cls == 'npc48') - block.event.height = 48; + if (block.event.cls.indexOf("enemy") == 0 && !block.event.trigger) { + block.event.trigger = 'battle'; } + if (block.event.cls == 'items' && !block.event.trigger) { + block.event.trigger = 'getItem'; + } + if (block.event.noPass == null) { + if (block.event.cls != 'items') { + block.event.noPass = true; + } + } + if (block.event.animate == null) { + block.event.animate = core.icons._getAnimateFrames(block.event.cls, false); + } + block.event.height = 32; + if (block.event.cls == 'enemy48' || block.event.cls == 'npc48') + block.event.height = 48; } ////// 向该楼层添加剧本的自定义事件 ////// maps.prototype._addEvent = function (block, x, y, event) { - if (!core.isset(event)) return; - if (!core.isset(block.event)) { // 本身是空地? - block.event = {'cls': 'terrains', 'id': 'none', 'noPass': false}; - } + if (!event) return; // event是字符串或数组? if (typeof event == "string") { event = {"data": [event]}; @@ -134,12 +132,11 @@ maps.prototype._addEvent = function (block, x, y, event) { else if (event instanceof Array) { event = {"data": event}; } - if (!core.isset(event.data)) - event.data = []; + event.data = event.data || []; // 覆盖enable - if (!core.isset(block.disable) && core.isset(event.enable)) { - block.disable=!event.enable; + if (block.disable == null && event.enable != null) { + block.disable = !event.enable; } // 覆盖animate if (event.animate === false) { @@ -147,20 +144,21 @@ maps.prototype._addEvent = function (block, x, y, event) { } // 覆盖所有属性 for (var key in event) { - if (key!="enable" && key!="animate" && core.isset(event[key])) { - block.event[key]=core.clone(event[key]); + if (key != "enable" && key != "animate" && event[key] != null) { + block.event[key] = core.clone(event[key]); } } // 给无trigger的增加trigger:action - if (!core.isset(block.event.trigger)) { + if (!block.event.trigger) { block.event.trigger = 'action'; } } ////// 初始化所有地图 ////// -maps.prototype.initMaps = function (floorIds) { +maps.prototype._initMaps = function () { + var floorIds = core.floorIds; var maps = {}; - for (var i=0;i= core.floors[floorId].width || ny >= core.floors[floorId].width) + if (nx < 0 || ny < 0 || nx >= core.floors[floorId].width || ny >= core.floors[floorId].height) return false; // 2. 检查该点素材的 cannotOut 和下一个点的 cannotIn @@ -421,7 +455,7 @@ maps.prototype._canMoveHero_checkPoint = function (x, y, direction, floorId, ext // 3. 检查是否能进将死的领域 if (floorId == core.status.floorId - && core.status.hero.hp <= core.status.checkBlock.damage[nx + core.bigmap.width * ny] + && core.status.hero.hp <= (core.status.checkBlock.damage[nx + "," + ny]||0) && !core.flags.canGoDeadZone && extraData.eventArray[ny][nx] == 0) return false; @@ -436,15 +470,15 @@ maps.prototype._canMoveHero_checkCannotInOut = function (number, name, direction } return false; } - return core.inArray((this.initBlock(0, 0, number).event||{})[name], direction); + return core.inArray((this.initBlock(0, 0, number).event || {})[name], direction); } ////// 能否瞬间移动 ////// -maps.prototype.canMoveDirectly = function (destX,destY) { +maps.prototype.canMoveDirectly = function (destX, destY) { if (!this._canMoveDirectly_checkGlobal()) return -1; var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); - if (fromX==destX&&fromY==destY) return 0; + if (fromX == destX && fromY == destY) return 0; // 检查起点事件 if (!this._canMoveDirectly_checkStartPoint(fromX, fromY)) return -1; @@ -465,33 +499,34 @@ maps.prototype._canMoveDirectly_checkGlobal = function () { } maps.prototype._canMoveDirectly_checkStartPoint = function (sx, sy) { - if (core.status.checkBlock.damage[sx+core.bigmap.width*sy]>0) return false; - var id = core.getBlockId(sx, sy); - if (id != null) { - // 楼梯或者传送点才能无视 - if (["upFloor","downFloor","portal","upPortal","downPortal","leftPortal","rightPortal"].indexOf(id)>=0) - return true; - return false; + if (core.status.checkBlock.damage[sx + "," + sy]) return false; + var block = core.getBlock(sx, sy); + if (block != null) { + // 只有起点是传送点才是能无视 + return block.block.event.trigger == 'changeFloor'; } return true; } maps.prototype._canMoveDirectly_bfs = function (sx, sy, ex, ey) { - var canMoveArray = this._canMoveHero_generateArray(); + var canMoveArray = this.generateMovableArray(); var blocksObj = this.getMapBlocksObj(core.status.floorId); + // 滑冰 + var bgMap = this.getBgMapArray(); - var visited=[], queue=[]; - visited[sx+","+sy]=0; - queue.push(sx+","+sy); + var visited = [], queue = []; + visited[sx + "," + sy] = 0; + queue.push(sx + "," + sy); - while (queue.length>0) { - var now=queue.shift().split(","), x=parseInt(now[0]), y=parseInt(now[1]); + while (queue.length > 0) { + var now = queue.shift().split(","), x = parseInt(now[0]), y = parseInt(now[1]); for (var direction in core.utils.scan) { if (!core.inArray(canMoveArray[x][y], direction)) continue; - var nx=x+core.utils.scan[direction].x, ny=y+core.utils.scan[direction].y, nindex = nx+","+ny; + var nx = x + core.utils.scan[direction].x, ny = y + core.utils.scan[direction].y, nindex = nx + "," + ny; if (visited[nindex]) continue; + if (bgMap[ny][nx] == 167) continue; if (!this._canMoveDirectly_checkNextPoint(blocksObj, nx, ny)) continue; - visited[nindex] = visited[now]+1; + visited[nindex] = visited[now] + 1; if (nx == ex && ny == ey) return visited[nindex]; queue.push(nindex); } @@ -505,18 +540,657 @@ maps.prototype._canMoveDirectly_checkNextPoint = function (blocksObj, x, y) { // 该点是否有事件 if (blocksObj[index]) return false; // 是否存在阻激夹域伤害 - if (core.status.checkBlock.damage[x+core.bigmap.width*y]>0) return false; + if (core.status.checkBlock.damage[x + "," + y]) return false; // 是否存在捕捉 - if (core.status.checkBlock.ambush[x+core.bigmap.width*y]) return false; + if (core.status.checkBlock.ambush[x + "," + y]) return false; return true; } -// -------- Draw block, map, autotile, ... -------- // +////// 自动寻路找寻最优路径 ////// +maps.prototype.automaticRoute = function (destX, destY) { + var startX = core.getHeroLoc('x'), startY = core.getHeroLoc('y'); + if (destX == startX && destY == startY) return []; + // BFS找寻最短路径 + var route = this._automaticRoute_bfs(startX, startY, destX, destY); + if (route[destX+","+destY] == null) return []; + // 路径数组转换 + var ans = [], nowX = destX, nowY = destY; + while (nowX != startX || nowY != startY) { + var dir = route[nowX + "," + nowY]; + ans.push({'direction': dir, 'x': nowX, 'y': nowY}); + nowX -= core.utils.scan[dir].x; + nowY -= core.utils.scan[dir].y; + } + ans.reverse(); + return ans; +} -// 获得某个图块或素材的信息,包括 ID,cls,图片,坐标,faceIds 等等 +maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) { + var route = {}, canMoveArray = this.generateMovableArray(); + // 使用优先队列 + var queue = new PriorityQueue({comparator: function (a,b) { return a.depth - b.depth; }}); + route[startX + "," + startY] = ''; + queue.queue({depth: 0, x: startX, y: startY}); + while (queue.length!=0) { + var curr = queue.dequeue(), deep = curr.depth, nowX = curr.x, nowY = curr.y; + for (var direction in core.utils.scan) { + if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue; + var nx = nowX + core.utils.scan[direction].x; + var ny = nowY + core.utils.scan[direction].y; + if (nx<0 || nx>=core.bigmap.width || ny<0 || ny>=core.bigmap.height || route[nx+","+ny] != null) continue; + // 重点 + if (nx == destX && ny == destY) { + route[nx+","+ny] = direction; + break; + } + // 不可通行 + if (core.noPass(nx, ny)) continue; + route[nx+","+ny] = direction; + queue.queue({depth: deep + this._automaticRoute_deepAdd(nx, ny), x: nx, y: ny}); + } + if (route[destX+","+destY] != null) break; + } + return route; +} + +maps.prototype._automaticRoute_deepAdd = function (x, y) { + // 判定每个可通行点的损耗值,越高越应该绕路 + var deepAdd = 1; + var block = core.getBlock(x,y); + if (block != null){ + var id = block.block.event.id; + // 绕过亮灯 + if (id == "light") deepAdd += 100; + // 绕过路障 + if (id.endsWith("Net")) deepAdd += 100; + // 绕过血瓶 + if (!core.flags.potionWhileRouting && id.endsWith("Potion")) deepAdd += 100; + // 绕过传送点 + // if (block.block.event.trigger == 'changeFloor') deepAdd+=10; + } + // 绕过存在伤害的地方 + deepAdd += (core.status.checkBlock.damage[x+","+y]||0) * 100; + // 绕过捕捉 + if (core.status.checkBlock.ambush[x+","+y]) deepAdd += 1000; + return deepAdd; +} + +// -------- 绘制地图,各层图块,楼层贴图,Autotile -------- // + +////// 绘制一个图块 ////// +maps.prototype.drawBlock = function (block, animate) { + if (block.event.id == 'none') return; + var redraw = animate != null; + if (!redraw) animate = 0; + var x = block.x, y = block.y; + // --- 在界面外的动画不绘制 + if (redraw && block.event.animate > 1 && + (32 * x < core.bigmap.offsetX - 64 || 32 * x > core.bigmap.offsetX + core.__PIXELS__ + 32 + || 32 * y < core.bigmap.offsetY - 64 || 32 * y > core.bigmap.offsetY + core.__PIXELS__ + 32 + 16)) { + return; + } + + var blockInfo = this.getBlockInfo(block); + if (blockInfo == null) return; + if (blockInfo.cls != 'tileset') blockInfo.posX = animate % block.event.animate; + if (!block.name) + this._drawBlockInfo(blockInfo, block.x, block.y); + else + this._drawBlockInfo_bgfg(blockInfo, block.name, block.x, block.y); +} + +maps.prototype._drawBlockInfo = function (blockInfo, x, y) { + var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; + + core.clearMap('event', x * 32, y * 32, 32, 32); + core.drawImage('event', image, posX * 32, posY * height + height - 32, 32, 32, x * 32, y * 32, 32, 32); + if (height > 32) { + core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32) + core.drawImage('event2', image, posX * 32, posY * height, 32, height - 32, x * 32, y * 32 + 32 - height, 32, height - 32); + } +} + +maps.prototype._drawBlockInfo_bgfg = function (blockInfo, name, x, y) { + var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; + + core.clearMap(name, x * 32, y * 32 + 32 - height, 32, height); + if (name == 'bg') { + if (height > 32) { + core.clearMap('bg', x * 32, y * 32 - 32, 32, 32); + core.drawImage('bg', core.material.groundCanvas.canvas, x * 32, y * 32 - 32); + } + core.drawImage('bg', core.material.groundCanvas.canvas, x * 32, y * 32); + } + core.drawImage(name, image, posX * 32, posY * height, 32, height, x * 32, y * 32 + 32 - height, 32, height); +} + +////// 生成groundPattern ////// +maps.prototype.generateGroundPattern = function (floorId) { + // 生成floorId层的groundPattern(盒子内的怪物动画) + var groundId = ((core.status.maps || core.floors)[floorId || core.status.floorId] || {}).defaultGround || "ground"; + core.material.groundCanvas.clearRect(0, 0, 32, 32); + core.material.groundCanvas.drawImage(core.material.images.terrains, 0, 32 * core.material.icons.terrains[groundId], 32, 32, 0, 0, 32, 32); + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); + // 如果需要用纯色可以直接将下面代码改成改成 + // core.material.groundPattern = '#000000'; +} + +////// 绘制某张地图 ////// +maps.prototype.drawMap = function (floorId, callback) { + floorId = floorId || core.status.floorId; + if (!floorId) { + if (callback) callback(); + return; + } + core.clearMap('all'); + this.generateGroundPattern(floorId); + core.status.floorId = floorId; + core.status.thisMap = core.status.maps[floorId]; + + this._drawMap_drawAll(); + if (core.status.curtainColor) { + core.fillRect('curtain', 0, 0, core.__PIXELS__, core.__PIXELS__, + core.arrayToRGBA(core.status.curtainColor)); + } + core.drawHero(); + core.updateStatusBar(); + if (callback) callback(); +} + +maps.prototype._drawMap_drawAll = function (floorId) { + floorId = floorId || core.status.floorId; + this.drawBg(floorId); + this.drawEvents(floorId); + this.drawFg(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); + if (onMap) this.addGlobalAnimate(block); + return; + } + if (!onMap) { + var height = blockInfo.height; + core.drawImage(ctx, blockInfo.image, 32 * blockInfo.posX, height * blockInfo.posY, 32, height, 32 * block.x, 32 * block.y + 32 - height, 32, height); + return; + } + this.drawBlock(block); + this.addGlobalAnimate(block); +} + +////// 绘制背景层 ////// +maps.prototype.drawBg = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + var onMap = ctx == null; + if (onMap) { + ctx = core.canvas.bg; + core.clearMap(ctx); + } + core.maps._drawBg_drawBackground(floorId, ctx); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages(floorId, ctx, 'bg'); + core.maps._drawBgFgMap(floorId, ctx, 'bg', onMap); +} + +maps.prototype._drawBg_drawBackground = function (floorId, ctx) { + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var groundId = (core.status.maps || core.floors)[floorId].defaultGround || "ground"; + var yOffset = core.material.icons.terrains[groundId]; + if (yOffset != null) { + for (var i = 0; i < width; i++) { + for (var j = 0; j < height; j++) { + ctx.drawImage(core.material.images.terrains, 0, yOffset * 32, 32, 32, i * 32, j * 32, 32, 32); + } + } + } +} + +////// 绘制事件层 ////// +maps.prototype.drawEvents = function (floorId, blocks, ctx) { + floorId = floorId || core.status.floorId; + if (!blocks) blocks = core.status.maps[floorId].blocks; + 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) { + return block.event && !block.disable; + }).forEach(function (block) { + core.maps._drawMap_drawBlockInfo(ctx, block, core.maps.getBlockInfo(block), arr, onMap); + }); + if (onMap) core.status.autotileAnimateObjs.map = core.clone(arr); +} + +////// 绘制前景层 ////// +maps.prototype.drawFg = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + var onMap = ctx == null; + if (onMap) ctx = core.canvas.fg; + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + this._drawFloorImages(floorId, ctx, 'fg'); + this._drawBgFgMap(floorId, ctx, 'fg', onMap); +} + +////// 实际的背景/前景图块的绘制 ////// +maps.prototype._drawBgFgMap = function (floorId, ctx, name, onMap) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + if (!core.status[name + "maps"]) + core.status[name + "maps"] = {}; + + 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); + block.name = name; + var blockInfo = this.getBlockInfo(block); + if (!blockInfo) continue; + this._drawMap_drawBlockInfo(ctx, block, blockInfo, arr, onMap); + } + } + if (onMap) + core.status.autotileAnimateObjs[name + "map"] = core.clone(arr); +} + +////// 绘制楼层贴图 ////// +maps.prototype._drawFloorImages = function (floorId, ctx, name, images, currStatus) { + floorId = floorId || core.status.floorId; + if (!images) images = this._getFloorImages(floorId); + var redraw = currStatus != null; + if (!redraw) core.status.floorAnimateObjs = core.clone(images); + images.forEach(function (t) { + if (typeof t == 'string') t = [0, 0, t]; + var dx = parseInt(t[0]), dy = parseInt(t[1]), imageName = t[2], frame = core.clamp(parseInt(t[4]), 1, 8); + var image = core.material.images.images[imageName]; + if (redraw && frame == 1) return; // 不重绘 + + if (core.isset(dx) && core.isset(dy) && image && + !core.hasFlag("__floorImg__" + floorId + "_" + dx + "_" + dy)) { + var width = parseInt(image.width / frame), offsetX = (currStatus || 0) % frame * width; + if (/.*\.gif/i.test(imageName) && main.mode == 'play') { + if (redraw) return; // 忽略gif + this._drawFloorImages_gif(image, dx, dy); + return; + } + core.maps._drawFloorImage(ctx, name, t[3], image, offsetX, width, dx, dy, redraw); + } + }); +} + +maps.prototype._getFloorImages = function (floorId) { + floorId = floorId || core.status.floorId; + var images = []; + if ((core.status.maps || core.floors)[floorId].images) { + images = (core.status.maps || core.floors)[floorId].images; + if (typeof images == 'string') { + images = [[0, 0, images]]; + } + } + return images; +} + +maps.prototype._drawFloorImages_gif = function (image, dx, dy) { + core.dom.gif.innerHTML = ""; + var gif = new Image(); + gif.src = image.src; + gif.style.position = 'absolute'; + gif.style.left = (dx * core.domStyle.scale) + "px"; + gif.style.top = (dy * core.domStyle.scale) + "px"; + gif.style.width = image.width * core.domStyle.scale + "px"; + gif.style.height = image.height * core.domStyle.scale + "px"; + core.dom.gif.appendChild(gif); + return; +} + +maps.prototype._drawFloorImage = function (ctx, name, type, image, offsetX, width, dx, dy, redraw) { + var height = image.height; + var _draw = function () { + if (redraw) core.clearMap(ctx, dx, dy, width, height); + core.drawImage(ctx, image, offsetX, 0, width, height, dx, dy, width, height); + } + if (!type) { + if (name != 'bg') return; + return _draw(); + } + if (type == 1) { + if (name != 'fg') return; + return _draw(); + } + if (type == 2) { + if (name == 'bg') { + if (redraw) core.clearMap(ctx, dx, dy + height - 32, width, 32); + core.drawImage('bg', image, offsetX, height - 32, width, 32, dx, dy + height - 32, width, 32); + } + else if (name == 'fg') { + if (redraw) core.clearMap(ctx, dx, dy, width, height - 32); + core.drawImage('fg', image, offsetX, 0, width, height - 32, dx, dy, width, height - 32); + } + return; + } +} + +////// 绘制Autotile ////// +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 | + [10, 9, 4, 13], //1 bin:0001 +----+----+----+----+----+----+ + [10, 9, 18, 3], //2 bin:0010 | 7 | 8 | 9 | 10 | 11 | 12 | + [10, 9, 16, 15], //3 bin:0011 +----+----+----+----+----+----+ + [10, 43, 4, 3], //4 bin:0100 | 13 | 14 | 15 | 16 | 17 | 18 | + [10, 31, 4, 25], //5 bin:0101 +----+----+----+----+----+----+ + [10, 7, 2, 3], //6 bin:0110 | 19 | 20 | 21 | 22 | 23 | 24 | + [10, 31, 16, 5], //7 bin:0111 +----+----+----+----+----+----+ + [48, 9, 4, 3], //8 bin:1000 | 25 | 26 | 27 | 28 | 29 | 30 | + [8, 9, 4, 1], //9 bin:1001 +----+----+----+----+----+----+ + [36, 9, 30, 3], //10 bin:1010 | 31 | 32 | 33 | 34 | 35 | 36 | + [36, 9, 6, 15], //11 bin:1011 +----+----+----+----+----+----+ + [46, 45, 4, 3], //12 bin:1100 | 37 | 38 | 39 | 40 | 41 | 42 | + [46, 11, 4, 25], //13 bin:1101 +----+----+----+----+----+----+ + [12, 45, 30, 3], //14 bin:1110 | 43 | 44 | 45 | 46 | 47 | 48 | + [34, 33, 28, 27] //15 bin:1111 +----+----+----+----+----+----+ + ]; + + // 开始绘制autotile + var x = block.x, y = block.y; + var pieceIndexs = this._drawAutotile_getAutotileIndexs(x, y, mapArr, indexArrs); + + //修正四个边角的固定搭配 + if (pieceIndexs[0] == 13) { + if (pieceIndexs[1] == 16) pieceIndexs[1] = 14; + if (pieceIndexs[2] == 31) pieceIndexs[2] = 19; + } + if (pieceIndexs[1] == 18) { + if (pieceIndexs[0] == 15) pieceIndexs[0] = 17; + if (pieceIndexs[3] == 36) pieceIndexs[3] = 24; + } + if (pieceIndexs[2] == 43) { + if (pieceIndexs[0] == 25) pieceIndexs[0] = 37; + if (pieceIndexs[3] == 46) pieceIndexs[3] = 44; + } + if (pieceIndexs[3] == 48) { + if (pieceIndexs[1] == 30) pieceIndexs[1] = 42; + if (pieceIndexs[2] == 45) pieceIndexs[2] = 47; + } + for (var i = 0; i < 4; i++) { + var index = pieceIndexs[i]; + var dx = x * size + size / 2 * (i % 2), dy = y * size + size / 2 * (~~(i / 2)); + this._drawAutotile_drawBlockByIndex(ctx, dx + left, dy + top, core.material.images['autotile'][block.event.id], index, size, status); + } +} + +maps.prototype._drawAutotile_drawBlockByIndex = function (ctx, dx, dy, autotileImg, index, size, status) { + //index为autotile的图块索引1-48 + var sx = 16 * ((index - 1) % 6), sy = 16 * (~~((index - 1) / 6)); + status = status || 0; + status %= parseInt(autotileImg.width / 96); + ctx.drawImage(autotileImg, sx + 96 * status, sy, 16, 16, dx, dy, size / 2, size / 2); +} + +maps.prototype._drawAutotile_getAutotileAroundId = function (currId, x, y, mapArr) { + if (x < 0 || y < 0 || x >= mapArr[0].length || y >= mapArr.length) return 1; + else return core.material.autotileEdges[currId].indexOf(mapArr[y][x]) >= 0; +} + +maps.prototype._drawAutotile_checkAround = function (x, y, mapArr) { + // 得到周围四个32*32块(周围每块都包含当前块的1/4,不清楚的话画下图你就明白)的数组索引 + var currId = mapArr[y][x]; + var pointBlock = []; + for (var i = 0; i < 4; i++) { + var bsum = 0; + var offsetx = i % 2, offsety = ~~(i / 2); + for (var j = 0; j < 4; j++) { + var mx = j % 2, my = ~~(j / 2); + var b = this._drawAutotile_getAutotileAroundId(currId, x + offsetx + mx - 1, y + offsety + my - 1, mapArr); + bsum += b * (Math.pow(2, 3 - j)); + } + pointBlock.push(bsum); + } + return pointBlock; +} + +maps.prototype._drawAutotile_getAutotileIndexs = function (x, y, mapArr, indexArrs) { + var indexArr = []; + var pointBlocks = this._drawAutotile_checkAround(x, y, mapArr); + for (var i = 0; i < 4; i++) { + var arr = indexArrs[pointBlocks[i]] + indexArr.push(arr[3 - i]); + } + return indexArr; +} + +maps.prototype._drawAutotileAnimate = function (block, animate) { + var x = block.x, y = block.y; + // ------ 界面外的动画不绘制 + if (32 * x < core.bigmap.offsetX - 64 || 32 * x > core.bigmap.offsetX + core.__PIXELS__ + 32 + || 32 * y < core.bigmap.offsetY - 64 || 32 * y > core.bigmap.offsetY + core.__PIXELS__ + 32 + 16) { + return; + } + + var cv = block.name?core.canvas[block.name]:core.canvas.event; + cv.clearRect(32 * x, 32 * y, 32, 32); + if (block.name) { + if (block.name == 'bg') + core.drawImage('bg', core.material.groundCanvas.canvas, 32 * x, 32 * y); + this.drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, animate); + } + else { + this.drawAutotile(cv, core.status.autotileAnimateObjs.map, block, 32, 0, 0, animate); + } +} + +////// 为autotile判定边界 ////// +maps.prototype._makeAutotileEdges = function () { + var autotileIds = Object.keys(core.material.images.autotile); + core.material.autotileEdges = {}; + + var canvas = document.createElement("canvas"), ctx = canvas.getContext('2d'); + canvas.width = canvas.height = 32; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + autotileIds.forEach(function (t) { + var n = core.maps.getNumberById(t); + core.material.autotileEdges[n] = [n]; + + ctx.clearRect(0, 0, 32, 32); + ctx.drawImage(core.material.images.autotile[t], 0, 0, 32, 32, 0, 0, 32, 32); + var data = canvas.toDataURL("image/png"); + + autotileIds.forEach(function (t2) { + if (t == t2) return; + var n2 = core.maps.getNumberById(t2); + + ctx.clearRect(0, 0, 32, 32); + ctx.drawImage(core.material.images.autotile[t2], 32, 0, 32, 32, 0, 0, 32, 32); + if (data == canvas.toDataURL("image/png")) { + core.material.autotileEdges[n].push(n2); + } + }); + }); +} + +////// 绘制缩略图 ////// +// 此函数将绘制一个缩略图,floorId为目标floorId,blocks为地图的图块(可为null使用floorId对应默认的) +// options为绘制选项(可为null),包括: +// heroLoc: 勇士位置;heroIcon:勇士图标(默认当前勇士);damage:是否绘制显伤;flags:当前的flags(存读档时使用) +// toDraw为要绘制到的信息(可为null,或为一个画布名),包括: +// ctx:要绘制到的画布(名);x,y:起点横纵坐标(默认0);size:大小(默认416/480); +// all:是否绘制全图(默认false);centerX,centerY:截取中心(默认为地图正中心) +maps.prototype.drawThumbnail = function (floorId, blocks, options, toDraw) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + // Step1:绘制到tempCanvas上 + this._drawThumbnail_drawTempCanvas(floorId, blocks, options); + // Step2:从tempCanvas绘制到对应的画布上 + this._drawThumbnail_drawToTarget(floorId, toDraw); +} + +maps.prototype._drawThumbnail_drawTempCanvas = function (floorId, blocks, options) { + blocks = blocks || core.status.maps[floorId].blocks; + options = options || {} + + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + // 绘制到tempCanvas上面 + var tempCanvas = core.bigmap.tempCanvas; + var tempWidth = width * 32, tempHeight = height * 32; + tempCanvas.canvas.width = tempWidth; + tempCanvas.canvas.height = tempHeight; + tempCanvas.clearRect(0, 0, tempWidth, tempHeight); + + // --- 暂存 flags + var hasHero = core.status.hero != null, flags = null; + if (options.flags) { + if (!hasHero) core.status.hero = {}; + flags = core.status.hero.flags; + core.status.hero.flags = options.flags; + } + + this._drawThumbnail_realDrawTempCanvas(floorId, blocks, options, tempCanvas); + + // --- 恢复 flags + if (!hasHero) delete core.status.hero; + else if (flags != null) core.status.hero.flags = flags; +} + +maps.prototype._drawThumbnail_realDrawTempCanvas = function (floorId, blocks, options, tempCanvas) { + // 缩略图:背景 + this.drawBg(floorId, tempCanvas); + // 缩略图:事件 + this.drawEvents(floorId, blocks, tempCanvas); + // 缩略图:勇士 + if (options.heroLoc) { + options.heroIcon = options.heroIcon || core.getFlag("heroIcon", "hero.png"); + var icon = core.material.icons.hero[options.heroLoc.direction]; + var height = core.material.images.images[options.heroIcon].height / 4; + tempCanvas.drawImage(core.material.images.images[options.heroIcon], icon.stop * 32, icon.loc * height, 32, height, + 32 * options.heroLoc.x, 32 * options.heroLoc.y + 32 - height, 32, height); + } + // 缩略图:前景 + this.drawFg(floorId, tempCanvas); + // 缩略图:显伤 + if (options.damage) + core.control.updateDamage(floorId, tempCanvas); +} + +maps.prototype._drawThumbnail_drawToTarget = function (floorId, toDraw) { + if (toDraw == null) return; + if (typeof toDraw == 'string' || toDraw.canvas) toDraw = {ctx: toDraw}; + var ctx = core.getContextByName(toDraw.ctx); + if (ctx == null) return; + var x = toDraw.x || 0, y = toDraw.y || 0, size = toDraw.size || core.__PIXELS__; + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var centerX = toDraw.centerX, centerY = toDraw.centerY; + if (centerX == null) centerX = Math.floor(width / 2); + if (centerY == null) centerY = Math.floor(height / 2); + var tempCanvas = core.bigmap.tempCanvas, tempWidth = 32 * width, tempHeight = 32 * height; + + core.clearMap(ctx, x, y, size, size); + if (toDraw.all) { + // 绘制全景图 + if (tempWidth <= tempHeight) { + var realHeight = size, realWidth = realHeight * tempWidth / tempHeight; + var side = (size - realWidth) / 2; + core.fillRect(ctx, x, y, side, realHeight, '#000000'); + core.fillRect(ctx, x + size - side, y, side, realHeight); + ctx.drawImage(tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x + side, y, realWidth, realHeight); + } + else { + var realWidth = size, realHeight = realWidth * tempHeight / tempWidth; + var side = (size - realHeight) / 2; + core.fillRect(ctx, x, y, realWidth, side, '#000000'); + core.fillRect(ctx, x, y + size - side, realWidth, side); + ctx.drawImage(tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y + side, realWidth, realHeight); + } + } + else { + // 只绘制可见窗口 + var offsetX = core.clamp(centerX - core.__HALF_SIZE__, 0, width - core.__SIZE__), + offsetY = core.clamp(centerY - core.__HALF_SIZE__, 0, height - core.__SIZE__); + ctx.drawImage(tempCanvas.canvas, offsetX * 32, offsetY * 32, core.__PIXELS__, core.__PIXELS__, x, y, size, size); + } +} + +// -------- 获得某个点的图块信息 -------- // + +////// 某个点是否不可通行 ////// +maps.prototype.noPass = function (x, y, floorId) { + var block = core.getBlock(x, y, floorId); + if (block == null) return false; + return block.block.event.noPass; +} + +////// 某个点是否存在NPC ////// +maps.prototype.npcExists = function (x, y, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.block.event.cls.indexOf('npc') == 0; +} + +////// 某个点是否存在(指定的)地形 ////// +maps.prototype.terrainExists = function (x, y, id, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.block.event.cls == 'terrains' && (id ? block.block.event.id == id : true); +} + +////// 某个点是否存在楼梯 ////// +maps.prototype.stairExists = function (x, y, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.block.event.cls == 'terrains' && (block.block.event.id == 'upFloor' || block.block.event.id == 'downFloor'); +} + +////// 当前位置是否在楼梯边 ////// +maps.prototype.nearStair = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'); + return this.stairExists(x, y) || this.stairExists(x - 1, y) || this.stairExists(x, y - 1) || this.stairExists(x + 1, y) || this.stairExists(x, y + 1); +} + +////// 某个点是否存在(指定的)怪物 ////// +maps.prototype.enemyExists = function (x, y, id, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.block.event.cls.indexOf('enemy') == 0 && (id ? block.block.event.id == id : true); +} + +////// 获得某个点的block ////// +maps.prototype.getBlock = function (x, y, floorId, showDisable) { + floorId = floorId || core.status.floorId; + if (!floorId) return null; + var blocks = core.status.maps[floorId].blocks; + for (var n = 0; n < blocks.length; n++) { + if (blocks[n].x == x && blocks[n].y == y) { + if (!showDisable && blocks[n].disable) return null; + return {"index": n, "block": blocks[n]}; + } + } + return null; +} + +////// 获得某个点的blockId ////// +maps.prototype.getBlockId = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + return block == null ? null : block.block.event.id; +} + +////// 获得某个点的blockCls ////// +maps.prototype.getBlockCls = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + return block == null ? null : block.block.event.cls; +} + +////// 获得某个图块或素材的信息,包括 ID,cls,图片,坐标,faceIds 等等 ////// maps.prototype.getBlockInfo = function (block) { - if (!core.isset(block)) return null; + if (!block) return null; if (typeof block == 'string') { // 参数是ID block = this.getNumberById(block); } @@ -524,13 +1198,13 @@ maps.prototype.getBlockInfo = function (block) { if (block == 0) return null; block = this.initBlock(0, 0, block, true); } - if (!core.isset(block.event)) return null; - var id = block.event.id, cls = block.event.cls, image = null, posX = 0, posY = 0, + var number = block.id, id = block.event.id, cls = block.event.cls, + image = null, posX = 0, posY = 0, animate = block.event.animate, height = block.event.height || 32, faceIds = {}; if (id == 'none') return null; else if (id == 'airwall') { - if (!core.isset(core.material.images.airwall)) return null; + if (!core.material.images.airwall) return null; image = core.material.images.airwall; } else if (cls == 'tileset') { @@ -546,512 +1220,316 @@ maps.prototype.getBlockInfo = function (block) { else { image = core.material.images[cls]; posY = core.material.icons[cls][id]; - faceIds = block.event.faceIds||{}; + faceIds = block.event.faceIds || {}; } - return {id:id, cls:cls, image:image, posX:posX, posY:posY, height:height, faceIds:faceIds}; + return {number: number, id: id, cls: cls, image: image, posX: posX, posY: posY, height: height, faceIds: faceIds, animate: animate}; } -maps.prototype.drawBlock = function (block, animate, dx, dy) { - if (block.event.id == 'none') return; - animate = animate || 0; - dx = dx || 0; - dy = dy || 0; - var x = block.x, y = block.y; - // --- 在界面外的动画不绘制 - if (animate > 1 && block.event.animate > 1 && - (32*x + dx < core.bigmap.offsetX - 64 || 32*x + dx > core.bigmap.offsetX + this.DEFAULT_PIXEL_WIDTH + 32 - || 32*y + dy < core.bigmap.offsetY - 64 || 32*y + dy > core.bigmap.offsetY + this.DEFAULT_PIXEL_HEIGHT + 32 + 16)) { - return; - } - - var blockInfo = this.getBlockInfo(block); - if (blockInfo == null) return; - if (blockInfo.cls != 'tileset') blockInfo.posX = animate % block.event.animate; - if (!core.isset(block.name)) - this._drawBlockInfo(blockInfo, block.x, block.y, dx, dy); - else - this._drawBlockInfo_bgfg(blockInfo, block.name, block.x, block.y); -} - -maps.prototype._drawBlockInfo = function (blockInfo, x, y, dx, dy) { - var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; - - core.clearMap('event', x * 32 + dx, y * 32 + dy, 32, 32); - core.drawImage('event', image, posX * 32, posY * height + height - 32, 32, 32, x * 32 + dx, y * 32 + dy, 32, 32); - if (height>32) { - core.clearMap('event2', x * 32 + dx, y * 32 + 32 - height + dy, 32, height - 32) - core.drawImage('event2', image, posX * 32, posY * height, 32, height - 32, x * 32 + dx, y * 32 + 32 - height + dy, 32, height-32); - } -} - -maps.prototype._drawBlockInfo_bgfg = function (blockInfo, name, x, y) { - var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; - - core.clearMap(name, x * 32, y * 32 + 32 - height, 32, height); - if (name == 'bg') { - if (height>32) { - core.clearMap('bg', x * 32, y * 32 - 32, 32, 32); - core.drawImage('bg', core.material.groundCanvas.canvas, x * 32, y * 32 - 32); - } - core.drawImage('bg', core.material.groundCanvas.canvas, x * 32, y * 32); - } - core.drawImage(name, image, posX * 32, posY * height, 32, height, x * 32, y * 32 + 32 - height, 32, height); -} - -////// 背景/前景图块的绘制 ////// -maps.prototype.drawBgFgMap = function (floorId, ctx, name, onMap) { +////// 搜索某个图块出现的所有位置 ////// +maps.prototype.searchBlock = function (id, floorId, showDisable) { + if (typeof id == 'number') id = this.initBlock(0, 0, id).event.id; floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - var width = core.floors[floorId].width; - var height = core.floors[floorId].height; - - if (!core.isset(core.status[name+"maps"])) - core.status[name+"maps"] = {}; - - var arr = this.getBgFgMapArray(floorId, name); - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var block = this.initBlock(x, y, arr[y][x], true); - if (!core.isset(block.event)) continue; - block.name = name; - var blockInfo = this.getBlockInfo(block); - if (!core.isset(blockInfo)) continue; - this._drawBgFgMap_drawBlockInfo(ctx, block, blockInfo, arr, onMap); - } - } - if (onMap) - core.status.autotileAnimateObjs[name+"map"] = core.clone(arr); -} - -maps.prototype._drawBgFgMap_drawBlockInfo = function (ctx, block, blockInfo, arr, onMap) { - if (blockInfo.cls == 'autotile') { // Autotile单独处理 - this.drawAutotile(ctx, arr, block, 32, 0, 0); - if (onMap) core.addAutotileGlobalAnimate(block); - return; - } - if (!onMap) { - var height = blockInfo.height; - core.drawImage(ctx, blockInfo.image, 32 * blockInfo.posX, height * blockInfo.posY, 32, height, 32 * block.x, 32 * block.y, 32, height); - return; - } - this.drawBlock(block); - this.addGlobalAnimate(block); -} - -////// 生成groundPattern ////// -maps.prototype.generateGroundPattern = function (floorId) { - // 生成floorId层的groundPattern(盒子内的怪物动画) - var groundId = ((core.status.maps||core.floors)[floorId||core.status.floorId]||{}).defaultGround || "ground"; - core.material.groundCanvas.clearRect(0, 0, 32, 32); - core.material.groundCanvas.drawImage(core.material.images.terrains, 0, 32*core.material.icons.terrains[groundId], 32, 32, 0, 0, 32, 32); - core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); - // 如果需要用纯色可以直接将下面代码改成改成 - // core.material.groundPattern = '#000000'; -} - -maps.prototype._getFloorImages = function (floorId) { - floorId = floorId || core.status.floorId; - var images = []; - if (core.isset(core.status.maps[floorId].images)) { - images = core.status.maps[floorId].images; - if (typeof images == 'string') { - images = [[0, 0, images]]; - } - } - return images; -} - -maps.prototype.drawFloorImages = function (floorId, ctx, name, images, animate) { - floorId = floorId || core.status.floorId; - if (!core.isset(images)) images = this._getFloorImages(floorId); - var redraw = core.isset(animate); - if (!redraw) core.status.floorAnimateObjs = core.clone(images); - images.forEach(function (t) { - if (typeof t == 'string') t = [0,0,t]; - var dx=parseInt(t[0]), dy=parseInt(t[1]), imageName=t[2], frame = core.clamp(parseInt(t[4]), 1, 8); - var image = core.material.images.images[imageName]; - if (redraw && frame == 1) return; // 不重绘 - - if (core.isset(dx) && core.isset(dy) && core.isset(image) && - !core.hasFlag("floorimg_"+floorId+"@"+dx+"@"+dy)) { - var width = parseInt(image.width / frame), offsetX = (animate||0)%frame*width; - if (/.*\.gif/i.test(imageName) && main.mode=='play') { - if (redraw) return; // 忽略gif - this._drawFloorImages_gif(image, dx, dy); - return; - } - core.maps._drawFloorImage(ctx, name, t[3], image, offsetX, width, dx, dy, redraw); - } - }); -} - -maps.prototype._drawFloorImages_gif = function (image, dx, dy) { - core.dom.gif.innerHTML = ""; - var gif = new Image(); - gif.src = image.src; - gif.style.position = 'absolute'; - gif.style.left = (dx*core.domStyle.scale)+"px"; - gif.style.top = (dy*core.domStyle.scale)+"px"; - gif.style.width = image.width*core.domStyle.scale+"px"; - gif.style.height = image.height*core.domStyle.scale+"px"; - core.dom.gif.appendChild(gif); - return; -} - -maps.prototype._drawFloorImage = function (ctx, name, type, image, offsetX, width, dx, dy, redraw) { - var height = image.height; - var _draw = function () { - if (redraw) core.clearMap(ctx, dx, dy, width, height); - core.drawImage(ctx, image, offsetX, 0, width, height, dx, dy, width, height); - } - if (!type) { - if (name != 'bg') return; - return _draw(); - } - if (type==1) { - if (name != 'fg') return; - return _draw(); - } - if (type==2) { - if (name == 'bg') { - if (redraw) core.clearMap(ctx, dx, dy + height - 32, width, 32); - core.drawImage('bg', image, offsetX, height-32, width, 32, dx, dy + height - 32, width, 32); - } - else if (name == 'fg') { - if (redraw) core.clearMap(ctx, dx, dy, width, height-32); - core.drawImage('fg', image, offsetX, 0, width, height-32, dx, dy, width, height-32); - } - return; - } -} - -////// 绘制某张地图 ////// -maps.prototype.drawMap = function (floorId, callback) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) { - if (core.isset(callback)) callback(); - return; - } - core.clearMap('all'); - this.generateGroundPattern(floorId); - core.status.floorId = floorId; - core.status.thisMap = core.status.maps[floorId]; - - this._drawMap_drawBgFg(); - this._drawMap_drawEvent(); - if (core.isset(core.status.curtainColor)) { - core.fillRect('curtain', 0, 0, this.DEFAULT_PIXEL_WIDTH, this.DEFAULT_PIXEL_HEIGHT, - core.arrayToRGBA(core.status.curtainColor)); - } - core.setGlobalAnimate(core.values.animateSpeed); - core.drawHero(); - core.updateStatusBar(); - if (core.isset(callback)) - callback(); -} - -maps.prototype._drawMap_drawEvent = function (floorId) { - floorId = floorId || core.status.floorId; - var mapBlocks = core.status.maps[floorId].blocks; - - var mapArray = this.getMapArray(mapBlocks, core.bigmap.width, core.bigmap.height); - for (var b = 0; b < mapBlocks.length; b++) { - // 事件启用 - var block = mapBlocks[b]; - if (core.isset(block.event) && !block.disable) { - if (block.event.cls == 'autotile') { - core.drawAutotile(core.canvas.event, mapArray, block, 32, 0, 0); - core.addAutotileGlobalAnimate(block); - } - else { - core.drawBlock(block); - core.addGlobalAnimate(block); - } - } - } - core.status.autotileAnimateObjs.map = core.clone(mapArray); -} - -maps.prototype._drawMap_drawBgFg = function (floorId) { - floorId = floorId || core.status.floorId; - this.drawBg(floorId); - this.drawFg(floorId); -} - -maps.prototype.drawBg = function (floorId, ctx) { - var onMap = !core.isset(ctx); - if (onMap) { - ctx = core.canvas.bg; - core.clearMap(ctx); - var width = core.floors[floorId].width; - var height = core.floors[floorId].height; - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - core.drawImage(ctx, core.material.groundCanvas.canvas, 32*x, 32*y); - } - } - } - this._drawBg_drawContent(floorId, ctx, onMap); -} - -maps.prototype.drawFg = function (floorId, ctx) { - var onMap = !core.isset(ctx); - if (onMap) ctx = core.canvas.fg; - this._drawFg_drawContent(floorId, ctx, onMap); -} - -// --- 实际绘制背景层;可以调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块 -// 先绘制的会被后绘制的覆盖 -maps.prototype._drawBg_drawContent = function (floorId, ctx, onMap) { - this.drawFloorImages(floorId, ctx, 'bg'); - this.drawBgFgMap(floorId, ctx, 'bg', onMap); -} - -// --- 实际绘制前景层;可以调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块 -// 先绘制的会被后绘制的覆盖 -maps.prototype._drawFg_drawContent = function (floorId, ctx, onMap) { - this.drawFloorImages(floorId, ctx, 'fg'); - this.drawBgFgMap(floorId, ctx, 'fg', onMap); -} - -////// 绘制Autotile ////// -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 | - [10, 9, 4, 13], //1 bin:0001 +----+----+----+----+----+----+ - [10, 9, 18, 3 ], //2 bin:0010 | 7 | 8 | 9 | 10 | 11 | 12 | - [10, 9, 16, 15], //3 bin:0011 +----+----+----+----+----+----+ - [10, 43, 4, 3 ], //4 bin:0100 | 13 | 14 | 15 | 16 | 17 | 18 | - [10, 31, 4, 25], //5 bin:0101 +----+----+----+----+----+----+ - [10, 7, 2, 3 ], //6 bin:0110 | 19 | 20 | 21 | 22 | 23 | 24 | - [10, 31, 16, 5 ], //7 bin:0111 +----+----+----+----+----+----+ - [48, 9, 4, 3 ], //8 bin:1000 | 25 | 26 | 27 | 28 | 29 | 30 | - [ 8, 9, 4, 1 ], //9 bin:1001 +----+----+----+----+----+----+ - [36, 9, 30, 3 ], //10 bin:1010 | 31 | 32 | 33 | 34 | 35 | 36 | - [36, 9, 6, 15], //11 bin:1011 +----+----+----+----+----+----+ - [46, 45, 4, 3 ], //12 bin:1100 | 37 | 38 | 39 | 40 | 41 | 42 | - [46, 11, 4, 25], //13 bin:1101 +----+----+----+----+----+----+ - [12, 45, 30, 3 ], //14 bin:1110 | 43 | 44 | 45 | 46 | 47 | 48 | - [34, 33, 28, 27] //15 bin:1111 +----+----+----+----+----+----+ - ]; - - var drawBlockByIndex = function(ctx, dx, dy, autotileImg, index, size){ //index为autotile的图块索引1-48 - var sx = 16*((index-1)%6), sy = 16*(~~((index-1)/6)); - status = status || 0; - status %= parseInt(autotileImg.width/96); - ctx.drawImage(autotileImg, sx + 96*status, sy, 16, 16, dx, dy, size/2, size/2); - } - var getAutotileAroundId = function(currId, x, y) { - if(x<0 || y<0 || x>=mapArr[0].length || y>=mapArr.length) return 1; - else return core.material.autotileEdges[currId].indexOf(mapArr[y][x])>=0; - } - var checkAround = function(x, y){ // 得到周围四个32*32块(周围每块都包含当前块的1/4,不清楚的话画下图你就明白)的数组索引 - var currId = mapArr[y][x]; - var pointBlock = []; - for(var i=0; i<4; i++){ - var bsum = 0; - var offsetx = i%2, offsety = ~~(i/2); - for(var j=0; j<4; j++){ - var mx = j%2, my = ~~(j/2); - var b = getAutotileAroundId(currId, x+offsetx+mx-1, y+offsety+my-1); - bsum += b*(Math.pow(2, 3-j)); - } - pointBlock.push(bsum); - } - return pointBlock; - } - var getAutotileIndexs = function(x, y){ - var indexArr = []; - var pointBlocks = checkAround(x, y); - for(var i=0; i<4; i++){ - var arr = indexArrs[pointBlocks[i]] - indexArr.push(arr[3-i]); - } - return indexArr; - } - // 开始绘制autotile - var x = block.x, y = block.y; - var pieceIndexs = getAutotileIndexs(x, y); - - //修正四个边角的固定搭配 - if(pieceIndexs[0] == 13){ - if(pieceIndexs[1] == 16) pieceIndexs[1] = 14; - if(pieceIndexs[2] == 31) pieceIndexs[2] = 19; - } - if(pieceIndexs[1] == 18){ - if(pieceIndexs[0] == 15) pieceIndexs[0] = 17; - if(pieceIndexs[3] == 36) pieceIndexs[3] = 24; - } - if(pieceIndexs[2] == 43){ - if(pieceIndexs[0] == 25) pieceIndexs[0] = 37; - if(pieceIndexs[3] == 46) pieceIndexs[3] = 44; - } - if(pieceIndexs[3] == 48){ - if(pieceIndexs[1] == 30) pieceIndexs[1] = 42; - if(pieceIndexs[2] == 45) pieceIndexs[2] = 47; - } - for(var i=0; i<4; i++){ - var index = pieceIndexs[i]; - var dx = x*size + size/2*(i%2), dy = y*size + size/2*(~~(i/2)); - drawBlockByIndex(ctx, dx+left, dy+top, core.material.images['autotile'][block.event.id], index, size); - } -} - -////// 为autotile判定边界 ////// -maps.prototype.makeAutotileEdges = function () { - var autotileIds = Object.keys(core.material.images.autotile); - core.material.autotileEdges = {}; - - var canvas = document.createElement("canvas"), ctx = canvas.getContext('2d'); - canvas.width = canvas.height = 32; - ctx.mozImageSmoothingEnabled = false; - ctx.webkitImageSmoothingEnabled = false; - ctx.msImageSmoothingEnabled = false; - ctx.imageSmoothingEnabled = false; - - autotileIds.forEach(function (t) { - var n = core.maps.getNumberById(t); - core.material.autotileEdges[n] = [n]; - - ctx.clearRect(0,0,32,32); - ctx.drawImage(core.material.images.autotile[t], 0, 0, 32, 32, 0, 0, 32, 32); - var data = canvas.toDataURL("image/png"); - - autotileIds.forEach(function (t2) { - if (t==t2) return; - var n2 = core.maps.getNumberById(t2); - - ctx.clearRect(0,0,32,32); - ctx.drawImage(core.material.images.autotile[t2], 32, 0, 32, 32, 0, 0, 32, 32); - if (data == canvas.toDataURL("image/png")) { - core.material.autotileEdges[n].push(n2); - } + var result = []; + if (floorId instanceof Array) { + floorId.forEach(function (floorId) { + result = result.concat(core.searchBlock(id, floorId, showDisable)); }); + return result; + } + 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, x: block.x, y: block.y}); + } + return result; +} + +// -------- 启用/禁用图块,楼层贴图 -------- // + +////// 将某个块从禁用变成启用状态 ////// +maps.prototype.showBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + var block = core.getBlock(x, y, floorId, true); + if (block == null) return; // 不存在 + block = block.block; + // 本身是禁用事件,启用之 + if (block.disable) { + block.disable = false; + // 在本层,添加动画 + if (floorId == core.status.floorId) { + core.drawBlock(block); + core.addGlobalAnimate(block); + } + core.updateStatusBar(); + } +} + +////// 只隐藏但不删除某块 ////// +maps.prototype.hideBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + + var block = core.getBlock(x, y, floorId, true); + if (block == null) return; // 不存在 + + // 删除动画,清除地图 + if (floorId == core.status.floorId) { + core.removeGlobalAnimate(x, y); + core.clearMap('event', x * 32, y * 32, 32, 32); + var height = block.block.event.height || 32; + if (height > 32) + core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32); + } + + block.block.disable = true; + core.updateStatusBar(); +} + +////// 将某个块从启用变成禁用状态 ////// +maps.prototype.removeBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + + var block = core.getBlock(x, y, floorId, true); + if (block == null) return; // 不存在 + + var index = block.index; + + // 删除动画,清除地图 + if (floorId == core.status.floorId) { + core.removeGlobalAnimate(x, y); + core.clearMap('event', x * 32, y * 32, 32, 32); + var height = block.block.event.height || 32; + if (height > 32) + core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32); + } + + // 删除Index + core.removeBlockById(index, floorId); + core.updateStatusBar(); +} + +////// 根据block的索引(尽可能)删除该块 ////// +maps.prototype.removeBlockById = function (index, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + + var blocks = core.status.maps[floorId].blocks, block = blocks[index]; + + if (this.canRemoveBlock(block, floorId)) { // 能否彻底删除该图块 + blocks.splice(index, 1); + } + else { + block.disable = true; + } +} + +////// 一次性删除多个block ////// +maps.prototype.removeBlockByIds = function (floorId, ids) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + ids.sort(function (a, b) { + return b - a + }).forEach(function (id) { + core.removeBlockById(id, floorId); }); } -////// 某个点是否不可通行 ////// -maps.prototype.noPassExists = function (x, y, floorId) { - var block = core.getBlock(x,y,floorId); - if (block==null) return false; - return core.isset(block.block.event.noPass) && block.block.event.noPass; +////// 能否彻底从地图中删除一个图块 ////// +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; } -////// 某个点是否在区域内且不可通行 ////// -maps.prototype.noPass = function (x, y) { - return x<0 || x>=core.bigmap.width || y<0 || y>=core.bigmap.height || this.noPassExists(x,y); +////// 显示前景/背景地图 ////// +maps.prototype.showBgFgMap = function (name, loc, floorId, callback) { + this._triggerBgFgMap('show', name, loc, floorId, callback); } -////// 某个点是否存在NPC ////// -maps.prototype.npcExists = function (x, y, floorId) { - var block = this.getBlock(x,y,floorId); - if (block==null) return false; - return block.block.event.cls.indexOf('npc')==0; +////// 隐藏前景/背景地图 ////// +maps.prototype.hideBgFgMap = function (name, loc, floorId, callback) { + this._triggerBgFgMap('hide', name, loc, floorId, callback); } -////// 某个点是否存在(指定的)地形 ////// -maps.prototype.terrainExists = function (x, y, id, floorId) { - var block = this.getBlock(x,y,floorId); - if (block==null) return false; - return block.block.event.cls=='terrains' && (core.isset(id)?block.block.event.id==id:true); -} - -////// 某个点是否存在楼梯 ////// -maps.prototype.stairExists = function (x, y, floorId) { - var block = this.getBlock(x,y,floorId); - if (block==null) return false; - return block.block.event.cls=='terrains' && (block.block.event.id=='upFloor' || block.block.event.id=='downFloor'); -} - -////// 当前位置是否在楼梯边 ////// -maps.prototype.nearStair = function() { - var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'); - return this.stairExists(x,y) || this.stairExists(x-1,y) || this.stairExists(x,y-1) || this.stairExists(x+1,y) || this.stairExists(x,y+1); -} - -////// 某个点是否存在(指定的)怪物 ////// -maps.prototype.enemyExists = function (x, y, id,floorId) { - var block = this.getBlock(x,y,floorId); - if (block==null) return false; - return block.block.event.cls.indexOf('enemy')==0 && (core.isset(id)?block.block.event.id==id:true); -} - -////// 获得某个点的block ////// -maps.prototype.getBlock = function (x, y, floorId, showDisable) { +////// 设置前景/背景地图的显示状态 ////// +maps.prototype._triggerBgFgMap = function (type, name, loc, floorId, callback) { + if (type != 'show') type = 'hide'; + if (name != 'fg') name = 'bg'; + if (typeof loc[0] == 'number' && typeof loc[1] == 'number') + loc = [loc]; floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return null; - var blocks = core.status.maps[floorId].blocks; - for (var n=0;n= 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]); + if (floorId == core.status.floorId) { + core.removeGlobalAnimate(x, y); + core.clearMap('event', x * 32, y * 32, 32, 32); + if (originBlock != null) { + var height = (originBlock.block.event || {}).height || 32; + if (height > 32) + core.clearMap('event2', x * 32, y * 32 + 32 - height, 32, height - 32); } } - return null; -} - -////// 获得某个点的blockId ////// -maps.prototype.getBlockId = function (x, y, floorId, showDisable) { - var block = core.getBlock(x, y, floorId, showDisable); - if (block == null) return null; - if (core.isset(block.block.event)) return block.block.event.id; - return null; -} - -////// 获得某个点的blockCls ////// -maps.prototype.getBlockCls = function (x, y, floorId, showDisable) { - var block = core.getBlock(x, y, floorId, showDisable); - if (block == null) return null; - if (core.isset(block.block.event)) return block.block.event.cls; - return null; -} - -maps.prototype.__moveBlockCanvas = function (image, bx, by, height, nowX, nowY, opacity, headCanvas, bodyCanvas, damageCanvas) { - // 重绘block & 重定位 - if (headCanvas != null) { - core.dymCanvas[headCanvas].clearRect(0, 0, 32, height); - core.dymCanvas[headCanvas].drawImage(image, bx * 32, by * height, 32, height - 32, 0, 0, 32, height - 32); - core.relocateCanvas(headCanvas, nowX - core.bigmap.offsetX, nowY+32-height - core.bigmap.offsetY); - core.setOpacity(headCanvas, opacity); + if (originBlock == null) { + core.status.maps[floorId].blocks.push(block); } - if (bodyCanvas != null) { - core.dymCanvas[bodyCanvas].clearRect(0, 0, 32, 32); - core.dymCanvas[bodyCanvas].drawImage(image, bx * 32, by * height + height - 32, 32, 32, 0, 0, 32, 32); - core.relocateCanvas(bodyCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); - core.setOpacity(bodyCanvas, opacity); + else { + originBlock.block.id = number; + originBlock.block.event = block.event; + block = originBlock.block; } - if (damageCanvas != null) { - core.relocateCanvas(damageCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); - core.setOpacity(damageCanvas, opacity); + if (floorId == core.status.floorId && !block.disable) { + core.drawBlock(block); + core.addGlobalAnimate(block); + core.updateStatusBar(); } } -maps.prototype.__initBlockCanvas = function (block, height, x, y) { - var headCanvas = null, bodyCanvas = 'block'+x+"_"+y, damageCanvas = null; +////// 将地图中所有某个图块替换成另一个图块 ////// +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; + 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 (name != 'bg' && name != 'fg') return; + + var vFlag = "__" + name + "Value__" + floorId + "_" + x + "_" + y; + core.setFlag(vFlag, number); + core.status[name + "maps"][floorId] = null; + + if (floorId == core.status.floorId) + core.drawMap(floorId); +} + +////// 重置地图 ////// +maps.prototype.resetMap = function (floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + if (typeof floorId == 'string') floorId = [floorId]; + var needRefresh = false; + floorId.forEach(function (t) { + core.status.maps[t] = core.maps.loadFloor(t); + if (t == core.status.floorId) needRefresh = true; + }); + if (needRefresh) this.drawMap(); + core.drawTip("地图重置成功"); +} + +// -------- 移动/跳跃图块,图块的淡入淡出 -------- // + +////// 初始化独立的block canvas ////// +maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) { + var headCanvas = null, bodyCanvas = '__body_' + x + "_" + y, damageCanvas = null; + // head + if (blockInfo.height > 32) { + headCanvas = "__head_" + x + "_" + y; + core.createCanvas(headCanvas, 0, 0, 32, blockInfo.height - 32, 55); + } + // body core.createCanvas(bodyCanvas, 0, 0, 32, 32, 35); - if (height > 32) { - headCanvas = "blockHead"+x+"_"+y; - core.createCanvas(headCanvas, 0, 0, 32, height - 32, 55); - } - // 显伤 + // damage var damage = null, damageColor = null; - if ((block.event.cls == 'enemys' || block.event.cls == 'enemy48') && core.hasItem('book') - && block.event.displayDamage !== false) { - var damageString = core.enemys.getDamageString(block.event.id, x, y); - damage = damageString.damage; damageColor = damageString.color; + if (blockInfo.cls.indexOf('enemy') == 0 && core.hasItem('book') && displayDamage) { + var damageString = core.enemys.getDamageString(blockInfo.id, x, y); + damage = damageString.damage; + damageColor = damageString.color; } if (damage != null) { - damageCanvas = "blockDamage"+x+"_"+y; + damageCanvas = "__damage_" + x + "_" + y; var ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65); ctx.textAlign = 'left'; ctx.font = "bold 11px Arial"; core.fillBoldText(ctx, damage, 1, 31, damageColor); if (core.flags.displayCritical) { - var critical = core.enemys.nextCriticals(block.event.id); - if (critical.length>0) critical=critical[0]; + var critical = core.enemys.nextCriticals(blockInfo.id); + if (critical.length > 0) critical = critical[0]; critical = core.formatBigNumber(critical[0], true); if (critical == '???') critical = '?'; core.fillBoldText(ctx, critical, 1, 21, '#FFFFFF'); @@ -1064,455 +1542,267 @@ maps.prototype.__initBlockCanvas = function (block, height, x, y) { } } -////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// -maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { - time = time || 500; - var floorId = core.status.floorId; - - var block = core.getBlock(x,y); - if (block==null) {// 不存在 - if (core.isset(callback)) callback(); - return; +////// 移动独立的block canvas ////// +maps.prototype._moveDetachedBlock = function (blockInfo, nowX, nowY, opacity, canvases) { + var height = blockInfo.height, posX = blockInfo.posX, posY = blockInfo.posY, image = blockInfo.image; + var headCanvas = canvases.headCanvas, bodyCanvas = canvases.bodyCanvas, damageCanvas = canvases.damageCanvas; + if (headCanvas) { + core.dymCanvas[headCanvas].clearRect(0, 0, 32, height); + core.dymCanvas[headCanvas].drawImage(image, posX * 32, posY * height, 32, height - 32, 0, 0, 32, height - 32); + core.relocateCanvas(headCanvas, nowX - core.bigmap.offsetX, nowY + 32 - height - core.bigmap.offsetY); + core.setOpacity(headCanvas, opacity); } - var id = block.block.id; + if (bodyCanvas) { + core.dymCanvas[bodyCanvas].clearRect(0, 0, 32, 32); + core.dymCanvas[bodyCanvas].drawImage(image, posX * 32, posY * height + height - 32, 32, 32, 0, 0, 32, 32); + core.relocateCanvas(bodyCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); + core.setOpacity(bodyCanvas, opacity); + } + if (damageCanvas) { + core.relocateCanvas(damageCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); + core.setOpacity(damageCanvas, opacity); + } +} - // 需要删除该块 - core.removeBlock(x,y); +////// 删除独立的block canvas ////// +maps.prototype._deleteDetachedBlock = function (canvases) { + core.deleteCanvas(canvases.headCanvas); + core.deleteCanvas(canvases.bodyCanvas); + core.deleteCanvas(canvases.damageCanvas); +} - block=block.block; +maps.prototype._getAndRemoveBlock = function (x, y) { + var block = core.getBlock(x, y); + if (block == null) return null; + block = block.block; var blockInfo = this.getBlockInfo(block); - if (blockInfo == null) { - if (core.isset(callback)) callback(); + if (blockInfo == null) return; + core.removeBlock(x, y); + return [block, blockInfo]; +} + +////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// +maps.prototype.moveBlock = function (x, y, steps, time, keep, callback) { + time = time || 500; + var blockArr = this._getAndRemoveBlock(x, y); + if (blockArr == null) { + if (callback) callback(); return; } - var image = blockInfo.image, bx = blockInfo.bx, by = blockInfo.by, height = blockInfo.height, isTileset = blockInfo.isTileset, faceIds = blockInfo.faceIds; - - // 要运行的轨迹:将steps展开 - var moveSteps=[]; - steps.forEach(function (e) { - if (typeof e=="string") { - moveSteps.push(e); - } - else { - if (!core.isset(e.value)) { - moveSteps.push(e.direction) - } - else { - for (var i=0;i=0; }); - moveSteps = moveSteps.filter(function (t) { return ['up','down','left','right'].indexOf(t)>=0;}); + var canvases = this._initDetachedBlock(blockInfo, x, y, block.event.animate !== false); + this._moveDetachedBlock(blockInfo, 32 * x, 32 * y, 1, canvases); - var nowX=32*x, nowY=32*y, step=0; - var destX=x, destY=y; - moveSteps.forEach(function (t) { - destX += core.utils.scan[t].x; - destY += core.utils.scan[t].y; - }); + var moveInfo = { + x: x, y: y, px: 32 * x, py: 32 * y, opacity: 1, keep: keep, + moveSteps: moveSteps, step: 0, per_time: time / 16 / core.status.replay.speed + } + this._moveBlock_doMove(blockInfo, canvases, moveInfo, callback); +} - var animateValue = core.icons._getAnimateFrames(block.event.cls, true), animateCurrent = isTileset?bx:0, animateTime = 0; - var blockCanvas = this.__initBlockCanvas(block, height, x, y); - var headCanvas = blockCanvas.headCanvas, bodyCanvas = blockCanvas.bodyCanvas, damageCanvas = blockCanvas.damageCanvas; - var opacity = 1; - - core.maps.__moveBlockCanvas(image, animateCurrent, by, height, nowX, nowY, opacity, headCanvas, bodyCanvas, damageCanvas); - - var animate=window.setInterval(function() { - - animateTime += time / 16 / core.status.replay.speed; - if (animateTime >= core.values.animateSpeed) { - animateCurrent++; - animateTime = 0; - if (animateCurrent>=animateValue) animateCurrent=0; - } - if (isTileset) animateCurrent = bx; - - // 已经移动完毕,消失 - if (moveSteps.length==0 || floorId != core.status.floorId) { - if (keep || floorId!=core.status.floorId) opacity=0; - else opacity -= 0.06; - if (opacity<=0) { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.deleteCanvas(headCanvas); - core.deleteCanvas(bodyCanvas); - core.deleteCanvas(damageCanvas); - // 不消失 - if (keep) { - core.setBlock(id, destX, destY, floorId); - if (floorId == core.status.floorId) - core.showBlock(destX, destY); - } - if (core.isset(callback)) callback(); - } - else { - core.maps.__moveBlockCanvas(image, animateCurrent, by, height, nowX, nowY, opacity, headCanvas, bodyCanvas, damageCanvas); +maps.prototype._moveBlock_doMove = function (blockInfo, canvases, moveInfo, callback) { + var animateTotal = core.icons._getAnimateFrames(blockInfo.cls, true), animateTime = 0; + var animate = window.setInterval(function () { + if (blockInfo.cls != 'tileset') { + animateTime += moveInfo.per_time; + if (animateTime > core.values.animateSpeed) { + animateTime = 0; + blockInfo.posX = (blockInfo.posX + 1) % animateTotal; } } - else { - // 移动中 - var direction = moveSteps[0]; - if (step == 0) { - // 根据faceIds修改朝向 - var currid = faceIds[direction]; - if (core.isset(currid)) { - var tby = core.material.icons[block.event.cls][currid]; - if (core.isset(tby)) - by = tby; - } - } - step++; - nowX+=core.utils.scan[direction].x*2; - nowY+=core.utils.scan[direction].y*2; - // 移动 - core.maps.__moveBlockCanvas(image, animateCurrent, by, height, nowX, nowY, opacity, headCanvas, bodyCanvas, damageCanvas); - if (step==16) { - // 该移动完毕,继续 - step=0; - moveSteps.shift(); - } - } - }, time / 16 / core.status.replay.speed); + if (moveInfo.moveSteps.length != 0) + core.maps._moveBlock_moving(blockInfo, canvases, moveInfo); + else + core.maps._moveJumpBlock_finished(blockInfo, canvases, moveInfo, animate, callback); + }, moveInfo.per_time); core.animateFrame.asyncId[animate] = true; +} +maps.prototype._moveBlock_moving = function (blockInfo, canvases, moveInfo) { + var direction = moveInfo.moveSteps[0]; + if (moveInfo.step == 0) { + moveInfo.x += core.utils.scan[direction].x; + moveInfo.y += core.utils.scan[direction].y; + // 根据faceIds修改朝向 + var currid = blockInfo.faceIds[direction]; + if (currid) { + var posY = core.material.icons[blockInfo.cls][currid]; + if (posY != null) blockInfo.posY = posY; + } + } + moveInfo.step++; + moveInfo.px += core.utils.scan[direction].x * 2; + moveInfo.py += core.utils.scan[direction].y * 2; + this._moveDetachedBlock(blockInfo, moveInfo.px, moveInfo.py, moveInfo.opacity, canvases); + if (moveInfo.step == 16) { + moveInfo.step = 0; + moveInfo.moveSteps.shift(); + } } ////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// -maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { +maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, callback) { time = time || 500; - var floorId = core.status.floorId; - var block = core.getBlock(sx,sy); - if (block==null) { - if (core.isset(callback)) callback(); + var blockArr = this._getAndRemoveBlock(sx, sy); + if (blockArr == null) { + if (callback) callback(); return; } - var id = block.block.id; - - // 需要删除该块 - core.removeBlock(sx,sy); - - block=block.block; - var blockInfo = this.getBlockInfo(block); - if (blockInfo == null) { - if (core.isset(callback)) callback(); - return; - } - var image = blockInfo.image, bx = blockInfo.bx, by = blockInfo.by, height = blockInfo.height, isTileset = blockInfo.isTileset, faceIds = blockInfo.faceIds; - + 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); core.playSound('jump.mp3'); + var jumpInfo = this.__generateJumpInfo(sx, sy, ex, ey, time); + jumpInfo.keep = keep; - var dx = ex-sx, dy=ey-sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); + this._jumpBlock_doJump(blockInfo, canvases, jumpInfo, callback); +} + +maps.prototype.__generateJumpInfo = function (sx, sy, ex, ey, time) { + var dx = ex - sx, dy = ey - sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); var jump_peak = 6 + distance, jump_count = jump_peak * 2; - var currx = sx, curry = sy; + 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 / jump_count + }; +} - var drawX = function() { - return currx * 32; - } - - var drawY = function() { - var ret = curry * 32; - if(jump_count >= jump_peak){ - var n = jump_count - jump_peak; - }else{ - var n = jump_peak - jump_count; - } - return ret - (jump_peak * jump_peak - n * n) / 2; - } - - var updateJump = function() { - jump_count--; - currx = (currx * jump_count + ex) / (jump_count + 1.0); - curry = (curry * jump_count + ey) / (jump_count + 1.0); - } - - var blockCanvas = this.__initBlockCanvas(block, height, sx, sy); - var headCanvas = blockCanvas.headCanvas, bodyCanvas = blockCanvas.bodyCanvas, damageCanvas = blockCanvas.damageCanvas; - var opacity = 1; - - core.maps.__moveBlockCanvas(image, bx, by, height, drawX(), drawY(), opacity, headCanvas, bodyCanvas, damageCanvas); - - var animate=window.setInterval(function() { - - if (jump_count>0 && floorId == core.status.floorId) { - updateJump(); - core.maps.__moveBlockCanvas(image, bx, by, height, drawX(), drawY(), opacity, headCanvas, bodyCanvas, damageCanvas); - } - else { - if (keep || floorId != core.status.floorId) opacity=0; - else opacity -= 0.06; - if (opacity<=0) { - delete core.animateFrame.asyncId[animate]; - clearInterval(animate); - core.deleteCanvas(headCanvas); - core.deleteCanvas(bodyCanvas); - core.deleteCanvas(damageCanvas); - if (keep) { - core.setBlock(id, ex, ey, floorId); - if (floorId == core.status.floorId) - core.showBlock(ex, ey); - } - if (core.isset(callback)) callback(); - } - else { - core.maps.__moveBlockCanvas(image, bx, by, height, drawX(), drawY(), opacity, headCanvas, bodyCanvas, damageCanvas); - } - } - - }, time / 16 / core.status.replay.speed); +maps.prototype._jumpBlock_doJump = function (blockInfo, canvases, jumpInfo, callback) { + var animate = window.setInterval(function () { + if (jumpInfo.jump_count > 0) + core.maps._jumpBlock_jumping(blockInfo, canvases, jumpInfo) + else + core.maps._moveJumpBlock_finished(blockInfo, canvases, jumpInfo, animate, callback); + }, jumpInfo.per_time); core.animateFrame.asyncId[animate] = true; } +maps.prototype.__updateJumpInfo = function (jumpInfo) { + jumpInfo.jump_count--; + jumpInfo.x = (jumpInfo.x * jumpInfo.jump_count + jumpInfo.ex) / (jumpInfo.jump_count + 1.0); + jumpInfo.y = (jumpInfo.y * jumpInfo.jump_count + jumpInfo.ey) / (jumpInfo.jump_count + 1.0); + jumpInfo.px = 32 * jumpInfo.x; + var delta = Math.abs(jumpInfo.jump_count - jumpInfo.jump_peak); + jumpInfo.py = 32 * jumpInfo.y - (jumpInfo.jump_peak * jumpInfo.jump_peak - delta * delta) / 2; +} + +maps.prototype._jumpBlock_jumping = function (blockInfo, canvases, jumpInfo) { + this.__updateJumpInfo(jumpInfo); + core.maps._moveDetachedBlock(blockInfo, jumpInfo.px, jumpInfo.py, jumpInfo.opacity, canvases); +} + +maps.prototype._moveJumpBlock_finished = function (blockInfo, canvases, info, animate, callback) { + if (info.keep) info.opacity = 0; + else info.opacity -= 0.06; + if (info.opacity <= 0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + this._deleteDetachedBlock(canvases); + // 不消失 + if (info.keep) { + core.setBlock(blockInfo.number, info.x, info.y); + core.showBlock(info.x, info.y); + } + if (callback) callback(); + } + else { + this._moveDetachedBlock(blockInfo, info.px, info.py, info.opacity, canvases); + } +} + ////// 显示/隐藏某个块时的动画效果 ////// -maps.prototype.animateBlock = function (loc,type,time,callback) { - if (type!='hide') type='show'; - +maps.prototype.animateBlock = function (loc, type, time, callback) { + var isHide = type == 'hide'; if (typeof loc[0] == 'number' && typeof loc[1] == 'number') loc = [loc]; - - var list = []; - loc.forEach(function (t) { - var block = core.getBlock(t[0],t[1],null,true); - if (block==null) return; - block=block.block; - - var blockInfo = core.maps.getBlockInfo(block); - if (blockInfo == null) return; - var blockCanvas = core.maps.__initBlockCanvas(block, blockInfo.height, t[0], t[1]); - var headCanvas = blockCanvas.headCanvas, bodyCanvas = blockCanvas.bodyCanvas, damageCanvas = blockCanvas.damageCanvas; - - list.push({ - 'x': t[0], 'y': t[1], 'height': blockInfo.height, - 'bx': blockInfo.bx, 'by': blockInfo.by, 'image': blockInfo.image, - 'headCanvas': headCanvas, 'bodyCanvas': bodyCanvas, 'damageCanvas': damageCanvas - }); - - }); - - if (list.length==0) { - if (core.isset(callback)) callback(); + // --- 检测所有是0的点 + var list = this._animateBlock_getList(loc); + if (list.length == 0) { + if (callback) callback(); return; } + this._animateBlock_drawList(list, isHide ? 1 : 0); + this._animateBlock_doAnimate(loc, list, isHide, 10 / time, callback); +} - var opacity = 0; - if (type=='hide') opacity=1; - - var draw = function () { - list.forEach(function (t) { - core.maps.__moveBlockCanvas(t.image, t.bx, t.by, t.height, t.x*32, t.y*32, opacity, t.headCanvas, t.bodyCanvas, t.damageCanvas); - }) - }; - draw(); - - var per_time = 10, steps = parseInt(time / per_time), delta = 1 / steps; +maps.prototype._animateBlock_doAnimate = function (loc, list, isHide, delta, callback) { + var opacity = isHide ? 1 : 0; var animate = setInterval(function () { - if (type=='show') opacity += delta; - else opacity -= delta; - if (opacity >=1 || opacity<=0) { + opacity += isHide ? -delta : delta; + core.maps._animateBlock_drawList(list, opacity); + if (opacity >= 1 || opacity <= 0) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); list.forEach(function (t) { - core.deleteCanvas(t.headCanvas); - core.deleteCanvas(t.bodyCanvas); - core.deleteCanvas(t.damageCanvas); + if (t.blockInfo) + core.maps._deleteDetachedBlock(t.canvases); }); - if (type == 'show') { - loc.forEach(function (t) { - core.showBlock(t[0],t[1],data.floorId); - }); - } - else { - loc.forEach(function (t) { - core.removeBlock(t[0],t[1],data.floorId); - }); - } - if (core.isset(callback)) callback(); + loc.forEach(function (t) { + if (isHide) core.removeBlock(t[0], t[1]); + else core.showBlock(t[0], t[1]); + }); + if (callback) callback(); } - else { - draw(); - } - }, per_time); + }, 10); core.animateFrame.asyncId[animate] = true; } -////// 将某个块从禁用变成启用状态 ////// -maps.prototype.showBlock = function(x, y, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - var block = core.getBlock(x,y,floorId,true); - if (block==null) return; // 不存在 - block=block.block; - // 本身是禁用事件,启用之 - if (block.disable) { - block.disable = false; - // 在本层,添加动画 - if (floorId == core.status.floorId && core.isset(block.event)) { - core.drawBlock(block); - core.addGlobalAnimate(block); +maps.prototype._animateBlock_getList = function (loc) { + var list = []; + loc.forEach(function (t) { + var block = core.getBlock(t[0], t[1], null, true); + if (block == null) return; + block = block.block; + + var blockInfo = core.maps.getBlockInfo(block); + if (blockInfo == null) { + list.push({ 'x': t[0], 'y': t[1] }); + return; } - core.updateStatusBar(); - } + var canvases = core.maps._initDetachedBlock(blockInfo, t[0], t[1], block.event.displayDamage !== false); + + list.push({ + 'x': t[0], 'y': t[1], 'blockInfo': blockInfo, 'canvases': canvases + }); + + }); + return list; } -////// 只隐藏但不删除某块 ////// -maps.prototype.hideBlock = function (x, y, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - - var block = core.getBlock(x,y,floorId,true); - if (block==null) return; // 不存在 - - // 删除动画,清除地图 - if (floorId==core.status.floorId) { - core.removeGlobalAnimate(x, y); - core.clearMap('event', x * 32, y * 32, 32, 32); - var height = 32; - if (core.isset(block.block.event)) height=block.block.event.height||32; - if (height>32) - core.clearMap('event2', x * 32, y * 32 +32-height, 32, height-32); - } - - block.disable = true; - core.updateStatusBar(); -} - -////// 将某个块从启用变成禁用状态 ////// -maps.prototype.removeBlock = function (x, y, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - - var block = core.getBlock(x,y,floorId,true); - if (block==null) return; // 不存在 - - var index=block.index; - - // 删除动画,清除地图 - if (floorId==core.status.floorId) { - core.removeGlobalAnimate(x, y); - core.clearMap('event', x * 32, y * 32, 32, 32); - var height = 32; - if (core.isset(block.block.event)) height=block.block.event.height||32; - if (height>32) - core.clearMap('event2', x * 32, y * 32 +32-height, 32, height-32); - } - - // 删除Index - core.removeBlockById(index, floorId); - core.updateStatusBar(); -} - -////// 根据block的索引删除该块 ////// -maps.prototype.removeBlockById = function (index, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - - var blocks = core.status.maps[floorId].blocks, block = blocks[index]; - var x=block.x, y=block.y; - - // 检查该点是否存在事件 - var event = core.floors[floorId].events[x+","+y]; - if (!core.isset(event)) - event = core.floors[floorId].changeFloor[x+","+y]; - - // 检查是否存在重生 - var isReborn = false; - if (core.isset(block.event) && block.event.cls.indexOf('enemy')==0 - && core.enemys.hasSpecial(core.material.enemys[block.event.id].special, 23)) - isReborn = true; - - // 不存在事件,直接删除 - if (!isReborn && !core.isset(event)) { - blocks.splice(index,1); - return; - } - block.disable = true; -} - -////// 一次性删除多个block ////// -maps.prototype.removeBlockByIds = function (floorId, ids) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - ids.sort(function (a,b) {return b-a}).forEach(function (id) { - core.removeBlockById(id, floorId); +maps.prototype._animateBlock_drawList = function (list, opacity) { + list.forEach(function (t) { + if (t.blockInfo) + core.maps._moveDetachedBlock(t.blockInfo, t.x * 32, t.y * 32, opacity, t.canvases); }); } -////// 改变图块 ////// -maps.prototype.setBlock = function (number, x, y, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - if (!core.isset(number) || !core.isset(x) || !core.isset(y)) return; - if (x<0 || x>=core.floors[floorId].width || y<0 || y>=core.floors[floorId].height) return; +// ------ 全局动画控制,动画绘制 ------ // - var originBlock=core.getBlock(x,y,floorId,true); - var block = core.maps.initBlock(x,y,number,true,core.floors[floorId]); - if (core.isset(block.event)) { - if (floorId == core.status.floorId) { - core.removeGlobalAnimate(x, y); - core.clearMap('event', x * 32, y * 32, 32, 32); - if (originBlock != null) { - var height = (originBlock.block.event||{}).height||32; - if (height>32) - core.clearMap('event2', x * 32, y * 32 +32-height, 32, height-32); - } - } - if (originBlock==null) { - core.status.maps[floorId].blocks.push(block); - } - else { - originBlock.block.id = number; - originBlock.block.event = block.event; - block = originBlock.block; - } - if (floorId==core.status.floorId && !block.disable) { - core.drawBlock(block); - core.addGlobalAnimate(block); - core.updateStatusBar(); - } +////// 添加一个全局动画 ////// +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(block); + } + else { + if (!block.event.animate || block.event.animate == 1) return; + core.status.globalAnimateObjs.push(block); } } -////// 改变图层块 ////// -maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { - floorId = floorId || core.status.floorId; - if (!core.isset(floorId)) return; - if (!core.isset(number) || !core.isset(x) || !core.isset(y)) return; - if (x<0 || x>=core.floors[floorId].width || y<0 || y>=core.floors[floorId].height) return; - if (name!='bg' && name!='fg') return; - - core.setFlag(name+"v_"+floorId+"_"+x+"_"+y, number); - core.status[name+"maps"][floorId] = null; - - if (floorId == core.status.floorId) - core.drawMap(floorId); -} - -////// 添加一个全局动画 ////// -maps.prototype.addGlobalAnimate = function (b) { - if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; - if (!core.isset(b.event) || !core.isset(b.event.animate) || b.event.animate==1) return; - core.status.globalAnimateObjs.push(b); -} - -////// 添加一个Autotile全局动画 ////// -maps.prototype.addAutotileGlobalAnimate = function (b) { - if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; - if (!core.isset(b.event) || b.event.cls!='autotile') return; - var id = b.event.id, img = core.material.images.autotile[id]; - if (!core.isset(img) || img.width==96) return; - core.status.autotileAnimateObjs.blocks.push(b); -} - ////// 删除一个或所有全局动画 ////// -maps.prototype.removeGlobalAnimate = function (x, y, all, name) { - if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; - - if (all) { +maps.prototype.removeGlobalAnimate = function (x, y, name) { + // 没有定义xy,则全部删除 + if (x == null || y == null) { core.status.globalAnimateStatus = 0; core.status.globalAnimateObjs = []; core.status.autotileAnimateObjs = {"blocks": [], "map": null, "bgmap": null, "fgmap": null}; @@ -1520,23 +1810,20 @@ maps.prototype.removeGlobalAnimate = function (x, y, all, name) { return; } - core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) {return block.x!=x || block.y!=y || block.name!=name;}); + core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) { + return block.x != x || block.y != y || block.name != name; + }); // 检查Autotile - if (core.isset(core.status.autotileAnimateObjs.blocks)) { - core.status.autotileAnimateObjs.blocks = core.status.autotileAnimateObjs.blocks.filter(function (block) {return block.x!=x || block.y!=y || block.name!=name;}); + if (core.status.autotileAnimateObjs.blocks) { + core.status.autotileAnimateObjs.blocks = core.status.autotileAnimateObjs.blocks.filter(function (block) { + return block.x != x || block.y != y || block.name != name; + }); core.status.autotileAnimateObjs.map[y][x] = 0; } } -////// 设置全局动画的显示效果 ////// -maps.prototype.setGlobalAnimate = function (speed) { - if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; - core.status.globalAnimateStatus = 0; - core.animateFrame.globalAnimate = true; -} - ////// 绘制UI层的box动画 ////// maps.prototype.drawBoxAnimate = function () { core.status.boxAnimateObjs.forEach(function (obj) { @@ -1547,148 +1834,84 @@ maps.prototype.drawBoxAnimate = function () { }); } +////// 绘制动画 ////// +maps.prototype.drawAnimate = function (name, x, y, callback) { + + // 正在播放录像:不显示动画 + if (core.isReplaying()) { + if (callback) callback(); + return -1; + } + + // 检测动画是否存在 + if (!core.material.animates[name] || x == null || y == null) { + if (callback) callback(); + return -1; + } + + // 开始绘制 + var animate = core.material.animates[name], centerX = 32 * x + 16, centerY = 32 * y + 16; + // 播放音效 + core.playSound(animate.se); + + var id = setTimeout(null); + core.status.animateObjs.push({ + "id": id, + "animate": animate, + "centerX": centerX, + "centerY": centerY, + "index": 0, + "callback": callback + }); + + return id; +} + ////// 绘制动画的某一帧 ////// -maps.prototype.drawAnimateFrame = function (animate, centerX, centerY, index) { +maps.prototype._drawAnimateFrame = function (animate, centerX, centerY, index) { var frame = animate.frames[index]; var ratio = animate.ratio; frame.forEach(function (t) { var image = animate.images[t.index]; - if (!core.isset(image)) return; + if (!image) return; var realWidth = image.width * ratio * t.zoom / 100; var realHeight = image.height * ratio * t.zoom / 100; core.setAlpha('animate', t.opacity / 255); - var cx = centerX+t.x, cy=centerY+t.y; + var cx = centerX + t.x, cy = centerY + t.y; if (!t.mirror && !t.angle) { - core.drawImage('animate', image, cx-realWidth/2 - core.bigmap.offsetX, cy-realHeight/2 - core.bigmap.offsetY, realWidth, realHeight); + core.drawImage('animate', image, cx - realWidth / 2 - core.bigmap.offsetX, cy - realHeight / 2 - core.bigmap.offsetY, realWidth, realHeight); } else { core.saveCanvas('animate'); - core.canvas.animate.translate(cx,cy); + core.canvas.animate.translate(cx, cy); if (t.angle) - core.canvas.animate.rotate(-t.angle*Math.PI/180); + core.canvas.animate.rotate(-t.angle * Math.PI / 180); if (t.mirror) - core.canvas.animate.scale(-1,1); - core.drawImage('animate', image, -realWidth/2 - core.bigmap.offsetX, -realHeight/2 - core.bigmap.offsetY, realWidth, realHeight); + core.canvas.animate.scale(-1, 1); + core.drawImage('animate', image, -realWidth / 2 - core.bigmap.offsetX, -realHeight / 2 - core.bigmap.offsetY, realWidth, realHeight); core.loadCanvas('animate'); } core.setAlpha('animate', 1); }) } -////// 绘制动画 ////// -maps.prototype.drawAnimate = function (name, x, y, callback) { - - // 正在播放录像:不显示动画 - if (core.isReplaying()) { - if (core.isset(callback)) callback(); - return -1; - } - - // 检测动画是否存在 - if (!core.isset(core.material.animates[name]) || !core.isset(x) || !core.isset(y)) { - if (core.isset(callback)) callback(); - return -1; - } - - // 开始绘制 - var animate = core.material.animates[name], centerX = 32*x+16, centerY = 32*y+16; - // 播放音效 - core.playSound(animate.se); - - var animateId = parseInt(Math.random() * 100000000); - core.status.animateObjs.push({ - "animate": animate, - "centerX": centerX, - "centerY": centerY, - "index": 0, - "id": animateId, - "callback": callback - }); - - core.animateFrame.asyncId[animateId] = true; - return animateId; -} - ////// 停止动画 ////// maps.prototype.stopAnimate = function (id, doCallback) { - for (var i=0;i 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); if (!ctx) return; if (typeof image == 'string') { image = core.material.images.images[image]; - if (!core.isset(image)) return; + if (!image) return; } // 只能接受2, 4, 8个参数 @@ -224,9 +249,7 @@ ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1) { ////// 结束一切事件和绘制,关闭UI窗口,返回游戏进程 ////// ui.prototype.closePanel = function () { - core.status.boxAnimateObjs = []; - clearInterval(core.status.event.interval); - core.clearLastEvent(); + this.clearUI(); core.maps.generateGroundPattern(); core.updateStatusBar(); core.unLockControl(); @@ -237,21 +260,27 @@ ui.prototype.closePanel = function () { core.status.event.interval = null; } -////// 一般清除事件 ////// -ui.prototype.clearLastEvent = function () { - if (core.isset(core.dymCanvas.selector)) - core.deleteCanvas("selector"); +ui.prototype.clearUI = function () { + clearInterval(core.status.event.interval); + core.status.event.interval = null; + core.status.boxAnimateObjs = []; + if (core.dymCanvas._selector) core.deleteCanvas("_selector"); core.clearMap('ui'); core.setAlpha('ui', 1); } ////// 左上角绘制一段提示 ////// -ui.prototype.drawTip = function (text, itemIcon) { - var textX, textY, width, height, hide = false, alpha = 0; +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 (!core.isset(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,32 +292,32 @@ ui.prototype.drawTip = function (text, itemIcon) { width = textX + core.calWidth('data', text) + 8; height = 42; } + this._drawTip_animate(text, id, 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; - } - else { - alpha += 0.1; - } - core.clearMap('data', 5, 5, 416, height); + if (hide) alpha -= 0.1; + else alpha += 0.1; + core.clearMap('data', 5, 5, core.ui.PIXEL, height); core.setAlpha('data', alpha); core.fillRect('data', 5, 5, width, height, '#000'); - if (core.isset(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) { if (hide) { - core.clearMap('data', 5, 5, 416, height); + core.clearMap('data', 5, 5, core.ui.PIXEL, height); clearInterval(core.interval.tipAnimate); return; } else { - if (!core.isset(core.timeout.getItemTipTimeout)) { - core.timeout.getItemTipTimeout = window.setTimeout(function () { + if (!core.timeout.tipTimeout) { + core.timeout.tipTimeout = window.setTimeout(function () { hide = true; - core.timeout.getItemTipTimeout = null; + core.timeout.tipTimeout = null; }, 750); } alpha = 0.6; @@ -299,41 +328,12 @@ ui.prototype.drawTip = function (text, itemIcon) { ////// 地图中间绘制一段文字 ////// ui.prototype.drawText = function (contents, callback) { - if (core.isset(contents)) { - - // 合并 - if ((core.isset(core.status.event)&&core.status.event.id=='action') || core.isReplaying()) { - core.insertAction(contents,null,null,callback); - return; - } - - if (typeof contents == 'string') { - contents = [{'content': contents}]; - } - else if (contents instanceof Object && core.isset(contents.content)) { - contents = [contents]; - } - else if (!(contents instanceof Array)) { - core.drawTip("出错了"); - console.log(contents); - return; - } - - core.status.event = {'id': 'text', 'data': {'list': contents, 'callback': callback}}; - core.lockControl(); - - // wait the hero to stop - core.stopAutomaticRoute(); - setTimeout(function() { - core.drawText(); - }, 30); - return; - } + if (contents != null) return this._drawText_setContent(contents, callback); if (core.status.event.data.list.length==0) { var callback = core.status.event.data.callback; core.ui.closePanel(false); - if (core.isset(callback)) callback(); + if (callback) callback(); return; } @@ -342,77 +342,95 @@ ui.prototype.drawText = function (contents, callback) { core.ui.drawTextBox(data); else core.ui.drawTextBox(data.content, data.id); - // core.drawTextBox(content); } -ui.prototype.getTitleAndIcon = function (content) { - var id=null, name=null, image=null, icon=null, iconHeight=32, animate=null; - - var getInfo = function (v) { - var number = core.maps.getNumberById(v); - if (number>0) { - var block = core.maps.initBlock(0,0,number,true); - if (core.isset(block.event)) { - var cls = block.event.cls; - image = core.material.images[cls]; - icon = core.material.icons[cls][v]; - iconHeight = block.event.height; - animate = block.event.animate; - } - } - }; - - if (content.indexOf("\t[")==0 || content.indexOf("\\t[")==0) { - var index = content.indexOf("]"); - if (index>=0) { - var str=content.substring(2, index); - if (content.indexOf("\\t[")==0) str=content.substring(3, index); - content=content.substring(index+1); - var ss=str.split(","); - if (ss.length==1) { - if (/^[-\w.]+\.png$/.test(ss[0])) { - image = core.material.images.images[ss[0]]; - } - else { - id=ss[0]; - if (id=='hero') name = core.status.hero.name; - else if (core.isset(core.material.enemys[id])) { - name = core.material.enemys[id].name; - getInfo(id); - } - else { - name=id; - id='npc'; - } - } - } - else { - name=ss[0]; - id = 'npc'; - if (ss[1]=='hero') id = 'hero'; - else if (/^[-\w.]+\.png$/.test(ss[1])) { - image = core.material.images.images[ss[1]]; - } - else getInfo(ss[1]); - } - } +ui.prototype._drawText_setContent = function (contents, callback) { + // 合并进 insertAction + if ((core.status.event && core.status.event.id=='action') || core.isReplaying()) { + core.insertAction(contents,null,null,callback); + return; } + if (!(contents instanceof Array)) contents = [contents]; + + core.status.event = {'id': 'text', 'data': {'list': contents, 'callback': callback}}; + core.lockControl(); + + core.waitHeroToStop(core.drawText); + return; +} + +////// 正则处理 \t[xx,yy] 问题 +ui.prototype._getTitleAndIcon = function (content) { + var title = null, image = null, icon = null, height = 32, animate = 1; + content = content.replace(/(\t|\\t)\[(([^\],]+),)?([^\],]+)\]/g, function (s0, s1, s2, s3, s4) { + if (s4) { + if (s4 == 'hero') { + title = core.status.hero.name; + image = core.material.images.hero; + icon = 0; + height = core.material.icons.hero.height; + } + else if (/^[-\w.]+\.png$/.test(s4)) + image = core.material.images.images[s4]; + else { + var blockInfo = core.getBlockInfo(s4); + if (blockInfo != null) { + if (core.material.enemys[s4]) title = core.material.enemys[s4].name; + image = blockInfo.image; + icon = blockInfo.posY; + height = blockInfo.height; + animate = blockInfo.animate; + } + else title = s4; + } + } + if (s3) title = s3; + return ""; + }); return { - "content": content, - "id": id, - "name": name, - "image": image, - "icon": icon, - "iconHeight": iconHeight, - "animate": animate + content: content, + title: title, + image: image, + icon: icon, + height: height, + animate: animate }; } -// 绘制选择光标 -ui.prototype.drawWindowSelector = function(background,x,y,w,h) { +////// 正则处理 \b[up,xxx] 问题 +ui.prototype._getPosition = function (content) { + var pos = null, px = null, py = null; + if (core.status.event.id=='action') { + px = core.status.event.data.x; + py = core.status.event.data.y; + } + content = content.replace("\b", "\\b") + .replace(/\\b\[(up|center|down)(,(hero|null|\d+,\d+))?]/g, function (s0, s1, s2, s3) { + pos = s1; + if (s3 == 'hero') { + px = core.status.hero.loc.x; + py = core.status.hero.loc.y; + } + else if (s3 == 'null') { + px = py = null; + } + else if (s3) { + var str = s3.split(','); + px = parseInt(str[0]); + py = parseInt(str[1]); + } + return ""; + }); + return {content: content, position: pos, px: px, py: py}; +} + +////// 绘制选择光标 +ui.prototype.drawWindowSelector = function(background, x, y, w, h) { + if (typeof background == 'string') + background = core.material.images.images[background]; w = Math.round(w), h = Math.round(h); - var dstImage = core.ui.createCanvas("selector", x, y, w, h, 165); - core.setOpacity("selector", 0.8); + var dstImage = core.ui.createCanvas("_selector", x, y, w, h, 165); + core.setOpacity("_selector", 0.8); // back dstImage.drawImage(background, 130, 66, 28, 28, 2, 2,w-4,h-4); // corner @@ -427,10 +445,10 @@ ui.prototype.drawWindowSelector = function(background,x,y,w,h) { dstImage.drawImage(background, 158, 66, 2, 28,w-2, 2, 2,h-4); } -// 绘制皮肤 -ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py) { +////// 绘制 WindowSkin +ui.prototype.drawWindowSkin = function(background, ctx, x, y, w, h, direction, px, py) { // 仿RM窗口皮肤 ↓ - var dstImage = core.getContextByName(canvas); + var dstImage = core.getContextByName(ctx); if (!dstImage) return; // 绘制背景 dstImage.drawImage(background, 0, 0, 128, 128, x+2, y+2, w-4, h-4); @@ -456,7 +474,7 @@ ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py dstImage.drawImage(background, 176,48, 16, 16, x+w-16, y+h-16, 16, 16); // arrow - if(core.isset(px) && core.isset(py)){ + if(px != null && py != null){ if(direction == 'up'){ dstImage.drawImage(background,128,96,32,32,px,y+h-3,32,32); }else if(direction == 'down') { @@ -466,414 +484,457 @@ ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py // 仿RM窗口皮肤 ↑ } -// 计算有效文本框的宽度 -ui.prototype.calTextBoxWidth = function (canvas, content, min_width, max_width) { - // 无限长度自动换行 - var allLines = core.splitLines(canvas, content); +////// 绘制一个背景图,可绘制 winskin 或纯色背景;支持小箭头绘制 +ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) { + posInfo = posInfo || {}; + var px = posInfo.px == null ? null : posInfo.px * 32 - core.bigmap.offsetX; + var py = posInfo.py == null ? null : posInfo.py * 32 - core.bigmap.offsetY; + var xoffset = posInfo.xoffset || 0, yoffset = posInfo.yoffset || 0; + var background = core.status.textAttribute.background; - // 如果不存在手动换行,则二分自动换行 + if (typeof background == 'string' && core.material.images.images[background]) { + var image = core.material.images.images[background]; + if (image.width==192 && image.height==128) { + core.setAlpha('ui', 0.85); + this.drawWindowSkin(image, 'ui', left, top, right - left, bottom - top, posInfo.position, px, py); + core.setAlpha('ui', 1); + return true; + } + background = core.initStatus.textAttribute.background; + } + + var alpha = background[3]; + core.setAlpha('ui', alpha); + core.setStrokeStyle('ui', core.status.globalAttribute.borderColor); + core.setFillStyle('ui', core.arrayToRGB(background)); + core.setLineWidth('ui', 2); + + // 绘制 + var ctx = core.canvas.ui; + ctx.beginPath(); + ctx.moveTo(left, top); + // 上边缘三角 + if (posInfo.position == 'down' && px != null && py != null) { + ctx.lineTo(px + xoffset, top); + ctx.lineTo(px + 16, top - yoffset); + ctx.lineTo(px + 32 - xoffset, top); + } + ctx.lineTo(right, top); + ctx.lineTo(right, bottom); + // 下边缘三角 + if (posInfo.position == 'up' && px != null && py != null) { + ctx.lineTo(px + 32 - xoffset, bottom); + ctx.lineTo(px + 16, bottom + yoffset); + ctx.lineTo(px + xoffset, bottom); + } + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + core.setAlpha('ui', 1); + return false; +} + +////// 计算有效文本框的宽度 +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(canvas, allLines[0]); + var w = core.calWidth(ctx, allLines[0]) + 10; if (w= content.length) return false; - if (changed) { - core.setFillStyle(canvas, currcolor); - changed = false; - } + config.index = 0; + config.currcolor = config.color; + config.offsetX = 0; + config.offsetY = 0; + config.line = 0; + config.blocks = []; - // get next character - var ch = content.charAt(index++); - // \n, \\n - if (ch == '\n' || (ch=='\\' && content.charAt(index)=='n')) { - offsetx = content_left; - offsety += per_height; - if (ch=='\\') index++; - return drawNext(); - } - // \r, \\r - if (ch == '\r' || (ch=='\\' && content.charAt(index)=='r')) { - if (ch == '\\') index++; - changed = true; - // 检查是不是 [] - var index2; - if (content.charAt(index) == '[' && ((index2=content.indexOf(']', index))>=0)) { - // 变色 - var str = content.substring(index+1, index2); - if (str=="") currcolor = color; - else currcolor = str; - index = index2+1; - } - else currcolor = color; - return drawNext(); - } - // \\i 绘制图标 - if (ch == '\\' && content.charAt(index)=='i') { - // 绘制图标 - var index2; - if (content.charAt(index+1) == '[' && ((index2=content.indexOf(']', index+1))>=0)) { - var str = content.substring(index+2, index2); - // --- 获得图标 - var iconInfo = core.ui.__getIconInfo(str), image = iconInfo[0], icon = iconInfo[1]; - if (image != null) { - if (core.isset(valid_width) && offsetx + text_font + 6 > content_left + valid_width) { - index --; - offsetx = content_left; - offsety += per_height; - return drawNext(); - } - // --- 绘制 - core.drawImage(canvas, image, 0, 32*icon, 32, 32, offsetx + 2, offsety - text_font + 1, text_font+2, text_font+2); - offsetx += text_font + 6; - index = index2+1; - return true; - } - } - } - // 检查是不是自动换行 - var charwidth = core.calWidth(canvas, ch); - if (core.isset(valid_width) && offsetx + charwidth > content_left + valid_width) { - index--; - offsetx = content_left; - offsety += per_height; - return drawNext(); - } - // 输出 - core.fillText(canvas, ch, offsetx, offsety); - offsetx += charwidth; + // 创建一个新的临时画布 + var tempCtx = core.bigmap.tempCanvas; + tempCtx.canvas.height = ctx.canvas.height; + tempCtx.canvas.width = ctx.canvas.width; + var _textBaseLine = tempCtx.textBaseline; + tempCtx.textBaseline = 'top'; + tempCtx.font = this._buildFont(config.fontSize, config.bold); + tempCtx.fillStyle = config.color; + this._drawTextContent_draw(ctx, tempCtx, content, config); + tempCtx.textBaseline = _textBaseLine; +} + +// 绘制的基本逻辑: +// 1. 一个个字符绘制到对应画布上(靠左对齐);这个过程中,记下来每个字对应的方块 [x, y, w, h] +// 2. 每次换行时,计算当前行的宽度,然后如果是居中或者靠右对齐,则对当前行的每个小方块增加偏移量 +// 3. 实际绘制时,从临时画布直接将一个个小方块绘制到目标画布上,一次全部绘制,或者打字机效果一个个绘制 +ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) { + // Step 1: 绘制到tempCtx上,并记录下图块信息 + while (this._drawTextContent_next(tempCtx, content, config)); + + // Step 2: 从tempCtx绘制到画布上 + config.index = 0; + var _drawNext = function () { + if (config.index >= config.blocks.length) return false; + var block = config.blocks[config.index++]; + ctx.drawImage(tempCtx.canvas, block.left, block.top, block.width, block.height, + config.left + block.left + block.marginLeft, config.top + block.top, block.width, block.height); return true; - }; - - if (!core.isset(time) || time<=0) while (drawNext()); + } + if (config.time == 0) { + while (_drawNext()); + } else { core.status.event.interval = setInterval(function () { - changed = true; - if (!drawNext()) { + if (!_drawNext()) { clearInterval(core.status.event.interval); core.status.event.interval = null; } - }, time); + }, config.time); } } +ui.prototype._drawTextContent_next = function (tempCtx, content, config) { + if (config.index >= content.length) { + this._drawTextContent_newLine(tempCtx, config); + return false; + } + // get next character + var ch = content.charAt(config.index++); + return this._drawTextContent_drawChar(tempCtx, content, config, ch); +} + +// 绘制下一个字符 +ui.prototype._drawTextContent_drawChar = function (tempCtx, content, config, ch) { + // \n, \\n + if (ch == '\n' || (ch=='\\' && content.charAt(config.index)=='n')) { + this._drawTextContent_newLine(tempCtx, config); + if (ch=='\\') config.index++; + return this._drawTextContent_next(tempCtx, content, config); + } + // \r, \\r + if (ch == '\r' || (ch=='\\' && content.charAt(config.index)=='r')) { + if (ch == '\\') config.index++; + return this._drawTextContent_changeColor(tempCtx, content, config); + } + // \\i 绘制图标 + if (ch == '\\' && content.charAt(config.index)=='i') { + return this._drawTextContent_drawIcon(tempCtx, content, config); + } + // 检查是不是自动换行 + var charwidth = core.calWidth(tempCtx, ch); + if (config.maxWidth != null && config.offsetX + charwidth > config.maxWidth) { + this._drawTextContent_newLine(tempCtx, config); + config.index--; + return this._drawTextContent_next(tempCtx, content, config); + } + // 输出 + var left = config.offsetX, top = config.offsetY + (config.lineHeight - config.fontSize) / 2; + core.fillText(tempCtx, ch, left, top); + config.blocks.push({left: config.offsetX, top: config.offsetY, + width: charwidth, height: config.lineHeight, line: config.line, marginLeft: 0}); + config.offsetX += charwidth; + return true; +} + +ui.prototype._drawTextContent_newLine = function (tempCtx, config) { + // 计算偏移量 + var width = config.offsetX, totalWidth = config.right - config.left; + var marginLeft = 0; + if (config.align == 'center') + marginLeft = (totalWidth - width) / 2; + else if (config.align == 'right') + marginLeft = totalWidth - width; + + config.blocks.forEach(function (b) { + if (b.line == config.line) + b.marginLeft = marginLeft; + }); + + config.offsetX = 0; + config.offsetY += config.lineHeight; + config.line++; +} + +ui.prototype._drawTextContent_changeColor = function (tempCtx, content, config) { + // 检查是不是 [] + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2=content.indexOf(']', index))>=0)) { + // 变色 + var str = content.substring(index+1, index2); + if (str=="") tempCtx.fillStyle = config.color; + else tempCtx.fillStyle = str; + config.index = index2 + 1; + } + else tempCtx.fillStyle = config.color; + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._drawTextContent_drawIcon = function (tempCtx, content, config) { + // 绘制一个 \i 效果 + var index = config.index, index2; + if (content.charAt(config.index+1) == '[' && ((index2=content.indexOf(']', index+1))>=0)) { + var str = content.substring(index+2, index2); + // --- 获得图标 + var iconInfo = core.ui._getDrawableIconInfo(str), image = iconInfo[0], icon = iconInfo[1]; + if (image == null) return this._drawTextContent_next(tempCtx, content, config); + // 检查自动换行 + var width = config.fontSize + 2, left = config.offsetX + 2, top = config.offsetY + (config.lineHeight - width) / 2 - 1; + if (config.maxWidth != null && left + width > config.maxWidth) { + this._drawTextContent_newLine(tempCtx, config); + config.index--; + this._drawTextContent_next(tempCtx, content, config); + } + // 绘制到画布上 + core.drawImage(tempCtx, image, 0, 32*icon, 32, 32, left, top, width, width); + + config.blocks.push({left: left, top: config.offsetY, + width: config.lineHeight, height: config.lineHeight, line: config.line, marginLeft: 0}); + + config.offsetX += width + 6; + config.index = index2 + 1; + return true; + } + return this._drawTextContent_next(tempCtx, content, config); +} + +ui.prototype._getRealContent = function (content) { + return content.replace(/(\r|\\r)(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "占1"); +} + ////// 绘制一个对话框 ////// ui.prototype.drawTextBox = function(content, showAll) { - - if (core.isset(core.status.event) && core.status.event.id=='action') { + if (core.status.event && core.status.event.id == 'action') core.status.event.ui = content; - } - clearInterval(core.status.event.interval); - core.status.event.interval = null; - - // 获得name, image, icon - var info = this.getTitleAndIcon(content); - content = info.content; - var id=info.id, name=info.name, image=info.image, icon=info.icon, iconHeight=info.iconHeight, animate=info.animate; - - // 获得颜色的盒子等信息 - var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; - var titlefont = textAttribute.titlefont || 22; - var textfont = textAttribute.textfont || 16; - var offset = textAttribute.offset || 0; - var background = core.status.textAttribute.background; - var isWindowSkin = false; - if (typeof background == 'string') { - background = core.material.images.images[background]; - if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; - else background = core.initStatus.textAttribute.background; - } - - var titleColor = core.arrayToRGBA(textAttribute.title); - var textColor = core.arrayToRGBA(textAttribute.text); - var borderColor = core.status.globalAttribute.borderColor; - var alpha = isWindowSkin?0.85:background[3]; - - // 获得位置信息 - var position = textAttribute.position, px=null, py=null, ydelta=iconHeight-32; - if (content.indexOf("\b[")==0 || content.indexOf("\\b[")==0) { - var index = content.indexOf("]"); - if (index>=0) { - var str = content.substring(2, index); - if (content.indexOf("\\b[")==0) str = content.substring(3, index); - content = content.substring(index + 1); - - var ss=str.split(","); - - if (ss[0]=='up' || ss[0]=='center' || ss[0]=='down') { - position=ss[0]; - if (core.status.event.id=='action') { - px = core.status.event.data.x; - py = core.status.event.data.y; - } - else { - px = null; py=null; - } - - if (ss.length>=2) { - if (ss[1]=='hero') { - px=core.getHeroLoc('x'); - py=core.getHeroLoc('y'); - ydelta = core.material.icons.hero.height-32; - } - else if (ss[1] == 'null') { - px = py = null; - } - else if (ss.length>=3) { - px=parseInt(ss[1]); - py=parseInt(ss[2]); - } - } - } - } - } + this.clearUI(); content = core.replaceText(content); - core.status.boxAnimateObjs = []; - core.clearLastEvent(); + // Step 1: 获得标题信息和位置信息 + var textAttribute = core.status.textAttribute; + var titleInfo = this._getTitleAndIcon(content); + var posInfo = this._getPosition(titleInfo.content); + if (!posInfo.position) posInfo.position = textAttribute.position; + if (posInfo.position != 'up' && posInfo.position != 'down') posInfo.px = posInfo.py = null; + content = this._drawTextBox_drawImages(posInfo.content); - // drawImage - content = content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + // Step 2: 计算对话框的矩形位置 + var hPos = this._drawTextBox_getHorizontalPosition(content, titleInfo, posInfo); + var vPos = this._drawTextBox_getVerticalPosition(content, titleInfo, posInfo, hPos.validWidth); + + // Step 3: 绘制背景图 + var pInfo = core.clone(posInfo); + pInfo.xoffset = hPos.xoffset; pInfo.yoffset = vPos.yoffset - 4; + var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, pInfo); + var alpha = isWindowSkin ? 0.85 : textAttribute.background[3]; + + // Step 4: 绘制标题、头像、 + var content_top = this._drawTextBox_drawTitleAndIcon(titleInfo, hPos, vPos, alpha); + + // Step 5: 绘制正文 + this.drawTextContent('ui', content, { + left: hPos.content_left, top: content_top, maxWidth: hPos.validWidth, + lineHeight: vPos.lineHeight, time: (showAll || textAttribute.time<=0 || core.status.event.id!='action')?0:textAttribute.time + }); +} + +ui.prototype._drawTextBox_drawImages = function (content) { + return content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { var ss = str.split(","); - if (ss.length!=3 && ss.length!=5) return ""; + if (ss.length!=3 && ss.length!=5 && ss.length!=9) return ""; var img = core.material.images.images[ss[0]]; - if (!core.isset(img)) return ""; + if (!img) return ""; // 绘制 if (ss.length==3) core.drawImage('ui', img, parseFloat(ss[1]), parseFloat(ss[2])); - else + else if (ss.length==5) core.drawImage('ui', img, 0, 0, img.width, img.height, parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4])); + else if (ss.length==9) + core.drawImage('ui', img, parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4]), parseFloat(ss[5]), parseFloat(ss[6]), parseFloat(ss[7]), parseFloat(ss[8])); return ""; }); +} - var globalFont = core.status.globalAttribute.font; - var font = textfont + 'px '+globalFont; - if (textAttribute.bold) font = "bold "+font; - var realContent = content.replace(/(\r|\\r)(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "啊1"); - - var leftSpace = 25, rightSpace = 12; - if (core.isset(px) && core.isset(py)) leftSpace = 20; - if (id=='hero' || core.isset(icon)) leftSpace = 62; // 行走图:15+32+15 - else if (core.isset(image)) leftSpace = 90; // 大头像:10+70+10 - var left = 7, right = 416 - left, width = right - left, validWidth = width - leftSpace - rightSpace; - +ui.prototype._drawTextBox_getHorizontalPosition = function (content, titleInfo, posInfo) { + var realContent = this._getRealContent(content); + var paddingLeft = 25, paddingRight = 12; + if (posInfo.px != null && posInfo.py != null) paddingLeft = 20; + if (titleInfo.icon != null) paddingLeft = 62; // 15 + 32 + 15 + else if (titleInfo.image) paddingLeft = 90; // 10 + 70 + 10 + var left = 7 + 3 * (this.HSIZE - 6), right = this.PIXEL - left, + width = right - left, validWidth = width - paddingLeft - paddingRight; // 对话框效果:改为动态计算 - if (core.isset(px) && core.isset(py)) { - var min_width = 220 - leftSpace, max_width = validWidth; + if (posInfo.px != null && posInfo.py != null) { + var min_width = 220 - paddingLeft, max_width = validWidth; // 无行走图或头像,则可以适当缩小min_width - if (leftSpace == 20) min_width = 160; - core.setFont('ui', font); - validWidth = this.calTextBoxWidth('ui', realContent, min_width, max_width); - width = validWidth + leftSpace + rightSpace; - // left必须在7~416-7-width区间内,以保证left>=7,right<=416-7 - left = core.clamp(32*px+16-width/2-core.bigmap.offsetX, 7, 416-7-width); + if (titleInfo.image == null) min_width = 160; + 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; } + return { left: left, right: right, width: width, validWidth: validWidth, xoffset: 11, content_left: left + paddingLeft }; +} - var content_left = left + leftSpace; - var height = 30 + (textfont+5)*core.splitLines("ui", realContent, validWidth, font).length; - if (core.isset(name)) height += titlefont + 5; - if (id == 'hero') - height = Math.max(height, core.material.icons.hero.height+50); - else if (core.isset(icon)) - height = Math.max(height, iconHeight+50); - else if (core.isset(image)) +ui.prototype._drawTextBox_getVerticalPosition = function (content, titleInfo, posInfo, validWidth) { + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; + var lineHeight = textAttribute.textfont + 5; + var realContent = this._getRealContent(content); + var height = 30 + lineHeight * core.splitLines("ui", realContent, validWidth, this._buildFont()).length; + if (titleInfo.title) height += textAttribute.titlefont + 5; + if (titleInfo.icon != null) + height = Math.max(height, titleInfo.height+50); + else if (titleInfo.image) height = Math.max(height, 90); - var xoffset = 11, yoffset = 16; - - var top; - if (position=='center') { - top = parseInt((416 - height) / 2); - } - else if (position=='up') { - if (px==null || py==null) - top = 5 + offset; - else { - top = 32 * py - height - ydelta - yoffset; - top -= core.bigmap.offsetY; - } - } - else if (position=='down') { - if (px==null || py==null) - top = 416 - height - 5 - offset; - else { - top = 32 * py + 32 + yoffset; - top -= core.bigmap.offsetY; - } - } - var bottom = top + height; - - if (isWindowSkin) { - core.setAlpha('ui', alpha); - this.drawWindowSkin(background,'ui',left,top,width,height,position,px==null?null:px*32-core.bigmap.offsetX,py==null?null:py*32-core.bigmap.offsetY); - core.setAlpha('ui', 1); - } - else { - yoffset -= 4; - core.setAlpha('ui', alpha); - core.setStrokeStyle('ui', borderColor); - core.setFillStyle('ui', core.arrayToRGB(background)); - core.setLineWidth('ui', 2); - // 绘制 - var canvas = core.canvas.ui; - canvas.beginPath(); - canvas.moveTo(left,top); - // 上边缘 - if (position=='down' && core.isset(px) && core.isset(py)) { - canvas.lineTo(32*px+xoffset - core.bigmap.offsetX, top); - canvas.lineTo(32*px+16 - core.bigmap.offsetX, top-yoffset); - canvas.lineTo(32*(px+1)-xoffset - core.bigmap.offsetX, top); - } - canvas.lineTo(right, top); - canvas.lineTo(right, bottom); - // 下边缘 - if (position=='up' && core.isset(px) && core.isset(py)) { - canvas.lineTo(32*(px+1)-xoffset - core.bigmap.offsetX, bottom); - canvas.lineTo(32*px+16 - core.bigmap.offsetX, bottom+yoffset); - canvas.lineTo(32*px+xoffset - core.bigmap.offsetX, bottom); - } - canvas.lineTo(left, bottom); - canvas.closePath(); - canvas.fill(); - canvas.stroke(); - core.setAlpha('ui', 1); - } - // 名称 - core.setTextAlign('ui', 'left'); - - var content_top = top + 15 + textfont; - if (core.isset(id)) { - - content_top += (titlefont + 5); - core.setFillStyle('ui', titleColor); - core.setStrokeStyle('ui', titleColor); - - if (id == 'hero') { - var heroHeight=core.material.icons.hero.height; - core.setAlpha('ui', alpha); - core.strokeRect('ui', left + 15 - 1, top + 40 - 1, 34, heroHeight+2, null, 2); - core.setAlpha('ui', 1); - core.fillText('ui', name, content_left, top + 8 + titlefont, null, 'bold '+titlefont+'px '+globalFont); - core.clearMap('ui', left + 15, top + 40, 32, heroHeight); - core.fillRect('ui', left + 15, top + 40, 32, heroHeight, core.material.groundPattern); - var heroIcon = core.material.icons.hero['down']; - core.drawImage('ui', core.material.images.hero, heroIcon.stop * 32, heroIcon.loc * heroHeight, 32, heroHeight, left+15, top+40, 32, heroHeight); - } - else { - core.fillText('ui', name, content_left, top + 8 + titlefont, null, 'bold '+titlefont+'px '+globalFont); - if (core.isset(icon)) { - core.setAlpha('ui', alpha); - core.strokeRect('ui', left + 15 - 1, top + 40-1, 34, iconHeight + 2, null, 2); - core.setAlpha('ui', 1); - core.status.boxAnimateObjs = []; - core.status.boxAnimateObjs.push({ - 'bgx': left + 15, 'bgy': top + 40, 'bgWidth': 32, 'bgHeight': iconHeight, - 'x': left+15, 'y': top+40, 'height': iconHeight, 'animate': animate, - 'image': image, - 'pos': icon*iconHeight - }); - core.drawBoxAnimate(); + var yoffset = 16; + var top = parseInt((this.PIXEL - height) / 2); + switch (posInfo.position) { + case 'center': top = parseInt((this.PIXEL - height) / 2); break; + case 'up': + if (posInfo.px==null || posInfo.py==null) + top = 5 + textAttribute.offset; + else + top = 32 * posInfo.py - height - (titleInfo.height - 32) - yoffset - core.bigmap.offsetY; + break; + case 'down': + if (posInfo.px==null || posInfo.py==null) + top = this.PIXEL - height - 5 - textAttribute.offset; + else { + top = 32 * posInfo.py + 32 + yoffset - core.bigmap.offsetY; } + } + + return { top: top, height: height, bottom: top + height, yoffset: yoffset, lineHeight: lineHeight }; +} + +ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, alpha) { + core.setTextAlign('ui', 'left'); + var textAttribute = core.status.textAttribute; + var content_top = vPos.top + 15; + if (titleInfo.title != null) { + var titlefont = textAttribute.titlefont; + content_top += titlefont + 5; + core.setFillStyle('ui', core.arrayToRGB(textAttribute.title)); + core.setStrokeStyle('ui', core.arrayToRGB(textAttribute.title)); + + // --- title也要居中或者右对齐? + var title_width = core.calWidth('ui', titleInfo.title, this._buildFont(titlefont, true)); + var title_left = hPos.content_left; + if (textAttribute.align == 'center') + title_left = hPos.left + (hPos.width - title_width) / 2; + else if (textAttribute.align == 'right') + title_left = hPos.right - title_width - 12; + + core.fillText('ui', titleInfo.title, title_left, vPos.top + 8 + titlefont); + + if (titleInfo.icon != null) { + core.setAlpha('ui', alpha); + core.strokeRect('ui', hPos.left + 15 - 1, vPos.top + 40-1, 34, titleInfo.height + 2, null, 2); + core.setAlpha('ui', 1); + core.status.boxAnimateObjs = []; + core.status.boxAnimateObjs.push({ + 'bgx': hPos.left + 15, 'bgy': vPos.top + 40, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': vPos.top + 40, 'height': titleInfo.height, 'animate': titleInfo.animate, + 'image': titleInfo.image, 'pos': titleInfo.icon * titleInfo.height + }); + core.drawBoxAnimate(); } } - if (core.isset(image) && !core.isset(icon)) { - core.drawImage('ui', image, 0, 0, image.width, image.height, left+10, top+10, 70, 70); + if (titleInfo.image != null && titleInfo.icon == null) { // 头像图 + core.drawImage('ui', titleInfo.image, 0, 0, titleInfo.image.width, titleInfo.image.height, + hPos.left+10, vPos.top+10, 70, 70); } + return content_top; +} - core.setFont('ui', font); - - this.__drawText('ui', content, content_left, content_top, validWidth, textColor, textfont + 5, textfont, - (showAll || textAttribute.time<=0 || core.status.event.id!='action')?0:textAttribute.time); - +ui.prototype._createTextCanvas = function (content, lineHeight) { + var realContent = this._getRealContent(content); + var lines = core.splitLines('ui', realContent, null, this._buildFont()); + var width = this.PIXEL, height = lines.length * lineHeight; + var ctx = document.createElement('canvas').getContext('2d'); + ctx.canvas.width = width; + ctx.canvas.height = height; + ctx.clearRect(0, 0, width, height); + return ctx; } ////// 绘制滚动字幕 ////// -ui.prototype.drawScrollText = function (content, time, callback) { - - content = content || ""; +ui.prototype.drawScrollText = function (content, time, lineHeight, callback) { + content = core.replaceText(content || ""); + lineHeight = lineHeight || 1.4; time = time || 5000; + this.clearUI(); + var offset = core.status.textAttribute.offset || 15; + lineHeight *= core.status.textAttribute.textfont; + var ctx = this._createTextCanvas(content, lineHeight); + var obj = { align: core.status.textAttribute.align, lineHeight: lineHeight }; + if (obj.align == 'right') obj.left = this.PIXEL - offset; + else if (obj.align != 'center') obj.left = offset; + this.drawTextContent(ctx, content, obj); + this._drawScrollText_animate(ctx, time, callback); +} - clearInterval(core.status.event.interval); - core.status.event.interval = null; - - // 获得颜色的盒子等信息 - var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; - var textfont = textAttribute.textfont || 16; - var offset = textAttribute.offset || 15; - var textColor = core.arrayToRGBA(textAttribute.text); - - var font = textfont+"px "+core.status.globalAttribute.font; - if (textAttribute.bold) font = "bold "+font; - var realContent = content.replace(/(\r|\\r)(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "啊1"); - var contents = core.splitLines('ui', realContent), lines = contents.length; - - // 计算总高度,按1.4倍行距计算 - var width = 416, height = textfont * 1.4 * lines; - var tempCanvas = core.bigmap.tempCanvas; - tempCanvas.canvas.width = width; - tempCanvas.canvas.height = height; - tempCanvas.clearRect(0, 0, width, height); - tempCanvas.font = font; - - this.__drawText(tempCanvas, content, offset, textfont, null, textColor, 1.4*textfont, textfont, 0); - +ui.prototype._drawScrollText_animate = function (ctx, time, callback) { // 开始绘制到UI上 - core.clearMap('ui'); - var per_pixel = 1, per_time = time * per_pixel / (416+height); - var currH = 416; - core.drawImage('ui', tempCanvas.canvas, 0, currH); + var per_pixel = 1, height = ctx.canvas.height, per_time = time * per_pixel / (this.PIXEL+height); + var currH = this.PIXEL; + core.drawImage('ui', ctx.canvas, 0, currH); var animate = setInterval(function () { core.clearMap('ui'); currH -= per_pixel; @@ -883,237 +944,201 @@ ui.prototype.drawScrollText = function (content, time, callback) { if (core.isset(callback)) callback(); return; } - core.drawImage('ui', tempCanvas.canvas, 0, currH); + core.drawImage('ui', ctx.canvas, 0, currH); }, per_time); core.animateFrame.asyncId[animate] = true; } +////// 文本图片化 ////// +ui.prototype.textImage = function (content, lineHeight) { + content = core.replaceText(content || ""); + lineHeight = lineHeight || 1.4; + lineHeight *= core.status.textAttribute.textfont; + var ctx = this._createTextCanvas(content, lineHeight); + this.drawTextContent(ctx, content, { align: core.status.textAttribute.align, lineHeight: lineHeight }); + return ctx.canvas; +} + ////// 绘制一个选项界面 ////// ui.prototype.drawChoices = function(content, choices) { choices = core.clone(choices || []); - var background = core.status.textAttribute.background; - var isWindowSkin = false; - if (typeof background == 'string') { - background = core.material.images.images[background]; - if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; - else background = core.initStatus.textAttribute.background; - } - if (!isWindowSkin) background = core.arrayToRGBA(background); - var borderColor = core.status.globalAttribute.borderColor; - var textColor = core.arrayToRGBA(core.status.textAttribute.text); - var titleColor = core.arrayToRGBA(core.status.textAttribute.title); - core.status.event.ui = {"text": content, "choices": choices}; + this.clearUI(); - // Step 1: 计算长宽高 - var length = choices.length; + content = core.replaceText(content || ""); + var titleInfo = this._getTitleAndIcon(content); + var hPos = this._drawChoices_getHorizontalPosition(titleInfo, choices); + var vPos = this._drawChoices_getVerticalPosition(titleInfo, choices, hPos); + var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom); + this._drawChoices_drawTitle(titleInfo, hPos, vPos); + this._drawChoices_drawChoices(choices, isWindowSkin, hPos, vPos); +} + +ui.prototype._drawChoices_getHorizontalPosition = function (titleInfo, choices) { // 宽度计算:考虑选项的长度 - var width = 416 - 2*85; - var globalFont = core.status.globalAttribute.font; - core.setFont('ui', "bold 17px "+globalFont); + var width = 246; + core.setFont('ui', this._buildFont(17, true)); for (var i = 0; i < choices.length; i++) { if (typeof choices[i] === 'string') choices[i] = {"text": choices[i]}; choices[i].text = core.replaceText(choices[i].text); choices[i].width = core.calWidth('ui', core.replaceText(choices[i].text)); - if (core.isset(choices[i].icon)) choices[i].width += 28; + if (choices[i].icon != null) choices[i].width += 28; width = Math.max(width, choices[i].width+30); } + var left = (this.PIXEL - width) / 2, right = left + width; + var content_left = left + (titleInfo.icon == null ? 15: 60), validWidth = right - content_left - 10; - var left=parseInt((416 - width) / 2); // 左边界 - // 高度 - var height = 32*(length+2), bottom = 208+height/2; - if (length%2==0) bottom+=16; - var choice_top = bottom-height+56; + return { left: left, right: right, width: width, content_left: content_left, validWidth: validWidth }; +} - var id=null, name=null, image=null, icon=null, iconHeight=32, animate=null; - - var contents = null; - var content_left = left + 15; - var validWidth = width-(content_left-left)-10; - - if (core.isset(content)) { - // 获得name, image, icon - var info = this.getTitleAndIcon(content); - content = core.replaceText(info.content); - var realContent = content.replace(/(\r|\\r)(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "啊1"); - id=info.id; name=info.name; image=info.image; - icon=info.icon; iconHeight=info.iconHeight; animate=info.animate; - if (id=='hero' || core.isset(icon)) - content_left = left+60; - validWidth = width-(content_left-left)-10 - contents = core.splitLines('ui', realContent, validWidth, 'bold 15px '+globalFont); - - // content部分高度 - var cheight=0; - // 如果含有标题,标题高度 - if (name!=null) cheight+=25; - cheight += contents.length*20; - height+=cheight; +ui.prototype._drawChoices_getVerticalPosition = function (titleInfo, choices, hPos) { + var length = choices.length; + var height = 32 * (length + 2), bottom = this.HPIXEL + height / 2; + if (length % 2 == 0) bottom += 16; + var choice_top = bottom - height + 56; + if (titleInfo.content) { + var realContent = this._getRealContent(titleInfo.content); + var lines = core.splitLines('ui', realContent, hPos.validWidth, this._buildFont(15, true)); + if (titleInfo.title) height += 25; + height += lines.length * 20; } - var top = bottom-height; + return {top: bottom - height, height: height, bottom: bottom, choice_top: choice_top }; +} - core.clearMap('ui'); - if (isWindowSkin) { - core.setAlpha('ui', 0.85); - this.drawWindowSkin(background,'ui',left,top,width,height); - } - else { - core.fillRect('ui', left, top, width, height, background); - core.strokeRect('ui', left - 1, top - 1, width + 1, height + 1, borderColor, 2); - } - core.setAlpha('ui', 1); +ui.prototype._drawChoices_drawTitle = function (titleInfo, hPos, vPos) { + if (!titleInfo.content) return; + var content_top = vPos.top + 21; + if (titleInfo.title != null) { + core.setTextAlign('ui', 'center'); - // 如果有内容 - if (core.isset(contents)) { + content_top = vPos.top + 41; + var title_offset = hPos.left+hPos.width/2; + // 动画 - var content_top = top + 35; + if (titleInfo.icon != null) { + title_offset += 12; + core.strokeRect('ui', hPos.left + 15 - 1, vPos.top + 30 - 1, 34, titleInfo.height + 2, '#DDDDDD', 2); + core.status.boxAnimateObjs = []; + core.status.boxAnimateObjs.push({ + 'bgx': hPos.left + 15, 'bgy': vPos.top + 30, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': vPos.top + 30, 'height': titleInfo.height, 'animate': titleInfo.animate, + 'image': titleInfo.image, 'pos': titleInfo.icon * titleInfo.height + }); + core.drawBoxAnimate(); + }; - if (core.isset(id)) { - core.setTextAlign('ui', 'center'); - - content_top = top+55; - var title_offset = left+width/2; - // 动画 - - if (id=='hero' || core.isset(icon)) - title_offset += 12; - - if (id == 'hero') { - var heroHeight = core.material.icons.hero.height; - core.strokeRect('ui', left + 15 - 1, top + 30 - 1, 34, heroHeight+2, '#DDDDDD', 2); - core.fillText('ui', name, title_offset, top + 27, titleColor, 'bold 19px '+globalFont); - core.clearMap('ui', left + 15, top + 30, 32, heroHeight); - core.fillRect('ui', left + 15, top + 30, 32, heroHeight, core.material.groundPattern); - var heroIcon = core.material.icons.hero['down']; - core.drawImage('ui', core.material.images.hero, heroIcon.stop * 32, heroIcon.loc *heroHeight, 32, heroHeight, left+15, top+30, 32, heroHeight); - } - else { - core.fillText('ui', name, title_offset, top + 27, titleColor, 'bold 19px '+globalFont); - if (core.isset(icon)) { - core.strokeRect('ui', left + 15 - 1, top + 30 - 1, 34, iconHeight + 2, '#DDDDDD', 2); - core.status.boxAnimateObjs = []; - core.status.boxAnimateObjs.push({ - 'bgx': left + 15, 'bgy': top + 30, 'bgWidth': 32, 'bgHeight': iconHeight, - 'x': left+15, 'y': top+30, 'height': iconHeight, 'animate': animate, - 'image': image, - 'pos': icon*iconHeight - }); - core.drawBoxAnimate(); - } - } - } - - core.setFont('ui', 'bold 15px '+globalFont); - this.__drawText('ui', content, content_left, content_top, validWidth, textColor, 20, 15, 0); + core.fillText('ui', titleInfo.title, title_offset, vPos.top + 27, + core.arrayToRGBA(core.status.textAttribute.title), this._buildFont(19, true)); } + core.setTextAlign('ui', 'left'); + this.drawTextContent('ui', titleInfo.content, { + left: hPos.content_left, top: content_top, maxWidth: hPos.validWidth, + fontSize: 15, lineHeight: 20, bold: true + }); +} + +ui.prototype._drawChoices_drawChoices = function (choices, isWindowSkin, hPos, vPos) { // 选项 core.setTextAlign('ui', 'center'); + core.setFont('ui', this._buildFont(17, true)); for (var i = 0; i < choices.length; i++) { - var color = choices[i].color || textColor; + var color = choices[i].color || core.status.textAttribute.text; if (color instanceof Array) color = core.arrayToRGBA(color); core.setFillStyle('ui', color); - var offset = 208; + var offset = this.HPIXEL; if (core.isset(choices[i].icon)) { - var iconInfo = this.__getIconInfo(choices[i].icon), image = iconInfo[0], icon = iconInfo[1]; + var iconInfo = this._getDrawableIconInfo(choices[i].icon), image = iconInfo[0], icon = iconInfo[1]; if (image != null) { - core.drawImage('ui', image, 0, 32*icon, 32, 32, - 208 - choices[i].width/2, choice_top + 32*i - 17, 22, 22); + core.drawImage('ui', image, 0, 32 * icon, 32, 32, + this.HPIXEL - choices[i].width/2, vPos.choice_top + 32*i - 17, 22, 22); offset += 14; } } - core.fillText('ui', choices[i].text, offset, choice_top + 32 * i, null, "bold 17px "+globalFont); + core.fillText('ui', choices[i].text, offset, vPos.choice_top + 32 * i, color); } if (choices.length>0) { - if (!core.isset(core.status.event.selection)) core.status.event.selection=0; - while (core.status.event.selection<0) core.status.event.selection+=choices.length; - while (core.status.event.selection>=choices.length) core.status.event.selection-=choices.length; + core.status.event.selection = core.status.event.selection || 0; + while (core.status.event.selection < 0) core.status.event.selection += choices.length; + while (core.status.event.selection >= choices.length) core.status.event.selection -= choices.length; var len = choices[core.status.event.selection].width; if (isWindowSkin) - this.drawWindowSelector(background, 208-len/2-5, choice_top + 32 * core.status.event.selection - 20, len+10, 28); + this.drawWindowSelector(core.status.textAttribute.background, + this.HPIXEL - len/2 - 5, vPos.choice_top + 32 * core.status.event.selection - 20, len + 10, 28); else - core.strokeRect('ui', 208-len/2-5, choice_top + 32 * core.status.event.selection - 20, len+10, 28, "#FFD700", 2); + core.strokeRect('ui', this.HPIXEL - len/2 - 5, vPos.choice_top + 32 * core.status.event.selection - 20, + len+10, 28, "#FFD700", 2); } - return; } ////// 绘制一个确认/取消的警告页面 ////// 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.isset(core.status.event.selection) || core.status.event.selection>1) core.status.event.selection=1; - if (core.status.event.selection<0) core.status.event.selection=0; - - core.clearLastEvent(); - - var background = core.status.textAttribute.background; - var isWindowSkin = false; - if (typeof background == 'string') { - background = core.material.images.images[background]; - if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; - else background = core.initStatus.textAttribute.background; - } - if (!isWindowSkin) background = core.arrayToRGBA(background); - var borderColor = core.status.globalAttribute.borderColor; - var textColor = core.arrayToRGBA(core.status.textAttribute.text); - - var globalFont = core.status.globalAttribute.font; - core.setFont('ui', "bold 19px "+globalFont); - - var contents = text.split('\n'); - var lines = contents.length; - var max_length = 0; - for (var i in contents) { - max_length = Math.max(max_length, core.calWidth('ui', contents[i])); + // 处理自定义事件 + if (core.status.event.id != 'action') { + core.status.event.id = 'confirmBox'; + core.status.event.data = {'yes': yesCallback, 'no': noCallback}; } - var left = Math.min(208 - 40 - parseInt(max_length / 2), 100); - var top = 140 - (lines-1)*30; - var right = 416 - left, bottom = 416 - 140, width = right - left, height = bottom - top; + if (core.status.event.selection != 0) core.status.event.selection = 1; + this.clearUI(); - core.clearMap('ui'); - if (isWindowSkin) { - core.setAlpha('ui', 0.85); - this.drawWindowSkin(background,'ui',left,top,width,height); - } - else { - core.fillRect('ui', left, top, width, height, background); - core.strokeRect('ui', left - 1, top - 1, width + 1, height + 1, borderColor, 2); - } - core.setAlpha('ui', 1); + core.setFont('ui', this._buildFont(19, true)); + var contents = text.split("\n"); + var rect = this._drawConfirmBox_getRect(contents); + var isWindowSkin = this.drawBackground(rect.left, rect.top, rect.right, rect.bottom); core.setTextAlign('ui', 'center'); + core.setFillStyle('ui', core.arrayToRGBA(core.status.textAttribute.text)) for (var i in contents) { - core.fillText('ui', contents[i], 208, top + 50 + i*30, textColor); + core.fillText('ui', contents[i], this.HPIXEL, rect.top + 50 + i*30); } - core.fillText('ui', "确定", 208 - 38, bottom - 35, null, "bold 17px "+globalFont); - core.fillText('ui', "取消", 208 + 38, bottom - 35); - + core.fillText('ui', "确定", this.HPIXEL - 38, rect.bottom - 35, null, this._buildFont(17, true)); + core.fillText('ui', "取消", this.HPIXEL + 38, rect.bottom - 35); var len=core.calWidth('ui', "确定"); - - var strokeLeft = 208 + (76*core.status.event.selection-38) - parseInt(len/2) - 5; + var strokeLeft = this.HPIXEL + (76*core.status.event.selection-38) - parseInt(len/2) - 5; if (isWindowSkin) - this.drawWindowSelector(background, strokeLeft, bottom-35-20, len+10, 28); + this.drawWindowSelector(core.status.textAttribute.background, strokeLeft, rect.bottom-35-20, len+10, 28); else - core.strokeRect('ui', strokeLeft, bottom-35-20, len+10, 28, "#FFD700", 2); + core.strokeRect('ui', strokeLeft, rect.bottom-35-20, len+10, 28, "#FFD700", 2); } +ui.prototype._drawConfirmBox_getRect = function (contents) { + var max_width = contents.reduce(function (pre, curr) { + return Math.max(pre, core.calWidth('ui', curr)); + }, 0); + var left = Math.min(this.HPIXEL - 40 - parseInt(max_width / 2), 100), right = this.PIXEL - left; + var top = this.HPIXEL - 68 - (contents.length-1)*30, bottom = this.HPIXEL + 68; + 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'; - var choices = [ "背景音乐: "+(core.musicStatus.bgmStatus ? "[ON]" : "[OFF]"), "背景音效: "+(core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), @@ -1121,7 +1146,7 @@ ui.prototype.drawSwitchs = function() { "临界显伤: "+(core.flags.displayCritical ? "[ON]" : "[OFF]"), "领域显伤: "+(core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), "新版存档: "+(core.platform.useLocalForage ? "[ON]":"[OFF]"), - "单击瞬移: "+(core.getFlag('clickMove', true) ? "[ON]":"[OFF]"), + "单击瞬移: "+(!core.hasFlag("__noClickMove__") ? "[ON]":"[OFF]"), "拓展键盘: "+(core.platform.extendKeyboard ? "[ON]":"[OFF]"), "返回主菜单" ]; @@ -1131,7 +1156,6 @@ ui.prototype.drawSwitchs = function() { ////// 绘制系统菜单栏 ////// ui.prototype.drawSettings = function () { core.status.event.id = 'settings'; - this.drawChoices(null, [ "系统设置", "虚拟键盘", "浏览地图", "绘图模式", "同步存档", "游戏信息", "返回标题", "返回游戏" ]); @@ -1139,68 +1163,23 @@ ui.prototype.drawSettings = function () { ////// 绘制快捷商店选择栏 ////// ui.prototype.drawQuickShop = function () { - core.status.event.id = 'selectShop'; - - var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) {return shopList[shopId].visited || !shopList[shopId].mustEnable}); + var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) { + return shopList[shopId].visited || !shopList[shopId].mustEnable + }); var choices = keys.map(function (shopId) { return {"text": shopList[shopId].textInList, "color": shopList[shopId].visited?null:"#999999"}; }); - choices.push("返回游戏"); this.drawChoices(null, choices); } -////// 绘制等待界面 ////// -ui.prototype.drawWaiting = function(text) { - - core.lockControl(); - core.status.event.id = 'waiting'; - - core.clearLastEvent(); - - var background = core.status.textAttribute.background; - var isWindowSkin = false; - if (typeof background == 'string') { - background = core.material.images.images[background]; - if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; - else background = core.initStatus.textAttribute.background; - } - if (!isWindowSkin) background = core.arrayToRGBA(background); - var borderColor = core.status.globalAttribute.borderColor; - var textColor = core.arrayToRGBA(core.status.textAttribute.text); - - var globalFont = core.status.globalAttribute.font; - var text_length = core.calWidth('ui', text, "bold 19px "+globalFont); - - var right = Math.max(text_length+50, 220); - var left = 208-parseInt(right/2), top = 208 - 32 - 16, bottom = 416 - 2 * top; - - core.clearMap('ui'); - if (isWindowSkin) { - core.setAlpha('ui', 0.85); - this.drawWindowSkin(background,'ui',left,top,right,bottom); - } - else { - core.fillRect('ui', left, top, right, bottom, background); - core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, borderColor, 2); - } - core.setAlpha('ui', 1); - - core.setTextAlign('ui', 'center'); - core.fillText('ui', text, 208, top + 56, textColor); - -} - ////// 绘制存档同步界面 ////// ui.prototype.drawSyncSave = function () { - core.status.event.id = 'syncSave'; - this.drawChoices(null, [ "同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "回放当前录像", "下载当前录像", "清空本地存档", "返回主菜单" ]); - } ////// 绘制存档同步选择页面 ////// @@ -1243,46 +1222,37 @@ ui.prototype.drawGameInfo = function () { } ////// 绘制分页 ////// -ui.prototype.drawPagination = function (page, totalPage, top) { +ui.prototype.drawPagination = function (page, totalPage, y) { // if (totalPage 1) - core.fillText('ui', '上一页', 208 - 80, top*32+19); + core.fillText('ui', '上一页', this.HPIXEL - 80, y*32+19); if (page < totalPage) - core.fillText('ui', '下一页', 208 + 80, top*32+19); + core.fillText('ui', '下一页', this.HPIXEL + 80, y*32+19); } ////// 绘制键盘光标 ////// ui.prototype.drawCursor = function () { - - if (!core.isset(core.status.automaticRoute.cursorX)) - core.status.automaticRoute.cursorX=core.getHeroLoc('x'); - if (core.status.automaticRoute.cursorX<0) core.status.automaticRoute.cursorX=0; - if (core.status.automaticRoute.cursorX>12) core.status.automaticRoute.cursorX=12; - if (!core.isset(core.status.automaticRoute.cursorY)) - core.status.automaticRoute.cursorY=core.getHeroLoc('y'); - if (core.status.automaticRoute.cursorY<0) core.status.automaticRoute.cursorY=0; - if (core.status.automaticRoute.cursorY>12) core.status.automaticRoute.cursorY=12; - + var automaticRoute = core.status.automaticRoute; + if (automaticRoute.cursorX == null) + automaticRoute.cursorX = core.getHeroLoc('x'); + if (automaticRoute.cursorY == null) + automaticRoute.cursorY = core.getHeroLoc('y'); + automaticRoute.cursorX = core.clamp(automaticRoute.cursorX, 0, this.LAST); core.status.event.id = 'cursor'; core.lockControl(); - - core.clearMap('ui'); - core.setAlpha('ui', 1); - + core.clearUI(); var width = 4; - core.strokeRect('ui', 32*core.status.automaticRoute.cursorX+width/2, 32*core.status.automaticRoute.cursorY+width/2, + core.strokeRect('ui', 32*automaticRoute.cursorX+width/2, 32*automaticRoute.cursorY+width/2, 32-width, 32-width, '#FFD700', width); } @@ -1291,208 +1261,263 @@ ui.prototype.drawCursor = function () { ui.prototype.drawBook = function (index) { var floorId = core.floorIds[(core.status.event.ui||{}).index] || core.status.floorId; var enemys = core.enemys.getCurrentEnemys(floorId); - - core.clearLastEvent(); + core.clearUI(); core.clearMap('data'); - // 生成groundPattern core.maps.generateGroundPattern(floorId); - - core.setFillStyle('ui', core.material.groundPattern); - core.fillRect('ui', 0, 0, 416, 416); - - core.setAlpha('ui', 0.6); - core.setFillStyle('ui', '#000000'); - core.fillRect('ui', 0, 0, 416, 416); - + this._drawBook_drawBackground(); core.setAlpha('ui', 1); - core.setTextAlign('ui', 'left'); - var globalFont = core.status.globalAttribute.font; - core.setFont('ui', 'bold 15px '+globalFont); if (enemys.length == 0) { - core.fillText('ui', "本层无怪物", 83, 222, '#999999', "bold 50px "+globalFont); - // 退出 core.setTextAlign('ui', 'center'); - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); + core.fillText('ui', "本层无怪物", this.HPIXEL, this.HPIXEL + 14, '#999999', this._buildFont(50, true)); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD', this._buildFont(15, true)); return; } - if (index<0) index=0; - if (index>=enemys.length) index=enemys.length-1; - var perpage = 6; - var page=parseInt(index/perpage)+1; - var totalPage = parseInt((enemys.length - 1) / perpage) + 1; + index = core.clamp(index, 0, enemys.length - 1); core.status.event.data = index; - var start = (page - 1) * perpage, end = Math.min(page * perpage, enemys.length); + var perpage = this.HSIZE, page = parseInt(index / perpage) + 1, totalPage = Math.ceil(enemys.length / perpage); + var start = (page - 1) * perpage; + enemys = enemys.slice(start, page * perpage); - enemys = enemys.slice(start, end); - core.status.boxAnimateObjs = []; - for (var i = 0; i < enemys.length; i++) { - // 边框 - var enemy = enemys[i]; - core.strokeRect('ui', 22, 62 * i + 22, 42, 42, '#DDDDDD', 2); + for (var i = 0; i < enemys.length; i++) + this._drawBook_drawOne(floorId, i, enemys[i], index == start + i); - var cls = 'enemys'; - if (core.isset(core.material.icons.enemy48[enemy.id])) - cls = 'enemy48'; - var height = cls=='enemy48'?48:32; - var animate = cls=='enemy48'?4:2; - - // 怪物 - core.status.boxAnimateObjs.push({ - 'bgx': 22, 'bgy': 62 * i + 22, 'bgWidth': 42, 'bgHeight': 42, - 'x': 27, 'y': 62 * i + 27, 'height': 32, 'animate': animate, - 'image': core.material.images[cls], - 'pos': core.material.icons[cls][enemy.id] * height - }); - - // 数据 - core.setTextAlign('ui', 'center'); - - if (enemy.specialText=='') { - core.fillText('ui', enemy.name, 115, 62 * i + 47, '#DDDDDD', 'bold 17px '+globalFont); - } - else { - core.fillText('ui', enemy.name, 115, 62 * i + 40, '#DDDDDD', 'bold 17px '+globalFont); - core.fillText('ui', enemy.specialText, 115, 62 * i + 62, '#FF6A6A', 'bold 15px '+globalFont); - } - core.setTextAlign('ui', 'left'); - core.fillText('ui', '生命', 165, 62 * i + 32, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.hp||0), 195, 62 * i + 32, '#DDDDDD', 'bold 13px '+globalFont); - core.fillText('ui', '攻击', 255, 62 * i + 32, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.atk||0), 285, 62 * i + 32, '#DDDDDD', 'bold 13px '+globalFont); - core.fillText('ui', '防御', 335, 62 * i + 32, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.def||0), 365, 62 * i + 32, '#DDDDDD', 'bold 13px '+globalFont); - - var expOffset = 165, line_cnt=0; - if (core.flags.enableMoney) { - core.fillText('ui', '金币', 165, 62 * i + 50, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.money||0), 195, 62 * i + 50, '#DDDDDD', 'bold 13px '+globalFont); - expOffset = 255; - line_cnt++; - } - - // 加点 - if (core.flags.enableAddPoint) { - core.setTextAlign('ui', 'left'); - core.fillText('ui', '加点', expOffset, 62 * i + 50, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.point||0), expOffset + 30, 62 * i + 50, '#DDDDDD', 'bold 13px '+globalFont); - expOffset = 255; - line_cnt++; - } - - if (core.flags.enableExperience && line_cnt<2) { - core.setTextAlign('ui', 'left'); - core.fillText('ui', '经验', expOffset, 62 * i + 50, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.experience||0), expOffset + 30, 62 * i + 50, '#DDDDDD', 'bold 13px '+globalFont); - line_cnt++; - } - - var damageOffset = 281; - if (line_cnt==1) damageOffset=326; - if (line_cnt==2) damageOffset=361; - - core.setTextAlign('ui', 'center'); - - var damage = enemy.damage; - var color = '#FFFF00'; - if (damage == null) { - damage = '无法战斗'; - color = '#FF0000'; - } - else { - if (damage >= core.status.hero.hp) color = '#FF0000'; - if (damage<=0) color = '#00FF00'; - - damage = core.formatBigNumber(damage); - if (core.enemys.hasSpecial(enemy, 19)) - damage += "+"; - if (core.enemys.hasSpecial(enemy, 21)) - damage += "-"; - if (core.enemys.hasSpecial(enemy, 11)) - damage += "^"; - } - if (enemy.notBomb) - damage += "[b]"; - core.fillText('ui', damage, damageOffset, 62 * i + 50, color, 'bold 13px '+globalFont); - - core.setTextAlign('ui', 'left'); - - core.fillText('ui', '临界', 165, 62 * i + 68, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.critical||0), 195, 62 * i + 68, '#DDDDDD', 'bold 13px '+globalFont); - core.fillText('ui', '减伤', 255, 62 * i + 68, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.criticalDamage||0), 285, 62 * i + 68, '#DDDDDD', 'bold 13px '+globalFont); - core.fillText('ui', '1防', 335, 62 * i + 68, '#DDDDDD', '13px '+globalFont); - core.fillText('ui', core.formatBigNumber(enemy.defDamage||0), 365, 62 * i + 68, '#DDDDDD', 'bold 13px '+globalFont); - - if (index == start+i) { - core.strokeRect('ui', 10, 62 * i + 13, 416-10*2, 62, '#FFD700'); - } - - } core.drawBoxAnimate(); - this.drawPagination(page, totalPage, 12); + this.drawPagination(page, totalPage); core.setTextAlign('ui', 'center'); - // 退出 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13,'#DDDDDD', this._buildFont(15, true)); +} + +ui.prototype._drawBook_drawBackground = function () { + core.setAlpha('ui', 1); + core.setFillStyle('ui', core.material.groundPattern); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL); + + core.setAlpha('ui', 0.6); + core.setFillStyle('ui', '#000000'); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL); +} + +ui.prototype._drawBook_drawOne = function (floorId, index, enemy, selected) { + // --- 区域规划:每个区域总高度为62,宽度为 PIXEL + var top = 62 * index + 12; // 最上面margin是12px + // 横向规划: + // 22 + 42 = 64 是头像框 + this._drawBook_drawBox(index, enemy, top); + // 剩余 PIXEL - 64 的宽度,按照 10 : 9 : 8 : 8 的比例划分 + var left = 64, total_width = this.PIXEL - left; + var name_width = total_width * 10 / 35; + this._drawBook_drawName(index, enemy, top, left, name_width); + this._drawBook_drawContent(index, enemy, top, left + name_width); + if (selected) + core.strokeRect('ui', 10, top + 1, this.PIXEL - 10 * 2, 62, '#FFD700'); +} + +ui.prototype._drawBook_drawBox = function (index, enemy, top) { + // 横向:22+42;纵向:10 + 42 + 10(正好居中);内部图像 32x32 + var border_top = top + 10, border_left = 22; + var img_top = border_top + 5, img_left = border_left + 5; + core.strokeRect('ui', 22, border_top, 42, 42, '#DDDDDD', 2); + var blockInfo = core.getBlockInfo(enemy.id); + core.status.boxAnimateObjs.push({ + 'bgx': border_left, 'bgy': border_top, 'bgWidth': 42, 'bgHeight': 42, + 'x': img_left, 'y': img_top, 'height': 32, 'animate': blockInfo.animate, + 'image': blockInfo.image, 'pos': blockInfo.posY * blockInfo.height + }); +} + +ui.prototype._drawBook_drawName = function (index, enemy, top, left, width) { + // 绘制第零列(名称和特殊属性) + // 如果需要添加自己的比如怪物的称号等,也可以在这里绘制 + core.setTextAlign('ui', 'center'); + if (enemy.specialText=='') { + core.fillText('ui', enemy.name, left + width / 2, + top + 35, '#DDDDDD', this._buildFont(17, true)); + } + else { + core.fillText('ui', enemy.name, left + width / 2, + top + 28, '#DDDDDD', this._buildFont(17, true)); + core.fillText('ui', enemy.specialText, left + width / 2, + top + 50, '#FF6A6A', this._buildFont(15, true)); + } +} + +ui.prototype._drawBook_drawContent = function (index, enemy, top, left) { + var width = this.PIXEL - left; // 9 : 8 : 8 划分三列 + this._drawBook_drawRow1(index, enemy, top, left, width, top + 20); + this._drawBook_drawRow2(index, enemy, top, left, width, top + 38); + this._drawBook_drawRow3(index, enemy, top, left, width, top + 56); +} + +ui.prototype._drawBook_drawRow1 = function (index, enemy, top, left, width, position) { + // 绘制第一行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + core.fillText('ui', '生命', col1, position, '#DDDDDD', f13); + core.fillText('ui', core.formatBigNumber(enemy.hp||0), col1 + 30, position, null, b13); + core.fillText('ui', '攻击', col2, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.atk||0), col2 + 30, position, null, b13); + core.fillText('ui', '防御', col3, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.def||0), col3 + 30, position, null, b13); +} + +ui.prototype._drawBook_drawRow2 = function (index, enemy, top, left, width, position) { + // 绘制第二行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + // 获得第二行绘制的内容 + var second_line = []; + if (core.flags.enableMoney) second_line.push(["金币", core.formatBigNumber(enemy.money || 0)]); + if (core.flags.enableAddPoint) second_line.push(["加点", core.formatBigNumber(enemy.point || 0)]); + if (core.flags.enableExperience) second_line.push(["经验", core.formatBigNumber(enemy.experience || 0)]); + + var damage_offset = col1 + (this.PIXEL - col1) / 2 - 12; + // 第一列 + if (second_line.length > 0) { + var one = second_line.shift(); + core.fillText('ui', one[0], col1, position, '#DDDDDD', f13); + core.fillText('ui', one[1], col1 + 30, position, null, b13); + damage_offset = col2 + (this.PIXEL - col2) / 2 - 12; + } + // 第二列 + if (second_line.length > 0) { + var one = second_line.shift(); + core.fillText('ui', one[0], col2, position, '#DDDDDD', f13); + core.fillText('ui', one[1], col2 + 30, position, null, b13); + damage_offset = col3 + (this.PIXEL - col3) / 2 - 12; + } + // 忽略第三列,直接绘制伤害 + this._drawBook_drawDamage(index, enemy, damage_offset, position); +} + +ui.prototype._drawBook_drawRow3 = function (index, enemy, top, left, width, position) { + // 绘制第三行 + core.setTextAlign('ui', 'left'); + var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); + var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; + core.fillText('ui', '临界', col1, position, '#DDDDDD', f13); + core.fillText('ui', core.formatBigNumber(enemy.critical||0), col1 + 30, position, null, b13); + core.fillText('ui', '减伤', col2, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.criticalDamage||0), col2 + 30, position, null, b13); + core.fillText('ui', '1防', col3, position, null, f13); + core.fillText('ui', core.formatBigNumber(enemy.defDamage||0), col3 + 30, position, null, b13); +} + +ui.prototype._drawBook_drawDamage = function (index, enemy, offset, position) { + core.setTextAlign('ui', 'center'); + var damage = enemy.damage, color = '#FFFF00'; + if (damage == null) { + damage = '无法战斗'; + color = '#FF0000'; + } + else { + if (damage >= core.status.hero.hp) color = '#FF0000'; + if (damage <= 0) color = '#00FF00'; + damage = core.formatBigNumber(damage); + if (core.enemys.hasSpecial(enemy, 19)) damage += "+"; + if (core.enemys.hasSpecial(enemy, 21)) damage += "-"; + if (core.enemys.hasSpecial(enemy, 11)) damage += "^"; + } + if (enemy.notBomb) damage += "[b]"; + core.fillText('ui', damage, offset, position, color, this._buildFont(13, true)); } ////// 绘制怪物属性的详细信息 ////// ui.prototype.drawBookDetail = function (index) { + var info = this._drawBookDetail_getInfo(index), enemy = info[0]; + if (!enemy) return; + var content = info[1].join("\n"); + core.status.event.id = 'book-detail'; + clearInterval(core.interval.tipAnimate); + core.clearMap('data'); + + var left = 10, width = this.PIXEL - 2 * left, right = left + width; + var content_left = left + 25, validWidth = right - content_left - 13; + var contents = core.splitLines("data", content, validWidth, this._buildFont(16, false)); + var height = Math.max(24 * contents.length + 55, 80), top = (this.PIXEL - height) / 2, bottom = top + height; + + core.setAlpha('data', 0.9); + core.fillRect('data', left, top, width, height, '#000000'); + core.setAlpha('data', 1); + core.strokeRect('data', left - 1, top - 1, width + 1, height + 1, + core.status.globalAttribute.borderColor, 2); + + this._drawBookDetail_drawContent(enemy, contents, {top: top, content_left: content_left, bottom: bottom}); +} + +ui.prototype._drawBookDetail_getInfo = function (index) { var floorId = core.floorIds[(core.status.event.ui||{}).index] || core.status.floorId; var enemys = core.enemys.getCurrentEnemys(floorId); - - if (enemys.length==0) return; - if (index<0) index=0; - if (index>=enemys.length) index=enemys.length-1; - + if (enemys.length==0) return []; + index = core.clamp(index, 0, enemys.length - 1); var enemy = enemys[index], enemyId = enemy.id; - var hints=core.enemys.getSpecialHint(enemyId); + var texts=core.enemys.getSpecialHint(enemyId); + if (texts.length == 0) texts.push("该怪物无特殊属性。"); + texts.push(""); + this._drawBookDetail_getTexts(enemy, floorId, texts); + return [enemy, texts]; +} - if (hints.length==0) - hints.push("该怪物无特殊属性。"); +ui.prototype._drawBookDetail_getTexts = function (enemy, floorId, texts) { + // --- 模仿临界计算器 + this._drawBookDetail_mofang(enemy, texts); + // --- 吸血怪最低生命值 + this._drawBookDetail_vampire(enemy, texts); + // --- 仇恨伤害 + this._drawBookDetail_hatred(enemy, texts); + // --- 战斗回合数,临界表 + this._drawBookDetail_turnAndCriticals(enemy, floorId, texts); +} +ui.prototype._drawBookDetail_mofang = function (enemy, texts) { // 模仿临界计算器 if (core.enemys.hasSpecial(enemy.special, 10)) { var hp = enemy.hp; var delta = core.status.hero.atk - core.status.hero.def; if (delta0) { - hints.push(""); - hints.push("模仿临界计算器:(当前攻防差"+core.formatBigNumber(delta)+")"); - var arr = []; - (function () { - var last=0, start=0; - for (var i=1;i=0) { var x1 = text.substring(0, index+1); - core.fillText('data', x1, content_left, content_top, '#FF6A6A', 'bold 16px '+globalFont); + core.fillText('data', x1, pos.content_left, content_top, '#FF6A6A', this._buildFont(16, true)); var len=core.calWidth('data', x1); - core.fillText('data', text.substring(index+1), content_left+len, content_top, '#FFFFFF', '16px '+globalFont); + core.fillText('data', text.substring(index+1), pos.content_left+len, content_top, '#FFFFFF', this._buildFont(16, false)); } else { - core.fillText('data', contents[i], content_left, content_top, '#FFFFFF', '16px '+globalFont); + core.fillText('data', contents[i], pos.content_left, content_top, '#FFFFFF', this._buildFont(16, false)); } content_top+=24; } - - core.fillText('data', '<点击任意位置继续>', 270, top+height-13, '#CCCCCC', '13px '+globalFont); } ////// 绘制楼层传送器 ////// ui.prototype.drawFly = function(page) { - - if (page<0) page=0; - if (page>=core.status.hero.flyRange.length) page=core.status.hero.flyRange.length-1; core.status.event.data = page; - - var floorId = core.status.hero.flyRange[page]; + var floorId = core.floorIds[page]; var title = core.status.maps[floorId].title; - core.clearMap('ui'); core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000'); core.setAlpha('ui', 1); core.setTextAlign('ui', 'center'); - var globalFont = core.status.globalAttribute.font - core.fillText('ui', '楼层跳跃', 208, 60, '#FFFFFF', "bold 28px "+globalFont); - core.fillText('ui', '返回游戏', 208, 403, '#FFFFFF', "bold 15px "+globalFont) - core.fillText('ui', title, 356, 247, '#FFFFFF', "bold 19px "+globalFont); - if (page0) { - core.fillText('ui', '▼', 356, 247 + 64, '#FFFFFF', "17px "+globalFont); - core.fillText('ui', '▼', 356, 247 + 96, '#FFFFFF', "17px "+globalFont); - core.fillText('ui', '▼', 356, 247 + 96 + 7, '#FFFFFF', "17px "+globalFont); + if (core.actions._getNextFlyFloor(-1) != page) { + core.fillText('ui', '▼', this.PIXEL - 60, middle + 64, null, this._buildFont(17, false)); + core.fillText('ui', '▼', this.PIXEL - 60, middle + 96); + core.fillText('ui', '▼', this.PIXEL - 60, middle + 96 + 7); } - core.strokeRect('ui', 20, 100, 273, 273, '#FFFFFF', 2); - this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 20, 100, 273); + var size = this.PIXEL - 143; + core.strokeRect('ui', 20, 100, size, size, '#FFFFFF', 2); + core.drawThumbnail(floorId, null, null, {ctx: 'ui', x: 20, y: 100, size: size}); +} + +////// 绘制中心对称飞行器 +ui.prototype.drawCenterFly = function () { + core.lockControl(); + core.status.event.id = 'centerFly'; + var fillstyle = 'rgba(255,0,0,0.5)'; + if (core.canUseItem('centerFly')) fillstyle = 'rgba(0,255,0,0.5)'; + var toX = core.bigmap.width - 1 - core.getHeroLoc('x'), toY = core.bigmap.height - 1 - core.getHeroLoc('y'); + core.drawThumbnail(null, null, {heroLoc: core.status.hero.loc, heroIcon: core.getFlag('heroIcon', "hero.png")}, + {ctx: 'ui', centerX: toX, centerY: toY}); + var offsetX = core.clamp(toX - core.__HALF_SIZE__, 0, core.bigmap.width - core.__SIZE__), + offsetY = core.clamp(toY - core.__HALF_SIZE__, 0, core.bigmap.height - core.__SIZE__); + core.fillRect('ui', (toX - offsetX) * 32, (toY - offsetY) * 32, 32, 32, fillstyle); + core.status.event.data = {"x": toX, "y": toY, "posX": toX - offsetX, "posY": toY - offsetY}; + core.drawTip("请确认当前中心对称飞行器的位置"); + return; +} + +////// 绘制全局商店 +ui.prototype.drawShop = function (shopId) { + var shop = core.status.shops[shopId]; + var actions = [], fromList = (core.status.event.data||{}).fromList, selection = core.status.event.selection; + if (core.status.event.data && core.status.event.data.actions) actions=core.status.event.data.actions; + + core.ui.closePanel(); + core.lockControl(); + core.status.event.id = 'shop'; + core.status.event.data = {'id': shopId, 'shop': shop, 'actions': actions, 'fromList': fromList}; + core.status.event.selection = selection; + + var times = shop.times, need=core.calValue(shop.need, null, null, times); + var content = "\t["+shop.name+","+shop.icon+"]" + core.replaceText(shop.text, need, times); + var use = shop.use=='experience'?'经验':'金币'; + var choices = []; + for (var i=0;i=core.floorIds.length) index=core.floorIds.length-1; - var floorId = core.floorIds[index], mw = core.floors[floorId].width, mh = core.floors[floorId].height; - if (!core.isset(x)) x = parseInt(mw/2); - if (!core.isset(y)) y = parseInt(mh/2); - if (x<6) x=6; - if (x>mw-7) x=mw-7; - if (y<6) y=6; - if (y>mh-7) y=mh-7; - - core.status.event.data = {"index": index, "x": x, "y": y, "damage": damage, "paint": paint, "all": all}; - + this.clearUI(); + if (index == null) return this._drawMaps_drawHint(); clearTimeout(core.interval.tipAnimate); - core.clearLastEvent(); core.status.checkBlock.cache = {}; - this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 0, 0, 416, x, y); - + var data = this._drawMaps_buildData(index, x, y); + core.drawThumbnail(data.floorId, null, {damage: data.damage}, + {ctx: 'ui', centerX: data.x, centerY: data.y, all: data.all}); // 绘图 - if (core.status.event.data.paint) { - var offsetX = core.clamp(x-6, 0, mw-13), offsetY = core.clamp(y-6, 0, mh-13); - var value = core.paint[floorId]; - if (core.isset(value)) value = lzw_decode(value).split(","); - core.utils.decodeCanvas(value, 32*mw, 32*mh); - core.drawImage('ui', core.bigmap.tempCanvas.canvas, offsetX*32, offsetY*32, 416, 416, 0, 0, 416, 416); + if (data.paint) { + 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.drawImage('ui', core.bigmap.tempCanvas.canvas, offsetX * 32, offsetY * 32, + this.PIXEL, this.PIXEL, 0, 0, this.PIXEL, this.PIXEL); } - core.clearMap('data'); core.setTextAlign('data', 'left'); core.setFont('data', '16px Arial'); - - var text = core.status.maps[floorId].title; - if (!all && (mw>13 || mh>13)) text+=" ["+(x-6)+","+(y-6)+"]"; + var text = core.status.maps[data.floorId].title; + if (!data.all && (data.mw>this.SIZE || data.mh>this.SIZE)) + text+=" ["+(data.x-this.HSIZE)+","+(data.y-this.HSIZE)+"]"; var textX = 16, textY = 18, width = textX + core.calWidth('data', text) + 16, height = 42; core.fillRect('data', 5, 5, width, height, 'rgba(0,0,0,0.4)'); core.fillText('data', text, textX + 5, textY + 15, 'rgba(255,255,255,0.6)'); } +ui.prototype._drawMaps_drawHint = function () { + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, 'rgba(0,0,0,0.4)'); + core.setTextAlign('ui', 'center'); + var stroke = function (left, top, width, height, fillStyle, lineWidth) { + core.strokeRect('ui', left*32+2, top*32+2, width*32-4, height*32-4, fillStyle, lineWidth); + } + var per = this.HSIZE - 4; + stroke(per, 0, 9, per, '#FFD700', 4); // up + stroke(0, per, per, 9); // left + stroke(per, this.SIZE - per, 9, per); // down + stroke(this.SIZE - per, per, per, 9); // right + stroke(per, per, 9, 3); // prev + stroke(per, this.SIZE - per - 3, 9, 3); // next + stroke(0, 0, per-1, per-1); // left top + stroke(this.SIZE-(per - 1), 0, per-1, per-1); // right top + stroke(0, this.SIZE-(per-1), per-1, per-1); // left bottom + + core.setTextBaseline('ui', 'middle'); + core.fillText('ui', "上移地图 [W]", this.HPIXEL, per * 16, '#FFD700', '20px Arial'); + core.fillText('ui', "下移地图 [S]", this.HPIXEL, this.PIXEL - per * 16); + core.fillText('ui', 'V', (per-1)*16, (per-1)*16); + core.fillText('ui', 'Z', this.PIXEL - (per-1)*16, (per-1)*16); + core.fillText('ui', 'M', (per-1)*16, this.PIXEL - (per-1)*16); + + var top = this.HPIXEL - 66, left = per * 16, right = this.PIXEL - left; + var lt = ["左", "移", "地", "图", "[A]"], rt = ["右", "移", "地", "图", "[D]"]; + for (var i = 0; i < 5; ++i) { + core.fillText("ui", lt[i], left, top + 32 * i); + core.fillText("ui", rt[i], right, top + 32 * i); + } + core.fillText('ui', "前张地图 [▲ / PGUP]", this.HPIXEL, 32 * per + 48); + core.fillText('ui', "后张地图 [▼ / PGDN]", this.HPIXEL, this.PIXEL - (32 * per + 48)); + + core.fillText('ui', "退出 [ESC / ENTER]", this.HPIXEL, this.HPIXEL); + core.fillText('ui', "[X] 可查看怪物手册", this.HPIXEL + 77, this.HPIXEL + 32, null, '13px Arial'); + + core.setTextBaseline('ui', 'alphabetic'); +} + +ui.prototype._drawMaps_buildData = function (index, x, y) { + var damage = (core.status.event.data||{}).damage; + var paint = (core.status.event.data||{}).paint; + var all = (core.status.event.data||{}).all; + if (index.damage != null) damage=index.damage; + if (index.paint != null) paint=index.paint; + if (index.all != null) all=index.all; + if (index.index != null) { x=index.x; y=index.y; index=index.index; } + index = core.clamp(index, 0, core.floorIds.length-1); + if (damage == null) damage = true; // 浏览地图默认开显伤好了 + + var floorId = core.floorIds[index], mw = core.floors[floorId].width, mh = core.floors[floorId].height; + if (x == null) x = parseInt(mw / 2); + if (y == null) y = parseInt(mh / 2); + x = core.clamp(x, this.HSIZE, mw - this.HSIZE - 1); + y = core.clamp(y, this.HSIZE, mh - this.HSIZE - 1); + + core.status.event.data = {index: index, x: x, y: y, floorId: floorId, mw: mw, mh: mh, + damage: damage, paint: paint, all: all }; + return core.status.event.data; +} + ////// 绘制道具栏 ////// ui.prototype.drawToolbox = function(index) { - // 设定eventdata - if (!core.isset(core.status.event.data) || !core.isset(core.status.event.data.toolsPage) || !core.isset(core.status.event.data.constantsPage)) - core.status.event.data = {"toolsPage":1, "constantsPage":1, "selectId":null} + var info = this._drawToolbox_getInfo(index); + this._drawToolbox_drawBackground(); + // 绘制线 + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = this.PIXEL - 306; + this._drawToolbox_drawLine(line1, "消耗道具"); + var line2 = this.PIXEL - 146; + this._drawToolbox_drawLine(line2, "永久道具"); + + this._drawToolbox_drawDescription(info, line1); + + this._drawToolbox_drawContent(info, line1, info.tools, info.toolsPage, true); + this.drawPagination(info.toolsPage, info.toolsTotalPage, this.LAST - 5); + this._drawToolbox_drawContent(info, line2, info.constants, info.constantsPage); + this.drawPagination(info.constantsPage, info.constantsTotalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[装备栏]', this.PIXEL - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13); +} + +ui.prototype._drawToolbox_getInfo = function (index) { + // 设定eventdata + if (!core.status.event.data || core.status.event.data.toolsPage == null) + core.status.event.data = {"toolsPage":1, "constantsPage":1, "selectId":null} // 获取物品列表 var tools = Object.keys(core.status.hero.items.tools).sort(); var constants = Object.keys(core.status.hero.items.constants).sort(); - // 处理页数 var toolsPage = core.status.event.data.toolsPage; var constantsPage = core.status.event.data.constantsPage; - var toolsTotalPage = Math.ceil(tools.length/12); - var constantsTotalPage = Math.ceil(constants.length/12); - + var toolsTotalPage = Math.ceil(tools.length/this.LAST); + var constantsTotalPage = Math.ceil(constants.length/this.LAST); // 处理index - if (!core.isset(index)) { - if (tools.length>0) index=0; - else if (constants.length>0) index=12; - else index=0; - } + if (index == null) + index = tools.length == 0 && constants.length > 0 ? this.LAST : 0; core.status.event.selection=index; - // 确认选择对象 - var select; - var selectId; - if (index<12) { - select = index + (toolsPage-1)*12; + var select, selectId; + if (index=tools.length) select=Math.max(0, tools.length-1); selectId = tools[select]; } else { - select = index%12 + (constantsPage-1)*12; + select = index%this.LAST + (constantsPage-1)*this.LAST; if (select>=constants.length) select=Math.max(0, constants.length-1); selectId = constants[select]; } if (!core.hasItem(selectId)) selectId=null; core.status.event.data.selectId=selectId; + return { + index: index, tools: tools, constants: constants, toolsPage: toolsPage, constantsPage: constantsPage, + toolsTotalPage: toolsTotalPage, constantsTotalPage: constantsTotalPage, selectId: selectId + }; +} +ui.prototype._drawToolbox_drawBackground = function () { // 绘制 core.clearMap('ui'); core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); - core.setAlpha('ui', 1); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000'); +} + +ui.prototype._drawToolbox_drawLine = function (yoffset, text) { core.setFillStyle('ui', '#DDDDDD'); - core.setStrokeStyle('ui', '#DDDDDD'); - core.canvas.ui.lineWidth = 2; - core.canvas.ui.strokeWidth = 2; - - var ydelta = 20; - - // 画线 core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 130-ydelta); - core.canvas.ui.lineTo(416, 130-ydelta); + core.canvas.ui.moveTo(0, yoffset); + core.canvas.ui.lineTo(this.PIXEL, yoffset); core.canvas.ui.stroke(); core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,129-ydelta); - core.canvas.ui.lineTo(416,105-ydelta); - core.canvas.ui.lineTo(416-72,105-ydelta); - core.canvas.ui.lineTo(416-102,129-ydelta); + core.canvas.ui.moveTo(this.PIXEL, yoffset-1); + core.canvas.ui.lineTo(this.PIXEL, yoffset-25); + core.canvas.ui.lineTo(this.PIXEL-72, yoffset-25); + core.canvas.ui.lineTo(this.PIXEL-102, yoffset-1); core.canvas.ui.fill(); + core.fillText('ui', text, this.PIXEL - 5, yoffset-6, '#333333', this._buildFont(16, true)); +} - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 290-ydelta); - core.canvas.ui.lineTo(416, 290-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,289-ydelta); - core.canvas.ui.lineTo(416,265-ydelta); - core.canvas.ui.lineTo(416-72,265-ydelta); - core.canvas.ui.lineTo(416-102,289-ydelta); - core.canvas.ui.fill(); - - // 文字 - core.setTextAlign('ui', 'right'); - var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "消耗道具", 411, 124-ydelta, '#333333', "bold 16px "+globalFont); - core.fillText('ui', "永久道具", 411, 284-ydelta); - +ui.prototype._drawToolbox_drawDescription = function (info, max_height) { core.setTextAlign('ui', 'left'); - // 描述 - if (core.isset(selectId)) { - var item=core.material.items[selectId]; - core.fillText('ui', item.name, 10, 32, '#FFD700', "bold 20px "+globalFont) - - var text = item.text||"该道具暂无描述。"; - try { - // 检查能否eval - text = core.replaceText(text); - } catch (e) {} - - var lines = core.splitLines('ui', text, 406, '17px '+globalFont); - - core.fillText('ui', lines[0], 10, 62, '#FFFFFF', '17px '+globalFont); - - if (lines.length==1) { - core.fillText('ui', '<继续点击该道具即可进行使用>', 10, 89, '#CCCCCC', '14px '+globalFont); - } - else { - var leftText = text.substring(lines[0].length); - core.fillText('ui', leftText, 10, 89, '#FFFFFF', '17px '+globalFont); - } + if (!info.selectId) return; + var item=core.material.items[info.selectId]; + core.fillText('ui', item.name, 10, 32, '#FFD700', this._buildFont(20, true)) + var text = item.text||"该道具暂无描述。"; + try { + // 检查能否eval + text = core.replaceText(text); + } catch (e) {} + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); + // --- 开始逐行绘制 + var curr = 62, line_height = 25; + core.setFillStyle('ui', '#FFFFFF'); + for (var i=0;i=max_height) break; } + if (curr < max_height) { + core.fillText('ui', '<继续点击该道具即可进行使用>', 10, curr, '#CCCCCC', this._buildFont(14, false)); + } +} +ui.prototype._drawToolbox_drawContent = function (info, line, items, page, drawCount) { core.setTextAlign('ui', 'right'); - var images = core.material.images.items; - - // 消耗道具 - for (var i=0;i<12;i++) { - var tool=tools[12*(toolsPage-1)+i]; - if (!core.isset(tool)) break; - var yoffset = 144 + Math.floor(i/6)*54 + 5 - ydelta; - var icon=core.material.icons.items[tool]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, yoffset, 32, 32) - // 个数 - core.fillText('ui', core.itemCount(tool), 16*(4*(i%6)+1)+40, yoffset+33, '#FFFFFF', "bold 14px "+globalFont); - if (selectId == tool) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, yoffset-4, 40, 40, '#FFD700'); + for (var i = 0; i < this.LAST; i++) { + var item = items[this.LAST * (page - 1) + i]; + if (!item) continue; + var yoffset = line + 54 * Math.floor(i / this.HSIZE) + 19; + var icon = core.material.icons.items[item], image = core.material.images.items; + core.drawImage('ui', image, 0, 32 * icon, 32, 32, 64 * (i % this.HSIZE) + 21, yoffset, 32, 32); + if (drawCount) + core.fillText('ui', core.itemCount(item), 64 * (i % this.HSIZE) + 56, yoffset + 33, '#FFFFFF', this._buildFont(14, true)); + if (info.selectId == item) + core.strokeRect('ui', 64 * (i % this.HSIZE) + 17, yoffset - 4, 40, 40, '#FFD700'); } - - // 永久道具 - for (var i=0;i<12;i++) { - var constant=constants[12*(constantsPage-1)+i]; - if (!core.isset(constant)) break; - var yoffset = 304+Math.floor(i/6)*54+5-ydelta; - var icon=core.material.icons.items[constant]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, yoffset, 32, 32) - if (selectId == constant) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, yoffset-4, 40, 40, '#FFD700'); - } - - // 分页 - this.drawPagination(toolsPage, toolsTotalPage, 7); - this.drawPagination(constantsPage, constantsTotalPage, 12); - - core.setTextAlign('ui', 'center'); - - // 装备栏 - // if (core.flags.equipment) - core.fillText('ui', '[装备栏]', 370, 25,'#DDDDDD', 'bold 15px '+globalFont); - // core.fillText('ui', '删除道具', 370, 32,'#DDDDDD', 'bold 15px '+globalFont); - // 退出 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); } ////// 绘制装备界面 ////// ui.prototype.drawEquipbox = function(index) { - // 设定eventdata - if (!core.isset(core.status.event.data) || !core.isset(core.status.event.data.page)) - core.status.event.data = {"page":1, "selectId":null}; + var info = this._drawEquipbox_getInfo(index); + this._drawToolbox_drawBackground(); + core.setAlpha('ui', 1); + core.setStrokeStyle('ui', '#DDDDDD'); + core.canvas.ui.lineWidth = 2; + core.canvas.ui.strokeWidth = 2; + core.setTextAlign('ui', 'right'); + var line1 = this.PIXEL - 306; + this._drawToolbox_drawLine(line1, "当前装备"); + var line2 = this.PIXEL - 146; + this._drawToolbox_drawLine(line2, "拥有装备"); + + this._drawEquipbox_description(info, line1); + + this._drawEquipbox_drawEquiped(info, line1); + this._drawToolbox_drawContent(info, line2, info.ownEquipment, info.page, true); + this.drawPagination(info.page, info.totalPage); + + core.setTextAlign('ui', 'center'); + core.fillText('ui', '[道具栏]', this.PIXEL - 46, 25, '#DDDDDD', this._buildFont(15, true)); + core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13); +} + +ui.prototype._drawEquipbox_getInfo = function (index) { + if (!core.status.event.data || core.status.event.data.page == null) + core.status.event.data = {"page":1, "selectId":null}; var allEquips = core.status.globalAttribute.equipName; var equipLength = allEquips.length; - - if (!core.isset(core.status.hero.equipment)) core.status.hero.equipment = []; - + if (!core.status.hero.equipment) core.status.hero.equipment = []; var equipEquipment = core.status.hero.equipment; var ownEquipment = Object.keys(core.status.hero.items.equips).sort(); - var page = core.status.event.data.page; - var totalPage = Math.ceil(ownEquipment.length/12); - + var totalPage = Math.ceil(ownEquipment.length / this.LAST); // 处理index - if (!core.isset(index)) { - if (equipLength>0 && core.isset(equipEquipment[0])) index=0; - else if (ownEquipment.length>0) index=12; - else index=0; + if (index == null) { + if (equipLength > 0 && equipEquipment[0]) index = 0; + else if (ownEquipment.length > 0) index = this.LAST; + else index = 0; } - if (index>=12 && ownEquipment.length==0) index = 0; + if (index >= this.LAST && ownEquipment.length == 0) index = 0; var selectId=null; - if (index<12) { + if (index < this.LAST) { if (index >= equipLength) index=Math.max(0, equipLength - 1); - selectId = equipEquipment[index]||null; + selectId = equipEquipment[index] || null; } else { - if (page == totalPage) index = Math.min(index, (ownEquipment.length+11)%12+12); - selectId = ownEquipment[index-12 + (page-1)*12]; + if (page == totalPage) index = Math.min(index, (ownEquipment.length+this.LAST-1)%this.LAST+this.LAST); + selectId = ownEquipment[index - this.LAST + (page - 1) * this.LAST]; if (!core.hasItem(selectId)) selectId=null; } core.status.event.selection=index; core.status.event.data.selectId=selectId; + return { index: index, selectId: selectId, page: page, totalPage: totalPage, allEquips: allEquips, + equipLength: equipLength, equipEquipment: equipEquipment, ownEquipment: ownEquipment}; +} - core.clearMap('ui', 0, 0, 416, 416); - core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); - core.setAlpha('ui', 1); - core.setFillStyle('ui', '#DDDDDD'); - core.setStrokeStyle('ui', '#DDDDDD'); - core.canvas.ui.lineWidth = 2; - core.canvas.ui.strokeWidth = 2; - - var ydelta = 20; - - // 画线 - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 130-ydelta); - core.canvas.ui.lineTo(416, 130-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,129-ydelta); - core.canvas.ui.lineTo(416,105-ydelta); - core.canvas.ui.lineTo(416-72,105-ydelta); - core.canvas.ui.lineTo(416-102,129-ydelta); - core.canvas.ui.fill(); - - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(0, 290-ydelta); - core.canvas.ui.lineTo(416, 290-ydelta); - core.canvas.ui.stroke(); - core.canvas.ui.beginPath(); - core.canvas.ui.moveTo(416,289-ydelta); - core.canvas.ui.lineTo(416,265-ydelta); - core.canvas.ui.lineTo(416-72,265-ydelta); - core.canvas.ui.lineTo(416-102,289-ydelta); - core.canvas.ui.fill(); - - // 文字 - core.setTextAlign('ui', 'right'); - var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "当前装备", 411, 124-ydelta, '#333333', "bold 16px "+globalFont); - core.fillText('ui', "拥有装备", 411, 284-ydelta); - +ui.prototype._drawEquipbox_description = function (info, max_height) { core.setTextAlign('ui', 'left'); + if (!info.selectId) return; + var equip=core.material.items[info.selectId]; + // --- 标题 + if (!equip.equip) equip.equip = {"type": 0}; + var equipType = equip.equip.type, equipString; + if (typeof equipType === 'string') { + equipString = equipType || "未知部位"; + equipType = core.items.getEquipTypeByName(equipType); + } + else equipString = info.allEquips[equipType] || "未知部位"; + core.fillText('ui', equip.name + "(" + equipString + ")", 10, 32, '#FFD700', this._buildFont(20, true)) + // --- 描述 + var text = equip.text || "该装备暂无描述。"; + try { + text = core.replaceText(text); + } catch (e) {} + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(17, false)); + var curr = 62, line_height = 25; + core.setFillStyle('ui', '#FFFFFF'); + for (var i = 0; i < lines.length; ++i) { + core.fillText('ui', lines[i], 10, curr); + curr += line_height; + if (curr >= max_height) break; + } + // --- 变化值 + if (curr >= max_height) return; + this._drawEquipbox_drawStatusChanged(info, curr, equip, equipType); +} - // 描述 - if (core.isset(selectId)) { - var equip=core.material.items[selectId]; - if (!core.isset(equip.equip)) equip.equip = {"type": 0}; - var equipType = equip.equip.type; - var equipString; - if (typeof equipType === 'string') { - equipString = equipType||"未知部位"; - equipType = core.items.getEquipTypeByName(equipType); - } - else equipString = allEquips[equipType]||"未知部位"; - - core.fillText('ui', equip.name + "(" + equipString + ")", 10, 32, '#FFD700', "bold 20px "+globalFont) - - var text = equip.text||"该装备暂无描述。"; - try { - text = core.replaceText(text); - } catch (e) {} - var lines = core.splitLines('ui', text, 406, '17px '+globalFont); - - core.fillText('ui', lines[0], 10, 62, '#FFFFFF', '17px '+globalFont); - - // 比较属性 - if (lines.length==1) { - var compare, differentMode = null; - if (index<12) compare = core.compareEquipment(null, selectId); - else { - if (equipType<0) { - differentMode = '<当前没有该装备的空位,请先卸下装备>'; - } - else { - var last = core.material.items[equipEquipment[equipType]]||{}; - // 检查是不是数值模式和比例模式之间的切换 - if (core.isset(last.equip) && (last.equip.percentage||false) != (equip.equip.percentage||false)) { - differentMode = '<数值和比例模式之间的切换不显示属性变化>'; - } - else { - compare = core.compareEquipment(selectId, equipEquipment[equipType]); - } - } - } - if (differentMode != null) { - core.fillText('ui', differentMode, 10, 89, '#CCCCCC', '14px '+globalFont); - } - else { - var drawOffset = 10; - [['攻击','atk'], ['防御','def'], ['魔防','mdef']].forEach(function (t) { - var title = t[0], name = t[1]; - if (!core.isset(compare[name]) || compare[name]==0) return; - var color = '#00FF00'; - if (compare[name]<0) color = '#FF0000'; - var nowValue = core.getStatus(name), newValue = nowValue + compare[name]; - if (equip.equip.percentage) { - var nowBuff = core.getFlag('__'+name+"_buff__", 1), newBuff = nowBuff+compare[name]/100; - nowValue = Math.floor(nowBuff*core.getStatus(name)); - newValue = Math.floor(newBuff*core.getStatus(name)); - } - var content = title + ' ' + nowValue + '->'; - core.fillText('ui', content, drawOffset, 89, '#CCCCCC', 'bold 14px '+globalFont); - drawOffset += core.calWidth('ui', content); - core.fillText('ui', newValue, drawOffset, 89, color); - drawOffset += core.calWidth('ui', newValue) + 15; - }) - } - } +ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { + var compare, differentMode = null; + if (info.index < this.LAST) compare = core.compareEquipment(null, info.selectId); + else { + if (equipType<0) differentMode = '<当前没有该装备的空位,请先卸下装备>'; else { - var leftText = text.substring(lines[0].length); - core.fillText('ui', leftText, 10, 89, '#FFFFFF', '17px '+globalFont); + var last = core.material.items[info.equipEquipment[equipType]]||{}; + if (last.equip && (last.equip.percentage || false) != (equip.equip.percentage || false)) + differentMode = '<数值和比例模式之间的切换不显示属性变化>'; + else + compare = core.compareEquipment(info.selectId, info.equipEquipment[equipType]); } } + if (differentMode != null) { + core.fillText('ui', differentMode, 10, y, '#CCCCCC', this._buildFont(14, false)); + return; + } + var drawOffset = 10; + // --- 变化值... + core.setFont('ui', this._buildFont(14, true)); + for (var name in compare) { + var img = core.statusBar.icons[name]; + if (img) { // 绘制图标 + core.drawImage('ui', img, 0, 0, 32, 32, drawOffset, y - 13, 16, 16); + drawOffset += 20; + } + else { // 绘制文字 + core.fillText('ui', name + " ", drawOffset, y, '#CCCCCC'); + drawOffset += core.calWidth('ui', name + " "); + } + var nowValue = core.getStatus(name) * core.getBuff(name), newValue = (nowValue + compare[name]) * core.getBuff(name); + if (equip.equip.percentage) { + var nowBuff = core.getBuff(name), newBuff = nowBuff + compare[name] / 100; + nowValue = Math.floor(nowBuff * core.getStatus(name)); + newValue = Math.floor(newBuff * core.getStatus(name)); + } + nowValue = core.formatBigNumber(nowValue); + newValue = core.formatBigNumber(newValue); + core.fillText('ui', nowValue + "->", drawOffset, y, '#CCCCCC'); + drawOffset += core.calWidth('ui', nowValue + "->"); + core.fillText('ui', newValue, drawOffset, y, compare[name]>0?'#00FF00':'#FF0000'); + drawOffset += core.calWidth('ui', newValue) + 8; + } +} +ui.prototype._drawEquipbox_drawEquiped = function (info, line) { core.setTextAlign('ui', 'right'); - var images = core.material.images.items; - + var per_line = this.HSIZE - 3, width = Math.floor(this.PIXEL / (per_line + 0.25)); // 当前装备 - for (var i = 0 ; i < equipLength ; i++) { - var equipId = equipEquipment[i] || null; - if (core.isset(equipId)) { + for (var i = 0; i < info.equipLength ; i++) { + var equipId = info.equipEquipment[i] || null; + var offset_text = width * (i % per_line) + 56; + var offset_image = width * (i % per_line) + width * 2 / 3; + var y = line + 54 * Math.floor(i / per_line) + 19; + if (equipId) { var icon = core.material.icons.items[equipId]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(8*(i%3)+5)+5, 144+Math.floor(i/3)*54+5-ydelta, 32, 32); + core.drawImage('ui', core.material.images.items, 0, 32 * icon, 32, 32, offset_image, y, 32, 32); } - core.fillText('ui', allEquips[i]||"未知", 16*(8*(i%3)+1)+40, 144+Math.floor(i/3)*54+32-ydelta, '#FFFFFF', "bold 16px "+globalFont); - core.strokeRect('ui', 16*(8*(i%3)+5)+1, 144+Math.floor(i/3)*54+1-ydelta, 40, 40, index==i?'#FFD700':"#FFFFFF"); + core.fillText('ui', info.allEquips[i] || "未知", offset_text, y + 27, '#FFFFFF', this._buildFont(16, true)) + core.strokeRect('ui', offset_image - 4, y - 4, 40, 40, info.index==i?'#FFD700':"#FFFFFF"); } - - // 现有装备 - for (var i=0;i<12;i++) { - var ownEquip=ownEquipment[12*(page-1)+i]; - if (!core.isset(ownEquip)) continue; - var icon=core.material.icons.items[ownEquip]; - core.drawImage('ui', images, 0, icon*32, 32, 32, 16*(4*(i%6)+1)+5, 304+Math.floor(i/6)*54+5-ydelta, 32, 32) - // 个数 - if (core.itemCount(ownEquip)>1) - core.fillText('ui', core.itemCount(ownEquip), 16*(4*(i%6)+1)+40, 304+Math.floor(i/6)*54+38-ydelta, '#FFFFFF', "bold 14px "+globalFont); - if (index>=12 && selectId == ownEquip) - core.strokeRect('ui', 16*(4*(i%6)+1)+1, 304+Math.floor(i/6)*54+1-ydelta, 40, 40, '#FFD700'); - } - - this.drawPagination(page, totalPage, 12); - // 道具栏 - core.setTextAlign('ui', 'center'); - core.fillText('ui', '[道具栏]', 370, 25,'#DDDDDD', 'bold 15px '+globalFont); - // 退出按钮 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); } ////// 绘制存档/读档界面 ////// ui.prototype.drawSLPanel = function(index, refresh) { - if (!core.isset(index)) index=1; - if (index<0) index=0; + core.control._loadFavoriteSaves(); + if (index == null) index = 1; + if (index < 0) index = 0; var page = parseInt(index/10), offset=index%10; var max_page = main.savePages || 30; + if(core.status.event.data && core.status.event.data.mode=='fav') + max_page = Math.ceil((core.saves.favorite||[]).length/5); if (page>=max_page) page=max_page - 1; if (offset>5) offset=5; - index=10*page+offset; + if (core.status.event.data && core.status.event.data.mode=='fav' && page == max_page - 1) { + offset = Math.min(offset, (core.saves.favorite||[]).length - 5 * page); + } var last_page = -1; - if (core.isset(core.status.event.data)) { - last_page = parseInt(core.status.event.data/10); + var mode = 'all'; + if (core.status.event.data) { + last_page = core.status.event.data.page; + mode = core.status.event.data.mode; } - - core.status.event.data=index; - if (!core.isset(core.status.event.ui)) + core.status.event.data={ 'page':page, 'offset':offset, 'mode':mode }; + core.status.event.ui = core.status.event.ui || []; + if (refresh || page != last_page) { core.status.event.ui = []; + this._drawSLPanel_loadSave(page, function () { + core.ui._drawSLPanel_draw(page, max_page); + }); + } + else this._drawSLPanel_draw(page, max_page); +} - var u=416/6, size=118; +ui.prototype._drawSLPanel_draw = function (page, max_page) { + // --- 绘制背景 + this._drawSLPanel_drawBackground(); + // --- 绘制文字 + core.ui.drawPagination(page+1, max_page); + core.setTextAlign('ui', 'center'); + var bottom = this.PIXEL-13; + core.fillText('ui', '返回游戏', this.PIXEL-48, bottom, '#DDDDDD', this._buildFont(15, true)); + if (core.status.event.selection) + core.setFillStyle('ui', '#FF6A6A'); + if (core.status.event.id=='save') + core.fillText('ui', '删除模式', 48, bottom); + else{ + if(core.status.event.data.mode=='all'){ + core.fillText('ui', '[E]显示收藏', 52, bottom); + }else{ + core.fillText('ui', '[E]显示全部', 52, bottom); + } + } + // --- 绘制记录 + this._drawSLPanel_drawRecords(); +} + +ui.prototype._drawSLPanel_drawBackground = function() { + core.clearMap('ui'); + core.setAlpha('ui', 0.85); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');//可改成背景图图 + core.setAlpha('ui', 1); +} + +ui.prototype._drawSLPanel_loadSave = function(page, callback) { + var ids = [0]; + for (var i = 1; i <= 5; ++i) { + var id = 5 * page + i; + if(core.status.event.data.mode=='fav') + id = core.saves.favorite[id - 1]; // 因为favorite第一个不是自动存档 所以要偏移1 + ids.push(id); + } + core.getSaves(ids, function (data) { + for (var i = 0; i < ids.length; ++i) + core.status.event.ui[i] = data[i]; + core.saves.autosave.data = data[0]; + callback(); + }); +} + +// 在以x为中心轴 y为顶坐标 的位置绘制一条宽为size的记录 cho表示是否被选中 选中会加粗 highlight表示高亮标题 ✐ +ui.prototype._drawSLPanel_drawRecord = function(title, data, x, y, size, cho, highLight){ var strokeColor = '#FFD700'; if (core.status.event.selection) strokeColor = '#FF6A6A'; - var globalFont = (core.status.globalAttribute||core.initStatus.globalAttribute).font; + if (!data || !data.floorId) highLight = false; - var drawBg = function() { - core.clearMap('ui'); - core.setAlpha('ui', 0.85); - core.fillRect('ui', 0, 0, 416, 416, '#000000'); - core.setAlpha('ui', 1); - - core.ui.drawPagination(page+1, max_page, 12); - core.setTextAlign('ui', 'center'); - // 退出 - core.fillText('ui', '返回游戏', 370, 403,'#DDDDDD', 'bold 15px '+globalFont); - - if (core.status.event.selection) - core.setFillStyle('ui', '#FF6A6A'); - if (core.status.event.id=='save') - core.fillText('ui', '删除模式', 48, 403); - else - core.fillText('ui', '输入编号', 48, 403); - } - - var draw = function (data, i) { - var name=core.status.event.id=='save'?"存档":core.status.event.id=='load'?"读档":core.status.event.id=='replayLoad'?"回放":""; - core.status.event.ui[i] = data; - var id=5*page+i; - if (i<3) { - core.fillText('ui', i==0?"自动存档":name+id, (2*i+1)*u, 30, '#FFFFFF', "bold 17px "+globalFont); - core.strokeRect('ui', (2*i+1)*u-size/2, 45, size, size, i==offset?strokeColor:'#FFFFFF', i==offset?6:2); - if (core.isset(data) && core.isset(data.floorId)) { - core.ui.drawThumbnail(data.floorId, 'ui', core.maps.loadMap(data.maps, data.floorId).blocks, (2*i+1)*u-size/2, 45, size, data.hero.loc.x, data.hero.loc.y, data.hero.loc, data.hero.flags.heroIcon||"hero.png"); - var v = core.formatBigNumber(data.hero.hp,true)+"/"+core.formatBigNumber(data.hero.atk,true)+"/"+core.formatBigNumber(data.hero.def,true); - var v2 = "/"+core.formatBigNumber(data.hero.mdef,true); - if (v.length+v2.length<=21) v+=v2; - core.fillText('ui', v, (2*i+1)*u, 60+size, '#FFD700', '10px '+globalFont); - core.fillText('ui', core.formatDate(new Date(data.time)), (2*i+1)*u, 73+size, data.hero.flags.consoleOpened?'#FF6A6A':'#FFFFFF'); - } - else { - core.fillRect('ui', (2*i+1)*u-size/2, 45, size, size, '#333333', 2); - core.fillText('ui', '空', (2*i+1)*u, 112, '#FFFFFF', 'bold 30px '+globalFont); - } - } - else { - core.fillText('ui', name+id, (2*i-5)*u, 218, '#FFFFFF', "bold 17px "+globalFont); - core.strokeRect('ui', (2*i-5)*u-size/2, 233, size, size, i==offset?strokeColor:'#FFFFFF', i==offset?6:2); - if (core.isset(data) && core.isset(data.floorId)) { - core.ui.drawThumbnail(data.floorId, 'ui', core.maps.loadMap(data.maps, data.floorId).blocks, (2*i-5)*u-size/2, 233, size, data.hero.loc.x, data.hero.loc.y, data.hero.loc, data.hero.flags.heroIcon||"hero.png"); - var v = core.formatBigNumber(data.hero.hp,true)+"/"+core.formatBigNumber(data.hero.atk,true)+"/"+core.formatBigNumber(data.hero.def,true); - var v2 = "/"+core.formatBigNumber(data.hero.mdef,true); - if (v.length+v2.length<=21) v+=v2; - core.fillText('ui', v, (2*i-5)*u, 248+size, '#FFD700', '10px '+globalFont); - core.fillText('ui', core.formatDate(new Date(data.time)), (2*i-5)*u, 261+size, data.hero.flags.consoleOpened?'#FF6A6A':'#FFFFFF', '10px '+globalFont); - } - else { - core.fillRect('ui', (2*i-5)*u-size/2, 233, size, size, '#333333', 2); - core.fillText('ui', '空', (2*i-5)*u, 297, '#FFFFFF', 'bold 30px '+globalFont); - } - } - }; - - function loadSave(i, callback) { - if (i==6) { - callback(); - return; - } - - if (i==0) { - if (core.saves.autosave.data!=null) { - core.status.event.ui[i] = core.saves.autosave.data; - loadSave(1, callback); - } - else { - core.getLocalForage("autoSave", null, function(data) { - core.saves.autosave.data = data; - core.status.event.ui[i]=data; - loadSave(i+1, callback); - }, function(err) {main.log(err);}); - } - } - else { - core.getLocalForage("save"+(5*page+i), null, function(data) { - core.status.event.ui[i]=data; - loadSave(i+1, callback); - }, function(err) {main.log(err);}); - } - } - - function drawAll() { - drawBg(); - for (var i=0;i<6;i++) - draw(core.status.event.ui[i], i); - } - if (refresh || page!=last_page) { - core.status.event.ui = []; - loadSave(0, drawAll); - } - else drawAll(); -} - -////// 绘制一个缩略图 ////// -ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, centerX, centerY, heroLoc, heroIcon) { - - var mw = core.floors[floorId].width; - var mh = core.floors[floorId].height; - // 绘制到tempCanvas上面 - var tempCanvas = core.bigmap.tempCanvas; - var tempWidth = mw*32, tempHeight = mh*32; - tempCanvas.canvas.width = tempWidth; - tempCanvas.canvas.height = tempHeight; - tempCanvas.clearRect(0, 0, tempWidth, tempHeight); - - // -------- 1. 绘制地板 - var groundId = (core.status.maps||core.floors)[floorId].defaultGround || "ground"; - var blockIcon = core.material.icons.terrains[groundId]; - for (var i = 0; i < mw; i++) { - for (var j = 0; j < mh; j++) { - tempCanvas.drawImage(core.material.images.terrains, 0, blockIcon * 32, 32, 32, i * 32, j * 32, 32, 32); - } - } - - var images = []; - if (core.isset((core.status.maps||core.floors)[floorId].images)) { - images = (core.status.maps||core.floors)[floorId].images; - if (typeof images == 'string') { - images = [[0, 0, images]]; - } - } - - // -------- 2. 绘制背景贴图 - images.forEach(function (t) { - if (typeof t == 'string') t = [0,0,t]; - var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2], frame = core.clamp(parseInt(t[4]), 1, 8); - if (core.isset(dx) && core.isset(dy) && - !core.hasFlag("floorimg_"+floorId+"@"+dx+"@"+dy) && - core.isset(core.material.images.images[p])) { - var image = core.material.images.images[p]; - var width = image.width / frame, height = image.height; - if (!t[3]) - tempCanvas.drawImage(image, 0, 0, width, height, dx, dy, width, height); - else if (t[3]==2) - tempCanvas.drawImage(image, 0, height-32, width, 32, dx, dy + height - 32, width, 32); - } - }) - - // -------- 3. 绘制背景图块 - core.maps.drawBgFgMap(floorId, tempCanvas, "bg"); - - // -------- 4. 绘制事件层 - var mapArray = core.maps.getMapArray(blocks,mw,mh); - for (var b in blocks) { - var block = blocks[b]; - if (core.isset(block.event) && !block.disable) { - if (block.event.cls == 'autotile') { - core.drawAutotile(tempCanvas, mapArray, block, 32, 0, 0); - } - else if (block.event.cls == 'tileset') { - var offset = core.icons.getTilesetOffset(block.event.id); - if (offset!=null) { - tempCanvas.drawImage(core.material.images.tilesets[offset.image], 32*offset.x, 32*offset.y, 32, 32, 32*block.x, 32*block.y, 32, 32); - } - } - else if (block.id==17) { - if (core.isset(core.material.images.airwall)) { - tempCanvas.drawImage(core.material.images.airwall, 32*block.x, 32*block.y); - } - } - else if (block.event.id!='none') { - var blockIcon = core.material.icons[block.event.cls][block.event.id]; - var blockImage = core.material.images[block.event.cls]; - var height = block.event.height || 32; - tempCanvas.drawImage(blockImage, 0, blockIcon * height, 32, height, 32*block.x, 32*block.y + 32 - height, 32, height); - } - } - } - // -------- 5. 绘制勇士 - if (core.isset(heroLoc)) { - if (!core.isset(core.material.images.images[heroIcon])) - heroIcon = "hero.png"; - var icon = core.material.icons.hero[heroLoc.direction]; - var height = core.material.images.images[heroIcon].height/4; - tempCanvas.drawImage(core.material.images.images[heroIcon], icon.stop * 32, icon.loc * height, 32, height, 32*heroLoc.x, 32*heroLoc.y+32-height, 32, height); - } - - // -------- 6. 绘制前景贴图 - images.forEach(function (t) { - var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2], frame = core.clamp(parseInt(t[4]), 1, 8); - if (core.isset(dx) && core.isset(dy) && - !core.hasFlag("floorimg_"+floorId+"@"+dx+"@"+dy) && - core.isset(core.material.images.images[p])) { - var image = core.material.images.images[p]; - var width = image.width / frame, height = image.height; - if (t[3]==1) - tempCanvas.drawImage(image, 0, 0, width, height, dx, dy, width, height); - else if (t[3]==2) - tempCanvas.drawImage(image, 0, 0, width, height-32, dx, dy, width, height-32); - } - }) - - // -------- 7. 绘制前景图块 - core.maps.drawBgFgMap(floorId, tempCanvas, "fg"); - - // -------- 8. 绘制显伤 - if (core.status.event.id=='viewMaps' && (core.status.event.data||{}).damage) - core.control.updateDamage(floorId, tempCanvas); - - var ctx = core.getContextByName(canvas); - if (ctx == null) return; - - // draw to canvas - core.clearMap(canvas, x, y, size, size); - if (!core.isset(centerX)) centerX=parseInt(mw/2); - if (!core.isset(centerY)) centerY=parseInt(mh/2); - - // 如果是浏览地图的全模式 - if (core.status.event.id=='viewMaps' && (core.status.event.data||{}).all) { - if (tempWidth<=tempHeight) { - var realHeight = 416, realWidth = realHeight * tempWidth / tempHeight; - var side = (416 - realWidth) / 2; - core.fillRect(canvas, 0, 0, side, realHeight, '#000000'); - core.fillRect(canvas, 416-side, 0, side, realHeight); - ctx.drawImage(tempCanvas.canvas, 0, 0, tempWidth, tempHeight, side, 0, realWidth, realHeight); - } - else { - var realWidth = 416, realHeight = realWidth * tempHeight / tempWidth; - var side = (416 - realHeight) / 2; - core.fillRect(canvas, 0, 0, realWidth, side, '#000000'); - core.fillRect(canvas, 0, 416-side, realWidth, side); - ctx.drawImage(tempCanvas.canvas, 0, 0, tempWidth, tempHeight, 0, side, realWidth, realHeight); - } + core.fillText('ui', title, x, y, highLight?'#FFD700':'#FFFFFF', this._buildFont(17, true)); + core.strokeRect('ui', x-size/2, y+15, size, size, cho?strokeColor:'#FFFFFF', cho?6:2); + if (data && data.floorId) { + core.drawThumbnail(data.floorId, core.maps.loadMap(data.maps, data.floorId).blocks, { + heroLoc: data.hero.loc, heroIcon: data.hero.flags.heroIcon, flags: data.hero.flags + }, { + ctx: 'ui', x: x-size/2, y: y+15, size: size, centerX: data.hero.loc.x, centerY: data.hero.loc.y + }); + var v = core.formatBigNumber(data.hero.hp,true)+"/"+core.formatBigNumber(data.hero.atk,true)+"/"+core.formatBigNumber(data.hero.def,true); + var v2 = "/"+core.formatBigNumber(data.hero.mdef,true); + if (core.calWidth('ui', v + v2, this._buildFont(10, false)) <= size) v += v2; + core.fillText('ui', v, x, y+30+size, '#FFD700'); + core.fillText('ui', core.formatDate(new Date(data.time)), x, y+43+size, data.hero.flags.__consoleOpened__?'#FF6A6A':'#FFFFFF'); } else { - var offsetX = core.clamp(centerX-6, 0, mw-13), offsetY = core.clamp(centerY-6, 0, mh-13); - // offsetX~offsetX+12; offsetY~offsetY+12 - ctx.drawImage(tempCanvas.canvas, offsetX*32, offsetY*32, 416, 416, x, y, size, size); - } + core.fillRect('ui', x-size/2, y+15, size, size, '#333333', 2); + core.fillText('ui', '空', x, parseInt(y+22+size/2), '#FFFFFF', this._buildFont(30,true)); + } } +ui.prototype._drawSLPanel_drawRecords = function (n) { + var page = core.status.event.data.page; + var offset = core.status.event.data.offset; + var u = Math.floor(this.PIXEL/6), size = Math.floor(this.PIXEL/3-20); + var name=core.status.event.id=='save'?"存档":core.status.event.id=='load'?"读档":core.status.event.id=='replayLoad'?"回放":""; + + for (var i = 0; i < (n||6); i++){ + var data = core.status.event.ui[i]; + var id = 5 * page + i; + var highLight = (i>0&&core.saves.favorite.indexOf(id)>=0) || core.status.event.data.mode=='fav'; + var title = (highLight?'★ ':'☆ ') + (core.saves.favoriteName[id] || (name + id)); + if (i != 0 && core.status.event.data.mode=='fav') { + if (!data) break; + var real_id = core.saves.favorite[id - 1]; + title = (core.saves.favoriteName[real_id] || (name + real_id)) + ' ✐'; + } + + var charSize = 32;// 字体占用像素范围 + var topSpan = parseInt((this.PIXEL-charSize-2*(charSize*2 + size))/3);// Margin + var yTop1 = topSpan+parseInt(charSize/2) + 8;//文字的中心 + var yTop2 = yTop1+charSize*2+size+topSpan; + if (i<3) { + this._drawSLPanel_drawRecord(i==0?"自动存档":title, data, (2*i+1)*u, yTop1, size, i==offset, highLight); + } + else { + this._drawSLPanel_drawRecord(title, data, (2*i-5)*u, yTop2, size, i==offset, highLight); + } + } +}; + ui.prototype.drawKeyBoard = function () { core.lockControl(); core.status.event.id = 'keyBoard'; + core.clearUI(); - core.clearLastEvent(); - - var left = 16, top = 48, width = 416 - 2 * left, height = 416 - 2 * top; - - var background = core.status.textAttribute.background; - var isWindowSkin = false; - if (typeof background == 'string') { - background = core.material.images.images[background]; - if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; - else background = core.initStatus.textAttribute.background; - } - if (!isWindowSkin) background = core.arrayToRGBA(background); - var borderColor = core.status.globalAttribute.borderColor; - var titleColor = core.arrayToRGBA(core.status.textAttribute.title); - var textColor = core.arrayToRGBA(core.status.textAttribute.text); - - core.clearMap('ui'); - if (isWindowSkin) { - core.setAlpha('ui', 0.85); - this.drawWindowSkin(background,'ui',left,top,width,height); - } - else { - core.fillRect('ui', left, top, width, height, background); - core.strokeRect('ui', left - 1, top - 1, width + 1, height + 1, borderColor, 2); - } - core.setAlpha('ui', 1); + var width = 384, height = 320; + var left = (this.PIXEL - width) / 2, right = left + width; + var top = (this.PIXEL - height) / 2, bottom = top + height; + var isWindowSkin = this.drawBackground(left, top, right, bottom); core.setTextAlign('ui', 'center'); - var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "虚拟键盘", 208, top+35, titleColor, "bold 22px "+globalFont); - - core.setFont('ui', '17px '+globalFont); - core.setFillStyle('ui', textColor); - var offset = 128-9; + core.setFillStyle('ui', core.arrayToRGBA(core.status.textAttribute.title)); + core.fillText('ui', '虚拟键盘', this.HPIXEL, top + 35, null, this._buildFont(22, true)); + core.setFont('ui', this._buildFont(17, false)); + core.setFillStyle('ui', core.arrayToRGBA(core.status.textAttribute.text)); + var offset = this.HPIXEL - 89; var lines = [ ["F1","F2","F3","F4","F5","F6","F7","F8","F9","10","11"], @@ -2346,17 +2184,17 @@ ui.prototype.drawKeyBoard = function () { lines.forEach(function (line) { for (var i=0;i0) t+=atk+"攻"; - if (def>0) t+=def+"防"; - if (mdef>0) t+=mdef+"魔防"; - if (t!="") ext[id]=t; - } - - total.count[id]++; - total.add.hp+=hp; - total.add.atk+=atk; - total.add.def+=def; - total.add.mdef+=mdef; - if (floorId==core.status.floorId) { - current.count[id]++; - current.add.hp+=hp; - current.add.atk+=atk; - current.add.def+=def; - current.add.mdef+=mdef; - } - } - } - }) - }) - - var getText = function (type, data) { - var text = type+"地图中:\n"; - text += "共有怪物"+data.monster.count+"个"; - if (core.flags.enableMoney) text+=",总金币数"+data.monster.money; - if (core.flags.enableExperience) text+=",总经验数"+data.monster.experience; - if (core.flags.enableAddPoint) text+=",总加点数"+data.monster.point; - text+="。\n"; - - var prev = ""; - ids.forEach(function (key) { - var value = data.count[key]; - if (value==0) return; - var c = cls[key]; - if (c!=prev) { - if (prev != "") text += "。"; - text += "\n"; - } - else - text += ","; - prev = c; - var name = null; - if (key=='yellowDoor') name="黄门"; - else if (key=='blueDoor') name="蓝门"; - else if (key=='redDoor') name="红门"; - else if (key=='greenDoor') name="绿门"; - else if (key=='steelDoor') name="铁门"; - else name=core.material.items[key].name; - text+=name+value+"个"; - if (core.isset(ext[key])) - text+="("+ext[key]+")"; - }) - - if (prev!="") text+="。"; - text+="\n\n"; - text+="共加生命值"+core.formatBigNumber(data.add.hp)+"点,攻击" - +core.formatBigNumber(data.add.atk)+"点,防御" - +core.formatBigNumber(data.add.def)+"点,魔防" - +core.formatBigNumber(data.add.mdef)+"点。"; - return text; - } - - var formatTime = function (time) { - return core.setTwoDigits(parseInt(time/3600000)) - +":"+core.setTwoDigits(parseInt(time/60000)%60) - +":"+core.setTwoDigits(parseInt(time/1000)%60); - } - var statistics = core.status.hero.statistics; core.drawText([ - getText("全塔", total), - getText("当前", current), - "当前总步数:"+core.status.hero.steps+",当前游戏时长:"+formatTime(statistics.currTime) - +",总游戏时长"+formatTime(statistics.totalTime) + this._drawStatistics_generateText(obj, "全塔", obj.total), + this._drawStatistics_generateText(obj, "当前", obj.current), + "当前总步数:"+core.status.hero.steps+",当前游戏时长:"+core.formatTime(statistics.currTime) + +",总游戏时长"+core.formatTime(statistics.totalTime) +"。\n瞬间移动次数:"+statistics.moveDirectly+",共计少走"+statistics.ignoreSteps+"步。" +"\n\n总计通过血瓶恢复生命值为"+core.formatBigNumber(statistics.hp)+"点。\n\n" +"总计打死了"+statistics.battle+"个怪物,得到了"+core.formatBigNumber(statistics.money)+"金币,"+core.formatBigNumber(statistics.experience)+"点经验。\n\n" @@ -2548,10 +2224,148 @@ ui.prototype.drawStatistics = function () { +",领域/夹击/阻击/血网伤害"+core.formatBigNumber(statistics.extraDamage)+"点。", "\t[说明]1. 地图数据统计的效果仅模拟当前立刻获得该道具的效果。\n2. 不会计算“不可被浏览地图”的隐藏层的数据。\n" + "3. 不会计算任何通过事件得到的道具(显示事件、改变图块、或直接增加道具等)。\n"+ - "4. 在自定义道具(例如其他宝石)后,需在ui.js的drawStatistics中注册,不然不会进行统计。\n"+ + "4. 在自定义道具(例如其他宝石)后,需在脚本编辑的drawStatistics中注册,不然不会进行统计。\n"+ "5. 所有统计信息仅供参考,如有错误,概不负责。" ]) +} +ui.prototype._drawStatistics_buildObj = function () { + // 数据统计要统计如下方面: + // 1. 当前全塔剩余下的怪物数量,总金币数,总经验数,总加点数 + // 2. 当前全塔剩余的黄蓝红铁门数量,和对应的钥匙数量 + // 3. 当前全塔剩余的三种宝石数量,血瓶数量,装备数量;总共增加的攻防生命值 + // 4. 当前层的上述信息 + // 5. 当前已走的步数;瞬间移动的步数,瞬间移动的次数(和少走的步数);游戏时长 + // 6. 当前已恢复的生命值;当前总伤害、战斗伤害、阻激夹域血网伤害、中毒伤害。 + var ori = this.uidata.drawStatistics(); + var ids = ori.filter(function (e) { + return e.endsWith("Door") || core.material.items[e]; + }); + var cnt = {}, cls = {}, ext = {}; + ids.forEach(function (e) { + if (e.endsWith("Door")) cls[e] = "doors"; + else cls[e] = core.material.items[e].cls; + cnt[e] = 0; + }) + var order = ["doors", "keys", "items", "tools", "constants", "equips"]; + ids.sort(function (a, b) { + var c1 = order.indexOf(cls[a]), c2 = order.indexOf(cls[b]); + if (c1==c2) return ori.indexOf(a)-ori.indexOf(b); + return c1-c2; + }); + var obj = { + 'monster': { + 'count': 0, 'money': 0, 'experience': 0, 'point': 0, + }, + 'count': cnt, + 'add': { + 'hp': 0, 'atk': 0, 'def': 0, 'mdef': 0 + } + }; + return {ids: ids, cls: cls, ext: ext, total: core.clone(obj), current: core.clone(obj)}; +} + +ui.prototype._drawStatistics_add = function (floorId, obj, x1, x2, value) { + obj.total[x1][x2] += value || 0; + if (floorId == core.status.floorId) + obj.current[x1][x2] += value || 0; +} + +ui.prototype._drawStatistics_floorId = function (floorId, obj) { + var floor = core.status.maps[floorId], blocks = floor.blocks; + // 隐藏层不给看 + if (floor.cannotViewMap && floorId!=core.status.floorId) return; + blocks.forEach(function (block) { + if (block.disable) return; + var event = block.event; + if (event.cls.indexOf("enemy")==0) { + core.ui._drawStatistics_enemy(floorId, event.id, obj); + } + else { + var id = event.id; + if (obj.total.count[id] != null) + core.ui._drawStatistics_items(floorId, floor, id, obj); + } + }) +} + +ui.prototype._drawStatistics_enemy = function (floorId, id, obj) { + var enemy = core.material.enemys[id]; + this._drawStatistics_add(floorId, obj, 'monster', 'money', enemy.money); + this._drawStatistics_add(floorId, obj, 'monster', 'experience', enemy.experience); + this._drawStatistics_add(floorId, obj, 'monster', 'point', enemy.point); + this._drawStatistics_add(floorId, obj, 'monster', 'count', 1); +} + +ui.prototype._drawStatistics_items = function (floorId, floor, id, obj) { + var hp=0, atk=0, def=0, mdef=0; + if (obj.cls[id]=='items' && id!='superPotion') { + var temp = core.clone(core.status.hero); + core.setFlag("__statistics__", true); + var ratio = floor.item_ratio||1; + try { eval(core.items.itemEffect[id]); } + catch (e) {} + hp = core.status.hero.hp - temp.hp; + atk = core.status.hero.atk - temp.atk; + def = core.status.hero.def - temp.def; + mdef = core.status.hero.mdef - temp.mdef; + core.status.hero = temp; + } + else if (obj.cls[id]=='equips') { + var values = core.material.items[id].equip || {}; + atk = values.atk || 0; + def = values.def || 0; + mdef = values.mdef || 0; + } + if (id.indexOf('sword')==0 || id.indexOf('shield')==0 || obj.cls[id]=='equips') { + var t = ""; + if (atk > 0) t += atk + "攻"; + if (def > 0) t += def + "防"; + if (mdef > 0) t += mdef + "魔防"; + if (t != "") obj.ext[id] = t; + } + this._drawStatistics_add(floorId, obj, 'count', id, 1); + this._drawStatistics_add(floorId, obj, 'add', 'hp', hp); + this._drawStatistics_add(floorId, obj, 'add', 'atk', atk); + this._drawStatistics_add(floorId, obj, 'add', 'def', def); + this._drawStatistics_add(floorId, obj, 'add', 'mdef', mdef); +} + +ui.prototype._drawStatistics_generateText = function (obj, type, data) { + var text = type+"地图中:\n"; + text += "共有怪物"+data.monster.count+"个"; + if (core.flags.enableMoney) text+=",总金币数"+data.monster.money; + if (core.flags.enableExperience) text+=",总经验数"+data.monster.experience; + if (core.flags.enableAddPoint) text+=",总加点数"+data.monster.point; + text+="。\n"; + + var prev = ""; + obj.ids.forEach(function (key) { + var value = data.count[key]; + if (value==0) return; + if (obj.cls[key] != prev) { + if (prev != "") text += "。"; + text += "\n"; + } + else text += ","; + prev = obj.cls[key]; + text+=core.ui._drawStatistics_getName(key)+value+"个"; + if (obj.ext[key]) + text+="("+obj.ext[key]+")"; + }) + if (prev!="") text+="。"; + + text+="\n\n"; + text+="共加生命值"+core.formatBigNumber(data.add.hp)+"点,攻击" + +core.formatBigNumber(data.add.atk)+"点,防御" + +core.formatBigNumber(data.add.def)+"点,魔防" + +core.formatBigNumber(data.add.mdef)+"点。"; + return text; +} + +ui.prototype._drawStatistics_getName = function (key) { + return {"yellowDoor": "黄门", "blueDoor": "蓝门", "redDoor": "红门", "greenDoor": "绿门", + "steelDoor": "铁门"}[key] || core.material.items[key].name; } ////// 绘制“关于”界面 ////// @@ -2561,52 +2375,54 @@ ui.prototype.drawAbout = function () { ////// 绘制“画图”界面 ////// ui.prototype.drawPaint = function () { - core.drawText( "\t[进入绘图模式]你可以在此页面上任意进行绘图和标记操作。\nM键可以进入或退出此模式。\n\n"+ "绘图的内容会自动保存,且以页面为生命周期,和存读档无关,重新开始游戏或读档后绘制的内容仍有效,但刷新页面就会消失。\n"+ "你可以将绘制内容保存到文件,也可以从文件读取保存的绘制内容。\n"+ "浏览地图页面可以按楼传按钮或M键来开启/关闭该层的绘图显示。\n\n更多功能请详见文档-元件-绘图模式。", - function () { - core.drawTip("打开绘图模式,现在可以任意在界面上绘图标记"); - - core.lockControl(); - core.status.event.id = 'paint'; - core.status.event.data = {"x": null, "y": null, "erase": false}; - - core.clearLastEvent(); - 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.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0); - - core.setLineWidth('paint', 3); - core.setStrokeStyle('paint', '#FF0000'); - - core.statusBar.image.keyboard.style.opacity = 0; - core.statusBar.image.shop.style.opacity = 0; - - core.statusBar.image.book.src = core.statusBar.icons.paint.src; - core.statusBar.image.fly.src = core.statusBar.icons.erase.src; - core.statusBar.image.toolbox.src = core.statusBar.icons.empty.src; - core.statusBar.image.settings.src = core.statusBar.icons.exit.src; - core.statusBar.image.book.style.opacity = 1; - core.statusBar.image.fly.style.opacity = 1; - } + this._drawPaint_draw ); } +ui.prototype._drawPaint_draw = function () { + core.drawTip("打开绘图模式,现在可以任意在界面上绘图标记"); + + core.lockControl(); + core.status.event.id = 'paint'; + core.status.event.data = {"x": null, "y": null, "erase": false}; + + 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.drawImage('paint', core.bigmap.tempCanvas.canvas, 0, 0); + + core.setLineWidth('paint', 3); + core.setStrokeStyle('paint', '#FF0000'); + + core.statusBar.image.keyboard.style.opacity = 0; + core.statusBar.image.shop.style.opacity = 0; + + core.statusBar.image.book.src = core.statusBar.icons.paint.src; + core.statusBar.image.fly.src = core.statusBar.icons.erase.src; + core.statusBar.image.toolbox.src = core.statusBar.icons.empty.src; + core.statusBar.image.settings.src = core.statusBar.icons.exit.src; + core.statusBar.image.book.style.opacity = 1; + core.statusBar.image.fly.style.opacity = 1; +} + ////// 绘制帮助页面 ////// ui.prototype.drawHelp = function () { - core.clearLastEvent(); + core.clearUI(); if (core.material.images.keyboard) { core.status.event.id = 'help'; core.lockControl(); core.setAlpha('ui', 1); - core.drawImage('ui', core.material.images.keyboard, 0, 0); + core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#FFFFFF'); + core.drawImage('ui', core.material.images.keyboard, 32 * (this.HSIZE - 6), 32 * (this.HSIZE - 6)); } else { core.drawText([ diff --git a/libs/utils.js b/libs/utils.js index f8a9323b..d491197e 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -18,7 +18,7 @@ function utils() { utils.prototype._init = function () { // 定义Object.assign if (typeof Object.assign != "function") { - Object.assign = function(target, varArgs) { // .length of function is 2 + Object.assign = function (target, varArgs) { // .length of function is 2 if (target == null) { // TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object'); } @@ -41,7 +41,7 @@ utils.prototype._init = function () { }; } if (typeof String.prototype.endsWith != "function") { - String.prototype.endsWith = function(search, this_len) { + String.prototype.endsWith = function (search, this_len) { if (this_len === undefined || this_len > this.length) { this_len = this.length; } @@ -61,12 +61,12 @@ utils.prototype.replaceText = function (text, need, times) { ////// 计算表达式的值 ////// utils.prototype.calValue = function (value, prefix, need, times) { - if (!core.isset(value)) return value; + if (!core.isset(value)) return null; if (typeof value === 'string') { - value=value.replace(/status:([\w\d_]+)/g, "core.getStatus('$1')"); - value=value.replace(/item:([\w\d_]+)/g, "core.itemCount('$1')"); - value=value.replace(/flag:([\w\d_]+)/g, "core.getFlag('$1', 0)"); - value=value.replace(/switch:([\w\d_]+)/g, "core.getFlag('"+(prefix||"global")+"@$1', 0)"); + value = value.replace(/status:([\w\d_]+)/g, "core.getStatus('$1')"); + value = value.replace(/item:([\w\d_]+)/g, "core.itemCount('$1')"); + value = value.replace(/flag:([\w\d_]+)/g, "core.getFlag('$1', 0)"); + value = value.replace(/switch:([\w\d_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)"); return eval(value); } if (value instanceof Function) { @@ -75,38 +75,9 @@ utils.prototype.calValue = function (value, prefix, need, times) { return value; } -////// 字符串自动换行的分割 ////// -utils.prototype.splitLines = function(canvas, text, maxLength, font) { - if (core.isset(font)) core.setFont(canvas, font); - - var contents = []; - var last = 0; - for (var i=0;imaxLength) { - 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) || !core.isset(b)) return; +utils.prototype.unshift = function (a, b) { + if (!(a instanceof Array) || b == null) return; if (b instanceof Array) { core.clone(b).reverse().forEach(function (e) { a.unshift(e); @@ -117,8 +88,8 @@ utils.prototype.unshift = function (a,b) { } ////// 向某个数组后插入另一个数组或元素 ////// -utils.prototype.push = function (a,b) { - if (!(a instanceof Array) || !core.isset(b)) return; +utils.prototype.push = function (a, b) { + if (!(a instanceof Array) || b == null) return; if (b instanceof Array) { core.clone(b).forEach(function (e) { a.push(e); @@ -128,22 +99,44 @@ utils.prototype.push = function (a,b) { return a; } -////// 设置本地存储 ////// -utils.prototype.setLocalStorage = function(key, value) { +utils.prototype.decompress = function (value) { try { - if (!core.isset(value)) { + 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 { + if (value == null) { this.removeLocalStorage(key); return; } - var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function(chr) { + var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) }); var compressed = lzw_encode(str); // test if we can save to localStorage localStorage.setItem("__tmp__", compressed); - if (lzw_decode(localStorage.getItem("__tmp__"))==str) { + if (lzw_decode(localStorage.getItem("__tmp__")) == str) { localStorage.setItem(core.firstData.name + "_" + key, compressed); } else { @@ -163,35 +156,15 @@ utils.prototype.setLocalStorage = function(key, value) { } } -utils.prototype.decompress = function (value) { - try { - var output = lzw_decode(value); - if (core.isset(output) && output.length > 0) - return JSON.parse(output); - } - catch (e) {} - try { - var output = LZString.decompress(value); - if (core.isset(output) && output.length > 0) - 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)); - return res==null?defaultValue:res; +utils.prototype.getLocalStorage = function (key, defaultValue) { + var res = this.decompress(localStorage.getItem(core.firstData.name + "_" + key)); + return res == null ? defaultValue : res; } ////// 移除本地存储 ////// utils.prototype.removeLocalStorage = function (key) { - localStorage.removeItem(core.firstData.name+"_"+key); + localStorage.removeItem(core.firstData.name + "_" + key); if (key == 'autoSave') delete core.saves.ids[0]; else if (/^save\d+$/.test(key)) delete core.saves.ids[parseInt(key.substring(4))]; } @@ -200,31 +173,31 @@ utils.prototype.setLocalForage = function (key, value, successCallback, errorCal if (!core.platform.useLocalForage) { if (this.setLocalStorage(key, value)) { - if (core.isset(successCallback)) successCallback(); + if (successCallback) successCallback(); } else { - if (core.isset(errorCallback)) errorCallback(); + if (errorCallback) errorCallback(); } return; } - if (!core.isset(value)) { + if (value == null) { this.removeLocalForage(key); return; } // Save to localforage - var compressed = lzw_encode(JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function(chr) { + var compressed = lzw_encode(JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) })); - localforage.setItem(core.firstData.name+"_"+key, compressed, function (err) { - if (core.isset(err)) { - if (core.isset(errorCallback)) errorCallback(err); + localforage.setItem(core.firstData.name + "_" + key, compressed, function (err) { + if (err) { + if (errorCallback) errorCallback(err); } else { if (key == 'autoSave') core.saves.ids[0] = true; else if (/^save\d+$/.test(key)) core.saves.ids[parseInt(key.substring(4))] = true; - if (core.isset(successCallback)) successCallback(); + if (successCallback) successCallback(); } }); } @@ -232,22 +205,20 @@ utils.prototype.setLocalForage = function (key, value, successCallback, errorCal utils.prototype.getLocalForage = function (key, defaultValue, successCallback, errorCallback) { if (!core.platform.useLocalForage) { - var value=this.getLocalStorage(key, defaultValue); - if (core.isset(successCallback)) { - successCallback(value); - } + var value = this.getLocalStorage(key, defaultValue); + if (successCallback) successCallback(value); return; } - localforage.getItem(core.firstData.name+"_"+key, function (err, value) { - if (core.isset(err)) { - if (core.isset(errorCallback)) errorCallback(err); + localforage.getItem(core.firstData.name + "_" + key, function (err, value) { + if (err) { + if (errorCallback) errorCallback(err); } else { - if (!core.isset(successCallback)) return; - if (core.isset(value)) { + if (!successCallback) return; + if (value != null) { var res = core.utils.decompress(value); - successCallback(res==null?defaultValue:res); + successCallback(res == null ? defaultValue : res); return; } successCallback(defaultValue); @@ -259,34 +230,34 @@ utils.prototype.removeLocalForage = function (key, successCallback, errorCallbac if (!core.platform.useLocalForage) { this.removeLocalStorage(key); - if (core.isset(successCallback)) successCallback(); + if (successCallback) successCallback(); return; } - localforage.removeItem(core.firstData.name+"_"+key, function (err) { - if (core.isset(err)) { - if (core.isset(errorCallback)) errorCallback(err); + localforage.removeItem(core.firstData.name + "_" + key, function (err) { + if (err) { + if (errorCallback) errorCallback(err); } else { if (key == 'autoSave') delete core.saves.ids[0]; else if (/^save\d+$/.test(key)) delete core.saves.ids[parseInt(key.substring(4))]; - if (core.isset(successCallback)) successCallback(); + if (successCallback) successCallback(); } }) } ////// 深拷贝一个对象 ////// utils.prototype.clone = function (data) { - if (!core.isset(data)) return data; + if (!core.isset(data)) return null; // date if (data instanceof Date) { - var copy=new Date(); + var copy = new Date(); copy.setTime(data.getTime()); return copy; } // array if (data instanceof Array) { - var copy=[]; + var copy = []; // for (var i=0;i=one.val) { - var v = x/one.val; - return c + v.toFixed(Math.max(0, Math.floor(3-Math.log10(v+1)))) + one.c; + if (x >= one.val) { + var v = x / one.val; + return c + v.toFixed(Math.max(0, Math.floor(3 - Math.log10(v + 1)))) + one.c; } } else { - if (x>=10*one.val) { - var v = x/one.val; - return c + v.toFixed(Math.max(0, Math.floor(4-Math.log10(v+1)))) + one.c; + if (x >= 10 * one.val) { + var v = x / one.val; + return c + v.toFixed(Math.max(0, Math.floor(4 - Math.log10(v + 1)))) + one.c; } } } - return c+x; + return c + x; } ////// 数组转RGB ////// utils.prototype.arrayToRGB = function (color) { - var nowR = this.clamp(parseInt(color[0]),0,255), nowG = this.clamp(parseInt(color[1]),0,255), - nowB = this.clamp(parseInt(color[2]),0,255); - return "#"+((1<<24)+(nowR<<16)+(nowG<<8)+nowB).toString(16).slice(1); + var nowR = this.clamp(parseInt(color[0]), 0, 255), nowG = this.clamp(parseInt(color[1]), 0, 255), + nowB = this.clamp(parseInt(color[2]), 0, 255); + return "#" + ((1 << 24) + (nowR << 16) + (nowG << 8) + nowB).toString(16).slice(1); } utils.prototype.arrayToRGBA = function (color) { - if (!this.isset(color[3])) color[3]=1; - var nowR = this.clamp(parseInt(color[0]),0,255), nowG = this.clamp(parseInt(color[1]),0,255), - nowB = this.clamp(parseInt(color[2]),0,255), nowA = this.clamp(parseFloat(color[3]),0,1); - return "rgba("+nowR+","+nowG+","+nowB+","+nowA+")"; -} - -utils.prototype._encodeRoute_id2number = function (id) { - var number = core.maps.getNumberById(id); - return number==0?id:number; -} - -utils.prototype._encodeRoute_encodeOne = function (t) { - if (t.indexOf('item:')==0) - return "I"+this._encodeRoute_id2number(t.substring(5))+":"; - else if (t.indexOf('unEquip:')==0) - return "u"+t.substring(8); - else if (t.indexOf('equip:')==0) - return "e"+this._encodeRoute_id2number(t.substring(6))+":"; - else if (t.indexOf('fly:')==0) - return "F"+t.substring(4)+":"; - else if (t.indexOf('choices:')==0) - return "C"+t.substring(8); - else if (t.indexOf('shop:')==0) - return "S"+t.substring(5); - else if (t=='turn') - return 'T'; - else if (t.indexOf('turn:')==0) - return "t"+t.substring(5).substring(0,1).toUpperCase()+":"; - else if (t=='getNext') - return 'G'; - else if (t.indexOf('input:')==0) - return "P"+t.substring(6); - else if (t.indexOf('input2:')==0) - return "Q"+t.substring(7)+":"; - else if (t=='no') - return 'N'; - else if (t.indexOf('move:')==0) - return "M"+t.substring(5); - else if (t.indexOf('key:')==0) - return 'K'+t.substring(4); - else if (t.indexOf('random:')==0) - return 'X'+t.substring(7); - return ''; + if (color[3] == null) color[3] = 1; + var nowR = this.clamp(parseInt(color[0]), 0, 255), nowG = this.clamp(parseInt(color[1]), 0, 255), + nowB = this.clamp(parseInt(color[2]), 0, 255), nowA = this.clamp(parseFloat(color[3]), 0, 1); + return "rgba(" + nowR + "," + nowG + "," + nowB + "," + nowA + ")"; } ////// 加密路线 ////// utils.prototype.encodeRoute = function (route) { - var ans="", lastMove = "", cnt=0; + var ans = "", lastMove = "", cnt = 0; route.forEach(function (t) { - if (t=='up' || t=='down' || t=='left' || t=='right') { - if (t!=lastMove && cnt>0) { - ans+=lastMove.substring(0,1).toUpperCase(); - if (cnt>1) ans+=cnt; - cnt=0; + if (t == 'up' || t == 'down' || t == 'left' || t == 'right') { + if (t != lastMove && cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; + cnt = 0; } - lastMove=t; + lastMove = t; cnt++; } else { - if (cnt>0) { - ans+=lastMove.substring(0,1).toUpperCase(); - if (cnt>1) ans+=cnt; - cnt=0; + if (cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; + cnt = 0; } ans += core.utils._encodeRoute_encodeOne(t); } }); - if (cnt>0) { - ans+=lastMove.substring(0,1).toUpperCase(); - if (cnt>1) ans+=cnt; + if (cnt > 0) { + ans += lastMove.substring(0, 1).toUpperCase(); + if (cnt > 1) ans += cnt; } return LZString.compressToBase64(ans); } -utils.prototype._decodeRoute_getNumber = function (decodeObj, noparse) { - var num=""; - while (decodeObj.index= 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); + + var mp = {"U": "up", "D": "down", "L": "left", "R": "right"}; + + switch (c) { + case "U": + case "D": + case "L": + case "R": + for (var i = 0; i < nxt; i++) decodeObj.ans.push(mp[c]); + break; + case "I": + decodeObj.ans.push("item:" + this._decodeRoute_number2id(nxt)); + break; + case "u": + decodeObj.ans.push("unEquip:" + nxt); + break; + case "e": + decodeObj.ans.push("equip:" + this._decodeRoute_number2id(nxt)); + break; + case "F": + decodeObj.ans.push("fly:" + nxt); + break; + case "C": + decodeObj.ans.push("choices:" + nxt); + break; + case "S": + decodeObj.ans.push("shop:" + nxt + ":" + this._decodeRoute_getNumber(decodeObj, true)); + break; + case "T": + decodeObj.ans.push("turn"); + break; + case "t": + decodeObj.ans.push("turn:" + mp[nxt]); + break; + case "G": + decodeObj.ans.push("getNext"); + break; + case "P": + decodeObj.ans.push("input:" + nxt); + break; + case "Q": + decodeObj.ans.push("input2:" + nxt); + break; + case "N": + decodeObj.ans.push("no"); + break; + case "M": + ++decodeObj.index; + decodeObj.ans.push("move:" + nxt + ":" + this._decodeRoute_getNumber(decodeObj)); + break; + case "K": + decodeObj.ans.push("key:" + nxt); + break; + case "X": + decodeObj.ans.push("random:" + nxt); + break; + } +} + +////// 判断某对象是否不为null也不为NaN ////// +utils.prototype.isset = function (val) { + return val != null && !(typeof val == 'number' && isNaN(val)); } ////// 获得子数组 ////// utils.prototype.subarray = function (a, b) { - if (!core.isset(a) || !core.isset(b) || !(a instanceof Array) || !(b instanceof Array) || a.length0) { + var na = core.clone(a), nb = core.clone(b); + while (nb.length > 0) { if (na.shift() != nb.shift()) return null; } return na; } utils.prototype.inArray = function (array, element) { - return this.isset(array) && (array instanceof Array) && array.indexOf(element)>=0; + return (array instanceof Array) && array.indexOf(element) >= 0; } utils.prototype.clamp = function (x, a, b) { - var min=Math.min(a, b), max=Math.max(a, b); - return Math.min(Math.max(x||0, min), max); + var min = Math.min(a, b), max = Math.max(a, b); + return Math.min(Math.max(x || 0, min), max); } utils.prototype.getCookie = function (name) { var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); - return match?match[2]:null; + return match ? match[2] : null; +} + +////// 设置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); + var style = 'font-style: ' + (italic ? 'italic' : 'normal') + '; '; + // 判定是否需要缩放 + var length = this.strlen(value) || 1; + style += 'font-size: ' + Math.min(1, 7 / length) + 'em; '; + if (css) style += css; + core.statusBar[name].innerHTML = "" + value + ""; +} + +utils.prototype.strlen = function (str) { + var count = 0; + for (var i = 0, len = str.length; i < len; i++) { + count += str.charCodeAt(i) < 256 ? 1 : 2; + } + return count; +}; + +utils.prototype.reverseDirection = function (direction) { + direction = direction || core.getHeroLoc('direction'); + return {"left":"right","right":"left","down":"up","up":"down"}[direction] || direction; } ////// Base64加密 ////// @@ -579,7 +633,7 @@ utils.prototype.encodeBase64 = function (str) { ////// Base64解密 ////// utils.prototype.decodeBase64 = function (str) { - return decodeURIComponent(atob(str).split('').map(function(c) { + return decodeURIComponent(atob(str).split('').map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); } @@ -587,57 +641,42 @@ utils.prototype.decodeBase64 = function (str) { ////// 任意进制转换 ////// utils.prototype.convertBase = function (str, fromBase, toBase) { var map = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]\\|:;<>,.?/"; - if (fromBase==toBase) return str; - var len = str.length, ans=""; + if (fromBase == toBase) return str; + var len = str.length, ans = ""; var t = []; - for (var i=0;i0) { - for (var i=len; i>=1; i--) { - t[i-1]+=t[i]%toBase*fromBase; - t[i]=parseInt(t[i]/toBase); + for (var i = 0; i < len; i++) t[i] = map.indexOf(str.charAt(i)); + t[len] = 0; + while (len > 0) { + for (var i = len; i >= 1; i--) { + t[i - 1] += t[i] % toBase * fromBase; + t[i] = parseInt(t[i] / toBase); } - ans+=map.charAt(t[0]%toBase); - t[0]=parseInt(t[0]/toBase); - while (len>0 && t[len-1]==0) len--; + ans += map.charAt(t[0] % toBase); + t[0] = parseInt(t[0] / toBase); + while (len > 0 && t[len - 1] == 0) len--; } return ans; } -utils.prototype.__init_seed = function () { - var rand = new Date().getTime()%34834795 + 3534; - rand = this.__next_rand(rand); - rand = this.__next_rand(rand); - rand = this.__next_rand(rand); - core.setFlag('__seed__', rand); - core.setFlag('__rand__', rand); -} - -utils.prototype.__next_rand = function (_rand) { - _rand=(_rand%127773)*16807-~~(_rand/127773)*2836; - _rand+=_rand<0?2147483647:0; - return _rand; -} - utils.prototype.rand = function (num) { var rand = core.getFlag('__rand__'); rand = this.__next_rand(rand); core.setFlag('__rand__', rand); - var ans = rand/2147483647; - if (core.isset(num) && num>0) - return Math.floor(ans*num); + var ans = rand / 2147483647; + if (num && num > 0) + return Math.floor(ans * num); return ans; } ////// 生成随机数(录像方法) ////// utils.prototype.rand2 = function (num) { - num = num||2147483648; + num = num || 2147483648; var value; if (core.isReplaying()) { var action = core.status.replay.toReplay.shift(); - if (action.indexOf("random:")==0 ) { - value=parseInt(action.substring(7)); + if (action.indexOf("random:") == 0) { + value = parseInt(action.substring(7)); } else { core.stopReplay(); @@ -646,19 +685,34 @@ utils.prototype.rand2 = function (num) { } } else { - value = Math.floor(Math.random()*num); + value = Math.floor(Math.random() * num); } - core.status.route.push("random:"+value); + core.status.route.push("random:" + value); return value; } +utils.prototype.__init_seed = function () { + var rand = new Date().getTime() % 34834795 + 3534; + rand = this.__next_rand(rand); + rand = this.__next_rand(rand); + rand = this.__next_rand(rand); + core.setFlag('__seed__', rand); + core.setFlag('__rand__', rand); +} + +utils.prototype.__next_rand = function (_rand) { + _rand = (_rand % 127773) * 16807 - ~~(_rand / 127773) * 2836; + _rand += _rand < 0 ? 2147483647 : 0; + return _rand; +} + ////// 读取一个本地文件内容 ////// utils.prototype.readFile = function (success, error, readType) { core.platform.successCallback = success; core.platform.errorCallback = error; - if (core.isset(window.jsinterface)) { + if (window.jsinterface) { window.jsinterface.readFile(); return; } @@ -666,29 +720,29 @@ utils.prototype.readFile = function (success, error, readType) { // step 0: 不为http/https,直接不支持 if (!core.platform.isOnline) { alert("离线状态下不支持文件读取!"); - if (core.isset(error)) error(); + if (error) error(); return; } // Step 1: 如果不支持FileReader,直接不支持 - if (core.platform.fileReader==null) { + if (core.platform.fileReader == null) { alert("当前浏览器不支持FileReader!"); - if (core.isset(error)) error(); + if (error) error(); return; } - if (core.platform.fileInput==null) { + if (core.platform.fileInput == null) { core.platform.fileInput = document.createElement("input"); core.platform.fileInput.style.opacity = 0; core.platform.fileInput.type = 'file'; core.platform.fileInput.onchange = function () { var files = core.platform.fileInput.files; - if (files.length==0) { - if (core.isset(core.platform.errorCallback)) + if (files.length == 0) { + if (core.platform.errorCallback) core.platform.errorCallback(); return; } - if(!readType)core.platform.fileReader.readAsText(core.platform.fileInput.files[0]); + if (!readType) core.platform.fileReader.readAsText(core.platform.fileInput.files[0]); else core.platform.fileReader.readAsDataURL(core.platform.fileInput.files[0]); core.platform.fileInput.value = ''; } @@ -699,16 +753,16 @@ utils.prototype.readFile = function (success, error, readType) { ////// 读取文件完毕 ////// utils.prototype.readFileContent = function (content) { - var obj=null; - if(content.slice(0,4)==='data'){ - if (core.isset(core.platform.successCallback)) + var obj = null; + if (content.slice(0, 4) === 'data') { + if (core.platform.successCallback) core.platform.successCallback(content); return; } try { - obj=JSON.parse(content); - if (core.isset(obj)) { - if (core.isset(core.platform.successCallback)) + obj = JSON.parse(content); + if (obj) { + if (core.platform.successCallback) core.platform.successCallback(obj); return; } @@ -717,16 +771,16 @@ utils.prototype.readFileContent = function (content) { main.log(e); alert(e); } - alert("不是有效的JSON文件!"); + // alert("不是有效的JSON文件!"); - if (core.isset(core.platform.errorCallback)) + if (core.platform.errorCallback) core.platform.errorCallback(); } ////// 下载文件到本地 ////// utils.prototype.download = function (filename, content) { - if (core.isset(window.jsinterface)) { + if (window.jsinterface) { window.jsinterface.download(filename, content); return; } @@ -766,14 +820,14 @@ utils.prototype.download = function (filename, content) { alert("你当前使用的是Safari浏览器,不支持直接下载文件。\n即将打开一个新窗口为应下载内容,请自行全选复制然后创建空白文件并粘贴。"); var blob = new Blob([content], {type: 'text/plain;charset=utf-8'}); var href = window.URL.createObjectURL(blob); - var opened=window.open(href, "_blank"); + var opened = window.open(href, "_blank"); window.URL.revokeObjectURL(href); return; } // Step 4: 下载 var blob = new Blob([content], {type: 'text/plain;charset=utf-8'}); - if(window.navigator.msSaveOrOpenBlob) { + if (window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveBlob(blob, filename); } else { @@ -791,8 +845,8 @@ utils.prototype.download = function (filename, content) { ////// 复制一段内容到剪切板 ////// utils.prototype.copy = function (data) { - if (core.isset(window.jsinterface)) { - window.jsinterface.copy(filename, content); + if (window.jsinterface) { + window.jsinterface.copy(data); return true; } @@ -824,12 +878,36 @@ utils.prototype.copy = function (data) { return successful; } +////// 显示一段confirm ////// +utils.prototype.myconfirm = function (hint, yesCallback, noCallback) { + main.dom.inputDiv.style.display = 'block'; + main.dom.inputMessage.innerHTML = hint.replace(/\n/g, '
'); + main.dom.inputBox.style.display = 'none'; + main.dom.inputYes.focus(); + + core.platform.successCallback = yesCallback; + core.platform.errorCallback = noCallback; +} + +////// 让用户输入一段文字 ////// +utils.prototype.myprompt = function (hint, value, callback) { + main.dom.inputDiv.style.display = 'block'; + main.dom.inputMessage.innerHTML = hint.replace(/\n/g, '
'); + main.dom.inputBox.style.display = 'block'; + main.dom.inputBox.value = value==null?"":value; + setTimeout(function () { + main.dom.inputBox.focus(); + }); + + core.platform.successCallback = core.platform.errorCallback = callback; +} + ////// 动画显示某对象 ////// -utils.prototype.show = function (obj, speed, callback) { +utils.prototype.showWithAnimate = function (obj, speed, callback) { obj.style.display = 'block'; - if (!core.isset(speed) && main.mode!='play') { + if (!speed && main.mode != 'play') { obj.style.opacity = 1; - if (core.isset(callback)) callback(); + if (callback) callback(); return; } obj.style.opacity = 0; @@ -839,18 +917,16 @@ utils.prototype.show = function (obj, speed, callback) { obj.style.opacity = opacityVal; if (opacityVal > 1) { clearInterval(showAnimate); - if (core.isset(callback)) { - callback(); - } + if (callback) callback(); } }, speed); } ////// 动画使某对象消失 ////// -utils.prototype.hide = function (obj, speed, callback) { - if (!core.isset(speed) || main.mode!='play'){ +utils.prototype.hideWithAnimate = function (obj, speed, callback) { + if (!speed || main.mode != 'play') { obj.style.display = 'none'; - if (core.isset(callback)) callback(); + if (callback) callback(); return; } obj.style.opacity = 1; @@ -861,14 +937,12 @@ utils.prototype.hide = function (obj, speed, callback) { if (opacityVal < 0) { obj.style.display = 'none'; clearInterval(hideAnimate); - if (core.isset(callback)) { - callback(); - } + if (callback) 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; @@ -877,16 +951,16 @@ utils.prototype.encodeCanvas = function (ctx) { ctx.imageSmoothingEnabled = false; var imgData = ctx.getImageData(0, 0, width, height); - for (var i=0;i threshold - || window.outerHeight - zoom*window.innerHeight > threshold; + var zoom = Math.min(window.outerWidth / window.innerWidth, window.outerHeight / window.innerHeight); + return window.outerWidth - zoom * window.innerWidth > threshold + || window.outerHeight - zoom * window.innerHeight > threshold; } utils.prototype.hashCode = function (obj) { @@ -936,8 +1010,8 @@ utils.prototype.hashCode = function (obj) { var hash = 0, i, chr; if (obj.length === 0) return hash; for (i = 0; i < obj.length; i++) { - chr = obj.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; + chr = obj.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; hash |= 0; } return hash; @@ -946,8 +1020,8 @@ utils.prototype.hashCode = function (obj) { } utils.prototype.same = function (a, b) { - if (!core.isset(a) && !core.isset(b)) return true; - if (!core.isset(a) || !core.isset(b)) return false; + if (a == null && b == null) return true; + if (a == null || b == null) return false; if (a === b) return true; if (a instanceof Array && b instanceof Array) { if (a.length != b.length) return false; @@ -969,21 +1043,21 @@ utils.prototype.same = function (a, b) { } utils.prototype._export = function (floorIds) { - if (!core.isset(floorIds)) floorIds = [core.status.floorId]; - else if (floorIds=='all') floorIds = core.clone(core.floorIds); + if (!floorIds) floorIds = [core.status.floorId]; + else if (floorIds == 'all') floorIds = core.clone(core.floorIds); else if (typeof floorIds == 'string') floorIds = [floorIds]; var monsterMap = {}; // map - var content = floorIds.length+"\n13 13\n\n"; + 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, 13, 13); + var arr = core.maps._getMapArrayFromBlocks(core.status.maps[floorId].blocks); content += arr.map(function (x) { // check monster x.forEach(function (t) { var block = core.maps.initBlock(null, null, t); - if (core.isset(block.event) && block.event.cls.indexOf("enemy")==0) { + if (block.event.cls.indexOf("enemy") == 0) { monsterMap[t] = block.event.id; } }) @@ -993,7 +1067,9 @@ utils.prototype._export = function (floorIds) { // values content += ["redJewel", "blueJewel", "greenJewel", "redPotion", "bluePotion", - "yellowPotion", "greenPotion", "sword1", "shield1"].map(function (x) {return core.values[x]}).join(" ") + "\n\n"; + "yellowPotion", "greenPotion", "sword1", "shield1"].map(function (x) { + return core.values[x] + }).join(" ") + "\n\n"; // monster content += Object.keys(monsterMap).length + "\n"; @@ -1014,34 +1090,26 @@ utils.prototype._export = function (floorIds) { utils.prototype.http = function (type, url, formData, success, error, mimeType, responseType) { var xhr = new XMLHttpRequest(); xhr.open(type, url, true); - if (core.isset(mimeType)) - xhr.overrideMimeType(mimeType); - if (core.isset(responseType)) - xhr.responseType = responseType; - xhr.onload = function(e) { - if (xhr.status==200) { - if (core.isset(success)) { - success(xhr.response); - } + if (mimeType) xhr.overrideMimeType(mimeType); + if (responseType) xhr.responseType = responseType; + xhr.onload = function (e) { + if (xhr.status == 200) { + if (success) success(xhr.response); } else { - if (core.isset(error)) - error("HTTP "+xhr.status); + if (error) error("HTTP " + xhr.status); } }; xhr.onabort = function () { - if (core.isset(error)) - error("Abort"); + if (error) error("Abort"); } - xhr.ontimeout = function() { - if (core.isset(error)) - error("Timeout"); + xhr.ontimeout = function () { + if (error) error("Timeout"); } - xhr.onerror = function() { - if (core.isset(error)) - error("Error on Connection"); + xhr.onerror = function () { + if (error) error("Error on Connection"); } - if (core.isset(formData)) + if (formData) xhr.send(formData); else xhr.send(); } @@ -1055,8 +1123,8 @@ function lzw_encode(s) { var currChar; var phrase = data[0]; var code = 256; - for (var i=1; i 1 ? dict[phrase] : phrase.charCodeAt(0)); dict[phrase + currChar] = code; code++; - phrase=currChar; + phrase = currChar; } } out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0)); - for (var i=0; i=1:直接扣数值" }, { - "type": "setValue2", + "type": "addValue", "name": "status:atk", "value": "-core.values.weakValue" }, { - "type": "setValue2", + "type": "addValue", "name": "status:def", "value": "-core.values.weakValue" } @@ -118,14 +113,12 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "text": "<1:扣比例" }, { - "type": "setValue2", - "name": "flag:__atk_buff__", - "value": "-core.values.weakValue" + "type": "function", + "function": "function(){\ncore.addBuff('atk', -core.values.weakValue);\n}" }, { - "type": "setValue2", - "name": "flag:__def_buff__", - "value": "-core.values.weakValue" + "type": "function", + "function": "function(){\ncore.addBuff('def', -core.values.weakValue);\n}" } ] } @@ -135,7 +128,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = ] }, { - "case": "'curse'", + "case": "2", "action": [ { "type": "comment", @@ -156,11 +149,187 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = ] } ] + } + ], + "滑冰事件": [ + { + "type": "comment", + "text": "公共事件:滑冰事件" }, { - "type": "setValue", - "name": "flag:debuff", - "value": "null" + "type": "if", + "condition": "core.canMoveHero()", + "true": [ + { + "type": "comment", + "text": "检测下一个点是否可通行" + }, + { + "type": "setValue", + "name": "flag:nx", + "value": "core.nextX()" + }, + { + "type": "setValue", + "name": "flag:ny", + "value": "core.nextY()" + }, + { + "type": "if", + "condition": "core.noPass(flag:nx, flag:ny)", + "true": [ + { + "type": "comment", + "text": "不可通行,触发下一个点的事件" + }, + { + "type": "trigger", + "loc": [ + "flag:nx", + "flag:ny" + ] + } + ], + "false": [ + { + "type": "comment", + "text": "可通行,先移动到下个点,然后检查阻激夹域,并尝试触发该点事件" + }, + { + "type": "moveHero", + "time": 80, + "steps": [ + "forward" + ] + }, + { + "type": "function", + "function": "function(){\ncore.checkBlock();\n}" + }, + { + "type": "comment", + "text": "【触发事件】如果该点存在事件则会立刻结束当前事件" + }, + { + "type": "trigger", + "loc": [ + "flag:nx", + "flag:ny" + ] + }, + { + "type": "comment", + "text": "如果该点不存在事件,则继续检测该点是否是滑冰点" + }, + { + "type": "if", + "condition": "core.getBgNumber() == 167", + "true": [ + { + "type": "function", + "function": "function(){\ncore.insertAction(\"滑冰事件\",null,null,null,true)\n}" + } + ], + "false": [] + } + ] + } + ], + "false": [] + } + ], + "回收钥匙商店": [ + { + "type": "comment", + "text": "此事件在全局商店中被引用了(全局商店keyShop1)" + }, + { + "type": "comment", + "text": "解除引用前勿删除此事件" + }, + { + "type": "comment", + "text": "玩家在快捷列表(V键)中可以使用本公共事件" + }, + { + "type": "while", + "condition": "1", + "data": [ + { + "type": "choices", + "text": "\t[商人,woman]你有多余的钥匙想要出售吗?", + "choices": [ + { + "text": "黄钥匙(10金币)", + "color": [ + 255, + 255, + 0, + 1 + ], + "action": [ + { + "type": "if", + "condition": "item:yellowKey >= 1", + "true": [ + { + "type": "addValue", + "name": "item:yellowKey", + "value": "-1" + }, + { + "type": "addValue", + "name": "status:money", + "value": "10" + } + ], + "false": [ + "\t[商人,woman]你没有黄钥匙!" + ] + } + ] + }, + { + "text": "蓝钥匙(50金币)", + "color": [ + 0, + 0, + 255, + 1 + ], + "action": [ + { + "type": "if", + "condition": "item:blueKey >= 1", + "true": [ + { + "type": "addValue", + "name": "item:blueKey", + "value": "-1" + }, + { + "type": "addValue", + "name": "status:money", + "value": "50" + } + ], + "false": [ + "\t[商人,woman]你没有蓝钥匙!" + ] + } + ] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] } ] } diff --git a/project/floors/sample1.js b/project/floors/sample1.js index 50b9e828..92b3a0e8 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -1,25 +1,25 @@ main.floors.sample1= { - "floorId": "sample1", - "title": "样板 1 层", - "name": "1", - "canFlyTo": true, - "canUseQuickShop": true, - "defaultGround": "grass", - "images": [ - [ - 0, - 0, - "bg.jpg", - 0 - ] - ], - "weather": [ - "snow", - 6 - ], - "item_ratio": 1, - "map": [ +"floorId": "sample1", +"title": "样板 1 层", +"name": "1", +"canFlyTo": true, +"canUseQuickShop": true, +"defaultGround": "grass", +"images": [ + [ + 0, + 0, + "bg.jpg", + 0 + ] +], +"weather": [ + "snow", + 6 +], +"item_ratio": 1, +"map": [ [ 7,131, 8,152, 9,130, 10,152,166,165,132,165,166], [ 0, 0, 0, 0, 0, 0, 0,152,165,164, 0,162,165], [152,152,152,152,121,152,152,152, 0, 0,229, 0, 0], @@ -34,751 +34,745 @@ main.floors.sample1= [ 1, 0,123, 1, 0, 20,124, 0,121, 0,122, 0,126], [ 1, 0, 0, 1, 88, 20, 86, 0, 0, 0, 0, 0,122] ], - "firstArrive": [], - "events": { - "4,10": [ - "\t[样板提示]本层楼将会对各类事件进行介绍。", - "左边是一个仿50层的陷阱做法,上方是商店、快捷商店的使用方法,右上是一个典型的杀怪开门的例子,右下是各类可能的NPC事件。", - "本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nanimate: 显示动画\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)", - "openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nshowImage: 显示图片\nsetFg: 更改画面色调\nsetWeather: 更改天气\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频", - "if: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中,欢迎您宝贵的意见。", - "有关各事件的样例,可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后,需要调用{\"type\": \"hide\"}该事件才不会再次出现。", - { - "type": "hide" - } - ], - "1,5": { - "enable": false, - "data": [] - }, - "1,6": { - "enable": false, - "data": [] - }, - "0,7": { - "enable": false, - "data": [] - }, - "2,7": { - "enable": false, - "data": [] - }, - "1,8": { - "enable": false, - "data": [] - }, - "1,7": [ - { - "type": "show", - "loc": [ - 1, - 5 - ], - "time": 1500 - }, - { - "type": "sleep", - "time": 500 - }, - "\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下,我就与你一对一的决斗。\n现在你必须接受我的安排。", - { - "type": "show", - "loc": [ - [ - 1, - 6 - ], - [ - 0, - 7 - ], - [ - 1, - 8 - ], - [ - 2, - 7 - ] - ], - "time": 500 - }, - "\t[hero]什么?", - { - "type": "playSound", - "name": "attack.mp3" - }, - { - "type": "setValue", - "name": "status:atk", - "value": "status:atk/10" - }, - { - "type": "setValue", - "name": "status:def", - "value": "status:def/10" - }, - { - "type": "hide", - "loc": [ - [ - 1, - 6 - ], - [ - 0, - 7 - ], - [ - 2, - 7 - ], - [ - 1, - 8 - ] - ] - }, - { - "type": "hide", - "loc": [ - 1, - 5 - ], - "time": 500 - }, - { - "type": "hide" - }, - { - "type": "setFg", - "color": [ - 0, - 0, - 0 - ], - "time": 1250 - }, - { - "type": "sleep", - "time": 700 - }, - { - "type": "changeFloor", - "floorId": "sample1", - "loc": [ - 1, - 11 - ], - "direction": "right", - "time": 1000 - }, - { - "type": "trigger", - "loc": [ - 2, - 11 - ] - } - ], - "2,11": [ - "\t[杰克,thief]喂!醒醒!快醒醒!", - { - "type": "setFg", - "time": 1500 - }, - "\t[hero]额,我这是在什么地方?", - "\t[杰克,thief]你被魔王抓了起来扔进了监狱,和我关在了一起,但是幸运的是我在昨天刚刚挖好一条越狱的暗道!", - { - "type": "openDoor", - "loc": [ - 3, - 11 - ] - }, - { - "type": "sleep", - "time": 300 - }, - "\t[杰克,thief]我先走了,祝你好运!", - { - "type": "move", - "time": 750, - "steps": [ - { - "direction": "right", - "value": 2 - }, - "down" - ] - }, - "上面是个move事件,可以对NPC等进行移动。\n详见样板中小偷事件的写法。", - "\t[hero]怎么跑的这么快..." - ], - "4,2": [ - "\t[老人,man]本塔的商店有两类,全局商店和非全局商店。\n\n所谓非全局商店,就类似于右下角那个卖钥匙的老人一样,一定要碰到才能触发事件。\n\n而全局商店,则能在快捷商店中直接使用。", - "\t[老人,man]要注册一个全局商店,你需要在 data.js 中,找到 shops,并在内添加你的商店信息。", - "\t[老人,man]商店信息添加后,可以在需要的事件处调用{\"type\": \"openShop\"}来打开你添加的全局商店。", - "\t[老人,man]在上面的例子里,左边是一个仿50层的金币商店,右边是一个仿24层的经验商店。\n\n商店被访问后即可在快捷商店中进行使用。", - "\t[老人,man]如果你需要在某层暂时禁用快捷商店,可以在data.js中设置cannotUseQuickShop。\n如果需要永久禁用商店,请使用{\"type\":\"disableShop\"}", - { - "type": "hide", - "time": 500 - } - ], - "1,0": [ - { - "type": "openShop", - "id": "moneyShop1" - } - ], - "5,0": [ - { - "type": "openShop", - "id": "expShop1" - } - ], - "7,7": [ - "\t[老人,man]这是一个典型的杀怪开门、强制战斗事件。", - { - "type": "hide" - } - ], - "8,7": { - "enable": false, - "data": [] - }, - "9,7": [ - { - "type": "show", - "loc": [ - 8, - 7 - ] - }, - { - "type": "hide" - } - ], - "10,4": [ - "\t[blackKing]你终于还是来了。", - "\t[hero]放开我们的公主!", - "\t[blackKing]如果我不愿意呢?", - "\t[hero]无需多说,拔剑吧!", - { - "type": "battle", - "id": "blackKing" - }, - { - "type": "hide", - "loc": [ - 10, - 2 - ] - }, - { - "type": "openDoor", - "loc": [ - 8, - 7 - ] - }, - "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", - { - "type": "hide" - } - ], - "10,0": [ - "\t[hero]公主,我来救你了~", - "\t[公主,princess]快救我出去!我受够这里了!", - "\t[hero]公主别怕,我们走吧~", - { - "type": "win", - "reason": "救出公主" - } - ], - "6,12": { - "enable": false, - "data": [] - }, - "6,11": [ - "\t[仙子,fairy]通过调用 {\"type\": \"show\"} 可以使隐藏的事件显示出来。\n比如我下面这个机关门。", - { - "type": "show", - "loc": [ - 6, - 12 - ] - }, - "\t[仙子,fairy]通过调用 {\"type\": \"openDoor\"} 可以无需钥匙打开一扇门或暗墙。", - { - "type": "openDoor", - "loc": [ - 6, - 12 - ] - }, - "\t[仙子,fairy]同时,也可以对其它层进行操作,比如楼下的机关门,现在已经为你打开了。", - { - "type": "openDoor", - "loc": [ - 11, - 10 - ], - "floorId": "sample0" - }, - "\t[仙子,fairy]如果 show 或 hide 指定了 time 参数,则以动画效果显示,指定的参数作为消失时间(毫秒)来计算。", - "\t[仙子,fairy]现在到楼下来找我吧~", - { - "type": "show", - "loc": [ - 12, - 10 - ], - "floorId": "sample0" - }, - { - "type": "hide", - "time": 500 - } - ], - "8,11": [ - { - "type": "setValue", - "name": "flag:man_times", - "value": "flag:man_times+1" - }, - "\t[老人,man]在文字中使用${' ${ '}和 } 可以计算并显示一个表达式的结果。\n", - "\t[老人,man]例如:\n你的当前攻击力是${status:atk},防御力是${status:def}。\n攻防和的十倍是${10*(status:atk+status:def)},攻防之积是${status:atk*status:def}。\n你有${item:yellowKey}把黄钥匙,${item:blueKey}把蓝钥匙,${item:redKey}把红钥匙。\n你有${item:pickaxe}个破,${item:bomb}个炸,${item:centerFly}个飞。\n这是你第${flag:man_times}次和我对话。", - "\t[老人,man]同时,你也可以通过{\"type\": \"setValue\"}来设置一个勇士的属性、道具,或某个Flag。", - "\t[老人,man]例如:\n现在我将让你的攻防提升50%,再将攻防和的十倍加到生命值上。", - { - "type": "setValue", - "name": "status:atk", - "value": "status:atk*1.5" - }, - { - "type": "setValue", - "name": "status:def", - "value": "status:def*1.5" - }, - { - "type": "setValue", - "name": "status:hp", - "value": "status:hp+10*(status:atk+status:def)" - }, - "\t[老人,man]再送你500金币,1000经验,1破2炸3飞!", - { - "type": "setValue", - "name": "status:money", - "value": "status:money+500" - }, - { - "type": "setValue", - "name": "status:experience", - "value": "status:experience+1000" - }, - { - "type": "setValue", - "name": "item:pickaxe", - "value": "item:pickaxe+1" - }, - { - "type": "setValue", - "name": "item:bomb", - "value": "item:bomb+2" - }, - { - "type": "setValue", - "name": "item:centerFly", - "value": "item:centerFly+3" - }, - "\t[老人,man]status:xxx 代表勇士的某个属性。\n其中xxx可取hp, atk, def, mdef, money,experience这几项。\n\nitem:xxx 代表勇士的某个道具的个数。\nxxx为道具ID,具体可参见items.js中的定义。\n\nflag:xxx 代表某个自定义Flag或变量。\nxxx为Flag/变量名,可以自行定义,由字母、数字和下划线组成。\n未定义过而直接取用的Flag默认值为false。", - "\t[老人,man]你现在可以重新和我进行对话,进一步看到属性值的改变。" - ], - "10,11": [ - { - "type": "if", - "condition": "flag:woman_times==0", - "true": [ - "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", - "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", - "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" - ], - "false": [ - { - "type": "if", - "condition": "flag:woman_times==8", - "true": [ - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", - { - "type": "setValue", - "name": "item:bigKey", - "value": "item:bigKey+1" - }, - "\t[老人,woman]我先走了,拜拜~", - { - "type": "hide", - "time": 500 - }, - { - "type": "exit" - } - ], - "false": [ - { - "type": "choices", - "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", - "choices": [ - { - "text": "黄钥匙(${9+flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=9+flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(9+flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:yellowKey", - "value": "item:yellowKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "蓝钥匙(${18+2*flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=18+2*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(18+2*flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:blueKey", - "value": "item:blueKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "红钥匙(${36+4*flag:woman_times}金币)", - "action": [ - { - "type": "if", - "condition": "status:money>=36+4*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(36+4*flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:redKey", - "value": "item:redKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "离开", - "action": [ - { - "type": "exit" - } - ] - } - ] - } - ] - } - ] - }, - { - "type": "setValue", - "name": "flag:woman_times", - "value": "flag:woman_times+1" - }, - { - "type": "revisit" - } - ], - "12,11": [ - "\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的JS脚本。\n本塔支持的所有主要API会在doc文档内给出。", - "\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后会将你的输入结果直接加到你的攻击力上。", - { - "type": "input", - "text": "请输入你要加攻击力的数值:" - }, - { - "type": "if", - "condition": "flag:input>0", - "true": [ - { - "type": "setValue", - "name": "status:atk", - "value": "status:atk+flag:input" - }, - { - "type": "tip", - "text": "操作成功,攻击+${flag:input}" - }, - "操作成功,攻击+${flag:input}" - ], - "false": [] - }, - "\t[老人,womanMagician]具体可参见样板中本事件的写法。" - ], - "10,12": null, - "12,12": [ - { - "type": "switch", - "condition": "flag:woman_times", - "caseList": [ - { - "case": "0", - "action": [ - "\t[老人,woman]现在使用switch改写这个例子" - ] - }, - { - "case": "8", - "action": [ - "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", - "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", - { - "type": "setValue", - "name": "item:bigKey", - "value": "item:bigKey+1" - }, - "\t[老人,woman]我先走了,拜拜~", - { - "type": "hide", - "time": 500 - }, - { - "type": "exit" - } - ] - }, - { - "case": "default", - "action": [ - { - "type": "comment", - "text": "当没有符合的值的场合执行此事件" - }, - { - "type": "choices", - "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", - "choices": [ - { - "text": "黄钥匙(${9+flag:woman_times}金币)", - "color": [255,255,0,1], - "action": [ - { - "type": "if", - "condition": "status:money>=9+flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(9+flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:yellowKey", - "value": "item:yellowKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "蓝钥匙(${18+2*flag:woman_times}金币)", - "color": [0,0,255,1], - "action": [ - { - "type": "if", - "condition": "status:money>=18+2*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(18+2*flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:blueKey", - "value": "item:blueKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "红钥匙(${36+4*flag:woman_times}金币)", - "color": [255,0,0,1], - "action": [ - { - "type": "if", - "condition": "status:money>=36+4*flag:woman_times", - "true": [ - { - "type": "setValue", - "name": "status:money", - "value": "status:money-(36+4*flag:woman_times)" - }, - { - "type": "setValue", - "name": "item:redKey", - "value": "item:redKey+1" - } - ], - "false": [ - "\t[老人,woman]你的金钱不足!", - { - "type": "revisit" - } - ] - } - ] - }, - { - "text": "离开", - "action": [ - { - "type": "exit" - } - ] - } - ] - } - ] - } - ] - }, - { - "type": "setValue", - "name": "flag:woman_times", - "value": "flag:woman_times+1" - }, - { - "type": "revisit" - } - ] +"firstArrive": [], +"events": { + "4,10": [ + "\t[样板提示]本层楼将会对各类事件进行介绍。", + "左边是一个仿50层的陷阱做法,上方是商店、快捷商店的使用方法,右上是一个典型的杀怪开门的例子,右下是各类可能的NPC事件。", + "本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nanimate: 显示动画\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)", + "openShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nshowImage: 显示图片\nsetFg: 更改画面色调\nsetWeather: 更改天气\nmove: 移动事件效果\nmoveHero: 移动勇士效果\nplayBgm: 播放某个背景音乐\npauseBgm: 暂停背景音乐\nresumeBgm: 恢复背景音乐的播放\nplaySound: 播放某个音频", + "if: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中,欢迎您宝贵的意见。", + "有关各事件的样例,可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后,需要调用{\"type\": \"hide\"}该事件才不会再次出现。", + { + "type": "hide" + } + ], + "1,5": { + "enable": false, + "data": [] }, - "changeFloor": { - "4,12": { - "floorId": "sample0", + "1,6": { + "enable": false, + "data": [] + }, + "0,7": { + "enable": false, + "data": [] + }, + "2,7": { + "enable": false, + "data": [] + }, + "1,8": { + "enable": false, + "data": [] + }, + "1,7": [ + { + "type": "show", "loc": [ - 6, - 0 + 1, + 5 + ], + "time": 1500 + }, + { + "type": "sleep", + "time": 500 + }, + "\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下,我就与你一对一的决斗。\n现在你必须接受我的安排。", + { + "type": "show", + "loc": [ + [ + 1, + 6 + ], + [ + 0, + 7 + ], + [ + 1, + 8 + ], + [ + 2, + 7 + ] + ], + "time": 500 + }, + "\t[hero]什么?", + { + "type": "playSound", + "name": "attack.mp3" + }, + { + "type": "setValue", + "name": "status:atk", + "value": "status:atk/10" + }, + { + "type": "setValue", + "name": "status:def", + "value": "status:def/10" + }, + { + "type": "hide", + "loc": [ + [ + 1, + 6 + ], + [ + 0, + 7 + ], + [ + 2, + 7 + ], + [ + 1, + 8 + ] ] }, - "5,5": { - "floorId": "sample2", - "stair": "downFloor", - "direction": "up" + { + "type": "hide", + "loc": [ + 1, + 5 + ], + "time": 500 }, - "10,12": null + { + "type": "hide" + }, + { + "type": "setCurtain", + "color": [ + 0, + 0, + 0 + ], + "time": 1250 + }, + { + "type": "sleep", + "time": 700 + }, + { + "type": "changeFloor", + "floorId": "sample1", + "loc": [ + 1, + 11 + ], + "direction": "right", + "time": 1000 + }, + { + "type": "trigger", + "loc": [ + 2, + 11 + ] + } + ], + "2,11": [ + "\t[杰克,thief]喂!醒醒!快醒醒!", + { + "type": "setCurtain", + "time": 1500 + }, + "\t[hero]额,我这是在什么地方?", + "\t[杰克,thief]你被魔王抓了起来扔进了监狱,和我关在了一起,但是幸运的是我在昨天刚刚挖好一条越狱的暗道!", + { + "type": "openDoor", + "loc": [ + 3, + 11 + ] + }, + { + "type": "sleep", + "time": 300 + }, + "\t[杰克,thief]我先走了,祝你好运!", + { + "type": "move", + "time": 750, + "steps": [ + "right", + "right", + "down" + ] + }, + "上面是个move事件,可以对NPC等进行移动。\n详见样板中小偷事件的写法。", + "\t[hero]怎么跑的这么快..." + ], + "4,2": [ + "\t[老人,man]本塔的商店有两类,全局商店和非全局商店。\n\n所谓非全局商店,就类似于右下角那个卖钥匙的老人一样,一定要碰到才能触发事件。\n\n而全局商店,则能在快捷商店中直接使用。", + "\t[老人,man]要注册一个全局商店,你需要在 data.js 中,找到 shops,并在内添加你的商店信息。", + "\t[老人,man]商店信息添加后,可以在需要的事件处调用{\"type\": \"openShop\"}来打开你添加的全局商店。", + "\t[老人,man]在上面的例子里,左边是一个仿50层的金币商店,右边是一个仿24层的经验商店。\n\n商店被访问后即可在快捷商店中进行使用。", + "\t[老人,man]如果你需要在某层暂时禁用快捷商店,可以在data.js中设置cannotUseQuickShop。\n如果需要永久禁用商店,请使用{\"type\":\"disableShop\"}", + { + "type": "hide", + "time": 500 + } + ], + "1,0": [ + { + "type": "openShop", + "id": "moneyShop1" + } + ], + "5,0": [ + { + "type": "openShop", + "id": "expShop1" + } + ], + "7,7": [ + "\t[老人,man]这是一个典型的杀怪开门、强制战斗事件。", + { + "type": "hide" + } + ], + "8,7": { + "enable": false, + "data": [] }, - "afterBattle": { - "9,6": [ - { - "type": "setValue", - "name": "flag:door", - "value": "flag:door+1" - }, - { - "type": "if", - "condition": "flag:door==2", - "true": [ - { - "type": "openDoor", - "loc": [ - 10, - 5 - ] - } - ], - "false": [] - } - ], - "11,6": [ - { - "type": "setValue", - "name": "flag:door", - "value": "flag:door+1" - }, - { - "type": "if", - "condition": "flag:door==2", - "true": [ - { - "type": "openDoor", - "loc": [ - 10, - 5 - ] - } - ], - "false": [] - } - ], - "10,12": null + "9,7": [ + { + "type": "show", + "loc": [ + 8, + 7 + ] + }, + { + "type": "hide" + } + ], + "10,4": [ + "\t[blackKing]你终于还是来了。", + "\t[hero]放开我们的公主!", + "\t[blackKing]如果我不愿意呢?", + "\t[hero]无需多说,拔剑吧!", + { + "type": "battle", + "id": "blackKing" + }, + { + "type": "hide", + "loc": [ + 10, + 2 + ] + }, + { + "type": "openDoor", + "loc": [ + 8, + 7 + ] + }, + "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", + { + "type": "hide" + } + ], + "10,0": [ + "\t[hero]公主,我来救你了~", + "\t[公主,princess]快救我出去!我受够这里了!", + "\t[hero]公主别怕,我们走吧~", + { + "type": "win", + "reason": "救出公主" + } + ], + "6,12": { + "enable": false, + "data": [] }, - "afterGetItem": {}, - "afterOpenDoor": {}, - "cannotMove": {}, - "bgmap": [ + "6,11": [ + "\t[仙子,fairy]通过调用 {\"type\": \"show\"} 可以使隐藏的事件显示出来。\n比如我下面这个机关门。", + { + "type": "show", + "loc": [ + 6, + 12 + ] + }, + "\t[仙子,fairy]通过调用 {\"type\": \"openDoor\"} 可以无需钥匙打开一扇门或暗墙。", + { + "type": "openDoor", + "loc": [ + 6, + 12 + ] + }, + "\t[仙子,fairy]同时,也可以对其它层进行操作,比如楼下的机关门,现在已经为你打开了。", + { + "type": "openDoor", + "loc": [ + 11, + 10 + ], + "floorId": "sample0" + }, + "\t[仙子,fairy]如果 show 或 hide 指定了 time 参数,则以动画效果显示,指定的参数作为消失时间(毫秒)来计算。", + "\t[仙子,fairy]现在到楼下来找我吧~", + { + "type": "show", + "loc": [ + 12, + 10 + ], + "floorId": "sample0" + }, + { + "type": "hide", + "time": 500 + } + ], + "8,11": [ + { + "type": "setValue", + "name": "flag:man_times", + "value": "flag:man_times+1" + }, + "\t[老人,man]在文字中使用${' ${ '}和 } 可以计算并显示一个表达式的结果。\n", + "\t[老人,man]例如:\n你的当前攻击力是${status:atk},防御力是${status:def}。\n攻防和的十倍是${10*(status:atk+status:def)},攻防之积是${status:atk*status:def}。\n你有${item:yellowKey}把黄钥匙,${item:blueKey}把蓝钥匙,${item:redKey}把红钥匙。\n你有${item:pickaxe}个破,${item:bomb}个炸,${item:centerFly}个飞。\n这是你第${flag:man_times}次和我对话。", + "\t[老人,man]同时,你也可以通过{\"type\": \"setValue\"}来设置一个勇士的属性、道具,或某个Flag。", + "\t[老人,man]例如:\n现在我将让你的攻防提升50%,再将攻防和的十倍加到生命值上。", + { + "type": "setValue", + "name": "status:atk", + "value": "status:atk*1.5" + }, + { + "type": "setValue", + "name": "status:def", + "value": "status:def*1.5" + }, + { + "type": "setValue", + "name": "status:hp", + "value": "status:hp+10*(status:atk+status:def)" + }, + "\t[老人,man]再送你500金币,1000经验,1破2炸3飞!", + { + "type": "setValue", + "name": "status:money", + "value": "status:money+500" + }, + { + "type": "setValue", + "name": "status:experience", + "value": "status:experience+1000" + }, + { + "type": "setValue", + "name": "item:pickaxe", + "value": "item:pickaxe+1" + }, + { + "type": "setValue", + "name": "item:bomb", + "value": "item:bomb+2" + }, + { + "type": "setValue", + "name": "item:centerFly", + "value": "item:centerFly+3" + }, + "\t[老人,man]status:xxx 代表勇士的某个属性。\n其中xxx可取hp, atk, def, mdef, money,experience这几项。\n\nitem:xxx 代表勇士的某个道具的个数。\nxxx为道具ID,具体可参见items.js中的定义。\n\nflag:xxx 代表某个自定义Flag或变量。\nxxx为Flag/变量名,可以自行定义,由字母、数字和下划线组成。\n未定义过而直接取用的Flag默认值为false。", + "\t[老人,man]你现在可以重新和我进行对话,进一步看到属性值的改变。" + ], + "10,11": [ + { + "type": "if", + "condition": "flag:woman_times==0", + "true": [ + "\t[老人,woman]这是个很复杂的例子,它将教会你如何使用if 语句进行条件判断,以及 choices 提供选项来供用户进行选择。", + "\t[老人,woman]第一次访问我将显示这段文字;从第二次开始将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后,将送你一把大黄门钥匙,并消失不再出现。", + "\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,是很容易看懂并理解的。" + ], + "false": [ + { + "type": "if", + "condition": "flag:woman_times==8", + "true": [ + "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", + "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", + { + "type": "setValue", + "name": "item:bigKey", + "value": "item:bigKey+1" + }, + "\t[老人,woman]我先走了,拜拜~", + { + "type": "hide", + "time": 500 + }, + { + "type": "exit" + } + ], + "false": [ + { + "type": "choices", + "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", + "choices": [ + { + "text": "黄钥匙(${9+flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=9+flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(9+flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:yellowKey", + "value": "item:yellowKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "蓝钥匙(${18+2*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=18+2*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(18+2*flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:blueKey", + "value": "item:blueKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "红钥匙(${36+4*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=36+4*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(36+4*flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:redKey", + "value": "item:redKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "setValue", + "name": "flag:woman_times", + "value": "flag:woman_times+1" + }, + { + "type": "revisit" + } + ], + "12,11": [ + "\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的JS脚本。\n本塔支持的所有主要API会在doc文档内给出。", + "\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后会将你的输入结果直接加到你的攻击力上。", + { + "type": "input", + "text": "请输入你要加攻击力的数值:" + }, + { + "type": "if", + "condition": "flag:input>0", + "true": [ + { + "type": "setValue", + "name": "status:atk", + "value": "status:atk+flag:input" + }, + { + "type": "tip", + "text": "操作成功,攻击+${flag:input}" + }, + "操作成功,攻击+${flag:input}" + ], + "false": [] + }, + "\t[老人,womanMagician]具体可参见样板中本事件的写法。" + ], + "10,12": null, + "12,12": [ + { + "type": "switch", + "condition": "flag:woman_times", + "caseList": [ + { + "case": "0", + "action": [ + "\t[老人,woman]现在使用switch改写这个例子" + ] + }, + { + "case": "8", + "action": [ + "\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话我会有危险的。", + "\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大黄门钥匙吧,希望你能好好用它。", + { + "type": "setValue", + "name": "item:bigKey", + "value": "item:bigKey+1" + }, + "\t[老人,woman]我先走了,拜拜~", + { + "type": "hide", + "time": 500 + }, + { + "type": "exit" + } + ] + }, + { + "case": "default", + "action": [ + { + "type": "comment", + "text": "当没有符合的值的场合执行此事件" + }, + { + "type": "choices", + "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的!", + "choices": [ + { + "text": "黄钥匙(${9+flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=9+flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(9+flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:yellowKey", + "value": "item:yellowKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "蓝钥匙(${18+2*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=18+2*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(18+2*flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:blueKey", + "value": "item:blueKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "红钥匙(${36+4*flag:woman_times}金币)", + "action": [ + { + "type": "if", + "condition": "status:money>=36+4*flag:woman_times", + "true": [ + { + "type": "setValue", + "name": "status:money", + "value": "status:money-(36+4*flag:woman_times)" + }, + { + "type": "setValue", + "name": "item:redKey", + "value": "item:redKey+1" + } + ], + "false": [ + "\t[老人,woman]你的金钱不足!", + { + "type": "revisit" + } + ] + } + ] + }, + { + "text": "离开", + "action": [ + { + "type": "exit" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "setValue", + "name": "flag:woman_times", + "value": "flag:woman_times+1" + }, + { + "type": "revisit" + } + ] +}, +"changeFloor": { + "4,12": { + "floorId": "sample0", + "loc": [ + 6, + 0 + ] + }, + "5,5": { + "floorId": "sample2", + "stair": "downFloor", + "direction": "up" + }, + "10,12": null +}, +"afterBattle": { + "9,6": [ + { + "type": "setValue", + "name": "flag:door", + "value": "flag:door+1" + }, + { + "type": "if", + "condition": "flag:door==2", + "true": [ + { + "type": "openDoor", + "loc": [ + 10, + 5 + ] + } + ], + "false": [] + } + ], + "11,6": [ + { + "type": "setValue", + "name": "flag:door", + "value": "flag:door+1" + }, + { + "type": "if", + "condition": "flag:door==2", + "true": [ + { + "type": "openDoor", + "loc": [ + 10, + 5 + ] + } + ], + "false": [] + } + ] +}, +"afterGetItem": {}, +"afterOpenDoor": {}, +"cannotMove": {}, +"bgmap": [ ], - "fgmap": [ +"fgmap": [ -] +], } \ No newline at end of file diff --git a/project/functions.js b/project/functions.js index f0561da5..4e4c0762 100644 --- a/project/functions.js +++ b/project/functions.js @@ -1,68 +1,81 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { "events": { - "initGame": function() { - // 游戏开始前的一些初始化操作 + "resetGame": function (hero, hard, floorId, maps, values) { + // 重置整个游戏;此函数将在游戏开始时,或者每次读档时最先被调用 + // hero:勇士信息;hard:难度;floorId:当前楼层ID;maps:地图信息;values:全局数值信息 - // 根据flag来对道具进行修改 - if (core.flags.bigKeyIsBox) - core.material.items.bigKey = {'cls': 'items', 'name': '钥匙盒'}; - // 面前的墙?四周的墙? - if (core.flags.pickaxeFourDirections) - core.material.items.pickaxe.text = "可以破坏勇士四周的墙"; - if (core.flags.bombFourDirections) - core.material.items.bomb.text = "可以炸掉勇士四周的怪物"; - if (core.flags.snowFourDirections) - core.material.items.bomb.text = "可以将四周的熔岩变成平地"; - // 是否启用装备栏 - if (core.flags.equipboxButton) { - core.statusBar.image.fly.src = core.statusBar.icons.equipbox.src; - core.flags.equipment = true; - } - if (core.flags.equipment) { - core.material.items.sword1.cls = 'equips'; - core.material.items.sword2.cls = 'equips'; - core.material.items.sword3.cls = 'equips'; - core.material.items.sword4.cls = 'equips'; - core.material.items.sword5.cls = 'equips'; - core.material.items.shield1.cls = 'equips'; - core.material.items.shield2.cls = 'equips'; - core.material.items.shield3.cls = 'equips'; - core.material.items.shield4.cls = 'equips'; - core.material.items.shield5.cls = 'equips'; - } + // 清除游戏数据 + // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 + core.clearStatus(); + // 初始化status + core.status = core.clone(core.initStatus); + core.status.played = true; + // 初始化人物,图标,统计信息 + core.status.hero = core.clone(hero); + core.events.setHeroIcon(core.getFlag('heroIcon', 'hero.png'), true); + core.control._initStatistics(core.animateFrame.totalTime); + core.status.hero.statistics.totalTime = core.animateFrame.totalTime = + Math.max(core.status.hero.statistics.totalTime, core.animateFrame.totalTime); + core.status.hero.statistics.start = null; + // 初始难度 + core.status.hard = hard || ""; + // 初始化地图 + core.status.floorId = floorId; + core.status.maps = core.clone(maps); + // 初始化怪物和道具 + core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + core.items._resetItems(); + // 初始化全局数值和全局开关 + core.values = core.clone(core.data.values); + for (var key in values || {}) + core.values[key] = values[key]; + core.flags = core.clone(core.data.flags); + var globalFlags = core.getFlag("globalFlags", {}); + for (var key in globalFlags) + core.flags[key] = globalFlags[key]; + core._init_sys_flags(); + // 初始化界面,状态栏等 + core.resize(); + core.updateGlobalAttribute(); + // 状态栏是否显示 + if (core.hasFlag('hideStatusBar')) + core.hideStatusBar(core.hasFlag('showToolbox')); + else + core.showStatusBar(); + // 隐藏右下角的音乐按钮 + core.dom.musicBtn.style.display = 'none'; }, - "setInitData": function (hard) { + "setInitData": function () { // 不同难度分别设置初始属性 - if (hard == 'Easy') { // 简单难度 + if (core.status.hard == 'Easy') { // 简单难度 core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度 // 可以在此设置一些初始福利,比如设置初始生命值可以调用: // core.setStatus("hp", 10000); // 赠送一把黄钥匙可以调用 // core.setItem("yellowKey", 1); } - if (hard == 'Normal') { // 普通难度 + if (core.status.hard == 'Normal') { // 普通难度 core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度 } - if (hard == 'Hard') { // 困难难度 + if (core.status.hard == 'Hard') { // 困难难度 core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度 } - if (hard == 'Hell') { // 噩梦难度 + if (core.status.hard == 'Hell') { // 噩梦难度 core.setFlag('hard', 4); // 可以用flag:hard来获得当前难度 } - // 设置三围的初始增幅属性(均为1) - ["atk", "def", "mdef"].forEach(function (name) { - core.setFlag("__" + name + "_buff__", 1); - }); + // 设置已经到过的楼层 + core.setFlag("__visited__", {}); - core.events.afterLoadData(); + core.updateEnemys(); }, "win": function(reason, norank) { // 游戏获胜事件 core.ui.closePanel(); var replaying = core.isReplaying(); - core.stopReplay(); + if (replaying) core.stopReplay(); core.waitHeroToStop(function() { core.clearMap('all'); // 清空全地图 core.deleteAllCanvas(); // 删除所有创建的画布 @@ -107,8 +120,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 检查重生怪并重置 if (!fromLoad) { core.status.maps[floorId].blocks.forEach(function(block) { - if (block.disable && core.isset(block.event) && block.event.cls.indexOf('enemy')==0 && - core.enemys.hasSpecial(core.material.enemys[block.event.id].special, 23)) { + if (block.disable && core.enemys.hasSpecial(block.event.id, 23)) { block.disable = false; } }); @@ -121,25 +133,23 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.drawMap(floorId); // 切换楼层BGM - if (core.isset(core.status.maps[floorId].bgm)) { + if (core.status.maps[floorId].bgm) { var bgm = core.status.maps[floorId].bgm; if (bgm instanceof Array) bgm = bgm[0]; core.playBgm(bgm); } // 更改画面色调 var color = core.getFlag('__color__', null); - if (!core.isset(color) && core.isset(core.status.maps[floorId].color)) + if (!color && core.status.maps[floorId].color) color = core.status.maps[floorId].color; core.clearMap('curtain'); core.status.curtainColor = color; - if (core.isset(color)) { - core.fillRect('curtain',0,0,416,416,core.arrayToRGBA(color)); - } + if (color) core.fillRect('curtain', 0, 0, core.__PIXELS__, core.__PIXELS__, core.arrayToRGBA(color)); // 更改天气 var weather = core.getFlag('__weather__', null); - if (!core.isset(weather) && core.isset(core.status.maps[floorId].weather)) + if (!weather && core.status.maps[floorId].weather) weather = core.status.maps[floorId].weather; - if (core.isset(weather)) + if (weather) core.setWeather(weather[0], weather[1]); else core.setWeather(); @@ -150,33 +160,76 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 转换楼层结束的事件;此函数会在整个楼层切换完全结束后再执行 // floorId是切换到的楼层;fromLoad若为true则代表是从读档行为造成的楼层切换 - // 如果是读档,则进行检查 + // 如果是读档,则进行检查(是否需要恢复事件) if (fromLoad) { core.events.recoverEvents(core.getFlag("__events__")); core.removeFlag("__events__"); - } - else { + } else { // 每次抵达楼层执行的事件 core.insertAction(core.floors[floorId].eachArrive); // 首次抵达楼层时执行的事件(后插入,先执行) - var visited = core.getFlag("__visited__", []); - if (visited.indexOf(floorId)===-1) { + if (!core.hasVisitedFloor(floorId)) { core.insertAction(core.floors[floorId].firstArrive); - visited.push(floorId); - core.setFlag("__visited__", visited); + core.visitFloor(floorId); } } }, - "addPoint": function (enemy) { - // 加点事件 - var point = enemy.point; - if (!core.flags.enableAddPoint || !core.isset(point) || point<=0) return []; + "flyTo": function (toId, callback) { + // 楼层传送器的使用,从当前楼层飞往toId + // 如果不能飞行请返回false - // 加点,返回一个choices事件 - // ----- 从V2.5.4开始,移动到“公共事件-加点事件”中 - core.setFlag('point', point); // 设置flag:point - return core.getCommonEvent('加点事件'); + var fromId = core.status.floorId; + + // 检查能否飞行 + if (!core.status.maps[fromId].canFlyTo || !core.status.maps[toId].canFlyTo) { + core.drawTip("无法飞往" + core.status.maps[toId].title + "!"); + return false; + } + + // 获得两个楼层的索引,以决定是上楼梯还是下楼梯 + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + var stair = fromIndex <= toIndex ? "downFloor" : "upFloor"; + // 地下层:同层传送至上楼梯 + if (fromIndex == toIndex && core.status.maps[fromId].underGround) stair = "upFloor"; + // 记录录像 + core.status.route.push("fly:" + toId); + // 传送 + core.ui.closePanel(); + core.changeFloor(toId, stair, null, null, callback); + + return true; +}, + "beforeBattle": function (enemyId, x, y) { + // 战斗前触发的事件,可以加上一些战前特效(详见下面支援的例子) + // 此函数在“检测能否战斗和自动存档”【之后】执行。如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 + // 返回true则将继续战斗,返回false将不再战斗。 + + // ------ 支援技能 ------ // + if (x != null && y != null) { + var index = x + "," + y, + cache = core.status.checkBlock.cache[index] || {}, + guards = cache.guards || []; + // 如果存在支援怪 + if (guards.length > 0) { + // 记录flag,当前要参与支援的怪物 + core.setFlag("__guards__" + x + "_" + y, guards); + var actions = []; + // 增加支援的特效动画(图块跳跃) + guards.forEach(function (g) { + core.push(actions, { "type": "jump", "from": [g[0], g[1]], "to": [x, y], "time": 300, "keep": false, "async": true }); + }); + core.push(actions, [ + { "type": "waitAsync" }, // 等待所有异步事件执行完毕 + { "type": "trigger", "loc": [x, y] } // 重要!重新触发本点事件(即重新触发战斗) + ]); + core.insertAction(actions); + return false; + } + } + + return true; }, "afterBattle": function (enemyId, x, y, callback) { // 战斗结束后触发的事件 @@ -186,12 +239,13 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 播放战斗音效和动画 var equipAnimate = 'hand', equipId = (core.status.hero.equipment || [])[0]; - if (core.isset(equipId) && core.isset((core.material.items[equipId].equip || {}).animate)) + if (equipId && (core.material.items[equipId].equip || {}).animate) equipAnimate = core.material.items[equipId].equip.animate; // 检查equipAnimate是否存在SE,如果不存在则使用默认音效 - if (!core.isset((core.material.animates[equipAnimate] || {}).se)) + if (!(core.material.animates[equipAnimate] || {}).se) core.playSound('attack.mp3'); - core.drawAnimate(equipAnimate, x, y); + // 强制战斗的战斗动画 + core.drawAnimate(equipAnimate, x != null ? x : core.getHeroLoc('x'), y != null ? y : core.getHeroLoc('y')); var damage = core.enemys.getDamage(enemyId, x, y); if (damage == null) damage = core.status.hero.hp + 1; @@ -212,33 +266,31 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 删除该块 var guards = []; // 支援 - if (core.isset(x) && core.isset(y)) { + if (x != null && y != null) { core.removeBlock(x, y); guards = core.getFlag("__guards__" + x + "_" + y, []); core.removeFlag("__guards__" + x + "_" + y); } // 获得金币和经验 - var money = enemy.money; - guards.forEach(function (g) { - money += core.material.enemys[g[2]].money; - }); + var money = guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].money; + }, enemy.money); if (core.hasItem('coin')) money *= 2; if (core.hasFlag('curse')) money = 0; core.status.hero.money += money; core.status.hero.statistics.money += money; - var experience = enemy.experience; - guards.forEach(function (g) { - experience += core.material.enemys[g[2]].experience; - }) + + var experience = guards.reduce(function (curr, g) { + return curr + core.material.enemys[g[2]].experience; + }, enemy.experience); if (core.hasFlag('curse')) experience = 0; core.status.hero.experience += experience; core.status.hero.statistics.experience += experience; + var hint = "打败 " + enemy.name; - if (core.flags.enableMoney) - hint += ",金币+" + money; - if (core.flags.enableExperience) - hint += ",经验+" + experience; + if (core.flags.enableMoney) hint += ",金币+" + money; + if (core.flags.enableExperience) hint += ",经验+" + experience; core.drawTip(hint); // 事件的处理 @@ -247,22 +299,19 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var special = enemy.special; // 中毒 if (core.enemys.hasSpecial(special, 12)) { - core.push(todo, [{ "type": "setValue", "name": "flag:debuff", "value": "'poison'" }]); - core.push(todo, [{ "type": "insert", "name": "毒衰咒处理" }]); + core.push(todo, [{ "type": "insert", "name": "毒衰咒处理", "args": [0] }]); } // 衰弱 if (core.enemys.hasSpecial(special, 13)) { - core.push(todo, [{ "type": "setValue", "name": "flag:debuff", "value": "'weak'" }]); - core.push(todo, [{ "type": "insert", "name": "毒衰咒处理" }]); + core.push(todo, [{ "type": "insert", "name": "毒衰咒处理", "args": [1] }]); } // 诅咒 if (core.enemys.hasSpecial(special, 14)) { - core.push(todo, [{ "type": "setValue", "name": "flag:debuff", "value": "'curse'" }]); - core.push(todo, [{ "type": "insert", "name": "毒衰咒处理" }]); + core.push(todo, [{ "type": "insert", "name": "毒衰咒处理", "args": [2] }]); } // 仇恨属性:减半 if (core.flags.hatredDecrease && core.enemys.hasSpecial(special, 17)) { - core.setFlag('hatred', parseInt(core.getFlag('hatred', 0) / 2)); + core.setFlag('hatred', Math.floor(core.getFlag('hatred', 0) / 2)); } // 自爆 if (core.enemys.hasSpecial(special, 19)) { @@ -293,15 +342,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 如果有加点 var point = core.material.enemys[enemyId].point; - if (core.flags.enableAddPoint && core.isset(point) && point > 0) { - core.push(todo, [{ "type": "setValue", "name": "flag:point", "value": point }]); - core.push(todo, [{ "type": "insert", "name": "加点事件" }]); + if (core.flags.enableAddPoint && point > 0) { + core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); } - // 如果该点存在,且有事件 -- V2.5.4 以后阻击怪也可以有战后事件了 - if (core.isset(x) && core.isset(y)) { - core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]); - } + // 如果该点存在事件 -- V2.5.4 以后阻击怪也可以有战后事件了 + core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]); // 在这里增加其他的自定义事件需求 /* @@ -313,59 +359,44 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ // 如果事件不为空,将其插入 - if (todo.length > 0) { - core.events.insertAction(todo, x, y); - } + if (todo.length > 0) core.insertAction(todo, x, y); // 如果已有事件正在处理中 - if (core.status.event.id == null) { + if (core.status.event.id == null) core.continueAutomaticRoute(); - } else { + else core.clearContinueAutomaticRoute(); - } - if (core.isset(callback)) callback(); + + if (callback) callback(); }, - "afterOpenDoor": function(doorId,x,y,callback) { + "afterOpenDoor": function (doorId, x, y, callback) { // 开一个门后触发的事件 - + var todo = []; - if (core.isset(x) && core.isset(y)) { - var event = core.floors[core.status.floorId].afterOpenDoor[x+","+y]; - if (core.isset(event)) { - core.unshift(todo, event); - } - } + var event = core.floors[core.status.floorId].afterOpenDoor[x + "," + y]; + if (event) core.unshift(todo, event); - if (todo.length>0) { - core.events.insertAction(todo,x,y); - } + if (todo.length > 0) core.insertAction(todo, x, y); - if (core.status.event.id == null) { + if (core.status.event.id == null) core.continueAutomaticRoute(); - } - else { + else core.clearContinueAutomaticRoute(); - } - if (core.isset(callback)) callback(); + + if (callback) callback(); }, - "afterGetItem": function(itemId,x,y,callback) { + "afterGetItem": function (itemId, x, y, callback) { // 获得一个道具后触发的事件 core.playSound('item.mp3'); var todo = []; - if (core.isset(x) && core.isset(y)) { - var event = core.floors[core.status.floorId].afterGetItem[x+","+y]; - if (core.isset(event)) { - core.unshift(todo, event); - } - } + var event = core.floors[core.status.floorId].afterGetItem[x + "," + y]; + if (event) core.unshift(todo, event); - if (todo.length>0) { - core.events.insertAction(todo,x,y); - } + if (todo.length > 0) core.insertAction(todo, x, y); - if (core.isset(callback)) callback(); + if (callback) callback(); }, "afterChangeLight": function(x,y) { // 改变亮灯之后,可以触发的事件 @@ -373,17 +404,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = }, "afterPushBox": function () { // 推箱子后的事件 - - var noBoxLeft = function () { - // 地图上是否还存在未推到的箱子,如果不存在则返回true,存在则返回false - for (var i=0;i=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:回放录像 - if (core.hasFlag('debug')) { - core.drawText("\t[系统提示]调试模式下无法回放录像"); + 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('当前不能使用破墙镐'); } - else { - core.ui.drawReplay(); + } + 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('当前不能使用炸弹'); } - 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.status.event.selection=1; - core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { - core.ui.closePanel(); - core.restart(); - }, function () { - core.ui.closePanel(); - }); - break; - case 79: // O:查看工程 - window.open(core.platform.isPC?"editor.html":"editor-mobile.html", "_blank"); - break; - case 80: // P:游戏主页 - window.open("/score.php?name="+core.firstData.name+"&num=10", "_blank"); - break; - case 49: // 快捷键1: 破 - if (core.hasItem('pickaxe')) { - if (core.canUseItem('pickaxe')) { - core.useItem('pickaxe'); - } - else { - core.drawTip('当前不能使用破墙镐'); - } - } - break; - case 50: // 快捷键2: 炸 - if (core.hasItem('bomb')) { - if (core.canUseItem('bomb')) { - core.useItem('bomb'); - } - 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.events.tryUseItem('centerFly'); - } - break; - case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... 其他道具依次判断 - { - var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; - for (var i=0;i= 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) { // 可以用flag:skill表示当前开启的技能类型,flag:skillName显示技能名;详见文档-个性化-技能塔的支持 - core.statusBar.skill.innerHTML = core.getFlag('skillName', '无'); + core.setStatusBarInnerHTML('skill', core.getFlag('skillName', '无')); } // 可以在这里添加自己额外的状态栏信息,比如想攻击显示 +0.5 可以这么写: - // if (core.hasFlag('halfAtk')) core.statusBar.atk.innerHTML += "+0.5"; + // if (core.hasFlag('halfAtk')) core.setStatusBarInnerHTML('atk', core.statusBar.atk.innerText + "+0.5"); // 如果是自定义添加的状态栏,也需要在这里进行设置显示的数值 @@ -984,32 +1035,32 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (core.flags.enableLevelUp && core.status.hero.lv < core.firstData.levelUp.length) { var need = core.calValue(core.firstData.levelUp[core.status.hero.lv].need); if (core.flags.levelUpLeftMode) - core.statusBar.up.innerHTML = core.formatBigNumber(need - core.getStatus('experience')) || " "; + core.setStatusBarInnerHTML('up', core.formatBigNumber(need - core.getStatus('experience')) || ""); else - core.statusBar.up.innerHTML = core.formatBigNumber(need) || " "; - } else core.statusBar.up.innerHTML = " "; + core.setStatusBarInnerHTML('up', core.formatBigNumber(need) || ""); + } else core.setStatusBarInnerHTML('up', ""); // 钥匙 var keys = ['yellowKey', 'blueKey', 'redKey']; keys.forEach(function (key) { - core.statusBar[key].innerHTML = core.setTwoDigits(core.status.hero.items.keys[key]); + core.setStatusBarInnerHTML(key, core.setTwoDigits(core.itemCount(key))); }); // 毒衰咒 if (core.flags.enableDebuff) { - core.statusBar.poison.innerHTML = core.hasFlag('poison') ? "毒" : ""; - core.statusBar.weak.innerHTML = core.hasFlag('weak') ? "衰" : ""; - core.statusBar.curse.innerHTML = core.hasFlag('curse') ? "咒" : ""; + core.setStatusBarInnerHTML('poison', core.hasFlag('poison') ? "毒" : ""); + core.setStatusBarInnerHTML('weak', core.hasFlag('weak') ? "衰" : ""); + core.setStatusBarInnerHTML('curse', core.hasFlag('curse') ? "咒" : ""); } // 破炸飞 if (core.flags.enablePZF) { - core.statusBar.pickaxe.innerHTML = "破" + core.itemCount('pickaxe'); - core.statusBar.bomb.innerHTML = "炸" + core.itemCount('bomb'); - core.statusBar.fly.innerHTML = "飞" + core.itemCount('centerFly'); + core.setStatusBarInnerHTML('pickaxe', "破" + core.itemCount('pickaxe')); + core.setStatusBarInnerHTML('bomb', "炸" + core.itemCount('bomb')); + core.setStatusBarInnerHTML('fly', "飞" + core.itemCount('centerFly')); } // 难度 core.statusBar.hard.innerHTML = core.status.hard; - // 状态栏绘制 + // 自定义状态栏绘制 core.drawStatusBar(); // 更新阻激夹域的伤害值 @@ -1017,200 +1068,281 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 更新全地图显伤 core.updateDamage(); }, - "updateCheckBlock": function () { + "updateCheckBlock": function (floorId) { // 领域、夹击、阻击等的伤害值计算 + floorId = floorId || core.status.floorId; + if (!floorId || !core.status.maps) return; - core.status.checkBlock = {}; - if (!core.isset(core.status.thisMap)) return; - var blocks = core.status.thisMap.blocks; + var width = core.floors[floorId].width, + height = core.floors[floorId].height; + var blocks = core.getMapBlocksObj(floorId); + + var damage = {}, // 每个点的伤害值 + type = {}, // 每个点的伤害类型 + snipe = {}, // 每个点的阻击怪信息 + ambush = {}; // 每个点的捕捉信息 + + // 计算血网和领域、阻击、激光的伤害,计算捕捉信息 + for (var loc in blocks) { + var block = blocks[loc], + x = block.x, + y = block.y, + id = block.event.id, + enemy = core.material.enemys[id]; - // Step1: 更新怪物地图 - core.status.checkBlock.map = []; // 记录怪物地图 - for (var n = 0; n < blocks.length; n++) { - var block = blocks[n]; - if (core.isset(block.event) && !block.disable && block.event.cls.indexOf('enemy') == 0) { - var id = block.event.id, - enemy = core.material.enemys[id]; - if (core.isset(enemy)) { - core.status.checkBlock.map[block.x + core.bigmap.width * block.y] = id; - } - } // 血网 - if (core.isset(block.event) && !block.disable && - block.event.id == 'lavaNet' && block.event.trigger == 'passNet' && !core.hasItem("shoes")) { - core.status.checkBlock.map[block.x + core.bigmap.width * block.y] = "lavaNet"; + if (id == 'lavaNet' && block.event.trigger == 'passNet' && !core.hasItem('shoes')) { + damage[loc] = (damage[loc] || 0) + core.values.lavaDamage; + type[loc] = "血网伤害"; } - } - // Step2: 更新领域、阻击伤害 - core.status.checkBlock.damage = []; // 记录(x,y)点的伤害;(x,y)对应的值是 x+core.bigmap.width*y - for (var x = 0; x < core.bigmap.width * core.bigmap.height; x++) core.status.checkBlock.damage[x] = 0; - core.status.checkBlock.ambush = []; + // 领域 + // 如果要防止领域伤害,可以直接简单的将 flag:no_zone 设为true + if (enemy && core.hasSpecial(enemy.special, 15) && !core.hasFlag('no_zone')) { + // 领域范围,默认为1 + var range = enemy.range || 1; + // 是否是九宫格领域 + var zoneSquare = false; + if (enemy.zoneSquare != null) zoneSquare = enemy.zoneSquare; + // 在范围内进行搜索,增加领域伤害值 + for (var dx = -range; dx <= range; dx++) { + for (var dy = -range; dy <= range; dy++) { + if (dx == 0 && dy == 0) continue; + var nx = x + dx, + ny = y + dy, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + // 如果是十字领域,则还需要满足 |dx|+|dy|<=range + if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); + type[currloc] = "领域伤害"; + } + } + } - for (var x = 0; x < core.bigmap.width; x++) { - for (var y = 0; y < core.bigmap.height; y++) { - var id = core.status.checkBlock.map[x + core.bigmap.width * y]; - if (core.isset(id)) { + // 阻击 + // 如果要防止阻击伤害,可以直接简单的将 flag:no_snipe 设为true + if (enemy && core.hasSpecial(enemy.special, 18) && !core.hasFlag('no_snipe')) { + for (var dir in core.utils.scan) { + var nx = x + core.utils.scan[dir].x, + ny = y + core.utils.scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); + type[currloc] = "阻击伤害"; - // 如果是血网,直接加上伤害值 - if (id == "lavaNet") { - core.status.checkBlock.damage[x + core.bigmap.width * y] += core.values.lavaDamage || 0; - continue; + var rdir = core.reverseDirection(dir); + // 检查下一个点是否存在事件(从而判定是否移动) + var rnx = x + core.utils.scan[rdir].x, + rny = y + core.utils.scan[rdir].y; + if (rnx >= 0 && rnx < width && rny >= 0 && rny < height && core.getBlock(rnx, rny, floorId) == null) { + snipe[currloc] = (snipe[currloc] || []).concat([ + [x, y, id, rdir] + ]); } + } + } - var enemy = core.material.enemys[id]; - // 存在领域 - // 如果要防止领域伤害,可以直接简单的将 flag:no_zone 设为true - if (core.enemys.hasSpecial(enemy.special, 15) && !core.hasFlag("no_zone")) { - // 领域范围,默认为1 - var range = enemy.range || 1; - // 是否是九宫格领域 - var zoneSquare = false; - if (core.isset(enemy.zoneSquare)) zoneSquare = enemy.zoneSquare; - // 在范围内进行搜索,增加领域伤害值 - for (var dx = -range; dx <= range; dx++) { - for (var dy = -range; dy <= range; dy++) { - if (dx == 0 && dy == 0) continue; - var nx = x + dx, - ny = y + dy; - if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height) continue; - // 如果是十字领域,则还需要满足 |dx|+|dy|<=range - if (!zoneSquare && Math.abs(dx) + Math.abs(dy) > range) continue; - core.status.checkBlock.damage[nx + ny * core.bigmap.width] += enemy.value || 0; - } - } + // 激光 + // 如果要防止激光伤害,可以直接简单的将 flag:no_laser 设为true + if (enemy && core.hasSpecial(enemy.special, 24) && !core.hasFlag("no_laser")) { + for (var nx = 0; nx < width; nx++) { + var currloc = nx + "," + y; + if (nx != x) { + damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); + type[currloc] = "激光伤害"; } - // 存在激光 - // 如果要防止激光伤害,可以直接简单的将 flag:no_laser 设为true - if (core.enemys.hasSpecial(enemy.special, 24) && !core.hasFlag("no_laser")) { - // 检查同行和同列,增加激光伤害值 - for (var nx = 0; nx < core.bigmap.width; nx++) { - if (nx != x) core.status.checkBlock.damage[nx + y * core.bigmap.width] += enemy.value || 0; - } - for (var ny = 0; ny < core.bigmap.height; ny++) { - if (ny != y) core.status.checkBlock.damage[x + ny * core.bigmap.width] += enemy.value || 0; - } - } - // 存在阻击 - // 如果要防止阻击伤害,可以直接简单的将 flag:no_snipe 设为true - if (core.enemys.hasSpecial(enemy.special, 18) && !core.hasFlag("no_snipe")) { - for (var dx = -1; dx <= 1; dx++) { - for (var dy = -1; dy <= 1; dy++) { - if (dx == 0 && dy == 0) continue; - var nx = x + dx, - ny = y + dy; - if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height || Math.abs(dx) + Math.abs(dy) > 1) continue; - core.status.checkBlock.damage[nx + ny * core.bigmap.width] += enemy.value || 0; - } - } - } - // 存在捕捉 - if (core.enemys.hasSpecial(enemy.special, 27)) { - // 给周围格子加上【捕捉】记号 - for (var dir in core.utils.scan) { - var nx = x + core.utils.scan[dir].x, - ny = y + core.utils.scan[dir].y; - if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height) continue; - if (!core.isset(core.status.checkBlock.ambush[nx + ny * core.bigmap.width])) - core.status.checkBlock.ambush[nx + ny * core.bigmap.width] = []; - core.status.checkBlock.ambush[nx + ny * core.bigmap.width].push([x, y, id, dir]); - } + } + for (var ny = 0; ny < height; ny++) { + var currloc = x + "," + ny; + if (ny != y) { + damage[currloc] = (damage[currloc] || 0) + (enemy.value || 0); + type[currloc] = "激光伤害"; } } } + + // 捕捉 + // 如果要防止捕捉效果,可以直接简单的将 flag:no_ambush 设为true + if (enemy && core.enemys.hasSpecial(enemy.special, 27)) { + // 给周围格子加上【捕捉】记号 + for (var dir in core.utils.scan) { + var nx = x + core.utils.scan[dir].x, + ny = y + core.utils.scan[dir].y, + currloc = nx + "," + ny; + if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue; + ambush[currloc] = (ambush[currloc] || []).concat([ + [x, y, id, dir] + ]); + } + } } - // Step3: 更新夹击点坐标,并将夹击伤害加入到damage中 - core.status.checkBlock.betweenAttack = []; // 记录(x,y)点是否有夹击 + // 更新夹击伤害 // 如果要防止夹击伤害,可以简单的将 flag:no_betweenAttack 设为true if (!core.hasFlag('no_betweenAttack')) { - for (var x = 0; x < core.bigmap.width; x++) { - for (var y = 0; y < core.bigmap.height; y++) { - // 该点是否存在夹击 - var has = false; - // 检测左右是否存在相同的怪物,且拥有夹击属性 - if (x > 0 && x < core.bigmap.width - 1) { - var id1 = core.status.checkBlock.map[x - 1 + core.bigmap.width * y], - id2 = core.status.checkBlock.map[x + 1 + core.bigmap.width * y]; - if (core.isset(id1) && core.isset(id2) && id1 == id2) { - var enemy = core.material.enemys[id1]; - if (core.isset(enemy) && core.enemys.hasSpecial(enemy.special, 16)) { - has = true; - } - } + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var loc = x + "," + y; + // 夹击怪物的ID + var enemyId = null; + // 检查左右夹击 + var leftBlock = blocks[(x - 1) + "," + y], + rightBlock = blocks[(x + 1) + "," + y]; + if (leftBlock && rightBlock && leftBlock.id == rightBlock.id) { + if (core.hasSpecial(leftBlock.event.id, 16)) + enemyId = leftBlock.event.id; } - // 检测上下是否存在相同的怪物,且拥有夹击属性 - if (y > 0 && y < core.bigmap.height - 1) { - var id1 = core.status.checkBlock.map[x + core.bigmap.width * (y - 1)], - id2 = core.status.checkBlock.map[x + core.bigmap.width * (y + 1)]; - if (core.isset(id1) && core.isset(id2) && id1 == id2) { - var enemy = core.material.enemys[id1]; - if (core.isset(enemy) && core.enemys.hasSpecial(enemy.special, 16)) { - has = true; - } - } + // 检查上下夹击 + var topBlock = blocks[x + "," + (y - 1)], + bottomBlock = blocks[x + "," + (y + 1)]; + if (topBlock && bottomBlock && topBlock.id == bottomBlock.id) { + if (core.hasSpecial(topBlock.event.id, 16)) + enemyId = topBlock.event.id; } - // 计算夹击伤害 - if (has) { - core.status.checkBlock.betweenAttack[x + core.bigmap.width * y] = true; - // 先扣除该点领域/阻击/激光造成的伤害,再算夹击 - var leftHp = core.status.hero.hp - core.status.checkBlock.damage[x + core.bigmap.width * y]; - // 1血不夹;core.flags.betweenAttackCeil控制向上还是向下 - if (leftHp > 1) - core.status.checkBlock.damage[x + core.bigmap.width * y] += Math.floor((leftHp + (core.flags.betweenAttackCeil ? 0 : 1)) / 2); + + if (enemyId != null) { + var leftHp = core.status.hero.hp - (damage[x + "," + y] || 0); + if (leftHp > 1) { + // 上整/下整 + var value = Math.floor((leftHp + (core.flags.betweenAttackCeil ? 0 : 1)) / 2); + damage[loc] = (damage[loc] || 0) + value; + type[loc] = "夹击伤害"; + } } } } } + + core.status.checkBlock = { + damage: damage, + type: type, + snipe: snipe, + ambush: ambush, + cache: {} + }; }, - "moveOneStep": function () { - // 勇士每走一步后执行的操作 - core.status.hero.steps++; - // 中毒状态:扣血 - if (core.hasFlag('poison')) { - core.status.hero.statistics.poisonDamage += core.values.poisonDamage; - core.status.hero.hp -= core.values.poisonDamage; - if (core.status.hero.hp<=0) { - core.status.hero.hp=0; - core.updateStatusBar(); - core.events.lose(); - return; - } - core.updateStatusBar(); - } - // 备注:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法: + "moveOneStep": function (x, y) { + // 勇士每走一步后执行的操作,x,y为要移动到的坐标。 + // 这个函数执行在“刚走完”的时候,即还没有检查该点的事件和领域伤害等。 + // 请注意:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法: // 1. 将全塔属性中的cannotMoveDirectly这个开关勾上,即可在全塔中全程禁止使用瞬移。 // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 + // 设置当前坐标,增加步数 + core.setHeroLoc('x', x, true); + core.setHeroLoc('y', y, true); + core.status.hero.steps++; + // 更新跟随者状态,并绘制 + core.updateFollowers(); + core.drawHero(); + // 检查中毒状态的扣血和死亡 + if (core.hasFlag('poison')) { + core.status.hero.statistics.poisonDamage += core.values.poisonDamage; + core.status.hero.hp -= core.values.poisonDamage; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + return; + } + } + // 如需强行终止行走可以在这里条件判定: + // core.stopAutomaticRoute(); + + core.updateStatusBar(); +}, + "moveDirectly": function (x, y) { + // 瞬间移动;x,y为要瞬间移动的点 + // 返回true代表成功瞬移,false代表没有成功瞬移 + + // 判定能否瞬移到该点 + var ignoreSteps = core.canMoveDirectly(x, y); + if (ignoreSteps >= 0) { + core.clearMap('hero'); + // 获得勇士最后的朝向 + var lastDirection = core.status.route[core.status.route.length - 1]; + if (['left', 'right', 'up', 'down'].indexOf(lastDirection) >= 0) + core.setHeroLoc('direction', lastDirection); + // 设置坐标,并绘制 + core.setHeroLoc('x', x); + core.setHeroLoc('y', y); + core.drawHero(); + // 记录录像 + core.status.route.push("move:" + x + ":" + y); + // 统计信息 + core.status.hero.statistics.moveDirectly++; + core.status.hero.statistics.ignoreSteps += ignoreSteps; + return true; + } + return false; +}, + "parallelDo": function (timestamp) { + // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件 + // 该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次 + // 参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 + + // 检查当前是否处于游戏开始状态 + if (!core.isPlaying()) return; + + // 执行当前楼层的并行事件处理 + if (core.status.floorId) { + try { + eval(core.floors[core.status.floorId].parallelDo); + } catch (e) { + main.log(e); + } + } + + // 下面是一个并行事件开门的样例 + /* + // 如果某个flag为真 + if (core.hasFlag("xxx")) { + // 千万别忘了将该flag清空!否则下次仍然会执行这段代码。 + core.removeFlag("xxx"); + // 使用insertAction来插入若干自定义事件执行 + core.insertAction([ + {"type":"openDoor", "loc":[0,0], "floorId": "MT0"} + ]) + // 也可以写任意其他的脚本代码 + } + */ } }, "ui": { "drawStatusBar": function () { + // 自定义绘制状态栏,需要开启状态栏canvas化 + // 如果是非状态栏canvas化,直接返回 if (!core.flags.statusCanvas) return; - var canvas = core.dom.statusCanvas, ctx = canvas.getContext('2d'); + var canvas = core.dom.statusCanvas, + ctx = core.dom.statusCanvasCtx; // 清空状态栏 ctx.clearRect(0, 0, canvas.width, canvas.height); // 如果是隐藏状态栏模式,直接返回 if (!core.domStyle.showStatusBar) return; - + // 作为样板,只绘制楼层、生命、攻击、防御、魔防、金币、钥匙这七个内容 // 需要其他的请自行进行修改;横竖屏都需要进行适配绘制。 // (可以使用Chrome浏览器开控制台来模拟手机上的竖屏模式的显示效果,具体方式自行百度) // 横屏模式下的画布大小是 129*416 // 竖屏模式下的画布大小是 416*(32*rows+9) 其中rows为状态栏行数,即全塔属性中statusCanvasRowsOnMobile值 // 可以使用 core.domStyle.isVertical 来判定当前是否是竖屏模式 - + ctx.fillStyle = core.status.globalAttribute.statusBarColor || core.initStatus.globalAttribute.statusBarColor; ctx.font = 'italic bold 18px Verdana'; - + // 距离左侧边框6像素,上侧边框9像素,行距约为39像素 - var leftOffset = 6, topOffset = 9, lineHeight = 39; + var leftOffset = 6, + topOffset = 9, + lineHeight = 39; if (core.domStyle.isVertical) { // 竖屏模式,行高32像素 - leftOffset = 6; topOffset = 6; lineHeight = 32; + leftOffset = 6; + topOffset = 6; + lineHeight = 32; } - + var toDraw = ["floor", "hp", "atk", "def", "mdef", "money"]; for (var index = 0; index < toDraw.length; index++) { // 绘制下一个数据 @@ -1218,9 +1350,10 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 图片大小25x25 ctx.drawImage(core.statusBar.icons[name], leftOffset, topOffset, 25, 25); // 文字内容 - var text = (core.statusBar[name]||{}).innerText || " "; + var text = (core.statusBar[name] || {}).innerText || " "; // 斜体判定:如果不是纯数字和字母,斜体会非常难看,需要取消 - if (!/^[-+_.a-zA-Z0-9]*$/.test(text)) ctx.font = 'bold 18px Verdana'; + if (!/^[-a-zA-Z0-9`~!@#$%^&*()_=+\[{\]}\\|;:'",<.>\/?]*$/.test(text)) + ctx.font = 'bold 18px Verdana'; // 绘制文字 ctx.fillText(text, leftOffset + 36, topOffset + 20); ctx.font = 'italic bold 18px Verdana'; @@ -1232,8 +1365,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = leftOffset = 6; topOffset += lineHeight; } - } - else { + } else { // 横屏模式 topOffset += lineHeight; } @@ -1268,7 +1400,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.lockControl(); core.status.event.id = 'about'; - var left = 48, top = 36, right = 416 - 2 * left, bottom = 416 - 2 * top; + var left = 48, top = 36, right = core.__PIXELS__ - 2 * left, bottom = core.__PIXELS__ - 2 * top; core.setAlpha('ui', 0.85); core.fillRect('ui', left, top, right, bottom, '#000000'); @@ -1285,115 +1417,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.fillText('ui', "作者: 艾之葵", text_start, top + 112); core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top+112+32); // TODO: 写自己的“关于”页面,每次增加32像素即可 -} - }, - "plugins": { - "parallelDo": function (timestamp) { - // 并行事件处理,可以在这里写任何需要并行处理的脚本或事件 - // 该函数将被系统反复执行,每次执行间隔视浏览器或设备性能而定,一般约为16.6ms一次 - // 参数timestamp为“从游戏资源加载完毕到当前函数执行时”的时间差,以毫秒为单位 - - // 检查当前是否处于游戏开始状态 - if (!core.isPlaying()) return; - - // 执行当前楼层的并行事件处理 - if (core.isset(core.status.floorId)) { - try { - eval(core.floors[core.status.floorId].parallelDo); - } catch (e) { - main.log(e); - } - } - - // 下面是一个并行事件开门的样例 - /* - // 如果某个flag为真 - if (core.hasFlag("xxx")) { - // 千万别忘了将该flag清空!否则下次仍然会执行这段代码。 - core.removeFlag("xxx"); - // 使用insertAction来插入若干自定义事件执行 - core.insertAction([ - {"type":"openDoor", "loc":[0,0], "floorId": "MT0"} - ]) - // 也可以写任意其他的脚本代码 - } - */ - - -}, - "plugin": function () { - ////// 插件编写,可以在这里写自己额外需要执行的脚本 ////// - - // 在这里写的代码,在所有模块加载完毕后,游戏开始前会被执行 - console.log("插件编写测试"); - // 可以写一些其他的被直接执行的代码 - - // 在这里写所有需要自定义的函数 - // 写法必须是 this.xxx = function (args) { ... - // 如果不写this的话,函数将无法被外部所访问 - this.test = function () { - console.log("插件函数执行测试"); - }; - - - // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) - // 【参数说明】 - // name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建 - // color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。 - // lights:可选,一个数组,定义了每个独立的灯光。 - // 其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。 - // lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。 - // 比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 - // 【调用样例】 - // core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9,等价于更改画面色调为[0,0,0,0.9]。 - // core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 - // core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 - // core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 - // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,且40%后才开始衰减。 - this.drawLight = function (name, color, lights, lightDec) { - // 清空色调层;也可以修改成其它层比如animate/weather层,或者用自己创建的canvas - var ctx = core.getContextByName(name); - if (ctx == null) { - if (typeof name == 'string') - ctx = core.createCanvas(name, 0, 0, 416, 416, 98); - else return; - } - - ctx.mozImageSmoothingEnabled = false; - ctx.webkitImageSmoothingEnabled = false; - ctx.msImageSmoothingEnabled = false; - ctx.imageSmoothingEnabled = false; - - core.clearMap(name); - // 绘制色调层,默认不透明度 - if (!core.isset(color)) color = 0.9; - ctx.fillStyle = "rgba(0,0,0,"+color+")"; - ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); - - lightDec = core.clamp(lightDec, 0, 1); - - // 绘制每个灯光效果 - ctx.globalCompositeOperation = 'destination-out'; - lights.forEach(function (light) { - // 坐标,半径,中心不透明度 - var x = light[0], y = light[1], r = light[2]; - // 计算衰减距离 - var decDistance = parseInt(r * lightDec); - // 正方形区域的直径和左上角坐标 - var grd=ctx.createRadialGradient(x,y,decDistance,x,y,r); - grd.addColorStop(0, "rgba(0,0,0,1)"); - grd.addColorStop(1, "rgba(0,0,0,0)"); - ctx.beginPath(); - ctx.fillStyle=grd; - ctx.arc(x,y,r,0,2*Math.PI); - ctx.fill(); - }); - ctx.globalCompositeOperation = 'source-over'; - } - - - // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); - } } } \ No newline at end of file diff --git a/project/icons.js b/project/icons.js index 71bce353..0fe339c2 100644 --- a/project/icons.js +++ b/project/icons.js @@ -259,7 +259,7 @@ var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = "hammer": 48, "jumpShoes": 49, "skill1": 30, - "I73": 10 + "wand": 10 }, "autotile": { "autotile": 0, diff --git a/project/items.js b/project/items.js index 793296cb..efda5b71 100644 --- a/project/items.js +++ b/project/items.js @@ -303,7 +303,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "text": "可以打开或关闭主动技能二倍斩", "hideInReplay": true }, - "I73": { + "wand": { "cls": "items", "name": "新物品" } @@ -360,7 +360,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = }, "useItemEffect": { "book": "core.ui.drawBook(0);", - "fly": "core.ui.drawFly(core.status.hero.flyRange.indexOf(core.status.floorId));", + "fly": "core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));", "earthquake": "core.removeBlockByIds(core.status.floorId, core.status.event.ui);\ncore.drawMap(core.status.floorId, function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n});", "pickaxe": "core.playSound('pickaxe.mp3');\ncore.removeBlockByIds(core.status.floorId, core.status.event.ui);\ncore.drawMap(core.status.floorId, function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n});", "icePickaxe": "core.removeBlockByIds(core.status.floorId, core.status.event.ui);\ncore.drawMap(core.status.floorId, function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n});", @@ -372,9 +372,9 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "upFly": "var loc = {'direction': core.status.hero.loc.direction, 'x': core.status.event.ui.x, 'y': core.status.event.ui.y};\nif (core.status.event.id == 'action') {\n\tcore.insertAction([\n\t\t{\"type\": \"changeFloor\", \"loc\": [loc.x, loc.y], \"direction\": loc.direction, \"floorId\": core.status.event.ui.id},\n\t\t{\"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功'}\n\t]);\n}\nelse {\n\tcore.changeFloor(core.status.event.ui.id, null, loc, null, function (){\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\tcore.replay();\n\t});\n}", "downFly": "var loc = {'direction': core.status.hero.loc.direction, 'x': core.status.event.ui.x, 'y': core.status.event.ui.y};\nif (core.status.event.id == 'action') {\n\tcore.insertAction([\n\t\t{\"type\": \"changeFloor\", \"loc\": [loc.x, loc.y], \"direction\": loc.direction, \"floorId\": core.status.event.ui.id},\n\t\t{\"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功'}\n\t]);\n}\nelse {\n\tcore.changeFloor(core.status.event.ui.id, null, loc, null, function (){\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\tcore.replay();\n\t});\n}\n", "poisonWine": "core.removeFlag('poison');", - "weakWine": "core.removeFlag('weak');\nif (core.values.weakValue>=1) { // >=1:直接扣数值\n\tcore.status.hero.atk += core.values.weakValue;\n\tcore.status.hero.def += core.values.weakValue;\n}\nelse { // <1:扣比例\n\tcore.addFlag(\"__atk_buff__\", core.values.weakValue);\n\tcore.addFlag(\"__def_buff__\", core.values.weakValue);\n}", + "weakWine": "core.removeFlag('weak');\nif (core.values.weakValue>=1) { // >=1:直接扣数值\n\tcore.status.hero.atk += core.values.weakValue;\n\tcore.status.hero.def += core.values.weakValue;\n}\nelse { // <1:扣比例\n\tcore.addBuff(\"atk\", core.values.weakValue);\n\tcore.addBuff(\"def\", core.values.weakValue);\n}", "curseWine": "core.removeFlag('curse');", - "superWine": "core.removeFlag('poison');\nif (core.hasFlag('weak')) {\n\tcore.removeFlag('weak');\n\tif (core.values.weakValue>=1) { // >=1:直接扣数值\n\t\tcore.status.hero.atk += core.values.weakValue;\n\t\tcore.status.hero.def += core.values.weakValue;\n\t}\n\telse { // <1:扣比例\n\t\tcore.addFlag(\"__atk_buff__\", core.values.weakValue);\n\t\tcore.addFlag(\"__def_buff__\", core.values.weakValue);\n\t}\n}\ncore.removeFlag('curse');", + "superWine": "core.removeFlag('poison');\nif (core.hasFlag('weak')) {\n\tcore.removeFlag('weak');\n\tif (core.values.weakValue>=1) { // >=1:直接扣数值\n\t\tcore.status.hero.atk += core.values.weakValue;\n\t\tcore.status.hero.def += core.values.weakValue;\n\t}\n\telse { // <1:扣比例\n\t\tcore.addBuff(\"atk\", core.values.weakValue);\n\t\tcore.addBuff(\"def\", core.values.weakValue);\n\t}\n}\ncore.removeFlag('curse');", "lifeWand": "core.insertAction([\n\t{\"type\": \"input\", \"text\": \"请输入生命魔杖使用次数:(0-${item:lifeWand})\"},\n\t{\"type\": \"if\", \"condition\": \"flag:input<=item:lifeWand\",\n\t\t\"true\": [\n\t\t\t{\"type\": \"setValue\", \"name\": \"item:lifeWand\", \"value\": \"item:lifeWand-flag:input\"},\n\t\t\t{\"type\": \"setValue\", \"name\": \"status:hp\", \"value\": \"status:hp+flag:input*100\"},\n\t\t\t\"成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。\"\n\t\t],\n\t\t\"false\": [\"输入不合法!\"]\n\t},\n]);\ncore.addItem('lifeWand', 1);", "jumpShoes": "core.insertAction({\"type\":\"jumpHero\",\"loc\":[core.nextX(2),core.nextY(2)]});", "redPotion": "core.status.hero.hp += core.values.redPotion", @@ -389,17 +389,17 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = }, "canUseItemEffect": { "book": "true", - "fly": "(function () {\n\treturn core.status.hero.flyRange.indexOf(core.status.floorId)>=0 && (core.status.maps[core.status.floorId]||{}).canFlyTo;\n})();", - "pickaxe": "(function() {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable && core.nearHero(block.x, block.y) && \n\t\t\t(block.event.canBreak || block.event.id == 'yellowWall' || block.event.id=='whiteWall' || block.event.id=='blueWall')) { // 能破哪些墙\n\t\t\t// 四个方向\n\t\t\tif (core.flags.pickaxeFourDirections || (block.x == core.nextX() && block.y == core.nextY()))\n\t\t\t\tids.push(i);\n\t\t\telse id2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\telse if (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", - "icePickaxe": "(function() {\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable && block.x==core.nextX() && block.y==core.nextY() && block.event.id=='ice') {\n\t\t\tcore.status.event.ui = [i];\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();", - "bomb": "(function () {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable && block.event.cls.indexOf('enemy')==0 && core.nearHero(block.x, block.y)) {\n\t\t\tvar enemy = core.material.enemys[block.event.id];\n\t\t\tif (core.isset(enemy) && enemy.notBomb) continue;\n\t\t\tif (core.flags.bombFourDirections || (block.x==core.nextX() && block.y==core.nextY()))\n\t\t\t\tids.push(i);\n\t\t\telse\n\t\t\t\tid2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\tif (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", - "hammer": "(function() {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable && block.event.cls.indexOf('enemy')==0 && core.nearHero(block.x, block.y)) {\n\t\t\tvar enemy = core.material.enemys[block.event.id];\n\t\t\tif (core.isset(enemy) && enemy.notBomb) continue;\n\t\t\tif (block.x==core.nextX() && block.y==core.nextY())\n\t\t\t\tids.push(i);\n\t\t\telse\n\t\t\t\tid2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\telse if (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", - "earthquake": "(function () {\n\tvar able=false;\n\tvar ids = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable &&\n\t\t\t(block.event.canBreak || block.event.id == 'yellowWall' || block.event.id == 'blueWall' || block.event.id == 'whiteWall')) { // 能炸的墙壁\n\t\t\tids.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\table=true;\n\t}\n\treturn able;\n})();", + "fly": "(function () {\n\treturn core.status.maps[core.status.floorId].canFlyTo;\n})();", + "pickaxe": "(function() {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && core.nearHero(block.x, block.y) && \n\t\t\t(block.event.canBreak || block.event.id == 'yellowWall' || block.event.id=='whiteWall' || block.event.id=='blueWall')) { // 能破哪些墙\n\t\t\t// 四个方向\n\t\t\tif (core.flags.pickaxeFourDirections || (block.x == core.nextX() && block.y == core.nextY()))\n\t\t\t\tids.push(i);\n\t\t\telse id2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\telse if (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", + "icePickaxe": "(function() {\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && block.x==core.nextX() && block.y==core.nextY() && block.event.id=='ice') {\n\t\t\tcore.status.event.ui = [i];\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n})();", + "bomb": "(function () {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && block.event.cls.indexOf('enemy')==0 && core.nearHero(block.x, block.y)) {\n\t\t\tvar enemy = core.material.enemys[block.event.id];\n\t\t\tif (core.isset(enemy) && enemy.notBomb) continue;\n\t\t\tif (core.flags.bombFourDirections || (block.x==core.nextX() && block.y==core.nextY()))\n\t\t\t\tids.push(i);\n\t\t\telse\n\t\t\t\tid2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\tif (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", + "hammer": "(function() {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && block.event.cls.indexOf('enemy')==0 && core.nearHero(block.x, block.y)) {\n\t\t\tvar enemy = core.material.enemys[block.event.id];\n\t\t\tif (core.isset(enemy) && enemy.notBomb) continue;\n\t\t\tif (block.x==core.nextX() && block.y==core.nextY())\n\t\t\t\tids.push(i);\n\t\t\telse\n\t\t\t\tid2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\telse if (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", + "earthquake": "(function () {\n\tvar able=false;\n\tvar ids = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable &&\n\t\t\t(block.event.canBreak || block.event.id == 'yellowWall' || block.event.id == 'blueWall' || block.event.id == 'whiteWall')) { // 能炸的墙壁\n\t\t\tids.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\table=true;\n\t}\n\treturn able;\n})();", "centerFly": "(function () {\n\tvar toX = core.bigmap.width-1-core.getHeroLoc('x'), toY = core.bigmap.height-1-core.getHeroLoc('y');\n\tvar id = core.getBlockId(toX, toY);\n\treturn id == null;\n})();", "upFly": "(function() {\n\tvar floorId = core.status.floorId, index = core.floorIds.indexOf(floorId);\n\tif (index=0 && toX=0 && toY0) {\n\t\tvar toId = core.floorIds[index-1], toX = core.getHeroLoc('x'), toY = core.getHeroLoc('y');\n\t\tvar mw = core.floors[toId].width, mh = core.floors[toId].height;\n\t\tif (toX>=0 && toX=0 && toY0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\tif (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", - "bigKey": "(function() {\n\tvar able=false, ids = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (core.isset(block.event) && !block.disable && block.event.id == 'yellowDoor') {\n\t\t\tids.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\table=true;\n\t}\n\treturn able;\n})();", + "snow": "(function () {\n\tvar ids = [], id2s = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && block.event.id == 'lava' && core.nearHero(block.x, block.y)) {\n\t\t\tif (core.flags.snowFourDirections || (block.x == core.nextX() && block.y == core.nextY()))\n\t\t\t\tids.push(i);\n\t\t\telse id2s.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\treturn true;\n\t}\n\tif (id2s.length==1) {\n\t\tcore.status.event.ui = id2s;\n\t\treturn true;\n\t}\n\treturn false;\n})();", + "bigKey": "(function() {\n\tvar able=false, ids = [];\n\tfor (var i in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[i];\n\t\tif (!block.disable && block.event.id == 'yellowDoor') {\n\t\t\tids.push(i);\n\t\t}\n\t}\n\tif (ids.length>0) {\n\t\tcore.status.event.ui = ids;\n\t\table=true;\n\t}\n\treturn able;\n})();", "poisonWine": "core.hasFlag('poison');", "weakWine": "core.hasFlag('weak');", "curseWine": "core.hasFlag('curse');", diff --git a/project/maps.js b/project/maps.js index 78a610d0..d631d0ba 100644 --- a/project/maps.js +++ b/project/maps.js @@ -67,7 +67,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "70": {"cls":"items","id":"sword0"}, "71": {"cls":"items","id":"shield0"}, "72": {"cls":"items","id":"skill1"}, - "73": {"cls":"items","id":"I73"}, + "73": {"cls":"items","id":"wand"}, "81": {"cls":"terrains","id":"yellowDoor","trigger":"openDoor"}, "82": {"cls":"terrains","id":"blueDoor","trigger":"openDoor"}, "83": {"cls":"terrains","id":"redDoor","trigger":"openDoor"}, diff --git a/project/plugins.js b/project/plugins.js new file mode 100644 index 00000000..d09e9944 --- /dev/null +++ b/project/plugins.js @@ -0,0 +1,88 @@ +var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = +{ + "init": 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中,详见文档-脚本-函数的转发。 +}, + "drawLight": function () { + + // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) + // 【参数说明】 + // name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建 + // color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。 + // lights:可选,一个数组,定义了每个独立的灯光。 + // 其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。 + // lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。 + // 比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 + // 【调用样例】 + // core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9,等价于更改画面色调为[0,0,0,0.9]。 + // core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 + // core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 + // core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 + // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,且40%后才开始衰减。 + this.drawLight = function (name, color, lights, lightDec) { + + // 清空色调层;也可以修改成其它层比如animate/weather层,或者用自己创建的canvas + var ctx = core.getContextByName(name); + if (ctx == null) { + if (typeof name == 'string') + ctx = core.createCanvas(name, 0, 0, core.__PIXELS__, core.__PIXELS__, 98); + else return; + } + + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + core.clearMap(name); + // 绘制色调层,默认不透明度 + if (color == null) color = 0.9; + ctx.fillStyle = "rgba(0,0,0," + color + ")"; + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + lightDec = core.clamp(lightDec, 0, 1); + + // 绘制每个灯光效果 + ctx.globalCompositeOperation = 'destination-out'; + lights.forEach(function (light) { + // 坐标,半径,中心不透明度 + var x = light[0], + y = light[1], + r = light[2]; + // 计算衰减距离 + var decDistance = parseInt(r * lightDec); + // 正方形区域的直径和左上角坐标 + var grd = ctx.createRadialGradient(x, y, decDistance, x, y, r); + grd.addColorStop(0, "rgba(0,0,0,1)"); + grd.addColorStop(1, "rgba(0,0,0,0)"); + ctx.beginPath(); + ctx.fillStyle = grd; + ctx.arc(x, y, r, 0, 2 * Math.PI); + ctx.fill(); + }); + ctx.globalCompositeOperation = 'source-over'; + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + } +} +} \ No newline at end of file diff --git a/styles.css b/styles.css index 9f36a7d1..a1d6649e 100644 --- a/styles.css +++ b/styles.css @@ -79,6 +79,15 @@ z-index: 15; } +#startTopHint { + color: #66CCFF; + position: absolute; + bottom: 0; + left: 5%; + z-index: 15; + font-size: 18px; +} + #startBackground { position:absolute; top:50%; @@ -142,10 +151,8 @@ } #floorMsgGroup { - /* width: 100%; - height: 100%; */ - /* top: 0; - right: 0; */ + top: 3px; + right: 3px; position: absolute; text-align: center; display: none; @@ -181,11 +188,15 @@ background: url(project/images/ground.png) repeat; z-index: 185; display: none; + top: 0; + left: 0; + padding: 3px; } #statusBar .status{ position: relative; display: block; float: left; + width: 100%; } .status img{ vertical-align: middle; @@ -194,7 +205,6 @@ max-height: 1.6em; } #statusBar span{ - color: white; font: bold italic 1.1em Verdana; } #statusBar p { @@ -214,6 +224,8 @@ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; display: none; + left: 0; + padding: 3px; } #toolBar .tools{ position: relative; @@ -300,17 +312,57 @@ p#name { } #ui { - z-index: 160; + z-index: 140; } #data { z-index: 170; } -.clearfix:before, -.clearfix:after { - content: "."; - display: block; - height: 0; - visibility: hidden; +#inputDiv { + display: none; + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background: rgba(127,127,127,0.6); + z-index: 2000 +} + +#inputDialog { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -60%); + background: white; + width: 250px; + min-height: 50px; + max-height: 250px; +} + +#inputMessage { + word-wrap: break-word; + text-align: left; + margin-left: 8%; + margin-right: 5%; +} + +#inputBox { + margin-left: 8%; + width: 80%; + margin-bottom: 10px; + padding: 5px 3px; + border: 1px solid; + background: #F0F0F0; +} + +#inputYes { + margin-bottom: 15px; + margin-left: 8%; +} + +#inputNo { + float:right; + margin-right: 10%; } \ No newline at end of file