diff --git a/API列表.txt b/API列表.txt new file mode 100644 index 00000000..2f846828 --- /dev/null +++ b/API列表.txt @@ -0,0 +1,2185 @@ +# 附录:API列表 + +?> 目前版本**v2.6.5**,上次更新时间:* {docsify-updated} * + +这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 + +本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 + +如有任何疑问,请联系小艾寻求帮助。 + +## core.js + +core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 + +但是,core中定义了很多游戏运行时的状态,这些状态很多都会被使用到。 + +``` text +core.__SIZE__, core.__PIXELS__ +游戏窗口大小;对于13x13的游戏而言这两个值分别是13和416,15x15来说分别是15和480。 + + +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.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.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.status.maps不同的是,后者在每次重新开始和读档时都会重置,也允许被修改(会存入存档)。 +而core.floors全程唯一,不允许被修改。 + + +core.statusBar +状态栏信息,例如状态栏图片,图标,以及各个内容的DOM定义等。 +core.statusBar.images (所有的系统图标,和icons.png对应) +core.statusBar.icons (状态栏中绘制的图标内容) + + +core.values +所有的全局数值信息,和全塔属性中的values一致。 +此项允许被直接修改,会存入存档。 + + +core.flags +所有的全塔开关,和全塔属性中的flags一致。 +此项不允许被直接修改,如有需要请使用“设置系统开关”事件,或者调用core.setGlobalFlag这个API。 + + +core.plugin +定义的插件函数。 + + +core.doFunc(func, _this) +执行一个函数,func为函数体或者插件中的函数名,_this为使用的this。 +如果func为一个字符串,则视为插件中的函数名,同时_this将被设置成core.plugin。 +此函数剩余参数将作为参数被传入func。 +``` + +## actions.js + +actions.js主要是处理一些和用户交互相关的内容。 + +```text +core.registerAction(action, name, func, priority) +注册一个用户交互行为。 +action:要注册的交互类型,如 ondown, onclick, keyDown 等等。 +name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 +func:执行函数;可以是一个具体的函数体,或者是一个插件中的函数名。 +priority:优先级;优先级高的被注册项将会被执行。此项可不填,默认为0。 +返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 + + +core.unregisterAction(action, name) +注销一个用户交互行为。 + + +core.doRegisteredAction(action) +执行一个用户交互行为。 +此函数将在该交互行为所注册的所有函数中,按照优先级从高到底依次执行。 +此函数剩余的参数将会作为参数传入该执行函数中。 +当某个执行函数返回true时将终止这一过程。 + + +core.onkeyDown(e) +当按下某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyDown"的交互函数。 + + +core.onkeyUp(e) +当放开某个键时的操作,e为KeyboardEvent。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onkeyUp"的交互函数。 + + +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(loc) +当从屏幕上离开时的操作。loc为当前的坐标信息。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onup"的交互函数。 +注册的onup交互函数需要接受x, y, px, py四个参数,代表当前的的位置和像素坐标。 + + +core.onclick(x, y) +当点击屏幕上的某点位置时执行的操作,请注意这里的x和y是位置坐标。 +一般而言,一个完整的ondown到onup将触发一个onclick事件。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onclick"的交互函数。 + + +core.onmousewheel(direct) +当滚动鼠标滑轮时执行的操作。direct为滑轮方向,上为1,下为-1。 +请勿直接覆盖或调用此函数,如有需要请注册一个"onmousewheel"的交互函数。 + + +core.keyDownCtrl() +当长按Ctrl键不动时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"keyDownCtrl"的交互函数。 + + +core.longClick() +当长按住屏幕时执行的操作。 +请勿直接覆盖或调用此函数,如有需要请注册一个"longClick"的交互函数。 +注册的交互函数如果某一项返回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) +每走完一步后执行的操作,被转发到了脚本编辑中,执行脚本编辑moveOneStep中的内容。 + + +core.moveAction(callback) +尝试执行单步行走。callback是执行完毕的回调。 +如果勇士面对的方向是noPass的,将直接触发事件并执行回调。 + + +core.moveHero(direction, callback) +令勇士朝一个方向行走。如果设置了callback,则只会行走一步,并执行回调。 +否则,将一直朝该方向行走,直到core.status.heroStop为true为止。 +direction可为"up","down","right","left",分别对应上,下,右,左。 + + +core.isMoving() +当前是否正在处于行走状态 + + +core.waitHeroToStop(callback) +停止勇士的行走,等待行动结束后,再异步执行回调。 + + +core.turnHero(direction) +转向。如果设置了direction则会转到该方向,否则会右转。该函数会自动计入录像。 +direction可为"up","down","right","left",分别对应上,下,右,左。 + + +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(n) +获得勇士面对的第n个位置的横纵坐标。n可不填,默认为1。 + + +core.nearHero(x, y, n) +判定某个点是否和勇士的距离不大于n。n可不填,默认为1。 + + +core.gatherFollowers() +聚集所有的跟随者到勇士的位置。 + + +core.updateFollowers() +更新跟随者们的坐标。 + + +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.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) +设置勇士当前的某个属性,name可为"atk","def","hp"等。 +如core.setStatus("atk", 100)则为设置勇士攻击为100。 + + +core.addStatus(name, value) +加减勇士当前的某个属性,name可为"atk","def","hp"等。 +如core.addStatus("atk", 100)则为增加勇士攻击100点。 +等价于 core.setStatus(name, core.getStatus(name) + value)。 + + +core.getStatus(name) +获得勇士的某个原始属性值,该值不受百分比增幅影响。 +譬如你有一件道具加10%的攻击,你可以使用该函数获得你的攻击被增幅前的数值 + + +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'(勇士x坐标), 'y'(勇士y坐标)和'direction'(勇士朝向)之一。 +如果noGather为true,则不会聚集所有的跟随者。 +譬如core.setHeroLoc("x", 1, true)则为设置勇士x坐标为1,不聚集所有跟随者。 + + +core.getHeroLoc(name) +获得勇士的某个位置属性。如果name为null则直接返回core.status.hero.loc。 +譬如core.getHeroLoc("x")则返回勇士当前x坐标 + + +core.getLvName(lv) +获得某个等级对应的名称,其在全塔属性的levelUp中定义。如果不存在则返回原始数值。 + + +core.setFlag(name, value) +设置某个自定义变量或flag。如果value为null则会调用core.removeFlag进行删除。 +这里的变量与事件中使用的变量等价 +譬如core.setFlag("xxx",1)则为设置变量xxx为1。 + + +core.addFlag(name, value) +加减某个自定义的变量或flag。等价于 core.setFlag(name, core.getFlag(name, 0) + value) +这里的变量与事件中使用的变量等价 +譬如core.addFlag("xxx",1)则为增加变量xxx1 + + +core.getFlag(name, defaultValue) +获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue的值。 +这里的变量与事件中使用的变量等价 +譬如core.getFlag("xxx",1)则为获得变量xxx的值,如变量xxx不存在则返回1 + + +core.hasFlag(name) +判定是否拥有某个自定义变量或flag。等价于 !!core.getFlag(name, 0) +这里的变量与事件中使用的变量等价 +譬如core.hasFlag("xxx",1)则为判断变量xxx是否存在 + +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.setCurtain([255,255,255,1], 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(doNotCheckAutoEvents) +更新状态栏,被转发到了脚本编辑中。此函数还会根据是否在回放来设置工具栏的图标。 +如果doNotCheckAutoEvents为true则此时不检查自动事件。 + +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,x,y,floorId为当前xy坐标和楼层。 + + +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.getCurrentEnemys(floorId) +获得某个楼层不重复的怪物信息,floorId不填默认为当前楼层。该函数会被怪物手册所调用。 +该函数将返回一个列表,每一项都是一个不同的怪物,按照伤害值从小到大排序。 +另外值得注意的是,如果设置了某个怪物的displayIdInBook,则会返回对应的怪物。 + + +core.hasEnemyLeft(enemyId, floorId) +检查某个楼层是否还有剩余的(指定)怪物。 +floorId为楼层ID,可忽略表示当前楼层。也可以填数组如["MT0","MT1"]同时检测多个楼层。 +enemyId如果不填或null则检查是否剩余任何怪物,否则只检查是否剩余指定的某类怪物。 +``` + +## 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.getItem("yellowKey",2)会直接获得两把黄钥匙。 + + +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(keepUI) +执行下一个自定义事件。 +此函数将检测事件列表是否全部执行完毕,如果是则执行回调函数。 +否则,将从事件列表中弹出下一个事件,并调用core.doEvent进行执行。 +如果keepUI为true,则不会清掉UI层和selector,适合于自己用脚本的绘制。 + + +core.insertAction(action, x, y, callback, addToLast) +向当前的事件列表中插入一个或多个事件并执行。 +如果当前并不是在事件执行流中,则会调用core.startEvents()开始执行事件,否则仅仅执行插入操作。 +action为要插入的事件,可以是一个单独的事件,或者是一个事件列表。 +x,y,callback如果设置了且不为null,则会覆盖当前的坐标和回调函数。 +addToLast如果为真,则会插入到事件执行列表的尾部,否则是插入到执行列表的头部。 + + +core.getCommonEvent(name) +根据名称获得某个公共事件内容。 + + +core.recoverEvents(data) +恢复事件现场。一般用于呼出怪物手册、呼出存读档页面等时,恢复事件执行流。 + + +core.checkAutoEvents() +检测自动事件并执行。 + + +core.precompile(events) +尝试预编译一段事件。 + + +// ------ 点击状态栏图标时执行的一些操作 ------ // + +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.setEnemy(id, name, value, prefix) +设置一个怪物属性。id为怪物的ID,name为要设置的项,比如hp,atk,def等等。 +value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用,脚本中一般忽略。 + + +core.setFloorInfo(name, values, floorId, prefix) +设置某层楼的楼层属性,其中name为该楼层属性对应的条目,values为要设置的值,floorId为楼层id,prefix一般直接忽略。 +譬如core.setFloorInfo("name","4", "MT1")则为设置MT1显示在状态栏中的层数为4 + + +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.getEquipTypeByName("武器")默认返回全塔属性中武器对应的装备孔号0。 + + +core.getEquipTypeById(equipId) +获得某个装备的装备类型。 +如果其type写的是装备名(多重装备),则调用core.getEquipTypeByName()函数。 + + +core.canEquip(equipId, hint) +当前能否穿上某个装备。如果hint为真,则不可装备时会气泡提示原因。 + + +core.loadEquip(equipId, callback) +穿上某个装备,equipId为装备id。 + + +core.unloadEquip(equipType, callback) +脱下某个装备孔的装备。 +譬如core.unloadEquip(0)则为脱下0号装备孔中的装备,默认0号装备孔对应“武器”,1号装备孔对应“盾牌” + + +core.compareEquipment(compareEquipId, beComparedEquipId) +比较两个套装的差异。 +此函数将对所有的勇士属性包括生命魔力攻防魔防金币等进行比较。 +如果存在差异的,将作为一个对象返回其差异内容。 + + +core.quickSaveEquip(index) +保存当前套装。index为保存的套装编号。 + + +core.quickLoadEquip() +读取当前套装。index为读取的套装编号。 + + +core.getEquippedStatus(name) +获得装备直接增加的属性数据。 +``` + +## 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.removeMaps(fromId, toId) +删除某个区域的地图。调用此函数后,这些楼层将不可飞,不可被浏览地图,也不计入存档。 +fromId和toId为要删除的起终点楼层ID;toId也可以不填代表只删除某一层。 +此函数适用于高层塔的砍层,例如每100层一个区域且互相独立,不可再返回的情况。 + + +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为目标楼层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) +判定某个点是否有(id对应的)地形存在。 +如果id为null,则只要存在terrains即为真,否则还会判定对应点的ID。 + + +core.stairExists(x, y, floorId) +判定某个点是否存在楼梯。 + + +core.nearStair() +判定当前勇士是否在楼梯上或旁边(距离不超过1)。 + + +core.enemyExists(x, y, id, floorId) +判定某个点是否有(id对应的)怪物存在。 +如果id为null,则只要存在怪物即为真,否则还会判定对应点的怪物ID。 +请注意,如果需要判定某个楼层是否存在怪物请使用core.hasEnemyLeft()函数。 + + +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。如果该点不存在图块则返回null。 + + +core.getBlockCls(x, y, floorId, showDisable) +获得某个点的图块类型。如果该点不存在图块则返回null。 + + +core.getBlockInfo(block) +根据某个的图块信息获得其详细的素材信息。 +如果参数block为字符串,则视为图块ID;如果参数为数字,则视为图块的数字。 +此函数将返回一个非常详尽的素材信息,目前包括如下几项: +number:素材数字;id:素材id;cls:素材类型;image:素材所在的素材图片;animate:素材的帧数。 +posX, posY:素材在该素材图片上的位置;height:素材的高度;faceIds:NPC朝向记录。 + + +core.searchBlock(id, floorId, showDisable) +搜索一个图块出现过的所有位置。id为图块ID,也可以传入图块的数字。 +id支持通配符搜索,比如"*Door"可以搜索所有的门,"unknownEvent*"可以所有所有的unknownEvent。 +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表示当前楼层。 +此函数不会实际将该块从地图中进行删除,而是将该点设置为禁用,以供以后可能的启用事件。 + + +core.removeBlock(x, y, floorId) +将从启用变成禁用状态,并尽可能将其从地图上删除。 +和hideBlock相比,如果该点不存在自定义事件(比如门或普通的怪物),则将直接从地图中删除。 +如果存在自定义事件,则简单的禁用它,以供以后可能的启用事件。 + + +core.removeBlockById(index, floorId) +每个楼层的图块存成一个数组,index即为该数组中的索引,每个索引对应该地图中的一个图块 +根据索引从地图的block数组中尽可能删除一个图块。floorId可不填或null表示当前楼层。 + + +core.removeBlockByIds(floorId, ids) +ids为由索引组成的数组,如[0,1]等 +根据索引数组从地图的block数组中尽可能删除一系列图块。floorId可不填或null表示当前楼层。 + + +core.canRemoveBlock(block, floorId) +block为图块信息,可由core.getBlock获取 +判定当前能否完全删除某个图块。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为要改变到的数字,也可以传入图块id(将调用core.getNumberById()来获得数字)。 +x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 + + +core.replaceBlock(fromNumber, toNumber, floorId) +将某个或某些楼层中的所有某个图块替换成另一个图块 +fromNumber和toNumber为要被替换和替换到的数字。 +floorId可为某个楼层ID,或者一个楼层数组;如果不填只视为当前楼层。 +值得注意的是,使用此函数转了的点上的自定义事件可能无法被执行。 +如有需要,再对那些存在事件的点执行core.setBlock()即可 + + +core.setBgFgBlock(name, number, x, y, floorId) +设置前景/背景层的某个图块。name只能为'bg'或'fg'表示前景或背景层。 +number为要设置到的图块数字,x,y和floorId为目标点坐标和楼层,可忽略为当前点和当前楼层。 + + +core.resetMap(floorId) +重置某层或若干层的地图和楼层属性。 +floorId可为某个楼层ID,或者一个楼层数组如["MT1","MT2"](同时重置若干层);如果不填则只重置当前楼层。 + +// ------ 移动/跳跃图块,淡入淡出图块 ------ // + +core.moveBlock(x, y, steps, time, keep, callback) +移动一个图块,x和y为图块的坐标。 +steps为移动的数组,每一项只能是"up","down","left","right"之一。 +time为每一步的移动时间,不填默认为500ms。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 + + +core.jumpBlock(sx, sy, ex, ey, time, keep, callback) +跳跃一个图块,sx和sy为图块的坐标,ex和ey为目标坐标。time为整个跳跃过程中的全程用时,不填默认500。 +如果keep为真,则在移动完毕后将自动调用一个setBlock事件,改变目标点的图块(即不消失), +否则会按照time时间来淡出消失。callback会执行完毕后的回调。 + + +core.animateBlock(loc, type, time, callback) +淡入/淡出一个或多个图块。 +loc为一个图块坐标,或者一个二维数组表示一系列图块坐标(将同时显示和隐藏)。 +type只能为'show'或'hide'表示是淡入但是淡出。time为动画时间,callback为执行完毕的回调。 + +// ------ 全局动画控制,动画的绘制 ------ // + +core.addGlobalAnimate(block) +添加一个全局帧动画。 + + +core.removeGlobalAnimate(x, y, name) +删除一个或全部的全局帧动画。name可为'bg',null或'fg'表示某个图层。 +x和y如果为null,则会删除全部的全局帧动画,否则只会删除该点的该层的帧动画。 + + +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, maxWidth) +在某个画布上绘制一段文字。 +text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) +style可直接使用"red","white"等或用"rgba(255,255,255,1)"或用"#FFFFFF"等方式来获得字体对应的颜色 +font的格式为"20px Verdana"前者为字体大小,后者为字体 +如果maxWidth不为null,则视为文字最大宽度,如果超过此宽度则会自动放缩文字直到自适应为止。 +请注意textAlign和textBaseline将决定绘制的左右对齐和上下对齐方式。 +具体可详见core.setTextAlign()和core.setTextBaseline()函数。 +譬如:core.fillText("ui", "这是要描绘的文字", 10, 10, "red", "20px Verdana", 100) +即是在ui图层上,以10,10为起始点,描绘20像素大小,Verdana字体的红色字,长度不超过100像素 + + +core.fillBoldText(name, text, x, y, style, font) +在某个画布上绘制一个描黑边的文字。 + + +core.fillRect(name, x, y, width, height, style) +绘制一个矩形。width, height为矩形宽高,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) +绘制一条线,x1y1为起始点像素,x2y2为终止点像素。 + + +core.drawArrow(name, x1, y1, x2, y2, style, lineWidth) +绘制一个箭头,x1y1为起始点像素,x2y2为终止点像素。 + + +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允许传一个图片,画布。也允许传递图片名,将从你导入的图片中获取图片内容。 + + +core.drawIcon(name, id, x, y, w, h) +在一张画布上绘制一个图标。 +id为注册过的图标ID,也可以使用状态栏的图标ID,例如lv, hp, up, save, settings等。 +x和y为绘制的左上角坐标;w和h可选为绘制的宽高,如果不填或null则使用该图标的默认宽高。 + + +// ------ 具体的某个UI界面的绘制 ------ // +core.closePanel() +结束一切事件和UI绘制,关闭UI窗口,返回游戏。 +此函数将以此调用core.clearUI(),core.unlockControl(),并清空core.status.event里面的内容。 + + +core.clearUI() +重置UI窗口。此函数将清掉所有的UI帧动画和光标,清空UI画布,并将alpha设为1。 + + +core.drawTip(text, id, clear) +在左上角以气泡的形式绘制一段提示。 +text为文字内容,仅支持${}的表达式计算,不支持换行和变色。 +id可选,为同时绘制的图标ID,如果不为null则会同时绘制该图标(仅对32x32的素材有效)。 +也可以使用状态栏的图标ID,例如lv, hp, up, save, settings等。 +如果clear为true,则会清空当前所有正在显示的提示。 + + +core.clearTip() +清空当前所有正在显示的提示。 + + +core.drawText(content, callback) +绘制一段文字。contents为一个字符串或一个字符串数组,callback为全部绘制完毕的回调。 +支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\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,\\c,\\d,\\e等) + ,但不支持支持\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定时器。 + - interval:字符间的间距。值表示绘制每个字符之间间隔的距离,默认为0。 + + +core.drawTextBox(content, showAll) +绘制一个对话框。content为一个字符串或一个字符串数组。 +支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\t和\b的语法。 +该函数将使用用户在剧情文本设置中的配置项进行绘制。 +实际执行时,会计算文本框宽度并绘制背景,绘制标题和头像,再调用core.drawTextContent()绘制正文内容。 +showAll可选,如果为true则不会使用打字机效果而全部显示,主要用于打字机效果的点击显示全部。 + + +core.drawScrollText(content, time, lineHeight, callback) +绘制一个滚动字幕。content为绘制内容,time为总时间(默认为5000),lineHeight为行距比例(默认为1.4)。 +滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),但不支持\t和\b效果。 +可以通过剧情文本设置中的align控制是否居中绘制,offset控制其距离左边的偏移量。 + + +core.textImage(content, lineHeight) +将文本图片化。content为绘制内容,lineHeight为行距比例(默认为1.4)。 +可以通过剧情文本设置中的align控制是否居中绘制。 +此函数将返回一个临时画布的canvas,供转绘到其他画布上使用。 + + +core.drawChoices(content, choices) +绘制一个选项框。 +content可选,为选项上方的提示文字,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\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为画布的左上角坐标,name为画布名。 + + +core.ui.resizeCanvas(name, width, height, styleOnly) +重新设置一个自定义画布的大小。width和height为新设置的宽高。 +styleOnly控制是否只修改画布的显示大小(而不修改画布的内部大小)。 +如果styleOnly为true,则只修改其显示大小(即canvas.style.width); +否则,则会同时修改画布的显示大小和内部大小并清空画布内容。 + + +core.ui.deleteCanvas(name) +删除一个自定义画布。 + + +core.ui.deleteAllCanvas() +删除所有的自定义画布。 +``` + +## utils.js + +utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 + +```text +core.replayText(text, need, times) +将一段文字中的${}(表达式)进行替换。need和time一般可以直接忽略。 + + +core.replaceValue(value) +对一个表达式中的特殊规则进行替换,如status:xxx等。 +请注意,此项不会对独立开关如switch:A进行替换。 + + +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中获得一个键的值,会立刻返回结果。 +此函数会自动给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中获得一个键的值,异步返回结果。 +如果对应的键值不存在,则会使用defaultValue。 +如果获得成功,则会将core.decompress后的结果传入successCallback回调函数执行。 +errorCallback可选,如果失败,则会将错误信息传入errorCallback()。 +此函数是异步的,只能通过回调函数来获得读取的结果或错误信息。 + + +core.setGlobal(key, value) +设置一个全局存储,适用于global:xxx。 +录像播放时将忽略此函数,否则直接调用core.setLocalStorage。 + + +core.getGlobal(key, value) +获得一个全局存储,适用于global:xxx,支持录像。 +正常游戏时将使用core.getLocalStorage获得具体的数据,并将结果存放到录像中。 +录像播放时会直接从录像中获得对应的数据。 + + +core.clone(data, filter, recursion) +深拷贝一个对象。有关浅拷贝,深拷贝,基本类型和引用类型等相关知识可参见: +https://zhuanlan.zhihu.com/p/26282765 +filter为过滤函数,如果设置且不为null则需传递一个可接受(name, value)的函数, +并返回true或false,表示该项是否应该被深拷贝。 +recursion表示该filter是否应递归向下传递,如果为true则递归函数也将传该filter。 +例如: +core.clone(core.status.hero, function(name, value) { + return name == 'items' || typeof value == 'number'; +}, false); +这个例子将会深拷贝勇士的属性和道具。 + + +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.matchWildcard(pattern, string) +进行通配符的匹配判定,目前仅支持*(可匹配0或任意个字符)。比如"a*b*c"可以匹配"aa012bc"。 + + +core.encodeBase64(str) / core.decodeBase64(str) +将字符串进行base64加密或解密。 +可用于解压缩录像数据 + + +core.convertBase(str, fromBase, toBase) +任意进制转换。此函数可能执行的非常慢,慎用。 + + +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大法。 +但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 +对于需要大量生成随机数,但又想使用真随机支持SL大法的(例如随机生成地图等),可以采用如下方式: + var x = core.rand2(100); for (var i = 0; i < x; i++) core.rand() +即先生成一个真随机数,根据该数来推进伪随机的种子,这样就可以放心调用core.rand()啦。 + + +core.readFile(success, error, accept) +读取一个本地文件内容。success和error分别为读取成功或失败的回调函数。 +accept如果设置则控制能选择的文件类型。 +iOS平台暂不支持读取文件操作。 + + +core.readFileContent(content) +读取到的文件内容。此函数会被APP等调用,来传递文件的具体内容。 + + +core.download(filename, content) +生成一个文件并下载。filename为文件名,content为具体的文件内容。 +iOS平台暂不支持下载文件操作。 + + +core.copy(data) +将一段内容拷贝到剪切板。 + + +core.myconfirm(hint, yesCallback, noCallback) +弹窗绘制一段提示信息并让用户确认。hint为提示信息。 +yesCallback和noCallback分别为确定和取消的回调函数。 +此函数和core.drawConfirmBox的区别主要在于: + - drawConfirmBox会清掉UI层原有的绘制信息,此函数不会清除。 + - drawConfirmBox可以用键盘进行操作,此函数必须用鼠标点击。 +另外请注意:本函数的选择也不会进录像,一般用于全局的提示。 +如果需要在事件流中调用请使用显示确认框或显示选择项。 + + +core.myprompt(hint, value, callback) +弹窗让用户输入一段内容。hint为提示信息,value为框内的默认填写内容。 +callback为用户点击确认或取消后的回调。 +如果用户点击了确认,则会把框内的内容(可能是空串)传递给callback,否则把null传递给callback。 + + +core.showWithAnimate(obj, speed, callback) / core.hideWithAnimate(obj, speed, callback) +动画淡入或淡出一个对象。 + + +core.consoleOpened() +检测当前的控制台是否处于开启状态。仅在全塔属性中的检查控制台开关开启时有效。 +此函数有可能会存在误伤行为,即没开过控制台仍认为开启过。 + + +core.hashCode(obj) +计算一个对象的哈希值。 + + +core.same(a, b) +判定a和b是否相同,包括类型相同和值相同。 +如果a和b都是数组,则会递归依次比较数组中的值;如果都是对象亦然。 + + +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/README.md b/README.md index da2183a4..17ab6be6 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ``` bash ├── /_server/ # 为可视化地图编辑器提供一些支持的目录 ├── /docs/ # 文档目录 +├── /extensions/ # 拓展工具目录,发布到网站后不会加载 ├── /libs/ # 系统库目录 │ ├─ /thirdparty/ # 游戏所用到的第三方库文件 │ ├─ actions.js # 处理用户交互的文件 @@ -25,6 +26,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! │ ├─ data.js # 记录了一些初始化信息 │ ├─ enemys.js # 记录了怪物的信息,包括特殊属性、伤害计算公式、临界值计算等。 │ ├─ events.js # 处理事件的文件,所有自定义事件都会在此文件中进行处理 +│ ├─ extensions.js # 加载拓展工具的文件 │ ├─ icons.js # 图标信息,会被转发到project下 │ ├─ items.js # 道具信息,会被转发到project下 │ ├─ loader.js # 动态加载JS代码、图片、音效等 @@ -55,6 +57,108 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 +### 2019.12.31 HTML5魔塔样板V2.6.6 + +* [x] 编辑器增加【最近使用的图块】的区域 +* [x] 编辑器拉伸到铺满全屏幕,还可以Ctrl+滚轮放缩 +* [x] 编辑器支持连续Ctrl+Z的撤销,Ctrl+Y的重做 +* [x] 新增tileset右键绑定宽高,以替代贴图模式 +* [x] 多重自动存档,可以连续A键读档 +* [x] 高层塔分区域支持 +* [x] 自绘状态栏点击事件 +* [x] 绘制的锁定模式 +* [x] 等待用户操作增设分歧选项 +* [x] 增设压缩模式,会对图片等进行zip压缩 +* [x] 追加素材现在可以同时进行自动注册 +* [x] 可以复制和粘贴怪物或道具的属性 +* [x] 折叠素材时设置每一列个数 +* [x] 标题界面和显示选择项时光标跟随鼠标 +* [x] 修复所有已知的bug,大量细节优化 + +### 2019.12.1 HTML5魔塔样板V2.6.5 + +* [x] 事件:设置怪物属性;穿脱装备 +* [x] 新值块:enemy:xxx:atk可获得怪物数据 +* [x] 新值块:blockId:x,y获得某点图块ID +* [x] 部分事件预编译,加快执行速度 +* [x] 在系统设置中可以设置bgm的播放音量 +* [x] 通关事件可以不退出游戏 +* [x] 失败时允许直接读取自动存档 +* [x] NPC48自动注册可以自动绑定faceIds +* [x] 编辑器Alt+1-9存图块,1-9读取图块 +* [x] 编辑器现在可以跨楼层复制粘贴图块了 +* [x] 可以对flags.进行自动补全 +* [x] 部分Bug修复,大量细节优化 + +### 2019.10.29 HTML5魔塔样板V2.6.4 + +* [x] 自动事件,多事件页 +* [x] 增加开场logo动画 +* [x] 拓展:游戏时动态修改地图和怪物数据 +* [x] 插件:道具商店,支持买入和卖出道具 +* [x] 编辑器可以搜索变量出现位置 +* [x] 变量的中文替换 +* [x] 可以给图块绑定自定义脚本,碰触时触发 +* [x] 编辑器右键可以绑定机关门和出生点 +* [x] 支持多个drawTip同时出现 +* [x] 闪烁光标同时支持多个同时存在 +* [x] 插件:镜头平滑移动,默认禁用 +* [x] 素材的快速追加 +* [x] 批量导出动画 +* [x] 部分Bug修复,大量细节优化 + +### 2019.7.24 V2.6.3 + +* [x] 标题界面大幅美化,增加闪烁光标,支持键盘开始游戏 +* [x] 事件编辑器支持自动补全,能对flag和API列表等进行补全 +* [x] 剧情文本中\\c修改字体大小,\\d和\\e切换粗体和斜体 +* [x] 事件:设置视角&移动视角 +* [x] 可以指定显示选择项的出现条件并动态生成 +* [x] 楼层传送器的平面传送模式(哪里离开飞回到哪里) +* [x] UI绘制事件增添绘制圆和绘制圆边框 +* [x] 所有的UI绘制事件均可以双击预览 +* [x] 播放BGM事件可以一直持续播放直到下次调用 +* [x] \f立绘支持alpha值 +* [x] 支持在脚本编辑中直接flags.xxx调用自定义变量 +* [x] 首次获得道具将给予提示 +* [x] 等待用户操作支持滚轮,视为PgUp和PgDn +* [x] 脚本编辑器语法错误将禁止保存 +* [x] 录像播放时B键查看数据统计 +* [x] 所有已知bug的修复,大量细节优化 + +### 2019.6.7 V2.6.2 + +* [x] 可以拖动地图上的图块和事件,复制剪切和跨楼层粘贴 +* [x] 新增事件的地图选点功能,可以在地图上选择落点 +* [x] 现在素材区可以进行折叠与自动换列了 +* [x] 新增UI绘制系列事件,并且可以进行预览 +* [x] 显示文本事件的标题解析 +* [x] 新增常用工具:额外素材合并工具 +* [x] 进一步提升24倍速的播放速度 +* [x] 楼层转换增加对称点 +* [x] 增加编辑器快捷键说明,H键查看 +* [x] 文档-事件增加事件编辑器截图 +* [x] 大量细节优化,所有已知的Bug修复 + +### 2019.5.2 V2.6.1 + +* [x] 区域优化的录像播放功能,R键使用 +* [x] 强制战斗可以指定怪物坐标,将自动隐藏并执行该点战后事件 +* [x] flag:xxx也支持中文,例如 flag:2楼机关门 +* [x] 增加文件名映射,可以用中文映射到某个图片或bgm文件并使用 +* [x] 勇士宽度可以超过32(例如48x48的勇士行走图) +* [x] 现在允许修改floorId和图块ID了(在表格下方) +* [x] 增加事件:自动存档,返回标题界面;部分事件优化 +* [x] 商店长按空格可以连续加点 +* [x] 增设global:xxx使用全局存储,可被录像支持 +* [x] 支持\b[hero]和\b[null,x,y]来自动调整上下方向 +* [x] 支持\t[yellowKey]等只显示图标而没有标题 +* [x] 编辑器中前景层对于有事件的点半透明显示 +* [x] 存档改成1000页,长按上下页可快速翻页 +* [x] 录像播放初始默认暂停,N键可以单步执行 +* [x] 增设本地API文档,部分API和事件的优化 +* [x] 所有已知的bug修复,大量细节优化 + ### 2019.4.13 V2.6 * [x] 拆分整个项目,大幅重构代码,新增大量API diff --git a/_docs/V2.0.md b/_docs/V2.0.md index f10852d8..00d1084e 100644 --- a/_docs/V2.0.md +++ b/_docs/V2.0.md @@ -1,6 +1,6 @@ # V2.0版本介绍 -?> 目前版本**v2.5.5**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6*,上次更新时间:* {docsify-updated} * 目前样板已经更新到V2.0版本以上,本章将对V2.0的一些内容进行介绍。 diff --git a/_docs/api.md b/_docs/api.md index 3efce8eb..4965d648 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1,6 +1,6 @@ # 附录:API列表 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 @@ -57,7 +57,6 @@ core.platform.isPC (是否是电脑端) core.platform.isAndroid (是否是安卓端) core.platform.isIOS (是否是iOS端) core.platform.useLocalForage (是否开启了新版存档) -core.platform.extendKeyBoard (是否开启了拓展键盘) core.domStyle @@ -339,7 +338,7 @@ core.setHeroMoveInterval(callback) core.moveOneStep(x, y) -每走完一步后执行的操作,被转发到了脚本编辑中。 +每走完一步后执行的操作,被转发到了脚本编辑中,执行脚本编辑moveOneStep中的内容。 core.moveAction(callback) @@ -350,6 +349,7 @@ core.moveAction(callback) core.moveHero(direction, callback) 令勇士朝一个方向行走。如果设置了callback,则只会行走一步,并执行回调。 否则,将一直朝该方向行走,直到core.status.heroStop为true为止。 +direction可为"up","down","right","left",分别对应上,下,右,左。 core.isMoving() @@ -362,10 +362,11 @@ core.waitHeroToStop(callback) core.turnHero(direction) 转向。如果设置了direction则会转到该方向,否则会右转。该函数会自动计入录像。 +direction可为"up","down","right","left",分别对应上,下,右,左。 core.moveDirectly(destX, destY) -尝试瞬间移动到某点,被转发到了脚本编辑中。 +尝试瞬间移动到某点,被转发到了脚本编辑中,执行脚本编辑中的内容。 此函数返回非负值代表成功进行瞬移,返回值是省略的步数;如果返回-1则代表没有成功瞬移。 @@ -500,12 +501,12 @@ core.syncSave(type) / core.syncLoad() core.saveData() -获得要存档的内容,实际转发到了脚本编辑中。 +获得要存档的内容,实际转发到了脚本编辑中,执行脚本编辑中的内容。 core.loadData(data, callback) 实际执行一次读档行为,data为读取到的数据,callback为执行完毕的回调。 -实际转发到了脚本编辑中。 +实际转发到了脚本编辑中,执行脚本编辑中的内容。 core.getSave(index, callback) @@ -535,19 +536,23 @@ core.removeSave(index) // ------ 属性、状态、位置、变量等 ------ // core.setStatus(name, value) -设置勇士当前的某个属性。 +设置勇士当前的某个属性,name可为"atk","def","hp"等。 +如core.setStatus("atk", 100)则为设置勇士攻击为100。 core.addStatus(name, value) -加减勇士当前的某个属性。等价于 core.setStatus(name, core.getStatus(name) + value) +加减勇士当前的某个属性,name可为"atk","def","hp"等。 +如core.addStatus("atk", 100)则为增加勇士攻击100点。 +等价于 core.setStatus(name, core.getStatus(name) + value)。 core.getStatus(name) -获得勇士的某个原始属性值。 +获得勇士的某个原始属性值,该值不受百分比增幅影响。 +譬如你有一件道具加10%的攻击,你可以使用该函数获得你的攻击被增幅前的数值 core.getStatusOrDefault(status, name) -尝试从status中获得某个原始属性值;如果status为null或不存在对应属性值则从勇士属性中获取。 +尝试从status中获得某个原始属性值,该值不受百分比增幅影响;如果status为null或不存在对应属性值则从勇士属性中获取。 此项在伤害计算函数中使用较多,例如传递新的攻击和防御来计算临界和1防减伤。 @@ -573,12 +578,14 @@ core.getBuff(name) core.setHeroLoc(name, value, noGather) -设置勇士位置属性。name只能为'x', 'y'和'direction'之一。 +设置勇士位置属性。name只能为'x'(勇士x坐标), 'y'(勇士y坐标)和'direction'(勇士朝向)之一。 如果noGather为true,则不会聚集所有的跟随者。 +譬如core.setHeroLoc("x", 1, true)则为设置勇士x坐标为1,不聚集所有跟随者。 core.getHeroLoc(name) 获得勇士的某个位置属性。如果name为null则直接返回core.status.hero.loc。 +譬如core.getHeroLoc("x")则返回勇士当前x坐标 core.getLvName(lv) @@ -587,19 +594,26 @@ core.getLvName(lv) core.setFlag(name, value) 设置某个自定义变量或flag。如果value为null则会调用core.removeFlag进行删除。 +这里的变量与事件中使用的变量等价 +譬如core.setFlag("xxx",1)则为设置变量xxx为1。 core.addFlag(name, value) 加减某个自定义的变量或flag。等价于 core.setFlag(name, core.getFlag(name, 0) + value) +这里的变量与事件中使用的变量等价 +譬如core.addFlag("xxx",1)则为增加变量xxx1 core.getFlag(name, defaultValue) -获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue值。 +获得某个自定义的变量或flag。如果该flag不存在(从未赋值过),则返回defaultValue的值。 +这里的变量与事件中使用的变量等价 +譬如core.getFlag("xxx",1)则为获得变量xxx的值,如变量xxx不存在则返回1 core.hasFlag(name) 判定是否拥有某个自定义变量或flag。等价于 !!core.getFlag(name, 0) - +这里的变量与事件中使用的变量等价 +譬如core.hasFlag("xxx",1)则为判断变量xxx是否存在 core.removeFlag(name) 删除一个自定义变量或flag。 @@ -620,6 +634,8 @@ core.setWeather(type, level) core.setCurtain(color, time, callback) 更改画面色调。color为更改到的色调,是个三元或四元组;time为渐变时间,0代表立刻切换。 +譬如core.setCurtain([255,255,255,1], 0)即为无回调无等待更改画面色调为白色 + core.screenFlash(color, time, times, callback) @@ -655,9 +671,9 @@ core.clearStatusBar() 清空状态栏的数据。 -core.updateStatusBar() +core.updateStatusBar(doNotCheckAutoEvents) 更新状态栏,被转发到了脚本编辑中。此函数还会根据是否在回放来设置工具栏的图标。 - +如果doNotCheckAutoEvents为true则此时不检查自动事件。 core.showStatusBar() / core.hideStatusBar(showToolbox) 显示和隐藏状态栏。 @@ -708,7 +724,7 @@ special为要测试的内容,允许接收如下类型参数: core.getSpecials() -获得所有特殊属性的列表。实际上被转发到了脚本编辑中。 +获得所有特殊属性的列表。实际上被转发到了脚本编辑中,执行脚本编辑中的内容。 core.getSpecialText(enemy) @@ -748,7 +764,7 @@ number为要计算的临界值数量,不填默认为1。 core.getDefDamage(enemy, k, x, y, floorId) -获得某个怪物的k防减伤值。k可不填默认为1。 +获得某个怪物的k防减伤值。k可不填默认为1,x,y,floorId为当前xy坐标和楼层。 core.getEnemyInfo(enemy, hero, x, y, floorId) @@ -765,10 +781,6 @@ hero可为null或一个对象,具体将使用core.getRealStatusOrDefault(hero, 从V2.5.5开始,该函数也允许直接返回一个数字,代表战斗伤害值,此时回合数将视为0。 -core.updateEnemys() -更新怪物数据。该函数实际被转发到了脚本编辑中。详见文档-事件-更新怪物数据。 - - core.getCurrentEnemys(floorId) 获得某个楼层不重复的怪物信息,floorId不填默认为当前楼层。该函数会被怪物手册所调用。 该函数将返回一个列表,每一项都是一个不同的怪物,按照伤害值从小到大排序。 @@ -776,7 +788,8 @@ core.getCurrentEnemys(floorId) core.hasEnemyLeft(enemyId, floorId) -检查某个楼层是否还有剩余的(指定)怪物。floorId为楼层ID,可忽略表示当前楼层。 +检查某个楼层是否还有剩余的(指定)怪物。 +floorId为楼层ID,可忽略表示当前楼层。也可以填数组如["MT0","MT1"]同时检测多个楼层。 enemyId如果不填或null则检查是否剩余任何怪物,否则只检查是否剩余指定的某类怪物。 ``` @@ -806,16 +819,16 @@ seed为开始时要设置的的种子,route为要开始播放的录像,callb core.setInitData() 根据难度分歧来初始化难度,包括设置flag:hard,设置初始属性等。 -该函数实际被转发到了脚本编辑中。 +该函数实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.win(reason, norank) 游戏胜利,reason为结局名,norank如果为真则该结局不计入榜单。 -该函数实际被转发到了脚本编辑中。 +该函数实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.lose(reason) -游戏失败,reason为结局名。该函数实际被转发到了脚本编辑中。 +游戏失败,reason为结局名。该函数实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.gameOver(ending, fromReplay, norank) @@ -855,7 +868,7 @@ id为怪物的ID,x和y为怪物坐标,force如果为真将强制战斗,cal core.beforeBattle(enemyId, x, y) -战前事件。实际被转发到了脚本编辑中,可以在这里加上一些战前特效。 +战前事件。实际被转发到了脚本编辑中,执行脚本编辑中的内容,可以用于加上一些战前特效。 此函数在“检测能否战斗和自动存档”【之后】执行。 如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。 此函数返回true则将继续本次战斗,返回false将不再战斗。 @@ -863,7 +876,7 @@ core.beforeBattle(enemyId, x, y) core.afterBattle(enemyId, x, y, callback) 战后事件,将执行扣血、加金币经验、特殊属性处理、战后事件处理等操作。 -实际被转发到了脚本编辑中。 +实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.openDoor(x, y, needKey, callback) @@ -872,16 +885,17 @@ core.openDoor(x, y, needKey, callback) core.afterOpenDoor(doorId, x, y, callback) -开完一个门后执行的事件,实际被转发到了脚本编辑中。 +开完一个门后执行的事件,实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.getItem(id, num, x, y, callback) 获得若干个道具。itemId为道具ID,itemNum为获得的道具个数,不填默认为1。 -x和y为道具点的坐标,如果设置则会擦除地图上的该点。 +x和y为道具点的坐标,如果设置则会擦除地图上的该点,也可不填。 +譬如core.getItem("yellowKey",2)会直接获得两把黄钥匙。 core.afterGetItem(id, x, y, callback) -获得一个道具后执行的事件,实际被转发到了脚本编辑中。 +获得一个道具后执行的事件,实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.getNextItem(noRoute) @@ -918,7 +932,7 @@ core.pushBox(data) core.afterPushBox() -推箱子之后触发的事件,实际被转发到了脚本编辑中。 +推箱子之后触发的事件,实际被转发到了脚本编辑中,执行脚本编辑中的内容。 core.changeLight(id, x, y) @@ -951,10 +965,11 @@ core.startEvents(list, x, y, callback) 此函数将调用core.setEvents,然后停止勇士,再执行core.doAction()。 -core.doAction() +core.doAction(keepUI) 执行下一个自定义事件。 此函数将检测事件列表是否全部执行完毕,如果是则执行回调函数。 否则,将从事件列表中弹出下一个事件,并调用core.doEvent进行执行。 +如果keepUI为true,则不会清掉UI层和selector,适合于自己用脚本的绘制。 core.insertAction(action, x, y, callback, addToLast) @@ -972,6 +987,15 @@ core.getCommonEvent(name) core.recoverEvents(data) 恢复事件现场。一般用于呼出怪物手册、呼出存读档页面等时,恢复事件执行流。 + +core.checkAutoEvents() +检测自动事件并执行。 + + +core.precompile(events) +尝试预编译一段事件。 + + // ------ 点击状态栏图标时执行的一些操作 ------ // core.openBook(fromUserAction) @@ -1019,15 +1043,21 @@ core.follow(name) / core.unfollow(name) core.setValue(name, value, prefix) / core.addValue(name, value, prefix) 设置/增减某个数值。name可以是status:xxx,item:xxx或flag:xxx。 -value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用。 +value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用,脚本中一般忽略。 core.doEffect(effect, need, times) 执行一个effect操作。该函数目前仅被全局商店的status:xxx+=yyy所调用。 +core.setEnemy(id, name, value, prefix) +设置一个怪物属性。id为怪物的ID,name为要设置的项,比如hp,atk,def等等。 +value可以是一个表达式,将调用core.calValue()计算。prefix为前缀,独立开关使用,脚本中一般忽略。 + + core.setFloorInfo(name, values, floorId, prefix) -设置某层楼的楼层属性。 +设置某层楼的楼层属性,其中name为该楼层属性对应的条目,values为要设置的值,floorId为楼层id,prefix一般直接忽略。 +譬如core.setFloorInfo("name","4", "MT1")则为设置MT1显示在状态栏中的层数为4 core.setGlobalAttribute(name, value) @@ -1186,8 +1216,9 @@ core.addItem(itemId, itemNum) core.getEquipTypeByName(name) -根据装备位名称来找到一个空的装备孔,适用于多重装备。 +根据装备位名称来找到一个空的装备孔,适用于多重装备,装备位名称可在全塔属性中设置。 如果没有一个装备孔是该装备名称,则返回-1。 +譬如:core.getEquipTypeByName("武器")默认返回全塔属性中武器对应的装备孔号0。 core.getEquipTypeById(equipId) @@ -1200,11 +1231,12 @@ core.canEquip(equipId, hint) core.loadEquip(equipId, callback) -穿上某个装备。 +穿上某个装备,equipId为装备id。 core.unloadEquip(equipType, callback) 脱下某个装备孔的装备。 +譬如core.unloadEquip(0)则为脱下0号装备孔中的装备,默认0号装备孔对应“武器”,1号装备孔对应“盾牌” core.compareEquipment(compareEquipId, beComparedEquipId) @@ -1219,6 +1251,10 @@ core.quickSaveEquip(index) core.quickLoadEquip() 读取当前套装。index为读取的套装编号。 + + +core.getEquippedStatus(name) +获得装备直接增加的属性数据。 ``` ## loader.js @@ -1276,7 +1312,7 @@ map为存档信息,如果某项在map中不存在则会从core.floors中读取 core.getNumberById(id) -给定一个图块ID,找到对应的数字。 +给定一个图块ID,找到图块对应的图块编号。 core.initBlock(x, y, id, addInfo, eventFloor) @@ -1305,6 +1341,12 @@ core.loadMap(data, floorId) 从data中读取楼层数据,并调用core.loadFloor()进行初始化。 +core.removeMaps(fromId, toId) +删除某个区域的地图。调用此函数后,这些楼层将不可飞,不可被浏览地图,也不计入存档。 +fromId和toId为要删除的起终点楼层ID;toId也可以不填代表只删除某一层。 +此函数适用于高层塔的砍层,例如每100层一个区域且互相独立,不可再返回的情况。 + + core.resizeMap(floorId) 根据某层楼的地图大小来调整大地图的画布大小。floorId可为null表示当前层。 @@ -1412,7 +1454,7 @@ toDraw为要绘制到的信息(可为null,或为一个画布名),包括 // ------ 获得某个点的图块信息 ------ // core.noPass(x, y, floorId) -判定某个点是否有noPass的图块。 +判定某个点是否有noPass(不可通行)的图块。 core.npcExists(x, y, floorId) @@ -1420,7 +1462,7 @@ core.npcExists(x, y, floorId) core.terrainExists(x, y, id, floorId) -判定某个点是否有(指定的)地形存在。 +判定某个点是否有(id对应的)地形存在。 如果id为null,则只要存在terrains即为真,否则还会判定对应点的ID。 @@ -1433,7 +1475,7 @@ core.nearStair() core.enemyExists(x, y, id, floorId) -判定某个点是否有(指定的)怪物存在。 +判定某个点是否有(id对应的)怪物存在。 如果id为null,则只要存在怪物即为真,否则还会判定对应点的怪物ID。 请注意,如果需要判定某个楼层是否存在怪物请使用core.hasEnemyLeft()函数。 @@ -1490,14 +1532,17 @@ core.removeBlock(x, y, floorId) core.removeBlockById(index, floorId) +每个楼层的图块存成一个数组,index即为该数组中的索引,每个索引对应该地图中的一个图块 根据索引从地图的block数组中尽可能删除一个图块。floorId可不填或null表示当前楼层。 core.removeBlockByIds(floorId, ids) +ids为由索引组成的数组,如[0,1]等 根据索引数组从地图的block数组中尽可能删除一系列图块。floorId可不填或null表示当前楼层。 core.canRemoveBlock(block, floorId) +block为图块信息,可由core.getBlock获取 判定当前能否完全删除某个图块。floorId可不填或null表示当前楼层。 如果该点存在自定义事件,或者是重生怪,则不可进行删除。 @@ -1541,7 +1586,7 @@ number为要设置到的图块数字,x,y和floorId为目标点坐标和楼层 core.resetMap(floorId) 重置某层或若干层的地图和楼层属性。 -floorId可为某个楼层ID,或者一个楼层数组(同时重置若干层);如果不填则只重置当前楼层。 +floorId可为某个楼层ID,或者一个楼层数组如["MT1","MT2"](同时重置若干层);如果不填则只重置当前楼层。 // ------ 移动/跳跃图块,淡入淡出图块 ------ // @@ -1585,6 +1630,12 @@ core.drawAnimate(name, x, y, callback) 此函数会返回一个动画id,可以通过core.stopAnimate()立刻停止该动画的播放。 +core.drawHeroAnimate(name, callback) +绘制一个跟随勇士行动的动画。name为动画名,callback为绘制完毕的回调函数。 +此函数将播放动画音效,并异步开始绘制该动画。 +此函数会返回一个动画id,可以通过core.stopAnimate()立刻停止该动画的播放。 + + core.stopAnimate(id, doCallback) 立刻停止某个动画的播放。id为上面core.drawAnimate的返回值。 如果doCallback为真,则会执行该动画所对应的回调函数。 @@ -1614,11 +1665,16 @@ core.clearMap(name) 该函数的name也可以是'all',若为'all'则为清空所有系统画布。 -core.fillText(name, text, x, y, style, font) +core.fillText(name, text, x, y, style, font, maxWidth) 在某个画布上绘制一段文字。 text为要绘制的文本,x,y为要绘制的坐标,style可选为绘制的样式,font可选为绘制的字体。(下同) +style可直接使用"red","white"等或用"rgba(255,255,255,1)"或用"#FFFFFF"等方式来获得字体对应的颜色 +font的格式为"20px Verdana"前者为字体大小,后者为字体 +如果maxWidth不为null,则视为文字最大宽度,如果超过此宽度则会自动放缩文字直到自适应为止。 请注意textAlign和textBaseline将决定绘制的左右对齐和上下对齐方式。 具体可详见core.setTextAlign()和core.setTextBaseline()函数。 +譬如:core.fillText("ui", "这是要描绘的文字", 10, 10, "red", "20px Verdana", 100) +即是在ui图层上,以10,10为起始点,描绘20像素大小,Verdana字体的红色字,长度不超过100像素 core.fillBoldText(name, text, x, y, style, font) @@ -1626,7 +1682,7 @@ core.fillBoldText(name, text, x, y, style, font) core.fillRect(name, x, y, width, height, style) -绘制一个矩形。style可选为绘制样式。如果设置将调用core.setFillStyle()。(下同) +绘制一个矩形。width, height为矩形宽高,style可选为绘制样式。如果设置将调用core.setFillStyle()。(下同) core.strokeRect(name, x, y, width, height, style, lineWidth) @@ -1635,11 +1691,11 @@ lineWidth如果设置将调用core.setLineWidth()。(下同) core.drawLine(name, x1, y1, x2, y2, style, lineWidth) -绘制一条线。 +绘制一条线,x1y1为起始点像素,x2y2为终止点像素。 core.drawArrow(name, x1, y1, x2, y2, style, lineWidth) -绘制一个箭头。 +绘制一个箭头,x1y1为起始点像素,x2y2为终止点像素。 core.setFont(name, font) / core.setLineWidth(name, lineWidth) @@ -1661,7 +1717,7 @@ core.setFillStyle(name, style) / core.setStrokeStyle(name, style) core.setTextAlign(name, align) -设置一个画布的文字横向对齐模式,这里的align只能为'left', 'right'和'center'。 +设置一个画布的文字横向对齐模式,这里的align只能为'left', 'right'和'center',分别对应左对齐,右对齐,居中。 默认为'left'。 @@ -1692,6 +1748,13 @@ core.drawImage(name, image, x, y, w, h, x1, y1, w1, h1) http://www.w3school.com.cn/html5/canvas_drawimage.asp 这里的image允许传一个图片,画布。也允许传递图片名,将从你导入的图片中获取图片内容。 + +core.drawIcon(name, id, x, y, w, h) +在一张画布上绘制一个图标。 +id为注册过的图标ID,也可以使用状态栏的图标ID,例如lv, hp, up, save, settings等。 +x和y为绘制的左上角坐标;w和h可选为绘制的宽高,如果不填或null则使用该图标的默认宽高。 + + // ------ 具体的某个UI界面的绘制 ------ // core.closePanel() 结束一切事件和UI绘制,关闭UI窗口,返回游戏。 @@ -1702,16 +1765,21 @@ core.clearUI() 重置UI窗口。此函数将清掉所有的UI帧动画和光标,清空UI画布,并将alpha设为1。 -core.drawTip(text, id) +core.drawTip(text, id, clear) 在左上角以气泡的形式绘制一段提示。 text为文字内容,仅支持${}的表达式计算,不支持换行和变色。 id可选,为同时绘制的图标ID,如果不为null则会同时绘制该图标(仅对32x32的素材有效)。 也可以使用状态栏的图标ID,例如lv, hp, up, save, settings等。 +如果clear为true,则会清空当前所有正在显示的提示。 + + +core.clearTip() +清空当前所有正在显示的提示。 core.drawText(content, callback) 绘制一段文字。contents为一个字符串或一个字符串数组,callback为全部绘制完毕的回调。 -支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\t和\b的语法。 如果当前在事件处理中或录像回放中,则会自动转成core.insertAction处理。 不建议使用该函数,如有绘制文字的需求请尽量使用core.insertAction()插入剧情文本事件。 @@ -1736,7 +1804,8 @@ posInfo如果不为null则是一个含position, px和py的对象,表示一个 core.drawTextContent(ctx, content, config) 根据配置在某个画布上绘制一段文字。此函数会被core.drawTextBox()所调用。 ctx为画布名或画布本身,如果不设置则会忽略该函数。 -content为要绘制的文字内容,支持所有的文字效果(如\n,${},\r,\\i等),但不支持支持\t和\b的语法。 +content为要绘制的文字内容,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等) + ,但不支持支持\t和\b的语法。 config为绘制的配置项,目前可以包括如下几项: - left, top:在该画布上绘制的左上角像素位置,不设置默认为(0,0)。 > 该函数绘制时会将textBaseline设置为'top',因此只需要考虑第一个字的左上角位置。 @@ -1745,13 +1814,14 @@ config为绘制的配置项,目前可以包括如下几项: - bold:是否粗体。如果不设置默认为false。 - align:文字对齐方式,仅在maxWidth设置时有效,默认为'left'。 - fontSize:字体大小,如果不设置则使用剧情文本设置中的正文字体大小。 - - lineHeight:绘制的行距值,如果不设置则使用fontSize*1.3(即1.3被行距)。 + - lineHeight:绘制的行距值,如果不设置则使用fontSize*1.3(即1.3倍行距)。 - time:打字机效果。若不为0,则会逐个字进行绘制,并设置core.status.event.interval定时器。 + - interval:字符间的间距。值表示绘制每个字符之间间隔的距离,默认为0。 core.drawTextBox(content, showAll) 绘制一个对话框。content为一个字符串或一个字符串数组。 -支持所有的文字效果(如\n,${},\r,\\i等),也支持\t和\b的语法。 +支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\t和\b的语法。 该函数将使用用户在剧情文本设置中的配置项进行绘制。 实际执行时,会计算文本框宽度并绘制背景,绘制标题和头像,再调用core.drawTextContent()绘制正文内容。 showAll可选,如果为true则不会使用打字机效果而全部显示,主要用于打字机效果的点击显示全部。 @@ -1759,7 +1829,7 @@ showAll可选,如果为true则不会使用打字机效果而全部显示,主 core.drawScrollText(content, time, lineHeight, callback) 绘制一个滚动字幕。content为绘制内容,time为总时间(默认为5000),lineHeight为行距比例(默认为1.4)。 -滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i等),但不支持\t和\b效果。 +滚动字幕将绘制在UI上,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),但不支持\t和\b效果。 可以通过剧情文本设置中的align控制是否居中绘制,offset控制其距离左边的偏移量。 @@ -1771,7 +1841,7 @@ core.textImage(content, lineHeight) core.drawChoices(content, choices) 绘制一个选项框。 -content可选,为选项上方的提示文字,支持所有的文字效果(如\n,${},\r,\\i等),也支持\t效果。 +content可选,为选项上方的提示文字,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),也支持\t。 choices必选,为要绘制的选项内容,是一个列表。其中的每一项: - 可以是一个字符串,表示选项文字,将使用剧情文本设置中的正文颜色来绘制,仅支持${}表达式计算。 - 或者是一个包含text, color和icon的对象。 @@ -1838,11 +1908,14 @@ z值为创建的纵向高度(关系到画布之间的覆盖),z值高的将 core.ui.relocateCanvas(name, x, y) -重新定位一个自定义画布。x和y为画布的左上角坐标。 +重新定位一个自定义画布。x和y为画布的左上角坐标,name为画布名。 -core.ui.resizeCanvas(name, width, height) -重新设置一个自定义画布的大小。width和height为新设置的宽高。此操作会清空画布。 +core.ui.resizeCanvas(name, width, height, styleOnly) +重新设置一个自定义画布的大小。width和height为新设置的宽高。 +styleOnly控制是否只修改画布的显示大小(而不修改画布的内部大小)。 +如果styleOnly为true,则只修改其显示大小(即canvas.style.width); +否则,则会同时修改画布的显示大小和内部大小并清空画布内容。 core.ui.deleteCanvas(name) @@ -1862,6 +1935,11 @@ core.replayText(text, need, times) 将一段文字中的${}(表达式)进行替换。need和time一般可以直接忽略。 +core.replaceValue(value) +对一个表达式中的特殊规则进行替换,如status:xxx等。 +请注意,此项不会对独立开关如switch:A进行替换。 + + core.calValue(value, prefix, need, time) 计算一个表达式的值,支持status:xxx等的计算。 prefix为前缀(switch:xxx的独立开关使用),need和time一般可以直接忽略。 @@ -1914,6 +1992,17 @@ errorCallback可选,如果失败,则会将错误信息传入errorCallback() 此函数是异步的,只能通过回调函数来获得读取的结果或错误信息。 +core.setGlobal(key, value) +设置一个全局存储,适用于global:xxx。 +录像播放时将忽略此函数,否则直接调用core.setLocalStorage。 + + +core.getGlobal(key, value) +获得一个全局存储,适用于global:xxx,支持录像。 +正常游戏时将使用core.getLocalStorage获得具体的数据,并将结果存放到录像中。 +录像播放时会直接从录像中获得对应的数据。 + + core.clone(data, filter, recursion) 深拷贝一个对象。有关浅拷贝,深拷贝,基本类型和引用类型等相关知识可参见: https://zhuanlan.zhihu.com/p/26282765 @@ -2013,6 +2102,7 @@ core.matchWildcard(pattern, string) core.encodeBase64(str) / core.decodeBase64(str) 将字符串进行base64加密或解密。 +可用于解压缩录像数据 core.convertBase(str, fromBase, toBase) @@ -2035,8 +2125,9 @@ num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一 即先生成一个真随机数,根据该数来推进伪随机的种子,这样就可以放心调用core.rand()啦。 -core.readFile(success, error) +core.readFile(success, error, accept) 读取一个本地文件内容。success和error分别为读取成功或失败的回调函数。 +accept如果设置则控制能选择的文件类型。 iOS平台暂不支持读取文件操作。 @@ -2087,7 +2178,16 @@ core.same(a, b) 如果a和b都是数组,则会递归依次比较数组中的值;如果都是对象亦然。 -core.utils.http(type, url, formData, success, error, mimeType, responseType) +core.unzip(blobOrUrl, success, error, convertToText) +解压一个zip文件。 +blobOrUrl为传入的二进制zip文件Blob格式,或zip文件的地址。 +success为成功后的回调,接收 文件名-文件内容 形式的对象,即 +{"filename1": ..., "filename2": ...} +error为失败的回调,接收参数message为错误信息。 +convertToText如果为true则会将每个文件内容转成纯文本而不是二进制格式。 + + +core.http(type, url, formData, success, error, mimeType, responseType) 发送一个异步HTTP请求。 type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 success为成功后的回调,error为失败后的回调。 diff --git a/_docs/element.md b/_docs/element.md index 4fed9b92..2b386a88 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 @@ -282,10 +282,13 @@ floorId指定的是目标楼层的唯一标识符(ID)。 - 使用`${}`来计算一个表达式的值,如`${status:atk+status:def}`。 - 使用`\f[...]`来同时插入一张立绘图,如`\f[1.png,100,200]`。 - 使用`\\i[...]`来在对话框中绘制一个图标,如`\\i[fly]`。 +- 使用`\\c[...]`来修改字体大小,如`\\b[16]`。 +- 使用`\\d`来加粗或者取消粗体。 +- 使用`\\e`来加斜体或取消斜体。 从V2.5.2开始,也允许绘制一张头像图在对话框中,只要通过`\t[1.png]`或`\t[标题,1.png]`的写法。 -**使用`\\i[...]`绘制图标请注意:在事件块中,允许只写一个反斜杠`\i`,系统会自动转义成`\\i`;但是在脚本中必须两个反斜杠都写上!** +**使用`\\i,\\c,\\d,\\e`时请注意:在事件块中,允许只写一个反斜杠`\`,系统会自动转义成`\\`;但是在脚本中必须两个反斜杠都写上!** 详细信息请参见[剧情文本控制](event#text:显示一段文字(剧情))中的说明。 diff --git a/_docs/event.md b/_docs/event.md index bb586464..cce0cbe6 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 @@ -24,7 +24,7 @@ 它能通过拖动、复制粘贴等方式帮助你快速生成事件列表,而不用手动打大量字符。 -下述所说的都是在事件编辑器右边所展示的,该事件的代码化写法。 +下述所说的都是在事件编辑器右边所展示的,该事件的代码化写法;部分增加了可视化事件编辑器的截图示意(感谢秋橙的制作)。 强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 @@ -154,6 +154,8 @@ ] ``` +![](img/events/1.jpg) + 该项可以简写成直接的字符串的形式,即下面这种方式也是可以的: ``` js @@ -188,6 +190,10 @@ 从V2.5.2以后,新增了绘制大头像的功能。绘制大头像图的基本写法是`\t[1.png]`或者`\t[标题,1.png]`。 +从V2.6开始,所有图块都允许只写ID,对于非怪物则仅当图块属性中设置了name才有标题(否则不显示标题)。 + +另外注意的是,名字可以用null从而只显示动画而不显示标题。 + ``` js [ "一段普通文字", @@ -198,10 +204,15 @@ "\t[小妖精,fairy]这是一段小妖精说的话,使用仙子(fairy)的图标", "\t[你赢了]直接显示标题为【你赢了】", "\t[1.png]绘制1.png这个头像图", - "\t[标题,1.png]同时绘制标题和1.png这个头像图" + "\t[标题,1.png]同时绘制标题和1.png这个头像图", + "\t[sword1]获得铁剑,没有标题", + "\t[man]没有标题的npc动画", + "\t[null,greenSlime]只绘制怪物动画而不显示标题" ] ``` +![](img/events/2.jpg) + !> 大头像的头像图需要在全塔属性中注册,且必须是png格式,不可以用jpg或者其他格式,请自行转换。 除此以外,我们还能实现“对话框效果”,只要有`\b[...]`就可以。 @@ -227,9 +238,7 @@ ] ``` -从V2.6开始,`\b`提供了更多功能,包括: - -- `\b[hero]` +![](img/events/3.jpg) !> `\t[...]`必须在`\b[...]`前面!不然两者都无法正常显示。 @@ -243,10 +252,14 @@ ] ``` +![](img/events/4.jpg) + 从V2.5.3以后,也可以使用`\f[...]`来同时绘制一张图片。 其基本写法是`\f[img,x,y]`,或者`\f[img,x,y,w,h]`,或者`\f[img,sx,sy,sw,sh,x,y,w,h]`。 +从V2.6.3开始,也可以在最后加上alpha值,即`\f[img,sx,sy,sw,sh,x,y,w,h,alpha]`。 + 需要注意的是,这个图片是绘制在UI层上的,下一个事件执行时即会擦除;同时如果使用了\t的图标动画效果,重叠的地方也会被图标动画给覆盖掉。 ``` js @@ -254,10 +267,13 @@ "\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,64,64,128,128,100,100,128,128]裁剪1.png上以(64,64)开始的128x128图片,并绘制到画布的(100,100)处" + "\f[1.png,64,64,128,128,100,100,128,128]裁剪1.png上以(64,64)开始的128x128图片,并绘制到画布的(100,100)处", + "\f[1.png,64,64,128,128,100,100,128,128,0.5]同上,不透明度0.5", ] ``` +![](img/events/5.jpg) + 从V2.5.5以后,也可以使用`\\i[...]`来在对话框中绘制一个图标。 这里可以使用一个合法ID(32x48图块除外),或使用一个系统图标(`core.statusBar.icons`中的内容)。 @@ -269,10 +285,24 @@ ] ``` +![](img/events/6.jpg) + **可以在控制台中输入`core.statusBar.icons`以查看所有的系统图标定义。** !> 注意,在事件块中,允许只写一个反斜杠`\i`,系统会自动转义成`\\i`;但是在脚本中必须两个反斜杠都写上! +从V2.6.3开始,也可以使用`\\c[...]`来切换当前字体,`\\d`来加粗或取消粗体,`\\e`来加斜体或取消斜体。 + +``` js +[ + "这是原始字体,\\c[20]使用20号字体,\\c[10]使用10号字体", + "\\c如果不加中括号则切换回原始字体。", + "\\d这是粗体\\d取消粗体,\\e加斜体\\e取消斜体" +] +``` + +!> 注意,在事件块中,允许只写一个反斜杠`\c`,系统会自动转义成`\\c`;但是在脚本中必须两个反斜杠都写上!`\d`和`\e`同理。 + 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js @@ -283,18 +313,32 @@ 我们可以使用 `status:xxx` 代表勇士的一个属性值;`item:xxx` 代表某个道具的个数;`flag:xxx` 代表某个自定义的变量或flag值。 +从V2.6开始,也可以使用`global:xxx`代表全局存储(和存档无关的存储)。 + +从V2.6.5开始,也可以使用`enemy:id:atk`来获得某个怪物的属性,`blockId:x,y`来获得某个点的图块ID,`equip:x`来获得某个装备孔的装备ID。 + ``` js [ "你当前的攻击力是${status:atk}, 防御是${status:def},坐标是(${status:x},${status:y})", "你的攻防和的十倍是${10*(status:atk+status:def)}", "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", "你访问某个老人的次数为${flag:man_times}", + "当前的存档编号是${global:saveIndex}", + "绿色史莱姆的攻击力是${enemy:greenSlime:atk}", + "(2,3)点的图块ID是${blockId:2,3},图块类型是${blockCls:2,3}", + "装备孔0的当前装备ID是${equip:0}", ] ``` +![](img/events/7.png) + - `status:xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(魔防值),money(金币),experience(经验),x(勇士的横坐标),y(勇士的纵坐标),direction(勇士的方向)。 - `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 -- `flag:xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 +- `flag:xxx` 中的xxx为一个自定义的变量/Flag(支持中文);如果没有对其进行赋值则默认值为0。 +- `global:xxx` 中的xxx为一个全局存储的名称(支持中文);如果没有对其进行赋值则默认值为0。 +- `enemy:xxx:yyy` 中的xxx为怪物ID;yyy为要获得的项,比如hp, atk, def等等 +- `blockId:x,y` 和 `blockCls:x,y` 中的x,y为坐标值 +- `equip:x` 中的x为装备孔编号,从0开始。 ### autoText:自动剧情文本 @@ -306,6 +350,8 @@ ] ``` +![](img/events/8.jpg) + text为文本正文内容,和上面的写法完全一致。 time为可选项,代表该自动文本的时间。可以不指定,不指定默认为3000毫秒。 @@ -326,6 +372,8 @@ time为可选项,代表该自动文本的时间。可以不指定,不指定 ] ``` +![](img/events/9.jpg) + text为正文文本内容。可以使用`${ }`来计算表达式的值,且使用`\n`手动换行。系统不会对滚动剧情文本进行自动换行。 time为可选项,代表总的滚动时间。默认为5000毫秒。 @@ -351,6 +399,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ] ``` +![](img/events/10.jpg) + title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 默认值:`[255,215,0,1]` text为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示正文颜色。 默认值:`[255,255,255,1]` @@ -371,6 +421,8 @@ textfont为可选项,表示正文字体大小(px为单位)。默认值:` time为可选项,表示文字添加的速度。若此项设置为0将直接全部显示,若大于0则会设置为相邻字符依次显示的时间间隔。 默认值:`0` +interval为可选项,表示文字之间的间距。单位为像素值。默认值:`0` + ### tip:显示一段提示文字 `{"type": "tip"}`可以在左上角显示一段提示文字。 @@ -381,6 +433,8 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 ] ``` +![](img/events/11.jpg) + text必填,为显示的内容,支持`${}`的表达式计算。 icon是可选的,如果设置则会绘制图标,其可以是一个有效的ID,或者`core.statusBar.icons`中的系统图标。 @@ -395,6 +449,8 @@ icon是可选的,如果设置则会绘制图标,其可以是一个有效的I ] ``` +![](img/events/12.jpg) + 这个事件将在运行时被游戏跳过。 ### setValue:设置勇士的某个属性、道具个数,或某个变量/Flag的值 @@ -413,7 +469,7 @@ icon是可选的,如果设置则会绘制图标,其可以是一个有效的I name为你要修改的属性/道具/Flag,每次只能修改一个值。写法和上面完全相同,`status:xxx` 表示勇士一个属性,`item:xxx` 表示某个道具个数,`flag:xxx` 表示某个变量或flag值。参见上面的介绍。 -value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`的写法表示勇士当前属性,道具个数和某个变量/Flag值。 +value是一个表达式,将通过这个表达式计算出的结果赋值给name。该表达式同样可以使用`status:xxx`, `item:xxx`, `flag:xxx`, `global:xxx`的写法表示勇士当前属性,道具个数,某个变量/Flag值和某个全局存储值。 ``` js [ @@ -424,10 +480,16 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam {"type": "setValue", "name": "item:bomb", "value": "item:bomb+10" } // 炸弹个数+10 {"type": "setValue", "name": "flag:man_times", "value": "0" } // 将变量man_times设为0 {"type": "setValue", "name": "flag:man_times", "value": "flag:man_times+2*status:atk" } // 将变量man_times的值加上勇士的攻击数值的两倍 + {"type": "setValue", "name": "global:123", "value": "4"} // 设置全局存储233为4(任何存档都可以读取它) ] ``` -另外注意一点的是,如果hp被设置成了0或以下,将触发lose事件,直接死亡。 +![](img/events/13.jpg) + +从V2.6.5开始,当设置了`"norefresh": true`后可以不刷新状态栏、地图显伤和自动事件,从而加速事件执行。 + + +在刷新的情况下,如果hp被设置成了0或以下,将触发lose事件,直接死亡。 ### addValue:增减勇士的某个属性、道具个数,或某个变量/Flag的值 @@ -446,6 +508,30 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam ] ``` +![](img/events/14.jpg) + +从V2.6.5开始,当设置了`"norefresh": true`后可以不刷新状态栏、地图显伤和自动事件,从而加速事件执行。 + +### setEnemy:设置怪物属性 + +使用`{"type":"setEnemy"}`可以设置某个怪物的某个属性 + +``` js +[ + {"type": "setEnemy", "id": "greenSlime", "name": "hp", "value": "1000"}, // 设置绿色史莱姆生命1000 + {"type": "setEnemy", "id": "redSlime", "name": "special", "value": "[1,2]"}, // 设置红色史莱姆先攻魔攻 + {"type": "setEnemy", "id": "redSlime", "name": "name", "value": "'小史莱姆'"}, // 设置怪物名称 +] +``` + +![](img/events/15.png) + +id为必填项,代表要修改的怪物ID。 + +name为必填项,代表要修改的项,例如`hp`, `atk`, `def`, `money`, `experience`, `point`, `special`, `name`。 + +value为必填项,代表要修改到的内容。对于修改名称的,必须加单引号。 + ### setFloor:设置楼层属性 使用`{"type":"setFloor"}`可以设置某层楼的楼层属性。 @@ -462,6 +548,8 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam ] ``` +![](img/events/15.jpg) + name为必填项,代表要修改的楼层属性,和楼层属性中的一一对应。 floorId为可选项,代表要修改的楼层ID;可以省略代表当前楼层。 @@ -480,6 +568,8 @@ value为必填项,代表要修改到的数值。其应该和楼层属性中的 ] ``` +![](img/events/16.jpg) + name必填项,代表要修改的全局属性。 value为必填项,代表要修改到的结果。此项无需再手动加单引号。 @@ -494,6 +584,8 @@ value为必填项,代表要修改到的结果。此项无需再手动加单引 ] ``` +![](img/events/17.jpg) + name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。 value为必填项,代表要修改到的结果。该项必须是个数值。 @@ -508,6 +600,8 @@ value为必填项,代表要修改到的结果。该项必须是个数值。 ] ``` +![](img/events/18.jpg) + name必填项,代表要修改的系统开关,其是全塔属性中的flags中的一部分。 value为必填项,只能为true或false,代表要修改到的结果。 @@ -528,6 +622,8 @@ value为必填项,只能为true或false,代表要修改到的结果。 ] ``` +![](img/events/19.jpg) + show事件需要用loc指定目标点的坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。 从V2.2开始,loc也可以用变量来代替,例如 `"loc": ["flag:x", "flag:y"]`。下同。 @@ -566,28 +662,30 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` ] ``` +![](img/events/20.jpg) + ### trigger:立即触发另一个地点的事件 `{"type":"trigger"}` 会立刻触发当层另一个地点的自定义事件。 -上面我们说到,show事件会让一个禁用事件启用且可被交互;但是如果我想立刻让它执行应该怎么办呢?使用trigger就行。 - 其基本写法如下: ``` js [ {"type": "trigger", "loc": [3,6]}, // 立即触发loc位置的事件,当前剩下的事件全部不再执行 - "执行trigger后,这段文字将不会再被显示" + {"type": "trigger", "loc": [3,6], "keep": true}, // 触发loc位置的事件,不结束当前事件 ] ``` +![](img/events/21.jpg) + 其后面带有loc选项,代表另一个地点的坐标。 -执行trigger事件后,当前事件将立刻被结束,剩下所有内容被忽略;然后重新启动另一个地点的action事件。 +keep可选,如果此项为true则不会结束当前的事件列表,否则会中断当前的事件流。 -例如上面这个例子,下面的文字将不会再被显示,而是直接跳转到`"3,6"`对应的事件列表从头执行。 +从V2.5.4开始允许执行另一个点的系统事件,如打怪开门拾取道具等等,对应点的战后等事件也会被插入执行。 -!> 从V2.5.4开始,允许执行另一个点的“系统事件”,如打怪、开门、拾取道具等等。对应点的战后事件等也会被执行。 +值得注意的是,此事件会**改变当前点坐标**为目标点。 ### insert:插入公共事件或另一个地点的事件并执行 @@ -606,6 +704,8 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` ] ``` +![](img/events/22.jpg) + `insert`的写法有两种,可以写`name`,或者`loc`。 - 如果写了`"name": "xxx"`,则会去公共事件列表中找寻对应的事件,并执行。 @@ -615,11 +715,12 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` - floorId可选,代表另一个地点所在的楼层;如果不写则默认为当前层。 - 从V2.6开始,还可以传可选的which,可以为`afterBattle`/`afterGetItem`/`afterOpenDoor`,代表插入该点的战后/获得道具后/开门后事件。 -和`type:trigger`不同的是,**`type:trigger`是立刻将当前事件结束(剩下所有内容都忽略),然后重新启动另一个地点的action事件。** +和`type:trigger`不同点如下: +- `type:trigger`只能指定当前楼层且会改变当前点坐标,`type:insert`可以跨楼层且不会改变当前点坐标。 +- `type:trigger`如果不设置`keep:true`则还会结束当前事件,`type:insert`而是将另一个点的事件插入到当前事件列表中执行。 +- `type:trigger`可以触发系统事件,`type:insert`只能触发该点的自定义事件或战后事件等。 -但是`type:insert`不会结束当前事件,而是直接将另一个地点的事件列表“插入”到当前事件列表中执行。 - -**这个过程中,当前事件不会被结束,当前的楼层和事件坐标不会发生改变。** 插入的事件执行完毕后,会继续执行接下来的内容。 +插入的事件执行完毕后,会继续执行接下来的内容。 从V2.6开始,插入事件允许传参。如果需要传参,则需要增加一个`args`数组。 @@ -642,6 +743,8 @@ revisit和trigger完全相同,只不过是立刻触发的还是本地点的事 ] ``` +![](img/events/23.jpg) + revisit其实是trigger的简写,只不过是loc固定为当前点。 revisit常常使用在一些商人之类的地方,当用户购买物品后不是离开,而是立刻重新访问重新进入购买页面。 @@ -661,6 +764,8 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ] ``` +![](img/events/24.jpg) + ### setBlock:设置某个图块 我们可以采用 `{"type": "setBlock"}` 来改变某个地图块。 @@ -674,6 +779,8 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不 ] ``` +![](img/events/25.jpg) + floorId为可选的,表示要更改的目标楼层。如果忽略此项,则默认为当前楼层。 loc为可选的,表示要更改地图块的坐标。如果忽略此项,则默认为当前事件点。 @@ -704,6 +811,8 @@ number为**要更改到的数字**,有关“数字”的定义详见参见[素 ] ``` +![](img/events/26.jpg) + loc为要隐藏的贴图的左上角坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。 如果同时存在若干个贴图都是是该坐标为左上角,则这些贴图全部会被隐藏。 @@ -736,6 +845,8 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 ] ``` +![](img/events/27.jpg) + name为必选的,且只能是`bg`和`fg`之一,分别代表背景图层和前景图层。 loc为要隐藏的贴图的左上角坐标,可以简单的写[x,y]代表一个点,也可以写个二维数组[[x1,y1],[x2,y2],...]来同时显示多个点。 @@ -766,6 +877,8 @@ floorId为目标点的楼层,如果是当前楼层可以忽略不写。 ] ``` +![](img/events/28.jpg) + name为必选的,且只能是`bg`和`fg`之一,分别代表背景层和前景层。 floorId为可选的,表示要更改的目标楼层。如果忽略此项,则默认为当前楼层。 @@ -786,6 +899,8 @@ loc为可选的,表示要更改地图块的坐标。如果忽略此项,则 ] ``` +![](img/events/29.jpg) + name是可选的,代表目标行走图的文件名。 !> **目标行走图必须在全塔属性的this.images中被定义过,且宽度必须是128像素(高度不限)。** @@ -808,11 +923,13 @@ name是可选的,代表目标行走图的文件名。 使用`{"type": "showStatusBar"}`会重新显示状态栏。 -### updateEnemys:更新怪物数据 +### hideHero:隐藏勇士 -使用 `{"type": "updateEnemys"}` 可以动态修改怪物数据。 +使用`{"type": "hideHero"}`可以隐藏勇士。 -详见[怪物数据的动态修改](#怪物数据的动态修改)。 +### showHero:显示勇士 + +使用`{"type": "showHero"}`会重新显示勇士。 ### sleep:等待多少毫秒 @@ -828,6 +945,8 @@ name是可选的,代表目标行走图的文件名。 ] ``` +![](img/events/30.jpg) + 默认的等待事件可以被Ctrl跳过,下面两种情况下不可呗跳过: - 加上`"noSkip": true`后 - 当前存在尚未执行完毕的异步事件。 @@ -836,28 +955,22 @@ name是可选的,代表目标行走图的文件名。 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 -其基本写法是: `{"type": "battle", "id": xxx}`,其中xxx为怪物ID。 - ``` js [ - "\t[blackKing]你终于还是来了。", - "\t[hero]放开我们的公主!", - "\t[blackKing]如果我不愿意呢?", - "\t[hero]无需多说,拔剑吧!", - {"type": "battle", "id": "blackKing"}, // 强制战斗 - // 如果战斗失败直接死亡,不会继续触发接下来的剧情。 - {"type": "hide", "loc": [10,2]}, // 战斗后需要手动使怪物消失;战斗后不会引发afterBattle事件。 - {"type": "openDoor", "loc": [8,7]}, // 开门口的机关门 - "\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了,请好好对她。", - {"type": "hide"} // 隐藏本事件 + {"type": "battle", "id": "blackKing"}, // 强制战斗黑衣魔王 + {"type": "battle", "loc": [2,3]}, // 强制战斗(2,3)点的怪物 ], ``` -上面就是样板层中右上角的强制战斗例子。 +![](img/events/31.jpg) -如果强制战斗失败,则会立刻生命归0并死亡,调用lose函数,接下来的事件不会再被执行。 +从V2.6开始,有两种写法: +- 写id,则视为和空降怪物进行强制战斗,不会执行一些战后事件,也不会隐藏任何点。 +- 写loc(可选,不填默认当前点),则视为和某点怪物进行强制战斗,会隐藏该点并且插入该点战后事件执行。 -强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。 +强制战斗不会自动存档,如果战斗失败则立刻死亡。 + +值得注意的是,如果是和某个点的怪物强制战斗,并且该点存在战后事件,执行时会**修改当前点坐标**为目标点。 ### openDoor:开门 @@ -867,10 +980,12 @@ name是可选的,代表目标行走图的文件名。 [ {"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门 {"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId - {"type": "openDoor", "loc": [3,6], "needKey": true} // 打开此门需要钥匙 + {"type": "openDoor", "loc": [3,6], "needKey": true, "async": true} // 打开此门需要钥匙,异步执行 ] ``` +![](img/events/32.jpg) + loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层则可以忽略floorId选项。 如果loc所在的点是一个墙壁,则作为暗墙来开启。 @@ -879,8 +994,12 @@ loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层 needKey是可选的,如果设置为true则需要钥匙才能打开此门。如果没有钥匙则跳过此事件。 +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + !> needKey仅对当前楼层开门有效!跨楼层的门仍然不需要钥匙即可打开,如有需求请自行判定。 +值得注意的是,如果该点存在开门事件,执行时会**修改当前点坐标**为目标点。 + ### closeDoor:关门 从V2.6开始提供了关门事件`{"type": "closeDoor"}`,拥有关门动画和对应的音效。 @@ -892,6 +1011,8 @@ needKey是可选的,如果设置为true则需要钥匙才能打开此门。如 ] ``` +![](img/events/33.jpg) + id为你要关门的ID,需要是一个合法的门,系统默认只支持如下几种: ``` @@ -903,6 +1024,8 @@ yellowWall, blueWall, whiteWall loc可选,为要关的位置,不填默认为当前点。 +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + 关门事件需要保证该点是空地,否则将无视此事件。 ### changeFloor:楼层切换 @@ -918,6 +1041,8 @@ changeFloor的事件写法大致如下。 ] ``` +![](img/events/34.jpg) + 可以看到,与上面的楼梯、传送门的写法十分类似。 但是相比那个而言,不支持stair楼梯位置(只能写坐标),没有穿透选项。 @@ -942,24 +1067,54 @@ time为可选的,指定的话将作为楼层切换动画的时间。 ] ``` +![](img/events/35.jpg) + ### useItem:使用道具 调用`{"type": "useItem"}`可以使用一个道具。 ``` js [ - {"type": "changePos", "id": "pickaxe"}, // 尝试使用破 - {"type": "changePos", "id": "bomb"}, // 尝试使用炸 - {"type": "changePos", "id": "centerFly"} // 尝试使用飞 + {"type": "useItem", "id": "pickaxe"}, // 尝试使用破 + {"type": "useItem", "id": "bomb"}, // 尝试使用炸 + {"type": "useItem", "id": "centerFly"} // 尝试使用飞 ] ``` +![](img/events/36.jpg) + 使用道具事件会消耗对应的道具。 如果当前不可使用该道具(如没有,或者达不到使用条件),则会进行提示并跳过本事件。 不可使用“怪物手册”(请使用【呼出怪物手册】事件)或楼层传送器(如果[覆盖楼传事件](personalization#覆盖楼传事件)则可忽视本项)。 +### loadEquip:装上装备 + +使用`{"type": "loadEquip"}`可以装上一个装备。 + +``` js +[ + {"type": "loadEquip", "id": "sword1"}, // 尝试装上铁剑 +] +``` + +id必填,为需要装备的ID。 + +使用装备时仍会检查条件(比如装备是否存在,能否装备等等)。 + +### unloadEquip:卸下装备 + +使用`{"type": "unloadEquip"}`卸下某个装备孔的装备。 + +``` js +[ + {"type": "unloadEquip", "pos": 0}, // 卸下装备孔0的装备 +] +``` + +pos必填,为要卸下的装备孔编号,从0开始。 + ### openShop:打开一个全局商店 使用openShop可以打开一个全局商店。有关全局商店的说明可参见[全局商店](#全局商店)。 @@ -990,7 +1145,7 @@ name所指定的图片必须存在,在全塔属性中的images中被定义过 ``` js [ {"type": "unfollow", "name": "npc.png"}, // 将 npc.png 这个行走图取消跟随 - {"type": "follow"}, // 取消所有跟随 + {"type": "unfollow"}, // 取消所有跟随 ] ``` @@ -1023,6 +1178,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ] ``` +![](img/events/37.jpg) + name为动画名,**请确保动画在全塔属性中的animates中被定义过。** loc为动画的位置,可以是`[x,y]`表示在(x,y)点显示,也可以是字符串`"hero"`表示在勇士点显示。 @@ -1046,6 +1203,8 @@ loc可忽略,如果忽略则显示为事件当前点。 ] ``` +![](img/events/38.jpg) + code为图片编号,如果两张图片重叠,编号较大会覆盖编号较小的。该值需要在1~50之间。 image为图片名。**请确保图片在全塔属性中的images中被定义过。** @@ -1132,6 +1291,8 @@ loc为动图左上角坐标,以像素为单位进行计算。 ] ``` +![](img/events/39.jpg) + code为图片编号。该值需要在1~50之间。 to为终点图片左上角坐标,以像素为单位进行计算,不填写则视为当前图片位置。 @@ -1154,6 +1315,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ] ``` +![](img/events/40.jpg) + color为需要更改画面色调的颜色。它是一个数组,分别指定目标颜色的R,G,B,A值。 - 常见RGB颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,0,0],等等。 - 第四元为Alpha值,即不透明度,为一个0到1之间的数。可以不指定,则默认为Alpha=1 @@ -1221,6 +1384,8 @@ level为天气的强度等级,在1-10之间。1级为最弱,10级为最强 ] ``` +![](img/events/41.jpg) + time选项必须指定,为每移动一步所需要用到的时间。 loc为需要移动的事件位置。可以省略,如果省略则移动本事件。 @@ -1251,6 +1416,8 @@ keep为一个可选项,代表该事件移动完毕后是否消失。如果该 } ``` +![](img/events/42.jpg) + 即,在移动的到达点指定一个事件,然后move事件中指定"keep":true,然后就可以触发目标点的事件了。 async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 @@ -1329,6 +1496,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 目前支持mp3/ogg/wav等多种格式的音乐播放。 +从V2.6.3开始,还提供了keep项。如果此项为真,则会记录该bgm,并且持续到下次调用本事件位置(楼层切换不改变bgm,读档也有效)。 + 有关BGM播放的详细说明参见[背景音乐](element#背景音乐) ### pauseBgm:暂停背景音乐 @@ -1375,6 +1544,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 使用方法: `{"type": "setVolume", "value": 90, "time": 500, "async": true}` +![](img/events/43.jpg) + value为音量大小,在0到100之间,默认为100。设置后,BGM将使用该音量进行播放。SE的音量大小不会发生改变。 可以设置time为音量渐变时间。 @@ -1434,6 +1605,8 @@ async可选,如果为true则会异步执行(即不等待当前事件执行 ] ``` +![](img/events/44.jpg) + text为提示文字,可以在这里给输入提示文字。这里同样可以使用${ }来计算表达式的值。 当执行input事件时,将显示一个弹窗,并提示用户输入一个内容。 @@ -1453,6 +1626,8 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 ] ``` +![](img/events/45.jpg) + text为提示文字,可以在这里给输入提示文字。这里同样可以使用${ }来计算表达式的值。 当执行input2事件时,将显示一个弹窗,并提示用户输入一个内容。 @@ -1504,6 +1679,8 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 ] ``` +![](img/events/46.jpg) + 需要额外注意的几点: - 给定的表达式(condition)一般需要返回true或false。 @@ -1569,6 +1746,8 @@ nobreak是可选的,如果设置,则在当前条件满足并插入事件后 ] ``` +![](img/events/47.jpg) + 需要额外注意的几点: - 各个条件分支的判断是顺序执行的,因此若多个分支的条件都满足,将只执行最靠前的分支。 @@ -1597,6 +1776,7 @@ nobreak是可选的,如果设置,则在当前条件满足并插入事件后 ] ``` +![](img/events/48.jpg) ### choices:给用户提供选项 @@ -1702,12 +1882,16 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ] ``` +![](img/events/49.jpg) + ### dowhile:后置条件循环 `type:dowhile`可以制作一个后置条件循环。 其写法与参数和`type:while`完全一致,不过与其不同的是,会先执行一次事件列表,再对条件进行判定,就和C/C++中的 `do {...} while (...);` 语法一样。 +![](img/events/50.jpg) + ### break:跳出循环 使用 `{"type": "break"}` 可以跳出当前循环。 @@ -1737,6 +1921,8 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ] ``` +![](img/events/51.jpg) + !> 如果continue事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! ### wait:等待用户操作 @@ -1776,6 +1962,33 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 ] ``` +![](img/events/52.jpg) + +从V2.6.6开始,也允许直接在`type:wait`中增加`data`项判定按键或点击坐标。 + +```js +[ + {"type": "wait", "data": [ + {"case": "keyboard", "keycode": 13, "action": [ + {"type": "comment", "text": "当按下回车(keycode=13)时执行此事件"}, + ]}, + {"case": "mouse", "px": [0,32], "py": [0,32], "action": [ + {"type": "comment", "text": "当点击地图左上角时执行此事件"}, + ]}, + ]}, +] +``` + +![](img/events/52.png) + +`data`是一个数组,每一项中,case只能为`keyboard`和`mouse`二选一,分别对应键盘和鼠标(即`type=0`和`type=1`)。 + +如果是键盘,则可以指定`keycode`为键盘的按键内容;否则指定`px`和`py`为点击的像素区间。 + +action为如果满足该条件时应该执行的事件列表。 + + + ### waitAsync:等待所有异步事件执行完毕 上面有很多很多的异步事件(也就是执行时不等待执行完毕)。 @@ -1789,6 +2002,290 @@ yes和no均为必填项,即用户点击确认或取消后执行的事件。 该事件会进行等待,直到所有可能的异步事件(异步动画除外)执行完毕。 +### previewUI:UI绘制并预览 + +此项可在地图编辑器中预览UI界面的绘制效果。 + +在编辑器中将会把此项包含的所有UI绘制事件进行绘制从而可以进行预览。 + +值得注意的是,在游戏中,UI绘制事件都是绘制在uievent层上的。 + +### clearMap:清除画布 + +UI绘制事件。 + +`{"type": "clearMap"}`可以清除`uievent`画布的内容。 + +```js +[ + {"type": "clearMap", "x": 0, "y": 0, "width": "flag:width", "height": 416}, // 清除画布的一部分 + {"type": "clearMap"}, // 清空并删除画布 +] +``` + +x, y, width, height均可选,表示要清除的坐标和长宽。也可以使用`flag:xxx`。 + +如果存在某一项不填则会清空全部画布并删除。 + +### setAttribute:设置画布属性 + +UI绘制事件。 + +此项可以设置`uievent`画布的各项属性。 + +```js +[ + {"type": "setAttribute", "font": "17px Verdana", "fillStyle": [255,0,0,1]}, +] +``` + +可以选择性的设置如下几项内容: +- `font`:字体,必须是`[italic] [bold] 14px Verdana`这种形式 +- `fillStyle`:填充样式,必须是三元组RGB或四元组RGBA +- `strokeStyle`:边框样式,必须是三元组RGB或者四元组RGBA +- `lineWidth`:线宽度,必须是正整数 +- `alpha`:不透明度,必须是0到1之间的浮点数 +- `align`:对齐方式,只能是`left`, `center`, `right`,分别代表左对齐,居中和右对齐 +- `baseline`:基准线,只能是`top`, `middle`, `alphabetic`, `bottom`,分别代表顶部,居中,标准值和底部。 +- `z`:画布的z值,必须是正整数。初始创建时画布的z值是135。请注意,闪烁光标所在画布的z值永远比该画布大1。 + +### fillText:绘制文本 + +UI绘制事件。 + +此项可以绘制一行文本。 + +```js +[ + {"type": "fillText", "text":"要绘制的文本", "x": 10, "y": 20, "maxWidth": 50} +] +``` + +text必填,为要绘制的文本,支持`${}`的写法,不支持一切转义字符或换行符。 + +x和y必填,为要绘制的左上角坐标。请使用`setAttribute`来设置绘制的对齐方式和基准线。 + +style可选,如果设置需要是三元组RGB或四元组RBGA,代表绘制样式。 + +font可选,如果设置则是要绘制的字体。 + +maxWidth可选,如果设置且不为0,则代表要绘制的最大宽度;超过此宽度会自动放缩。 + +### fillBoldText:绘制描边文本 + +UI绘制事件。 + +此项可以绘制一行描边文本。 + +```js +[ + {"type": "fillText", "text":"要绘制的描边文本", "x": 10, "y": 20, "style": [255,0,0,1]} +] +``` + +text必填,为要绘制的文本,支持`${}`的写法,不支持一切转义字符或换行符。 + +x和y必填,为要绘制的左上角坐标。请使用`setAttribute`来设置绘制的对齐方式和基准线。 + +style可选,如果设置需要是三元组RGB或四元组RBGA,代表绘制样式。 + +font可选,如果设置则是要绘制的字体。 + +### drawTextContent:绘制多行文本 + +UI绘制事件。 + +此项可以绘制多行文本。 + +```js +[ + {"type": "drawTextContent", "text":"要绘制的多行文本", "left": 10, "top": 20, "maxWidth": 100} +] +``` + +text必填,为要绘制的文本,支持所有的文字效果(如\n,${},\r,\\i,\\c,\\d,\\e等),但不支持支持\t和\b的语法。 + +left和top必填,为要绘制的起始像素坐标。实际绘制时会将textBaseline设置为'top',因此只需要考虑第一个字的左上角位置。 + +maxWidth可选,为单行最大宽度,超过此宽度将自动换行,不设置不会自动换行。 + +color可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。如果不设置则使用剧情文本设置中的正文颜色。 + +bold可选,是否粗体。如果不设置默认为false。 + +align可选,文字对齐方式,仅在maxWidth设置时有效,默认为'left'。 + +fontSize可选,为字体大小,如果不设置则使用剧情文本设置中的正文字体大小。 + +lineHeight可选,绘制的行距值,如果不设置则使用fontSize*1.3(即1.3倍行距)。 + +此项不支持字体样式的设置,使用的是全塔属性中的全局字体;如有需要请使用“设置全局属性”事件来设置字体样式。 + +### fillRect:绘制矩形 + +UI绘制事件。此项可以绘制一个矩形。 + +```js +[ + {"type": "fillRect", "x": 100, "y": 100, "width": 120, "height": 120, "style": [255,0,0,1]} +] +``` + +x, y, width, height必填,为要绘制的起点坐标和宽高;也可以用`flag:xxx`。 + +color可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 + +### strokeRect:绘制矩形边框 + +UI绘制事件。此项可以绘制一个矩形边框。 + +```js +[ + {"type": "strokeRect", "x": 100, "y": 100, "width": 120, "height": 120, "style": [255,0,0,1], "lineWidth": 4} +] +``` + +x, y, width, height必填,为要绘制的起点坐标和宽高;也可以用`flag:xxx`。 + +style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 + +lineWidth可选,表示边框的线宽。 + +### drawLine:绘制线段 + +UI绘制事件。此事件可以绘制一个函数。 + +```js +[ + {"type": "drawLine", "x1": 0, "y1": 0, "x2": "flag:x", "y2": 200, "style": [255,0,0,1]} +] +``` + +x1, y1, x2, x2必填,为要绘制的起点和终点坐标;也可以用`flag:xxx`的写法。 + +style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 + +lineWidth可选,表示边框的线宽。 + +### drawArrow:绘制箭头 + +UI绘制事件。此事件可以绘制一个箭头。 + +参数和写法与`drawLine`完全一致,只不过是会多画一个箭头标记。 + +### fillPolygon:绘制多边形 + +UI绘制事件。此事件可以绘制一个多边形。 + +```js +[ + {"type": "fillPolygon", "nodes": [[0,0],[0,100],[100,0]], "style": [255,0,0,1]} +] +``` + +nodes必填,为一个二维数组,其中每一项都是多边形一个顶点坐标。(与显示/隐藏事件写法相同) + +style可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 + +### strokePolygon:绘制多边形边框 + +UI绘制事件。此事件可以绘制一个多边形边框。 + +参数列表和`fillPolygon`基本相同,不过多了一个`lineWidth`表示的绘制线宽。 + +### fillCircle:绘制圆 + +UI绘制事件。此项可以绘制一个圆。 + +```js +[ + {"type": "fillCircle", "x": 100, "y": 100, "r": 10, "style": [255,0,0,1]} +] +``` + +x, y, r必填,为要绘制的圆心和半径;也可以用`flag:xxx`。 + +color可选,表示绘制时的颜色,为三元组RGB或四元组RGBA。 + +### strokeCircle:绘制圆边框 + +UI绘制事件。此项可以绘制一个圆边框。 + +参数列表和`fillCircle`基本相同,不过多了一个`lineWidth`表示的绘制线宽。 + +### drawImage:绘制图片 + +UI绘制事件。此事件可以绘制一个图片。 + +```js +[ + {"type": "drawImage", "image": "bg.jpg", "x": 0, "y": 0}, // 在(0,0)绘制bg.jpg + {"type": "drawImage", "image": "bg.jpg", "x": 0, "y": 0, "w": 100, "h": 100}, // 在(0,0)绘制bg.jpg,且放缩到100x100 + // 裁剪并放缩图片 + {"type": "drawImage", "image": "bg.jpg", "x": 0, "y": 0, "w": 100, "h": 100, "x1": 0, "y1": 0, "w1": 100, "h1": 100} +] +``` + +image必填,为图片名。图片必须在全塔属性中被注册过。 + +此函数有三种写法: + +- 只写x和y:表示要绘制到的位置。 +- 写x, y, w, h:表示要绘制到的位置,且将图片放缩到指定宽高。 +- 写x, y, w, h, x1, y1, w1, h1:从原始图片上裁剪[x,y,w,h]的图片,并绘制画布上的[x1,y1,w1,h1] + +可以查看下面的文档以了解各项参数的信息: +http://www.w3school.com.cn/html5/canvas_drawimage.asp + +### drawIcon:绘制图标 + +UI绘制事件。此事件可以绘制一个图标。 + +```js +[ + {"type": "drawIcon", "id": "yellowKey", "x": 100, "y": 100}, // 在(100,100)绘制黄钥匙 +] +``` + +id必填,为要绘制的图标ID。可以是一个注册过的图标ID,也可以使用状态栏的图标ID,例如lv, hp, up, save, settings等。 + +x, y必填,为要绘制的左上角坐标。width和height可选,如果设置则会将图标放缩成对应的宽高。 + +### drawBackground:绘制背景图 + +UI绘制事件。此事件可以绘制一个背景图。 + +```js +[ + {"type": "drawBackground", "background": "winskin.png", "x": 0, "y": 0, "width": 100, "height": 100}, +] +``` + +background必填,为要绘制的背景图内容。其可以是一个三元组RGB或四元组RGBA(纯色绘制),或一个WindowSkin的图片名。 + +x, y, width, height必填,分别为要绘制的起点坐标和长宽。 + +可以使用“设置画布属性”来设置不透明度和纯色绘制时的边框颜色。 + +### drawSelector:绘制闪烁光标 + +UI绘制事件。此事件可以绘制闪烁光标。 + +```js +[ + {"type": "drawSelector", "image": "winskin.png", "x": 0, "y": 0, "width": 100, "height": 100, "clear": true}, + {"type": "drawSelector"} // 清除闪烁光标 +] +``` + +image为要绘制的WindowSkin图片名;如果不填则视为“清除闪烁光标”。 + +x, y, width, height分别为要绘制的起点坐标和长宽。clear可选,如果为true则在绘制前清空已有光标。 + +请注意,同时只会有一个闪烁光标存在,如果创建多个则后者会替换前者。 + +闪烁光标将会一直存在即使事件流结束;请使用本事件并不填`image`来清除闪烁光标。 + ### function: 自定义JS脚本 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 @@ -1825,11 +2322,31 @@ core.insertAction([ !> 从V2.5.3开始,提供了一个"不自动执行下一个事件"的选项(`"async": true`)。如果设置了此项,那么在该部分代码执行完毕后,不会立刻执行下一个事件。你需要在脚本中手动调用`core.events.doAction()`来执行下一个事件。可以通过此项来实现一些异步的代码,即在异步函数的回调中再执行下一个事件。使用此选项请谨慎,最好向开发者寻求咨询。 +## 自动事件 + +从V2.6.4开始,提供了自动事件。每个点都可以绑定若干个自动事件,其类似于RM的事件页。 + +自动事件可以设置一个触发条件,当满足此条件时将自动执行。 + +![](img/autoEvent.png) + +自动事件可以设置如下几项内容: + +- 条件:当满足此条件时将自动执行 +- 优先级:当多个自动事件的条件同时满足时,将按照优先级从大到小执行;相同优先级的按照楼层和坐标排序。 +- 仅在本层检测:是否仅在本层检测该条件。 +- 事件流中延迟执行:如果此项为true,则若满足条件时正在事件流的处理中,则将该自动事件延迟到事件流结束时执行。 +- 允许多次执行:如果此项为true,则该自动事件允许被多次触发;否则只会被触发一次。值得注意的是,即使允许多次触发,也不允许在正在执行本自动事件时再触发。(即在执行本自动事件时将暂时禁用自身,直到执行完毕为止) + +自动事件的检测时机为刷新状态栏,即每次刷新状态栏时都会进行检测。 + +可以给自动事件加上【转变图块】,从而达到类似RM的多事件页并转变图块的效果 + ## 独立开关 从V2.5.3开始,针对每个事件都提供了独立开关。 -独立开关的写法是`switch:A`, `switch:A`直到`switch:Z`,共计26个;不过样板中的值块默认只提供前6个。 +独立开关的写法是`switch:A`, `switch:A`直到`switch:Z`,共计26个。 独立开关算是特殊的flag,它在事件中使用时会和事件的楼层及坐标进行绑定;换句话说每个事件对应的`switch:A`都是不同的。 @@ -1840,6 +2357,8 @@ core.insertAction([ 通过独立开关的方式,我们无需对某些NPC的对话都设立单独的互不重复flag,只需要关注该事件自身的逻辑即可。 +![](img/events/53.jpg) + ## 同一个点的多事件处理 我们可以发现,就目前而且,每个点的事件是和该点进行绑定,并以该点坐标作为唯一索引来查询。 @@ -1884,6 +2403,8 @@ core.insertAction([ } ``` +![](img/events/54.jpg) + ### 获得圣水后变成墙 这个例子要求获得圣水时不前进(也就是不能走到圣水地方),然后把圣水位置变成墙。 @@ -1911,6 +2432,8 @@ core.insertAction([ } ``` +![](img/events/55.jpg) + 总之,记住如下两点: - 可以使用setBlock来更改一个图块。 @@ -2092,11 +2615,13 @@ if (core.flags.enableAddPoint && point > 0) { ] ``` +![](img/events/56.jpg) + `id`, `textInList`, `mustEnable`和上述完全相同。 `commonEvent`为公共事件名,即选择此项时要执行的公共事件。 -`args`为向该公共事件传递的参数,参见[type:insert](#insert:插入公共事件或另一个地点的事件并执行)的说明。 +`args`可选,为向该公共事件传递的参数,参见[type:insert](#insert:插入公共事件或另一个地点的事件并执行)的说明。 ## 系统引发的自定义事件 @@ -2134,6 +2659,8 @@ if (core.flags.enableAddPoint && point > 0) { }, ``` +![](img/events/60.jpg) + !> 多个机关门请分别设置开门变量如door1, door2等等。请勿存在两个机关门用相同的变量! 除此以外,每层楼还提供了`firstArrive`和`eachArrive`事件,分别为首次到达该楼层和每次到达该楼层时执行的事件。 @@ -2197,43 +2724,6 @@ if (core.flags.enableAddPoint && point > 0) { } ``` -## 怪物数据的动态修改 - -有时候我们可能还需要在游戏过程中动态修改怪物数据,例如50层魔塔的封印魔王,或者根据难度分歧来调整最终Boss的属性数据。 - -而在我们的存档中,是不会对怪物数据进行存储的,只会存各个变量和Flag,因此我们需要在读档后根据变量或Flag来调整怪物数据。 - -我们可以在脚本编辑中的`updateEnemys`进行处理。 - -``` js -"updateEnemys" : function () { - // 更新怪物数据,可以在这里对怪物属性和数据进行动态更新,详见文档——事件——怪物数据的动态修改 - // 比如下面这个例子,如果flag:xxx为真,则将绿头怪的攻击设为100,红头怪的金币设为20 - // 推荐写变化后的具体数值,以免多次变化导致冲突 - /* - // 如果flag:xxx为真;你也可以写其他判断语句比如core.hasItem(...)等等 - if (core.hasFlag('xxx')) { - core.material.enemys.greenSlime.atk = 100; - core.material.enemys.redSlime.money = 20; - } - */ - // 别忘了在事件中调用“更新怪物数据”事件! -} -``` - -当我们获得一个道具(或者触发某个事件等)后,需要在事件中调用“更新怪物数据”事件。 - -``` js -// 调用`updateEnemys`(更新怪物数据)事件就可以触发了 -[ - "将flag:xxx置为真,就可以让怪物数据发生改变!", - {"type": "setValue", "name": "flag:xxx", "value": "true"}, // 将flag:xxx置为真 - {"type": "updateEnemys"} // 更新怪物数据;此时绿头怪攻击就会变成100了 -] -``` - -事实上,除了动态修改怪物属性外,也可以在这里动态修改道具的类型、使用效果等等。都是一样的。 - ## 战前剧情 有时候光战后事件`afterBattle`是不够的,我们可能还需要战前剧情,例如Boss战之前和Boss进行一段对话。 @@ -2253,6 +2743,8 @@ if (core.flags.enableAddPoint && point > 0) { } ``` +![](img/events/57.jpg) + 另外,从V2.6开始,脚本编辑中提供了战前事件`beforeBattle`,这里不再详细展开,如有需求可自行前往研究。 ## 经验升级(进阶/境界塔) @@ -2282,6 +2774,8 @@ if (core.flags.enableAddPoint && point > 0) { ] ``` +![](img/events/58.jpg) + `levelUp`是一个数组,里面分别定义了每个等级的信息。里面每一项有三个参数`need`, `title`, `effect` - `need` 该等级所需要的经验值,可以是个表达式。请确保数组中的need依次递增。 - `title` 该等级的名称,比如“佣兵下级”等。该项可以忽略,以使用系统默认的等级。该项将显示在状态栏中。 @@ -2335,6 +2829,8 @@ if (core.flags.enableAddPoint && point > 0) { ] ``` +![](img/events/59.jpg) + ========================================================================================== [继续阅读下一章:个性化](personalization) diff --git a/_docs/img/autoEvent.png b/_docs/img/autoEvent.png new file mode 100644 index 00000000..06dd923e Binary files /dev/null and b/_docs/img/autoEvent.png differ diff --git a/_docs/img/events/1.jpg b/_docs/img/events/1.jpg new file mode 100644 index 00000000..6cc44ab5 Binary files /dev/null and b/_docs/img/events/1.jpg differ diff --git a/_docs/img/events/10.jpg b/_docs/img/events/10.jpg new file mode 100644 index 00000000..9e7166a6 Binary files /dev/null and b/_docs/img/events/10.jpg differ diff --git a/_docs/img/events/11.jpg b/_docs/img/events/11.jpg new file mode 100644 index 00000000..c6f3aa1b Binary files /dev/null and b/_docs/img/events/11.jpg differ diff --git a/_docs/img/events/12.jpg b/_docs/img/events/12.jpg new file mode 100644 index 00000000..12551990 Binary files /dev/null and b/_docs/img/events/12.jpg differ diff --git a/_docs/img/events/13.jpg b/_docs/img/events/13.jpg new file mode 100644 index 00000000..af81a7f0 Binary files /dev/null and b/_docs/img/events/13.jpg differ diff --git a/_docs/img/events/14.jpg b/_docs/img/events/14.jpg new file mode 100644 index 00000000..53221968 Binary files /dev/null and b/_docs/img/events/14.jpg differ diff --git a/_docs/img/events/15.jpg b/_docs/img/events/15.jpg new file mode 100644 index 00000000..14aa4cd2 Binary files /dev/null and b/_docs/img/events/15.jpg differ diff --git a/_docs/img/events/15.png b/_docs/img/events/15.png new file mode 100644 index 00000000..b1fe91cc Binary files /dev/null and b/_docs/img/events/15.png differ diff --git a/_docs/img/events/16.jpg b/_docs/img/events/16.jpg new file mode 100644 index 00000000..4e77816f Binary files /dev/null and b/_docs/img/events/16.jpg differ diff --git a/_docs/img/events/17.jpg b/_docs/img/events/17.jpg new file mode 100644 index 00000000..a8e94528 Binary files /dev/null and b/_docs/img/events/17.jpg differ diff --git a/_docs/img/events/18.jpg b/_docs/img/events/18.jpg new file mode 100644 index 00000000..8cbe6f94 Binary files /dev/null and b/_docs/img/events/18.jpg differ diff --git a/_docs/img/events/19.jpg b/_docs/img/events/19.jpg new file mode 100644 index 00000000..e250b7bd Binary files /dev/null and b/_docs/img/events/19.jpg differ diff --git a/_docs/img/events/2.jpg b/_docs/img/events/2.jpg new file mode 100644 index 00000000..e2461d34 Binary files /dev/null and b/_docs/img/events/2.jpg differ diff --git a/_docs/img/events/20.jpg b/_docs/img/events/20.jpg new file mode 100644 index 00000000..3c616d9e Binary files /dev/null and b/_docs/img/events/20.jpg differ diff --git a/_docs/img/events/21.jpg b/_docs/img/events/21.jpg new file mode 100644 index 00000000..6cccb422 Binary files /dev/null and b/_docs/img/events/21.jpg differ diff --git a/_docs/img/events/22.jpg b/_docs/img/events/22.jpg new file mode 100644 index 00000000..e814be0c Binary files /dev/null and b/_docs/img/events/22.jpg differ diff --git a/_docs/img/events/23.jpg b/_docs/img/events/23.jpg new file mode 100644 index 00000000..5cad5714 Binary files /dev/null and b/_docs/img/events/23.jpg differ diff --git a/_docs/img/events/24.jpg b/_docs/img/events/24.jpg new file mode 100644 index 00000000..2852e509 Binary files /dev/null and b/_docs/img/events/24.jpg differ diff --git a/_docs/img/events/25.jpg b/_docs/img/events/25.jpg new file mode 100644 index 00000000..d5492386 Binary files /dev/null and b/_docs/img/events/25.jpg differ diff --git a/_docs/img/events/26.jpg b/_docs/img/events/26.jpg new file mode 100644 index 00000000..6eaca21b Binary files /dev/null and b/_docs/img/events/26.jpg differ diff --git a/_docs/img/events/27.jpg b/_docs/img/events/27.jpg new file mode 100644 index 00000000..6c5b3a25 Binary files /dev/null and b/_docs/img/events/27.jpg differ diff --git a/_docs/img/events/28.jpg b/_docs/img/events/28.jpg new file mode 100644 index 00000000..5f6c6a19 Binary files /dev/null and b/_docs/img/events/28.jpg differ diff --git a/_docs/img/events/29.jpg b/_docs/img/events/29.jpg new file mode 100644 index 00000000..a04439c1 Binary files /dev/null and b/_docs/img/events/29.jpg differ diff --git a/_docs/img/events/3.jpg b/_docs/img/events/3.jpg new file mode 100644 index 00000000..b5aff14b Binary files /dev/null and b/_docs/img/events/3.jpg differ diff --git a/_docs/img/events/30.jpg b/_docs/img/events/30.jpg new file mode 100644 index 00000000..c1e9aca2 Binary files /dev/null and b/_docs/img/events/30.jpg differ diff --git a/_docs/img/events/31.jpg b/_docs/img/events/31.jpg new file mode 100644 index 00000000..663f3a30 Binary files /dev/null and b/_docs/img/events/31.jpg differ diff --git a/_docs/img/events/32.jpg b/_docs/img/events/32.jpg new file mode 100644 index 00000000..9a0fddfa Binary files /dev/null and b/_docs/img/events/32.jpg differ diff --git a/_docs/img/events/33.jpg b/_docs/img/events/33.jpg new file mode 100644 index 00000000..10246b96 Binary files /dev/null and b/_docs/img/events/33.jpg differ diff --git a/_docs/img/events/34.jpg b/_docs/img/events/34.jpg new file mode 100644 index 00000000..222ad18b Binary files /dev/null and b/_docs/img/events/34.jpg differ diff --git a/_docs/img/events/35.jpg b/_docs/img/events/35.jpg new file mode 100644 index 00000000..ff8ef556 Binary files /dev/null and b/_docs/img/events/35.jpg differ diff --git a/_docs/img/events/36.jpg b/_docs/img/events/36.jpg new file mode 100644 index 00000000..40a951fa Binary files /dev/null and b/_docs/img/events/36.jpg differ diff --git a/_docs/img/events/37.jpg b/_docs/img/events/37.jpg new file mode 100644 index 00000000..5267ed13 Binary files /dev/null and b/_docs/img/events/37.jpg differ diff --git a/_docs/img/events/38.jpg b/_docs/img/events/38.jpg new file mode 100644 index 00000000..b6ccf550 Binary files /dev/null and b/_docs/img/events/38.jpg differ diff --git a/_docs/img/events/39.jpg b/_docs/img/events/39.jpg new file mode 100644 index 00000000..d30c8d79 Binary files /dev/null and b/_docs/img/events/39.jpg differ diff --git a/_docs/img/events/4.jpg b/_docs/img/events/4.jpg new file mode 100644 index 00000000..d447565f Binary files /dev/null and b/_docs/img/events/4.jpg differ diff --git a/_docs/img/events/40.jpg b/_docs/img/events/40.jpg new file mode 100644 index 00000000..e10f56b7 Binary files /dev/null and b/_docs/img/events/40.jpg differ diff --git a/_docs/img/events/41.jpg b/_docs/img/events/41.jpg new file mode 100644 index 00000000..ec9b7e49 Binary files /dev/null and b/_docs/img/events/41.jpg differ diff --git a/_docs/img/events/42.jpg b/_docs/img/events/42.jpg new file mode 100644 index 00000000..24d0c2f4 Binary files /dev/null and b/_docs/img/events/42.jpg differ diff --git a/_docs/img/events/43.jpg b/_docs/img/events/43.jpg new file mode 100644 index 00000000..cb8f1c31 Binary files /dev/null and b/_docs/img/events/43.jpg differ diff --git a/_docs/img/events/44.jpg b/_docs/img/events/44.jpg new file mode 100644 index 00000000..59b3ecb7 Binary files /dev/null and b/_docs/img/events/44.jpg differ diff --git a/_docs/img/events/45.jpg b/_docs/img/events/45.jpg new file mode 100644 index 00000000..44dec550 Binary files /dev/null and b/_docs/img/events/45.jpg differ diff --git a/_docs/img/events/46.jpg b/_docs/img/events/46.jpg new file mode 100644 index 00000000..1eea4e37 Binary files /dev/null and b/_docs/img/events/46.jpg differ diff --git a/_docs/img/events/47.jpg b/_docs/img/events/47.jpg new file mode 100644 index 00000000..dd7bb88e Binary files /dev/null and b/_docs/img/events/47.jpg differ diff --git a/_docs/img/events/48.jpg b/_docs/img/events/48.jpg new file mode 100644 index 00000000..2086b1c3 Binary files /dev/null and b/_docs/img/events/48.jpg differ diff --git a/_docs/img/events/49.jpg b/_docs/img/events/49.jpg new file mode 100644 index 00000000..f81e0451 Binary files /dev/null and b/_docs/img/events/49.jpg differ diff --git a/_docs/img/events/5.jpg b/_docs/img/events/5.jpg new file mode 100644 index 00000000..333c972c Binary files /dev/null and b/_docs/img/events/5.jpg differ diff --git a/_docs/img/events/50.jpg b/_docs/img/events/50.jpg new file mode 100644 index 00000000..568eabf0 Binary files /dev/null and b/_docs/img/events/50.jpg differ diff --git a/_docs/img/events/51.jpg b/_docs/img/events/51.jpg new file mode 100644 index 00000000..1a3f70c3 Binary files /dev/null and b/_docs/img/events/51.jpg differ diff --git a/_docs/img/events/52.jpg b/_docs/img/events/52.jpg new file mode 100644 index 00000000..b66d2a22 Binary files /dev/null and b/_docs/img/events/52.jpg differ diff --git a/_docs/img/events/52.png b/_docs/img/events/52.png new file mode 100644 index 00000000..3c0c13c8 Binary files /dev/null and b/_docs/img/events/52.png differ diff --git a/_docs/img/events/53.jpg b/_docs/img/events/53.jpg new file mode 100644 index 00000000..c3e488bb Binary files /dev/null and b/_docs/img/events/53.jpg differ diff --git a/_docs/img/events/54.jpg b/_docs/img/events/54.jpg new file mode 100644 index 00000000..6dbfc95e Binary files /dev/null and b/_docs/img/events/54.jpg differ diff --git a/_docs/img/events/55.jpg b/_docs/img/events/55.jpg new file mode 100644 index 00000000..fff606c2 Binary files /dev/null and b/_docs/img/events/55.jpg differ diff --git a/_docs/img/events/56.jpg b/_docs/img/events/56.jpg new file mode 100644 index 00000000..15fac681 Binary files /dev/null and b/_docs/img/events/56.jpg differ diff --git a/_docs/img/events/57.jpg b/_docs/img/events/57.jpg new file mode 100644 index 00000000..60196800 Binary files /dev/null and b/_docs/img/events/57.jpg differ diff --git a/_docs/img/events/58.jpg b/_docs/img/events/58.jpg new file mode 100644 index 00000000..e9ff8af9 Binary files /dev/null and b/_docs/img/events/58.jpg differ diff --git a/_docs/img/events/59.jpg b/_docs/img/events/59.jpg new file mode 100644 index 00000000..48c58e01 Binary files /dev/null and b/_docs/img/events/59.jpg differ diff --git a/_docs/img/events/6.jpg b/_docs/img/events/6.jpg new file mode 100644 index 00000000..7be2810b Binary files /dev/null and b/_docs/img/events/6.jpg differ diff --git a/_docs/img/events/60.jpg b/_docs/img/events/60.jpg new file mode 100644 index 00000000..408cebb3 Binary files /dev/null and b/_docs/img/events/60.jpg differ diff --git a/_docs/img/events/7.jpg b/_docs/img/events/7.jpg new file mode 100644 index 00000000..546a8bab Binary files /dev/null and b/_docs/img/events/7.jpg differ diff --git a/_docs/img/events/7.png b/_docs/img/events/7.png new file mode 100644 index 00000000..a2f60686 Binary files /dev/null and b/_docs/img/events/7.png differ diff --git a/_docs/img/events/8.jpg b/_docs/img/events/8.jpg new file mode 100644 index 00000000..1c78a313 Binary files /dev/null and b/_docs/img/events/8.jpg differ diff --git a/_docs/img/events/9.jpg b/_docs/img/events/9.jpg new file mode 100644 index 00000000..bb6d752d Binary files /dev/null and b/_docs/img/events/9.jpg differ diff --git a/_docs/index.md b/_docs/index.md index d2f290e2..d3943d2c 100644 --- a/_docs/index.md +++ b/_docs/index.md @@ -1,6 +1,6 @@ # HTML5 魔塔样板说明文档 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 diff --git a/_docs/personalization.md b/_docs/personalization.md index c8f517e6..ca4755ae 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -1,6 +1,6 @@ # 个性化 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 @@ -22,6 +22,7 @@ HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们 - paint**[D]**:绘图层;主要用来进行绘图模式。(z-index: 95) - curtain:色调层;用来控制当前楼层的画面色调 (z-index: 125) - image1\~50**[D]**:图片层;用来绘制图片等操作。(z-index: 100+code, 101~150) +- uievent**[D]**:自定义UI绘制层;用来进行自定义UI绘制等操作。(z-index:135,可以通过事件设置该值) - ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 (z-index: 140) - data:数据层;用来绘制一些顶层的或更新比较快的数据,如左上角的提示,战斗界面中数据的变化等等。 (z-index: 170) @@ -29,6 +30,8 @@ HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们 而,色调层的z-index是25,ui层的z-index是140;因此,图片编号在1~24的将被色调层遮挡,25~40的将被ui层遮挡,41~50的将遮挡UI层。 +uievent层为自定义UI绘制所在的层,其z值初始是135,可以通过事件设置;自定义绘制的闪烁光标所在层的z值永远比该值大1。 + ### 动态创建canvas 从V2.5.3开始,可以在H5样板中任意动态创建canvas并进行使用。 @@ -559,8 +562,6 @@ core.statusBar.speed.innerHTML = core.getFlag('speed', 0); 如果flag:skill不为0,则代表当前处于某个技能开启状态,且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。 -!> 关于魔力上限:样板中默认没有提供status:manamax - ### 状态栏的显示 从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。 diff --git a/_docs/script.md b/_docs/script.md index 96a48838..76903d02 100644 --- a/_docs/script.md +++ b/_docs/script.md @@ -1,6 +1,6 @@ # 脚本 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 @@ -224,7 +224,11 @@ function () { - 清晰明了。很容易方便知道自己修改过什么,尤其是可以和系统原有代码进行对比。 - 方便整理成新的插件,给其他的塔使用。 -如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` +一般而言,复写规则如下: + +**对xxx文件中的yyy函数进行复写,规则是`core.xxx.yyy = function (参数列表) { ... }`。** + +但是,对于`registerXXX`所注册的函数是无效的,例如直接复写`core.control._animationFrame_globalAnimate`函数是没有效果的。对于这种情况引入的函数,需要注册同名函数,可参见最下面的样例。 下面是几个例子,从简单到复杂。 @@ -317,6 +321,32 @@ core.maps.drawMap = function (floorId, callback) { 详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 +### 复写全局动画绘制函数 + +全局动画绘制在`control.js`的`_animationFrame_globalAnimate`函数。 + +注意到此函数是由`registerAnimationFrame`注册的,因此直接复写是无效的。 + +其在control.js的注册的定义如下: + +```js +// 注册全局动画函数 +this.registerAnimationFrame("globalAnimate", true, this._animationFrame_globalAnimate); +``` + +因此,可以在插件中自行注册一个**同名**的函数来覆盖原始的内容。 + +```js +// 插件中复写全局动画绘制函数 +this.myGlobalAnimate = function (timestamp) { + // ...... 实际复写的函数内容 +} + +// 注册同名(globalAnimate)函数来覆盖系统原始内容 +core.registerAnimationFrame("globalAnimate", true, "myGlobalAnimate"); +``` + + ========================================================================================== [继续阅读下一章:API列表](api) diff --git a/_docs/start.md b/_docs/start.md index 004ea21f..880bfd08 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.6.6**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! @@ -235,7 +235,7 @@ HTML5的塔都是可以进行控制台调试的。 ## 编辑器的基本操作 -- **Alt+0~9, Ctrl+0~9** 保存和读取当前选中图块 +- **Alt+0~9, 0~9** 保存和读取当前选中图块 - **W/A/S/D** 移动大地图 - **Ctrl+Z** 撤销上次绘图 - **Ctrl+Y** 重做上次绘图 diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index b9bf42d0..5bc7ce65 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -10,7 +10,7 @@ event_m /* event_m tooltip : 编辑魔塔的事件 -helpUrl : https://h5mota.com/games/template/docs/#/event +helpUrl : https://h5mota.com/games/template/_docs/#/event default : [false,null,null,null,null] B_0_List_0=eval(B_0_List_0); var code = { @@ -26,6 +26,27 @@ return code; */; +//自动事件 事件编辑器入口之一 +autoEvent_m + : '自动事件:' '触发条件' EvalString '优先级' Int BGNL? Newline '仅在本层检测' Bool '事件流中延迟执行' Bool '允许多次执行' Bool BGNL? Newline action+ BEND + + +/* autoEvent_m +tooltip : ????? +helpUrl : https://h5mota.com/games/template/_docs/#/event +default : ["flag:__door__==2",0,true,false,false,null] +var code = { + "condition": EvalString_0, // 条件不可为null + "currentFloor": Bool_0, // 是否仅在本层检测 + "priority": Int_0, // 优先级 + "delayExecute": Bool_1, // 延迟执行 + "multiExecute": Bool_2, // 是否允许多次执行 + "data": 'autoEvent_asdfefw', // 事件列表 +}; +code=JSON.stringify(code,null,2).split('"autoEvent_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; + //升级 事件编辑器入口之一 level_m : '等级提升' BGNL? Newline levelCase+ BEND @@ -33,7 +54,7 @@ level_m /* level_m tooltip : 升级事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%bb%8f%e9%aa%8c%e5%8d%87%e7%ba%a7%ef%bc%88%e8%bf%9b%e9%98%b6%2f%e5%a2%83%e7%95%8c%e5%a1%94%ef%bc%89 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%bb%8f%e9%aa%8c%e5%8d%87%e7%ba%a7%ef%bc%88%e8%bf%9b%e9%98%b6%2f%e5%a2%83%e7%95%8c%e5%a1%94%ef%bc%89 var code = '[\n'+levelCase_0+']\n'; return code; */; @@ -44,7 +65,7 @@ levelCase /* levelCase tooltip : 升级设定 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%bb%8f%e9%aa%8c%e5%8d%87%e7%ba%a7%ef%bc%88%e8%bf%9b%e9%98%b6%2f%e5%a2%83%e7%95%8c%e5%a1%94%ef%bc%89 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%bb%8f%e9%aa%8c%e5%8d%87%e7%ba%a7%ef%bc%88%e8%bf%9b%e9%98%b6%2f%e5%a2%83%e7%95%8c%e5%a1%94%ef%bc%89 default : [0,"",false,null] colour : this.subColor Bool_0 = Bool_0?', "clear": true':''; @@ -58,13 +79,14 @@ shop_m /* shop_m tooltip : 全局商店列表 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 var code = '['+shoplist_0+']\n'; return code; */; shoplist : shopsub + | shopitem | shopcommonevent | emptyshop ; @@ -79,11 +101,11 @@ return code; */; shopcommonevent - : '商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? '执行的公共事件 id' EvalString '参数列表' EvalString? + : '公共事件版商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? '执行的公共事件 id' EvalString '参数列表' EvalString? /* shopcommonevent tooltip : 全局商店, 执行一个公共事件 -helpUrl : https://h5mota.com/games/template/docs/#/ +helpUrl : https://h5mota.com/games/template/_docs/#/ default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] if (EvalString_2) { if (EvalString_2.indexOf('"')>=0) @@ -109,12 +131,12 @@ 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 + : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '未开启状态则不显示在列表中' Bool BGNL? Newline '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND /* shopsub tooltip : 全局商店,消耗填-1表示每个选项的消耗不同,正数表示消耗数值 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 default : ["shop1","贪婪之神","blueShop","1F金币商店",false,false,null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] var code = { 'id': IdString_0, @@ -138,7 +160,7 @@ shopChoices /* shopChoices tooltip : 商店选项,商店消耗是-1时,这里的消耗对应各自选项的消耗,商店消耗不是-1时这里的消耗不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 default : ["攻击+1",""] colour : this.subColor EvalString_1 = EvalString_1 && (', "need": "'+EvalString_1+'"'); @@ -156,6 +178,45 @@ var code = idString_e_0+'+='+expression_0+';' return code; */; +shopitem + : '道具商店 id' IdString '快捷商店栏中名称' EvalString BGNL? '未开启状态则不显示在列表中' Bool BGNL? Newline shopItemChoices+ BEND + + +/* shopitem +tooltip : 道具商店 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 +default : ["itemShop","道具商店",false] +var code = { + 'id': IdString_0, + 'item': true, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'choices': 'choices_aqwedsa' +} +code=JSON.stringify(code,null,2).split('"choices_aqwedsa"').join('[\n'+shopItemChoices_0+']\n')+',\n'; +return code; +*/; + +shopItemChoices + : '道具名' IdString '存量' EvalString? '买入价格' EvalString? '卖出价格' EvalString? '出现条件' EvalString? BEND + + + +/* shopItemChoices +tooltip : 道具商店选项,每一项是道具名;买入或卖出可以不填表示只能卖出或买入 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 +default : ["yellowKey","","10","",""] +colour : this.subColor +if (EvalString_0 && !/^\d+$/.test(EvalString_0)) throw "存量必须不填或非负整数"; +EvalString_0 = EvalString_0 ? (', "number": '+EvalString_0) : ''; +EvalString_1 = EvalString_1 ? (', "money": "'+EvalString_1+'"') : ''; +EvalString_2 = EvalString_2 ? (', "sell": "'+EvalString_2+'"') : ''; +if (!EvalString_1 && !EvalString_2) throw "买入金额和卖出金额至少需要填写一个"; +EvalString_3 = EvalString_3 ? (', "condition": "'+EvalString_3+'"') : ''; +var code = '{"id": "' + IdString_0 + '"' + EvalString_0 + EvalString_1 + EvalString_2 + EvalString_3 + '},\n'; +return code; +*/; + //afterBattle 事件编辑器入口之一 afterBattle_m : '战斗结束后' BGNL? Newline action+ BEND @@ -163,7 +224,7 @@ afterBattle_m /* afterBattle_m tooltip : 系统引发的自定义事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 var code = '[\n'+action_0+']\n'; return code; */; @@ -175,7 +236,7 @@ afterGetItem_m /* afterGetItem_m tooltip : 系统引发的自定义事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 var code = '[\n'+action_0+']\n'; return code; */; @@ -187,7 +248,7 @@ afterOpenDoor_m /* afterOpenDoor_m tooltip : 系统引发的自定义事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 var code = '[\n'+action_0+']\n'; return code; */; @@ -199,7 +260,7 @@ firstArrive_m /* firstArrive_m tooltip : 首次到达楼层 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 var code = '[\n'+action_0+']\n'; return code; */; @@ -211,7 +272,7 @@ eachArrive_m /* eachArrive_m tooltip : 每次到达楼层 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=%e7%b3%bb%e7%bb%9f%e5%bc%95%e5%8f%91%e7%9a%84%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6 var code = '[\n'+action_0+']\n'; return code; */; @@ -223,12 +284,12 @@ changeFloor_m /* changeFloor_m tooltip : 楼梯, 传送门, 如果目标楼层有多个楼梯, 写upFloor或downFloor可能会导致到达的楼梯不确定, 这时候请使用loc方式来指定具体的点位置 -helpUrl : https://h5mota.com/games/template/docs/#/element?id=%e8%b7%af%e9%9a%9c%ef%bc%8c%e6%a5%bc%e6%a2%af%ef%bc%8c%e4%bc%a0%e9%80%81%e9%97%a8 -default : [null,"MT1",null,0,0,null,500,null] +helpUrl : https://h5mota.com/games/template/_docs/#/element?id=%e8%b7%af%e9%9a%9c%ef%bc%8c%e6%a5%bc%e6%a2%af%ef%bc%8c%e4%bc%a0%e9%80%81%e9%97%a8 +default : [null,"MTx",null,0,0,null,500,null] var toFloorId = IdString_0; if (Floor_List_0!='floorId') toFloorId = Floor_List_0; var loc = ', "loc": ['+Number_0+', '+Number_1+']'; -if (Stair_List_0==='now')loc = ''; +if (Stair_List_0===':now') loc = ''; else 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):''; @@ -244,7 +305,19 @@ commonEvent_m /* commonEvent_m tooltip : 公共事件 -helpUrl : https://h5mota.com/games/template/docs/#/event +helpUrl : https://h5mota.com/games/template/_docs/#/event +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//item 事件编辑器入口之一 +item_m + : '使用道具事件' BGNL? Newline action+ BEND + + +/* item_m +tooltip : 使用道具事件 +helpUrl : https://h5mota.com/games/template/_docs/#/event var code = '[\n'+action_0+']\n'; return code; */; @@ -261,6 +334,7 @@ action | tip_s | setValue_s | addValue_s + | setEnemy_s | setFloor_s | setGlobalAttribute_s | setGlobalValue_s @@ -282,17 +356,23 @@ action | update_s | showStatusBar_s | hideStatusBar_s - | updateEnemys_s + | showHero_s + | hideHero_s | sleep_s | wait_s | waitAsync_s | battle_s + | battle_1_s | openDoor_s | closeDoor_s | changeFloor_s | changePos_0_s | changePos_1_s + | setViewport_s + | moveViewport_s | useItem_s + | loadEquip_s + | unloadEquip_s | openShop_s | disableShop_s | follow_s @@ -340,6 +420,27 @@ action | callSave_s | autoSave_s | callLoad_s + | previewUI_s + | clearMap_s + | clearMap_1_s + | setAttribute_s + | fillText_s + | fillBoldText_s + | drawTextContent_s + | fillRect_s + | strokeRect_s + | drawLine_s + | drawArrow_s + | fillPolygon_s + | strokePolygon_s + | fillCircle_s + | strokeCircle_s + | drawImage_s + | drawImage_1_s + | drawIcon_s + | drawBackground_s + | drawSelector_s + | drawSelector_1_s | unknown_s | function_s | pass_s @@ -351,7 +452,7 @@ text_0_s /* text_0_s tooltip : text:显示一段文字(剧情) -helpUrl : https://h5mota.com/games/template/docs/#/event?id=text%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%96%87%E5%AD%97%EF%BC%88%E5%89%A7%E6%83%85%EF%BC%89 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=text%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%96%87%E5%AD%97%EF%BC%88%E5%89%A7%E6%83%85%EF%BC%89 default : ["欢迎使用事件编辑器(双击方块进入多行编辑)"] var code = '"'+EvalString_0+'",\n'; return code; @@ -363,7 +464,7 @@ text_1_s /* text_1_s tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=text%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%96%87%E5%AD%97%EF%BC%88%E5%89%A7%E6%83%85%EF%BC%89 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=text%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%96%87%E5%AD%97%EF%BC%88%E5%89%A7%E6%83%85%EF%BC%89 default : ["小妖精","fairy","","欢迎使用事件编辑器(双击方块进入多行编辑)"] var title=''; if (EvalString_0==''){ @@ -373,7 +474,7 @@ if (EvalString_0==''){ if (IdString_0=='')title='\\t['+EvalString_0+']'; else title='\\t['+EvalString_0+','+IdString_0+']'; } -if(EvalString_1 && !(/^(up|down)(,hero)?(,([+-]?\d+),([+-]?\d+))?$/.test(EvalString_1))) { +if(EvalString_1 && !(/^(up|center|down|hero|null)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_1))) { throw new Error('对话框效果的用法请右键点击帮助'); } EvalString_1 = EvalString_1 && ('\\b['+EvalString_1+']'); @@ -387,7 +488,7 @@ comment_s /* comment_s tooltip : comment:添加一段会被游戏跳过的注释内容 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=comment%ef%bc%9a%e6%b7%bb%e5%8a%a0%e6%b3%a8%e9%87%8a +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=comment%ef%bc%9a%e6%b7%bb%e5%8a%a0%e6%b3%a8%e9%87%8a default : ["可以在这里写添加任何注释内容"] colour : this.commentColor var code = '{"type": "comment", "text": "'+EvalString_0+'"},\n'; @@ -400,7 +501,7 @@ autoText_s /* autoText_s tooltip : autoText:自动剧情文本,用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=autotext%EF%BC%9A%E8%87%AA%E5%8A%A8%E5%89%A7%E6%83%85%E6%96%87%E6%9C%AC +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=autotext%EF%BC%9A%E8%87%AA%E5%8A%A8%E5%89%A7%E6%83%85%E6%96%87%E6%9C%AC default : ["小妖精","fairy","",3000,"用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示"] var title=''; if (EvalString_0==''){ @@ -424,7 +525,7 @@ scrollText_s /* 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 +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,1.4,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] Bool_0 = Bool_0?', "async": true':''; var code = '{"type": "scrollText", "text": "'+EvalString_0+'"'+Bool_0+', "time" :'+Int_0+', "lineHeight": '+Number_0+'},\n'; @@ -432,16 +533,16 @@ return code; */; setText_s - : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' EvalString? '对齐' SetTextAlign_List? BGNL? '标题颜色' EvalString? Colour '正文颜色' EvalString? Colour '背景色' EvalString? Colour BGNL? '粗体' B_1_List '标题字体大小' EvalString? '正文字体大小' EvalString? '打字间隔' EvalString? Newline + : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' EvalString? '对齐' TextAlign_List? BGNL? '标题颜色' EvalString? Colour '正文颜色' EvalString? Colour '背景色' EvalString? Colour BGNL? '粗体' B_1_List '标题字体大小' EvalString? '正文字体大小' 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,"",null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',null,"","",""] +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,"",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)?$/; +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +var colorRe = MotaActionFunctions.pattern.colorRe; if (EvalString_0) { if (!/^\d+$/.test(EvalString_0))throw new Error('像素偏移量必须是整数或不填'); EvalString_0 = ', "offset": '+EvalString_0; @@ -477,8 +578,12 @@ if (EvalString_6) { if (!/^\d+$/.test(EvalString_6))throw new Error('打字时间间隔必须是整数或不填'); EvalString_6 = ', "time": '+EvalString_6; } +if (EvalString_7) { + if (!/^\d+$/.test(EvalString_7))throw new Error('字符间距必须是整数或不填'); + EvalString_7 = ', "interval": '+EvalString_7; +} B_1_List_0 = B_1_List_0==='null'?'':', "bold": '+B_1_List_0; -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'; +var code = '{"type": "setText"'+SetTextPosition_List_0+EvalString_0+TextAlign_List_0+EvalString_1+EvalString_2+B_1_List_0+EvalString_3+EvalString_4+EvalString_5+EvalString_6+EvalString_7+'},\n'; return code; */; @@ -488,7 +593,7 @@ tip_s /* tip_s tooltip : tip:显示一段提示文字 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=tip%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%8F%90%E7%A4%BA%E6%96%87%E5%AD%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=tip%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%8F%90%E7%A4%BA%E6%96%87%E5%AD%97 default : ["这段话将在左上角以气泡形式显示",""] IdString_0 = IdString_0 && (', "icon": "' + IdString_0 + '"'); var code = '{"type": "tip", "text": "'+EvalString_0+'"'+IdString_0+'},\n'; @@ -496,36 +601,53 @@ return code; */; setValue_s - : '数值操作' ':' '名称' idString_e '值' expression Newline + : '数值操作' ':' '名称' idString_e '值' expression '不刷新状态栏' Bool Newline /* setValue_s tooltip : setValue:设置勇士的某个属性、道具个数, 或某个变量/Flag的值 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setvalue%EF%BC%9A%E8%AE%BE%E7%BD%AE%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%8Fflag%E7%9A%84%E5%80%BC +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setvalue%EF%BC%9A%E8%AE%BE%E7%BD%AE%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%8Fflag%E7%9A%84%E5%80%BC colour : this.dataColor -var code = '{"type": "setValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"},\n'; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"' + Bool_0 + '},\n'; return code; */; addValue_s - : '数值增减' ':' '名称' idString_e '+=' expression Newline + : '数值增减' ':' '名称' idString_e '+=' expression '不刷新状态栏' Bool Newline /* 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 +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": "addValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"},\n'; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "addValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"' + Bool_0 + '},\n'; return code; */; + +setEnemy_s + : '设置怪物属性' ':' '怪物ID' IdString '的' EnemyId_List '值' expression Newline + + +/* setEnemy_s +tooltip : setEnemy:设置某个怪物的属性 +default : ["greenSlime", "atk", "0"] +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": "setEnemy", "id": "'+IdString_0+'", "name": "'+EnemyId_List_0+'", "value": "'+expression_0+'"},\n'; +return code; +*/; + + setFloor_s : '设置楼层属性' ':' Floor_Meta_List '楼层名' IdString? '值' EvalString Newline /* setFloor_s tooltip : setFloor:设置楼层属性;该楼层属性和编辑器中的楼层属性一一对应 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setFloor%ef%bc%9a%e8%ae%be%e7%bd%ae%e6%a5%bc%e5%b1%82%e5%b1%9e%e6%80%a7 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setFloor%ef%bc%9a%e8%ae%be%e7%bd%ae%e6%a5%bc%e5%b1%82%e5%b1%9e%e6%80%a7 default : ["title","","'字符串类型的值要加引号,其他类型则不用'"] colour : this.dataColor IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); @@ -540,7 +662,7 @@ setGlobalAttribute_s /* setGlobalAttribute_s tooltip : setGlobalAttribute:设置全局属性 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setGlobalAttribute%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e5%85%a8%e5%b1%80%e5%b1%9e%e6%80%a7 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setGlobalAttribute%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e5%85%a8%e5%b1%80%e5%b1%9e%e6%80%a7 default : ["font","Verdana"] colour : this.dataColor var code = '{"type": "setGlobalAttribute", "name": "'+Global_Attribute_List_0+'", "value": "'+EvalString_0+'"},\n'; @@ -554,7 +676,7 @@ setGlobalValue_s /* setGlobalValue_s tooltip : setGlobalValue:设置全局属性 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setGlobalValue%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e5%85%a8%e5%b1%80%e6%95%b0%e5%80%bc +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setGlobalValue%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e5%85%a8%e5%b1%80%e6%95%b0%e5%80%bc default : ["lavaDamage","100"] colour : this.dataColor var code = '{"type": "setGlobalValue", "name": "'+Global_Value_List_0+'", "value": '+EvalString_0+'},\n'; @@ -568,7 +690,7 @@ setGlobalFlag_s /* setGlobalFlag_s tooltip : setGlobalFlag:设置系统开关 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setGlobalFlag%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e7%b3%bb%e7%bb%9f%e5%bc%80%e5%85%b3 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setGlobalFlag%ef%bc%9a%e8%ae%be%e7%bd%ae%e4%b8%80%e4%b8%aa%e7%b3%bb%e7%bb%9f%e5%bc%80%e5%85%b3 default : ["enableFloor","true"] colour : this.dataColor var code = '{"type": "setGlobalFlag", "name": "'+Global_Flag_List_0+'", "value": '+Bool_0+'},\n'; @@ -582,12 +704,12 @@ show_s /* show_s tooltip : show: 将禁用事件启用,楼层和动画时间可不填,xy可用逗号分隔表示多个点 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=show%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E7%A6%81%E7%94%A8%E4%BA%8B%E4%BB%B6%E5%90%AF%E7%94%A8 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=show%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E7%A6%81%E7%94%A8%E4%BA%8B%E4%BB%B6%E5%90%AF%E7%94%A8 default : ["","","",500,false] colour : this.mapColor var floorstr = ''; if (EvalString_0 && EvalString_1) { - var pattern1 = /^flag:[0-9a-zA-Z_][0-9a-zA-Z_\-:]*$/; + var pattern1 = MotaActionFunctions.pattern.id; if(pattern1.test(EvalString_0) || pattern1.test(EvalString_1)){ EvalString_0=MotaActionFunctions.PosString_pre(EvalString_0); EvalString_1=MotaActionFunctions.PosString_pre(EvalString_1); @@ -615,12 +737,12 @@ hide_s /* hide_s tooltip : hide: 将一个启用事件禁用,所有参数均可不填,代表禁用事件自身,xy可用逗号分隔表示多个点 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=hide%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E5%90%AF%E7%94%A8%E4%BA%8B%E4%BB%B6%E7%A6%81%E7%94%A8 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=hide%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E5%90%AF%E7%94%A8%E4%BA%8B%E4%BB%B6%E7%A6%81%E7%94%A8 default : ["","","",500,false] colour : this.mapColor var floorstr = ''; if (EvalString_0 && EvalString_1) { - var pattern1 = /^flag:[0-9a-zA-Z_][0-9a-zA-Z_\-:]*$/; + var pattern1 = MotaActionFunctions.pattern.id; if(pattern1.test(EvalString_0) || pattern1.test(EvalString_1)){ EvalString_0=MotaActionFunctions.PosString_pre(EvalString_0); EvalString_1=MotaActionFunctions.PosString_pre(EvalString_1); @@ -643,15 +765,16 @@ return code; */; trigger_s - : '触发事件' 'x' PosString ',' 'y' PosString Newline + : '触发事件' 'x' PosString ',' 'y' PosString '不结束当前事件' Bool Newline /* trigger_s tooltip : trigger: 立即触发另一个地点的事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=trigger%EF%BC%9A%E7%AB%8B%E5%8D%B3%E8%A7%A6%E5%8F%91%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%9C%B0%E7%82%B9%E7%9A%84%E4%BA%8B%E4%BB%B6 -default : ["0","0"] +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=trigger%EF%BC%9A%E7%AB%8B%E5%8D%B3%E8%A7%A6%E5%8F%91%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%9C%B0%E7%82%B9%E7%9A%84%E4%BA%8B%E4%BB%B6 +default : ["0","0",false] colour : this.eventColor -var code = '{"type": "trigger", "loc": ['+PosString_0+','+PosString_1+']},\n'; +Bool_0 = Bool_0 ?', "keep": true':''; +var code = '{"type": "trigger", "loc": ['+PosString_0+','+PosString_1+']'+Bool_0+'},\n'; return code; */; @@ -661,7 +784,7 @@ insert_1_s /* 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 +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 : ["加点事件", ""] colour : this.eventColor if (EvalString_1) { @@ -681,12 +804,12 @@ return code; */; insert_2_s - : '插入事件' 'x' PosString ',' 'y' PosString Event_List? '楼层' IdString? '参数列表' EvalString? ENewline + : '插入事件' 'x' PosString ',' 'y' PosString Event_List? '楼层' IdString? '参数列表' EvalString? Newline /* 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 +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",null,"",""] colour : this.eventColor IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); @@ -714,7 +837,7 @@ revisit_s /* revisit_s tooltip : revisit: 立即重启当前事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=revisit%EF%BC%9A%E7%AB%8B%E5%8D%B3%E9%87%8D%E5%90%AF%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=revisit%EF%BC%9A%E7%AB%8B%E5%8D%B3%E9%87%8D%E5%90%AF%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 colour : this.eventColor var code = '{"type": "revisit"},\n'; return code; @@ -726,24 +849,37 @@ exit_s /* exit_s tooltip : exit: 立刻结束当前事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=exit%EF%BC%9A%E7%AB%8B%E5%88%BB%E7%BB%93%E6%9D%9F%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=exit%EF%BC%9A%E7%AB%8B%E5%88%BB%E7%BB%93%E6%9D%9F%E5%BD%93%E5%89%8D%E4%BA%8B%E4%BB%B6 colour : this.eventColor var code = '{"type": "exit"},\n'; return code; */; setBlock_s - : '转变图块为' EvalString 'x' PosString? ',' 'y' PosString? '楼层' IdString? Newline + : '转变图块为' EvalString 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline /* setBlock_s tooltip : setBlock:设置某个图块,忽略坐标楼层则为当前事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 colour : this.mapColor default : ["yellowDoor","","",""] var floorstr = ''; -if (PosString_0 && PosString_1) { - floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +if (EvalString_1 && EvalString_2) { + var pattern1 = MotaActionFunctions.pattern.id; + if(pattern1.test(EvalString_1) || pattern1.test(EvalString_2)){ + EvalString_1=MotaActionFunctions.PosString_pre(EvalString_1); + EvalString_2=MotaActionFunctions.PosString_pre(EvalString_2); + EvalString_1=[EvalString_1,EvalString_2] + } else { + var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; + if(!pattern2.test(EvalString_1) || !pattern2.test(EvalString_2))throw new Error('坐标格式错误,请右键点击帮助查看格式'); + EvalString_1=EvalString_1.split(','); + EvalString_2=EvalString_2.split(','); + if(EvalString_1.length!==EvalString_2.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); + for(var ii=0;ii50) throw new Error('图片编号在1~50之间'); @@ -1196,7 +1435,7 @@ showImage_1_s /* 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 +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之间'); @@ -1215,7 +1454,7 @@ showTextImage_s /* 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 +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.4,1,0,false] if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); @@ -1230,7 +1469,7 @@ hideImage_s /* hideImage_s tooltip : hideImage:清除图片 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=hideImage%ef%bc%9a%e6%b8%85%e9%99%a4%e5%9b%be%e7%89%87 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=hideImage%ef%bc%9a%e6%b8%85%e9%99%a4%e5%9b%be%e7%89%87 colour : this.printColor default : [1,0,false] if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); @@ -1245,7 +1484,7 @@ showGif_0_s /* showGif_0_s tooltip : showGif:显示动图 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=showgif%EF%BC%9A%E6%98%BE%E7%A4%BA%E5%8A%A8%E5%9B%BE +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=showgif%EF%BC%9A%E6%98%BE%E7%A4%BA%E5%8A%A8%E5%9B%BE default : ["bg.gif","0","0"] colour : this.printColor var code = '{"type": "showGif", "name": "'+EvalString_0+'", "loc": ['+PosString_0+','+PosString_1+']},\n'; @@ -1258,7 +1497,7 @@ showGif_1_s /* showGif_1_s tooltip : showGif:清除所有显示的动图 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=showgif%EF%BC%9A%E6%98%BE%E7%A4%BA%E5%8A%A8%E5%9B%BE +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=showgif%EF%BC%9A%E6%98%BE%E7%A4%BA%E5%8A%A8%E5%9B%BE colour : this.printColor var code = '{"type": "showGif"},\n'; return code; @@ -1271,7 +1510,7 @@ moveImage_s /* moveImage_s tooltip : moveImage:图片移动 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=moveImage%ef%bc%9a%e5%9b%be%e7%89%87%e7%a7%bb%e5%8a%a8 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=moveImage%ef%bc%9a%e5%9b%be%e7%89%87%e7%a7%bb%e5%8a%a8 default : [1,'','','',500,false] colour : this.printColor if(Int_0<=0 || Int_0>50) throw new Error('图片编号在1~50之间'); @@ -1290,10 +1529,10 @@ setCurtain_0_s /* 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 +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)?$/; +var colorRe = MotaActionFunctions.pattern.colorRe; 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':''; @@ -1307,7 +1546,7 @@ setCurtain_1_s /* 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 +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):''; @@ -1321,10 +1560,10 @@ screenFlash_s /* screenFlash_s tooltip : screenFlash: 画面闪烁,动画时间可不填 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=screenFlash%EF%BC%9A%E7%94%BB%E9%9D%A2%E9%97%AA%E7%83%81 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=screenFlash%EF%BC%9A%E7%94%BB%E9%9D%A2%E9%97%AA%E7%83%81 default : ["255,255,255,1",'rgba(255,255,255,1)',500,1,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)?$/; +var colorRe = MotaActionFunctions.pattern.colorRe; if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); Int_1 = Int_1!=='' ?(', "times": '+Int_1):''; var async = Bool_0?', "async": true':''; @@ -1338,7 +1577,7 @@ setWeather_s /* setWeather_s tooltip : setWeather:更改天气 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setweather%EF%BC%9A%E6%9B%B4%E6%94%B9%E5%A4%A9%E6%B0%94 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setweather%EF%BC%9A%E6%9B%B4%E6%94%B9%E5%A4%A9%E6%B0%94 default : [null,1] colour : this.soundColor if(Int_0<1 || Int_0>10) throw new Error('天气的强度等级, 在1-10之间'); @@ -1353,8 +1592,8 @@ move_s /* move_s tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=move%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AAnpc%E6%80%AA%E7%89%A9%E7%A7%BB%E5%8A%A8 -default : ["","",500,false,false,"上右3下2左上左2"] +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=move%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AAnpc%E6%80%AA%E7%89%A9%E7%A7%BB%E5%8A%A8 +default : ["","",500,false,false,"上右3下2后4左前2"] colour : this.mapColor var floorstr = ''; if (PosString_0 && PosString_1) { @@ -1373,7 +1612,7 @@ moveHero_s /* moveHero_s tooltip : moveHero:移动勇士,用这种方式移动勇士的过程中将无视一切地形, 无视一切事件, 中毒状态也不会扣血 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=movehero%EF%BC%9A%E7%A7%BB%E5%8A%A8%E5%8B%87%E5%A3%AB +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=movehero%EF%BC%9A%E7%A7%BB%E5%8A%A8%E5%8B%87%E5%A3%AB default : [500,false,"上右3下2后4左前2"] colour : this.dataColor Int_0 = Int_0!=='' ?(', "time": '+Int_0):''; @@ -1388,7 +1627,7 @@ jump_s /* jump_s tooltip : jump: 让某个NPC/怪物跳跃 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=jump%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AANPC%2F%E6%80%AA%E7%89%A9%E8%B7%B3%E8%B7%83 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=jump%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AANPC%2F%E6%80%AA%E7%89%A9%E8%B7%B3%E8%B7%83 default : ["","","","",500,true,false] colour : this.mapColor var floorstr = ''; @@ -1411,7 +1650,7 @@ jumpHero_s /* jumpHero_s tooltip : jumpHero: 跳跃勇士 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=jumpHero%EF%BC%9A%E8%B7%B3%E8%B7%83%E5%8B%87%E5%A3%AB +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=jumpHero%EF%BC%9A%E8%B7%B3%E8%B7%83%E5%8B%87%E5%A3%AB default : ["","",500,false] colour : this.dataColor var floorstr = ''; @@ -1425,15 +1664,16 @@ return code; */; playBgm_s - : '播放背景音乐' EvalString Newline + : '播放背景音乐' EvalString '持续到下个本事件' Bool Newline /* playBgm_s tooltip : playBgm: 播放背景音乐 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=playbgm%EF%BC%9A%E6%92%AD%E6%94%BE%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 -default : ["bgm.mp3"] +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=playbgm%EF%BC%9A%E6%92%AD%E6%94%BE%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 +default : ["bgm.mp3", true] colour : this.soundColor -var code = '{"type": "playBgm", "name": "'+EvalString_0+'"},\n'; +Bool_0 = Bool_0 ? ', "keep": true' : ''; +var code = '{"type": "playBgm", "name": "'+EvalString_0+'"'+Bool_0+'},\n'; return code; */; @@ -1443,7 +1683,7 @@ pauseBgm_s /* pauseBgm_s tooltip : pauseBgm: 暂停背景音乐 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=pausebgm%EF%BC%9A%E6%9A%82%E5%81%9C%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=pausebgm%EF%BC%9A%E6%9A%82%E5%81%9C%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 colour : this.soundColor var code = '{"type": "pauseBgm"},\n'; return code; @@ -1455,7 +1695,7 @@ resumeBgm_s /* resumeBgm_s tooltip : resumeBgm: 恢复背景音乐 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=resumebgm%EF%BC%9A%E6%81%A2%E5%A4%8D%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=resumebgm%EF%BC%9A%E6%81%A2%E5%A4%8D%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 colour : this.soundColor var code = '{"type": "resumeBgm"},\n'; return code; @@ -1467,7 +1707,7 @@ loadBgm_s /* loadBgm_s tooltip : loadBgm: 预加载某个背景音乐,之后可以直接播放 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=loadBgm%ef%bc%9a%e9%a2%84%e5%8a%a0%e8%bd%bd%e4%b8%80%e4%b8%aa%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=loadBgm%ef%bc%9a%e9%a2%84%e5%8a%a0%e8%bd%bd%e4%b8%80%e4%b8%aa%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90 default : ["bgm.mp3"] colour : this.soundColor var code = '{"type": "loadBgm", "name": "'+EvalString_0+'"},\n'; @@ -1480,7 +1720,7 @@ freeBgm_s /* freeBgm_s tooltip : freeBgm: 释放背景音乐的缓存 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=freeBgm%ef%bc%9a%e9%87%8a%e6%94%be%e4%b8%80%e4%b8%aa%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90%e7%9a%84%e7%bc%93%e5%ad%98 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=freeBgm%ef%bc%9a%e9%87%8a%e6%94%be%e4%b8%80%e4%b8%aa%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90%e7%9a%84%e7%bc%93%e5%ad%98 default : ["bgm.mp3"] colour : this.soundColor var code = '{"type": "freeBgm", "name": "'+EvalString_0+'"},\n'; @@ -1493,7 +1733,7 @@ playSound_s /* 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 +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",false] colour : this.soundColor Bool_0 = Bool_0 ? ', "stop": true' : ''; @@ -1507,7 +1747,7 @@ stopSound_s /* stopSound_s tooltip : stopSound: 停止所有音效 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=stopSound%ef%bc%9a%e5%81%9c%e6%ad%a2%e6%89%80%e6%9c%89%e9%9f%b3%e6%95%88 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=stopSound%ef%bc%9a%e5%81%9c%e6%ad%a2%e6%89%80%e6%9c%89%e9%9f%b3%e6%95%88 colour : this.soundColor var code = '{"type": "stopSound"},\n'; return code; @@ -1519,7 +1759,7 @@ setVolume_s /* setVolume_s tooltip : setVolume: 设置音量 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=setvolume%EF%BC%9A%E8%AE%BE%E7%BD%AE%E9%9F%B3%E9%87%8F +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setvolume%EF%BC%9A%E8%AE%BE%E7%BD%AE%E9%9F%B3%E9%87%8F default : [90, 500, false] colour : this.soundColor Int_1 = Int_1!==''?(', "time": '+Int_1):"" @@ -1529,15 +1769,16 @@ return code; */; win_s - : '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool Newline + : '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool '不结束游戏' Bool Newline /* win_s tooltip : win: 获得胜利, 该事件会显示获胜页面, 并重新游戏 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=win%EF%BC%9A%E8%8E%B7%E5%BE%97%E8%83%9C%E5%88%A9 -default : ["",false] +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=win%EF%BC%9A%E8%8E%B7%E5%BE%97%E8%83%9C%E5%88%A9 +default : ["",false, false] Bool_0 = Bool_0?', "norank": 1':''; -var code = '{"type": "win", "reason": "'+EvalString_0+'"'+Bool_0+'},\n'; +Bool_1 = Bool_1?', "noexit": 1':''; +var code = '{"type": "win", "reason": "'+EvalString_0+'"'+Bool_0+Bool_1+'},\n'; return code; */; @@ -1547,7 +1788,7 @@ lose_s /* lose_s tooltip : lose: 游戏失败, 该事件会显示失败页面, 并重新开始游戏 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=lose%EF%BC%9A%E6%B8%B8%E6%88%8F%E5%A4%B1%E8%B4%A5 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=lose%EF%BC%9A%E6%B8%B8%E6%88%8F%E5%A4%B1%E8%B4%A5 default : [""] var code = '{"type": "lose", "reason": "'+EvalString_0+'"},\n'; return code; @@ -1559,7 +1800,7 @@ restart_s /* restart_s tooltip : restart: 直接回到标题界面 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=restart%ef%bc%9a%e7%9b%b4%e6%8e%a5%e5%9b%9e%e5%88%b0%e6%a0%87%e9%a2%98%e7%95%8c%e9%9d%a2 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=restart%ef%bc%9a%e7%9b%b4%e6%8e%a5%e5%9b%9e%e5%88%b0%e6%a0%87%e9%a2%98%e7%95%8c%e9%9d%a2 var code = '{"type": "restart"},\n'; return code; */; @@ -1570,7 +1811,7 @@ input_s /* input_s tooltip : input:接受用户输入数字, 事件只能接受非负整数输入, 所有非法的输入将全部变成0 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=input%ef%bc%9a%e6%8e%a5%e5%8f%97%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e6%95%b0%e5%ad%97 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=input%ef%bc%9a%e6%8e%a5%e5%8f%97%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e6%95%b0%e5%ad%97 default : ["请输入一个数"] colour : this.dataColor var code = '{"type": "input", "text": "'+EvalString_0+'"},\n'; @@ -1583,7 +1824,7 @@ input2_s /* input2_s tooltip : input2:接受用户输入文本, 允许用户输入任何形式的文本 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=input2%ef%bc%9a%e6%8e%a5%e5%8f%97%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e6%96%87%e6%9c%ac +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=input2%ef%bc%9a%e6%8e%a5%e5%8f%97%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e6%96%87%e6%9c%ac default : ["请输入文本"] colour : this.dataColor var code = '{"type": "input2", "text": "'+EvalString_0+'"},\n'; @@ -1596,7 +1837,7 @@ if_s /* if_s tooltip : if: 条件判断 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD colour : this.eventColor var code = ['{"type": "if", "condition": "',expression_0,'",\n', '"true": [\n',action_0,'],\n', @@ -1611,7 +1852,7 @@ if_1_s /* if_1_s tooltip : if: 条件判断 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD colour : this.eventColor var code = ['{"type": "if", "condition": "',expression_0,'",\n', '"true": [\n',action_0,'],\n', @@ -1625,7 +1866,7 @@ switch_s /* switch_s tooltip : switch: 多重条件分歧 -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 +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 : ["判别值"] colour : this.eventColor var code = ['{"type": "switch", "condition": "',expression_0,'", "caseList": [\n', @@ -1640,7 +1881,7 @@ switchCase /* 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 +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 Bool_0 = Bool_0?', "nobreak": true':''; @@ -1654,7 +1895,7 @@ choices_s /* choices_s tooltip : choices: 给用户提供选项 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 default : ["","流浪者","woman"] var title=''; if (EvalString_1==''){ @@ -1673,23 +1914,24 @@ return code; */; choicesContext - : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour BGNL? Newline action+ + : '子选项' EvalString '图标' IdString? '颜色' EvalString? Colour '出现条件' EvalString? BGNL? Newline action+ /* choicesContext tooltip : 选项的选择 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 -default : ["提示文字:红钥匙","",""] +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 +default : ["提示文字:红钥匙","","",""] colour : this.subColor if (EvalString_1) { - var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; + var colorRe = MotaActionFunctions.pattern.colorRe; if (colorRe.test(EvalString_1)) EvalString_1 = ', "color": ['+EvalString_1+']'; else EvalString_1 = ', "color": "'+EvalString_1+'"'; } +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"') IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):''; -var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+', "action": [\n'+action_0+']},\n'; +var code = '{"text": "'+EvalString_0+'"'+IdString_0+EvalString_1+EvalString_2+', "action": [\n'+action_0+']},\n'; return code; */; @@ -1698,7 +1940,7 @@ confirm_s /* confirm_s tooltip : 弹出确认框 -helpUrl : https://h5mota.com/games/template/docs/#/ +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', @@ -1713,7 +1955,7 @@ while_s /* while_s tooltip : while:前置条件循环 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=while%ef%bc%9a%e5%89%8d%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=while%ef%bc%9a%e5%89%8d%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af colour : this.eventColor var code = ['{"type": "while", "condition": "',expression_0,'",\n', '"data": [\n',action_0,'],\n', @@ -1726,7 +1968,7 @@ dowhile_s /* dowhile_s tooltip : dowhile:后置条件循环 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=dowhile%ef%bc%9a%e5%90%8e%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=dowhile%ef%bc%9a%e5%90%8e%e7%bd%ae%e6%9d%a1%e4%bb%b6%e5%be%aa%e7%8e%af colour : this.eventColor var code = ['{"type": "dowhile", "condition": "',expression_0,'",\n', '"data": [\n',action_0,'],\n', @@ -1735,11 +1977,11 @@ return code; */; break_s - : '跳出循环' Newline + : '跳出当前循环或公共事件' Newline /* break_s tooltip : break:跳出循环, 如果break事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! -helpUrl : https://h5mota.com/games/template/docs/#/event?id=break%EF%BC%9A%E8%B7%B3%E5%87%BA%E5%BE%AA%E7%8E%AF +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=break%EF%BC%9A%E8%B7%B3%E5%87%BA%E5%BE%AA%E7%8E%AF colour : this.eventColor var code = '{"type": "break"},\n'; return code; @@ -1750,7 +1992,7 @@ continue_s /* continue_s tooltip : continue:继续执行当前循环的下一轮, 如果continue事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! -helpUrl : https://h5mota.com/games/template/docs/#/event?id=continue%EF%BC%9A%E7%BB%A7%E7%BB%AD%E6%89%A7%E8%A1%8C%E5%BD%93%E5%89%8D%E5%BE%AA%E7%8E%AF +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=continue%EF%BC%9A%E7%BB%A7%E7%BB%AD%E6%89%A7%E8%A1%8C%E5%BD%93%E5%89%8D%E5%BE%AA%E7%8E%AF colour : this.eventColor var code = '{"type": "continue"},\n'; return code; @@ -1758,25 +2000,63 @@ return code; wait_s - : '等待用户操作并获得按键或点击信息' + : '等待用户操作并获得按键或点击信息' BGNL? Newline waitContext* BEND Newline /* wait_s -tooltip : wait: 等待用户操作并获得按键或点击信息(具体用法看文档) -helpUrl : https://h5mota.com/games/template/docs/#/event?id=wait%EF%BC%9A%E7%AD%89%E5%BE%85%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=wait%EF%BC%9A%E7%AD%89%E5%BE%85%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C colour : this.soundColor -var code = '{"type": "wait"},\n'; +waitContext_0 = waitContext_0 ? (', "data": [\n' + waitContext_0 + ']') : ''; +var code = '{"type": "wait"' + waitContext_0 + '},\n'; return code; */; +waitContext + : waitContext_1 + | waitContext_2 + | waitContext_empty; + + +waitContext_1 + : '按键的场合' '键值' Int BGNL? Newline action+ BEND Newline + +/* waitContext_1 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=wait%EF%BC%9A%E7%AD%89%E5%BE%85%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C +colour : this.subColor +var code = '{"case": "keyboard", "keycode": ' + Int_0 + ', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + + +waitContext_2 + : '点击的场合' '像素x范围' PosString '~' PosString '; y范围' PosString '~' PosString BGNL? Newline action+ BEND Newline + +/* waitContext_2 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=wait%EF%BC%9A%E7%AD%89%E5%BE%85%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C +default : [0,32,0,32] +colour : this.subColor +var code = '{"case": "mouse", "px": [' + PosString_0 + ',' + PosString_1 + '], "py": [' + PosString_2 + ',' + PosString_3 + '], "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_empty : Newline + +/* waitContext_empty +return ''; +*/; + + waitAsync_s : '等待所有异步事件执行完毕' /* waitAsync_s tooltip : waitAsync: 等待所有异步事件执行完毕 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=waitAsync%ef%bc%9a%e7%ad%89%e5%be%85%e6%89%80%e6%9c%89%e5%bc%82%e6%ad%a5%e4%ba%8b%e4%bb%b6%e6%89%a7%e8%a1%8c%e5%ae%8c%e6%af%95 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=waitAsync%ef%bc%9a%e7%ad%89%e5%be%85%e6%89%80%e6%9c%89%e5%bc%82%e6%ad%a5%e4%ba%8b%e4%bb%b6%e6%89%a7%e8%a1%8c%e5%ae%8c%e6%af%95 colour : this.soundColor var code = '{"type": "waitAsync"},\n'; return code; @@ -1789,7 +2069,7 @@ callBook_s /* callBook_s tooltip : callBook: 呼出怪物手册;返回游戏后将继续执行后面的事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=callBook%ef%bc%9a%e5%91%bc%e5%87%ba%e6%80%aa%e7%89%a9%e6%89%8b%e5%86%8c +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=callBook%ef%bc%9a%e5%91%bc%e5%87%ba%e6%80%aa%e7%89%a9%e6%89%8b%e5%86%8c colour : this.soundColor var code = '{"type": "callBook"},\n'; return code; @@ -1802,7 +2082,7 @@ callSave_s /* callSave_s tooltip : callSave: 呼出存档页面 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=callSave%ef%bc%9a%e5%91%bc%e5%87%ba%e5%ad%98%e6%a1%a3%e7%95%8c%e9%9d%a2 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=callSave%ef%bc%9a%e5%91%bc%e5%87%ba%e5%ad%98%e6%a1%a3%e7%95%8c%e9%9d%a2 colour : this.soundColor var code = '{"type": "callSave"},\n'; return code; @@ -1810,36 +2090,463 @@ return code; autoSave_s - : '自动存档' + : '自动存档' '不提示' Bool Newline /* autoSave_s tooltip : autoSave: 自动存档 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=autoSave%ef%bc%9a%e8%87%aa%e5%8a%a8%e5%ad%98%e6%a1%a3 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=autoSave%ef%bc%9a%e8%87%aa%e5%8a%a8%e5%ad%98%e6%a1%a3 colour : this.soundColor -var code = '{"type": "autoSave"},\n'; +default : [false] +Bool_0 = Bool_0 ? (', "nohint": true') : ''; +var code = '{"type": "autoSave"'+Bool_0+'},\n'; return code; */; callLoad_s - : '呼出读档页面' + : '呼出读档页面' Newline /* callLoad_s tooltip : callLoad: 呼出存档页面;返回游戏后将继续执行后面的事件 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=callLoad%ef%bc%9a%e5%91%bc%e5%87%ba%e8%af%bb%e6%a1%a3%e7%95%8c%e9%9d%a2 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=callLoad%ef%bc%9a%e5%91%bc%e5%87%ba%e8%af%bb%e6%a1%a3%e7%95%8c%e9%9d%a2 colour : this.soundColor var code = '{"type": "callLoad"},\n'; return code; */; + +previewUI_s + : 'ui绘制并预览' '(双击此项可进行预览)' BGNL? Newline action+ BEND Newline + + +/* previewUI_s +tooltip : previewUI: ui绘制并预览 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=previewUI%ef%bc%9aUI%e7%bb%98%e5%88%b6%e5%b9%b6%e9%a2%84%e8%a7%88 +var code = ['{"type": "previewUI", "action": [\n', action_0,']},\n'].join(''); +return code; +*/; + + +clearMap_s + : '清除画布' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString Newline + +/* clearMap_s +tooltip : clearMap: 清除画布 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=clearMap%ef%bc%9a%e6%b8%85%e9%99%a4%e7%94%bb%e5%b8%83 +colour : this.subColor +default : ["0", "0", "100", "100"] +var code = '{"type": "clearMap", "x": ' + PosString_0 + ', "y": ' + PosString_1 + + ', "width": ' + PosString_2 + ', "height": ' + PosString_3 + '},\n'; +return code; +*/; + + +clearMap_1_s + : '清空画布' Newline + +/* clearMap_1_s +tooltip : clearMap: 清除画布 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=clearMap%ef%bc%9a%e6%b8%85%e9%99%a4%e7%94%bb%e5%b8%83 +colour : this.subColor +var code = '{"type": "clearMap"},\n'; +return code; +*/; + + +setAttribute_s + : '设置画布属性' '字体' EvalString? '填充样式' EvalString? Colour '边框样式' EvalString? Colour BGNL? '线宽度' EvalString? '不透明度' EvalString? '对齐' TextAlign_List '基准线' TextBaseline_List 'z值' EvalString? Newline + +/* setAttribute_s +tooltip : setAttribute:设置画布属性 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=setAttribute%ef%bc%9a%e8%ae%be%e7%bd%ae%e7%94%bb%e5%b8%83%e5%b1%9e%e6%80%a7 +colour : this.subColor +default : ["","",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","",null,null,""] +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +TextBaseline_List_0 = TextBaseline_List_0==='null'?'': ', "baseline": "'+TextBaseline_List_0+'"'; +var colorRe = MotaActionFunctions.pattern.colorRe; +var fontRe = MotaActionFunctions.pattern.fontRe; +if (EvalString_0) { + if (!fontRe.test(EvalString_0)) throw new Error('字体必须是 [italic] [bold] 14px Verdana 这种形式或不填'); + EvalString_0 = ', "font": "' + EvalString_0 + '"'; +} +if (EvalString_1) { + if (!colorRe.test(EvalString_1))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_1 = ', "fillStyle": ['+EvalString_1+']'; +} +if (EvalString_2) { + if (!colorRe.test(EvalString_2))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_2 = ', "strokeStyle": ['+EvalString_2+']'; +} +if (EvalString_3) { + if (!/^\d+$/.test(EvalString_3))throw new Error('线宽必须是整数或不填'); + EvalString_3 = ', "lineWidth": '+EvalString_3; +} +if (EvalString_4) { + var f = parseFloat(EvalString_4); + if (isNaN(f) || f<0 || f>1) throw new Error('不透明度必须是0到1的浮点数或不填'); + EvalString_4 = ', "alpha": '+EvalString_4; +} +if (EvalString_5) { + if (!/^\d+$/.test(EvalString_5))throw new Error('z值必须是整数或不填'); + EvalString_5 = ', "z": '+EvalString_5; +} +var code = '{"type": "setAttribute"'+EvalString_0+EvalString_1+EvalString_2+EvalString_3+EvalString_4+TextAlign_List_0+TextBaseline_List_0+EvalString_5+'},\n'; +return code; +*/; + +fillText_s + : '绘制文本' 'x' PosString 'y' PosString '样式' EvalString? Colour '字体' EvalString? '最大宽度' EvalString? BGNL? EvalString Newline + +/* fillText_s +tooltip : fillText:绘制一行文本;可以设置最大宽度进行放缩 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=fillText%ef%bc%9a%e7%bb%98%e5%88%b6%e6%96%87%e6%9c%ac +colour : this.subColor +default : ["0","0","",'rgba(255,255,255,1)',"","","绘制一行文本"] +var colorRe = MotaActionFunctions.pattern.colorRe; +var fontRe = MotaActionFunctions.pattern.fontRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +if (EvalString_1) { + if (!fontRe.test(EvalString_1)) throw new Error('字体必须是 [italic] [bold] 14px Verdana 这种形式或不填'); + EvalString_1 = ', "font": "' + EvalString_1 + '"'; +} +if (EvalString_2) { + if (!/^\d+$/.test(EvalString_2)) throw new Error('最大宽度必须是整数或不填'); + EvalString_2 = ', "maxWidth": ' + EvalString_2; +} +var code = '{"type": "fillText", "x": '+PosString_0+', "y": '+PosString_1+EvalString_0+EvalString_1+EvalString_2+', "text": "'+EvalString_3+'"},\n'; +return code; +*/; + +fillBoldText_s + : '绘制描边文本' 'x' PosString 'y' PosString '样式' EvalString? Colour '字体' EvalString? BGNL? EvalString Newline + +/* fillBoldText_s +tooltip : fillBoldText:绘制一行描边文本 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=fillBoldText%ef%bc%9a%e7%bb%98%e5%88%b6%e6%8f%8f%e8%be%b9%e6%96%87%e6%9c%ac +colour : this.subColor +default : ["0","0","",'rgba(255,255,255,1)',"","绘制一行描边文本"] +var colorRe = MotaActionFunctions.pattern.colorRe; +var fontRe = MotaActionFunctions.pattern.fontRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +if (EvalString_1) { + if (!fontRe.test(EvalString_1)) throw new Error('字体必须是 [italic] [bold] 14px Verdana 这种形式或不填'); + EvalString_1 = ', "font": "' + EvalString_1 + '"'; +} +var code = '{"type": "fillBoldText", "x": '+PosString_0+', "y": '+PosString_1+EvalString_0+EvalString_1+', "text": "'+EvalString_2+'"},\n'; +return code; +*/; + +drawTextContent_s + : '绘制多行文本' EvalString BGNL? '起点像素' 'x' PosString 'y' PosString '最大宽度' EvalString? '颜色' EvalString? Colour BGNL? '对齐' TextAlign_List '字体大小' EvalString? '行距' EvalString? '粗体' Bool Newline + +/* drawTextContent_s +tooltip : drawTextContent:绘制多行文本 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=drawTextContent%ef%bc%9a%e7%bb%98%e5%88%b6%e5%a4%9a%e8%a1%8c%e6%96%87%e6%9c%ac +colour : this.subColor +default : ["绘制多行文本\\n可双击编辑","0","0","","",'rgba(255,255,255,1)',null,"","",false] +var colorRe = MotaActionFunctions.pattern.colorRe; +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +Bool_0 = Bool_0 ? (', "bold": true') : ''; +if (EvalString_1) { + if (!/^\d+$/.test(EvalString_1)) throw new Error('最大宽度必须是整数或不填'); + EvalString_1 = ', "maxWidth": ' + EvalString_1; +} +if (EvalString_2) { + if (!colorRe.test(EvalString_2))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_2 = ', "color": ['+EvalString_2+']'; +} +if (EvalString_3) { + if (!/^\d+$/.test(EvalString_3)) throw new Error('字体大小必须是整数或不填'); + EvalString_3 = ', "fontSize": ' + EvalString_3; +} +if (EvalString_4) { + if (!/^\d+$/.test(EvalString_4)) throw new Error('行距必须是整数或不填'); + EvalString_4 = ', "lineHeight": ' + EvalString_4; +} +var code = '{"type": "drawTextContent", "text": "'+EvalString_0+'", "left": '+PosString_0+', "top": '+PosString_1+TextAlign_List_0+EvalString_1+EvalString_2+EvalString_3+EvalString_4+Bool_0+'},\n'; +return code; +*/; + +fillRect_s + : '绘制矩形' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '颜色' EvalString? Colour Newline + +/* fillRect_s +tooltip : fillRect:绘制矩形 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=fillRect%ef%bc%9a%e7%bb%98%e5%88%b6%e7%9f%a9%e5%bd%a2 +colour : this.subColor +default : ["0","0","flag:x","300","",null] +var colorRe = MotaActionFunctions.pattern.colorRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +var code = '{"type": "fillRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+EvalString_0+'},\n'; +return code; +*/; + +strokeRect_s + : '绘制矩形边框' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '颜色' EvalString? Colour '线宽' EvalString? Newline + +/* strokeRect_s +tooltip : strokeRect:绘制矩形边框 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=strokeRect%ef%bc%9a%e7%bb%98%e5%88%b6%e7%9f%a9%e5%bd%a2%e8%be%b9%e6%a1%86 +colour : this.subColor +default : ["0","0","flag:x","300","",null,""] +var colorRe = MotaActionFunctions.pattern.colorRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +if (EvalString_1) { + if (!/^\d+$/.test(EvalString_1))throw new Error('线宽必须是整数或不填'); + EvalString_1 = ', "lineWidth": '+EvalString_1; +} +var code = '{"type": "strokeRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+EvalString_0+EvalString_1+'},\n'; +return code; +*/; + +drawLine_s + : '绘制线段' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' EvalString? Colour '线宽' EvalString? Newline + +/* drawLine_s +tooltip : drawLine:绘制线段 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=drawLine%ef%bc%9a%e7%bb%98%e5%88%b6%e7%ba%bf%e6%ae%b5 +colour : this.subColor +default : ["0","0","flag:x","300","",null,""] +var colorRe = MotaActionFunctions.pattern.colorRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +if (EvalString_1) { + if (!/^\d+$/.test(EvalString_1))throw new Error('线宽必须是整数或不填'); + EvalString_1 = ', "lineWidth": '+EvalString_1; +} +var code = '{"type": "drawLine", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+EvalString_0+EvalString_1+'},\n'; +return code; +*/; + +drawArrow_s + : '绘制箭头' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' EvalString? Colour '线宽' EvalString? Newline + +/* drawArrow_s +tooltip : drawArrow:绘制箭头 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=drawArrow%ef%bc%9a%e7%bb%98%e5%88%b6%e7%ae%ad%e5%a4%b4 +colour : this.subColor +default : ["0","0","flag:x","300","",null,""] +var colorRe = MotaActionFunctions.pattern.colorRe; +if (EvalString_0) { + if (!colorRe.test(EvalString_0))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); + EvalString_0 = ', "style": ['+EvalString_0+']'; +} +if (EvalString_1) { + if (!/^\d+$/.test(EvalString_1))throw new Error('线宽必须是整数或不填'); + EvalString_1 = ', "lineWidth": '+EvalString_1; +} +var code = '{"type": "drawArrow", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+EvalString_0+EvalString_1+'},\n'; +return code; +*/; + + +fillPolygon_s + : '绘制多边形' '顶点像素列表' 'x' EvalString 'y' EvalString '颜色' EvalString? Colour Newline + +/* fillPolygon_s +tooltip : fillPolygon:绘制多边形 +helpUrl : https://h5mota.com/games/template/_docs/#/event?id=fillPolygon%ef%bc%9a%e7%bb%98%e5%88%b6%e5%a4%9a%e8%be%b9%e5%bd%a2 +colour : this.subColor +default : ["0,0,100","0,100,0","",null] +var colorRe = MotaActionFunctions.pattern.colorRe; +var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; +if(!pattern2.test(EvalString_0) || !pattern2.test(EvalString_1))throw new Error('坐标格式错误,请右键点击帮助查看格式'); +EvalString_0=EvalString_0.split(','); +EvalString_1=EvalString_1.split(','); +if(EvalString_0.length!==EvalString_1.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); +for(var ii=0;ii'+xml_text+''); @@ -2852,19 +3870,22 @@ MotaActionFunctions.parse = function(obj,type) { MotaActionFunctions.EvalString_pre = function(EvalString){ if (EvalString.indexOf('__door__')!==-1) throw new Error('请修改开门变量__door__,如door1,door2,door3等依次向后。请勿存在两个门使用相同的开门变量。'); + EvalString = MotaActionFunctions.replaceFromName(EvalString); return EvalString.replace(/([^\\])"/g,'$1\\"').replace(/^"/g,'\\"').replace(/""/g,'"\\"'); } MotaActionFunctions.IdString_pre = function(IdString){ if (IdString.indexOf('__door__')!==-1) throw new Error('请修改开门变量__door__,如door1,door2,door3等依次向后。请勿存在两个门使用相同的开门变量。'); - if (IdString && !(/^[0-9a-zA-Z_][0-9a-zA-Z_\-:]*$/.test(IdString)))throw new Error('id: '+IdString+'中包含了0-9 a-z A-Z _ - :之外的字符'); + IdString = MotaActionFunctions.replaceFromName(IdString); + if (IdString && !(MotaActionFunctions.pattern.id.test(IdString)) && !(MotaActionFunctions.pattern.idWithoutFlag.test(IdString))) + throw new Error('id: '+IdString+'中包含了0-9 a-z A-Z _ - :之外的字符'); return IdString; } MotaActionFunctions.PosString_pre = function(PosString){ if (!PosString || /^-?\d+$/.test(PosString)) return PosString; - if (!(/^flag:[0-9a-zA-Z_][0-9a-zA-Z_:]*$/.test(PosString)))throw new Error(PosString+'中包含了0-9 a-z A-Z _ :之外的字符,或者是没有以flag: 开头'); - return '"'+PosString+'"'; + //if (!(MotaActionFunctions.pattern.id.test(PosString)))throw new Error(PosString+'中包含了0-9 a-z A-Z _ 和中文之外的字符,或者是没有以flag: 开头'); + return '"'+MotaActionFunctions.replaceFromName(PosString)+'"'; } MotaActionFunctions.StepString_pre = function(StepString){ @@ -2905,4 +3926,153 @@ MotaActionFunctions.StepString_pre = function(StepString){ return ans; } +MotaActionFunctions.pattern=MotaActionFunctions.pattern||{}; +MotaActionFunctions.pattern.id=/^(flag|global):([a-zA-Z0-9_\u4E00-\u9FCC]+)$/; +MotaActionFunctions.pattern.id2=/^flag:([a-zA-Z0-9_\u4E00-\u9FCC]+),flag:([a-zA-Z0-9_\u4E00-\u9FCC]+)$/; +MotaActionFunctions.pattern.idWithoutFlag=/^[0-9a-zA-Z_][0-9a-zA-Z_\-:]*$/; +MotaActionFunctions.pattern.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)?$/; +MotaActionFunctions.pattern.fontRe=/^(italic )?(bold )?(\d+)px ([a-zA-Z0-9_\u4E00-\u9FCC]+)$/; + + +MotaActionFunctions.pattern.replaceStatusList = [ + // 保证顺序! + ["hpmax", "生命上限"], + ["hp", "生命"], + ["name", "名称"], + ["lv", "等级"], + ["atk", "攻击"], + ["def", "防御"], + ["mdef", "魔防"], + ["manamax", "魔力上限"], + ["mana", "魔力"], + ["money", "金币"], + ["experience", "经验"], + ["steps", "步数"], +]; + +MotaActionFunctions.pattern.replaceItemList = [ + // 保证顺序! + ["yellowKey", "黄钥匙"], + ["blueKey", "蓝钥匙"], + ["redKey", "红钥匙"], + ["redJewel", "红宝石"], + ["blueJewel", "蓝宝石"], + ["greenJewel", "绿宝石"], + ["yellowJewel", "黄宝石"], + ["redPotion", "红血瓶"], + ["bluePotion", "蓝血瓶"], + ["yellowPotion", "黄血瓶"], + ["greenPotion", "绿血瓶"], + ["sword1", "铁剑"], + ["sword2", "银剑"], + ["sword3", "骑士剑"], + ["sword4", "圣剑"], + ["sword5", "神圣剑"], + ["shield1", "铁盾"], + ["shield2", "银盾"], + ["shield3", "骑士盾"], + ["shield4", "圣盾"], + ["shield5", "神圣盾"], + ["superPotion", "圣水"], + ["moneyPocket", "金钱袋"], + ["book", "怪物手册"], + ["fly", "楼层传送器"], + ["coin", "幸运金币"], + ["snow", "冰冻徽章"], + ["cross", "十字架"], + ["knife", "屠龙匕首"], + ["shoes", "绿鞋"], + ["bigKey", "大黄门钥匙"], + ["greenKey", "绿钥匙"], + ["steelKey", "铁门钥匙"], + ["pickaxe", "破墙镐"], + ["icePickaxe", "破冰镐"], + ["bomb", "炸弹"], + ["centerFly", "中心对称飞行器"], + ["upFly", "上楼器"], + ["downFly", "下楼器"], + ["earthquake", "地震卷轴"], + ["poisonWine", "解毒药水"], + ["weakWine", "解衰药水"], + ["curseWine", "解咒药水"], + ["superWine", "万能药水"], + ["hammer", "圣锤"], + ["lifeWand", "生命魔杖"], + ["jumpShoes", "跳跃靴"], +]; + +MotaActionFunctions.pattern.replaceEnemyList = [ + // 保证顺序! + ["name", "名称"], + ["atk", "攻击"], + ["def", "防御"], + ["money", "金币"], + ["experience", "经验"], + ["point", "加点"], + ["special", "属性"], +]; + +MotaActionFunctions.disableReplace = false; + +MotaActionFunctions.replaceToName = function (str) { + if (!str || MotaActionFunctions.disableReplace) return str; + var map = {}, list = []; + MotaActionFunctions.pattern.replaceStatusList.forEach(function (v) { + map[v[0]] = v[1]; list.push(v[0]); + }); + str = str.replace(new RegExp("status:(" + list.join("|") + ")", "g"), function (a, b) { + return map[b] ? ("状态:" + map[b]) : b; + }).replace(/status:/g, "状态:"); + map = {}; list = []; + MotaActionFunctions.pattern.replaceItemList.forEach(function (v) { + map[v[0]] = v[1]; list.push(v[0]); + }); + str = str.replace(new RegExp("item:(" + list.join("|") + ")", "g"), function (a, b) { + return map[b] ? ("物品:" + map[b]) : b; + }).replace(/item:/g, "物品:"); + str = str.replace(/flag:/g, "变量:").replace(/switch:/g, "独立开关:").replace(/global:/g, "全局存储:"); + + map = {}; list = []; + MotaActionFunctions.pattern.replaceEnemyList.forEach(function (v) { + map[v[0]] = v[1]; list.push(v[0]); + }); + str = str.replace(new RegExp("enemy:([a-zA-Z0-9_]+).(" + list.join("|") + ")", "g"), function (a, b, c) { + return map[c] ? ("怪物:" + b + ":" + map[c]) : c; + }).replace(/enemy:/g, "怪物:"); + + str = str.replace(/blockId:/g, "图块ID:").replace(/blockCls:/g, "图块类别:").replace(/equip:/g, "装备孔:"); + return str; +} + +MotaActionFunctions.replaceFromName = function (str) { + if (!str || MotaActionFunctions.disableReplace) return str; + var map = {}, list = []; + MotaActionFunctions.pattern.replaceStatusList.forEach(function (v) { + map[v[1]] = v[0]; list.push(v[1]); + }); + str = str.replace(new RegExp("状态[::](" + list.join("|") + ")", "g"), function (a, b) { + return map[b] ? ("status:" + map[b]) : b; + }).replace(/状态[::]/g, "status:"); + map = {}; list = []; + MotaActionFunctions.pattern.replaceItemList.forEach(function (v) { + map[v[1]] = v[0]; list.push(v[1]); + }); + str = str.replace(new RegExp("物品[::](" + list.join("|") + ")", "g"), function (a, b) { + return map[b] ? ("item:" + map[b]) : b; + }).replace(/物品[::]/g, "item:"); + str = str.replace(/变量[::]/g, "flag:").replace(/独立开关[::]/g, "switch:").replace(/全局存储[::]/g, "global:"); + + map = {}; list = []; + MotaActionFunctions.pattern.replaceEnemyList.forEach(function (v) { + map[v[1]] = v[0]; list.push(v[1]); + }); + str = str.replace(new RegExp("(enemy:|怪物[::])([a-zA-Z0-9_]+)[::](" + list.join("|") + ")", "g"), function (a, b, c, d) { + return map[d] ? ("enemy:" + c + ":" + map[d]) : d; + }).replace(/怪物[::]/g, "enemy:"); + + str = str.replace(/图块I[dD][::]/g, "blockId:").replace(/图块类别[::]/g, "blockCls:").replace(/装备孔[::]/g, "equip:"); + + return str; +} + */ \ No newline at end of file diff --git a/_server/README.md b/_server/README.md index d23bfb43..7bd750b1 100644 --- a/_server/README.md +++ b/_server/README.md @@ -1,142 +1,57 @@ # editor -[重构](refactoring.md) +直接使用游戏运行时(之后简称core)的代码来绘制游戏画面, 借助fs.js来实现浏览器编辑文件. 通过表格编辑数据, blockly图块编辑事件, code mirror编辑文本的可视化魔塔编辑器. ->! 以下均是v2.0时的说明, 未及时改动 +![](img_md/view1.png) -本目录下所有文件,以及`../editor.html`和`../启动服务.exe`([源码](http://github.com/ckcz123/mota-js-server/))是地图编辑器的所有组件. +左侧数据区, 中间地图区, 右侧素材区 -`editor.js`,`editor_file.js`和`editor_mode.js`耦合较强,`editor_blockly.js`和`editor_multi.js`和`fs.js`基本可以独立使用. +![](img_md/view2.png) -## 各组件功能 +事件编辑器 -### 总体上 +![](img_md/view3.png) -以`display:none`的形式引入了`index.html`的`dom`,修改了原来的`.gameCanvas #ui #data`等的名字以避免冲突 +脚本编辑器 -通过`main.init('editor')`加载数据 +> 此文件是editor的结构说明, 不是使用文档 -`editor`模式关闭了部分动画 +## 组成 -`core.drawMap`中`editor`模式下不再画图,而是生成画图的函数提供给`editor` +本目录下所有文件,以及`../editor.html`,`../editor-mobile.html`和`../启动服务.exe`,`../server.py`是地图编辑器的所有组件. -`editor`模式下`GlobalAnimate`可以独立的选择是否播放 +### 父目录 ++ editor(-mobile).html + 编辑器的[入口页面](http://127.0.0.1:1055/editor.html) + 以`display:none`的形式引入了core的`index.html`的`dom`,修改了原来的`.gameCanvas #ui #data`等的名字以避免冲突 ++ 启动服务.exe [源码](http://github.com/ckcz123/mota-js-server/) + 为fs.js提供后端支持, 同时集成了一些实用工具 ++ server.py + 非windows平台中为fs.js提供后端支持 -`core.playBgm`和`core.playSound`中非`play`模式不再播放声音 +### core -`core.show`和`core.hide`中非`play`模式不再进行动画而是立刻完成并执行回调 +游戏运行时中部分代码根据`main.mod=='editor'`进行了调整 -`editor`模式不执行`core.resize` ++ 通过`main.init('editor')`加载数据 -### editor.js ++ `editor`模式关闭了部分动画 -``` js -editor.mapInit();//清空地图 -editor.changeFloor('MT2')//切换地图 -editor.util.guid()//产生一个可以作为id的长随机字符串 -``` ++ `core.drawMap`中`editor`模式下不再画图,而是生成画图的函数提+ 供给`editor` -`editor.updateMap`中画未定义快的报错 ++ `editor`模式下`GlobalAnimate`可以独立的选择是否播放 -### editor_file.js ++ `core.playBgm`和`core.playSound`中非`play`模式不再播放声音 -提供了以下函数进行楼层`map`数组相关的操作 -```javascript -editor.file.getFloorFileList -editor.file.loadFloorFile -editor.file.saveFloorFile -editor.file.saveFloorFileAs -``` ++ `core.show`和`core.hide`中非`play`模式不再进行动画而是立刻+ 完成并执行回调 -编辑模式有关的查询 -```javascript -editor.file.editItem('redJewel',[],function(a){console.log(a)}); -editor.file.editEnemy('redBat',[],function(a){console.log(a)}); -editor.file.editLoc(2,0,[],function(a){console.log(a)}); -editor.file.editFloor([],function(a){console.log(a)}); -editor.file.editTower([],function(a){console.log(a)}); -editor.file.editFunctions([],function(a){console.log(a)}); -``` ++ `editor`模式不执行`core.resize` -编辑模式有关的编辑 -```javascript -editor.info={images: "terrains", y: 9}; -editor.file.changeIdAndIdnum('yellowWall2',16,editor.info,function(a){console.log(a)}); -editor.file.editItem('book',[["change","['items']['name']","怪物手册的新名字"]],function(a){console.log(a)}); -editor.file.editEnemy('redBat',[['change',"['atk']",20]],function(a){console.log(a)}); -editor.file.editLoc(2,6,[["change","['afterBattle']",null]],function(a){console.log(a)}); -editor.file.editFloor([["change","['title']",'样板 33 层']],function(a){console.log(a)}); -editor.file.editTower([["change","['values']['lavaDamage']",200]],function(a){console.log(a)}); -editor.file.editFunctions(["change","['events']['afterChangeLight']","function(x,y){console.log(x,y)}"],function(a){console.log(a)}); -``` - -### editor_mode.js -生成表格并绑定事件的函数 -```javascript -editor.mode.loc(); -editor.mode.enemyitem(); -editor.mode.floor(); -editor.mode.tower(); -editor.mode.functions(); -``` - -切换模式 -```javascript -editor.mode.onmode('');//清空 -editor.mode.onmode('save');//保存 -editor.mode.onmode('nextChange');//下次onmode时前端进行切换 - -editor.mode.onmode('loc'); -editor.mode.onmode('enemyitem'); -editor.mode.onmode('floor'); -editor.mode.onmode('tower'); -editor.mode.onmode('functions'); -editor.mode.onmode('map'); -editor.mode.onmode('appendpic'); -``` -在`onmode('save')`时,改动才会保存到文件,涉及到图片的改动需要刷新页面使得`editor`能看到 - -表格的`onchange`的实现中,获得当前模式的方式.不注意的话,修改`index.html`中页面的结构,会被坑 -```javascript -var node = thisTr.parentNode; -while (!editor_mode._ids.hasOwnProperty(node.getAttribute('id'))) { - node = node.parentNode; -} -editor_mode.onmode(editor_mode._ids[node.getAttribute('id')]); -``` - -`editor.mode.listen`中提供了追加素材的支持. - - -### editor_blockly.js - -把选定`id_`的事件用blockly编辑 -``` js -editor_blockly.import(id_,{type:'event'}); -``` - -把文本区域的代码转换成图块 -``` js -editor_blockly.parse(); -``` - -### editor_multi.js - -用[CodeMirror](https://github.com/codemirror/CodeMirror) 实现有高亮的多行文本编辑 - -编辑选定`id_`的文本域 -``` js -editor_multi.import(id_,{lint:true}) -``` - -编辑blockly方块的特定域 -``` js -editor_multi.multiLineEdit(value,b,f,{lint:true},callback) -``` ### fs.js -模仿node的fs模块提供如下api,与`启动服务.exe`配合为js提供文件读写功能 +依照[issue#31](https://github.com/ckcz123/mota-js/issues/13)的约定, 模仿node的fs模块提供如下api,与`启动服务.exe`,`server.py`配合为js提供文件读写功能, 是编辑器成立的前提 + ``` js fs.readFile('file.in','utf-8',callback) //读文本文件 @@ -164,3 +79,195 @@ fs.readdir(path, callback) //所有参数不允许缺省 ``` +### editor_multi.js + +用[CodeMirror](https://github.com/codemirror/CodeMirror) 实现有高亮的多行文本编辑 + +编辑选定`id_`的文本域 +``` js +editor_multi.import(id_,{lint:true}) +``` + +编辑blockly方块的特定域 +``` js +editor_multi.multiLineEdit(value,b,f,{lint:true},callback) +``` + +配置表格 +``` js +editor_multi.editCommentJs(mod) +``` + +### MotaAction.g4 + +通过[antlr-blockly](https://github.com/zhaouv/antlr-blockly)的语法定义core中各事件对应的方块. + +借助google的[blockly](https://github.com/google/blockly)来实现事件的可视化编辑. + +入口方块以`_m`结尾 + +一般语句写在`action`中, 以`_s`结尾 + +### editor_blockly.js + +把选定`id_`的事件用blockly编辑 +``` js +editor_blockly.import(id_,{type:'event'}); +``` + +把文本区域的代码转换成图块 +``` js +editor_blockly.parse(); +``` + +`initscript中`的`toolboxObj`定义了侧边栏中显示的图块 + +自定义`Blockly.FieldColour.prototype.createWidget_`修改了颜色选择器的行为 + +自定义`Blockly.FieldTextInput.prototype.showInlineEditor_`添加了自动补全 + + +### editor_mode.js + +用于切换数据区 + +加载数据 +```javascript +editor.mode.loc(); +editor.mode.enemyitem(); +editor.mode.floor(); +editor.mode.tower(); +editor.mode.functions(); +``` + +切换模式 +```javascript +editor.mode.onmode('');//清空 +editor.mode.onmode('save');//保存 +editor.mode.onmode('nextChange');//下次onmode时前端进行切换 + +editor.mode.onmode('loc'); +editor.mode.onmode('enemyitem'); +editor.mode.onmode('floor'); +editor.mode.onmode('tower'); +editor.mode.onmode('functions'); +editor.mode.onmode('map'); +editor.mode.onmode('appendpic'); +``` +在`onmode('save')`时,改动才会保存到文件,涉及到图片的改动需要刷新页面使得`editor`能看到 + +数据区一些通用的函数也定义在这里 + +### editor_table.js + +处理表格的生成, 及其响应的事件 + +其接受来自../project/\*.js的数据`obj`和来自table/\*.comment.js的注释`commentObj` + +commentObj的结构如示例 +``` js +{ + "_type": "object", + "_data": { + "events": { + "_type": "object", + "_data": { + "resetGame": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "重置整个游戏" + }, + "setInitData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "设置初始属性" + }, +``` +一层正常数据, 一层`_`开头的结构说明, 忽略`_`层的话与obj同结构 + +通过 +``` js +editor.table.objToTable(obj, commentObj) +editor.table.objToTr +editor.table.objToTd +``` +遍历这两个对象来生成表格, 叶节点根据`_type`渲染成对应的dom + +表格的值变化`onchange`, 双击`dblclickfunc`, 删除`deletefunc`, 添加`addfunc`也定义在此文件 + +### editor_mappanel.js + +与地图区相关的功能 ++ 画地图 线/矩形/tileset ++ 通过地图选中事件或素材 ++ 右键菜单 ++ 切换楼层 ++ 大地图移动可视窗口 + +### editor_materialpanel.js + +与素材区相关的功能 ++ 选中 ++ 展开/折叠 + +### editor_datapanel.js + +与数据区相关的功能 (且与表格无关的功能) ++ 地图编辑 + - 创建新地图 + - 批量创建 ++ 地图选点 ++ 图块属性 + - 注册素材 + - 修改id ++ 楼层属性 + - 修改楼层id ++ 全塔属性 ++ 脚本编辑 ++ 追加素材 + - 选择导入的区域 + - 导入图片 + - 改色相 + - 选中图片中的格子 + - 确认追加 ++ 公共事件 ++ 插件编写 + +### editor_ui.js + +ui事件中没有具体到前三个区中的函数 ++ 响应点击 ++ 快捷键 ++ 显示帮助 ++ UI预览 & 地图选点相关 + +### editor_util.js + +一些通用的函数 ++ 生成id ++ HTML转义 ++ 像素处理 ++ base64的encode/decode ++ 检查值是否为空 + +### editor_listen.js + +界面与功能的绑定 + +### editor_file.js + +包装fs.js, 把数据读写到对应的文件 + +### editor_game.js + +游戏数据的处理 + +此部分的重构未完成, 实际上是由editor_file.js和editor_file_unsorted.js来做的 + +### editor.js + +初始化加整合各模块 + +现状是还放了一些游戏数据有关的函数未挪到editor_game, 以及部分和入口页面生成有关的函数 \ No newline at end of file diff --git a/_server/colorPicker/LICENSE.md b/_server/colorPicker/LICENSE.md deleted file mode 100644 index 8394aa4a..00000000 --- a/_server/colorPicker/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Peter Dematté - -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. \ No newline at end of file diff --git a/_server/config.json b/_server/config.json new file mode 100644 index 00000000..fe6a45a9 --- /dev/null +++ b/_server/config.json @@ -0,0 +1 @@ +{"lastUsed":[],"foldPerCol":50,"folded":false,"editorLastFloorId":null,"disableBlocklyReplace":false,"shortcut":{"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0}} \ No newline at end of file diff --git a/_server/css/editor.css b/_server/css/editor.css index dd9c0e18..642fd168 100644 --- a/_server/css/editor.css +++ b/_server/css/editor.css @@ -40,7 +40,7 @@ body { width: 104px; } -#left, #mid, #right { +#left, #mid, #mid2, #right { border-radius: 2px; box-sizing: border-box; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); @@ -130,6 +130,14 @@ body { height: 630px; } +#mid2 { + position: absolute; + left: 448px; + top: 650px; + width: 440px; + bottom: 10px; +} + #mapEdit { overflow: hidden; } @@ -142,6 +150,13 @@ body { height: 416px; } +#lastUsedDiv { + height: auto; + bottom: 0; + margin-top: 20px; + overflow: hidden; +} + #mid .tools { position: absolute; width: 425px; @@ -239,15 +254,15 @@ body { position: absolute; left: 900px; top: 10px; - width: 440px; - height: 630px; + right: 0; + bottom: 0; /* border: 1px solid rgb(238, 13, 13); */ } #iconLib { position: absolute; - width: 435px; - height: 620px; + right: 0; + bottom: 0; left: 5px; top: 5px; overflow: auto; @@ -262,10 +277,6 @@ body { position: absolute; } -.egameCanvas { - position: absolute; -} - .gameCanvas { position: absolute; } @@ -287,6 +298,15 @@ body { 0 0 0 3px #000; } +#iconExpandBtn { + position: absolute; + left: 20px; + bottom: 30px; + font-size: 15px; + padding: 6px; + display: none; +} + .warnText { color: #D50000; font-weight: 700; @@ -458,4 +478,88 @@ table.row td { left: 6px; top: 6px; background-image:url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJmdWxsIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpldj0iaHR0cDovL3d3dy53My5vcmcvMjAwMS94bWwtZXZlbnRzIj4KPGc%2BCgk8cG9seWdvbiBmaWxsPSIjNjY2IiBwb2ludHM9IjkuMjA3LDYuMTI2IDcuNzkzLDcuNTQxIDExLjc5MywxMS41NDEgMTMuMjA3LDEwLjEyNiIgLz4KCTxwYXRoIGZpbGw9IiM2NjYiIGQ9Ik01LjkxNywyYzEuNjA4LDAsMi45MTcsMS4zMDgsMi45MTcsMi45MTdTNy41MjUsNy44MzMsNS45MTcsNy44MzNTMyw2LjUyNSwzLDQuOTE3UzQuMzA4LDIsNS45MTcsMgoJCSBNNS45MTcsMEMzLjIwMSwwLDEsMi4yMDEsMSw0LjkxN3MyLjIwMSw0LjkxNyw0LjkxNyw0LjkxN3M0LjkxNy0yLjIwMSw0LjkxNy00LjkxN0MxMC44MzMsMi4yMDEsOC42MzIsMCw1LjkxNywwTDUuOTE3LDB6IiAvPgo8L2c%2BCjwvc3ZnPgo%3D'); +} + +#uieventDiv { + display: none; + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background: rgba(127,127,127,0.6); + z-index: 2000 +} + +#uieventDialog { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -60%); + background: white; + width: 436px; +} + +#uieventHead { + margin: 10px 20px; +} + +#uieventTitle { + font-weight: bold; +} + +#uieventNo { + float: right; +} + +#uieventYes { + display: none; + float: right; + margin-right: 15px; +} + +#uieventBody { + width: 416px; + height: 416px; + position: relative; + margin-left: 10px; + margin-bottom: 5px; + overflow: hidden; +} + +#uievent { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; +} + +#selectPoint { + display: none; + margin-left: 10px; + margin-bottom: 10px; +} + +#selectPointFloor { + margin-right: 10px; +} + +#selectPointButtons { + display: inline; +} + +#selectPointBox { + position: absolute; + z-index: 75; + width: 26px; + height: 26px; + margin: 3px 0 0 3px; + padding: 0; + /* display: none; */ + box-sizing: border-box; + background-color: rgba(255, 255, 255, 0.0); + border: 1px solid #000; + box-shadow: 0 0 0 2px #fff, + 0 0 0 3px #000; } \ No newline at end of file diff --git a/_server/css/editor_mobile.css b/_server/css/editor_mobile.css index 1120c9fd..6dfaa651 100644 --- a/_server/css/editor_mobile.css +++ b/_server/css/editor_mobile.css @@ -110,6 +110,10 @@ body { position: absolute; } +#mid2 { + display: none; +} + #mapEdit { overflow: hidden; } @@ -247,10 +251,6 @@ body { position: absolute; } -.egameCanvas { - position: absolute; -} - .gameCanvas { position: absolute; } @@ -272,6 +272,15 @@ body { 0 0 0 3px #000; } +#iconExpandBtn { + position: absolute; + left: 20px; + bottom: 30px; + font-size: 15px; + padding: 6px; + display: none; +} + .warnText { color: #D50000; font-weight: 700; @@ -433,7 +442,7 @@ div.row .rowtd .rowtext{ font: normal 2.5vw Arial, sans-serif; list-style: none; margin: 0; - padding: 4px 7em 4px 28px; + padding: 4px 7em 4px 4px; white-space: nowrap; /* padding-left: 12px; */ @@ -482,4 +491,88 @@ div.row .rowtd .rowtext{ margin: 2px 0; border-radius: 3px; width: 90px; +} + +#uieventDiv { + display: none; + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background: rgba(127,127,127,0.6); + z-index: 2000 +} + +#uieventDialog { + position: fixed; + top: 50%; + left: 0; + transform: translateY(-50%); + background: white; + width: 100vw; +} + +#uieventHead { + margin: 10px 20px; +} + +#uieventTitle { + font-weight: bold; +} + +#uieventNo { + float: right; +} + +#uieventYes { + display: none; + float: right; + margin-right: 15px; +} + +#uieventBody { + width: 100vw; + height: 100vw; + position: relative; + margin-left: 0; + margin-bottom: 5px; + overflow: hidden; +} + +#uievent { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; +} + +#selectPoint { + display: none; + margin-left: 10px; + margin-bottom: 10px; +} + +#selectPointFloor { + margin-right: 10px; +} + +#selectPointButtons { + display: inline; +} + +#selectPointBox { + position: absolute; + z-index: 75; + width: 26px; + height: 26px; + margin: 3px 0 0 3px; + padding: 0; + /* display: none; */ + box-sizing: border-box; + background-color: rgba(255, 255, 255, 0.0); + border: 1px solid #000; + box-shadow: 0 0 0 2px #fff, + 0 0 0 3px #000; } \ No newline at end of file diff --git a/_server/css/editor_mode.css b/_server/css/editor_mode.css index a1859f65..33f1a9bd 100644 --- a/_server/css/editor_mode.css +++ b/_server/css/editor_mode.css @@ -7,7 +7,7 @@ .leftTab { overflow: auto; position: absolute; - height: 630px; + bottom: 0; } .leftTab > * { @@ -121,6 +121,7 @@ display: block; width: 100%; overflow: auto; + word-break: break-all; } .etable table th { @@ -157,14 +158,19 @@ } .etable tr > :nth-child(3) { - width: 60% + width: 35%; +} + +.etable tr > :nth-child(4) { + width: 25%; + padding: 0; } .etable table { overflow: visible; } -.etable tr:not(:first-child) > :last-child:hover { +.etable tr:not(:first-child) > :nth-child(3):hover { border: 1px solid rgb(87, 198, 232); box-shadow: 0px 0px 3px rgb(87, 198, 232); } @@ -175,14 +181,17 @@ box-shadow: 0px 0px 3px rgb(87, 232, 198); } -.etable tr:not(:first-child) > :last-child { +.etable tr:not(:first-child) > :nth-child(3) { margin: 0; padding: 0; height: 100%; - width: 100%; position: relative; } +.etable tr > :nth-child(4) { + text-align: center; +} + div.etableInputDiv { position: absolute; padding: 5px 0 0 5px; diff --git a/_server/css/editor_mode_mobile.css b/_server/css/editor_mode_mobile.css index 3098df27..bb680213 100644 --- a/_server/css/editor_mode_mobile.css +++ b/_server/css/editor_mode_mobile.css @@ -135,6 +135,7 @@ display: block; width: 100%; overflow: auto; + word-break: break-all; } .etable table th { @@ -171,14 +172,19 @@ } .etable tr > :nth-child(3) { - width: 60% + width: 30%; +} + +.etable tr > :nth-child(4) { + width: 30%; + text-align: center; } .etable table { overflow: visible; } -.etable tr:not(:first-child) > :last-child:hover { +.etable tr:not(:first-child) > :nth-child(3):hover { border: 1px solid rgb(87, 198, 232); box-shadow: 0px 0px 3px rgb(87, 198, 232); } @@ -189,11 +195,10 @@ box-shadow: 0px 0px 3px rgb(87, 232, 198); } -.etable tr:not(:first-child) > :last-child { +.etable tr:not(:first-child) > :nth-child(3) { margin: 0; padding: 0; height: 100%; - width: 100%; position: relative; } diff --git a/_server/editor.js b/_server/editor.js index 812d901d..7ddc0f60 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -4,6 +4,107 @@ function editor() { this.layerMod = "map";//["fgmap","map","bgmap"] this.isMobile = false; + this.dom={ + body:document.body, + eui:document.getElementById('eui'), + euiCtx:document.getElementById('eui').getContext('2d'), + efgCtx:document.getElementById('efg').getContext('2d'), + mid:document.getElementById('mid'), + mapEdit:document.getElementById('mapEdit'), + selectFloor:document.getElementById('selectFloor'), + iconExpandBtn :document.getElementById('iconExpandBtn'), + dataSelection : document.getElementById('dataSelection'), + iconLib:document.getElementById('iconLib'), + midMenu:document.getElementById('midMenu'), + extraEvent: document.getElementById('extraEvent'), + chooseThis : document.getElementById('chooseThis'), + chooseInRight : document.getElementById('chooseInRight'), + copyLoc : document.getElementById('copyLoc'), + moveLoc : document.getElementById('moveLoc'), + clearEvent : document.getElementById('clearEvent'), + clearLoc : document.getElementById('clearLoc'), + brushMod:document.getElementById('brushMod'), + brushMod2:document.getElementById('brushMod2'), + brushMod3:document.getElementById('brushMod3'), + bgc : document.getElementById('bg'), + bgCtx : document.getElementById('bg').getContext('2d'), + fgc : document.getElementById('fg'), + fgCtx : document.getElementById('fg').getContext('2d'), + evc : document.getElementById('event'), + evCtx : document.getElementById('event').getContext('2d'), + ev2c : document.getElementById('event2'), + ev2Ctx : document.getElementById('event2').getContext('2d'), + layerMod:document.getElementById('layerMod'), + layerMod2:document.getElementById('layerMod2'), + layerMod3:document.getElementById('layerMod3'), + viewportButtons:document.getElementById('viewportButtons'), + appendPicCanvas : document.getElementById('appendPicCanvas'), + bg : document.getElementById('appendPicCanvas').children[0], + source : document.getElementById('appendPicCanvas').children[1], + picClick : document.getElementById('appendPicCanvas').children[2], + sprite : document.getElementById('appendPicCanvas').children[3], + sourceCtx:document.getElementById('appendPicCanvas').children[1].getContext('2d'), + spriteCtx:document.getElementById('appendPicCanvas').children[3].getContext('2d'), + appendPicSelection : document.getElementById('appendPicSelection'), + selectAppend : document.getElementById('selectAppend'), + selectFileBtn :document.getElementById('selectFileBtn'), + changeFloorId :document.getElementById('changeFloorId'), + left1 : document.getElementById('left1'), + editModeSelect :document.getElementById('editModeSelect'), + mid2 : document.getElementById('mid2'), + lastUsedDiv: document.getElementById('lastUsedDiv'), + lastUsed: document.getElementById('lastUsed'), + lastUsedCtx: document.getElementById('lastUsed').getContext('2d'), + lockMode: document.getElementById('lockMode'), + }; + + this.uivalues={ + // 绘制区拖动有关 + holdingPath : 0, + stepPostfix : null,//用于存放寻路检测的第一个点之后的后续移动 + mouseOutCheck : 2, + startPos:null, + endPos:null, + // 撤销/恢复 + preMapData : [], + preMapMax: 10, + postMapData: [], + // + shortcut:{}, + copyedInfo : null, + // 折叠素材 + scrollBarHeight :0, + folded:false, + foldPerCol: 50, + // 画图区菜单 + lastRightButtonPos:[{x:0,y:0},{x:0,y:0}], + lastCopyedInfo : [null, null], + // + ratio : 1, + // blockly转义 + disableBlocklyReplace: false, + + // 绑定机关门事件相关 + bindSpecialDoor: { + loc: null, + n: -1, + enemys: [] + }, + + // 复制怪物或道具属性 + copyEnemyItem : { + type: null, + data: {} + }, + + // tile + tileSize: [1,1], + lockMode: false, + + // 最近使用的图块 + lastUsed: [], + }; + window.onerror = function (msg, url, lineNo, columnNo, error) { var string = msg.toLowerCase(); var substring = "script error"; @@ -30,6 +131,8 @@ function editor() { }; } +editor.prototype.uifunctions={}; + /* editor.loc editor.pos @@ -48,11 +151,20 @@ editor.prototype.init = function (callback) { editor.airwallImg.src = './project/images/airwall.png'; main.init('editor', function () { - editor_util_wrapper(editor); - editor_game_wrapper(editor, main, core); - editor_table_wrapper(editor); - editor_unsorted_1_wrapper(editor); - afterMainInit(); + editor.config = new editor_config(); + editor.config.load(function() { + editor_util_wrapper(editor); + editor_game_wrapper(editor, main, core); + editor_file_wrapper(editor); + editor_table_wrapper(editor); + editor_ui_wrapper(editor); + editor_mappanel_wrapper(editor); + editor_datapanel_wrapper(editor); + editor_materialpanel_wrapper(editor); + editor_listen_wrapper(editor); + editor.printe=printe; + afterMainInit(); + }) }); var afterMainInit = function () { @@ -63,10 +175,11 @@ editor.prototype.init = function (callback) { editor_file = editor_file(editor, function () { editor.file = editor_file; editor_mode = editor_mode(editor); - editor_unsorted_2_wrapper(editor_mode); 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 () { + core.resetGame(core.firstData.hero, null, core.firstData.floorId, core.clone(core.initStatus.maps)); + var lastFloorId = editor.config.get('editorLastFloorId', core.status.floorId); + if (core.floorIds.indexOf(lastFloorId) < 0) lastFloorId = core.status.floorId; + core.changeFloor(lastFloorId, null, core.firstData.hero.loc, null, function () { afterCoreReset(); }, true); core.events.setInitData(null); @@ -96,6 +209,17 @@ editor.prototype.init = function (callback) { editor_multi = editor_multi(); editor_blockly = editor_blockly(); + // --- 所有用到的flags + editor.used_flags = {}; + for (var floorId in editor.main.floors) { + editor.addUsedFlags(JSON.stringify(editor.main.floors[floorId])); + } + if (events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + for (var name in events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + editor.addUsedFlags(JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent[name])); + } + } + if (editor.useCompress == null) editor.useCompress = useCompress; if (Boolean(callback)) callback(); @@ -105,9 +229,9 @@ editor.prototype.init = function (callback) { } editor.prototype.mapInit = function () { - var ec = document.getElementById('event').getContext('2d'); + var ec = editor.dom.evCtx; ec.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); - document.getElementById('event2').getContext('2d').clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); + editor.dom.ev2Ctx.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); editor.map = []; var sy=editor.currentFloorData.map.length,sx=editor.currentFloorData.map[0].length; for (var y = 0; y < sy; y++) { @@ -140,53 +264,80 @@ editor.prototype.changeFloor = function (floorId, callback) { }); editor.currentFloorData[name]=mapArray; } - editor.preMapData = null; + editor.uivalues.preMapData = []; + editor.uivalues.postMapData = []; + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(true); core.changeFloor(floorId, null, {"x": 0, "y": 0, "direction": "up"}, null, function () { - core.bigmap.offsetX=0; - core.bigmap.offsetY=0; - editor.moveViewport(0,0); - editor.game.fetchMapFromCore(); editor.updateMap(); editor_mode.floor(); editor.drawEventBlock(); - if (callback) callback(); + + editor.viewportLoc = editor.viewportLoc || {}; + var loc = editor.viewportLoc[floorId] || [], x = loc[0] || 0, y = loc[1] || 0; + editor.setViewport(x, y); + + editor.config.set('editorLastFloorId', floorId, function() { + if (callback) callback(); + }); }); } /////////// 游戏绘图相关 /////////// editor.prototype.drawEventBlock = function () { - var fg=document.getElementById('efg').getContext('2d'); + var fg=editor.dom.efgCtx; fg.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + var firstData = editor.game.getFirstData(); for (var i=0;i= 0) { + fg.textAlign = 'right'; + editor.game.doCoreFunc("fillBoldText", fg, index + 1, + 32 * i + 28, 32 * j + 15, '#FF7F00', '14px Verdana'); + } } } } editor.prototype.drawPosSelection = function () { this.drawEventBlock(); - var fg=document.getElementById('efg').getContext('2d'); + var fg=editor.dom.efgCtx; fg.strokeStyle = 'rgba(255,255,255,0.7)'; fg.lineWidth = 4; fg.strokeRect(32*editor.pos.x - core.bigmap.offsetX + 4, 32*editor.pos.y - core.bigmap.offsetY + 4, 24, 24); @@ -238,30 +389,61 @@ editor.prototype.updateMap = function () { //ctx.drawImage(core.material.images[tileInfo.images], 0, tileInfo.y*32, 32, 32, x*32, y*32, 32, 32); } // 绘制地图 start - var eventCtx = document.getElementById('event').getContext("2d"); - var fgCtx = document.getElementById('fg').getContext("2d"); - var bgCtx = document.getElementById('bg').getContext("2d"); - for (var y = 0; y < editor.map.length; y++) + for (var y = 0; y < editor.map.length; y++) { for (var x = 0; x < editor.map[0].length; x++) { var tileInfo = editor.map[y][x]; - drawTile(eventCtx, x, y, tileInfo); + drawTile(editor.dom.evCtx, x, y, tileInfo); tileInfo = editor.fgmap[y][x]; - drawTile(fgCtx, x, y, tileInfo); + drawTile(editor.dom.fgCtx, x, y, tileInfo); tileInfo = editor.bgmap[y][x]; - drawTile(bgCtx, x, y, tileInfo); + drawTile(editor.dom.bgCtx, x, y, tileInfo); } + } // 绘制地图 end - + + this.updateLastUsedMap(); } -editor.prototype.moveViewport=function(x,y){ - core.bigmap.offsetX = core.clamp(core.bigmap.offsetX+32*x, 0, 32*core.bigmap.width-core.__PIXELS__); - core.bigmap.offsetY = core.clamp(core.bigmap.offsetY+32*y, 0, 32*core.bigmap.height-core.__PIXELS__); +editor.prototype.updateLastUsedMap = function () { + // 绘制最近使用事件 + var ctx = editor.dom.lastUsedCtx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.strokeStyle = 'rgba(255,128,0,0.85)'; + ctx.lineWidth = 4; + for (var i = 0; i < editor.uivalues.lastUsed.length; ++i) { + try { + var x = i % core.__SIZE__, y = parseInt(i / core.__SIZE__); + var info = editor.uivalues.lastUsed[i]; + if (!info || !info.images) continue; + if (info.isTile && core.material.images.tilesets[info.images]) { + ctx.drawImage(core.material.images.tilesets[info.images], 32 * info.x, 32 * info.y, 32, 32, x*32, y*32, 32, 32); + } else if (info.images == 'autotile' && core.material.images.autotile[info.id]) { + ctx.drawImage(core.material.images.autotile[info.id], 0, 0, 32, 32, x * 32, y * 32, 32, 32); + } else { + var per_height = info.images.endsWith('48') ? 48 : 32; + ctx.drawImage(core.material.images[info.images], 0, info.y * per_height, 32, per_height, x * 32, y * 32, 32, 32); + } + if (selectBox.isSelected() && editor.info.id == info.id) { + ctx.strokeRect(32 * x + 2, 32 * y + 2, 28, 28); + } + } catch (e) {} + } +} + +editor.prototype.setViewport=function (x, y) { + core.bigmap.offsetX = core.clamp(x, 0, 32*core.bigmap.width-core.__PIXELS__); + core.bigmap.offsetY = core.clamp(y, 0, 32*core.bigmap.height-core.__PIXELS__); + editor.viewportLoc = editor.viewportLoc || {}; + editor.viewportLoc[editor.currentFloorId] = [core.bigmap.offsetX, core.bigmap.offsetY]; core.control.updateViewport(); editor.buildMark(); editor.drawPosSelection(); } +editor.prototype.moveViewport=function(x,y){ + editor.setViewport(core.bigmap.offsetX+32*x, core.bigmap.offsetY+32*y); +} + /////////// 界面交互相关 /////////// editor.prototype.drawInitData = function (icons) { @@ -270,7 +452,11 @@ editor.prototype.drawInitData = function (icons) { var maxHeight = 700; var sumWidth = 0; editor.widthsX = {}; + editor.uivalues.folded = editor.config.get('folded', false); + // editor.uivalues.folded = true; + editor.uivalues.foldPerCol = editor.config.get('foldPerCol', 50); // var imgNames = Object.keys(images); //还是固定顺序吧; + editor.uivalues.lastUsed = editor.config.get("lastUsed", []); var imgNames = ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"]; for (var ii = 0; ii < imgNames.length; ii++) { @@ -280,20 +466,21 @@ editor.prototype.drawInitData = function (icons) { for (var im in autotiles) { tempy += autotiles[im].height; } - editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + 3 * 32) / 32, tempy]; - sumWidth += 3 * 32; + var tempx = editor.uivalues.folded ? 32 : 3 * 32; + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + tempx) / 32, tempy]; + sumWidth += tempx; maxHeight = Math.max(maxHeight, tempy); continue; } - if (img == 'terrains') { - editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + images[img].width) / 32, images[img].height + 32*2] - sumWidth += images[img].width; - maxHeight = Math.max(maxHeight, images[img].height + 32*2); - continue; + var width = images[img].width, height = images[img].height, mh = height; + if (editor.uivalues.folded) { + var per_height = (img == 'enemy48' || img == 'npc48' ? 48 : 32); + width = Math.ceil(height / per_height / editor.uivalues.foldPerCol) * 32; + if (width > 32) mh = per_height * editor.uivalues.foldPerCol; } - editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + images[img].width) / 32, images[img].height]; - sumWidth += images[img].width; - maxHeight = Math.max(maxHeight, images[img].height); + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + width) / 32, height]; + sumWidth += width; + maxHeight = Math.max(maxHeight, mh + 64); } var tilesets = images.tilesets; for (var ii in core.tilesets) { @@ -310,82 +497,75 @@ editor.prototype.drawInitData = function (icons) { if (fullWidth > edata.width) edata.style.width = (edata.width = fullWidth) / ratio + 'px'; edata.style.height = (edata.height = fullHeight) / ratio + 'px'; */ + var iconImages = document.getElementById('iconImages'); iconImages.style.width = (iconImages.width = fullWidth) / ratio + 'px'; iconImages.style.height = (iconImages.height = fullHeight) / ratio + 'px'; - var dc = {drawImage:function(){ - var image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight; - var a=Array.prototype.slice.call(arguments) - if(arguments.length==3){ - // [image, dx, dy]=arguments - // [sx, sy, sWidth, sHeight, dWidth, dHeight]=[0,0,image.width,image.height,image.width,image.height] - image=a[0] - a=[a[0],0,0,image.width,image.height,a[1],a[2],image.width,image.height] - } - if(arguments.length==5){ - // [image, dx, dy, dWidth, dHeight]=arguments - // [sx, sy, sWidth, sHeight]=[0,0,image.width,image.height] - image=a[0] - a=[a[0],0,0,image.width,image.height,a[1],a[2],a[3],a[4]] - } - if(arguments.length==9){ - // [image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight]=arguments - } - image=a[0]; - sx=a[1]; - sy=a[2]; - sWidth=a[3]; - sHeight=a[4]; - dx=a[5]; - dy=a[6]; - dWidth=a[7]; - dHeight=a[8]; - //放弃对 dWidth, dHeight 的支持, 始终画一样大的 - var dimg=new Image() - dimg.src = image.src; - dimg.style.clip=['rect(',sy,'px,',sx+sWidth,'px,',sy+sHeight,'px,',sx,'px)'].join('') - dimg.style.top=dy-sy+'px' - dimg.style.left=dx-sx+'px' - dimg.width=image.width/ratio - dimg.height=image.height/ratio - iconImages.appendChild(dimg) - }} - // var dc = edata.getContext('2d'); - var nowx = 0; - var nowy = 0; + var drawImage = function (image, x, y) { + image.style.left = x + 'px'; + image.style.top = y + 'px'; + iconImages.appendChild(image); + } + + var nowx = 0, nowy = 0; for (var ii = 0; ii < imgNames.length; ii++) { var img = imgNames[ii]; if (img == 'terrains') { - (function(image,dc,nowx){ + (function(image,nowx){ if (image.complete) { - dc.drawImage(image, nowx, 32); + drawImage(image, nowx, 32); core.material.images.airwall = image; delete(editor.airwallImg); } else image.onload = function () { - dc.drawImage(image, nowx, 32); + drawImage(image, nowx, 32); core.material.images.airwall = image; delete(editor.airwallImg); editor.updateMap(); } - })(editor.airwallImg,dc,nowx); - dc.drawImage(images[img], nowx, 32*2); - nowx += images[img].width; + })(editor.airwallImg,nowx); + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var subimgs = core.splitImage(images[img], 32, editor.uivalues.foldPerCol * 32); + var frames = images[img].width / 32; + for (var i = 0; i < subimgs.length; i+=frames) { + drawImage(subimgs[i], nowx, i==0?2*32:0); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 32*2); + nowx += images[img].width; + } continue; } if (img == 'autotile') { var autotiles = images[img]; + var tempx = editor.uivalues.folded ? 32 : 96; for (var im in autotiles) { - dc.drawImage(autotiles[im], 0, 0, 96, 128, nowx, nowy, 96, 128); + var subimgs = core.splitImage(autotiles[im], tempx, autotiles[im].height); + drawImage(subimgs[0], nowx, nowy); nowy += autotiles[im].height; } - nowx += 3 * 32; + nowx += tempx; continue; } - dc.drawImage(images[img], nowx, 0) - nowx += images[img].width; + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var per_height = img.endsWith('48') ? 48 : 32; + var subimgs = core.splitImage(images[img], 32, editor.uivalues.foldPerCol * per_height); + var frames = images[img].width / 32; + for (var i = 0; i < subimgs.length; i+=frames) { + drawImage(subimgs[i], nowx, 0); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 0); + nowx += images[img].width; + } } for (var ii in core.tilesets) { var img = core.tilesets[ii]; - dc.drawImage(tilesets[img], nowx, 0) + drawImage(tilesets[img], nowx, 0); nowx += tilesets[img].width; } //editor.mapInit(); @@ -459,14 +639,20 @@ editor.prototype.setSelectBoxFromInfo=function(thisevent){ pos.x=editor.widthsX[thisevent.images][1]; pos.y=thisevent.y; if(thisevent.x)pos.x+=thisevent.x; - if(thisevent.images=='terrains')pos.y+=2; ysize = thisevent.images.endsWith('48') ? 48 : 32; + if (editor.uivalues.folded && core.tilesets.indexOf(thisevent.images)==-1) { + pos.x += Math.floor(pos.y / editor.uivalues.foldPerCol); + pos.y %= editor.uivalues.foldPerCol; + } + if(pos.x == 0) pos.y+=2; } - var dataSelection = document.getElementById('dataSelection'); - 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);}); + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + setTimeout(function(){ + selectBox.isSelected(true); + editor.updateLastUsedMap(); + }); editor.info = JSON.parse(JSON.stringify(thisevent)); tip.infos(JSON.parse(JSON.stringify(thisevent))); editor.pos=pos; @@ -474,12 +660,31 @@ editor.prototype.setSelectBoxFromInfo=function(thisevent){ editor_mode.onmode('enemyitem'); } +editor.prototype.addUsedFlags = function (s) { + s.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC]+)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + s.replace(/flags\.([a-zA-Z_]\w*)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + if (window.flags) { + for (var s in editor.used_flags) { + if (!(s in window.flags)) { + window.flags[s] = null; + } + } + } +} + editor.prototype.listen = function () { - // 移动至 editor_unsorted_1.js + // 移动至 editor_listen.js }//绑定事件 editor.prototype.mobile_listen=function(){ - // 移动至 editor_unsorted_1.js + // 移动至 editor_listen.js } + + + editor = new editor(); \ No newline at end of file diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 02453bab..aa221608 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -2,7 +2,11 @@ editor_blockly = function () { var editor_blockly = {}; - initscript = String.raw` +/////////////////initscript start///////////////////////////// +// do not use String.raw because of highlighting +// Comment tagged templates +// https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates + initscript = /* js */` (function(){ var getCategory = function(name,custom){ for(var node of document.getElementById('toolbox').children) { @@ -22,6 +26,16 @@ editor_blockly = function () { "本事件触发一次后会消失", {"type": "hide", "time": 500}, ],'event'), + MotaActionFunctions.actionParser.parse({ + "condition": "flag:__door__==2", + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + {"type": "openDoor", "loc": [10,5]} + ], + },'autoEvent'), MotaActionBlocks['changeFloor_m'].xmlText(), MotaActionFunctions.actionParser.parse([{ "id": "moneyShop1", @@ -30,12 +44,17 @@ editor_blockly = function () { "textInList": "1F金币商店", "use": "money", "need": "20+10*times*(times+1)", - "text": "勇敢的武士啊,给我\${need}金币就可以:", + "text": "勇敢的武士啊,给我\\\${need}金币就可以:", "choices": [ {"text": "生命+800", "effect": "status:hp+=800"}, {"text": "攻击+4", "effect": "status:atk+=4"}, - {"text": "防御+4", "effect": "status:def+=4"}, - {"text": "魔防+10", "effect": "status:mdef+=10"} + ] + },{ + "id": "itemShop", + "item": true, + "textInList": "道具商店", + "choices": [ + {"id": "yellowKey", "number": 10, "money": 10} ] },{ "id": "keyShop1", @@ -50,6 +69,7 @@ editor_blockly = function () { MotaActionBlocks['eachArrive_m'].xmlText(), MotaActionBlocks['level_m'].xmlText(), MotaActionBlocks['commonEvent_m'].xmlText(), + MotaActionBlocks['item_m'].xmlText(), ], '显示文字':[ MotaActionBlocks['text_0_s'].xmlText(), @@ -72,20 +92,21 @@ editor_blockly = function () { MotaActionBlocks['confirm_s'].xmlText(), MotaActionBlocks['choices_s'].xmlText([ '选择剑或者盾','流浪者','man',MotaActionBlocks['choicesContext'].xmlText([ - '剑','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), + '剑','','',null,'',MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), MotaActionBlocks['choicesContext'].xmlText([ - '盾','','',null,MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), + '盾','','',null,'',MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [9,3]}]), ]) ]) ]), ], '数据相关':[ - MotaActionBlocks['setValue_s'].xmlText([ - MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) - ]), MotaActionBlocks['addValue_s'].xmlText([ - MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) + MotaActionBlocks['idString_1_e'].xmlText(['status','生命']), '', false ]), + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idString_1_e'].xmlText(['status','生命']), '', false + ]), + MotaActionBlocks['setEnemy_s'].xmlText(), MotaActionBlocks['setFloor_s'].xmlText(), MotaActionBlocks['setGlobalAttribute_s'].xmlText(), MotaActionBlocks['setGlobalValue_s'].xmlText(), @@ -93,7 +114,6 @@ editor_blockly = function () { MotaActionBlocks['input_s'].xmlText(), MotaActionBlocks['input2_s'].xmlText(), MotaActionBlocks['update_s'].xmlText(), - MotaActionBlocks['updateEnemys_s'].xmlText(), MotaActionBlocks['moveHero_s'].xmlText(), MotaActionBlocks['jumpHero_s'].xmlText(), MotaActionBlocks['changeFloor_s'].xmlText(), @@ -101,6 +121,8 @@ editor_blockly = function () { MotaActionBlocks['changePos_1_s'].xmlText(), MotaActionBlocks['battle_s'].xmlText(), MotaActionBlocks['useItem_s'].xmlText(), + MotaActionBlocks['loadEquip_s'].xmlText(), + MotaActionBlocks['unloadEquip_s'].xmlText(), MotaActionBlocks['openShop_s'].xmlText(), MotaActionBlocks['disableShop_s'].xmlText(), MotaActionBlocks['setHeroIcon_s'].xmlText(), @@ -108,6 +130,7 @@ editor_blockly = function () { MotaActionBlocks['unfollow_s'].xmlText(), ], '地图处理':[ + MotaActionBlocks['battle_1_s'].xmlText(), MotaActionBlocks['openDoor_s'].xmlText(), MotaActionBlocks['closeDoor_s'].xmlText(), MotaActionBlocks['show_s'].xmlText(), @@ -122,8 +145,8 @@ editor_blockly = function () { MotaActionBlocks['hideFloorImg_s'].xmlText(), ], '事件控制':[ - MotaActionBlocks['if_s'].xmlText(), MotaActionBlocks['if_1_s'].xmlText(), + MotaActionBlocks['if_s'].xmlText(), MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, {"action": [], "nobreak": true}, @@ -141,12 +164,19 @@ editor_blockly = function () { ], '特效/声音':[ MotaActionBlocks['sleep_s'].xmlText(), - MotaActionBlocks['wait_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "wait", "data": [ + {"case": "keyboard", "keycode": 13, "action": [{"type": "comment", "text": "当按下回车(keycode=13)时执行此事件"}]}, + {"case": "mouse", "px": [0,32], "py": [0,32], "action": [{"type": "comment", "text": "当点击地图左上角时执行此事件"}]}, + ]}), MotaActionBlocks['waitAsync_s'].xmlText(), MotaActionBlocks['vibrate_s'].xmlText(), MotaActionBlocks['animate_s'].xmlText(), + MotaActionBlocks['setViewport_s'].xmlText(), + MotaActionBlocks['moveViewport_s'].xmlText(), MotaActionBlocks['showStatusBar_s'].xmlText(), MotaActionBlocks['hideStatusBar_s'].xmlText(), + MotaActionBlocks['showHero_s'].xmlText(), + MotaActionBlocks['hideHero_s'].xmlText(), MotaActionBlocks['setCurtain_0_s'].xmlText(), MotaActionBlocks['setCurtain_1_s'].xmlText(), MotaActionBlocks['screenFlash_s'].xmlText(), @@ -164,16 +194,39 @@ editor_blockly = function () { MotaActionBlocks['autoSave_s'].xmlText(), MotaActionBlocks['callLoad_s'].xmlText(), ], + 'UI绘制':[ + MotaActionBlocks['previewUI_s'].xmlText(), + MotaActionBlocks['clearMap_s'].xmlText(), + MotaActionBlocks['clearMap_1_s'].xmlText(), + MotaActionBlocks['setAttribute_s'].xmlText(), + MotaActionBlocks['fillText_s'].xmlText(), + MotaActionBlocks['fillBoldText_s'].xmlText(), + MotaActionBlocks['drawTextContent_s'].xmlText(), + MotaActionBlocks['fillRect_s'].xmlText(), + MotaActionBlocks['strokeRect_s'].xmlText(), + MotaActionBlocks['drawLine_s'].xmlText(), + MotaActionBlocks['drawArrow_s'].xmlText(), + MotaActionBlocks['fillPolygon_s'].xmlText(), + MotaActionBlocks['strokePolygon_s'].xmlText(), + MotaActionBlocks['fillCircle_s'].xmlText(), + MotaActionBlocks['strokeCircle_s'].xmlText(), + MotaActionBlocks['drawImage_s'].xmlText(), + MotaActionBlocks['drawImage_1_s'].xmlText(), + MotaActionBlocks['drawIcon_s'].xmlText(), + MotaActionBlocks['drawBackground_s'].xmlText(), + MotaActionBlocks['drawSelector_s'].xmlText(), + MotaActionBlocks['drawSelector_1_s'].xmlText(), + ], '原生脚本':[ MotaActionBlocks['function_s'].xmlText(), MotaActionBlocks['unknown_s'].xmlText(), ], '值块':[ - MotaActionBlocks['setValue_s'].xmlText([ - MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) - ]), MotaActionBlocks['addValue_s'].xmlText([ - MotaActionBlocks['idString_1_e'].xmlText(['status','hp']) + MotaActionBlocks['idString_1_e'].xmlText(['status','生命']), '', false + ]), + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idString_1_e'].xmlText(['status','生命']), '', false ]), MotaActionBlocks['expression_arithmetic_0'].xmlText(), MotaActionBlocks['evFlag_e'].xmlText(), @@ -182,33 +235,37 @@ editor_blockly = function () { MotaActionBlocks['idString_e'].xmlText(), MotaActionBlocks['idString_1_e'].xmlText(), MotaActionBlocks['idString_2_e'].xmlText(), + MotaActionBlocks['idString_3_e'].xmlText(), + MotaActionBlocks['idString_4_e'].xmlText(), + MotaActionBlocks['idString_5_e'].xmlText(), + MotaActionBlocks['idString_6_e'].xmlText(), MotaActionBlocks['evalString_e'].xmlText(), ], '常见事件模板':[ '', MotaActionFunctions.actionParser.parseList({"type": "if", "condition": "!core.musicStatus.bgmStatus", "true": [ - "\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳" + "\\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳" ], "false": [] }), '', MotaActionFunctions.actionParser.parse([ - {"type": "choices", "text": "\t[老人,man]少年,你需要钥匙吗?\n我这里有大把的!", + {"type": "choices", "text": "\\t[老人,man]少年,你需要钥匙吗?\\n我这里有大把的!", "choices": [ - {"text": "黄钥匙(\${9+flag:shop_times}金币)", "color": [255,255,0,1], "action": [ + {"text": "黄钥匙(\\\${9+flag:shop_times}金币)", "color": [255,255,0,1], "action": [ {"type": "if", "condition": "status:money>=9+flag:shop_times", "true": [ {"type": "addValue", "name": "status:money", "value": "-(9+flag:shop_times)"}, {"type": "addValue", "name": "item:yellowKey", "value": "1"}, ], "false": [ - "\t[老人,man]你的金钱不足!", + "\\t[老人,man]你的金钱不足!", {"type": "revisit"} ] } ]}, - {"text": "蓝钥匙(\${18+2*flag:shop_times}金币)", "color": [0,0,255,1], "action": [ + {"text": "蓝钥匙(\\\${18+2*flag:shop_times}金币)", "color": [0,0,255,1], "action": [ ]}, {"text": "离开", "action": [ {"type": "exit"} @@ -361,7 +418,7 @@ function omitedcheckUpdateFunction(event) { } } try { - var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\i/g, '\\\\i'); + var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\\\(i|c|d|e)/g, '\\\\\\\\$1'); codeAreaHL.setValue(code); } catch (error) { codeAreaHL.setValue(String(error)); @@ -433,6 +490,18 @@ function omitedcheckUpdateFunction(event) { } })(); `; +/////////////////initscript end ///////////////////////////// + + editor.uivalues.disableBlocklyReplace = editor.config.get("disableBlocklyReplace", false); + var replaceCheckbox = document.getElementById('blocklyReplace'); + replaceCheckbox.checked = !editor.uivalues.disableBlocklyReplace; + + editor_blockly.triggerReplace = function () { + editor.uivalues.disableBlocklyReplace = !replaceCheckbox.checked; + editor.config.set("disableBlocklyReplace", !replaceCheckbox.checked); + if (MotaActionFunctions) MotaActionFunctions.disableReplace = !replaceCheckbox.checked; + alert("已" + (replaceCheckbox.checked ? "开启" : "关闭") + "中文变量名替换!\n关闭并重开事件编辑器以生效。"); + } var input_ = ''; editor_blockly.runOne = function () { @@ -463,6 +532,7 @@ function omitedcheckUpdateFunction(event) { } input_ = xhr.responseText; editor_blockly.runOne(); + MotaActionFunctions.disableReplace = editor.uivalues.disableBlocklyReplace; } xhr.open('GET', '_server/MotaAction.g4', true); xhr.send(null); @@ -503,8 +573,7 @@ function omitedcheckUpdateFunction(event) { MotaActionFunctions.parse( eval('obj=' + codeAreaHL.getValue().replace(/[<>&]/g, function (c) { return {'<': '<', '>': '>', '&': '&'}[c]; - }).replace(/\\r/g, '\\\\r').replace(/\\f/g, '\\\\f') - .replace(/\\i/,'\\\\i')), + }).replace(/\\(r|f|i|c|d|e)/g,'\\\\$1')), document.getElementById('entryType').value ); } @@ -578,14 +647,86 @@ function omitedcheckUpdateFunction(event) { return; } var code = Blockly.JavaScript.workspaceToCode(editor_blockly.workspace); - code = code.replace(/\\i/g, '\\\\i'); + code = code.replace(/\\(i|c|d|e)/g, '\\\\$1'); eval('var obj=' + code); + if (this.checkAsync(obj) && confirm("警告!存在不等待执行完毕的事件但却没有用【等待所有异步事件处理完毕】来等待" + + "它们执行完毕,这样可能会导致录像检测系统出问题。\n你要返回修改么?")) return; setvalue(JSON.stringify(obj)); } + // 检查"不等待处理完毕" + editor_blockly.checkAsync = function (obj) { + if (!(obj instanceof Array)) return false; + var hasAsync = false; + for (var i = 0; i < obj.length; ++i) { + var one = obj[i]; + if (one.type == 'if' && (this.checkAsync(one['true']) || this.checkAsync(one['false']))) + return true; + if ((one.type == 'while' || one.type == 'dowhile') && this.checkAsync(one.data)) + return true; + if (one.type == 'if' && (this.checkAsync(one.yes) || this.checkAsync(one.no))) + return true; + if (one.type == 'choices') { + var list = one.choices; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'switch') { + var list = one.caseList; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.async && one.type != 'animate') hasAsync = true; + if (one.type == 'waitAsync') hasAsync = false; + } + return hasAsync; + } + + var previewBlock = function (b) { + var types = [ + "previewUI_s", "clearMap_s", "clearMap_1_s", "setAttribute_s", "fillText_s", + "fillBoldText_s", "fillRect_s", "strokeRect_s", "drawLine_s", + "drawArrow_s", "fillPolygon_s", "strokePolygon_s", "fillCircle_s", "strokeCircle_s", + "drawImage_s", "drawImage_1_s", "drawIcon_s", "drawBackground_s", "drawSelector_s", "drawSelector_1_s", + "waitContext_2" + ]; + if (b && types.indexOf(b.type)>=0) { + try { + var code = "[" + Blockly.JavaScript.blockToCode(b).replace(/\\(i|c|d|e)/g, '\\\\$1') + "]"; + eval("var obj="+code); + if (obj.length > 0 && b.type == 'waitContext_2') { + var dt = obj[0]; + editor.uievent.previewUI([{"type": "fillRect", "x": dt.px[0], "y": dt.py[0], + "width": "(" + dt.px[1] + ")-(" + dt.px[0] + ")", "height": "(" + dt.py[1] + ")-(" + dt.py[0] + ")", + "style": "rgba(255,0,0,0.5)"}]) + } + else if (obj.length > 0 && b.type.startsWith(obj[0].type)) { + if (b.type == 'previewUI_s') + editor.uievent.previewUI(obj[0].action); + else editor.uievent.previewUI([obj[0]]); + } + } catch (e) {main.log(e);} + return true; + } + return false; + } + editor_blockly.doubleClickBlock = function (blockId) { var b = editor_blockly.workspace.getBlockById(blockId); - //console.log(b); + + if (previewBlock(b)) return; + + if (b && b.type in selectPointBlocks) { // selectPoint + this.selectPoint(); + return; + } + var textStringDict = { 'text_0_s': 'EvalString_0', 'text_1_s': 'EvalString_2', @@ -597,6 +738,7 @@ function omitedcheckUpdateFunction(event) { 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', 'confirm_s': 'EvalString_0', + 'drawTextContent_s': 'EvalString_0', } var f = b ? textStringDict[b.type] : null; if (f) { @@ -615,7 +757,7 @@ function omitedcheckUpdateFunction(event) { 'comment_s', 'show_s', 'hide_s', - 'setValue_s', + 'addValue_s', 'if_s', 'battle_s', 'openDoor_s', @@ -644,40 +786,26 @@ function omitedcheckUpdateFunction(event) { // Index from 1 - 9 editor_blockly.openToolbox = function(index) { - // var element = document.getElementById(':'+index); - // if (element == null || element.getAttribute("aria-selected")=="true") return; - // element.click(); - editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index-1]); + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); } editor_blockly.reopenToolbox = function(index) { - // var element = document.getElementById(':'+index); - // if (element == null) return; - // if (element.getAttribute("aria-selected")=="true") element.click(); - // element.click(); - editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index-1]); - editor_blockly.workspace.getFlyout_().show(editor_blockly.workspace.toolbox_.tree_.children_[index-1].blocks); + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); + editor_blockly.workspace.getFlyout_().show(editor_blockly.workspace.toolbox_.tree_.children_[index].blocks); } editor_blockly.closeToolbox = function() { - /* - for (var i=1; i<=10; i++) { - var element = document.getElementById(':'+i); - if (element && element.getAttribute("aria-selected")=="true") { - element.click(); - return; - } - } - */ editor_blockly.workspace.toolbox_.clearSelection(); } var searchInput = document.getElementById("searchBlock"); searchInput.onfocus = function () { - editor_blockly.reopenToolbox(10); + editor_blockly.reopenToolbox(-1); } searchInput.oninput = function () { - editor_blockly.reopenToolbox(10); + editor_blockly.reopenToolbox(-1); } editor_blockly.searchBlock = function (value) { @@ -702,6 +830,342 @@ function omitedcheckUpdateFunction(event) { return results.length == 0 ? editor_blockly.lastUsedType : results; } + // ------ select point ------ + + // id: [x, y, floorId, forceFloor] + var selectPointBlocks = { + "changeFloor_m": ["Number_0", "Number_1", "IdString_0", true], + "jumpHero_s": ["PosString_0", "PosString_1"], + "changeFloor_s": ["PosString_0", "PosString_1", "IdString_0", true], + "changePos_0_s": ["PosString_0", "PosString_1"], + "battle_1_s": ["PosString_0", "PosString_1"], + "openDoor_s": ["PosString_0", "PosString_1", "IdString_0"], + "closeDoor_s": ["PosString_0", "PosString_1"], + "show_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "hide_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "setBlock_s": ["EvalString_1", "EvalString_2", "IdString_0"], + "move_s": ["PosString_0", "PosString_1"], + "jump_s": ["PosString_2", "PosString_3"], // 跳跃暂时只考虑终点 + "showBgFgMap_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "hideBgFgMap_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "setBgFgBlock_s": ["EvalString_1", "EvalString_2", "IdString_0"], + "showFloorImg_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "hideFloorImg_s": ["EvalString_0", "EvalString_1", "IdString_0"], + "trigger_s": ["PosString_0", "PosString_1"], + "insert_2_s": ["PosString_0", "PosString_1", "IdString_0"], + "animate_s": ["EvalString_0", "EvalString_0"], + "setViewport_s": ["PosString_0", "PosString_1"] + } + + editor_blockly.selectPoint = function () { + var block = Blockly.selected, arr = null; + var floorId = editor.currentFloorId, pos = editor.pos, x = pos.x, y = pos.y; + if (block != null && block.type in selectPointBlocks) { + arr = selectPointBlocks[block.type]; + var xv = parseInt(block.getFieldValue(arr[0])), yv = parseInt(block.getFieldValue(arr[1])); + if (block.type == 'animate_s') { + var v = block.getFieldValue(arr[0]).split(","); + xv = parseInt(v[0]); yv = parseInt(v[1]); + } + if (!isNaN(xv)) x = xv; + if (!isNaN(yv)) y = yv; + if (arr[2] != null) floorId = block.getFieldValue(arr[2]) || floorId; + } + editor.uievent.selectPoint(floorId, x, y, arr && arr[2] == null, function (fv, xv, yv) { + if (!arr) return; + if (arr[2] != null) { + if (fv != editor.currentFloorId) block.setFieldValue(fv, arr[2]); + else block.setFieldValue(arr[3] ? fv : "", arr[2]); + } + if (block.type == 'animate_s') { + block.setFieldValue(xv+","+yv, arr[0]); + } + else { + block.setFieldValue(xv+"", arr[0]); + block.setFieldValue(yv+"", arr[1]); + } + if (block.type == 'changeFloor_m') { + block.setFieldValue("floorId", "Floor_List_0"); + block.setFieldValue("loc", "Stair_List_0"); + } + }); + } + + editor_blockly.getAutoCompletions = function (content) { + // --- content为当前框中输入内容;将返回一个列表,为后续所有可补全内容 + + // 检查 status:xxx,item:xxx和flag:xxx + var index = Math.max(content.lastIndexOf(":"), content.lastIndexOf(":")); + if (index >= 0) { + var ch = content.charAt(index); + var before = content.substring(0, index), token = content.substring(index+1); + if (/^[a-zA-Z0-9_\u4E00-\u9FCC]*$/.test(token)) { + if (before.endsWith("状态") || (ch == ':' && before.endsWith("status"))) { + var list = Object.keys(core.status.hero); + if (before.endsWith("状态") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceStatusList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("物品") || (ch == ':' && before.endsWith("item"))) { + var list = Object.keys(core.material.items); + if (before.endsWith("物品") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceItemList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("变量") || (ch == ':' && before.endsWith("flag"))) { + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } else if (before.endsWith("怪物") || (ch == ':' && before.endsWith("enemy"))) { + return Object.keys(core.material.enemys).filter(function (one) { + return one != token && one.startsWith(token); + }) + } else { + var index2 = Math.max(content.lastIndexOf(":", index-1), content.lastIndexOf(":", index-1)); + var ch2 = content.charAt(index2); + if (index2 >= 0) { + before = content.substring(0, index2); + if (before.endsWith("怪物") || (ch == ':' && ch2 == ':' && before.endsWith("enemy"))) { + var list = ["name", "hp", "atk", "def", "money", "experience", "point", "special"]; + if (before.endsWith("怪物") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceEnemyList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }) + } + } + + } + } + } + + // 提供 core.xxx 的补全 + index = content.lastIndexOf("core."); + if (index >= 0) { + var s = content.substring(index + 5); + if (/^[\w.]*$/.test(s)) { + var tokens = s.split("."); + var now = core, prefix = tokens[tokens.length - 1]; + for (var i = 0; i < tokens.length - 1; ++i) { + now = now[tokens[i]]; + if (now == null) break; + } + if (now != null) { + var candidates = []; + for (var i in now) { + candidates.push(i); + } + return candidates.filter(function (one) { + return one != prefix && one.startsWith(prefix); + }).sort(); + } + } + } + + // 提供 flags.xxx 补全 + index = content.lastIndexOf("flags."); + if (index >= 0) { + var token = content.substring(index+6); + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token) + && /^[a-zA-Z_]\w*$/.test(one); + }).sort(); + } + + return []; + } + + editor_blockly.completeItems = []; return editor_blockly; } -//editor_blockly=editor_blockly(); \ No newline at end of file + +// --- modify Blockly + +Blockly.FieldColour.prototype.createWidget_ = function() { + Blockly.WidgetDiv.hide(); + + // console.log('here') + var self=this; + var pb=self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf=args[args.indexOf(self.name)-1] + + var getValue=function(){ + // return self.getValue() // css颜色 + var f = pb.getFieldValue(targetf); + if (/^(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)?$/.test(f)) { + return f; + } + return ""; + // 也可以用 pb.getFieldValue(targetf) 获得颜色块左边的域的内容 + } + + var setValue=function(newValue){ // css颜色 + self.setValue(newValue) + var c=new Colors(); + c.setColor(newValue) + var rgbatext = [c.colors.webSmart.r,c.colors.webSmart.g,c.colors.webSmart.b,c.colors.alpha].join(","); + pb.setFieldValue(rgbatext, targetf) // 放在颜色块左边的域中 + } + + setTimeout(function () { + document.getElementById("colorPicker").value = getValue(); + window.jsColorPicker.confirm = setValue; + // 设置位置 + triggerColorPicker(Blockly.WidgetDiv.DIV.style.left, Blockly.WidgetDiv.DIV.style.top); + }); + + return document.createElement('table'); +}; + +Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { + Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_()); + var div = Blockly.WidgetDiv.DIV; + // Create the input. + var htmlInput = + goog.dom.createDom(goog.dom.TagName.INPUT, 'blocklyHtmlInput'); + htmlInput.setAttribute('spellcheck', this.spellcheck_); + var fontSize = + (Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale) + 'pt'; + div.style.fontSize = fontSize; + htmlInput.style.fontSize = fontSize; + + Blockly.FieldTextInput.htmlInput_ = htmlInput; + div.appendChild(htmlInput); + + htmlInput.value = htmlInput.defaultValue = this.text_; + htmlInput.oldValue_ = null; + + // console.log('here') + var self=this; + var pb=self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf=args[args.indexOf(self.name)+1] + + // ------ colour + + if(targetf && targetf.slice(0,7)==='Colour_'){ + var inputDom = htmlInput; + // var getValue=function(){ // 获得自己的字符串 + // return pb.getFieldValue(self.name); + // } + var setValue = function(newValue){ // 设置右边颜色块的css颜色 + pb.setFieldValue(newValue, targetf) + } + // 给inputDom绑事件 + inputDom.oninput=function(){ + var value=inputDom.value + if(/[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?/.test(value)){ + setValue('rgba('+value+')') + } + } + } + else { + + htmlInput.onkeydown = function (e) { + if (e.keyCode == 13 && awesomplete.opened && awesomplete.selected) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + awesomplete.select(); + return false; + } + } + + // --- awesomplete + var awesomplete = new Awesomplete(htmlInput, { + minChars: pb.type == "idString_3_e" ? 1 : 2, + maxItems: 12, + autoFirst: true, + replace: function (text) { + text = text.toString(); + var value = this.input.value, index = this.input.selectionEnd; + if (index == null) index = value.length; + if (index < awesomplete.prefix.length) index = awesomplete.prefix.length; + var str = value.substring(0, index - awesomplete.prefix.length) + text + value.substring(index); + this.input.value = str; + pb.setFieldValue(str, self.name); + index += text.length - awesomplete.prefix.length; + this.input.setSelectionRange(index, index); + + editor_blockly.completeItems = editor_blockly.completeItems.filter(function (x) { + return x != text; + }); + editor_blockly.completeItems.unshift(text); + }, + filter: function () {return true;}, + item: function (text, input) { + var li = document.createElement("li"); + li.setAttribute("role", "option"); + li.setAttribute("aria-selected", "false"); + input = awesomplete.prefix.trim(); + if (input != "") text = text.replace(new RegExp("^"+input, "i"), "$&"); + li.innerHTML = text; + return li; + }, + sort: function (a, b) { + a = a.toString(); b = b.toString(); + var ia = editor_blockly.completeItems.indexOf(a), ib = editor_blockly.completeItems.indexOf(b); + if (ia < 0) ia = editor_blockly.completeItems.length; + if (ib < 0) ib = editor_blockly.completeItems.length; + if (ia != ib) return ia - ib; + if (a.length != b.length) return a.length - b.length; + return a < b ? -1 : 1; + } + }); + + htmlInput.oninput = function () { + var value = htmlInput.value, index = htmlInput.selectionEnd; + if (index == null) index = value.length; + value = value.substring(0, index); + // cal prefix + awesomplete.prefix = ""; + for (var i = index - 1; i>=0; i--) { + var c = value.charAt(i); + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC]$/.test(c)) { + awesomplete.prefix = value.substring(i+1); + break; + } + } + + var list = editor_blockly.getAutoCompletions(value); + if (pb.type == "idString_3_e") { + list = list.concat(Object.keys(core.material.enemys).filter(function (one) { + return one != value && one.startsWith(value); + })); + list.sort(); + } + + awesomplete.list = list; + awesomplete.ul.style.marginLeft = getCaretCoordinates(htmlInput, htmlInput.selectionStart).left - + htmlInput.scrollLeft - 20 + "px"; + awesomplete.evaluate(); + } + + awesomplete.container.style.width = "100%"; + + window.awesomplete = awesomplete; + } + + if (!quietInput) { + htmlInput.focus(); + htmlInput.select(); + } + this.validate_(); + this.resizeEditor_(); + + this.bindEvents_(htmlInput); +}; \ No newline at end of file diff --git a/_server/editor_config.js b/_server/editor_config.js new file mode 100644 index 00000000..016b316c --- /dev/null +++ b/_server/editor_config.js @@ -0,0 +1,34 @@ +function editor_config() { + this.address = "_server/config.json"; +} + +editor_config.prototype.load = function(callback) { + var _this = this; + fs.readFile(this.address, "utf-8", function(e, d) { + if (e) { + console.warn("无法读取配置文件, 已重新生成"); + _this.config = {}; + _this.save(callback); + } else { + _this.config = JSON.parse(d); + if (callback) callback(); + } + }); +} + +editor_config.prototype.get = function(key, defaultValue) { + value = this.config[key]; + return value != null ? value : defaultValue; +} + +editor_config.prototype.set = function(key, value, callback) { + this.config[key] = value; + if (callback !== false) this.save(callback); +} + +editor_config.prototype.save = function(callback) { + fs.writeFile(this.address, JSON.stringify(this.config) ,'utf-8', function(e) { + if (e) alert("写入配置文件失败"); + if (callback instanceof Function) callback(); + }) +} diff --git a/_server/editor_datapanel.js b/_server/editor_datapanel.js new file mode 100644 index 00000000..948ce9c0 --- /dev/null +++ b/_server/editor_datapanel.js @@ -0,0 +1,809 @@ +editor_datapanel_wrapper = function (editor) { + + // 此文件内的内容仅做了分类, 未仔细整理函数 + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + editor.uifunctions.newMap_func = function () { + + var newMap = document.getElementById('newMap'); + var newFileName = document.getElementById('newFileName'); + newMap.onclick = function () { + if (!newFileName.value) return; + if (core.floorIds.indexOf(newFileName.value) >= 0) { + printe("该楼层已存在!"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newFileName.value)) { + printe("楼层名不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + if (!core.isset(width) || !core.isset(height) || width < core.__SIZE__ || height < core.__SIZE__ || width * height > 1000) { + printe("新建地图的宽高都不得小于" + core.__SIZE__ + ",且宽高之积不能超过1000"); + return; + } + + editor_mode.onmode(''); + editor.file.saveNewFile(newFileName.value, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds.push(newFileName.value); + 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刷新编辑器生效'); + }); + }); + } + + } + + + editor.uifunctions.createNewMaps_func = function () { + var newMaps = document.getElementById('newMaps'); + var newFloors = document.getElementById('newFloors'); + newMaps.onclick = function () { + if (newFloors.style.display == 'none') newFloors.style.display = 'block'; + else newFloors.style.display = 'none'; + } + + var createNewMaps = document.getElementById('createNewMaps'); + createNewMaps.onclick = function () { + var floorIds = document.getElementById('newFloorIds').value; + if (!floorIds) return; + var from = parseInt(document.getElementById('newMapsFrom').value), + to = parseInt(document.getElementById('newMapsTo').value); + if (!core.isset(from) || !core.isset(to) || from > to || from < 0 || to < 0) { + printe("请输入有效的起始和终止楼层"); + return; + } + if (to - from >= 100) { + printe("一次最多创建99个楼层"); + return; + } + var floorIdList = []; + for (var i = from; i <= to; i++) { + var floorId = floorIds.replace(/\${(.*?)}/g, function (word, value) { + return eval(value); + }); + if (core.floorIds.indexOf(floorId) >= 0) { + printe("要创建的楼层 " + floorId + " 已存在!"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (floorIdList.indexOf(floorId) >= 0) { + printe("尝试重复创建楼层 " + floorId + " !"); + return; + } + floorIdList.push(floorId); + } + + var width = parseInt(document.getElementById('newMapsWidth').value); + var height = parseInt(document.getElementById('newMapsHeight').value); + if (!core.isset(width) || !core.isset(height) || width < core.__SIZE__ || height < core.__SIZE__ || width * height > 1000) { + printe("新建地图的宽高都不得小于" + core.__SIZE__ + ",且宽高之积不能超过1000"); + return; + } + editor_mode.onmode(''); + + editor.file.saveNewFiles(floorIdList, from, to, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds = core.floorIds.concat(floorIdList); + 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('批量创建 ' + floorIdList[0] + '~' + floorIdList[floorIdList.length - 1] + ' 成功,请F5刷新编辑器生效'); + }); + }); + } + + } + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图选点 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + // 添加自动事件页,无需双击 + editor.uifunctions.addAutoEvent = function () { + if (editor_mode.mode != 'loc') return false; + var newid = '2'; + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); + newid = testid + ''; + } + editor_mode.addAction(['add', "['autoEvent']['" + newid + "']", null]); + editor_mode.onmode('save'); + } + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 图块属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + editor.uifunctions.newIdIdnum_func = function () { + var newIdIdnum = document.getElementById('newIdIdnum'); + newIdIdnum.children[2].onclick = function () { + if (newIdIdnum.children[0].value && newIdIdnum.children[1].value) { + var id = newIdIdnum.children[0].value; + var idnum = parseInt(newIdIdnum.children[1].value); + if (!core.isset(idnum)) { + printe('不合法的idnum'); + return; + } + if (!/^[0-9a-zA-Z_]+$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线') + return; + } + editor.file.changeIdAndIdnum(id, idnum, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('添加id和idnum成功,请F5刷新编辑器'); + }); + } else { + printe('请输入id和idnum'); + } + } + newIdIdnum.children[4].onclick = function () { + editor.file.autoRegister(editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('该列所有剩余项全部自动注册成功,请F5刷新编辑器'); + }) + } + } + + editor.uifunctions.changeId_func = function () { + var changeId = document.getElementById('changeId'); + changeId.children[1].onclick = function () { + var id = changeId.children[0].value; + if (id) { + if (!/^[0-9a-zA-Z_]+$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线') + return; + } + editor.file.changeIdAndIdnum(id, null, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('修改id成功,请F5刷新编辑器'); + }); + } else { + printe('请输入要修改到的ID'); + } + } + } + + editor.uifunctions.copyPasteEnemyItem_func = function () { + var copyEnemyItem = document.getElementById('copyEnemyItem'); + var pasteEnemyItem = document.getElementById('pasteEnemyItem'); + + copyEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || (cls != 'enemys' && cls != 'enemy48' && cls != 'items')) return; + editor.uivalues.copyEnemyItem.type = cls; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + editor.uivalues.copyEnemyItem.data = core.clone(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id]); + printf("怪物属性复制成功"); + } else { + editor.uivalues.copyEnemyItem.data = {}; + for (var x in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + if (items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[x][id] != null) { + editor.uivalues.copyEnemyItem.data[x] = core.clone(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[x][id]); + } + } + printf("道具属性复制成功"); + } + } + + pasteEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls || cls != editor.uivalues.copyEnemyItem.type) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要覆盖此怪物的全部属性么?这是个不可逆操作!")) { + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id] = core.clone(editor.uivalues.copyEnemyItem.data); + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("怪物属性粘贴成功\n请再重新选中该怪物方可查看更新后的表格。"); + }) + } + } else { + if (confirm("你确定要覆盖此道具的全部属性么?这是个不可逆操作!")) { + for (var x in editor.uivalues.copyEnemyItem.data) { + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[x][id] = core.clone(editor.uivalues.copyEnemyItem.data[x]); + } + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("道具属性粘贴成功\n请再重新选中该道具方可查看更新后的表格。"); + }) + } + } + + } + + + } + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 楼层属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + editor.uifunctions.changeFloorId_func = function () { + + editor.dom.changeFloorId.children[1].onclick = function () { + var floorId = editor.dom.changeFloorId.children[0].value; + if (floorId) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (main.floorIds.indexOf(floorId) >= 0) { + printe("楼层名 " + floorId + " 已存在!"); + return; + } + var currentFloorId = editor.currentFloorId; + editor.currentFloorId = floorId; + editor.currentFloorData.floorId = floorId; + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err); + } + core.floorIds[core.floorIds.indexOf(currentFloorId)] = floorId; + 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]) + } + alert("修改floorId成功,需要刷新编辑器生效。\n请注意,原始的楼层文件没有删除,请根据需要手动删除。"); + window.location.reload(); + }); + }); + } else { + printe('请输入要修改到的floorId'); + } + } + } + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 全塔属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 脚本编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 追加素材 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + editor.uifunctions.fixCtx_func = function () { + [editor.dom.sourceCtx, editor.dom.spriteCtx].forEach(function (ctx) { + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + }) + } + + editor.uifunctions.selectAppend_func = function () { + + var selectAppend_str = []; + ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"].forEach(function (image) { + selectAppend_str.push(["\n'].join('')); + }); + editor.dom.selectAppend.innerHTML = selectAppend_str.join(''); + editor.dom.selectAppend.onchange = function () { + + var value = editor.dom.selectAppend.value; + + if (value == 'autotile') { + editor_mode.appendPic.imageName = 'autotile'; + for (var jj = 0; jj < 4; jj++) editor.dom.appendPicSelection.children[jj].style = 'display:none'; + if (editor_mode.appendPic.img) { + editor.dom.sprite.style.width = (editor.dom.sprite.width = editor_mode.appendPic.img.width) / editor.uivalues.ratio + 'px'; + editor.dom.sprite.style.height = (editor.dom.sprite.height = editor_mode.appendPic.img.height) / editor.uivalues.ratio + 'px'; + editor.dom.spriteCtx.clearRect(0, 0, editor.dom.sprite.width, editor.dom.sprite.height); + editor.dom.spriteCtx.drawImage(editor_mode.appendPic.img, 0, 0); + } + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + editor_mode.appendPic.imageName = value; + var img = core.material.images[value]; + editor_mode.appendPic.toImg = img; + var num = ~~img.width / 32; + editor_mode.appendPic.num = num; + editor_mode.appendPic.index = 0; + var selectStr = ''; + for (var ii = 0; ii < num; ii++) { + editor.dom.appendPicSelection.children[ii].style = 'left:0;top:0;height:' + (ysize - 6) + 'px'; + selectStr += '{"x":0,"y":0},' + } + editor_mode.appendPic.selectPos = eval('[' + selectStr + ']'); + for (var jj = num; jj < 4; jj++) { + editor.dom.appendPicSelection.children[jj].style = 'display:none'; + } + editor.dom.sprite.style.width = (editor.dom.sprite.width = img.width) / editor.uivalues.ratio + 'px'; + editor.dom.sprite.style.height = (editor.dom.sprite.height = img.height + ysize) / editor.uivalues.ratio + 'px'; + editor.dom.spriteCtx.drawImage(img, 0, 0); + } + editor.dom.selectAppend.onchange(); + } + + editor.uifunctions.selectFileBtn_func = function () { + + var autoAdjust = function (image, callback) { + var changed = false; + + // Step 1: 检测白底 + var tempCanvas = document.createElement('canvas').getContext('2d'); + tempCanvas.canvas.width = image.width; + tempCanvas.canvas.height = image.height; + tempCanvas.mozImageSmoothingEnabled = false; + tempCanvas.webkitImageSmoothingEnabled = false; + tempCanvas.msImageSmoothingEnabled = false; + tempCanvas.imageSmoothingEnabled = false; + tempCanvas.drawImage(image, 0, 0); + var imgData = tempCanvas.getImageData(0, 0, image.width, image.height); + var trans = 0, white = 0, black = 0; + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.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 (white > black && white > trans * 10 && confirm("看起来这张图片是以纯白为底色,是否自动调整为透明底色?")) { + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.getPixel(imgData, i, j); + if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255 && pixel[3] == 255) { + editor.util.setPixel(imgData, i, j, [0, 0, 0, 0]); + } + } + } + tempCanvas.clearRect(0, 0, image.width, image.height); + tempCanvas.putImageData(imgData, 0, 0); + changed = true; + } + /* + if (black>white && black>trans*10 && confirm("看起来这张图片是以纯黑为底色,是否自动调整为透明底色?")) { + for (var i=0;i= num) editor_mode.appendPic.index = ii + 1 - num; + else editor_mode.appendPic.index++; + editor_mode.appendPic.selectPos[ii] = pos; + editor.dom.appendPicSelection.children[ii].style = [ + 'left:', pos.x * 32, 'px;', + 'top:', pos.y * pos.ysize, 'px;', + 'height:', pos.ysize - 6, 'px;' + ].join(''); + } + } + + editor.uifunctions.appendConfirm_func = function () { + + var appendRegister = document.getElementById('appendRegister'); + + var appendConfirm = document.getElementById('appendConfirm'); + appendConfirm.onclick = function () { + + var confirmAutotile = function () { + var image = editor_mode.appendPic.img; + if (image.width % 96 != 0 || image.height != 128) { + printe("不合法的Autotile图片!"); + return; + } + var imgData = editor.dom.sourceCtx.getImageData(0, 0, image.width, image.height); + editor.dom.spriteCtx.putImageData(imgData, 0, 0); + var imgbase64 = editor.dom.sprite.toDataURL().split(',')[1]; + + // Step 1: List文件名 + fs.readdir('./project/images', function (err, data) { + if (err) { + printe(err); + throw (err); + } + + // Step 2: 选择Autotile文件名 + var filename; + for (var i = 1; ; ++i) { + filename = 'autotile' + i; + if (data.indexOf(filename + ".png") == -1) break; + } + + // Step 3: 写入文件 + fs.writeFile('./project/images/' + filename + ".png", imgbase64, 'base64', function (err, data) { + if (err) { + printe(err); + throw (err); + } + // Step 4: 自动注册 + editor.file.registerAutotile(filename, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('自动元件' + filename + '注册成功,请F5刷新编辑器'); + }) + + }) + + }) + + } + + if (editor.dom.selectAppend.value == 'autotile') { + confirmAutotile(); + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + for (var ii = 0, v; v = editor_mode.appendPic.selectPos[ii]; ii++) { + // var imgData = editor.dom.sourceCtx.getImageData(v.x * 32, v.y * ysize, 32, ysize); + // editor.dom.spriteCtx.putImageData(imgData, ii * 32, editor.dom.sprite.height - ysize); + // editor.dom.spriteCtx.drawImage(editor_mode.appendPic.img, v.x * 32, v.y * ysize, 32, ysize, ii * 32, height, 32, ysize) + + editor.dom.spriteCtx.drawImage(editor.dom.sourceCtx.canvas, v.x * 32, v.y * ysize, 32, ysize, 32 * ii, editor.dom.sprite.height - ysize, 32, ysize); + } + var dt = editor.dom.spriteCtx.getImageData(0, 0, editor.dom.sprite.width, editor.dom.sprite.height); + var imgbase64 = editor.dom.sprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/images/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.sprite.height; + editor.dom.sprite.style.height = (editor.dom.sprite.height = (currHeight + ysize)) + "px"; + editor.dom.spriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }); + } else { + printf('追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + } + + var quickAppendConfirm = document.getElementById('quickAppendConfirm'); + quickAppendConfirm.onclick = function () { + var value = editor.dom.selectAppend.value; + if (value != 'enemys' && value != 'enemy48' && value != 'npcs' && value != 'npc48') + return printe("只有怪物或NPC才能快速导入!"); + var ysize = value.endsWith('48') ? 48 : 32; + if (editor.dom.sourceCtx.canvas.width != 128 || editor.dom.sourceCtx.canvas.height != 4 * ysize) + return printe("只有 4*4 的素材图片才可以快速导入!"); + + var dt = editor.dom.spriteCtx.getImageData(0, 0, editor.dom.sprite.width, editor.dom.sprite.height); + editor.dom.sprite.style.height = (editor.dom.sprite.height = (editor.dom.sprite.height + 3 * ysize)) + "px"; + editor.dom.spriteCtx.putImageData(dt, 0, 0); + if (editor.dom.sprite.width == 64) { // 两帧 + editor.dom.spriteCtx.drawImage(editor.dom.sourceCtx.canvas, 32, 0, 64, 4 * ysize, 0, editor.dom.sprite.height - 4 * ysize, 64, 4 * ysize); + } else { // 四帧 + editor.dom.spriteCtx.drawImage(editor.dom.sourceCtx.canvas, 0, 0, 128, 4 * ysize, 0, editor.dom.sprite.height - 4 * ysize, 128, 4 * ysize); + } + + dt = editor.dom.spriteCtx.getImageData(0, 0, editor.dom.sprite.width, editor.dom.sprite.height); + var imgbase64 = editor.dom.sprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/images/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.sprite.height; + editor.dom.sprite.style.height = (editor.dom.sprite.height = (currHeight + ysize)) + "px"; + editor.dom.spriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('快速追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }) + } else { + printf('快速追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + + } + } + + /////////////////////////////////////////////////////////////////////// + //////////////////// 公共事件 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 插件编写 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/_server/editor_file.js b/_server/editor_file.js index 1a8a9fa0..f737f910 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -1,827 +1,65 @@ -editor_file = function (editor, callback) { - - var editor_file = {}; - - - var commentjs = { - 'comment': 'comment', - 'data.comment': 'dataComment', - 'functions.comment': 'functionsComment', - 'events.comment': 'eventsComment', - 'plugins.comment': 'pluginsComment', - } - for (var key in commentjs) { - (function (key) { - var value = commentjs[key]; - var script = document.createElement('script'); - if (window.location.href.indexOf('_server') !== -1) - script.src = key + '.js'; - else - script.src = '_server/table/' + key + '.js'; - document.body.appendChild(script); - script.onload = function () { - editor_file[value] = eval(key.replace('.', '_') + '_c456ea59_6018_45ef_8bcc_211a24c627dc'); - var loaded = Boolean(callback); - for (var key_ in commentjs) { - loaded = loaded && editor_file[commentjs[key_]] - } - if (loaded) callback(); - } - })(key); +editor_file_wrapper = function (editor) { + editor_file_proto = function () { + /** + * 以 + * { + * "floor.MT1":, + * "plugins": + * } + * 的形式记录所有更改过的文件,save时写入 + * 的内容暂时还没想好 + */ + this.fileMark = {} } - - editor_file.getFloorFileList = function (callback) { - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') + // 这个函数之后挪到editor.table? + editor_file_proto.prototype.loadCommentjs = function (callback) { + var commentjs = { + 'comment': 'comment', + 'data.comment': 'dataComment', + 'functions.comment': 'functionsComment', + 'events.comment': 'eventsComment', + 'plugins.comment': 'pluginsComment', } - ; - /* var fs = editor.fs; - fs.readdir('project/floors',function(err, data){ - callback([data,err]); - }); */ - callback([editor.core.floorIds, null]); - } - //callback([Array,err:String]) - editor_file.loadFloorFile = function (filename, callback) { - //filename不含'/'不含'.js' - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - - editor.currentFloorId = editor.core.status.floorId; - editor.currentFloorData = editor.core.floors[editor.currentFloorId]; - } - //callback(err:String) - editor_file.saveFloorFile = function (callback) { - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - /* if (!isset(editor.currentFloorId) || !isset(editor.currentFloorData)) { - callback('未选中文件或无数据'); - } */ - var filename = 'project/floors/' + editor.currentFloorId + '.js'; - var datastr = ['main.floors.', editor.currentFloorId, '=\n']; - if (editor.currentFloorData.map == 'new') { - /* - editor.currentFloorData.map = editor.map.map(function (v) { - return v.map(function () { - return 0 - }) - }); - */ - var width = parseInt(document.getElementById('newMapWidth').value); - var height = parseInt(document.getElementById('newMapHeight').value); - var row = []; - for (var i=0;i0) - saveSetting('icons', iconActions, tempcallback); - else tempcallback(null); - - saveSetting('maps', mapActions, tempcallback); - - if (image=='items') - saveSetting('items', templateActions, tempcallback); - else if (image.indexOf('enemy')==0) - saveSetting('enemys', templateActions, tempcallback); - else tempcallback(null); - } - - editor_file.registerAutotile = function (filename, callback) { - var idnum = 140; - while (editor.core.maps.blocksInfo[idnum]) idnum++; - - var iconActions = []; - var mapActions = []; - - iconActions.push(["add", "['autotile']['" + filename + "']", 0]); - mapActions.push(["add", "['" + idnum + "']", {'cls': 'autotile', 'id': filename, 'noPass': true}]); - - var templist = []; - var tempcallback = function (err) { - templist.push(err); - if (templist.length == 2) { - if (templist[0] != null || templist[1] != null) - callback((templist[0] || '') + '\n' + (templist[1] || '')); - //这里如果一个成功一个失败会出严重bug - else - callback(null); - } - } - - saveSetting('icons', iconActions, tempcallback); - saveSetting('maps', mapActions, tempcallback); - } - - editor_file.changeIdAndIdnum = function (id, idnum, info, callback) { - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - //检查maps中是否有重复的idnum或id - var change = -1; - for (var ii in editor.core.maps.blocksInfo) { - if (ii == idnum) { - //暂时只允许创建新的不允许修改已有的 - //if (info.idnum==idnum){change=ii;break;}//修改id - callback('idnum重复了'); - return; - } - if (editor.core.maps.blocksInfo[ii].id == id) { - //if (info.id==id){change=ii;break;}//修改idnum - callback('id重复了'); - return; - } - } - /* - if (change!=-1 && change!=idnum){//修改idnum - editor.core.maps.blocksInfo[idnum] = editor.core.maps.blocksInfo[change]; - delete(editor.core.maps.blocksInfo[change]); - } else if (change==idnum) {//修改id - var oldid = editor.core.maps.blocksInfo[idnum].id; - editor.core.maps.blocksInfo[idnum].id = id; - for(var ii in editor.core.icons.icons){ - if (ii.hasOwnProperty(oldid)){ - ii[id]=ii[oldid]; - delete(ii[oldid]); - } - } - } else {//创建新的 - editor.core.maps.blocksInfo[idnum]={'cls': info.images, 'id':id}; - editor.core.icons.icons[info.images][id]=info.y; - } - */ - var templist = []; - var tempcallback = function (err) { - templist.push(err); - if (templist.length == 2) { - if (templist[0] != null || templist[1] != null) - callback((templist[0] || '') + '\n' + (templist[1] || '')); - //这里如果一个成功一个失败会出严重bug - else - callback(null); - } - } - saveSetting('maps', [["add", "['" + idnum + "']", {'cls': info.images, 'id': id}]], tempcallback); - saveSetting('icons', [["add", "['" + info.images + "']['" + id + "']", info.y]], tempcallback); - if (info.images === 'items') { - saveSetting('items', [["add", "['items']['" + id + "']", editor_file.comment._data.items_template]], function (err) { - if (err) { - printe(err); - throw(err) - } - }); - } - if (info.images === 'enemys' || info.images === 'enemy48') { - saveSetting('enemys', [["add", "['" + id + "']", editor_file.comment._data.enemys_template]], function (err) { - if (err) { - printe(err); - throw(err) - } - }); - } - - callback(null); - } - //callback(err:String) - editor_file.editItem = function (id, actionList, callback) { - /*actionList:[ - ["change","['items']['name']","红宝石的新名字"], - ["add","['items']['新的和name同级的属性']",123], - ["change","['itemEffectTip']","',攻击力+'+editor.core.values.redJewel"], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - actionList.forEach(function (value) { - var tempindex = value[1].indexOf(']') + 1; - value[1] = [value[1].slice(0, tempindex), "['" + id + "']", value[1].slice(tempindex)].join(''); - }); - saveSetting('items', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var locObj_ = {}; - Object.keys(editor_file.comment._data.items._data).forEach(function (v) { - if (isset(editor.core.items[v][id]) && v !== 'items') - locObj_[v] = editor.core.items[v][id]; - else - locObj_[v] = null; - }); - locObj_['items'] = (function () { - var locObj = Object.assign({}, editor.core.items.items[id]); - Object.keys(editor_file.comment._data.items._data.items._data).forEach(function (v) { - if (!isset(editor.core.items.items[id][v])) - locObj[v] = null; - }); - return locObj; - })(); - return locObj_; - })(), - editor_file.comment._data.items, - null]); - } - //只有items.cls是items的才有itemEffect和itemEffectTip,keys和constants和tools只有items - } - //callback([obj,commentObj,err:String]) - editor_file.editEnemy = function (id, actionList, callback) { - /*actionList:[ - ["change","['name']","初级巫师的新名字"], - ["add","['新的和name同级的属性']",123], - ["change","['bomb']",null], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - actionList.forEach(function (value) { - value[1] = "['" + id + "']" + value[1]; - }); - saveSetting('enemys', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var locObj = Object.assign({}, editor.core.enemys.enemys[id]); - Object.keys(editor_file.comment._data.enemys._data).forEach(function (v) { - if (!isset(editor.core.enemys.enemys[id][v])) - /* locObj[v]=editor.core.enemys.enemys[id][v]; - else */ - locObj[v] = null; - }); - return locObj; - })(), - editor_file.comment._data.enemys, - null]); + })(key); } } - //callback([obj,commentObj,err:String]) - editor_file.editMapBlocksInfo = function (idnum, actionList, callback) { - /*actionList:[ - ["change","['events']",["\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], - ["change","['afterBattle']",null], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - var tempmap=[]; - for(var ii=0;ii=editor.core.icons.tilesetStartOffset && !isset(editor.core.maps.blocksInfo[idnum]) && tempmap.indexOf(idnum)===-1){ - actionList.splice(ii,0,["add","['" + idnum + "']",{"cls": "tileset", "id": "X"+idnum, "noPass": true}]); - tempmap.push(idnum); - ii++; - } - value[1] = "['" + idnum + "']" + value[1]; - } - saveSetting('maps', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var sourceobj=editor.core.maps.blocksInfo[idnum]; - if(!isset(sourceobj) && idnum>=editor.core.icons.tilesetStartOffset)sourceobj={"cls": "tileset", "id": "X"+idnum, "noPass": true} - var locObj = Object.assign({}, sourceobj); - Object.keys(editor_file.comment._data.maps._data).forEach(function (v) { - if (!isset(sourceobj[v])) - locObj[v] = null; - }); - locObj.idnum = idnum; - return locObj; - })(), - editor_file.comment._data.maps, - null]); + editor_file_proto.prototype.alertWhenCompress = function () { + if (editor.useCompress === true) { + editor.useCompress = 'alerted'; + setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请使用启动服务.exe->Js代码压缩工具重新压缩,或者把main.js的useCompress改成false来使用原始文件')", 1000) } } - //callback([obj,commentObj,err:String]) - //////////////////////////////////////////////////////////////////// - - editor_file.editLoc = function (x, y, actionList, callback) { - /*actionList:[ - ["change","['events']",["\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], - ["change","['afterBattle']",null], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - actionList.forEach(function (value) { - value[1] = value[1] + "['" + x + "," + y + "']"; - }); - saveSetting('floorloc', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var locObj = {}; - Object.keys(editor_file.comment._data.floors._data.loc._data).forEach(function (v) { - if (isset(editor.currentFloorData[v][x + ',' + y])) - locObj[v] = editor.currentFloorData[v][x + ',' + y]; - else - locObj[v] = null; - }); - return locObj; - })(), - editor_file.comment._data.floors._data.loc, - null]); - } - - } - //callback([obj,commentObj,err:String]) - - //////////////////////////////////////////////////////////////////// - - editor_file.editFloor = function (actionList, callback) { - /*actionList:[ - ["change","['title']",'样板 3 层'], - ["change","['color']",null], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - saveSetting('floors', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var locObj = Object.assign({}, editor.currentFloorData); - Object.keys(editor_file.comment._data.floors._data.floor._data).forEach(function (v) { - if (!isset(editor.currentFloorData[v])) - /* locObj[v]=editor.currentFloorData[v]; - else */ - locObj[v] = null; - }); - Object.keys(editor_file.comment._data.floors._data.loc._data).forEach(function (v) { - delete(locObj[v]); - }); - delete(locObj.map); - delete(locObj.bgmap); - delete(locObj.fgmap); - return locObj; - })(), - editor_file.comment._data.floors._data.floor, - null]); - } - } - //callback([obj,commentObj,err:String]) - - //////////////////////////////////////////////////////////////////// - - editor_file.editTower = function (actionList, callback) { - /*actionList:[ - ["change","['firstData']['version']",'Ver 1.0.1 (Beta)'], - ["change","['values']['lavaDamage']",200], - ] - 为[]时只查询不修改 - */ - var data_obj = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - saveSetting('data', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - //var locObj=Object.assign({'main':{}},editor.core.data); - var locObj = Object.assign({}, data_obj, {'main': {}}); - Object.keys(editor_file.dataComment._data.main._data).forEach(function (v) { - if (isset(editor.main[v])) - locObj.main[v] = data_obj.main[v]; - else - locObj.main[v] = null; - }); - return locObj; - })(), - editor_file.dataComment, - null]); - } - } - //callback([obj,commentObj,err:String]) - - //////////////////////////////////////////////////////////////////// - - var fmap = {}; - var fjson = JSON.stringify(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a, function (k, v) { - if (v instanceof Function) { - var id_ = editor.util.guid(); - fmap[id_] = v.toString(); - return id_; - } else return v - }, 4); - var fobj = JSON.parse(fjson); - editor_file.functionsMap = fmap; - editor_file.functionsJSON = fjson; - var buildlocobj = function (locObj) { - for (var key in locObj) { - if (typeof(locObj[key]) !== typeof('')) buildlocobj(locObj[key]); - else locObj[key] = fmap[locObj[key]]; - } - }; - - editor_file.editFunctions = function (actionList, callback) { - /*actionList:[ - ["change","['events']['afterChangeLight']","function(x,y){console.log(x,y)}"], - ["change","['ui']['drawAbout']","function(){...}"], - ] - 为[]时只查询不修改 - */ - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - saveSetting('functions', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - (function () { - var locObj = JSON.parse(fjson); - buildlocobj(locObj); - return locObj; - })(), - editor_file.functionsComment, - null]); - } - } - //callback([obj,commentObj,err:String]) - - //////////////////////////////////////////////////////////////////// - - editor_file.editCommonEvent = function (actionList, callback) { - /*actionList:[ - ["change","['test']",['123']], - ] - 为[]时只查询不修改 - */ - var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; - if (!isset(callback)) { - printe('未设置callback'); - throw('未设置callback') - } - ; - if (isset(actionList) && actionList.length > 0) { - actionList.forEach(function (value) { - value[1] = "['commonEvent']" + value[1]; - }); - saveSetting('events', actionList, function (err) { - callback([err]); - }); - } else { - callback([ - Object.assign({},data_obj), - editor_file.eventsComment._data.commonEvent, - null]); - } - } - //callback([obj,commentObj,err:String]) - - //////////////////////////////////////////////////////////////////// - - 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([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; - } - return true - } - - var formatMap = function (mapArr,trySimplify) { - if(!mapArr || JSON.stringify(mapArr)==JSON.stringify([]))return ''; - if(trySimplify){ + editor_file_proto.prototype.formatMap = function (mapArr, trySimplify) { + if (!mapArr || JSON.stringify(mapArr) == JSON.stringify([])) return ''; + if (trySimplify) { //检查是否是全0二维数组 - var jsoncheck=JSON.stringify(mapArr).replace(/\D/g,''); - if(jsoncheck==Array(jsoncheck.length+1).join('0'))return ''; + var jsoncheck = JSON.stringify(mapArr).replace(/\D/g, ''); + if (jsoncheck == Array(jsoncheck.length + 1).join('0')) return ''; } //把二维数组格式化 var formatArrStr = ''; var arr = JSON.stringify(mapArr).replace(/\s+/g, '').split('],['); - var si=mapArr.length-1,sk=mapArr[0].length-1; + var si = mapArr.length - 1, sk = mapArr[0].length - 1; for (var i = 0; i <= si; i++) { var a = []; formatArrStr += ' ['; @@ -836,168 +74,75 @@ editor_file = function (editor, callback) { return formatArrStr; } - var encode = editor.util.encode64 + editor_file_proto.prototype.saveFloor = function (floorData, callback) { + //callback(err:String) + var floorId = floorData.floorId; + var filename = 'project/floors/' + floorId + '.js'; + var datastr = ['main.floors.', floorId, '=\n']; - var alertWhenCompress = function(){ - if(editor.useCompress===true){ - editor.useCompress='alerted'; - setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请使用启动服务.exe->Js代码压缩工具重新压缩,或者把main.js的useCompress改成false来使用原始文件')",1000) - } + var tempJsonObj = Object.assign({}, floorData); + 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]; + }); + var tempJson = JSON.stringify(tempJsonObj, editor.game.replacerForSaving, 4); + tempMap.forEach(function (v) { + tempJson = tempJson.replace('"' + v[1] + '"', '[\n' + editor.file.formatMap(v[2], v[0] != 'map') + '\n]') + }); + datastr = datastr.concat([tempJson]); + datastr = datastr.join(''); + editor.file.alertWhenCompress(); + editor.fs.writeFile(filename, editor.util.encode64(datastr), 'base64', function (err, data) { + editor.addUsedFlags(datastr); + callback(err); + }); } - var saveSetting = function (file, actionList, callback) { - //console.log(file); - //console.log(actionList); - alertWhenCompress(); - - if (file == 'icons') { - actionList.forEach(function (value) { - eval("icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1" + value[1] + '=' + JSON.stringify(value[2])); - }); - var datastr = 'var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = \n'; - datastr += JSON.stringify(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1, null, '\t'); - fs.writeFile('project/icons.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'maps') { - actionList.forEach(function (value) { - eval("maps_90f36752_8815_4be8_b32b_d7fad1d0542e" + value[1] + '=' + JSON.stringify(value[2])); - }); - var datastr = 'var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = \n'; - //datastr+=JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e,null,4); + editor_file_proto.prototype.saveScript = function (name, varName, dataObj, callback) { + // 此处格式化以及写入 project/xxx.js 形式的文件 + editor.file.alertWhenCompress(); + if (['maps', 'enemys'].indexOf(name) === -1) { + // 全部用\t展开 + var content = JSON.stringify(dataObj, editor.game.replacerForSaving, '\t'); + } else { + // 只用\t展开第一层 var emap = {}; - var estr = JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e, function (k, v) { + var estr = JSON.stringify(dataObj, function (_k, v) { if (v.id != null) { var id_ = editor.util.guid(); - emap[id_] = JSON.stringify(v); + emap[id_] = JSON.stringify(v, editor.game.replacerForSaving); return id_; } else return v }, '\t'); for (var id_ in emap) { - estr = estr.replace('"' + id_ + '"', emap[id_]) + estr = estr.replace('"' + id_ + '"', emap[id_]); } - datastr += estr; + var content = estr; + } - fs.writeFile('project/maps.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'items') { - actionList.forEach(function (value) { - eval("items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a" + value[1] + '=' + JSON.stringify(value[2])); - }); - var datastr = 'var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = \n'; - datastr += JSON.stringify(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a, null, '\t'); - fs.writeFile('project/items.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'enemys') { - actionList.forEach(function (value) { - eval("enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80" + value[1] + '=' + JSON.stringify(value[2])); - }); - var datastr = 'var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = \n'; - var emap = {}; - var estr = JSON.stringify(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80, function (k, v) { - if (v.hp != null) { - var id_ = editor.util.guid(); - emap[id_] = JSON.stringify(v); - return id_; - } else return v - }, '\t'); - for (var id_ in emap) { - estr = estr.replace('"' + id_ + '"', emap[id_]) - } - datastr += estr; - fs.writeFile('project/enemys.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'data') { - actionList.forEach(function (value) { - eval("data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d" + value[1] + '=' + JSON.stringify(value[2])); - }); - if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId) < 0) - data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds[0]; - var datastr = 'var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = \n'; - datastr += JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, null, '\t'); - fs.writeFile('project/data.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'functions') { - actionList.forEach(function (value) { - eval("fmap[fobj" + value[1] + ']=' + JSON.stringify(value[2])); - }); - var fraw = fjson; - for (var id_ in fmap) { - fraw = fraw.replace('"' + id_ + '"', fmap[id_]) - } - var datastr = 'var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = \n'; - datastr += fraw; - fs.writeFile('project/functions.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - return; - } - if (file == 'floorloc') { - actionList.forEach(function (value) { - // 检测null/undefined - 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; - } - if (file == 'floors') { - actionList.forEach(function (value) { - eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); - }); - editor_file.saveFloorFile(callback); - return; - } - if (file == 'events') { - actionList.forEach(function (value) { - eval("events_c12a15a8_c380_4b28_8144_256cba95f760" + value[1] + '=' + JSON.stringify(value[2])); - }); - var datastr = 'var events_c12a15a8_c380_4b28_8144_256cba95f760 = \n'; - datastr += JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760, null, '\t'); - fs.writeFile('project/events.js', encode(datastr), 'base64', function (err, data) { - callback(err); - }); - 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('出错了,要设置的文件名不识别'); + var strToWrite = `var ${varName} = \n${content}`; + editor.fs.writeFile(`project/${name}.js`, editor.util.encode64(strToWrite), 'base64', function (err, data) { + callback(err); + }); } - return editor_file; -} -//editor_file = editor_file(editor); \ No newline at end of file + editor_file_proto.prototype.saveCommentJs = function () { + // 无需格式化的写入, 把multi的那部分略微修改 + } + + editor_file_proto.prototype.saveImage = function () { + // 给追加素材使用 + } + + editor_file_proto.prototype.addMark = function (name) { + // 把name对应的文件在editor.file.fileMark添加标记 + } + + editor_file_proto.prototype.save = function (callback) { + // 根据 editor.file.fileMark 把游戏对象格式化写入文件 + } + + +} \ No newline at end of file diff --git a/_server/editor_file_unsorted.js b/_server/editor_file_unsorted.js new file mode 100644 index 00000000..7ee709c8 --- /dev/null +++ b/_server/editor_file_unsorted.js @@ -0,0 +1,904 @@ +editor_file = function (editor, callback) { + + var editor_file = new editor_file_proto(); + editor.file=editor_file; + + editor.file.loadCommentjs(callback); + + editor.file.saveFloorFile = function (callback) { + //callback(err:String) + checkCallback(callback); + /* if (!isset(editor.currentFloorId) || !isset(editor.currentFloorData)) { + callback('未选中文件或无数据'); + } */ + var filename = 'project/floors/' + editor.currentFloorId + '.js'; + var datastr = ['main.floors.', editor.currentFloorId, '=\n']; + + if (core.floorIds.indexOf(editor.currentFloorId) >= 0) { + for(var ii=0,name;name=['map','bgmap','fgmap'][ii];ii++){ + var mapArray=editor[name].map(function (v) { + return v.map(function (v) { + return v.idnum || v || 0 + }) + }); + editor.currentFloorData[name]=mapArray; + } + } + editor.file.saveFloor(editor.currentFloorData, callback) + } + + /////////////////////////////////////////////////////////////////////////// + + editor.file.saveNewFile = function (saveFilename, callback) { + //saveAsFilename不含'/'不含'.js' + checkCallback(callback); + var currData=editor.currentFloorData; + var saveStatus = document.getElementById('newMapStatus').checked; + + var title = saveStatus?currData.title:"新建楼层"; + var name = saveStatus?currData.name:"0"; + if (/^mt\d+$/i.test(saveFilename)) { + name = saveFilename.substring(2); + title = "主塔 "+name+" 层"; + } + + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + var row = [], map = []; + for (var i=0;i0) + saveSetting('icons', iconActions, tempcallback); + else tempcallback(null); + + saveSetting('maps', mapActions, tempcallback); + + if (image=='items') + saveSetting('items', templateActions, tempcallback); + else if (image.indexOf('enemy')==0) + saveSetting('enemys', templateActions, tempcallback); + else tempcallback(null); + } + + editor.file.registerAutotile = function (filename, callback) { + var idnum = 140; + while (editor.core.maps.blocksInfo[idnum]) idnum++; + + var iconActions = []; + var mapActions = []; + + iconActions.push(["add", "['autotile']['" + filename + "']", 0]); + mapActions.push(["add", "['" + idnum + "']", {'cls': 'autotile', 'id': filename, 'noPass': true}]); + + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + + saveSetting('icons', iconActions, tempcallback); + saveSetting('maps', mapActions, tempcallback); + } + + editor.file.changeIdAndIdnum = function (id, idnum, info, callback) { + checkCallback(callback); + + var changeOrNew=core.isset(editor_mode.info.id)?'change':'new' + if(changeOrNew=='new'){ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (ii == idnum) { + callback('idnum重复了'); + return; + } + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + saveSetting('maps', [["add", "['" + idnum + "']", {'cls': info.images, 'id': id}]], tempcallback); + saveSetting('icons', [["add", "['" + info.images + "']['" + id + "']", info.y]], tempcallback); + if (info.images === 'items') { + saveSetting('items', [["add", "['items']['" + id + "']", editor.file.comment._data.items_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + if (info.images === 'enemys' || info.images === 'enemy48') { + saveSetting('enemys', [["add", "['" + id + "']", editor.file.comment._data.enemys_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + + callback(null); + + }else{ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + idnum = info.idnum; + + maps_90f36752_8815_4be8_b32b_d7fad1d0542e[idnum].id = id; + + var arr=[icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1,items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a,{enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80:enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80}] + arr.forEach(function (obj) { + for(var jj in obj){ + var ii=obj[jj] + if (ii.hasOwnProperty(info.id)){ + ii[id]=ii[info.id]; + delete(ii[info.id]); + } + } + }); + + editor.file.save_icons_maps_items_enemys(callback) + + } + } + //callback(err:String) + editor.file.editItem = function (id, actionList, callback) { + /*actionList:[ + ["change","['items']['name']","红宝石的新名字"], + ["add","['items']['新的和name同级的属性']",123], + ["change","['itemEffectTip']","',攻击力+'+editor.core.values.redJewel"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + var tempindex = value[1].indexOf(']') + 1; + value[1] = [value[1].slice(0, tempindex), "['" + id + "']", value[1].slice(tempindex)].join(''); + }); + saveSetting('items', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj_ = {}; + Object.keys(editor.file.comment._data.items._data).forEach(function (v) { + if (isset(editor.core.items[v][id]) && v !== 'items') + locObj_[v] = editor.core.items[v][id]; + else + locObj_[v] = null; + }); + locObj_['items'] = (function () { + var locObj = Object.assign({}, editor.core.items.items[id]); + Object.keys(editor.file.comment._data.items._data.items._data).forEach(function (v) { + if (!isset(editor.core.items.items[id][v])) + locObj[v] = null; + }); + return locObj; + })(); + return locObj_; + })(), + editor.file.comment._data.items, + null]); + } + //只有items.cls是items的才有itemEffect和itemEffectTip,keys和constants和tools只有items + } + //callback([obj,commentObj,err:String]) + editor.file.editEnemy = function (id, actionList, callback) { + /*actionList:[ + ["change","['name']","初级巫师的新名字"], + ["add","['新的和name同级的属性']",123], + ["change","['bomb']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['" + id + "']" + value[1]; + }); + saveSetting('enemys', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.core.enemys.enemys[id]); + Object.keys(editor.file.comment._data.enemys._data).forEach(function (v) { + if (!isset(editor.core.enemys.enemys[id][v])) + /* locObj[v]=editor.core.enemys.enemys[id][v]; + else */ + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.enemys, + null]); + } + } + //callback([obj,commentObj,err:String]) + + editor.file.editMapBlocksInfo = function (idnum, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + var tempmap=[]; + for(var ii=0;ii=editor.core.icons.tilesetStartOffset && !isset(editor.core.maps.blocksInfo[idnum]) && tempmap.indexOf(idnum)===-1){ + actionList.splice(ii,0,["add","['" + idnum + "']",{"cls": "tileset", "id": "X"+idnum, "noPass": true}]); + tempmap.push(idnum); + ii++; + } + value[1] = "['" + idnum + "']" + value[1]; + } + saveSetting('maps', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var sourceobj=editor.core.maps.blocksInfo[idnum]; + if(!isset(sourceobj) && idnum>=editor.core.icons.tilesetStartOffset)sourceobj={"cls": "tileset", "id": "X"+idnum, "noPass": true} + var locObj = Object.assign({}, sourceobj); + Object.keys(editor.file.comment._data.maps._data).forEach(function (v) { + if (!isset(sourceobj[v])) + locObj[v] = null; + }); + locObj.idnum = idnum; + return locObj; + })(), + editor.file.comment._data.maps, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editLoc = function (x, y, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + if(/\['autoEvent'\]\['\d+'\]$/.test(value[1]))value[1]=value[1].replace(/\['\d+'\]$/,function(v){return "['" + x + "," + y + "']"+v}) + else value[1] = value[1] + "['" + x + "," + y + "']"; + }); + saveSetting('floorloc', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = {}; + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + if (isset(editor.currentFloorData[v][x + ',' + y])) + locObj[v] = editor.currentFloorData[v][x + ',' + y]; + else + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.floors._data.loc, + null]); + } + + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editFloor = function (actionList, callback) { + /*actionList:[ + ["change","['title']",'样板 3 层'], + ["change","['color']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('floors', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.currentFloorData); + Object.keys(editor.file.comment._data.floors._data.floor._data).forEach(function (v) { + if (!isset(editor.currentFloorData[v])) + /* locObj[v]=editor.currentFloorData[v]; + else */ + locObj[v] = null; + }); + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + delete(locObj[v]); + }); + delete(locObj.map); + delete(locObj.bgmap); + delete(locObj.fgmap); + return locObj; + })(), + editor.file.comment._data.floors._data.floor, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editTower = function (actionList, callback) { + /*actionList:[ + ["change","['firstData']['version']",'Ver 1.0.1 (Beta)'], + ["change","['values']['lavaDamage']",200], + ] + 为[]时只查询不修改 + */ + var data_obj = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('data', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + //var locObj=Object.assign({'main':{}},editor.core.data); + var locObj = Object.assign({}, data_obj, {'main': {}}); + Object.keys(editor.file.dataComment._data.main._data).forEach(function (v) { + if (isset(editor.main[v])) + locObj.main[v] = data_obj.main[v]; + else + locObj.main[v] = null; + }); + return locObj; + })(), + editor.file.dataComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var fmap = {}; + var fjson = JSON.stringify(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + fmap[id_] = v.toString(); + return id_; + } else return v + }, 4); + var fobj = JSON.parse(fjson); + editor.file.functionsMap = fmap; + editor.file.functionsJSON = fjson; + var buildlocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildlocobj(locObj[key]); + else locObj[key] = fmap[locObj[key]]; + } + }; + + editor.file.editFunctions = function (actionList, callback) { + /*actionList:[ + ["change","['events']['afterChangeLight']","function(x,y){console.log(x,y)}"], + ["change","['ui']['drawAbout']","function(){...}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('functions', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(fjson); + buildlocobj(locObj); + return locObj; + })(), + editor.file.functionsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editCommonEvent = function (actionList, callback) { + /*actionList:[ + ["change","['test']",['123']], + ] + 为[]时只查询不修改 + */ + var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['commonEvent']" + value[1]; + }); + saveSetting('events', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + Object.assign({},data_obj), + editor.file.eventsComment._data.commonEvent, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + 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)}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('plugins', actionList, function (err) { + callback([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; + } + return true + } + + var checkCallback=function(callback){ + if (!isset(callback)) { + printe('未设置callback'); + throw('未设置callback') + } + } + + var encode = editor.util.encode64; + + var alertWhenCompress = function(){ + if(editor.useCompress===true){ + editor.useCompress='alerted'; + setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请使用启动服务.exe->Js代码压缩工具重新压缩,或者把main.js的useCompress改成false来使用原始文件')",1000) + } + } + + editor.file.save_icons_maps_items_enemys=function(callback){ + var check=[] + saveSetting('icons',[],function(err){ + if(err){callback(err);return;} + check.push('icons') + if(check.length==4)callback(null); + }) + saveSetting('maps',[],function(err){ + if(err){callback(err);return;} + check.push('maps') + if(check.length==4)callback(null); + }) + saveSetting('items',[],function(err){ + if(err){callback(err);return;} + check.push('items') + if(check.length==4)callback(null); + }) + saveSetting('enemys',[],function(err){ + if(err){callback(err);return;} + check.push('enemys') + if(check.length==4)callback(null); + }) + } + + var saveSetting = function (file, actionList, callback) { + //console.log(file); + //console.log(actionList); + editor.file.alertWhenCompress(); + + if (file == 'icons') { + actionList.forEach(function (value) { + eval("icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1" + value[1] + '=' + JSON.stringify(value[2])); + }); + var datastr = 'var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = \n'; + datastr += JSON.stringify(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1, null, '\t'); + fs.writeFile('project/icons.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'maps') { + actionList.forEach(function (value) { + eval("maps_90f36752_8815_4be8_b32b_d7fad1d0542e" + value[1] + '=' + JSON.stringify(value[2])); + }); + var datastr = 'var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = \n'; + //datastr+=JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e,null,4); + + var emap = {}; + var estr = JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e, function (k, v) { + if (v.id != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + + fs.writeFile('project/maps.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'items') { + actionList.forEach(function (value) { + eval("items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a" + value[1] + '=' + JSON.stringify(value[2])); + }); + var datastr = 'var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = \n'; + datastr += JSON.stringify(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a, null, '\t'); + fs.writeFile('project/items.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'enemys') { + actionList.forEach(function (value) { + eval("enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80" + value[1] + '=' + JSON.stringify(value[2])); + }); + var datastr = 'var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = \n'; + var emap = {}; + var estr = JSON.stringify(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80, function (k, v) { + if (v.hp != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + fs.writeFile('project/enemys.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'data') { + actionList.forEach(function (value) { + eval("data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d" + value[1] + '=' + JSON.stringify(value[2])); + }); + if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId) < 0) + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds[0]; + var datastr = 'var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = \n'; + datastr += JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, null, '\t'); + fs.writeFile('project/data.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'functions') { + actionList.forEach(function (value) { + eval("fmap[fobj" + value[1] + ']=' + JSON.stringify(value[2])); + }); + var fraw = fjson; + for (var id_ in fmap) { + fraw = fraw.replace('"' + id_ + '"', fmap[id_]) + } + var datastr = 'var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = \n'; + datastr += fraw; + fs.writeFile('project/functions.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'floorloc') { + actionList.forEach(function (value) { + // 检测null/undefined + if(/\['autoEvent'\]\['\d+,\d+'\]\['\d+'\]$/.test(value[1])){ + var tempvalue=value[1].replace(/\['\d+'\]$/,'') + tempvalue="editor.currentFloorData" +tempvalue + tempvalue=tempvalue+'='+tempvalue+'||{}' + eval(tempvalue) + } + if (value[2]==null && value[0]!=='add') + eval("delete editor.currentFloorData" + value[1]); + else + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'floors') { + actionList.forEach(function (value) { + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'events') { + actionList.forEach(function (value) { + eval("events_c12a15a8_c380_4b28_8144_256cba95f760" + value[1] + '=' + JSON.stringify(value[2])); + }); + var datastr = 'var events_c12a15a8_c380_4b28_8144_256cba95f760 = \n'; + datastr += JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760, null, '\t'); + fs.writeFile('project/events.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + 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('出错了,要设置的文件名不识别'); + } + + editor.file.saveSetting = saveSetting; + + return editor_file; +} +//editor_file = editor_file(editor); \ No newline at end of file diff --git a/_server/editor_game.js b/_server/editor_game.js index efd447bf..4690f17c 100644 --- a/_server/editor_game.js +++ b/_server/editor_game.js @@ -1,37 +1,83 @@ editor_game_wrapper = function (editor, main, core) { + // 原则上重构后只有此文件允许`\s(main|core)`形式的调用, 以及其初始化 editor_game_wrapper(editor, main, core) editor_game = function () { + this.replacerRecord = {} + } + + //////////////////// 修改数据相关 //////////////////// + // 三个 replacer 和 replacerRecord 暂时放在此处 + + editor_game.prototype.replacerForLoading = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (value instanceof Function) { + var guid_ = editor.util.guid() + rmap[guid_] = value.toString() + return guid_ + } else if (value === null) { + // 为了包含plugins的新建 + var guid_ = editor.util.guid() + rmap[guid_] = null + return guid_ + } + return value + } + + editor_game.prototype.replacerForSaving = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (rmap.hasOwnProperty(value)) { + return rmap[value] + } + return value + } + + editor_game.prototype.getValue = function (field) { + var rmap = editor.game.replacerRecord; + var value = eval(field) + if (rmap.hasOwnProperty(oldval)) { + return rmap[value] + } else { + return value + } + } + + editor_game.prototype.setValue = function (field, value) { + var rmap = editor.game.replacerRecord; + var oldval = eval(field) + if (rmap.hasOwnProperty(oldval)) { + rmap[value] = eval(value) + } else { + eval(field + '=' + value) + } + } + + editor_game.prototype.replacerWithoutRecord = function (_key, value) { + if (value instanceof Function) { + return value.toString() + } else return value } 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 - })); + var rf = editor.game.replacerWithoutRecord + core.floors = JSON.parse(JSON.stringify(core.floors, rf)); + core.data = JSON.parse(JSON.stringify(core.data, rf)); + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, rf)); } + //////////////////// 加载游戏数据相关 //////////////////// + + // 初始化数字与地图图块的对应 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 MAX_NUM && v < core.icons.tilesetStartOffset) MAX_NUM = v; } - editor.MAX_NUM=MAX_NUM; + editor.MAX_NUM = MAX_NUM; var getInfoById = function (id) { var block = maps.initBlock(0, 0, id); if (Object.prototype.hasOwnProperty.call(block, 'event')) { @@ -46,15 +92,15 @@ editor_game_wrapper = function (editor, main, core) { var id = indexBlock.event.id; var indexId = indexBlock.id; var allCls = Object.keys(icons); - if(i==17){ - editor.ids.push({'idnum': 17, 'id': id, 'images': 'terrains'}); + if (i == 17) { + editor.ids.push({ 'idnum': 17, 'id': id, 'images': 'terrains' }); point++; editor.indexs[i].push(point); continue; } for (var j = 0; j < allCls.length; j++) { if (id in icons[allCls[j]]) { - editor.ids.push({'idnum': indexId, 'id': id, 'images': allCls[j], 'y': icons[allCls[j]][id]}); + editor.ids.push({ 'idnum': indexId, 'id': id, 'images': allCls[j], 'y': icons[allCls[j]][id] }); point++; editor.indexs[i].push(point); } @@ -62,36 +108,39 @@ editor_game_wrapper = function (editor, main, core) { } } editor.indexs[0] = [0]; - + var startOffset = core.icons.tilesetStartOffset; 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(img.width%32 || img.height%32){ - alert(imgName+'的长或宽不是32的整数倍, 请修改后刷新页面'); + var width = Math.floor(img.width / 32), height = Math.floor(img.height / 32); + if (img.width % 32 || img.height % 32) { + // alert(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); + console.warn(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); } - if(img.width*img.height > 32*32*3000){ - alert(imgName+'上的图块数量超过了3000,请修改后刷新页面'); + if (img.width * img.height > 32 * 32 * 3000) { + // alert(imgName + '上的图块数量超过了3000,请修改后刷新页面'); + console.warn(imgName + '上的图块数量超过了3000,请修改后刷新页面'); } - for (var id=startOffset; id,err:String]) + editor.util.checkCallback(callback); + /* editor.fs.readdir('project/floors',function(err, data){ + callback([data,err]); + }); */ + callback([editor.core.floorIds, null]); + } + + editor_game.prototype.doCoreFunc = function (funcname) { + return core[funcname].apply(core, Array.prototype.slice.call(arguments, 1)); + } + + editor_game.prototype.getEnemy = function (id) { + return core.material.enemys[id]; + } + + editor_game.prototype.getFirstData = function () { + return core.firstData; + } editor.constructor.prototype.game = new editor_game(); } diff --git a/_server/editor_unsorted_3.js b/_server/editor_legacy.js similarity index 90% rename from _server/editor_unsorted_3.js rename to _server/editor_legacy.js index 170238e9..35e5518d 100644 --- a/_server/editor_unsorted_3.js +++ b/_server/editor_legacy.js @@ -1,4 +1,6 @@ - +// 由于历史遗留原因, 以下变量作为全局变量使用 +// exportMap mapEditArea pout mapEditArea copyMap clearMapButton deleteMap printf printe tip selectBox +// 除对这些量的功能修改外, 误在此文件中增加新变量或函数 exportMap = document.getElementById('exportMap') exportMap.isExport=false exportMap.onclick=function(){ @@ -206,13 +208,21 @@ printf = function (str_, type) { printe = function (str_) { printf(str_, 'error') } -tip_in_showMode = [ - '涉及图片的更改需要F5刷新浏览器来生效', - '文本域可以通过双击,在文本编辑器或事件编辑器中编辑', - '事件编辑器中的显示文本和自定义脚本的方块也可以双击', - "画出的地图要点击\"保存地图\"才会写入到文件中", -]; tip=document.getElementById('tip') +tip.showHelp = function(value) { + var tips = [ + '表格的文本域可以双击进行编辑', + '双击地图可以选中素材,右键可以弹出菜单', + '双击事件编辑器的图块可以进行长文本编辑/脚本编辑/地图选点/UI绘制预览等操作', + 'ESC或点击空白处可以自动保存当前修改', + 'H键可以打开操作帮助哦', + 'tileset贴图模式可以在地图上拖动来一次绘制一个区域;右键额外素材也可以绑定宽高', + '可以拖动地图上的图块和事件,或按Ctrl+C, Ctrl+X和Ctrl+V进行复制,剪切和粘贴,Delete删除', + 'Alt+数字键保存图块,数字键读取保存的图块', + ]; + if (value == null) value = Math.floor(Math.random() * tips.length); + printf('tips: ' + tips[value]) +} tip._infos= {} tip.infos=function(value){ if(value!=null){ @@ -354,7 +364,6 @@ tip.whichShow=function(value){ return tip._whichShow } selectBox=document.getElementById('selectBox') -dataSelection=document.getElementById('dataSelection') selectBox._isSelected=false selectBox.isSelected=function(value){ if(value!=null){ @@ -362,8 +371,9 @@ selectBox.isSelected=function(value){ tip.isSelectedBlock(value); tip.whichShow(0); clearTimeout(tip.timer); - dataSelection.style.display=value?'':'none' + editor.dom.dataSelection.style.display=value?'':'none' } return selectBox._isSelected } +// 修改此文件前先看文件开头的说明 \ No newline at end of file diff --git a/_server/editor_listen.js b/_server/editor_listen.js new file mode 100644 index 00000000..31825aa5 --- /dev/null +++ b/_server/editor_listen.js @@ -0,0 +1,164 @@ +editor_listen_wrapper = function (editor) { + + editor.constructor.prototype.listen = function () { + + editor.dom.body.onmousedown = editor.uifunctions.body_click; + + editor.dom.eui.oncontextmenu = function (e) { e.preventDefault() } // 自定义了右键菜单, 阻止默认行为 + editor.dom.midMenu.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.eui.ondblclick = editor.uifunctions.map_doubleClick + + editor.dom.eui.onmousedown = editor.uifunctions.map_ondown + editor.dom.eui.onmousemove = editor.uifunctions.map_onmove + editor.dom.eui.onmouseup = editor.uifunctions.map_onup + + editor.dom.mid.onmousewheel = editor.uifunctions.map_mousewheel + + editor.uivalues.shortcut = editor.config.get('shortcut', { 48: 0, 49: 0, 50: 0, 51: 0, 52: 0, 53: 0, 54: 0, 55: 0, 56: 0, 57: 0 }); + editor.dom.body.onkeydown = editor.uifunctions.body_shortcut + + editor.uivalues.scrollBarHeight = editor.uifunctions.getScrollBarHeight(); + editor.dom.iconExpandBtn.style.display = 'block'; + editor.dom.iconExpandBtn.innerText = editor.uivalues.folded ? "展开素材区" : "折叠素材区"; + editor.dom.iconExpandBtn.onclick = editor.uifunctions.fold_material_click + + editor.dom.iconLib.onmousedown = editor.uifunctions.material_ondown + editor.dom.iconLib.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.extraEvent.onmousedown = editor.uifunctions.extraEvent_click + editor.dom.chooseThis.onmousedown = editor.uifunctions.chooseThis_click + editor.dom.chooseInRight.onmousedown = editor.uifunctions.chooseInRight_click + editor.dom.copyLoc.onmousedown = editor.uifunctions.copyLoc_click + editor.dom.moveLoc.onmousedown = editor.uifunctions.moveLoc_click + editor.dom.clearEvent.onmousedown = editor.uifunctions.clearEvent_click + editor.dom.clearLoc.onmousedown = editor.uifunctions.clearLoc_click + + editor.dom.lastUsed.onmousedown = editor.uifunctions.lastUsed_click; + editor.dom.lockMode.onchange = editor.uifunctions.lockMode_onchange; + + editor.dom.brushMod.onchange = editor.uifunctions.brushMod_onchange + if (editor.dom.brushMod2) editor.dom.brushMod2.onchange = editor.uifunctions.brushMod2_onchange; + if (editor.dom.brushMod3) editor.dom.brushMod3.onchange = editor.uifunctions.brushMod3_onchange; + + editor.dom.layerMod.onchange = editor.uifunctions.layerMod_onchange + if (editor.dom.layerMod2) editor.dom.layerMod2.onchange = editor.uifunctions.layerMod2_onchange; + if (editor.dom.layerMod3) editor.dom.layerMod3.onchange = editor.uifunctions.layerMod3_onchange; + + editor.uifunctions.viewportButtons_func() + } + + editor.constructor.prototype.mobile_listen = function () { + if (!editor.isMobile) return; + + var mobileview = document.getElementById('mobileview'); + var mid = document.getElementById('mid'); + var right = document.getElementById('right'); + // var mobileeditdata = document.getElementById('mobileeditdata'); + + + editor.showdataarea = function (callShowMode) { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = ''; + if (callShowMode) editor.mode.showMode(editor.dom.editModeSelect.value); + editor.uifunctions.hideMidMenu(); + } + mobileview.children[0].onclick = function () { + editor.showdataarea(true) + } + mobileview.children[1].onclick = function () { + mid.style = ''; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + mobileview.children[3].onclick = function () { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = ''; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + + /* + var gettrbyid = function () { + if (!editor.lastClickId) return false; + thisTr = document.getElementById(editor.lastClickId); + input = thisTr.children[2].children[0].children[0]; + field = thisTr.children[0].getAttribute('title'); + cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + return [thisTr, input, field, cobj]; + } + mobileeditdata.children[0].onclick = function () { + var info = gettrbyid() + if (!info) return; + info[1].ondblclick() + } + mobileeditdata.children[1].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[2]) + } + mobileeditdata.children[2].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[0].children[1].getAttribute('title')) + } + */ + + //===== + + document.body.ontouchstart = document.body.onmousedown; + document.body.onmousedown = null; + + editor.dom.eui.ontouchstart = editor.dom.eui.onmousedown + editor.dom.eui.onmousedown = null + editor.dom.eui.ontouchmove = editor.dom.eui.onmousemove + editor.dom.eui.onmousemove = null + editor.dom.eui.ontouchend = editor.dom.eui.onmouseup + editor.dom.eui.onmouseup = null + + + editor.dom.chooseThis.ontouchstart = editor.dom.chooseThis.onmousedown + editor.dom.chooseThis.onmousedown = null + editor.dom.chooseInRight.ontouchstart = editor.dom.chooseInRight.onmousedown + editor.dom.chooseInRight.onmousedown = null + editor.dom.copyLoc.ontouchstart = editor.dom.copyLoc.onmousedown + editor.dom.copyLoc.onmousedown = null + editor.dom.moveLoc.ontouchstart = editor.dom.moveLoc.onmousedown + editor.dom.moveLoc.onmousedown = null + editor.dom.clearLoc.ontouchstart = editor.dom.clearLoc.onmousedown + editor.dom.clearLoc.onmousedown = null + } + + editor.constructor.prototype.mode_listen = function (callback) { + + // 这里的函数还没有写jsdoc, 通过_func()的方式先完成分类 + + editor.uifunctions.newIdIdnum_func() + editor.uifunctions.changeId_func() + editor.uifunctions.copyPasteEnemyItem_func(); + + editor.uifunctions.selectFloor_func() + editor.uifunctions.saveFloor_func() + + editor.uifunctions.newMap_func() + + editor.uifunctions.createNewMaps_func() + + editor.uifunctions.changeFloorId_func() + + editor.uifunctions.fixCtx_func() + + editor.uifunctions.selectAppend_func() + editor.uifunctions.selectFileBtn_func() + editor.uifunctions.changeColorInput_func() + editor.uifunctions.picClick_func() + editor.uifunctions.appendConfirm_func() + + editor.dom.editModeSelect.onchange = editor.mode.editModeSelect_onchange + + if (Boolean(callback)) callback(); + } + +} \ No newline at end of file diff --git a/_server/editor_mappanel.js b/_server/editor_mappanel.js new file mode 100644 index 00000000..9068eff9 --- /dev/null +++ b/_server/editor_mappanel.js @@ -0,0 +1,898 @@ +editor_mappanel_wrapper = function (editor) { + + // 暂时先 注释+分类 内部函数未完成重构 + + /** + * 在绘图区格子内画一个随机色块 + */ + editor.uifunctions.fillPos = function (pos) { + editor.dom.euiCtx.fillStyle = '#' + ~~(Math.random() * 8) + ~~(Math.random() * 8) + ~~(Math.random() * 8); + editor.dom.euiCtx.fillRect(pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8); + } + + /** + * 从鼠标点击返回可用的组件内坐标 + */ + editor.uifunctions.eToLoc = function (e) { + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var xx = e.clientX, yy = e.clientY + if (editor.isMobile) { xx = e.touches[0].clientX, yy = e.touches[0].clientY } + editor.loc = { + 'x': scrollLeft + xx - editor.dom.mid.offsetLeft - editor.dom.mapEdit.offsetLeft, + 'y': scrollTop + yy - editor.dom.mid.offsetTop - editor.dom.mapEdit.offsetTop, + 'size': editor.isMobile ? (32 * innerWidth * 0.96 / core.__PIXELS__) : 32 + }; + return editor.loc; + } + + /** + * 组件内坐标转地图位置 + * @param {Boolean} addViewportOffset 是否加上大地图的偏置 + */ + editor.uifunctions.locToPos = function (loc, addViewportOffset) { + var offsetX = 0, offsetY = 0; + if (addViewportOffset) { + offsetX = core.bigmap.offsetX / 32; + offsetY = core.bigmap.offsetY / 32; + } + editor.pos = { 'x': ~~(loc.x / loc.size) + offsetX, 'y': ~~(loc.y / loc.size) + offsetY } + return editor.pos; + } + + /** + * editor.dom.eui.ondblclick + * 双击地图可以选中素材 + */ + editor.uifunctions.map_doubleClick = function (e) { + if (editor.uivalues.bindSpecialDoor.loc != null) return; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x]); + return; + } + + /** + * 用于鼠标移出map后清除状态 + */ + editor.uifunctions.clearMapStepStatus = function () { + if (editor.uivalues.mouseOutCheck > 1) { + editor.uivalues.mouseOutCheck--; + setTimeout(editor.uifunctions.clearMapStepStatus, 1000); + return; + } + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + } + + /** + * editor.dom.eui.onmousedown + * + 绑定机关门事件的选择怪物 + * + 右键进入菜单 + * + 非绘图时选中 + * + 绘图时画个矩形在那个位置 + */ + editor.uifunctions.map_ondown = function (e) { + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + if (editor.uivalues.bindSpecialDoor.loc != null) { + var x = editor.pos.x, y = editor.pos.y, id = (editor.map[y][x] || {}).id; + // 检测是否是怪物 + if (id && editor.game.getEnemy(id)) { + var locstr = x + "," + y, index = editor.uivalues.bindSpecialDoor.enemys.indexOf(locstr); + if (index >= 0) editor.uivalues.bindSpecialDoor.enemys.splice(index, 1); + else editor.uivalues.bindSpecialDoor.enemys.push(locstr); + editor.drawEventBlock(); + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(); + } + return false; + } + if (e.button == 2) { + editor.uifunctions.showMidMenu(e.clientX, e.clientY); + return false; + } + if (!selectBox.isSelected()) { + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + //tip.whichShow(1); + tip.showHelp(6); + editor.uivalues.startPos = pos; + editor.dom.euiCtx.strokeStyle = '#FF0000'; + editor.dom.euiCtx.lineWidth = 3; + if (editor.isMobile) editor.uifunctions.showMidMenu(e.clientX, e.clientY); + return false; + } + + editor.uivalues.holdingPath = 1; + editor.uivalues.mouseOutCheck = 2; + setTimeout(editor.uifunctions.clearMapStepStatus); + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.stepPostfix = []; + editor.uivalues.stepPostfix.push(pos); + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + return false; + } + + /** + * editor.dom.eui.onmousemove + * + 非绘图模式时维护起止位置并画箭头 + * + 绘图模式时找到与队列尾相邻的鼠标方向的点画个矩形 + */ + editor.uifunctions.map_onmove = function (e) { + if (!selectBox.isSelected()) { + if (editor.uivalues.startPos == null) return; + //tip.whichShow(1); + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + if (editor.uivalues.endPos != null && editor.uivalues.endPos.x == pos.x && editor.uivalues.endPos.y == pos.y) return; + if (editor.uivalues.endPos != null) { + editor.dom.euiCtx.clearRect(Math.min(32 * editor.uivalues.startPos.x - core.bigmap.offsetX, 32 * editor.uivalues.endPos.x - core.bigmap.offsetX), + Math.min(32 * editor.uivalues.startPos.y - core.bigmap.offsetY, 32 * editor.uivalues.endPos.y - core.bigmap.offsetY), + (Math.abs(editor.uivalues.startPos.x - editor.uivalues.endPos.x) + 1) * 32, (Math.abs(editor.uivalues.startPos.y - editor.uivalues.endPos.y) + 1) * 32) + } + editor.uivalues.endPos = pos; + if (editor.uivalues.startPos != null) { + if (editor.uivalues.startPos.x != editor.uivalues.endPos.x || editor.uivalues.startPos.y != editor.uivalues.endPos.y) { + core.drawArrow('eui', + 32 * editor.uivalues.startPos.x + 16 - core.bigmap.offsetX, 32 * editor.uivalues.startPos.y + 16 - core.bigmap.offsetY, + 32 * editor.uivalues.endPos.x + 16 - core.bigmap.offsetX, 32 * editor.uivalues.endPos.y + 16 - core.bigmap.offsetY); + } + } + // editor_mode.onmode('nextChange'); + // editor_mode.onmode('loc'); + //editor_mode.loc(); + //tip.whichShow(1); + // tip.showHelp(6); + return false; + } + + if (editor.uivalues.holdingPath == 0) { + return false; + } + editor.uivalues.mouseOutCheck = 2; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + var pos0 = editor.uivalues.stepPostfix[editor.uivalues.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 i = 0; i < 4; i++) { + if (directionDistance[i] > max) { + index = i; + max = directionDistance[i]; + } + } + var 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; + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + else { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = pos.x; + var y1 = pos.y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + // draw rect + editor.dom.euiCtx.clearRect(0, 0, editor.dom.euiCtx.canvas.width, editor.dom.euiCtx.canvas.height); + editor.dom.euiCtx.fillStyle = 'rgba(0, 127, 255, 0.4)'; + editor.dom.euiCtx.fillRect(32 * x0 - core.bigmap.offsetX, 32 * y0 - core.bigmap.offsetY, + 32 * (x1 - x0) + 32, 32 * (y1 - y0) + 32); + } + editor.uivalues.stepPostfix.push(pos); + } + return false; + } + + /** + * editor.dom.eui.onmouseup + * + 非绘图模式时, 交换首末点的内容 + * + 绘图模式时, 根据画线/画矩形/画tileset 做对应的绘制 + */ + editor.uifunctions.map_onup = function (e) { + if (!selectBox.isSelected()) { + //tip.whichShow(1); + // editor.movePos(editor.uivalues.startPos, editor.uivalues.endPos); + if (editor.layerMod == 'map') + editor.exchangePos(editor.uivalues.startPos, editor.uivalues.endPos); + else + editor.exchangeBgFg(editor.uivalues.startPos, editor.uivalues.endPos, editor.layerMod); + editor.uivalues.startPos = editor.uivalues.endPos = null; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + return false; + } + editor.uivalues.holdingPath = 0; + if (editor.uivalues.stepPostfix && editor.uivalues.stepPostfix.length) { + editor.savePreMap(); + if (editor.brushMod !== 'line') { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].x; + var y1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj <= y1; jj++) { + for (var ii = x0; ii <= x1; ii++) { + editor.uivalues.stepPostfix.push({ x: ii, y: jj }) + } + } + } + var useBrushMode = editor.brushMod == 'tileset'; + if (editor.uivalues.stepPostfix.length == 1 && (editor.uivalues.tileSize[0] > 1 || editor.uivalues.tileSize[1] > 1)) { + useBrushMode = true; + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj < y0 + editor.uivalues.tileSize[1]; ++jj) { + for (var ii = x0; ii < x0 + editor.uivalues.tileSize[0]; ++ii) { + if (jj >= editor[editor.layerMod].length || ii >= editor[editor.layerMod][0].length) continue; + editor.uivalues.stepPostfix.push({ x: ii, y: jj }); + } + } + } + if (useBrushMode && core.tilesets.indexOf(editor.info.images) !== -1) { + var imgWidth = ~~(core.material.images.tilesets[editor.info.images].width / 32); + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var idnum = editor.info.idnum; + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) { + if (editor.uivalues.stepPostfix[ii].y != y0) { + y0++; + idnum += imgWidth; + } + editor[editor.layerMod][editor.uivalues.stepPostfix[ii].y][editor.uivalues.stepPostfix[ii].x] = editor.ids[editor.indexs[idnum + editor.uivalues.stepPostfix[ii].x - x0]]; + } + } else { + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) + editor[editor.layerMod][editor.uivalues.stepPostfix[ii].y][editor.uivalues.stepPostfix[ii].x] = editor.info; + } + // console.log(editor.map); + if (editor.info.y != null) { + editor.uivalues.lastUsed = [editor.info].concat(editor.uivalues.lastUsed.filter(function (e) { return e.id != editor.info.id})); + editor.config.set("lastUsed", editor.uivalues.lastUsed); + } + editor.updateMap(); + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + } + return false; + } + + /** + * editor.dom.mid.onmousewheel + * 在地图编辑区域滚轮切换楼层 + */ + editor.uifunctions.map_mousewheel = function (e) { + var wheel = function (direct) { + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var toId = editor.currentFloorId; + + if (direct > 0 && index < editor.core.floorIds.length - 1) + toId = editor.core.floorIds[index + 1]; + else if (direct < 0 && index > 0) + toId = editor.core.floorIds[index - 1]; + else return; + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = toId; + editor.changeFloor(toId); + } + + try { + if (e.wheelDelta) + wheel(Math.sign(e.wheelDelta)) + else if (e.detail) + wheel(Math.sign(e.detail)); + } + catch (ee) { + console.log(ee); + } + return false; + } + + /** + * 显示右键菜单 + */ + editor.uifunctions.showMidMenu = function (x, y) { + editor.uivalues.lastRightButtonPos = JSON.parse(JSON.stringify( + [editor.pos, editor.uivalues.lastRightButtonPos[0]] + )); + // --- copy + editor.uivalues.lastCopyedInfo = [editor.copyFromPos(), editor.uivalues.lastCopyedInfo[0]]; + var locStr = '(' + editor.uivalues.lastRightButtonPos[1].x + ',' + editor.uivalues.lastRightButtonPos[1].y + ')'; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + + // 检测是否是上下楼 + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + var extraEvent = editor.dom.extraEvent, parent = extraEvent.parentElement; + if (thisevent == 0) { + parent.removeChild(extraEvent); + parent.appendChild(extraEvent); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定出生点为此点'; + } else if (thisevent.id == 'upFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定上楼事件'; + } + else if (thisevent.id == 'downFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定下楼事件'; + } + else if (['leftPortal', 'rightPortal', 'downPortal', 'upPortal'].indexOf(thisevent.id) >= 0) { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定楼传事件'; + } + else if (thisevent.id == 'specialDoor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定机关门事件'; + } + else editor.dom.extraEvent.style.display = 'none'; + + editor.dom.chooseThis.children[0].innerHTML = '选中此点' + '(' + editor.pos.x + ',' + editor.pos.y + ')' + editor.dom.copyLoc.children[0].innerHTML = '复制事件' + locStr + '到此处'; + editor.dom.moveLoc.children[0].innerHTML = '交换事件' + locStr + '与此事件的位置'; + editor.dom.midMenu.style = 'top:' + (y + scrollTop) + 'px;left:' + (x + scrollLeft) + 'px;'; + } + + /** + * 隐藏右键菜单 + */ + editor.uifunctions.hideMidMenu = function () { + if (editor.isMobile) { + setTimeout(function () { + editor.dom.midMenu.style = 'display:none'; + }, 200) + } else { + editor.dom.midMenu.style = 'display:none'; + } + } + + /** + * editor.dom.extraEvent.onmousedown + * 菜单 附加点操作 + */ + editor.uifunctions.extraEvent_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + return editor.uifunctions._extraEvent_bindStartPoint(thisevent) + || editor.uifunctions._extraEvent_bindStair(thisevent) + || editor.uifunctions._extraEvent_bindSpecialDoor(thisevent); + } + + /** + * 绑定该空地点为起始点 + */ + editor.uifunctions._extraEvent_bindStartPoint = function (thisevent) { + if (thisevent != 0) return false; + editor.mode.onmode('tower'); + editor.mode.addAction(["change", "['firstData']['floorId']", editor.currentFloorId]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['x']", editor.pos.x]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['y']", editor.pos.y]); + editor.mode.onmode('save', function () { + core.firstData.floorId = editor.currentFloorId; + core.firstData.hero.loc.x = editor.pos.x; + core.firstData.hero.loc.y = editor.pos.y; + editor.drawPosSelection(); + editor.drawEventBlock(); + editor.mode.tower(); + printf('绑定初始点成功'); + }); + } + + /** + * 绑定该楼梯的楼传事件 + */ + editor.uifunctions._extraEvent_bindStair = function (thisevent) { + if (['upFloor', 'downFloor', 'leftPortal', 'rightPortal', 'upPortal', 'downPortal'].indexOf(thisevent.id) < 0) + return false; + var loc = editor.pos.x + "," + editor.pos.y; + if (thisevent.id == 'upFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": "downFloor" }; + } + else if (thisevent.id == 'downFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":before", "stair": "upFloor" }; + } + else if (thisevent.id == 'leftPortal' || thisevent.id == 'rightPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_x" } + } + else if (thisevent.id == 'upPortal' || thisevent.id == 'downPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_y" } + } + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawPosSelection(); + editor.drawEventBlock(); + editor_mode.showMode('loc'); + printf('添加楼梯事件成功'); + }); + return true; + } + + /** + * 绑定该机关门的事件 + */ + editor.uifunctions._extraEvent_bindSpecialDoor = function (thisevent) { + if (thisevent.id != 'specialDoor') return false; + var number = parseInt(prompt("请输入该机关门的怪物数量", "0"))|| 0; + if (number <= 0) return true; + editor.uivalues.bindSpecialDoor.n = number; + editor.uivalues.bindSpecialDoor.loc = editor.pos.x + ',' + editor.pos.y; + editor.uivalues.bindSpecialDoor.enemys = []; + printf("请点击选择" + number + "个怪物;切换楼层或刷新页面取消操作。"); + } + + /** + * 确定绑定该机关门的事件 + * cancel:是否取消此模式 + */ + editor.uifunctions._extraEvent_bindSpecialDoor_doAction = function (cancel) { + var bindSpecialDoor = editor.uivalues.bindSpecialDoor; + if (cancel) { + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + editor.drawEventBlock(); + printf(""); + return; + } + if (bindSpecialDoor.loc == null || bindSpecialDoor.enemys.length != bindSpecialDoor.n) return; + // 添加机关门自动事件 + var doorFlag = "flag:door_" + editor.currentFloorId + "_" + bindSpecialDoor.loc.replace(',', '_'); + editor.currentFloorData.autoEvent[bindSpecialDoor.loc] = { + '0': { + "condition": doorFlag + "==" + bindSpecialDoor.n, + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + {"type": "openDoor"} + ] + } + }; + bindSpecialDoor.enemys.forEach(function (loc) { + editor.currentFloorData.afterBattle[loc] = [ + {"type": "addValue", "name": doorFlag, "value": "1"} + ] + }); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawEventBlock(); + editor.drawPosSelection(); + editor_mode.showMode('loc'); + printf('绑定机关门事件成功'); + }); + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + } + + /** + * editor.dom.chooseThis.onmousedown + * 菜单 选中此点 + */ + editor.uifunctions.chooseThis_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + selectBox.isSelected(false); + + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + //tip.whichShow(1); + if (editor.isMobile) editor.showdataarea(false); + } + + /** + * editor.dom.chooseInRight.onmousedown + * 菜单 在素材区选中此图块 + */ + editor.uifunctions.chooseInRight_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + var thisevent = editor[editor.layerMod][editor.pos.y][editor.pos.x]; + editor.setSelectBoxFromInfo(thisevent); + } + + /** + * editor.dom.copyLoc.onmousedown + * 菜单 复制此事件 + */ + editor.uifunctions.copyLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + editor.savePreMap(); + editor_mode.onmode(''); + var now = editor.pos, last = editor.uivalues.lastRightButtonPos[1]; + if (now.x == last.x && now.y == last.y) return; + editor.pasteToPos(editor.uivalues.lastCopyedInfo[1]); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('复制事件成功'); + editor.drawPosSelection(); + }); + } + + /** + * editor.dom.moveLoc.onmousedown + * 菜单 移动此事件 + */ + editor.uifunctions.moveLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + editor.savePreMap(); + editor_mode.onmode(''); + editor.exchangePos(editor.pos, editor.uivalues.lastRightButtonPos[1]); + } + + /** + * editor.dom.clearEvent.onmousedown + * 菜单 仅清空此点事件 + */ + editor.uifunctions.clearEvent_click = function (e) { + e.stopPropagation(); + editor.clearPos(false); + } + + /** + * editor.dom.clearLoc.onmousedown + * 菜单 清空此点及事件 + */ + editor.uifunctions.clearLoc_click = function (e) { + e.stopPropagation(); + editor.clearPos(true); + } + + /** + * editor.dom.lockMode.onchange + * 点击【】 + */ + editor.uifunctions.lockMode_onchange = function () { + tip.msgs[11] = String('锁定模式开启下将不再点击空白处自动保存,请谨慎操作。'); + tip.whichShow(12); + editor.uivalues.lockMode = editor.dom.lockMode.checked; + } + + /** + * editor.dom.brushMod.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod_onchange = function () { + editor.brushMod = editor.dom.brushMod.value; + } + + /** + * editor.dom.brushMod2.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod2_onchange = function () { + editor.brushMod = editor.dom.brushMod2.value; + } + + /** + * editor.dom.brushMod3.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod3_onchange = function () { + if (!editor.config.get('alertTileMode') && + !confirm("从V2.6.6开始,tileset贴图模式已被废弃。\n请右键额外素材,并输入所需要绘制的宽高,然后单击地图以绘制一个区域。\n\n点取消将不再显示此提示。")) { + editor.config.set('alertTileMode', true); + } + // tip.showHelp(5) + tip.isSelectedBlock(false) + tip.msgs[11] = String('tileset贴图模式下可以按选中tileset素材,并在地图上拖动来一次绘制一个区域'); + tip.whichShow(12); + editor.brushMod = editor.dom.brushMod3.value; + } + + /** + * editor.dom.layerMod.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod_onchange = function () { + editor.layerMod = editor.dom.layerMod.value; + [editor.dom.bgc, editor.dom.fgc, editor.dom.evc, editor.dom.ev2c].forEach(function (x) { + x.style.opacity = 1; + }); + + // 手机端.... + if (editor.isMobile) { + if (editor.dom.layerMod.value == 'bgmap') { + [editor.dom.fgc, editor.dom.evc, editor.dom.ev2c].forEach(function (x) { + x.style.opacity = 0.3; + }); + } + if (editor.dom.layerMod.value == 'fgmap') { + [editor.dom.bgc, editor.dom.evc, editor.dom.ev2c].forEach(function (x) { + x.style.opacity = 0.3; + }); + } + } + } + + /** + * editor.dom.layerMod2.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod2_onchange = function () { + editor.layerMod = editor.dom.layerMod2.value; + [editor.dom.fgc, editor.dom.evc, editor.dom.ev2c].forEach(function (x) { + x.style.opacity = 0.3; + }); + editor.dom.bgc.style.opacity = 1; + } + + /** + * editor.dom.layerMod3.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod3_onchange = function () { + editor.layerMod = editor.dom.layerMod3.value; + [editor.dom.bgc, editor.dom.evc, editor.dom.ev2c].forEach(function (x) { + x.style.opacity = 0.3; + }); + editor.dom.fgc.style.opacity = 1; + } + + /** + * 移动大地图可视窗口的绑定 + */ + editor.uifunctions.viewportButtons_func = function () { + var pressTimer = null; + for (var ii = 0, node; node = editor.dom.viewportButtons.children[ii]; ii++) { + (function (x, y) { + var move = function () { + editor.moveViewport(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + } + + editor.uifunctions.selectFloor_func = function () { + var selectFloor = document.getElementById('selectFloor'); + editor.game.getFloorFileList(function (floors) { + var outstr = []; + floors[0].forEach(function (floor) { + outstr.push(["\n'].join('')); + }); + selectFloor.innerHTML = outstr.join(''); + selectFloor.value = core.status.floorId; + selectFloor.onchange = function () { + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.changeFloor(selectFloor.value); + } + }); + } + + + editor.uifunctions.saveFloor_func = function () { + var saveFloor = document.getElementById('saveFloor'); + editor_mode.saveFloor = function () { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('保存成功'); + }); + } + saveFloor.onclick = editor_mode.saveFloor; + } + + editor.uifunctions.lastUsed_click = function (e) { + if (editor.isMobile) return; + + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var px = scrollLeft + e.clientX - editor.dom.mid2.offsetLeft - editor.dom.lastUsedDiv.offsetLeft, + py = scrollTop + e.clientY - editor.dom.mid2.offsetTop - editor.dom.lastUsedDiv.offsetTop; + var x = parseInt(px / 32), y = parseInt(py / 32); + var index = x + core.__SIZE__ * y; + if (index >= editor.uivalues.lastUsed.length) return; + editor.setSelectBoxFromInfo(editor.uivalues.lastUsed[index]); + return; + } + + + + ///////////////////////////////////////////////////////////////////////////// + + + editor.constructor.prototype.copyFromPos = function (pos) { + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var map = core.clone(editor.map[pos.y][pos.x]); + var events = {}; + fields.forEach(function(v){ + events[v] = core.clone(editor.currentFloorData[v][pos.x+','+pos.y]); + }) + return {map: map, events: events}; + } + + editor.constructor.prototype.pasteToPos = function (info, pos) { + if (info == null) return; + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + editor.map[pos.y][pos.x] = core.clone(info.map); + fields.forEach(function(v){ + if (info.events[v] == null) delete editor.currentFloorData[v][pos.x+","+pos.y]; + else editor.currentFloorData[v][pos.x+","+pos.y] = core.clone(info.events[v]); + }); + } + + editor.constructor.prototype.movePos = function (startPos, endPos, callback) { + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var copyed = editor.copyFromPos(startPos); + editor.pasteToPos({map:0, events: {}}, startPos); + editor.pasteToPos(copyed, endPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('移动事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.exchangePos = function (startPos, endPos, callback) { + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var startInfo = editor.copyFromPos(startPos); + var endInfo = editor.copyFromPos(endPos); + editor.pasteToPos(startInfo, endPos); + editor.pasteToPos(endInfo, startPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('交换事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.savePreMap = function () { + var dt = { + map: editor.map, + fgmap: editor.fgmap, + bgmap: editor.bgmap, + }; + if (editor.uivalues.preMapData.length == 0 + || !core.same(editor.uivalues.preMapData[editor.uivalues.preMapData.length - 1], dt)) { + editor.uivalues.preMapData.push(core.clone(dt)); + if (editor.uivalues.preMapData.length > editor.uivalues.preMapMax) { + editor.uivalues.preMapData.shift(); + } + } + } + + editor.constructor.prototype.moveBgFg = function (startPos, endPos, name, callback) { + if (!startPos || !endPos || ["bgmap","fgmap"].indexOf(name)<0) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + editor[name][endPos.y][endPos.x] = editor[name][startPos.y][startPos.x]; + editor[name][startPos.y][startPos.x] = 0; + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('移动图块成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.exchangeBgFg = function (startPos, endPos, name, callback) { + if (!startPos || !endPos || ["bgmap","fgmap"].indexOf(name)<0) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var value = editor[name][endPos.y][endPos.x]; + editor[name][endPos.y][endPos.x] = editor[name][startPos.y][startPos.x]; + editor[name][startPos.y][startPos.x] = value; + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('交换图块成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + + } + + editor.constructor.prototype.clearPos = function (clearPos, pos, callback) { + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + editor.uifunctions.hideMidMenu(); + editor.savePreMap(); + editor.info = 0; + editor_mode.onmode(''); + if (clearPos) + editor.map[pos.y][pos.x]=editor.info; + editor.updateMap(); + fields.forEach(function(v){ + delete editor.currentFloorData[v][pos.x+','+pos.y]; + }) + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf(clearPos?'清空该点和事件成功':'只清空该点事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/_server/editor_materialpanel.js b/_server/editor_materialpanel.js new file mode 100644 index 00000000..ca3713fb --- /dev/null +++ b/_server/editor_materialpanel.js @@ -0,0 +1,158 @@ +editor_materialpanel_wrapper = function (editor) { + + editor.uifunctions.getScrollBarHeight = function () { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps + + document.body.appendChild(outer); + + var widthNoScroll = outer.offsetWidth; + // force scrollbars + outer.style.overflow = "scroll"; + + // add innerdiv + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); + + var widthWithScroll = inner.offsetWidth; + + // remove divs + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; + } + + /** + * editor.dom.iconExpandBtn.onclick + */ + editor.uifunctions.fold_material_click = function () { + if (editor.uivalues.folded) { + if (confirm("你想要展开素材吗?\n展开模式下将显示全素材内容。")) { + editor.config.set('folded', false, function() { + window.location.reload(); + }); + } + } else { + var perCol = parseInt(prompt("请输入折叠素材模式下每列的个数:", "50")) || 0; + if (perCol > 0) { + editor.config.set('foldPerCol', perCol, false); + editor.config.set('folded', true, function() { + window.location.reload(); + }); + } + } + } + + /** + * editor.dom.iconLib.onmousedown + * 素材区的单击事件 + */ + editor.uifunctions.material_ondown = function (e) { + e.stopPropagation(); + e.preventDefault(); + if (!editor.isMobile && e.clientY >= editor.dom.iconLib.offsetHeight - editor.uivalues.scrollBarHeight) return; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + var loc = { + 'x': scrollLeft + e.clientX + editor.dom.iconLib.scrollLeft - right.offsetLeft - editor.dom.iconLib.offsetLeft, + 'y': scrollTop + e.clientY + editor.dom.iconLib.scrollTop - right.offsetTop - editor.dom.iconLib.offsetTop, + 'size': 32 + }; + editor.loc = loc; + editor.uivalues.tileSize = [1,1]; + var pos = editor.uifunctions.locToPos(loc); + for (var spriter in editor.widthsX) { + if (pos.x >= editor.widthsX[spriter][1] && pos.x < editor.widthsX[spriter][2]) { + var ysize = spriter.endsWith('48') ? 48 : 32; + loc.ysize = ysize; + pos.images = editor.widthsX[spriter][0]; + pos.y = ~~(loc.y / loc.ysize); + if (!editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) pos.x = editor.widthsX[spriter][1]; + var autotiles = core.material.images['autotile']; + if (pos.images == 'autotile') { + var imNames = Object.keys(autotiles); + if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) + pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 4; + else { + for (var i = 0; i < imNames.length; i++) { + if (pos.y >= 4 * i && pos.y < 4 * (i + 1)) { + pos.images = imNames[i]; + pos.y = 4 * i; + } + } + } + } + else { + var height = editor.widthsX[spriter][3], col = height / ysize; + if (editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) { + col = (pos.x == editor.widthsX[spriter][2] - 1) ? ((col - 1) % editor.uivalues.foldPerCol + 1) : editor.uivalues.foldPerCol; + } + if (spriter == 'terrains' && pos.x == editor.widthsX[spriter][1]) col += 2; + pos.y = Math.min(pos.y, col - 1); + } + + selectBox.isSelected(true); + // console.log(pos,core.material.images[pos.images].height) + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + + if (pos.x == 0 && pos.y == 0) { + // editor.info={idnum:0, id:'empty','images':'清除块', 'y':0}; + editor.info = 0; + } else if (pos.x == 0 && pos.y == 1) { + editor.info = editor.ids[editor.indexs[17]]; + } else { + if (autotiles[pos.images]) editor.info = { 'images': pos.images, 'y': 0 }; + else if (core.tilesets.indexOf(pos.images) != -1) editor.info = { 'images': pos.images, 'y': pos.y, 'x': pos.x - editor.widthsX[spriter][1] }; + else { + var y = pos.y; + if (editor.uivalues.folded) { + y += editor.uivalues.foldPerCol * (pos.x - editor.widthsX[spriter][1]); + } + if (pos.images == 'terrains' && pos.x == 0) y -= 2; + editor.info = { 'images': pos.images, 'y': y } + } + + for (var ii = 0; ii < editor.ids.length; ii++) { + if ((core.tilesets.indexOf(pos.images) != -1 && editor.info.images == editor.ids[ii].images + && editor.info.y == editor.ids[ii].y && editor.info.x == editor.ids[ii].x) + || (Object.prototype.hasOwnProperty.call(autotiles, pos.images) && editor.info.images == editor.ids[ii].id + && editor.info.y == editor.ids[ii].y) + || (core.tilesets.indexOf(pos.images) == -1 && editor.info.images == editor.ids[ii].images + && editor.info.y == editor.ids[ii].y) + ) { + + editor.info = editor.ids[ii]; + break; + } + } + + if (editor.info.isTile && e.button == 2) { + var v = prompt("请输入该额外素材区域绑定宽高,以逗号分隔", "1,1"); + if (v != null && /^\d+,\d+$/.test(v)) { + v = v.split(","); + var x = parseInt(v[0]), y = parseInt(v[1]); + var widthX = editor.widthsX[editor.info.images]; + if (x <= 0 || y <= 0 || editor.info.x + x > widthX[2] - widthX[1] || 32*(editor.info.y + y) > widthX[3]) { + alert("不合法的输入范围,已经越界"); + } else { + editor.uivalues.tileSize = [x, y]; + } + } + } + + } + tip.infos(JSON.parse(JSON.stringify(editor.info))); + editor_mode.onmode('nextChange'); + editor_mode.onmode('enemyitem'); + editor.updateLastUsedMap(); + //editor_mode.enemyitem(); + } + } + } + +} \ No newline at end of file diff --git a/_server/editor_mode.js b/_server/editor_mode.js index 240baee0..8e42b7e0 100644 --- a/_server/editor_mode.js +++ b/_server/editor_mode.js @@ -21,7 +21,7 @@ editor_mode = function (editor) { this.mode = ''; this.info = {}; this.appendPic = {}; - this.doubleClickMode='change'; + this.doubleClickMode = 'change'; } editor_mode.prototype.init = function (callback) { @@ -49,15 +49,16 @@ editor_mode = function (editor) { editor_mode.actionList.push(action); } - editor_mode.prototype.doActionList = function (mode, actionList) { + editor_mode.prototype.doActionList = function (mode, actionList, callback) { if (actionList.length == 0) return; printf('修改中...'); - var cb=function(objs_){ + var cb = function (objs_) { if (objs_.slice(-1)[0] != null) { printe(objs_.slice(-1)[0]); - throw(objs_.slice(-1)[0]) + throw (objs_.slice(-1)[0]) } - ;printf('修改成功'); + ; printf('修改成功' + (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name == 'template' ? '\n\n请注意:全塔属性的name尚未修改,请及时予以设置' : '')); + if (callback) callback(); } switch (mode) { case 'loc': @@ -95,9 +96,9 @@ editor_mode = function (editor) { } } - editor_mode.prototype.onmode = function (mode) { + editor_mode.prototype.onmode = function (mode, callback) { if (editor_mode.mode != mode) { - if (mode === 'save') editor_mode.doActionList(editor_mode.mode, editor_mode.actionList); + if (mode === 'save') editor_mode.doActionList(editor_mode.mode, editor_mode.actionList, callback); if (editor_mode.mode === 'nextChange' && mode) editor_mode.showMode(mode); if (mode !== 'save') editor_mode.mode = mode; editor_mode.actionList = []; @@ -109,15 +110,78 @@ editor_mode = function (editor) { editor_mode.dom[name].style = 'z-index:-1;opacity: 0;'; } editor_mode.dom[mode].style = ''; - editor_mode.doubleClickMode='change'; + editor_mode.doubleClickMode = 'change'; // clear editor.drawEventBlock(); 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())]); + editor.dom.editModeSelect.value = mode; + if (!selectBox.isSelected()) tip.showHelp(); } + editor_mode.prototype.change = function (value) { + editor_mode.onmode('nextChange'); + editor_mode.onmode(value); + if (editor.isMobile) editor.showdataarea(false); + } + + + editor_mode.prototype.checkUnique = function (thiseval) { + if (!(thiseval instanceof Array)) return false; + var map = {}; + for (var i = 0; i < thiseval.length; ++i) { + if (map[thiseval[i]]) { + alert("警告:存在重复定义!"); + return false; + } + map[thiseval[i]] = true; + } + return true; + } + + editor_mode.prototype.checkFloorIds = function (thiseval) { + if (!editor_mode.checkUnique(thiseval)) return false; + var oldvalue = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds; + fs.readdir('project/floors', function (err, data) { + if (err) { + printe(err); + throw Error(err); + } + var newfiles = thiseval.map(function (v) { return v + '.js' }); + var notExist = ''; + for (var name, ii = 0; name = newfiles[ii]; ii++) { + if (data.indexOf(name) === -1) notExist = name; + } + if (notExist) { + var discard = confirm('文件' + notExist + '不存在, 保存会导致工程无法打开, 是否放弃更改'); + if (discard) { + editor.file.editTower([['change', "['main']['floorIds']", oldvalue]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('已放弃floorIds的修改,请F5进行刷新'); + }); + } + } + }); + return true + } + + editor_mode.prototype.changeDoubleClickModeByButton = function (mode) { + ({ + delete: function () { + printf('下一次双击表格的项删除,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + }, + add: function () { + printf('下一次双击表格的项则在同级添加新项,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + } + }[mode])(); + } + + ///////////////////////////////////////////////////////////////////////////// + editor_mode.prototype.loc = function (callback) { //editor.pos={x: 0, y: 0}; if (!core.isset(editor.pos)) return; @@ -141,17 +205,19 @@ editor_mode = function (editor) { //editor.info=editor.ids[editor.indexs[201]]; if (!core.isset(editor.info)) return; - if (Object.keys(editor.info).length !== 0 && editor.info.idnum!=17) editor_mode.info = editor.info;//避免editor.info被清空导致无法获得是物品还是怪物 + if (Object.keys(editor.info).length !== 0 && editor.info.idnum != 17) editor_mode.info = editor.info;//避免editor.info被清空导致无法获得是物品还是怪物 if (!core.isset(editor_mode.info.id)) { // document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = ''; - document.getElementById('enemyItemTable').style.display = 'none'; document.getElementById('newIdIdnum').style.display = 'block'; + document.getElementById('enemyItemTable').style.display = 'none'; + document.getElementById('changeId').style.display = 'none'; return; } document.getElementById('newIdIdnum').style.display = 'none'; document.getElementById('enemyItemTable').style.display = 'block'; + document.getElementById('changeId').style.display = 'block'; var objs = []; if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { @@ -231,7 +297,7 @@ editor_mode = function (editor) { tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); } - + editor_mode.prototype.plugins = function (callback) { var objs = []; editor.file.editPlugins([], function (objs_) { @@ -245,11 +311,17 @@ editor_mode = function (editor) { if (Boolean(callback)) callback(); } -///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + + /** + * editor.dom.editModeSelect.onchange + */ + editor_mode.prototype.editModeSelect_onchange = function () { + editor_mode.change(editor.dom.editModeSelect.value); + } editor_mode.prototype.listen = function (callback) { - - // 移动至 editor_unsorted_2.js + // 移动至 editor_listen.js -> editor.constructor.prototype.mode_listen } var editor_mode = new editor_mode(); diff --git a/_server/editor_multi.js b/_server/editor_multi.js index 476536bb..b694bf51 100644 --- a/_server/editor_multi.js +++ b/_server/editor_multi.js @@ -81,6 +81,12 @@ editor_multi = function () { _format(); } + editor_multi.hasError = function () { + if (!editor_multi.lintAutocomplete) return false; + return JSHINT.errors.filter(function (e) { + return e.code.startsWith("E") + }).length > 0; + } editor_multi.import = function (id_, args) { var thisTr = document.getElementById(id_); @@ -94,6 +100,8 @@ editor_multi = function () { editor_multi.lintAutocomplete = false; if (args.lint === true) editor_multi.lintAutocomplete = true; if (field.indexOf('Effect') !== -1) editor_multi.lintAutocomplete = true; + if ((!input.value || input.value == 'null') && editor_mode.mode == 'plugins') + input.value = '"function () {\\n\\t// 在此增加新插件\\n\\t\\n}"'; if (input.value.slice(0, 1) === '"' || args.string) { editor_multi.isString = true; codeEditor.setValue(JSON.parse(input.value) || ''); @@ -113,6 +121,7 @@ editor_multi = function () { } codeEditor.setValue(tstr || ''); } + document.getElementById('showPlugins').style.display = editor_mode.mode == 'plugins' ? 'block': 'none'; editor_multi.show(); return true; } @@ -124,6 +133,11 @@ editor_multi = function () { } editor_multi.confirm = function () { + if (editor_multi.hasError()) { + alert("当前好像存在严重的语法错误,请处理后再保存。\n严重的语法错误可能会导致整个编辑器的崩溃。"); + return; + } + if (!editor_multi.id) { editor_multi.id = ''; return; @@ -173,6 +187,11 @@ editor_multi = function () { setvalue(codeEditor.getValue() || ''); } + editor_multi.showPlugins = function () { + if (editor.isMobile && !confirm("你确定要跳转到云端插件列表吗?")) return; + window.open("https://h5mota.com/plugins/", "_blank"); + } + var multiLineArgs = [null, null, null]; editor_multi.multiLineEdit = function (value, b, f, args, callback) { editor_multi.id = 'callFromBlockly'; diff --git a/_server/editor_table.js b/_server/editor_table.js index a3b742cd..c21220fd 100644 --- a/_server/editor_table.js +++ b/_server/editor_table.js @@ -12,34 +12,41 @@ editor_table_wrapper = function (editor) { values.map(function (v) { return editor.table.option(v) }).join('') - return `\n` + return /* html */`\n` } editor_table.prototype.option = function (value) { - return `\n` + return /* html */`\n` } editor_table.prototype.text = function (value) { - return `\n` + return /* html */`\n` } editor_table.prototype.checkbox = function (value) { - return `\n` + return /* html */`\n` } editor_table.prototype.textarea = function (value, indent) { - return `\n` + return /* html */`\n` + } + editor_table.prototype.editGrid = function (showComment) { + var html = ""; + if (showComment) html += "
"; + html += ""; + return html; } editor_table.prototype.title = function () { - return `\n条目注释值\n` + return /* html */`\n条目注释值操作\n` } editor_table.prototype.gap = function (field) { - return `--------${field}\n` + return /* html */`--------${field}----\n` } editor_table.prototype.tr = function (guid, field, shortField, commentHTMLescape, cobjstr, shortCommentHTMLescape, tdstr) { - return ` + return /* html */` ${shortField} ${shortCommentHTMLescape}
${tdstr}
+ ${editor.table.editGrid(commentHTMLescape != shortCommentHTMLescape)} \n` } @@ -145,6 +152,7 @@ editor_table_wrapper = function (editor) { if (key === '_data') continue; if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); } + pvobj[ii] = vobj = args.vobj; // 标记为_hide的属性不展示 if (cobj._hide) continue; if (!cobj._leaf) { @@ -193,7 +201,7 @@ editor_table_wrapper = function (editor) { // "['a']['b']" => "b" var shortField = field.split("']").slice(-2)[0].split("['").slice(-1)[0]; // 把长度超过 charlength 的字符改成 固定长度+...的形式 - shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); + // shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); // 完整的内容转义后供悬停查看 var commentHTMLescape = editor.util.HTMLescape(comment); @@ -285,6 +293,7 @@ editor_table_wrapper = function (editor) { var thiseval = null; if (input.checked != null) input.value = input.checked; try { + if (input.value == '') input.value = 'null'; thiseval = JSON.parse(input.value); } catch (ee) { printe(field + ' : ' + ee); @@ -298,6 +307,25 @@ editor_table_wrapper = function (editor) { } } + /** + * 当"显示完整注释"被按下时 + */ + editor_table.prototype.onCommentBtnClick = function (button) { + var tr = button.parentNode.parentNode; + printf(tr.children[1].getAttribute('title')); + } + + /** + * 当"编辑表格内容"被按下时 + */ + editor_table.prototype.onEditBtnClick = function (button) { + var tr = button.parentNode.parentNode; + var guid = tr.getAttribute('id'); + var cobj = JSON.parse(tr.children[1].getAttribute('cobj')); + 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 }); + } + /** * 双击表格时 * 正常编辑: 尝试用事件编辑器或多行文本编辑器打开 @@ -337,12 +365,22 @@ editor_table_wrapper = function (editor) { 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; + var mode = editor.dom.editModeSelect.value; // 1.输入id - var newid = prompt('请输入新项的ID(仅公共事件支持中文ID)'); - if (newid == null || newid.length == 0) { - return; + var newid = '2'; + if (mode == 'loc') { + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); // 从3开始是因为comment中设置了始终显示012 + newid = testid + ''; + } + } else { + newid = prompt('请输入新项的ID(仅公共事件支持中文ID)'); + if (newid == null || newid.length == 0) { + return; + } } // 检查commentEvents diff --git a/_server/editor_ui.js b/_server/editor_ui.js new file mode 100644 index 00000000..4a5e4386 --- /dev/null +++ b/_server/editor_ui.js @@ -0,0 +1,570 @@ +editor_ui_wrapper = function (editor) { + + + /** + * 根据鼠标点击, 得到从元素向上到body的所有id + */ + editor.uifunctions.getClickpath = function (e) { + //console.log(e); + var clickpath = []; + var getpath = function (e) { + var path = []; + var currentElem = e.target; + while (currentElem) { + path.push(currentElem); + currentElem = currentElem.parentElement; + } + if (path.indexOf(window) === -1 && path.indexOf(document) === -1) + path.push(document); + if (path.indexOf(window) === -1) + path.push(window); + return path; + } + getpath(e).forEach(function (node) { + if (!node.getAttribute) return; + var id_ = node.getAttribute('id'); + if (id_) { + if (['left', 'left1', 'left2', 'left3', 'left4', 'left5', 'left8', 'mobileview'].indexOf(id_) !== -1) clickpath.push('edit'); + clickpath.push(id_); + } + }); + return clickpath; + } + + /** + * editor.dom.body.onmousedown + * 检测鼠标点击, + * + 如果选中了绘图区域之外, 就保存地图 + * + 维护绘图区的菜单的隐藏 + * + 记录最后一次点击的id(主要为了数据区服务) + */ + editor.uifunctions.body_click = function (e) { + var clickpath = editor.uifunctions.getClickpath(e); + + var unselect = true; + for (var ii = 0, thisId; thisId = ['edit', 'tip', 'brushMod', 'brushMod2', 'brushMod3', 'layerMod', 'layerMod2', 'layerMod3', 'viewportButtons'][ii]; ii++) { + if (clickpath.indexOf(thisId) !== -1) { + unselect = false; + break; + } + } + if (unselect && !editor.uivalues.lockMode) { + if (clickpath.indexOf('eui') === -1 && clickpath.indexOf('lastUsed') === -1) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + }); + } + selectBox.isSelected(false); + editor.info = {}; + } + } + //editor.mode.onmode(''); + if (e.button != 2 && !editor.isMobile) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.indexOf('down') !== -1 && editor.isMobile && clickpath.indexOf('midMenu') === -1) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.length >= 2 && clickpath[0].indexOf('id_') === 0) { editor.lastClickId = clickpath[0] } + } + + /** + * editor.dom.body.onkeydown + * 绑定快捷键 + */ + editor.uifunctions.body_shortcut = function (e) { + + // UI预览 & 地图选点 + if (editor.uievent && editor.uievent.isOpen) { + e.preventDefault(); + if (e.keyCode == 27) editor.uievent.close(); + else if (e.keyCode == 13) editor.uievent.confirm(); + else if (e.keyCode == 87) editor.uievent.move(0, -1) + else if (e.keyCode == 65) editor.uievent.move(-1, 0) + else if (e.keyCode == 83) editor.uievent.move(0, 1); + else if (e.keyCode == 68) editor.uievent.move(1, 0); + return; + } + + // 监听Ctrl+S保存 + if (e.ctrlKey && e.keyCode == 83) { + e.preventDefault(); + if (editor_multi.id != "") { + editor_multi.confirm(); // 保存脚本编辑器 + } + else if (editor_blockly.id != "") { + editor_blockly.confirm(); // 保存事件编辑器 + } + else { + editor_mode.saveFloor(); + } + return; + } + + // 如果是开启事件/脚本编辑器状态,则忽略 + if (editor_multi.id != "" || editor_blockly.id != "") + return; + + // PGUP和PGDOWN切换楼层 + if (e.keyCode == 33 || e.keyCode == 34) { + e.preventDefault(); + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var nextIndex = index + (e.keyCode == 33 ? 1 : -1); + if (nextIndex >= 0 && nextIndex < editor.core.floorIds.length) { + var toId = editor.core.floorIds[nextIndex]; + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + document.getElementById('selectFloor').value = toId; + editor.changeFloor(toId); + } + return; + } + + var focusElement = document.activeElement; + if (!focusElement || focusElement.tagName.toLowerCase() == 'body' + || focusElement.id == 'selectFloor') { + + //Ctrl+z 撤销上一步undo + if (e.keyCode == 90 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.preMapData.length > 0) { + var data = editor.uivalues.preMapData.pop(); + editor.map = JSON.parse(JSON.stringify(data.map)); + editor.fgmap = JSON.parse(JSON.stringify(data.fgmap)); + editor.bgmap = JSON.parse(JSON.stringify(data.bgmap)); + editor.updateMap(); + editor.uivalues.postMapData.push(data); + printf("已撤销此操作,你可能需要重新保存地图。"); + } + return; + } + //Ctrl+y 重做一步redo + if (e.keyCode == 89 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.postMapData.length > 0) { + var data = editor.uivalues.postMapData.pop(); + editor.map = JSON.parse(JSON.stringify(data.map)); + editor.fgmap = JSON.parse(JSON.stringify(data.fgmap)); + editor.bgmap = JSON.parse(JSON.stringify(data.bgmap)); + editor.updateMap(); + editor.uivalues.preMapData.push(data); + printf("已重做此操作,你可能需要重新保存地图。"); + } + return; + } + + // Ctrl+C, Ctrl+X, Ctrl+V + if (e.ctrlKey && e.keyCode == 67 && !selectBox.isSelected()) { + e.preventDefault(); + editor.uivalues.copyedInfo = editor.copyFromPos(); + printf('该点事件已复制'); + return; + } + if (e.ctrlKey && e.keyCode == 88 && !selectBox.isSelected()) { + e.preventDefault(); + editor.uivalues.copyedInfo = editor.copyFromPos(); + editor.clearPos(true, null, function () { + printf('该点事件已剪切'); + }) + return; + } + if (e.ctrlKey && e.keyCode == 86 && !selectBox.isSelected()) { + e.preventDefault(); + if (!editor.uivalues.copyedInfo) { + printe("没有复制的事件"); + return; + } + editor.pasteToPos(editor.uivalues.copyedInfo); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('粘贴事件成功'); + editor.drawPosSelection(); + }); + return; + } + // DELETE + if (e.keyCode == 46 && !selectBox.isSelected()) { + editor.clearPos(true); + return; + } + // ESC + if (e.keyCode == 27) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + }); + } + selectBox.isSelected(false); + editor.info = {}; + return; + } + //alt + 0~9 改变快捷图块 + if (e.altKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + var infoToSave = JSON.stringify(editor.info || 0); + if (infoToSave == JSON.stringify({})) return; + editor.uivalues.shortcut[e.keyCode] = JSON.parse(infoToSave); + printf('已保存该快捷图块, 数字键 ' + (e.keyCode - 48) + ' 使用.') + editor.config.set('shortcut', editor.uivalues.shortcut); + return; + } + //ctrl + 0~9 切换到快捷图块 + if ([48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + editor.setSelectBoxFromInfo(JSON.parse(JSON.stringify(editor.uivalues.shortcut[e.keyCode] || 0))); + return; + } + switch (e.keyCode) { + // WASD + case 87: editor.moveViewport(0, -1); break; + case 65: editor.moveViewport(-1, 0); break; + case 83: editor.moveViewport(0, 1); break; + case 68: editor.moveViewport(1, 0); break; + // Z~. + case 90: editor_mode.change('map'); break; // Z + case 88: editor_mode.change('loc'); break; // X + case 67: editor_mode.change('enemyitem'); break; // C + case 86: editor_mode.change('floor'); break; // V + case 66: editor_mode.change('tower'); break; // B + case 78: editor_mode.change('functions'); break; // N + case 77: editor_mode.change('appendpic'); break; // M + case 188: editor_mode.change('commonevent'); break; // , + case 190: editor_mode.change('plugins'); break; // . + // H + case 72: editor.uifunctions.showHelp(); break; + } + return; + } + } + + editor.uifunctions.showHelp = function () { + alert( + "快捷操作帮助:\n" + + "ESC / 点击空白处:自动保存当前修改" + + "WASD / 长按箭头:平移大地图\n" + + "PgUp, PgDn / 鼠标滚轮:上下切换楼层\n" + + "Z~.(键盘的第三排):快捷切换标签\n" + + "双击地图:选中对应点的素材\n" + + "右键地图:弹出菜单栏\n" + + "Alt+0~9:保存当前使用的图块\n" + + "0~9:选中保存的图块\n" + + "Ctrl+Z / Ctrl+Y:撤销/重做上次绘制\n" + + "Ctrl+S:事件与脚本编辑器的保存并退出\n" + + "双击事件编辑器:长文本编辑/脚本编辑/地图选点/UI绘制预览" + ); + } + + + // ------ UI预览 & 地图选点相关 ------ // + + var uievent = { + elements: {}, + values: {}, + isOpen: false, + mode: "" + }; + + uievent.elements.div = document.getElementById('uieventDiv'); + uievent.elements.title = document.getElementById('uieventTitle'); + uievent.elements.yes = document.getElementById('uieventYes'); + uievent.elements.no = document.getElementById('uieventNo'); + uievent.elements.selectBackground = document.getElementById('uieventBackground'); + uievent.elements.selectPoint = document.getElementById('selectPoint'); + uievent.elements.selectFloor = document.getElementById('selectPointFloor'); + uievent.elements.selectPointBox = document.getElementById('selectPointBox'); + uievent.elements.body = document.getElementById('uieventBody'); + uievent.elements.selectPointButtons = document.getElementById('selectPointButtons'); + uievent.elements.canvas = document.getElementById('uievent'); + uievent.elements.usedFlags = document.getElementById('uieventUsedFlags'); + uievent.elements.usedFlagList = document.getElementById('uieventUsedFlagList'); + + uievent.confirm = function () { + var callback = uievent.values.callback, floorId = uievent.values.floorId, + x = uievent.values.x, y = uievent.values.y; + uievent.close(); + if (callback) { + callback(floorId, x, y); + } + } + uievent.elements.yes.onclick = uievent.confirm; + + uievent.close = function () { + uievent.isOpen = false; + uievent.elements.div.style.display = 'none'; + uievent.values = {}; + } + uievent.elements.no.onclick = uievent.close; + + uievent.elements.selectBackground.onchange = function () { + uievent.drawPreviewUI(); + } + + uievent.drawPreviewUI = function () { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + + // 绘制UI + var background = uievent.elements.selectBackground.value; + if (background == 'thumbnail') { + core.drawThumbnail(editor.currentFloorId, null, {}, 'uievent'); + } + else { + core.fillRect('uievent', 0, 0, core.__PIXELS__, core.__PIXELS__, background); + } + + if (uievent.values.list instanceof Array) { + uievent.values.list.forEach(function (data) { + var type = data.type; + if (!type || !core.ui["_uievent_" + type]) return; + core.ui["_uievent_" + type](data); + }) + } + } + + uievent.previewUI = function (list) { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'previewUI'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = 'UI绘制预览'; + uievent.elements.selectBackground.style.display = 'inline'; + uievent.elements.selectBackground.value = 'thumbnail'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.usedFlags.style.display = 'none'; + uievent.elements.usedFlagList.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + + uievent.values.list = list; + uievent.drawPreviewUI(); + } + + uievent.selectPoint = function (floorId, x, y, hideFloor, callback) { + uievent.values.hideFloor = hideFloor; + uievent.values.callback = callback; + uievent.values.size = editor.isMobile ? window.innerWidth / core.__SIZE__ : 32; + uievent.elements.selectPointBox.style.width = (uievent.values.size - 6) + "px"; + uievent.elements.selectPointBox.style.height = (uievent.values.size - 6) + "px"; + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectPoint'; + uievent.elements.selectPoint.style.display = 'block'; + uievent.elements.yes.style.display = 'inline'; + uievent.elements.selectBackground.style.display = 'none'; + uievent.elements.selectFloor.style.display = hideFloor ? 'none' : 'inline'; + uievent.elements.selectPointBox.style.display = 'block'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.usedFlags.style.display = 'none'; + uievent.elements.usedFlagList.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + + // Append children + var floors = ""; + core.floorIds.forEach(function (f) { + floors += ""; + }) + uievent.elements.selectFloor.innerHTML = floors; + + this.setPoint(floorId || editor.currentFloorId, core.calValue(x) || 0, core.calValue(y) || 0); + } + + uievent.updateSelectPoint = function (redraw) { + uievent.elements.title.innerText = '地图选点 (' + uievent.values.x + "," + uievent.values.y + ')'; + if (redraw) { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + core.drawThumbnail(uievent.values.floorId, null, null, + { + ctx: 'uievent', centerX: uievent.values.left + core.__HALF_SIZE__, + centerY: uievent.values.top + core.__HALF_SIZE__ + }); + } + uievent.elements.selectPointBox.style.left = uievent.values.size * (uievent.values.x - uievent.values.left) + "px"; + uievent.elements.selectPointBox.style.top = uievent.values.size * (uievent.values.y - uievent.values.top) + "px"; + } + + uievent.setPoint = function (floorId, x, y) { + if (core.floorIds.indexOf(floorId) == -1) floorId = editor.currentFloorId; + uievent.values.floorId = floorId; + uievent.elements.selectFloor.value = floorId; + uievent.values.x = x != null ? x : (uievent.values.x || 0); + uievent.values.y = y != null ? y : (uievent.values.y || 0); + uievent.values.width = core.floors[uievent.values.floorId].width || core.__SIZE__; + uievent.values.height = core.floors[uievent.values.floorId].height || core.__SIZE__; + uievent.values.left = core.clamp(uievent.values.x - core.__HALF_SIZE__, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.y - core.__HALF_SIZE__, 0, uievent.values.height - core.__SIZE__); + uievent.updateSelectPoint(true); + } + + uievent.elements.selectFloor.onchange = function () { + uievent.setPoint(uievent.elements.selectFloor.value); + } + + uievent.elements.selectPointBox.onclick = function (e) { + e.stopPropagation(); + } + + uievent.elements.body.onclick = function (e) { + if (uievent.mode != 'selectPoint') return; + uievent.values.x = uievent.values.left + Math.floor(e.offsetX / uievent.values.size); + uievent.values.y = uievent.values.top + Math.floor(e.offsetY / uievent.values.size); + uievent.updateSelectPoint(false); + } + + uievent.move = function (dx, dy) { + if (uievent.mode != 'selectPoint') return; + uievent.values.left = core.clamp(uievent.values.left + dx, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.top + dy, 0, uievent.values.height - core.__SIZE__); + this.updateSelectPoint(true); + }; + + (function () { + + var viewportButtons = uievent.elements.selectPointButtons; + var pressTimer = null; + for (var ii = 0, node; node = viewportButtons.children[ii]; ii++) { + (function (x, y) { + var move = function () { + uievent.move(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + })(); + + uievent.elements.div.onmousewheel = function (e) { + if (uievent.mode != 'selectPoint' || uievent.values.hideFloor) return; + var index = core.floorIds.indexOf(uievent.values.floorId); + try { + if (e.wheelDelta) + index += Math.sign(e.wheelDelta); + else if (e.detail) + index += Math.sign(e.detail); + } catch (ee) { main.log(ee); } + index = core.clamp(index, 0, core.floorIds.length - 1); + uievent.setPoint(core.floorIds[index]); + } + + // ------ 搜索变量出现的位置,也放在uievent好了 ------ // + + uievent.searchUsedFlags = function () { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'searchUsedFlags'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = '搜索变量'; + uievent.elements.selectBackground.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.usedFlags.style.display = 'inline'; + uievent.elements.usedFlagList.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + // build flags + var html = ""; + Object.keys(editor.used_flags).sort().forEach(function (v) { + v = "flag:" + v; + html += ""; + }); + uievent.elements.usedFlags.innerHTML = html; + + uievent.doSearchUsedFlags(); + } + + uievent.doSearchUsedFlags = function () { + var flag = uievent.elements.usedFlags.value; + + var html = "

该变量出现的所有位置如下:

    "; + var list = uievent._searchUsedFlags(flag); + list.forEach(function (v) { + var x = "
  • "; + if (v[0] != null) x += v[0] + "层 "; + else x += "公共事件 "; + x += v[1]; + if (v[2] != null) x += " 的 (" + v[2] + ") 点"; + x += "
  • "; + html += x; + }); + html += "
"; + uievent.elements.usedFlagList.innerHTML = html; + } + + var hasUsedFlags = function (obj, flag) { + if (obj == null) return false; + if (typeof obj != 'string') return hasUsedFlags(JSON.stringify(obj), flag); + + var index = -1, length = flag.length; + while (true) { + index = obj.indexOf(flag, index + 1); + if (index < 0) return false; + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC]$/.test(obj.charAt(index + length))) return true; + } + } + + uievent._searchUsedFlags = function (flag) { + var list = []; + var events = ["events", "autoEvent", "changeFloor", "afterBattle", "afterGetItem", "afterOpenDoor"] + for (var floorId in core.floors) { + var floor = core.floors[floorId]; + if (hasUsedFlags(floor.firstArrive, flag)) list.push([floorId, "firstArrive"]); + if (hasUsedFlags(floor.eachArrive, flag)) list.push([floorId, "eachArrive"]); + events.forEach(function (e) { + if (floor[e]) { + for (var loc in floor[e]) { + if (hasUsedFlags(floor[e][loc], flag)) { + list.push([floorId, e, loc]); + } + } + } + }); + } + // 公共事件 + if (core.events.commonEvent) { + for (var name in core.events.commonEvent) { + if (hasUsedFlags(core.events.commonEvent[name], flag)) + list.push([null, name]); + } + } + return list; + } + + editor.constructor.prototype.uievent=uievent; + +} \ No newline at end of file diff --git a/_server/editor_unsorted_1.js b/_server/editor_unsorted_1.js deleted file mode 100644 index 0d3861d8..00000000 --- a/_server/editor_unsorted_1.js +++ /dev/null @@ -1,839 +0,0 @@ -editor_unsorted_1_wrapper=function(editor){ - -editor.constructor.prototype.listen=function () { - document.body.onmousedown = function (e) { - //console.log(e); - var clickpath = []; - var getpath=function(e) { - var path = []; - var currentElem = e.target; - while (currentElem) { - path.push(currentElem); - currentElem = currentElem.parentElement; - } - if (path.indexOf(window) === -1 && path.indexOf(document) === -1) - path.push(document); - if (path.indexOf(window) === -1) - path.push(window); - return path; - } - getpath(e).forEach(function (node) { - if (!node.getAttribute) return; - var id_ = node.getAttribute('id'); - if (id_) { - if (['left', 'left1', 'left2', 'left3', 'left4', 'left5', 'left8', 'mobileview'].indexOf(id_) !== -1) clickpath.push('edit'); - clickpath.push(id_); - } - }); - - var unselect=true; - for(var ii=0,thisId;thisId=['edit','tip','brushMod','brushMod2','brushMod3','layerMod','layerMod2','layerMod3','viewportButtons'][ii];ii++){ - if (clickpath.indexOf(thisId) !== -1){ - unselect=false; - break; - } - } - if (unselect) { - if (clickpath.indexOf('eui') === -1) { - if (selectBox.isSelected()) { - editor_mode.onmode(''); - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('地图保存成功'); - }); - } - selectBox.isSelected(false); - editor.info = {}; - } - } - //editor.mode.onmode(''); - if (e.button!=2 && !editor.isMobile){ - editor.hideMidMenu(); - } - if (clickpath.indexOf('down') !== -1 && editor.isMobile && clickpath.indexOf('midMenu') === -1){ - editor.hideMidMenu(); - } - if(clickpath.length>=2 && clickpath[0].indexOf('id_')===0){editor.lastClickId=clickpath[0]} - } - - var eui=document.getElementById('eui'); - var uc = eui.getContext('2d'); - - function fillPos(pos) { - uc.fillStyle = '#' + ~~(Math.random() * 8) + ~~(Math.random() * 8) + ~~(Math.random() * 8); - uc.fillRect(pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8); - }//在格子内画一个随机色块 - - function eToLoc(e) { - var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft - var scrollTop = document.documentElement.scrollTop || document.body.scrollTop - var xx=e.clientX,yy=e.clientY - if(editor.isMobile){xx=e.touches[0].clientX,yy=e.touches[0].clientY} - editor.loc = { - 'x': scrollLeft + xx - mid.offsetLeft - mapEdit.offsetLeft, - 'y': scrollTop + yy - mid.offsetTop - mapEdit.offsetTop, - 'size': editor.isMobile?(32*innerWidth*0.96/core.__PIXELS__):32 - }; - return editor.loc; - }//返回可用的组件内坐标 - - function locToPos(loc, addViewportOffset) { - var offsetX=0, offsetY=0; - if (addViewportOffset){ - offsetX=core.bigmap.offsetX/32; - offsetY=core.bigmap.offsetY/32; - } - editor.pos = {'x': ~~(loc.x / loc.size)+offsetX, 'y': ~~(loc.y / loc.size)+offsetY} - return editor.pos; - } - - var holdingPath = 0; - var stepPostfix = null;//用于存放寻路检测的第一个点之后的后续移动 - - var mouseOutCheck = 2; - - function clear1() { - if (mouseOutCheck > 1) { - mouseOutCheck--; - setTimeout(clear1, 1000); - return; - } - holdingPath = 0; - stepPostfix = []; - uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); - }//用于鼠标移出canvas时的自动清除状态 - - eui.oncontextmenu=function(e){e.preventDefault()} - - eui.ondblclick = function(e) { - // 双击地图可以选中素材 - var loc = eToLoc(e); - var pos = locToPos(loc,true); - editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x]); - return; - } - - eui.onmousedown = function (e) { - if (e.button==2){ - var loc = eToLoc(e); - var pos = locToPos(loc,true); - editor.showMidMenu(e.clientX,e.clientY); - return; - } - 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); - if(editor.isMobile)editor.showMidMenu(e.clientX,e.clientY); - return; - } - - - holdingPath = 1; - mouseOutCheck = 2; - setTimeout(clear1); - e.stopPropagation(); - uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); - var loc = eToLoc(e); - var pos = locToPos(loc,true); - stepPostfix = []; - stepPostfix.push(pos); - fillPos(pos); - } - - eui.onmousemove = function (e) { - if (!selectBox.isSelected()) { - //tip.whichShow(1); - return; - } - - if (holdingPath == 0) { - return; - } - mouseOutCheck = 2; - e.stopPropagation(); - var loc = eToLoc(e); - var pos = locToPos(loc,true); - var pos0 = stepPostfix[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 i = 0; i < 4; i++) { - if (directionDistance[i] > max) { - index = i; - max = directionDistance[i]; - } - } - var 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; - stepPostfix.push(pos); - fillPos(pos); - } - } - - eui.onmouseup = function (e) { - if (!selectBox.isSelected()) { - //tip.whichShow(1); - return; - } - holdingPath = 0; - e.stopPropagation(); - if (stepPostfix && stepPostfix.length) { - editor.preMapData = JSON.parse(JSON.stringify({map:editor.map,fgmap:editor.fgmap,bgmap:editor.bgmap})); - if(editor.brushMod!=='line'){ - var x0=stepPostfix[0].x; - var y0=stepPostfix[0].y; - var x1=stepPostfix[stepPostfix.length-1].x; - var y1=stepPostfix[stepPostfix.length-1].y; - if(x0>x1){x0^=x1;x1^=x0;x0^=x1;}//swap - if(y0>y1){y0^=y1;y1^=y0;y0^=y1;}//swap - stepPostfix=[]; - for(var jj=y0;jj<=y1;jj++){ - for(var ii=x0;ii<=x1;ii++){ - stepPostfix.push({x:ii,y:jj}) - } - } - } - currDrawData.pos = JSON.parse(JSON.stringify(stepPostfix)); - currDrawData.info = JSON.parse(JSON.stringify(editor.info)); - reDo = null; - // console.log(stepPostfix); - if(editor.brushMod==='tileset' && core.tilesets.indexOf(editor.info.images)!==-1){ - var imgWidth=~~(core.material.images.tilesets[editor.info.images].width/32); - var x0=stepPostfix[0].x; - var y0=stepPostfix[0].y; - var idnum=editor.info.idnum; - for (var ii = 0; ii < stepPostfix.length; ii++){ - if(stepPostfix[ii].y!=y0){ - y0++; - idnum+=imgWidth; - } - editor[editor.layerMod][stepPostfix[ii].y][stepPostfix[ii].x] = editor.ids[editor.indexs[idnum+stepPostfix[ii].x-x0]]; - } - } else { - for (var ii = 0; ii < stepPostfix.length; ii++) - editor[editor.layerMod][stepPostfix[ii].y][stepPostfix[ii].x] = editor.info; - } - // console.log(editor.map); - editor.updateMap(); - holdingPath = 0; - stepPostfix = []; - uc.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); - } - } - - /* - document.getElementById('mid').onkeydown = function (e) { - console.log(e); - if (e.keyCode==37) { - editor.moveViewport(-1, 0); - } - if (e.keyCode==38) { - editor.moveViewport(0, -1); - } - if (e.keyCode==39) { - editor.moveViewport(1, 0); - } - if (e.keyCode==40) { - editor.moveViewport(0, 1); - } - } - */ - - document.getElementById('mid').onmousewheel = function (e) { - e.preventDefault(); - var wheel = function (direct) { - var index=editor.core.floorIds.indexOf(editor.currentFloorId); - var toId = editor.currentFloorId; - - if (direct>0 && index0) - toId = editor.core.floorIds[index-1]; - else return; - - editor_mode.onmode('nextChange'); - editor_mode.onmode('floor'); - document.getElementById('selectFloor').value = toId; - editor.changeFloor(toId); - } - - try { - if (e.wheelDelta) - wheel(Math.sign(e.wheelDelta)) - else if (e.detail) - wheel(Math.sign(e.detail)); - } - catch (ee) { - console.log(ee); - } - } - - editor.preMapData = null; - var currDrawData = { - pos: [], - info: {} - }; - var reDo = null; - var shortcut = core.getLocalStorage('shortcut',{48: 0, 49: 0, 50: 0, 51: 0, 52: 0, 53: 0, 54: 0, 55: 0, 56: 0, 57: 0}); - document.body.onkeydown = function (e) { - - // 监听Ctrl+S保存 - if (e.ctrlKey && e.keyCode == 83) { - e.preventDefault(); - if (editor_multi.id != "") { - editor_multi.confirm(); // 保存脚本编辑器 - } - else if (editor_blockly.id != "") { - editor_blockly.confirm(); // 保存事件编辑器 - } - else { - editor_mode.saveFloor(); - } - return; - } - - // 如果是开启事件/脚本编辑器状态,则忽略 - if (editor_multi.id!="" || editor_blockly.id!="") - return; - - // 禁止快捷键的默认行为 - if (e.ctrlKey && [89, 90, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) - e.preventDefault(); - 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()) { - 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)); - editor.updateMap(); - reDo = JSON.parse(JSON.stringify(currDrawData)); - currDrawData = {pos: [], info: {}}; - editor.preMapData = null; - } - //Ctrl+y 重做一步redo - 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)); - - editor.updateMap(); - currDrawData = JSON.parse(JSON.stringify(reDo)); - reDo = null; - } - - // PGUP和PGDOWN切换楼层 - if (e.keyCode==33) { - e.preventDefault(); - var index=editor.core.floorIds.indexOf(editor.currentFloorId); - if (index0) { - var toId = editor.core.floorIds[index-1]; - editor_mode.onmode('nextChange'); - editor_mode.onmode('floor'); - document.getElementById('selectFloor').value = toId; - editor.changeFloor(toId); - } - } - //ctrl + 0~9 切换到快捷图块 - if (e.ctrlKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1){ - editor.setSelectBoxFromInfo(JSON.parse(JSON.stringify(shortcut[e.keyCode]||0))); - } - //alt + 0~9 改变快捷图块 - if (e.altKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1){ - var infoToSave = JSON.stringify(editor.info||0); - if(infoToSave==JSON.stringify({}))return; - shortcut[e.keyCode]=JSON.parse(infoToSave); - printf('已保存该快捷图块, ctrl + '+(e.keyCode-48)+' 使用.') - core.setLocalStorage('shortcut',shortcut); - } - var focusElement = document.activeElement; - if (!focusElement || focusElement.tagName.toLowerCase()=='body') { - // wasd平移大地图 - if (e.keyCode==87) - editor.moveViewport(0,-1) - else if (e.keyCode==65) - editor.moveViewport(-1,0) - else if (e.keyCode==83) - editor.moveViewport(0,1); - else if (e.keyCode==68) - editor.moveViewport(1,0); - } - } - - var getScrollBarHeight = function () { - var outer = document.createElement("div"); - outer.style.visibility = "hidden"; - outer.style.width = "100px"; - outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps - - document.body.appendChild(outer); - - var widthNoScroll = outer.offsetWidth; - // force scrollbars - outer.style.overflow = "scroll"; - - // add innerdiv - var inner = document.createElement("div"); - inner.style.width = "100%"; - outer.appendChild(inner); - - var widthWithScroll = inner.offsetWidth; - - // remove divs - outer.parentNode.removeChild(outer); - - return widthNoScroll - widthWithScroll; - } - var scrollBarHeight = getScrollBarHeight(); - - var dataSelection = document.getElementById('dataSelection'); - var iconLib=document.getElementById('iconLib'); - iconLib.onmousedown = function (e) { - e.stopPropagation(); - if (!editor.isMobile && e.clientY>=((core.__SIZE__==13?630:655) - scrollBarHeight)) return; - var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; - var loc = { - 'x': scrollLeft + e.clientX + iconLib.scrollLeft - right.offsetLeft - iconLib.offsetLeft, - 'y': scrollTop + e.clientY + iconLib.scrollTop - right.offsetTop - iconLib.offsetTop, - 'size': 32 - }; - editor.loc = loc; - var pos = locToPos(loc); - for (var spriter in editor.widthsX) { - if (pos.x >= editor.widthsX[spriter][1] && pos.x < editor.widthsX[spriter][2]) { - var ysize = spriter.indexOf('48') === -1 ? 32 : 48; - loc.ysize = ysize; - pos.images = editor.widthsX[spriter][0]; - pos.y = ~~(loc.y / loc.ysize); - if(core.tilesets.indexOf(pos.images)==-1)pos.x = editor.widthsX[spriter][1]; - var autotiles = core.material.images['autotile']; - if (pos.images == 'autotile') { - var imNames = Object.keys(autotiles); - if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) - pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 4; - else { - for (var i = 0; i < imNames.length; i++) { - if (pos.y >= 4 * i && pos.y < 4 * (i + 1)) { - pos.images = imNames[i]; - pos.y = 4 * i; - } - } - } - } else if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) - pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 1; - - 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'; - dataSelection.style.height = ysize - 6 + 'px'; - - if (pos.x == 0 && pos.y == 0) { - // editor.info={idnum:0, id:'empty','images':'清除块', 'y':0}; - editor.info = 0; - } else if(pos.x == 0 && pos.y == 1){ - editor.info = editor.ids[editor.indexs[17]]; - } else { - if (Object.prototype.hasOwnProperty.call(autotiles, pos.images)) editor.info = {'images': pos.images, 'y': 0}; - else if (pos.images == 'terrains') editor.info = {'images': pos.images, 'y': pos.y - 2}; - else if (core.tilesets.indexOf(pos.images)!=-1) editor.info = {'images': pos.images, 'y': pos.y, 'x': pos.x-editor.widthsX[spriter][1]}; - else editor.info = {'images': pos.images, 'y': pos.y}; - - for (var ii = 0; ii < editor.ids.length; ii++) { - if ((core.tilesets.indexOf(pos.images)!=-1 && editor.info.images == editor.ids[ii].images - && editor.info.y == editor.ids[ii].y && editor.info.x == editor.ids[ii].x) - || (Object.prototype.hasOwnProperty.call(autotiles, pos.images) && editor.info.images == editor.ids[ii].id - && editor.info.y == editor.ids[ii].y) - || (core.tilesets.indexOf(pos.images)==-1 && editor.info.images == editor.ids[ii].images - && editor.info.y == editor.ids[ii].y ) - ) { - - editor.info = editor.ids[ii]; - break; - } - } - } - tip.infos(JSON.parse(JSON.stringify(editor.info))); - editor_mode.onmode('nextChange'); - editor_mode.onmode('enemyitem'); - //editor_mode.enemyitem(); - } - } - } - - var midMenu=document.getElementById('midMenu'); - midMenu.oncontextmenu=function(e){e.preventDefault()} - editor.lastRightButtonPos=[{x:0,y:0},{x:0,y:0}]; - editor.showMidMenu=function(x,y){ - editor.lastRightButtonPos=JSON.parse(JSON.stringify( - [editor.pos,editor.lastRightButtonPos[0]] - )); - var locStr='('+editor.lastRightButtonPos[1].x+','+editor.lastRightButtonPos[1].y+')'; - var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; - - // 检测是否是上下楼 - var thisevent = editor.map[editor.pos.y][editor.pos.x]; - if (thisevent.id=='upFloor') { - addFloorEvent.style.display='block'; - addFloorEvent.children[0].innerHTML='绑定上楼事件'; - } - else if (thisevent.id=='downFloor') { - addFloorEvent.style.display='block'; - addFloorEvent.children[0].innerHTML='绑定下楼事件'; - } - else addFloorEvent.style.display='none'; - - chooseThis.children[0].innerHTML='选中此点'+'('+editor.pos.x+','+editor.pos.y+')' - copyLoc.children[0].innerHTML='复制事件'+locStr+'到此处'; - moveLoc.children[0].innerHTML='交换事件'+locStr+'与此事件的位置'; - midMenu.style='top:'+(y+scrollTop)+'px;left:'+(x+scrollLeft)+'px;'; - } - editor.hideMidMenu=function(){ - if(editor.isMobile){ - setTimeout(function(){ - midMenu.style='display:none'; - },200) - } else { - midMenu.style='display:none'; - } - } - - var addFloorEvent = document.getElementById('addFloorEvent'); - addFloorEvent.onmousedown = function(e) { - editor.hideMidMenu(); - e.stopPropagation(); - var thisevent = editor.map[editor.pos.y][editor.pos.x]; - if (thisevent.id=='upFloor') { - editor.currentFloorData.changeFloor[editor.pos.x+","+editor.pos.y] = {"floorId": ":next", "stair": "downFloor"}; - } - else if (thisevent.id=='downFloor') { - editor.currentFloorData.changeFloor[editor.pos.x+","+editor.pos.y] = {"floorId": ":before", "stair": "upFloor"}; - } - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('添加楼梯事件成功'); - editor.drawPosSelection(); - editor_mode.showMode('loc'); - }); - } - - var chooseThis = document.getElementById('chooseThis'); - chooseThis.onmousedown = function(e){ - editor.hideMidMenu(); - e.stopPropagation(); - selectBox.isSelected(false); - - editor_mode.onmode('nextChange'); - editor_mode.onmode('loc'); - //editor_mode.loc(); - //tip.whichShow(1); - if(editor.isMobile)editor.showdataarea(false); - } - - var chooseInRight = document.getElementById('chooseInRight'); - chooseInRight.onmousedown = function(e){ - editor.hideMidMenu(); - e.stopPropagation(); - var thisevent = editor[editor.layerMod][editor.pos.y][editor.pos.x]; - editor.setSelectBoxFromInfo(thisevent); - } - - var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); - - var copyLoc = document.getElementById('copyLoc'); - copyLoc.onmousedown = function(e){ - editor.hideMidMenu(); - e.stopPropagation(); - editor.preMapData = null; - reDo = null; - editor_mode.onmode(''); - var now = editor.pos; - var last = editor.lastRightButtonPos[1]; - var lastevent = editor.map[last.y][last.x]; - var lastinfo = 0; - if(lastevent==0){ - lastinfo = 0; - } else { - var ids=editor.indexs[lastevent.idnum]; - ids=ids[0]?ids[0]:ids; - lastinfo=editor.ids[ids]; - } - editor.map[now.y][now.x]=lastinfo; - editor.updateMap(); - fields.forEach(function(v){ - editor.currentFloorData[v][now.x+','+now.y]=editor.currentFloorData[v][last.x+','+last.y] - }) - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('复制事件成功'); - editor.drawPosSelection(); - }); - } - - var moveLoc = document.getElementById('moveLoc'); - moveLoc.onmousedown = function(e){ - editor.hideMidMenu(); - e.stopPropagation(); - editor.preMapData = null; - reDo = null; - var thisevent = editor.map[editor.pos.y][editor.pos.x]; - if(thisevent==0){ - editor.info = 0; - } else { - var ids=editor.indexs[thisevent.idnum]; - ids=ids[0]?ids[0]:ids; - editor.info=editor.ids[ids]; - } - editor_mode.onmode(''); - var now = editor.pos; - var last = editor.lastRightButtonPos[1]; - - var lastevent = editor.map[last.y][last.x]; - var lastinfo = 0; - if(lastevent==0){ - lastinfo = 0; - } else { - var ids=editor.indexs[lastevent.idnum]; - ids=ids[0]?ids[0]:ids; - lastinfo=editor.ids[ids]; - } - editor.map[last.y][last.x]=editor.info; - editor.map[now.y][now.x]=lastinfo; - editor.updateMap(); - - fields.forEach(function(v){ - var temp_atsfcytaf=editor.currentFloorData[v][now.x+','+now.y]; - editor.currentFloorData[v][now.x+','+now.y]=editor.currentFloorData[v][last.x+','+last.y]; - editor.currentFloorData[v][last.x+','+last.y]=temp_atsfcytaf; - }) - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('两位置的事件已互换'); - editor.drawPosSelection(); - }); - } - - var _clearPoint = function (clearPoint) { - editor.hideMidMenu(); - editor.preMapData = null; - reDo = null; - editor.info = 0; - editor_mode.onmode(''); - var now = editor.pos; - if (clearPoint) - editor.map[now.y][now.x]=editor.info; - editor.updateMap(); - fields.forEach(function(v){ - delete editor.currentFloorData[v][now.x+','+now.y]; - }) - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf(clearPoint?'清空该点和事件成功':'只清空该点事件成功'); - editor.drawPosSelection(); - }); - } - - var clearEvent = document.getElementById('clearEvent'); - clearEvent.onmousedown = function (e) { - e.stopPropagation(); - _clearPoint(false); - } - - var clearLoc = document.getElementById('clearLoc'); - clearLoc.onmousedown = function(e){ - e.stopPropagation(); - _clearPoint(true); - } - - var brushMod=document.getElementById('brushMod'); - brushMod.onchange=function(){ - editor.brushMod=brushMod.value; - } - - var brushMod2=document.getElementById('brushMod2'); - if(brushMod2)brushMod2.onchange=function(){ - editor.brushMod=brushMod2.value; - } - - var brushMod3=document.getElementById('brushMod3'); - if(brushMod3)brushMod3.onchange=function(){ - editor.brushMod=brushMod3.value; - } - - var bgc = document.getElementById('bg'), fgc = document.getElementById('fg'), - evc = document.getElementById('event'), ev2c = document.getElementById('event2'); - - var layerMod=document.getElementById('layerMod'); - layerMod.onchange=function(){ - editor.layerMod=layerMod.value; - [bgc,fgc,evc,ev2c].forEach(function (x) { - x.style.opacity = 1; - }); - - // 手机端.... - if (editor.isMobile) { - if (layerMod.value == 'bgmap') { - [fgc,evc,ev2c].forEach(function (x) { - x.style.opacity = 0.3; - }); - } - if (layerMod.value == 'fgmap') { - [bgc,evc,ev2c].forEach(function (x) { - x.style.opacity = 0.3; - }); - } - } - } - - var layerMod2=document.getElementById('layerMod2'); - if(layerMod2)layerMod2.onchange=function(){ - editor.layerMod=layerMod2.value; - [fgc,evc,ev2c].forEach(function (x) { - x.style.opacity = 0.3; - }); - bgc.style.opacity = 1; - } - - var layerMod3=document.getElementById('layerMod3'); - if(layerMod3)layerMod3.onchange=function(){ - editor.layerMod=layerMod3.value; - [bgc,evc,ev2c].forEach(function (x) { - x.style.opacity = 0.3; - }); - fgc.style.opacity = 1; - } - - var viewportButtons=document.getElementById('viewportButtons'); - for(var ii=0,node;node=viewportButtons.children[ii];ii++){ - (function(x,y){ - node.onclick=function(){ - editor.moveViewport(x,y); - } - })([-1,0,0,1][ii],[0,-1,1,0][ii]); - } -} - -editor.constructor.prototype.mobile_listen=function () { - if(!editor.isMobile)return; - - var mobileview=document.getElementById('mobileview'); - var editModeSelect=document.getElementById('editModeSelect'); - var mid=document.getElementById('mid'); - var right=document.getElementById('right'); - var mobileeditdata=document.getElementById('mobileeditdata'); - - - editor.showdataarea=function(callShowMode){ - mid.style='z-index:-1;opacity: 0;'; - right.style='z-index:-1;opacity: 0;'; - mobileeditdata.style=''; - if(callShowMode)editor.mode.showMode(editModeSelect.value); - editor.hideMidMenu(); - } - mobileview.children[0].onclick=function(){ - editor.showdataarea(true) - } - mobileview.children[1].onclick=function(){ - mid.style=''; - right.style='z-index:-1;opacity: 0;'; - mobileeditdata.style='z-index:-1;opacity: 0;'; - editor.lastClickId=''; - } - mobileview.children[3].onclick=function(){ - mid.style='z-index:-1;opacity: 0;'; - right.style=''; - mobileeditdata.style='z-index:-1;opacity: 0;'; - editor.lastClickId=''; - } - - - var gettrbyid=function(){ - if(!editor.lastClickId)return false; - thisTr = document.getElementById(editor.lastClickId); - input = thisTr.children[2].children[0].children[0]; - field = thisTr.children[0].getAttribute('title'); - cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); - return [thisTr,input,field,cobj]; - } - mobileeditdata.children[0].onclick=function(){ - var info = gettrbyid() - if(!info)return; - info[1].ondblclick() - } - mobileeditdata.children[1].onclick=function(){ - var info = gettrbyid() - if(!info)return; - printf(info[2]) - } - mobileeditdata.children[2].onclick=function(){ - var info = gettrbyid() - if(!info)return; - printf(info[0].children[1].getAttribute('title')) - } - - //===== - - document.body.ontouchstart=document.body.onmousedown; - document.body.onmousedown=null; - - - var eui=document.getElementById('eui'); - eui.ontouchstart=eui.onmousedown - eui.onmousedown=null - eui.ontouchmove=eui.onmousemove - eui.onmousemove=null - eui.ontouchend=eui.onmouseup - eui.onmouseup=null - - - var chooseThis = document.getElementById('chooseThis'); - chooseThis.ontouchstart=chooseThis.onmousedown - chooseThis.onmousedown=null - var chooseInRight = document.getElementById('chooseInRight'); - chooseInRight.ontouchstart=chooseInRight.onmousedown - chooseInRight.onmousedown=null - var copyLoc = document.getElementById('copyLoc'); - copyLoc.ontouchstart=copyLoc.onmousedown - copyLoc.onmousedown=null - var moveLoc = document.getElementById('moveLoc'); - moveLoc.ontouchstart=moveLoc.onmousedown - moveLoc.onmousedown=null - var clearLoc = document.getElementById('clearLoc'); - clearLoc.ontouchstart=clearLoc.onmousedown - clearLoc.onmousedown=null -} - -} \ No newline at end of file diff --git a/_server/editor_unsorted_2.js b/_server/editor_unsorted_2.js deleted file mode 100644 index ef032c86..00000000 --- a/_server/editor_unsorted_2.js +++ /dev/null @@ -1,581 +0,0 @@ -editor_unsorted_2_wrapper=function(editor_mode){ - - editor_mode.constructor.prototype.listen=function (callback) { - var newIdIdnum = document.getElementById('newIdIdnum'); - newIdIdnum.children[2].onclick = function () { - if (newIdIdnum.children[0].value && newIdIdnum.children[1].value) { - var id = newIdIdnum.children[0].value; - var idnum = parseInt(newIdIdnum.children[1].value); - if (!core.isset(idnum)) { - printe('不合法的idnum'); - return; - } - if (!/^[0-9a-zA-Z_]+$/.test(id)) { - printe('不合法的id,请使用字母、数字或下划线') - return; - } - editor.file.changeIdAndIdnum(id, idnum, editor_mode.info, function (err) { - if (err) { - printe(err); - throw(err) - } - printe('添加id的idnum成功,请F5刷新编辑器'); - }); - } else { - printe('请输入id和idnum'); - } - } - - newIdIdnum.children[4].onclick = function () { - editor.file.autoRegister(editor_mode.info, function (err) { - if (err) { - printe(err); - throw(err) - } - printe('该列所有剩余项全部自动注册成功,请F5刷新编辑器'); - }) - } - - var selectFloor = document.getElementById('selectFloor'); - editor.file.getFloorFileList(function (floors) { - var outstr = []; - floors[0].forEach(function (floor) { - outstr.push(["\n'].join('')); - }); - selectFloor.innerHTML = outstr.join(''); - selectFloor.value = core.status.floorId; - selectFloor.onchange = function () { - editor_mode.onmode('nextChange'); - editor_mode.onmode('floor'); - editor.changeFloor(selectFloor.value); - } - }); - - var saveFloor = document.getElementById('saveFloor'); - editor_mode.saveFloor = function () { - editor_mode.onmode(''); - editor.file.saveFloorFile(function (err) { - if (err) { - printe(err); - throw(err) - } - ;printf('保存成功'); - }); - } - saveFloor.onclick = editor_mode.saveFloor; - - var newMap = document.getElementById('newMap'); - var newFileName = document.getElementById('newFileName'); - newMap.onclick = function () { - if (!newFileName.value) return; - if (core.floorIds.indexOf(newFileName.value)>=0) { - printe("该楼层已存在!"); - return; - } - if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newFileName.value)) { - printe("楼层名不合法!请使用字母、数字、下划线,且不能以数字开头!"); - return; - } - var width = parseInt(document.getElementById('newMapWidth').value); - var height = parseInt(document.getElementById('newMapHeight').value); - if (!core.isset(width) || !core.isset(height) || width1000) { - printe("新建地图的宽高都不得小于"+core.__SIZE__+",且宽高之积不能超过1000"); - return; - } - - editor_mode.onmode(''); - editor.file.saveNewFile(newFileName.value, function (err) { - if (err) { - printe(err); - throw(err) - } - core.floorIds.push(newFileName.value); - 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刷新编辑器生效'); - }); - }); - } - - var newMaps = document.getElementById('newMaps'); - var newFloors = document.getElementById('newFloors'); - newMaps.onclick = function () { - if (newFloors.style.display == 'none') newFloors.style.display = 'block'; - else newFloors.style.display = 'none'; - } - - var createNewMaps = document.getElementById('createNewMaps'); - createNewMaps.onclick = function () { - var floorIds = document.getElementById('newFloorIds').value; - if (!floorIds) return; - var from = parseInt(document.getElementById('newMapsFrom').value), - to = parseInt(document.getElementById('newMapsTo').value); - if (!core.isset(from) || !core.isset(to) || from>to || from<0 || to<0) { - printe("请输入有效的起始和终止楼层"); - return; - } - if (to-from >= 100) { - printe("一次最多创建99个楼层"); - return; - } - var floorIdList = []; - for (var i = from; i<=to; i++) { - var floorId = floorIds.replace(/\${(.*?)}/g, function (word, value) { - return eval(value); - }); - if (core.floorIds.indexOf(floorId)>=0) { - printe("要创建的楼层 "+floorId+" 已存在!"); - return; - } - if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { - printe("楼层名 "+floorId+" 不合法!请使用字母、数字、下划线,且不能以数字开头!"); - return; - } - if (floorIdList.indexOf(floorId)>=0) { - printe("尝试重复创建楼层 "+floorId+" !"); - return; - } - floorIdList.push(floorId); - } - - var width = parseInt(document.getElementById('newMapsWidth').value); - var height = parseInt(document.getElementById('newMapsHeight').value); - if (!core.isset(width) || !core.isset(height) || width1000) { - printe("新建地图的宽高都不得小于"+core.__SIZE__+",且宽高之积不能超过1000"); - return; - } - editor_mode.onmode(''); - - editor.file.saveNewFiles(floorIdList, from, to, function (err) { - if (err) { - printe(err); - throw(err) - } - core.floorIds = core.floorIds.concat(floorIdList); - 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('批量创建 '+floorIdList[0]+'~'+floorIdList[floorIdList.length-1]+' 成功,请F5刷新编辑器生效'); - }); - }); - } - - var ratio = 1; - var appendPicCanvas = document.getElementById('appendPicCanvas'); - var bg = appendPicCanvas.children[0]; - var source = appendPicCanvas.children[1]; - var source_ctx=source.getContext('2d'); - var picClick = appendPicCanvas.children[2]; - var sprite = appendPicCanvas.children[3]; - var sprite_ctx=sprite.getContext('2d'); - var appendPicSelection = document.getElementById('appendPicSelection'); - - [source_ctx,sprite_ctx].forEach(function(ctx){ - ctx.mozImageSmoothingEnabled = false; - ctx.webkitImageSmoothingEnabled = false; - ctx.msImageSmoothingEnabled = false; - ctx.imageSmoothingEnabled = false; - }) - - var selectAppend = document.getElementById('selectAppend'); - var selectAppend_str = []; - ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"].forEach(function (image) { - selectAppend_str.push(["\n'].join('')); - }); - selectAppend.innerHTML = selectAppend_str.join(''); - selectAppend.onchange = function () { - - var value = selectAppend.value; - - if (value == 'autotile') { - editor_mode.appendPic.imageName = 'autotile'; - for (var jj=0;jj<4;jj++) appendPicSelection.children[jj].style = 'display:none'; - if (editor_mode.appendPic.img) { - sprite.style.width = (sprite.width = editor_mode.appendPic.img.width) / ratio + 'px'; - sprite.style.height = (sprite.height = editor_mode.appendPic.img.height) / ratio + 'px'; - sprite_ctx.clearRect(0, 0, sprite.width, sprite.height); - sprite_ctx.drawImage(editor_mode.appendPic.img, 0, 0); - } - return; - } - - var ysize = selectAppend.value.endsWith('48') ? 48 : 32; - editor_mode.appendPic.imageName = value; - var img = core.material.images[value]; - editor_mode.appendPic.toImg = img; - var num = ~~img.width / 32; - editor_mode.appendPic.num = num; - editor_mode.appendPic.index = 0; - var selectStr = ''; - for (var ii = 0; ii < num; ii++) { - appendPicSelection.children[ii].style = 'left:0;top:0;height:' + (ysize - 6) + 'px'; - selectStr += '{"x":0,"y":0},' - } - editor_mode.appendPic.selectPos = eval('[' + selectStr + ']'); - for (var jj = num; jj < 4; jj++) { - appendPicSelection.children[jj].style = 'display:none'; - } - sprite.style.width = (sprite.width = img.width) / ratio + 'px'; - sprite.style.height = (sprite.height = img.height + ysize) / ratio + 'px'; - sprite_ctx.drawImage(img, 0, 0); - } - selectAppend.onchange(); - - var getPixel=editor.util.getPixel - var setPixel=editor.util.setPixel - - var autoAdjust = function (image, callback) { - var changed = false; - - // Step 1: 检测白底 - var tempCanvas = document.createElement('canvas').getContext('2d'); - tempCanvas.canvas.width = image.width; - tempCanvas.canvas.height = image.height; - tempCanvas.mozImageSmoothingEnabled = false; - tempCanvas.webkitImageSmoothingEnabled = false; - tempCanvas.msImageSmoothingEnabled = false; - tempCanvas.imageSmoothingEnabled = false; - tempCanvas.drawImage(image, 0, 0); - var imgData = tempCanvas.getImageData(0, 0, image.width, image.height); - var trans = 0, white = 0, black=0; - for (var i=0;iblack && white>trans*10 && confirm("看起来这张图片是以纯白为底色,是否自动调整为透明底色?")) { - for (var i=0;iwhite && black>trans*10 && confirm("看起来这张图片是以纯黑为底色,是否自动调整为透明底色?")) { - for (var i=0;i= num) editor_mode.appendPic.index = ii + 1 - num; - else editor_mode.appendPic.index++; - editor_mode.appendPic.selectPos[ii] = pos; - appendPicSelection.children[ii].style = [ - 'left:', pos.x * 32, 'px;', - 'top:', pos.y * pos.ysize, 'px;', - 'height:', pos.ysize - 6, 'px;' - ].join(''); - } - - var appendConfirm = document.getElementById('appendConfirm'); - appendConfirm.onclick = function () { - - var confirmAutotile = function () { - var image = editor_mode.appendPic.img; - if (image.width % 96 !=0 || image.height != 128) { - printe("不合法的Autotile图片!"); - return; - } - var imgData = source_ctx.getImageData(0,0,image.width,image.height); - sprite_ctx.putImageData(imgData, 0, 0); - var imgbase64 = sprite.toDataURL().split(',')[1]; - - // Step 1: List文件名 - fs.readdir('./project/images', function (err, data) { - if (err) { - printe(err); - throw(err); - } - - // Step 2: 选择Autotile文件名 - var filename; - for (var i=1;;++i) { - filename = 'autotile'+i; - if (data.indexOf(filename+".png")==-1) break; - } - - // Step 3: 写入文件 - fs.writeFile('./project/images/'+filename+".png", imgbase64, 'base64', function (err, data) { - if (err) { - printe(err); - throw(err); - } - // Step 4: 自动注册 - editor_file.registerAutotile(filename, function (err) { - if (err) { - printe(err); - throw(err); - } - printe('自动元件'+filename+'注册成功,请F5刷新编辑器'); - }) - - }) - - }) - - } - - if (selectAppend.value == 'autotile') { - confirmAutotile(); - return; - } - - var ysize = selectAppend.value.endsWith('48') ? 48 : 32; - for (var ii = 0, v; v = editor_mode.appendPic.selectPos[ii]; ii++) { - // var imgData = source_ctx.getImageData(v.x * 32, v.y * ysize, 32, ysize); - // sprite_ctx.putImageData(imgData, ii * 32, sprite.height - ysize); - // sprite_ctx.drawImage(editor_mode.appendPic.img, v.x * 32, v.y * ysize, 32, ysize, ii * 32, height, 32, ysize) - - sprite_ctx.drawImage(source_ctx.canvas, v.x*32, v.y*ysize, 32, ysize, 32*ii, sprite.height - ysize, 32, ysize); - } - var dt = sprite_ctx.getImageData(0, 0, sprite.width, sprite.height); - var imgbase64 = sprite.toDataURL().split(',')[1]; - fs.writeFile('./project/images/' + editor_mode.appendPic.imageName + '.png', imgbase64, 'base64', function (err, data) { - if (err) { - printe(err); - throw(err) - } - printe('追加素材成功,请F5刷新编辑器,或继续追加当前素材'); - sprite.style.height = (sprite.height = (sprite.height+ysize)) + "px"; - sprite_ctx.putImageData(dt, 0, 0); - }); - } - - var editModeSelect = document.getElementById('editModeSelect'); - editModeSelect.onchange = function () { - editor_mode.onmode('nextChange'); - editor_mode.onmode(editModeSelect.value); - if(editor.isMobile)editor.showdataarea(false); - } - - editor_mode.checkUnique = function (thiseval) { - if (!(thiseval instanceof Array)) return false; - var map = {}; - for (var i = 0; i 目前状态: 按功能分类已基本完成, 未完成file/game的重写 + +总体思路 + 按功能拆分文件 + 左侧页面模块化, 方便添加 + 不同的模式的文件操作尽可能模块化 +目前主要在重构editor_file, 思路是editor.file负责把editor.game内的游戏数据格式化成字符串以及写入到文件, 由editor.game来修改数据 ++ editor.file维护一些标记, 描述哪些数据需要格式化并写入, 在save时写入文件(自动保存的话就是每次修改数据都触发save) ++ editor.game修改数据, 并修改editor.file中的标记 ++ 此思路下editor.file的大部分内容会挪到editor.game, editor.game和editor.table可能会再进一步合并拆分 + +editor_file之后是更改editor.map的储存方式, 现有的存对象的模式要在对象和数字间来回转换, 非常繁琐和奇怪 ## 文件结构 +(全部小写,必要时用下划线分割) + + [ ] 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_file 调用fs.js编辑文件, 把原editor\_file模块化, 并且只负责文件写入 ++ [ ] editor_game 处理游戏数据, 导入为editor的数据, 编辑数据, 从原editor和editor_file中抽离. **只有此文件允许`\s(main|core)`形式的调用**(以及其初始化`editor_game_wrapper(editor, main, core);`) ++ [x] editor_util 生成guid/处理颜色 等函数, 从editor分离 ++ [ ] editor_listen 处理界面上的按钮/下拉框点击等用户的操作与功能函数的绑定, 维护editor.dom, unsorted_1/2中的绑定挪到此处, 其中的函数内容, 分类放在其他文件 ++ [ ] editor_mappanel 与地图区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_datapanel 与数据区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_materialpanel 与素材区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_ui 维护printe/printf/tip, 以及之后可能的窗口化, ui事件中没有具体到前三个区中的函数 <-unsorted_1/2/3 + [ ] editor 执行初始化流程加组合各组件 + [ ] 原editor_mode 移除 + [x] 原vm 移除 @@ -51,7 +67,7 @@ editor: { + 地图的编辑与其他(如全塔属性和楼层属性), 现在的文件操作的模式是完全不同的 楼层文件的储存与其他不同 -+ editor.file在修改时不再返回obj和commentobj,只在查询时返回 ++ [x] editor.file在修改时不再返回obj和commentobj,只在查询时返回 + editor.file中的各个条目, 非常相似, 但是细节的不同处理非常麻烦. 是类似的代码复制后修改一部分, 尝试模块化(或者重写) @@ -61,9 +77,13 @@ editor: { + 目前editor.map中储存的是info\, 准备改为和core一致只储存数字 ++ editor.widthX特别不直观 + ++ ? 编辑器使用可拖拽和调大小的窗口做容器 + ## 功能改进 -+ [ ] 大地图 ++ [x] 大地图 在切换时, 每次都回到最左上->每个楼层记录一个位置 四个箭头支持长按 ? 滚动条 @@ -76,12 +96,12 @@ editor: { + [ ] ? 简洁的事件方块注册 `editor.registerEvent('log',[['test','Int','测试',0],['floorId','Idstring','楼层','MT0']])` -+ [ ] 一个显示所有快捷键的文本 ++ [x] 一个显示所有快捷键的文本 -+ [ ] 更多快捷键 ++ [x] 更多快捷键 【全塔属性】、【楼层属性】等常用的编辑栏切换 -+ [ ] ? 地图编辑优化 ++ [x] ? 地图编辑优化 常用的地图编辑快捷键/命令:复制ctrl+c、粘贴ctrl+v、(复制可绑定为现在的“选中xx位置事件” 粘贴为复制xx事件到此处),撤回ctrl+z、取消撤回ctrl+y 可以按住拖动图块与事件。 @@ -92,16 +112,20 @@ editor: { + [x] 修改系统的触发器(下拉菜单增加新项) 在编辑器修改`comment.js`:现场发readFile请求读文件,然后开脚本编辑器进行编辑 -+ [ ] ? 删除注册项/修改图块ID ++ [x] ? 删除注册项/修改图块ID + [ ] ? 怪物和道具也能像其他类型那样查看“图块信息”(而不只是具体的怪物属性) -+ [ ] 素材区自动换列 ++ [x] 素材区自动换列 怪物或道具太多时, 按照每100个进行拆分新开列来显示 -+ [ ] 多帧素材只显示第一帧 ++ [x] 多帧素材只显示第一帧 -+ [ ] `显示文章`以及`选项`等方块, 把`标题`和`图像`从字符串提取出填回相应的空 ++ [x] `显示文章`以及`选项`等方块, 把`标题`和`图像`从字符串提取出填回相应的空 + ++ [x] blockly中某些需要选点的填空, 增加按钮, 点击后从缩略图中点击位置 + ++ [ ] 插件编写增加判定,如果保存时框内不是以 "function\s*()" 开头(也就是用户直接写的脚本),则自动添加一个 function() { } 将代码包装起来。 ## 左侧页面模式 diff --git a/_server/table/comment.js b/_server/table/comment.js index ff09aa48..5bac4a9b 100644 --- a/_server/table/comment.js +++ b/_server/table/comment.js @@ -67,6 +67,12 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_lint": true, "_data": "即捡即用类物品在获得时提示的文字,仅对cls为items有效。" }, + "useItemEvent": { + "_leaf": true, + "_type": "event", + "_event": "item", + "_data": "碰触或使用本道具所执行的事件" + }, "useItemEffect": { "_leaf": true, "_type": "textarea", @@ -81,12 +87,12 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_lint": true, "_data": "当前能否使用该道具,仅对cls为tools或constants有效。" }, - "canEquip": { + "equipCondition": { "_leaf": true, "_type": "textarea", "_string": true, "_lint": true, - "_data": "当前能否装备某个装备,仅对cls为equips有效。\n与canUseItemEffect不同,这里null代表可以装备。" + "_data": "能装备某个装备的条件,仅对cls为equips有效。\n与canUseItemEffect不同,这里null代表可以装备。" } } }, @@ -143,7 +149,7 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_leaf": true, "_type": "textarea", "_range": "thiseval==null || thiseval instanceof Array || (thiseval==~~thiseval && thiseval>=0)", - "_data": "特殊属性\n\n0:无,1:先攻,2:魔攻,3:坚固,4:2连击,\n5:3连击,6:n连击,7:破甲,8:反击,9:净化,\n10:模仿,11:吸血,12:中毒,13:衰弱,14:诅咒,\n15:领域,16:夹击,17:仇恨,18:阻击,19:自爆,\n20:无敌,21:退化,22:固伤,23:重生,24:激光,25:光环\n\n多个属性例如用[1,4,11]表示先攻2连击吸血" + "_data": "特殊属性\n\n0:无,1:先攻,2:魔攻,3:坚固,4:2连击,\n5:3连击,6:n连击,7:破甲,8:反击,9:净化,\n10:模仿,11:吸血,12:中毒,13:衰弱,14:诅咒,\n15:领域,16:夹击,17:仇恨,18:阻击,19:自爆,\n20:无敌,21:退化,22:固伤,23:重生,24:激光,25:光环\n26:支援,27:捕捉\n多个属性例如用[1,4,11]表示先攻2连击吸血" }, "value": { "_leaf": true, @@ -225,12 +231,18 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "false", "_data": "图块类别" }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "图块名称" + }, "trigger": { "_leaf": true, "_type": "select", "_select": { "values": [ - null, + "null", "openDoor", "passNet", "changeLight", @@ -245,18 +257,19 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "select", "_select": { "values": [ - null, - true, - false + "null", + "true", + "false" ] }, "_data": "该图块是否不可通行;true代表不可通行,false代表可通行,null代表使用系统缺省值" }, - "canBreak": { + "script": { "_leaf": true, - "_type": "checkbox", - "_bool": "bool", - "_data": "该图块是否可被破墙或地震" + "_type": "textarea", + "_string": true, + "_lint": true, + "_data": "触碰到该图块时自动执行的脚本内容;此脚本会在该点的触发器执行前执行" }, "cannotOut": { "_leaf": true, @@ -270,6 +283,12 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "thiseval==null||(thiseval instanceof Array)", "_data": "该图块的不可入方向\n可以在这里定义不能朝哪个方向进入该图块,可以达到悬崖之类的效果\n例如 [\"down\"] 代表不能从该图块的上方点朝向下进入此图块\n此值对背景层、事件层、前景层上的图块均有效" }, + "canBreak": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "该图块是否可被破墙或地震" + }, "animate": { "_leaf": true, "_type": "textarea", @@ -429,6 +448,24 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_event": "event", "_data": "该点的可能事件列表,可以双击进入事件编辑器。" }, + "autoEvent": { + "_type": "object", + "_leaf": false, + "_action": function (args) { + args.vobj=args.vobj||{}; + for(var ii=0;ii<2;ii++){ + args.vobj[ii]=args.vobj[ii]||null; + } + }, + "_data": function (key) { + return { + "_leaf": true, + "_type": "event", + "_event": "autoEvent", + "_data": "自动事件页" + } + } + }, "changeFloor": { "_leaf": true, "_type": "event", @@ -458,10 +495,40 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "textarea", "_range": "thiseval==null||(thiseval instanceof Array)", "_data": "该点不可通行的方向 \n 可以在这里定义该点不能前往哪个方向,可以达到悬崖之类的效果\n例如 [\"up\", \"left\"] 代表该点不能往上和左走" - } + }, } } } + }, + + "floors_template": { + "floorId": "to be covered", + "title": "new floor", + "name": "new floor", + "width": 13, + "height": 13, + "canFlyTo": true, + "canUseQuickShop": true, + "cannotViewMap": false, + "cannotMoveDirectly": false, + "images": [], + "item_ratio": 1, + "defaultGround": "ground", + "bgm": null, + "upFloor": null, + "downFloor": null, + "color": null, + "weather": null, + "firstArrive": [], + "eachArrive": [], + "parallelDo": "", + "events": {}, + "changeFloor": {}, + "afterBattle": {}, + "afterGetItem": {}, + "afterOpenDoor": {}, + "autoEvent": {}, + "cannotMove": {} } } } \ No newline at end of file diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js index 6b98a633..74237a8f 100644 --- a/_server/table/data.comment.js +++ b/_server/table/data.comment.js @@ -32,7 +32,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_leaf": true, "_type": "textarea", "_range": "editor.mode.checkUnique(thiseval)", - "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" \n 根据需求自行添加" + "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" 根据需求自行添加" }, "bgms": { "_leaf": true, @@ -46,6 +46,11 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "editor.mode.checkUnique(thiseval)", "_data": "在此存放所有的SE,和文件名一致 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" }, + "nameMap": { + "_leaf": true, + "_type": "textarea", + "_data": "文件名映射,目前仅对images, animates, bgms, sounds有效。\n例如定义 {\"精灵石.mp3\":\"jinglingshi.mp3\"} 就可以使用\ncore.playBgm(\"精灵石.mp3\") 或对应的事件来播放该bgm。" + }, "startBackground": { "_leaf": true, "_type": "textarea", @@ -58,6 +63,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_string": true, "_data": "标题样式:可以改变颜色,也可以写\"display: none\"来隐藏标题" }, + "startButtonsStyle": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "标题界面按钮的样式;caret-color指的是当前选中项的边框颜色" + }, "levelChoose": { "_leaf": true, "_type": "textarea", @@ -510,7 +521,13 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_leaf": true, "_type": "checkbox", "_bool": "bool", - "_data": "是否需要在楼梯边使用传送器" + "_data": "传送器是否需要在楼梯边使用;如果flyRecordPosition开启,则此项对箭头也有效。" + }, + "flyRecordPosition": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "传送器平面塔模式;此模式下楼层传送器将飞到上次离开该楼层的位置。" }, "pickaxeFourDirections": { "_leaf": true, @@ -537,11 +554,17 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_data": "如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙" }, "steelDoorWithoutKey": { - "_left": true, + "_leaf": true, "_type": "checkbox", "_bool": "bool", "_data": "铁门是否不需要钥匙开启。如果此项为true,则无需钥匙也可以开铁门。" }, + "itemFirstText": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "首次获得道具是否提示" + }, "equipment": { "_leaf": true, "_type": "checkbox", @@ -680,6 +703,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_bool": "bool", "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" }, + "blurFg": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否虚化前景层;如果此项开启,则在游戏中事件层有东西(如宝石等)时虚化前景层。" + }, "checkConsole": { "_leaf": true, "_type": "checkbox", diff --git a/_server/table/functions.comment.js b/_server/table/functions.comment.js index 44396901..81e18e7c 100644 --- a/_server/table/functions.comment.js +++ b/_server/table/functions.comment.js @@ -94,6 +94,12 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_lint": true, "_data": "炸弹事件" }, + "afterPassNet": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "经过特殊地形后的事件" + }, "canUseQuickShop": { "_leaf": true, "_type": "textarea", @@ -122,12 +128,6 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "textarea", "_lint": true, "_data": "获得战斗伤害信息" - }, - "updateEnemys": { - "_leaf": true, - "_type": "textarea", - "_lint": true, - "_data": "更新怪物数据" } } }, @@ -139,6 +139,12 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_type": "textarea", "_lint": true, "_data": "按键处理" + }, + "onStatusBarClick": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "状态栏点击事件,仅在开启自绘状态栏时生效" } } }, diff --git a/_server/table/plugins.comment.js b/_server/table/plugins.comment.js index fc4f5144..3d436a06 100644 --- a/_server/table/plugins.comment.js +++ b/_server/table/plugins.comment.js @@ -20,6 +20,18 @@ var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { "_range": "typeof(thiseval)=='string' || thiseval==null", "_data": "绘制灯光效果" }, + "drawItemShop": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "道具商店插件" + }, + "smoothCamera": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "平滑移动镜头效果" + }, } if (obj[key]) return obj[key]; return { diff --git a/_server/thirdparty/LICENSE.md b/_server/thirdparty/LICENSE.md new file mode 100644 index 00000000..8867f3df --- /dev/null +++ b/_server/thirdparty/LICENSE.md @@ -0,0 +1,60 @@ +/* jsColor */ + +The MIT License (MIT) + +Copyright (c) 2014 Peter Dematté + +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. + +/* awesomplete */ + +The MIT License (MIT) + +Copyright (c) 2015 Lea Verou + +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. + + +/* caret-position */ + +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Ong me@jongleberry.com + +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. diff --git a/_server/thirdparty/awesomplete.css b/_server/thirdparty/awesomplete.css new file mode 100644 index 00000000..b36a7035 --- /dev/null +++ b/_server/thirdparty/awesomplete.css @@ -0,0 +1,104 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + background: #fff; +} + +.awesomplete > ul:empty { + display: none; +} + +.awesomplete > ul { + border-radius: .3em; + margin: .8em 0 0; + background: hsla(0,0%,100%,.9); + background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: .05em .2em .6em rgba(0,0,0,.2); + text-shadow: none; +} + +@supports (transform: scale(0)) { + .awesomplete > ul { + transition: .3s cubic-bezier(.4,.2,.5,1.4); + transform-origin: 1.43em -.43em; + } + + .awesomplete > ul[hidden], + .awesomplete > ul:empty { + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; + } +} + +/* Pointer */ +.awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + padding: .4em; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); +} + +.awesomplete > ul > li { + position: relative; + padding: .2em .5em; + cursor: pointer; + word-break: keep-all; +} + +.awesomplete > ul > li:hover { + background: hsl(200, 40%, 80%); + color: black; +} + +.awesomplete > ul > li[aria-selected="true"] { + background: hsl(205, 40%, 40%); + color: white; +} + +.awesomplete mark { + background: hsl(65, 100%, 50%); +} + +.awesomplete li:hover mark { + background: hsl(68, 100%, 41%); +} + +.awesomplete li[aria-selected="true"] mark { + background: hsl(86, 100%, 21%); + color: inherit; +} +/*# sourceMappingURL=awesomplete.css.map */ \ No newline at end of file diff --git a/_server/thirdparty/awesomplete.min.js b/_server/thirdparty/awesomplete.min.js new file mode 100644 index 00000000..5328bd9b --- /dev/null +++ b/_server/thirdparty/awesomplete.min.js @@ -0,0 +1,3 @@ +// Awesomplete - Lea Verou - MIT license +!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); +//# sourceMappingURL=awesomplete.min.js.map \ No newline at end of file diff --git a/_server/thirdparty/caret-position.js b/_server/thirdparty/caret-position.js new file mode 100644 index 00000000..a46ddb78 --- /dev/null +++ b/_server/thirdparty/caret-position.js @@ -0,0 +1,155 @@ +/* jshint browser: true */ + +(function () { + +// We'll copy the properties below into the mirror div. +// Note that some browsers, such as Firefox, do not concatenate properties +// into their shorthand (e.g. padding-top, padding-bottom etc. -> padding), +// so we have to list every single property explicitly. + var properties = [ + 'direction', // RTL support + 'boxSizing', + 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does + 'height', + 'overflowX', + 'overflowY', // copy the scrollbar for IE + + 'borderTopWidth', + 'borderRightWidth', + 'borderBottomWidth', + 'borderLeftWidth', + 'borderStyle', + + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft', + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'fontStretch', + 'fontSize', + 'fontSizeAdjust', + 'lineHeight', + 'fontFamily', + + 'textAlign', + 'textTransform', + 'textIndent', + 'textDecoration', // might not make a difference, but better be safe + + 'letterSpacing', + 'wordSpacing', + + 'tabSize', + 'MozTabSize' + + ]; + + var isBrowser = (typeof window !== 'undefined'); + var isFirefox = (isBrowser && window.mozInnerScreenX != null); + + function getCaretCoordinates(element, position, options) { + if (!isBrowser) { + throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); + } + + var debug = options && options.debug || false; + if (debug) { + var el = document.querySelector('#input-textarea-caret-position-mirror-div'); + if (el) el.parentNode.removeChild(el); + } + + // The mirror div will replicate the textarea's style + var div = document.createElement('div'); + div.id = 'input-textarea-caret-position-mirror-div'; + document.body.appendChild(div); + + var style = div.style; + var computed = window.getComputedStyle ? window.getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 + var isInput = element.nodeName === 'INPUT'; + + // Default textarea styles + style.whiteSpace = 'nowrap'; + if (!isInput) + style.wordWrap = 'break-word'; // only for textarea-s + + // Position off-screen + style.position = 'absolute'; // required to return coordinates properly + if (!debug) + style.visibility = 'hidden'; // not 'display: none' because we want rendering + + // Transfer the element's properties to the div + properties.forEach(function (prop) { + if (isInput && prop === 'lineHeight') { + // Special case for s because text is rendered centered and line height may be != height + if (computed.boxSizing === "border-box") { + var height = parseInt(computed.height); + var outerHeight = + parseInt(computed.paddingTop) + + parseInt(computed.paddingBottom) + + parseInt(computed.borderTopWidth) + + parseInt(computed.borderBottomWidth); + var targetHeight = outerHeight + parseInt(computed.lineHeight); + if (height > targetHeight) { + style.lineHeight = height - outerHeight + "px"; + } else if (height === targetHeight) { + style.lineHeight = computed.lineHeight; + } else { + style.lineHeight = 0; + } + } else { + style.lineHeight = computed.height; + } + } else { + style[prop] = computed[prop]; + } + }); + + if (isFirefox) { + // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 + if (element.scrollHeight > parseInt(computed.height)) + style.overflowY = 'scroll'; + } else { + style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' + } + + div.textContent = element.value.substring(0, position); + // The second special handling for input type="text" vs textarea: + // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 + if (isInput) + div.textContent = div.textContent.replace(/\s/g, '\u00a0'); + + var span = document.createElement('span'); + // Wrapping must be replicated *exactly*, including when a long word gets + // onto the next line, with whitespace at the end of the line before (#7). + // The *only* reliable way to do that is to copy the *entire* rest of the + // textarea's content into the created at the caret position. + // For inputs, just '.' would be enough, but no need to bother. + span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all + div.appendChild(span); + + var coordinates = { + top: span.offsetTop + parseInt(computed['borderTopWidth']), + left: span.offsetLeft + parseInt(computed['borderLeftWidth']), + height: parseInt(computed['lineHeight']) + }; + + if (debug) { + span.style.backgroundColor = '#aaa'; + } else { + document.body.removeChild(div); + } + + return coordinates; + } + + if (typeof module != 'undefined' && typeof module.exports != 'undefined') { + module.exports = getCaretCoordinates; + } else if(isBrowser) { + window.getCaretCoordinates = getCaretCoordinates; + } + +}()); \ No newline at end of file diff --git a/_server/colorPicker/color.all.min.js b/_server/thirdparty/color.all.min.js similarity index 100% rename from _server/colorPicker/color.all.min.js rename to _server/thirdparty/color.all.min.js diff --git a/_server/colorPicker/jsColor.js b/_server/thirdparty/jsColor.js similarity index 84% rename from _server/colorPicker/jsColor.js rename to _server/thirdparty/jsColor.js index b3ca28ea..cbd9dee6 100644 --- a/_server/colorPicker/jsColor.js +++ b/_server/thirdparty/jsColor.js @@ -1,3 +1,5 @@ +// ------ ColorPicker ------ // + (function (window) { window.jsColorPicker = function(selectors, config) { var renderCallback = function(colors, mode) { @@ -254,7 +256,7 @@ })(this); // Added -var colors = jsColorPicker('input.color', { +jsColorPicker('input.color', { customBG: '#222', readOnly: false, // patch: false, @@ -303,85 +305,5 @@ function triggerColorPicker(left, top) { } } -Blockly.FieldColour.prototype.createWidget_ = function() { - Blockly.WidgetDiv.hide(); +// ------ AutoCompletion ------ - // console.log('here') - var self=this; - var pb=self.sourceBlock_ - var args = MotaActionBlocks[pb.type].args - var targetf=args[args.indexOf(self.name)-1] - - var getValue=function(){ - // return self.getValue() // css颜色 - return pb.getFieldValue(targetf); - // 也可以用 pb.getFieldValue(targetf) 获得颜色块左边的域的内容 - } - - var setValue=function(newValue){ // css颜色 - self.setValue(newValue) - var c=new Colors(); - c.setColor(newValue) - var rgbatext = [c.colors.webSmart.r,c.colors.webSmart.g,c.colors.webSmart.b,c.colors.alpha].join(","); - pb.setFieldValue(rgbatext, targetf) // 放在颜色块左边的域中 - } - - setTimeout(function () { - document.getElementById("colorPicker").value = getValue(); - window.jsColorPicker.confirm = setValue; - // 设置位置 - triggerColorPicker(Blockly.WidgetDiv.DIV.style.left, Blockly.WidgetDiv.DIV.style.top); - }); - - return document.createElement('table'); -}; - -Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { - Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_()); - var div = Blockly.WidgetDiv.DIV; - // Create the input. - var htmlInput = - goog.dom.createDom(goog.dom.TagName.INPUT, 'blocklyHtmlInput'); - htmlInput.setAttribute('spellcheck', this.spellcheck_); - var fontSize = - (Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale) + 'pt'; - div.style.fontSize = fontSize; - htmlInput.style.fontSize = fontSize; - - Blockly.FieldTextInput.htmlInput_ = htmlInput; - div.appendChild(htmlInput); - - htmlInput.value = htmlInput.defaultValue = this.text_; - htmlInput.oldValue_ = null; - this.validate_(); - this.resizeEditor_(); - if (!quietInput) { - htmlInput.focus(); - htmlInput.select(); - } - - // console.log('here') - var self=this; - var pb=self.sourceBlock_ - var args = MotaActionBlocks[pb.type].args - var targetf=args[args.indexOf(self.name)+1] - - if(targetf && targetf.slice(0,7)==='Colour_'){ - var inputDom = htmlInput; - // var getValue=function(){ // 获得自己的字符串 - // return pb.getFieldValue(self.name); - // } - var setValue = function(newValue){ // 设置右边颜色块的css颜色 - pb.setFieldValue(newValue, targetf) - } - // 给inputDom绑事件 - inputDom.oninput=function(){ - var value=inputDom.value - if(/[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?/.test(value)){ - setValue('rgba('+value+')') - } - } - } - - this.bindEvents_(htmlInput); -}; \ No newline at end of file diff --git a/_server/vendor/polyfill.min.js b/_server/vendor/polyfill.min.js deleted file mode 100644 index 80fe8dc8..00000000 --- a/_server/vendor/polyfill.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(){}function n(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,f._immediateFn(function(){var i=1===e._state?n.onFulfilled:n.onRejected;if(null!==i){var r;try{r=i(e._value)}catch(e){return void o(n.promise,e)}t(n.promise,r)}else(1===e._state?t:o)(n.promise,e._value)})):e._deferreds.push(n)}function t(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof f)return e._state=3,e._value=n,void i(e);if("function"==typeof t)return void r(function(e,n){return function(){e.apply(n,arguments)}}(t,n),e)}e._state=1,e._value=n,i(e)}catch(n){o(e,n)}}function o(e,n){e._state=2,e._value=n,i(e)}function i(e){2===e._state&&0===e._deferreds.length&&f._immediateFn(function(){e._handled||f._unhandledRejectionFn(e._value)});for(var t=0,o=e._deferreds.length;o>t;t++)n(e,e._deferreds[t]);e._deferreds=null}function r(e,n){var i=!1;try{e(function(e){i||(i=!0,t(n,e))},function(e){i||(i=!0,o(n,e))})}catch(e){if(i)return;i=!0,o(n,e)}}function f(e){if(!(this instanceof f))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],r(e,this)}var u=setTimeout,c=f.prototype;c.catch=function(e){return this.then(null,e)},c.then=function(t,o){var i=new this.constructor(e);return n(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(t,o,i)),i},f.all=function(e){return new f(function(n,t){function o(e,f){try{if(f&&("object"==typeof f||"function"==typeof f)){var u=f.then;if("function"==typeof u)return void u.call(f,function(n){o(e,n)},t)}i[e]=f,0==--r&&n(i)}catch(e){t(e)}}if(!e||void 0===e.length)throw new TypeError("Promise.all accepts an array");var i=Array.prototype.slice.call(e);if(0===i.length)return n([]);for(var r=i.length,f=0;i.length>f;f++)o(f,i[f])})},f.resolve=function(e){return e&&"object"==typeof e&&e.constructor===f?e:new f(function(n){n(e)})},f.reject=function(e){return new f(function(n,t){t(e)})},f.race=function(e){return new f(function(n,t){for(var o=0,i=e.length;i>o;o++)e[o].then(n,t)})},f._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){u(e,0)},f._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var a=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==a)return a;throw Error("unable to locate global object")}();a.Promise||(a.Promise=f)}); diff --git a/_server/vendor/vue.min.js b/_server/vendor/vue.min.js deleted file mode 100644 index 4eb55b79..00000000 --- a/_server/vendor/vue.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Vue.js v2.5.11 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Vue=e()}(this,function(){"use strict";function t(t){return void 0===t||null===t}function e(t){return void 0!==t&&null!==t}function n(t){return!0===t}function r(t){return"string"==typeof t||"number"==typeof t||"boolean"==typeof t}function i(t){return null!==t&&"object"==typeof t}function o(t){return"[object Object]"===Nn.call(t)}function a(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function s(t){return null==t?"":"object"==typeof t?JSON.stringify(t,null,2):String(t)}function c(t){var e=parseFloat(t);return isNaN(e)?t:e}function u(t,e){for(var n=Object.create(null),r=t.split(","),i=0;i-1)return t.splice(n,1)}}function f(t,e){return Mn.call(t,e)}function d(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}function p(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function v(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function h(t,e){for(var n in e)t[n]=e[n];return t}function m(t){for(var e={},n=0;n0&&(tt((s=et(s,(o||"")+"_"+a))[0])&&tt(u)&&(l[c]=x(u.text+s[0].text),s.shift()),l.push.apply(l,s)):r(s)?tt(u)?l[c]=x(u.text+s):""!==s&&l.push(x(s)):tt(s)&&tt(u)?l[c]=x(u.text+s.text):(n(i._isVList)&&e(s.tag)&&t(s.key)&&e(o)&&(s.key="__vlist"+o+"_"+a+"__"),l.push(s)));return l}function nt(t,e){return(t.__esModule||fr&&"Module"===t[Symbol.toStringTag])&&(t=t.default),i(t)?e.extend(t):t}function rt(t){return t.isComment&&t.asyncFactory}function it(t){if(Array.isArray(t))for(var n=0;n=0||n.indexOf(t[i])<0)&&r.push(t[i]);return r}return t}}(n[o],r[o],i[o]));return e}(t);r&&h(t.extendOptions,r),(e=t.options=F(n,t.extendOptions)).name&&(e.components[e.name]=t)}}return e}function Rt(t){this._init(t)}function Ht(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,i=t._Ctor||(t._Ctor={});if(i[r])return i[r];var o=t.name||n.options.name,a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=F(n.options,t),a.super=n,a.options.props&&function(t){var e=t.options.props;for(var n in e)mt(t.prototype,"_props",n)}(a),a.options.computed&&function(t){var e=t.options.computed;for(var n in e)gt(t.prototype,n,e[n])}(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,zn.forEach(function(t){a[t]=n[t]}),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=h({},a.options),i[r]=a,a}}function Bt(t){return t&&(t.Ctor.options.name||t.tag)}function Ut(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"==typeof t?t.split(",").indexOf(e)>-1:!!function(t){return"[object RegExp]"===Nn.call(t)}(t)&&t.test(e)}function Vt(t,e){var n=t.cache,r=t.keys,i=t._vnode;for(var o in n){var a=n[o];if(a){var s=Bt(a.componentOptions);s&&!e(s)&&zt(n,o,r,i)}}}function zt(t,e,n,r){var i=t[e];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),t[e]=null,l(n,e)}function Kt(t){for(var n=t.data,r=t,i=t;e(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(n=Jt(i.data,n));for(;e(r=r.parent);)r&&r.data&&(n=Jt(n,r.data));return function(t,n){if(e(t)||e(n))return qt(t,Wt(n));return""}(n.staticClass,n.class)}function Jt(t,n){return{staticClass:qt(t.staticClass,n.staticClass),class:e(t.class)?[t.class,n.class]:n.class}}function qt(t,e){return t?e?t+" "+e:t:e||""}function Wt(t){return Array.isArray(t)?function(t){for(var n,r="",i=0,o=t.length;i=0&&" "===(m=t.charAt(h));h--);m&&Ii.test(m)||(l=!0)}}else void 0===o?(v=i+1,o=t.slice(0,i).trim()):e();if(void 0===o?o=t.slice(0,i).trim():0!==v&&e(),a)for(i=0;i-1?{exp:t.slice(0,ii),key:'"'+t.slice(ii+1)+'"'}:{exp:t,key:null};ni=t,ii=oi=ai=0;for(;!ge();)_e(ri=ye())?be(ri):91===ri&&function(t){var e=1;oi=ii;for(;!ge();)if(t=ye(),_e(t))be(t);else if(91===t&&e++,93===t&&e--,0===e){ai=ii;break}}(ri);return{exp:t.slice(0,oi),key:t.slice(oi+1,ai)}}(t);return null===n.key?t+"="+e:"$set("+n.exp+", "+n.key+", "+e+")"}function ye(){return ni.charCodeAt(++ii)}function ge(){return ii>=ei}function _e(t){return 34===t||39===t}function be(t){for(var e=t;!ge()&&(t=ye())!==e;);}function $e(t,e,n,r,i){e=function(t){return t._withTask||(t._withTask=function(){Er=!0;var e=t.apply(null,arguments);return Er=!1,e})}(e),n&&(e=function(t,e,n){var r=si;return function i(){null!==t.apply(null,arguments)&&Ce(e,i,n,r)}}(e,t,r)),si.addEventListener(t,e,or?{capture:r,passive:i}:r)}function Ce(t,e,n,r){(r||si).removeEventListener(t,e._withTask||e,n)}function we(n,r){if(!t(n.data.on)||!t(r.data.on)){var i=r.data.on||{},o=n.data.on||{};si=r.elm,function(t){if(e(t[Li])){var n=Qn?"change":"input";t[n]=[].concat(t[Li],t[n]||[]),delete t[Li]}e(t[Mi])&&(t.change=[].concat(t[Mi],t.change||[]),delete t[Mi])}(i),X(i,o,$e,Ce,r.context),si=void 0}}function xe(n,r){if(!t(n.data.domProps)||!t(r.data.domProps)){var i,o,a=r.elm,s=n.data.domProps||{},u=r.data.domProps||{};e(u.__ob__)&&(u=r.data.domProps=h({},u));for(i in s)t(u[i])&&(a[i]="");for(i in u){if(o=u[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i){a._value=o;var l=t(o)?"":String(o);(function(t,n){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,n)||function(t,n){var r=t.value,i=t._vModifiers;if(e(i)){if(i.lazy)return!1;if(i.number)return c(r)!==c(n);if(i.trim)return r.trim()!==n.trim()}return r!==n}(t,n))})(a,l)&&(a.value=l)}else a[i]=o}}}function ke(t){var e=Ae(t.style);return t.staticStyle?h(t.staticStyle,e):e}function Ae(t){return Array.isArray(t)?m(t):"string"==typeof t?Fi(t):t}function Oe(n,r){var i=r.data,o=n.data;if(!(t(i.staticStyle)&&t(i.style)&&t(o.staticStyle)&&t(o.style))){var a,s,c=r.elm,u=o.staticStyle,l=o.normalizedStyle||o.style||{},f=u||l,d=Ae(r.data.style)||{};r.data.normalizedStyle=e(d.__ob__)?h({},d):d;var p=function(t,e){var n,r={};if(e)for(var i=t;i.componentInstance;)(i=i.componentInstance._vnode)&&i.data&&(n=ke(i.data))&&h(r,n);(n=ke(t.data))&&h(r,n);for(var o=t;o=o.parent;)o.data&&(n=ke(o.data))&&h(r,n);return r}(r,!0);for(s in f)t(p[s])&&Bi(c,s,"");for(s in p)(a=p[s])!==f[s]&&Bi(c,s,null==a?"":a)}}function Se(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.add(e)}):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function Te(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.remove(e)}):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" "+(t.getAttribute("class")||"")+" ",r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function Ee(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&h(e,Ki(t.name||"v")),h(e,t),e}return"string"==typeof t?Ki(t):void 0}}function je(t){Qi(function(){Qi(t)})}function Ne(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),Se(t,e))}function Ie(t,e){t._transitionClasses&&l(t._transitionClasses,e),Te(t,e)}function Le(t,e,n){var r=Me(t,e),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===qi?Zi:Yi,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout(function(){c0&&(n=qi,l=a,f=o.length):e===Wi?u>0&&(n=Wi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?qi:Wi:null)?n===qi?o.length:c.length:0;return{type:n,timeout:l,propCount:f,hasTransform:n===qi&&to.test(r[Gi+"Property"])}}function De(t,e){for(;t.length1}function Ue(t,e){!0!==e.data.show&&Fe(e)}function Ve(t,e,n){ze(t,e,n),(Qn||er)&&setTimeout(function(){ze(t,e,n)},0)}function ze(t,e,n){var r=e.value,i=t.multiple;if(!i||Array.isArray(r)){for(var o,a,s=0,c=t.options.length;s-1,a.selected!==o&&(a.selected=o);else if(g(Je(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));i||(t.selectedIndex=-1)}}function Ke(t,e){return e.every(function(e){return!g(e,t)})}function Je(t){return"_value"in t?t._value:t.value}function qe(t){t.target.composing=!0}function We(t){t.target.composing&&(t.target.composing=!1,Ge(t.target,"input"))}function Ge(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Ze(t){return!t.componentInstance||t.data&&t.data.transition?t:Ze(t.componentInstance._vnode)}function Xe(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Xe(it(e.children)):t}function Ye(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var i=n._parentListeners;for(var o in i)e[Pn(o)]=i[o];return e}function Qe(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function tn(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function en(t){t.data.newPos=t.elm.getBoundingClientRect()}function nn(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,i=e.top-n.top;if(r||i){t.data.moved=!0;var o=t.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}function rn(t,e){var n=e?zo:Vo;return t.replace(n,function(t){return Uo[t]})}function on(t,e,n){return{type:1,tag:t,attrsList:e,attrsMap:function(t){for(var e={},n=0,r=t.length;n=0&&a[i].lowerCasedTag!==s;i--);else i=0;if(i>=0){for(var c=a.length-1;c>=i;c--)e.end&&e.end(a[c].tag,n,r);a.length=i,o=i&&a[i-1].tag}else"br"===s?e.start&&e.start(t,[],!0,n,r):"p"===s&&(e.start&&e.start(t,[],!1,n,r),e.end&&e.end(t,n,r))}for(var i,o,a=[],s=e.expectHTML,c=e.isUnaryTag||Bn,u=e.canBeLeftOpenTag||Bn,l=0;t;){if(i=t,o&&Ho(o)){var f=0,d=o.toLowerCase(),p=Bo[d]||(Bo[d]=new RegExp("([\\s\\S]*?)(]*>)","i")),v=t.replace(p,function(t,n,r){return f=r.length,Ho(d)||"noscript"===d||(n=n.replace(//g,"$1").replace(//g,"$1")),Jo(d,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""});l+=t.length-v.length,t=v,r(d,l-f,l)}else{var h=t.indexOf("<");if(0===h){if(Ao.test(t)){var m=t.indexOf("--\x3e");if(m>=0){e.shouldKeepComment&&e.comment(t.substring(4,m)),n(m+3);continue}}if(Oo.test(t)){var y=t.indexOf("]>");if(y>=0){n(y+2);continue}}var g=t.match(ko);if(g){n(g[0].length);continue}var _=t.match(xo);if(_){var b=l;n(_[0].length),r(_[1],b,l);continue}var $=function(){var e=t.match(Co);if(e){var r={tagName:e[1],attrs:[],start:l};n(e[0].length);for(var i,o;!(i=t.match(wo))&&(o=t.match(_o));)n(o[0].length),r.attrs.push(o);if(i)return r.unarySlash=i[1],n(i[0].length),r.end=l,r}}();if($){!function(t){var n=t.tagName,i=t.unarySlash;s&&("p"===o&&go(n)&&r(o),u(n)&&o===n&&r(n));for(var l=c(n)||!!i,f=t.attrs.length,d=new Array(f),p=0;p=0){for(w=t.slice(h);!(xo.test(w)||Co.test(w)||Ao.test(w)||Oo.test(w)||(x=w.indexOf("<",1))<0);)h+=x,w=t.slice(h);C=t.substring(0,h),n(h)}h<0&&(C=t,t=""),e.chars&&C&&e.chars(C)}if(t===i){e.chars&&e.chars(t);break}}r()}(t,{warn:To,expectHTML:e.expectHTML,isUnaryTag:e.isUnaryTag,canBeLeftOpenTag:e.canBeLeftOpenTag,shouldDecodeNewlines:e.shouldDecodeNewlines,shouldDecodeNewlinesForHref:e.shouldDecodeNewlinesForHref,shouldKeepComment:e.comments,start:function(t,a,u){var l=i&&i.ns||Do(t);Qn&&"svg"===l&&(a=function(t){for(var e=[],n=0;na&&o.push(JSON.stringify(t.slice(a,i)));var s=ae(r[1].trim());o.push("_s("+s+")"),a=i+r[0].length}return a':'
',Ro.innerHTML.indexOf(" ")>0}var jn=Object.freeze({}),Nn=Object.prototype.toString,In=u("slot,component",!0),Ln=u("key,ref,slot,slot-scope,is"),Mn=Object.prototype.hasOwnProperty,Dn=/-(\w)/g,Pn=d(function(t){return t.replace(Dn,function(t,e){return e?e.toUpperCase():""})}),Fn=d(function(t){return t.charAt(0).toUpperCase()+t.slice(1)}),Rn=/\B([A-Z])/g,Hn=d(function(t){return t.replace(Rn,"-$1").toLowerCase()}),Bn=function(t,e,n){return!1},Un=function(t){return t},Vn="data-server-rendered",zn=["component","directive","filter"],Kn=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured"],Jn={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:Bn,isReservedAttr:Bn,isUnknownElement:Bn,getTagNamespace:y,parsePlatformTagName:Un,mustUseProp:Bn,_lifecycleHooks:Kn},qn=/[^\w.$]/,Wn="__proto__"in{},Gn="undefined"!=typeof window,Zn="undefined"!=typeof WXEnvironment&&!!WXEnvironment.platform,Xn=Zn&&WXEnvironment.platform.toLowerCase(),Yn=Gn&&window.navigator.userAgent.toLowerCase(),Qn=Yn&&/msie|trident/.test(Yn),tr=Yn&&Yn.indexOf("msie 9.0")>0,er=Yn&&Yn.indexOf("edge/")>0,nr=Yn&&Yn.indexOf("android")>0||"android"===Xn,rr=Yn&&/iphone|ipad|ipod|ios/.test(Yn)||"ios"===Xn,ir=(Yn&&/chrome\/\d+/.test(Yn),{}.watch),or=!1;if(Gn)try{var ar={};Object.defineProperty(ar,"passive",{get:function(){or=!0}}),window.addEventListener("test-passive",null,ar)}catch(t){}var sr,cr,ur=function(){return void 0===sr&&(sr=!Gn&&"undefined"!=typeof global&&"server"===global.process.env.VUE_ENV),sr},lr=Gn&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,fr="undefined"!=typeof Symbol&&w(Symbol)&&"undefined"!=typeof Reflect&&w(Reflect.ownKeys);cr="undefined"!=typeof Set&&w(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var dr=y,pr=0,vr=function(){this.id=pr++,this.subs=[]};vr.prototype.addSub=function(t){this.subs.push(t)},vr.prototype.removeSub=function(t){l(this.subs,t)},vr.prototype.depend=function(){vr.target&&vr.target.addDep(this)},vr.prototype.notify=function(){for(var t=this.subs.slice(),e=0,n=t.length;eVr&&Fr[n].id>t.id;)n--;Fr.splice(n+1,0,t)}else Fr.push(t);Br||(Br=!0,q(ht))}}(this)},Kr.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||i(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){V(t,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},Kr.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},Kr.prototype.depend=function(){for(var t=this.deps.length;t--;)this.deps[t].depend()},Kr.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||l(this.vm._watchers,this);for(var t=this.deps.length;t--;)this.deps[t].removeSub(this);this.active=!1}};var Jr={enumerable:!0,configurable:!0,get:y,set:y},qr={lazy:!0};Nt(It.prototype);var Wr={init:function(t,n,r,i){if(!t.componentInstance||t.componentInstance._isDestroyed){(t.componentInstance=function(t,n,r,i){var o={_isComponent:!0,parent:n,_parentVnode:t,_parentElm:r||null,_refElm:i||null},a=t.data.inlineTemplate;return e(a)&&(o.render=a.render,o.staticRenderFns=a.staticRenderFns),new t.componentOptions.Ctor(o)}(t,Pr,r,i)).$mount(n?t.elm:void 0,n)}else if(t.data.keepAlive){var o=t;Wr.prepatch(o,o)}},prepatch:function(t,e){var n=e.componentOptions;!function(t,e,n,r,i){var o=!!(i||t.$options._renderChildren||r.data.scopedSlots||t.$scopedSlots!==jn);if(t.$options._parentVnode=r,t.$vnode=r,t._vnode&&(t._vnode.parent=r),t.$options._renderChildren=i,t.$attrs=r.data&&r.data.attrs||jn,t.$listeners=n||jn,e&&t.$options.props){Cr.shouldConvert=!1;for(var a=t._props,s=t.$options._propKeys||[],c=0;c1?v(n):n;for(var r=v(arguments,1),i=0,o=n.length;iparseInt(this.max)&&zt(a,s[0],s,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}}};!function(t){var e={};e.get=function(){return Jn},Object.defineProperty(t,"config",e),t.util={warn:dr,extend:h,mergeOptions:F,defineReactive:E},t.set=j,t.delete=N,t.nextTick=q,t.options=Object.create(null),zn.forEach(function(e){t.options[e+"s"]=Object.create(null)}),t.options._base=t,h(t.options.components,ti),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=v(arguments,1);return n.unshift(this),"function"==typeof t.install?t.install.apply(t,n):"function"==typeof t&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=F(this.options,t),this}}(t),Ht(t),function(t){zn.forEach(function(e){t[e]=function(t,n){return n?("component"===e&&o(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"==typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}})}(t)}(Rt),Object.defineProperty(Rt.prototype,"$isServer",{get:ur}),Object.defineProperty(Rt.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Rt.version="2.5.11";var ei,ni,ri,ii,oi,ai,si,ci,ui=u("style,class"),li=u("input,textarea,option,select,progress"),fi=function(t,e,n){return"value"===n&&li(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},di=u("contenteditable,draggable,spellcheck"),pi=u("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),vi="http://www.w3.org/1999/xlink",hi=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},mi=function(t){return hi(t)?t.slice(6,t.length):""},yi=function(t){return null==t||!1===t},gi={svg:"http://www.w3.org/2000/svg",math:"http://www.w3.org/1998/Math/MathML"},_i=u("html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot"),bi=u("svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view",!0),$i=function(t){return _i(t)||bi(t)},Ci=Object.create(null),wi=u("text,number,password,search,email,tel,url"),xi=Object.freeze({createElement:function(t,e){var n=document.createElement(t);return"select"!==t?n:(e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)},createElementNS:function(t,e){return document.createElementNS(gi[t],e)},createTextNode:function(t){return document.createTextNode(t)},createComment:function(t){return document.createComment(t)},insertBefore:function(t,e,n){t.insertBefore(e,n)},removeChild:function(t,e){t.removeChild(e)},appendChild:function(t,e){t.appendChild(e)},parentNode:function(t){return t.parentNode},nextSibling:function(t){return t.nextSibling},tagName:function(t){return t.tagName},setTextContent:function(t,e){t.textContent=e},setAttribute:function(t,e,n){t.setAttribute(e,n)}}),ki={create:function(t,e){Xt(e)},update:function(t,e){t.data.ref!==e.data.ref&&(Xt(t,!0),Xt(e))},destroy:function(t){Xt(t,!0)}},Ai=new mr("",{},[]),Oi=["create","activate","update","remove","destroy"],Si={create:te,update:te,destroy:function(t){te(t,Ai)}},Ti=Object.create(null),Ei=[ki,Si],ji={create:re,update:re},Ni={create:oe,update:oe},Ii=/[\w).+\-_$\]]/,Li="__r",Mi="__c",Di={create:we,update:we},Pi={create:xe,update:xe},Fi=d(function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach(function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}}),e}),Ri=/^--/,Hi=/\s*!important$/,Bi=function(t,e,n){if(Ri.test(e))t.style.setProperty(e,n);else if(Hi.test(n))t.style.setProperty(e,n.replace(Hi,""),"important");else{var r=Vi(e);if(Array.isArray(n))for(var i=0,o=n.length;ip?v(n,t(i[g+1])?null:i[g+1].elm,i,d,g,o):d>g&&m(0,r,f,p)}function _(r,i,o,a){if(r!==i){var s=i.elm=r.elm;if(n(r.isAsyncPlaceholder))e(i.asyncFactory.resolved)?$(r.elm,i,o):i.isAsyncPlaceholder=!0;else if(n(i.isStatic)&&n(r.isStatic)&&i.key===r.key&&(n(i.isCloned)||n(i.isOnce)))i.componentInstance=r.componentInstance;else{var c,u=i.data;e(u)&&e(c=u.hook)&&e(c=c.prepatch)&&c(r,i);var l=r.children,d=i.children;if(e(u)&&f(i)){for(c=0;c-1?Ci[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Ci[t]=/HTMLUnknownElement/.test(e.toString())},h(Rt.options.directives,ro),h(Rt.options.components,so),Rt.prototype.__patch__=Gn?eo:y,Rt.prototype.$mount=function(t,e){return t=t&&Gn?Zt(t):void 0,function(t,e,n){t.$el=e,t.$options.render||(t.$options.render=gr),vt(t,"beforeMount");var r;return r=function(){t._update(t._render(),n)},new Kr(t,r,y,null,!0),n=!1,null==t.$vnode&&(t._isMounted=!0,vt(t,"mounted")),t}(this,t,e)},Rt.nextTick(function(){Jn.devtools&&lr&&lr.emit("init",Rt)},0);var co,uo=/\{\{((?:.|\n)+?)\}\}/g,lo=/[-.*+?^${}()|[\]\/\\]/g,fo=d(function(t){var e=t[0].replace(lo,"\\$&"),n=t[1].replace(lo,"\\$&");return new RegExp(e+"((?:.|\\n)+?)"+n,"g")}),po={staticKeys:["staticClass"],transformNode:function(t,e){e.warn;var n=ve(t,"class");n&&(t.staticClass=JSON.stringify(n));var r=pe(t,"class",!1);r&&(t.classBinding=r)},genData:function(t){var e="";return t.staticClass&&(e+="staticClass:"+t.staticClass+","),t.classBinding&&(e+="class:"+t.classBinding+","),e}},vo={staticKeys:["staticStyle"],transformNode:function(t,e){e.warn;var n=ve(t,"style");n&&(t.staticStyle=JSON.stringify(Fi(n)));var r=pe(t,"style",!1);r&&(t.styleBinding=r)},genData:function(t){var e="";return t.staticStyle&&(e+="staticStyle:"+t.staticStyle+","),t.styleBinding&&(e+="style:("+t.styleBinding+"),"),e}},ho=function(t){return co=co||document.createElement("div"),co.innerHTML=t,co.textContent},mo=u("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),yo=u("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),go=u("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),_o=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,bo="[a-zA-Z_][\\w\\-\\.]*",$o="((?:"+bo+"\\:)?"+bo+")",Co=new RegExp("^<"+$o),wo=/^\s*(\/?)>/,xo=new RegExp("^<\\/"+$o+"[^>]*>"),ko=/^]+>/i,Ao=/^ + + 自动注册

色相: @@ -100,7 +103,7 @@

-

地图选点     +

地图选点      

0,0

@@ -121,13 +124,6 @@

图块属性        

-
- - - -
- -
@@ -140,6 +136,21 @@
+
+ + +
+
+
+ + + +
+ +
+
+ +
@@ -158,6 +169,10 @@
+
+ + +
@@ -186,6 +201,7 @@ --> @@ -208,6 +225,10 @@
+ + + + 开启中文名替换 @@ -226,6 +247,7 @@ 语法检查 +
@@ -288,10 +310,16 @@ - +
+ +
@@ -325,7 +354,7 @@ + + @@ -522,23 +584,29 @@ + - + + - - - + + + + + + + - - - + + + + + diff --git a/editor.html b/editor.html index 7953d7ba..0e26d46f 100644 --- a/editor.html +++ b/editor.html @@ -4,6 +4,7 @@ + @@ -71,6 +72,8 @@ + +   自动注册

色相: @@ -96,7 +99,7 @@

-

地图选点     +

地图选点      

0,0

@@ -117,6 +120,23 @@

图块属性        

+
+
+ + + + + + + + +
条目注释
+
+
+ + +
+
@@ -124,18 +144,9 @@
-
-
- - - - - - - - -
条目注释
-
+
+ +
@@ -154,6 +165,10 @@
+
+ + +
@@ -182,6 +197,7 @@ --> @@ -198,12 +215,15 @@
- -
+ + + + + 开启中文名替换 @@ -222,6 +242,7 @@ 语法检查 +
@@ -284,7 +305,7 @@ - +
@@ -312,6 +333,7 @@ + 锁定模式
画线 @@ -336,6 +358,12 @@
+
+

最近使用的图块列表:

+
+ +
+
+ -

请输入文字...

- +
@@ -160,6 +188,7 @@ + diff --git a/libs/actions.js b/libs/actions.js index abdfb709..db190b0b 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -42,6 +42,7 @@ actions.prototype._init = function () { // --- onmove注册 this.registerAction('onmove', '_sys_checkReplay', this._sys_checkReplay, 100); this.registerAction('onmove', '_sys_onmove_paint', this._sys_onmove_paint, 50); + this.registerAction('onmove', '_sys_onmove_choices', this._sys_onmove_choices, 30); this.registerAction('onmove', '_sys_onmove', this._sys_onmove, 0); // --- onup注册 this.registerAction('onup', '_sys_checkReplay', this._sys_checkReplay, 100); @@ -58,6 +59,8 @@ actions.prototype._init = function () { // --- longClick注册 this.registerAction('longClick', '_sys_longClick_lockControl', this._sys_longClick_lockControl, 50); this.registerAction('longClick', '_sys_longClick', this._sys_longClick, 0); + // --- onStatusBarClick注册 + this.registerAction('onStatusBarClick', '_sys_onStatusBarClick', this._sys_onStatusBarClick, 0); } @@ -112,8 +115,8 @@ actions.prototype.doRegisteredAction = function (action) { } actions.prototype._checkReplaying = function () { - if (core.isReplaying() && core.status.event.id != 'save' - && (core.status.event.id || "").indexOf('book') != 0 && core.status.event.id != 'viewMaps') + if (core.isReplaying() && + ['save','book','book-detail','viewMaps','toolbox','equipbox','text'].indexOf(core.status.event.id)<0) return true; return false; } @@ -137,6 +140,7 @@ actions.prototype._sys_onkeyDown = function (e) { return; } } + if (e.preventDefault) e.preventDefault(); core.status.holdingKeys.push(e.keyCode); this.pressKey(e.keyCode); } else { @@ -162,19 +166,27 @@ actions.prototype._sys_onkeyUp_replay = function (e) { core.triggerReplay(); else if (e.keyCode == 65) // A core.rewindReplay(); - else if (e.keyCode == 83) + else if (e.keyCode == 83) // S core.saveReplay(); - else if (e.keyCode == 67) + else if (e.keyCode == 67) // C core.bookReplay(); - else if (e.keyCode == 33 || e.keyCode == 34) + else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn core.viewMapReplay(); - else if (e.keyCode >= 49 && e.keyCode <= 51) + else if (e.keyCode == 78) // N + core.stepReplay(); + else if (e.keyCode == 84) // T + core.toolboxReplay(); + else if (e.keyCode == 81) // Q + core.equipboxReplay(); + else if (e.keyCode == 66) // B + core.drawStatistics(); + else if (e.keyCode >= 49 && e.keyCode <= 51) // 1-3 core.setReplaySpeed(e.keyCode - 48); - else if (e.keyCode == 52) + else if (e.keyCode == 52) // 4 core.setReplaySpeed(6); - else if (e.keyCode == 53) + else if (e.keyCode == 53) // 5 core.setReplaySpeed(12); - else if (e.keyCode == 54) + else if (e.keyCode == 54) // 6 core.setReplaySpeed(24); return true; } @@ -190,6 +202,7 @@ actions.prototype._sys_onkeyUp = function (e) { break; } } + if (e.preventDefault) e.preventDefault(); this.keyUp(e.keyCode, e.altKey); } else { if (e.keyCode == 17) core.status.ctrlDown = false; @@ -245,9 +258,12 @@ actions.prototype._sys_keyDown_lockControl = function (keyCode) { case 'save': case 'load': case 'replayLoad': + case 'replayRemain': this._keyDownSL(keyCode); break; case 'shop': + this._keyDownShop(keyCode); + break; case 'selectShop': case 'switchs': case 'settings': @@ -346,6 +362,7 @@ actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { case 'save': case 'load': case 'replayLoad': + case 'replayRemain': this._keyUpSL(keyCode); break; case 'keyBoard': @@ -425,6 +442,7 @@ actions.prototype._sys_ondown_lockControl = function (x, y, px, py) { core.setFlag('px', px); core.setFlag('py', py); core.status.route.push("input:" + (1000000 + 1000 * px + py)); + core.events.__action_wait_afterGet(core.status.event.data.current); core.doAction(); } else { @@ -471,6 +489,30 @@ actions.prototype._sys_onmove_paint = function (x, y, px, py) { } } +actions.prototype._sys_onmove_choices = function (x, y) { + if (!core.status.lockControl) return false; + + switch (core.status.event.id) { + case 'action': + if (core.status.event.data.type != 'choices') break; + case 'shop': + case 'selectShop': + case 'switchs': + case 'settings': + case 'syncSave': + case 'syncSelect': + case 'localSaveSelect': + case 'storageRemove': + case 'replay': + case 'gameInfo': + this._onMoveChoices(x, y); + return true; + default: + break; + } + return false; +} + actions.prototype._sys_onmove = function (x, y) { if ((core.status.stepPostfix || []).length > 0) { var pos = {'x': x, 'y': y}; @@ -495,8 +537,10 @@ actions.prototype._sys_onmove = function (x, y) { } ////// 当点击(触摸)事件放开时 ////// -actions.prototype.onup = function () { - this.doRegisteredAction('onup'); +actions.prototype.onup = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('onup', x, y, px, py); } actions.prototype._sys_onup_paint = function () { @@ -553,12 +597,12 @@ actions.prototype._getClickLoc = function (x, y) { size = size * core.domStyle.scale; if (core.domStyle.isVertical) { - statusBar.x = 0; + statusBar.x = 3; statusBar.y = core.dom.statusBar.offsetHeight + 3; } else { statusBar.x = core.dom.statusBar.offsetWidth + 3; - statusBar.y = 0; + statusBar.y = 3; } var left = core.dom.gameGroup.offsetLeft + statusBar.x; @@ -570,7 +614,7 @@ actions.prototype._getClickLoc = function (x, y) { ////// 具体点击屏幕上(x,y)点时,执行的操作 ////// actions.prototype.onclick = function (x, y, stepPostfix) { // console.log("Click: (" + x + "," + y + ")"); - this.doRegisteredAction('onclick', x, y, stepPostfix || []); + return this.doRegisteredAction('onclick', x, y, stepPostfix || []); } actions.prototype._sys_onclick_lockControl = function (x, y) { @@ -612,6 +656,7 @@ actions.prototype._sys_onclick_lockControl = function (x, y) { case 'save': case 'load': case 'replayLoad': + case 'replayRemain': this._clickSL(x, y); break; case 'confirmBox': @@ -705,6 +750,17 @@ actions.prototype._sys_onmousewheel = function (direct) { return; } + // wait事件 + if (core.status.lockControl && core.status.event.id == 'action' && core.status.event.data.type == 'wait') { + core.setFlag('type', 0); + var keycode = direct == 1 ? 33 : 34; + core.setFlag('keycode', keycode); + core.status.route.push("input:" + keycode); + core.events.__action_wait_afterGet(core.status.event.data.current); + core.doAction(); + return; + } + } ////// 长按Ctrl键时 ////// @@ -755,13 +811,17 @@ actions.prototype._sys_longClick_lockControl = function (x, y) { return true; } } - // 长按SL快速翻页 - if (["save","load","replayLoad"].indexOf(core.status.event.id) >= 0) { + // 长按SL上下页快速翻页 + if (["save","load","replayLoad","replayRemain"].indexOf(core.status.event.id) >= 0) { if ([this.HSIZE-2, this.HSIZE-3, this.HSIZE+2, this.HSIZE+3].indexOf(x) >= 0 && y == this.LAST) { this._clickSL(x, y); return true; } } + // 长按商店连续购买 + if (core.status.event.id == 'shop' && x >= this.CHOICES_LEFT && x <= this.CHOICES_RIGHT) { + return this._clickShop(x, y); + } // 长按可以跳过等待事件 if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' && !core.status.event.data.current.noSkip) { @@ -786,6 +846,19 @@ actions.prototype._sys_longClick = function (x, y, fromEvent) { return false; } +actions.prototype.onStatusBarClick = function (e) { + if (!core.isPlaying()) return false; + var left = core.dom.gameGroup.offsetLeft + 3; + var top = core.dom.gameGroup.offsetTop + 3; + var px = parseInt((e.clientX - left) / core.domStyle.scale), py = parseInt((e.clientY - top) / core.domStyle.scale); + return this.doRegisteredAction('onStatusBarClick', px, py); +} + +actions.prototype._sys_onStatusBarClick = function (px, py) { + if (this.actionsdata.onStatusBarClick) + return this.actionsdata.onStatusBarClick(px, py); +} + /////////////////// 在某个界面时的按键点击效果 /////////////////// // 数字键快速选择选项 @@ -794,6 +867,13 @@ actions.prototype._selectChoices = function (length, keycode, callback) { if (keycode == 13 || keycode == 32 || keycode == 67) { callback.apply(this, [this.HSIZE, topIndex + core.status.event.selection]); } + //左右方向键调整 音量 行走速度 + if(core.status.event.id == "switchs" && (core.status.event.selection == 2 || core.status.event.selection == 3)) + { + if (keycode == 37) callback.apply(this, [this.HSIZE - 2, topIndex + core.status.event.selection]); + if (keycode == 39) callback.apply(this, [this.HSIZE + 2, topIndex + core.status.event.selection]); + } + if (keycode >= 49 && keycode <= 57) { var index = keycode - 49; if (index < length) { @@ -814,6 +894,21 @@ actions.prototype._keyDownChoices = function (keycode) { } } +// 移动光标 +actions.prototype._onMoveChoices = function (x, y) { + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return; + var choices = core.status.event.ui.choices; + + var topIndex = this.HSIZE - parseInt((choices.length - 1) / 2) + (core.status.event.ui.offset || 0); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + if (selection == core.status.event.selection) return; + core.status.event.selection = selection; + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices); + } +} + ////// 点击中心对称飞行器时 actions.prototype._clickCenterFly = function (x, y) { var posX = core.status.event.data.posX, posY = core.status.event.data.posY; @@ -913,7 +1008,7 @@ actions.prototype._clickAction = function (x, y) { core.insertAction(core.status.event.ui.yes); core.doAction(); } - if ((x == this.HSIZE+2 || x == this.HSIZE+1) && y == this.HSIZE+1) { + else 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(); @@ -949,6 +1044,7 @@ actions.prototype._keyUpAction = function (keycode) { core.setFlag('type', 0); core.setFlag('keycode', keycode); core.status.route.push("input:" + keycode); + core.events.__action_wait_afterGet(core.status.event.data.current); core.doAction(); return; } @@ -1240,13 +1336,25 @@ actions.prototype._clickShop = function (x, y) { return true; } +actions.prototype._keyDownShop = function (keycode) { + // 商店界面长按空格连续购买 + if (keycode == 32 && core.status.event.selection != core.status.event.data.shop.choices.length) { + this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); + return; + } + this._keyDownChoices(keycode); +} + ////// 商店界面时,放开某个键的操作 ////// actions.prototype._keyUpShop = function (keycode) { if (keycode == 27 || keycode == 88) { core.events._exitShop(); return; } - this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); + if (keycode != 32 || core.status.event.selection == core.status.event.data.shop.choices.length) { + this._selectChoices(core.status.event.data.shop.choices.length + 1, keycode, this._clickShop); + return; + } return; } @@ -1294,7 +1402,10 @@ actions.prototype._clickToolbox = function (x, y) { // 装备栏 if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); - core.openEquipbox(); + if (core.isReplaying()) + core.equipboxReplay(); + else + core.openEquipbox(); return; } if (x >= this.LAST - 2 && y == this.LAST) { @@ -1353,6 +1464,7 @@ actions.prototype._clickToolboxIndex = function (index) { if (select >= items.length) return; var itemId = items[select]; if (itemId == core.status.event.data.selectId) { + if (core.isReplaying()) return; core.events.tryUseItem(itemId); } else { @@ -1455,7 +1567,10 @@ actions.prototype._keyDownToolbox = function (keycode) { actions.prototype._keyUpToolbox = function (keycode) { if (keycode == 81) { core.ui.closePanel(); - core.openEquipbox(); + if (core.isReplaying()) + core.equipboxReplay(); + else + core.openEquipbox(); return; } if (keycode == 84 || keycode == 27 || keycode == 88) { @@ -1475,7 +1590,10 @@ actions.prototype._clickEquipbox = function (x, y) { // 道具栏 if (x >= this.LAST - 2 && y == 0) { core.ui.closePanel(); - core.openToolbox(); + if (core.isReplaying()) + core.toolboxReplay(); + else + core.openToolbox(); return; } // 返回 @@ -1524,6 +1642,7 @@ 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]) { + if (core.isReplaying()) return; core.unloadEquip(index); core.status.route.push("unEquip:" + index); } @@ -1531,6 +1650,7 @@ actions.prototype._clickEquipboxIndex = function (index) { else { var equips = Object.keys(core.status.hero.items.equips || {}).sort(); if (index == core.status.event.selection) { + if (core.isReplaying()) return; var equipId = equips[index - this.LAST + (core.status.event.data.page - 1) * this.LAST]; core.loadEquip(equipId); core.status.route.push("equip:" + equipId); @@ -1622,7 +1742,10 @@ actions.prototype._keyUpEquipbox = function (keycode, altKey) { } if (keycode == 84) { core.ui.closePanel(); - core.openToolbox(); + if (core.isReplaying()) + core.toolboxReplay(); + else + core.openToolbox(); return; } if (keycode == 81 || keycode == 27 || keycode == 88) { @@ -1657,6 +1780,7 @@ actions.prototype._clickSL = function (x, y) { if (core.events.recoverEvents(core.status.event.interval)) return; core.ui.closePanel(); + delete core.status.tempRoute; if (!core.isPlaying()) core.showStartAnimate(true); return; @@ -1814,6 +1938,11 @@ actions.prototype._keyUpSL = function (keycode) { this._clickSL(this.LAST, this.LAST); return; } + if (keycode >= 48 && keycode <= 57) { + if (keycode == 48) keycode = 58; + core.ui.drawSLPanel((keycode - 49) * 1000 + 1); + return; + } if (keycode == 13 || keycode == 32 || keycode == 67) { if (offset == 0) core.doSL("autoSave", core.status.event.id); @@ -1848,11 +1977,14 @@ actions.prototype._keyUpSL = function (keycode) { ////// 系统设置界面时的点击操作 ////// 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 = this.HSIZE - parseInt((choices.length - 1) / 2) + (core.status.event.ui.offset || 0); - if (y >= topIndex && y < topIndex + choices.length) { - var selection = y - topIndex; + var selection = y - topIndex; + if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) { + if (selection != 2 && selection != 3) return; + if (x != this.HSIZE - 2 && x != this.HSIZE + 2) return; + } + if (selection >= 0 && selection < choices.length) { core.status.event.selection = selection; switch (selection) { case 0: @@ -1860,18 +1992,24 @@ actions.prototype._clickSwitchs = function (x, y) { case 1: return this._clickSwitchs_sound(); case 2: - return this._clickSwitchs_displayEnemyDamage(); + if (x == this.HSIZE - 2) return this._clickSwitchs_userVolume(-1); + if (x == this.HSIZE + 2) return this._clickSwitchs_userVolume(1); + return; case 3: - return this._clickSwitchs_displayCritical(); + if (x == this.HSIZE - 2) return this._clickSwitchs_moveSpeed(-10); + if (x == this.HSIZE + 2) return this._clickSwitchs_moveSpeed(10); + return; case 4: - return this._clickSwitchs_displayExtraDamage(); + return this._clickSwitchs_displayEnemyDamage(); case 5: - return this._clickSwitchs_localForage(); + return this._clickSwitchs_displayCritical(); case 6: - return this._clickSwitchs_clickMove(); + return this._clickSwitchs_displayExtraDamage(); case 7: - return this._clickSwitchs_ExtendKeyboard(); + return this._clickSwitchs_localForage(); case 8: + return this._clickSwitchs_clickMove(); + case 9: core.status.event.selection = 0; core.ui.drawSettings(); break; @@ -1890,6 +2028,22 @@ actions.prototype._clickSwitchs_sound = function () { core.ui.drawSwitchs(); } +actions.prototype._clickSwitchs_userVolume = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); + core.musicStatus.userVolume = core.clamp(Math.pow(value + delta, 2) / 100, 0, 1); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + if (core.musicStatus.playingBgm) core.material.bgms[core.musicStatus.playingBgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + core.setLocalStorage('userVolume', core.musicStatus.userVolume); + core.ui.drawSwitchs(); +} + +actions.prototype._clickSwitchs_moveSpeed = function (delta) { + core.values.moveSpeed = core.clamp(core.values.moveSpeed + delta, 50, 200); + core.setLocalStorage("moveSpeed", core.values.moveSpeed); + core.ui.drawSwitchs(); +} + actions.prototype._clickSwitchs_displayEnemyDamage = function () { core.flags.displayEnemyDamage = !core.flags.displayEnemyDamage; core.updateDamage(); @@ -1926,13 +2080,6 @@ actions.prototype._clickSwitchs_clickMove = function () { 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) { @@ -2019,12 +2166,10 @@ actions.prototype._clickSyncSave = function (x, y) { case 4: return this._clickSyncSave_replay(); case 5: - return this._clickSyncSave_download(); - case 6: core.status.event.selection = 0; core.ui.drawStorageRemove(); break; - case 7: + case 6: core.status.event.selection = 4; core.ui.drawSettings(); break; @@ -2040,30 +2185,11 @@ actions.prototype._clickSyncSave_readFile = function () { if (obj.version != core.firstData.version) return alert("游戏版本不一致!"); if (!obj.data) return alert("无效的存档!"); core.control._syncLoad_write(obj.data); - }); + }, null, ".h5save"); } 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) - })); + core.ui.drawReplay(); } ////// 同步存档界面时,放开某个键的操作 ////// @@ -2252,9 +2378,10 @@ actions.prototype._clickReplay = function (x, y) { switch (selection) { 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(); + case 2: return this._clickReplay_replayRemain(); + case 3: return core.chooseReplayFile(); + case 4: return this._clickReplay_download(); + case 5: return core.ui.closePanel(); } } } @@ -2273,8 +2400,23 @@ actions.prototype._clickReplay_fromLoad = function () { core.ui.drawSLPanel(10 * page + offset); } +actions.prototype._clickReplay_replayRemain = function () { + core.closePanel(); + core.drawText([ + "\t[接续播放录像]该功能允许你播放\r[yellow]两个存档之间的录像\r,常常用于\r[yellow]区域优化\r。\n" + + "例如,有若干个区,已经全部通关;之后重打一区并进行了优化,则可以对剩余区域直接播放录像而无需全部重打。", + "\t[步骤1]请选择一个存档。\n\r[yellow]该存档的坐标必须和当前勇士坐标完全相同。\r\n将尝试从此处开始回放。", + ], function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + 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[系统提示]调试模式下无法下载录像"); + // 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, @@ -2566,7 +2708,6 @@ actions.prototype.exitPaint = function () { core.ui.closePanel(); core.statusBar.image.keyboard.style.opacity = 1; core.statusBar.image.shop.style.opacity = 1; - core.updateStatusBar(); core.drawTip("退出绘图模式"); } diff --git a/libs/control.js b/libs/control.js index d69cfac5..b0f58cdf 100644 --- a/libs/control.js +++ b/libs/control.js @@ -19,10 +19,10 @@ control.prototype._init = function () { 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("tips", true, this._animateFrame_tips); this.registerAnimationFrame("parallelDo", false, this._animationFrame_parallelDo); this.registerAnimationFrame("checkConsoleOpened", true, this._animationFrame_checkConsoleOpened); // --- 注册系统的replay @@ -151,19 +151,6 @@ control.prototype._animationFrame_globalAnimate = function (timestamp) { 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'); @@ -182,14 +169,17 @@ control.prototype._animationFrame_animate = function (timestamp) { return obj.index < obj.animate.frames.length; }); core.status.animateObjs.forEach(function (obj) { - core.maps._drawAnimateFrame(obj.animate, obj.centerX, obj.centerY, obj.index++); + if (obj.hero) { + core.maps._drawAnimateFrame(obj.animate, core.status.heroCenter.px, core.status.heroCenter.py, obj.index++); + } else { + 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; @@ -293,6 +283,40 @@ control.prototype._animationFrame_weather_fog = function () { } } +control.prototype._animateFrame_tips = function (timestamp) { + var tips = core.animateFrame.tips; + if (timestamp - tips.time <= 30) return; + var delta = timestamp - tips.time; + tips.time = timestamp; + if (tips.list.length == 0) return; + + var currentOffset = Math.max(tips.offset - 5, 0), firstOffset = null; + var currList = []; + core.setFont('data', "16px Arial"); + core.setTextAlign('data', 'left'); + core.clearMap('data', 0, 0, this.PIXEL, tips.lastSize * 50); + tips.lastLength = tips.list.length; + + while (tips.list.length > 0) { + var one = tips.list.shift(); + core.ui._drawTip_drawOne(one, currentOffset); + if (one.stage == 1) { + one.opacity += 0.05; + if (one.opacity >= 0.7) one.stage = 2; + } else if (one.stage == 2) { + one.time += delta; + if (one.time >= 2000) one.stage = 3; + } else one.opacity -= 0.05; + if (one.opacity > 0) { + currList.push(one); + if (firstOffset == null) firstOffset = currentOffset; + } + currentOffset += 50; + } + tips.list = currList; + tips.offset = firstOffset || 0; +} + control.prototype._animationFrame_parallelDo = function (timestamp) { core.control.controldata.parallelDo(timestamp); } @@ -334,6 +358,8 @@ control.prototype._showStartAnimate_resetDom = function () { control.prototype._showStartAnimate_finished = function (start, callback) { core.dom.startTop.style.display = 'none'; core.dom.startButtonGroup.style.display = 'block'; + main.selectedButton = null; + main.selectButton(0); if (start) core.startGame(); if (callback) callback(); } @@ -567,6 +593,12 @@ control.prototype.setAutoHeroMove = function (steps) { ////// 设置行走的效果动画 ////// control.prototype.setHeroMoveInterval = function (callback) { if (core.status.heroMoving > 0) return; + if (core.status.replay.speed == 24) { + core.moveOneStep(core.nextX(), core.nextY()); + if (callback) callback(); + return; + } + core.status.heroMoving=1; var toAdd = 1; @@ -742,25 +774,28 @@ control.prototype.turnHero = function(direction) { } ////// 瞬间移动 ////// -control.prototype.moveDirectly = function (destX, destY) { - return this.controldata.moveDirectly(destX, destY); +control.prototype.moveDirectly = function (destX, destY, ignoreSteps) { + return this.controldata.moveDirectly(destX, destY, ignoreSteps); } ////// 尝试瞬间移动 ////// 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)) { + var dirs = [[destX,destY],[destX-1,destY,"right"],[destX,destY-1,"down"],[destX,destY+1,"up"],[destX+1,destY,"left"]]; + var canMoveDirectlyArray = core.canMoveDirectlyArray(dirs); + + for (var i = 0; i < dirs.length; ++i) { + var d = dirs[i], dx = d[0], dy = d[1], dir = d[2]; + if (dx<0 || dx>=core.bigmap.width|| dy<0 || dy>=core.bigmap.height) continue; + if (dir && !core.inArray(canMoveArray[dx][dy],dir)) continue; + if (canMoveDirectlyArray[i]<0) continue; + if (core.control.moveDirectly(dx, dy, canMoveDirectlyArray[i])) { 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"); + return false; } ////// 绘制勇士 ////// @@ -775,20 +810,26 @@ control.prototype.drawHero = function (status, offset) { 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'); + core.status.heroCenter.px = 32 * x + offsetX + 16; + core.status.heroCenter.py = 32 * y + offsetY + 32 - core.material.icons.hero.height / 2; - 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); - }); + if (!core.hasFlag('hideHero')) { + this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (block) { + core.drawImage('hero', block.img, block.heroIcon[block.status]*block.width, + block.heroIcon.loc * block.height, block.width, block.height, + block.posx+(32-block.width)/2, block.posy+32-block.height, block.width, block.height); + }); + } core.control.updateViewport(); + core.setGameCanvasTranslate('hero', 0, 0); } 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, + "width": core.material.icons.hero.width || 32, "height": core.material.icons.hero.height, "heroIcon": heroIconArr[direction], "posx": x * 32 - core.bigmap.offsetX + core.utils.scan[direction].x * offset, @@ -799,6 +840,7 @@ control.prototype._drawHero_getDrawObjs = function (direction, x, y, status, off (core.status.hero.followers||[]).forEach(function (t) { drawObjs.push({ "img": core.material.images.images[t.name], + "width": core.material.images.images[t.name].width/4, "height": core.material.images.images[t.name].height/4, "heroIcon": heroIconArr[t.direction], "posx": 32*t.x - core.bigmap.offsetX + (t.stop?0:core.utils.scan[t.direction].x*offset), @@ -854,6 +896,48 @@ control.prototype.updateViewport = function() { core.relocateCanvas('route', core.status.automaticRoute.offsetX - core.bigmap.offsetX, core.status.automaticRoute.offsetY - core.bigmap.offsetY); } +////// 设置视野范围 ////// +control.prototype.setViewport = function (x, y) { + core.bigmap.offsetX = core.clamp(x, 0, 32 * core.bigmap.width - core.__PIXELS__); + core.bigmap.offsetY = core.clamp(y, 0, 32 * core.bigmap.height - core.__PIXELS__); + this.updateViewport(); + // ------ hero层也需要! + var hero_x = core.clamp((core.getHeroLoc('x') - core.__HALF_SIZE__) * 32, 0, 32*core.bigmap.width-core.__PIXELS__); + var hero_y = core.clamp((core.getHeroLoc('y') - core.__HALF_SIZE__) * 32, 0, 32*core.bigmap.height-core.__PIXELS__); + core.control.setGameCanvasTranslate('hero', hero_x - core.bigmap.offsetX, hero_y - core.bigmap.offsetY); +} + +////// 移动视野范围 ////// +control.prototype.moveViewport = function (steps, time, callback) { + time = time || core.values.moveSpeed || 300; + var step = 0, moveSteps = (steps||[]).filter(function (t) { + return ['up','down','left','right'].indexOf(t)>=0; + }); + var animate=window.setInterval(function() { + if (moveSteps.length==0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + else { + if (core.control._moveViewport_moving(++step, moveSteps)) + step = 0; + } + }, time / 16 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; +} + +control.prototype._moveViewport_moving = function (step, moveSteps) { + var direction = moveSteps[0], scan = core.utils.scan[direction]; + core.setViewport(core.bigmap.offsetX + 2 * scan.x, core.bigmap.offsetY + 2 * scan.y); + if (step == 16) { + moveSteps.shift(); + return true; + } + return false; +} + ////// 获得勇士面对位置的x坐标 ////// control.prototype.nextX = function(n) { if (n == null) n = 1; @@ -918,7 +1002,7 @@ control.prototype.checkBlock = function () { if (damage) { core.status.hero.hp -= damage; core.drawTip("受到"+(core.status.checkBlock.type[loc]||"伤害")+damage+"点"); - this._checkBlock_soundAndAnimate(x, y); + core.drawHeroAnimate("zone"); this._checkBlock_disableQuickShop(); core.status.hero.statistics.extraDamage += damage; if (core.status.hero.hp <= 0) { @@ -932,11 +1016,6 @@ control.prototype.checkBlock = function () { this._checkBlock_ambush(core.status.checkBlock.ambush[loc]); } -control.prototype._checkBlock_soundAndAnimate = function (x,y) { - core.playSound('zone.mp3'); - core.drawAnimate("zone", x, y); -} - control.prototype._checkBlock_disableQuickShop = function () { // 禁用快捷商店 if (core.flags.disableShopOnDamage) { @@ -1049,17 +1128,17 @@ control.prototype.chooseReplayFile = function () { return; } _replay(); - }); + }, null, ".h5route"); } ////// 开始播放 ////// control.prototype.startReplay = function (list) { if (!core.isPlaying()) return; core.status.replay.replaying=true; - core.status.replay.pausing=false; + core.status.replay.pausing=true; core.status.replay.speed=1.0; core.status.replay.toReplay = core.clone(list); - core.status.replay.totalList = core.clone(list); + core.status.replay.totalList = core.status.route.concat(list); core.status.replay.steps = 0; core.status.replay.save = []; core.updateStatusBar(); @@ -1092,16 +1171,24 @@ control.prototype.resumeReplay = function () { core.replay(); } +////// 单步播放 ////// +control.prototype.stepReplay = function () { + 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.replay(true); +} + ////// 加速播放 ////// control.prototype.speedUpReplay = function () { 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==2.5) core.status.replay.speed=3; - else if (core.status.replay.speed==2) core.status.replay.speed=2.5; - else { - core.status.replay.speed = parseInt(10*core.status.replay.speed + 2)/10; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = speeds.length - 2; i >= 0; i--) { + if (speeds[i] <= core.status.replay.speed) { + core.status.replay.speed = speeds[i+1]; + break; + } } core.drawTip("x"+core.status.replay.speed+"倍"); } @@ -1109,15 +1196,13 @@ control.prototype.speedUpReplay = function () { ////// 减速播放 ////// control.prototype.speedDownReplay = function () { if (!core.isPlaying() || !core.isReplaying()) return; - if (core.status.replay.speed==24) core.status.replay.speed=12; - else if (core.status.replay.speed==12) core.status.replay.speed=6; - else if (core.status.replay.speed==6) core.status.replay.speed=3; - else if (core.status.replay.speed==3) core.status.replay.speed=2.5; - else if (core.status.replay.speed==2.5) core.status.replay.speed=2; - else { - core.status.replay.speed = parseInt(10*core.status.replay.speed - 2)/10; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = 1; i <= speeds.length; i++) { + if (speeds[i] >= core.status.replay.speed) { + core.status.replay.speed = speeds[i-1]; + break; + } } - if (core.status.replay.speed<0.2) core.status.replay.speed=0.2; core.drawTip("x"+core.status.replay.speed+"倍"); } @@ -1190,10 +1275,11 @@ control.prototype.bookReplay = function () { if (core.isMoving() || core.status.replay.animate || (core.status.event.id && core.status.event.id != 'viewMaps')) return core.drawTip("请等待当前事件的处理结束"); + if (!core.hasItem('book')) return core.drawTip('你没有怪物手册'); // 从“浏览地图”页面打开 if (core.status.event.id=='viewMaps') - core.status.event.selection = core.status.event.data; + core.status.event.ui = core.status.event.data; core.lockControl(); core.status.event.id='book'; @@ -1212,15 +1298,38 @@ control.prototype.viewMapReplay = function () { core.ui.drawMaps(); } +control.prototype.toolboxReplay = function () { + 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='toolbox'; + core.ui.drawToolbox(); +} + +control.prototype.equipboxReplay = function () { + 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='equipbox'; + core.ui.drawEquipbox(); +} + ////// 是否正在播放录像 ////// control.prototype.isReplaying = function () { return (core.status.replay||{}).replaying; } ////// 回放 ////// -control.prototype.replay = function () { +control.prototype.replay = function (force) { if (!core.isPlaying() || !core.isReplaying() - || core.status.replay.pausing || core.status.replay.animate || core.status.event.id) return; + || core.status.replay.animate || core.status.event.id) return; + if (core.status.replay.pausing && !force) return; if (core.status.replay.toReplay.length==0) return this._replay_finished(); this._replay_save(); @@ -1278,7 +1387,7 @@ control.prototype._replay_finished = function () { control.prototype._replay_save = function () { core.status.replay.steps++; - if (core.status.replay.steps%50==0) { + if (core.status.replay.steps%40==1) { if (core.status.replay.save.length == 30) core.status.replay.save.shift(); core.status.replay.save.push({"data": core.saveData(), "replay": { @@ -1291,6 +1400,7 @@ control.prototype._replay_save = function () { } control.prototype._replay_error = function (action) { + core.ui.closePanel(); core.status.replay.replaying = false; var len = core.status.replay.toReplay.length; var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1); @@ -1316,14 +1426,13 @@ control.prototype._replay_error = function (action) { } control.prototype.__replay_getTimeout = function () { + if (core.status.replay.speed == 24) return 0; return 750 / Math.max(1, core.status.replay.speed); } control.prototype._replayAction_move = function (action) { if (["up","down","left","right"].indexOf(action)<0) return false; - core.moveHero(action, function () { - setTimeout(core.replay); - }); + core.moveHero(action, core.replay); return true; } @@ -1362,6 +1471,10 @@ control.prototype._replayAction_equip = function (action) { var index = ownEquipment.indexOf(equipId), per = core.__SIZE__-1; if (index<0) return false; core.status.route.push(action); + if (core.material.items[equipId].hideInReplay) { + core.loadEquip(equipId, core.replay); + return true; + } core.status.event.data = {"page":Math.floor(index/per)+1, "selectId":null}; index = index%per+per; core.ui.drawEquipbox(index); @@ -1405,21 +1518,20 @@ control.prototype._replayAction_shop = function (action) { if (selections.length == 0) return false; var shop=core.status.shops[shopId]; if (!shop || !shop.visited) return false; - // --- 判定commonEvent - if (shop.commonEvent) { + // --- 判定commonEvent或item + if (shop.commonEvent || shop.item) { 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 topIndex = core.__HALF_SIZE__ - parseInt(choices.length / 2) + (core.status.event.ui.offset || 0); var shopInterval = setInterval(function () { if (!core.actions._clickShop(core.__HALF_SIZE__, topIndex+core.status.event.selection)) { clearInterval(shopInterval); - core.stopReplay(); - core.drawTip("录像文件出错"); + core.control._replay_error(action); return; } if (selections.length==0) { @@ -1489,7 +1601,13 @@ control.prototype.autosave = function (removeLast) { } if (core.status.event.id == 'action') // 事件中的自动存档 core.setFlag("__events__", core.clone(core.status.event.data)); - core.saves.autosave.data = core.saveData(); + if (core.saves.autosave.data == null) { + core.saves.autosave.data = []; + } + core.saves.autosave.data.push(core.saveData()); + if (core.saves.autosave.data.length > core.saves.autosave.max) { + core.saves.autosave.data.shift(); + } core.saves.autosave.updated = true; core.saves.ids[0] = true; core.removeFlag("__events__"); @@ -1503,17 +1621,21 @@ control.prototype.autosave = function (removeLast) { 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); + var autosave = core.saves.autosave; + if (autosave.data == null || !autosave.updated || !autosave.storage) return; + autosave.updated = false; + if (autosave.data.length >= 1) { + core.setLocalForage("autoSave", autosave.data[autosave.data.length - 1]); + } } ////// 实际进行存读档事件 ////// control.prototype.doSL = function (id, type) { switch (type) { case 'save': this._doSL_save(id); break; - case 'load': this._doSL_load(id); break; - case 'replayLoad': this._doSL_replayLoad(id); break; + case 'load': this._doSL_load(id, this._doSL_load_afterGet); break; + case 'replayLoad': this._doSL_load(id, this._doSL_replayLoad_afterGet); break; + case 'replayRemain': this._doSL_load(id, this._doSL_replayRemain_afterGet); break; } } @@ -1542,14 +1664,24 @@ control.prototype._doSL_save = function (id) { return; } -control.prototype._doSL_load = function (id) { +control.prototype._doSL_load = function (id, callback) { if (id == 'autoSave' && core.saves.autosave.data != null) { - this._doSL_load_afterGet(id, core.clone(core.saves.autosave.data)); + var data = core.saves.autosave.data.pop(); + if (core.saves.autosave.data.length == 0) { + core.saves.autosave.data.push(core.clone(data)); + } + callback(id, 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); + if (id == 'autoSave' && data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + return core.control._doSL_load(id, callback); + } + callback(id, data); }, function(err) { main.log(err); alert("无效的存档"); @@ -1579,22 +1711,6 @@ control.prototype._doSL_load_afterGet = function (id, data) { 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) { @@ -1613,6 +1729,34 @@ control.prototype._doSL_replayLoad_afterGet = function (id, data) { } +control.prototype._doSL_replayRemain_afterGet = function (id, data) { + if (!data) return core.drawTip("无效的存档"); + + var route = core.decodeRoute(data.route); + if (core.status.tempRoute) { + var remainRoute = core.subarray(route, core.status.tempRoute); + if (remainRoute == null) + return alert("无法接续播放录像!\n该存档必须是前一个选择的存档的后续内容。"); + delete core.status.tempRoute; + core.ui.closePanel(); + core.startReplay(remainRoute); + core.drawTip("接续播放录像"); + return; + } + else if (data.floorId != core.status.floorId || data.hero.loc.x != core.getHeroLoc('x') || data.hero.loc.y != core.getHeroLoc('y')) + return alert("楼层或坐标不一致!"); + + core.status.tempRoute = route; + core.ui.closePanel(); + core.drawText("\t[步骤2]请选择第二个存档。\n\r[yellow]该存档必须是前一个存档的后续。\r\n将尝试播放到此存档。", function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui.drawSLPanel(10 * page + offset); + }); +} + ////// 同步存档到服务器 ////// control.prototype.syncSave = function (type) { core.ui.drawWaiting("正在同步,请稍后..."); @@ -1716,10 +1860,16 @@ control.prototype.getSave = function (index, callback) { if (index == 0) { // --- 自动存档先从缓存中获取 if (core.saves.autosave.data != null) - callback(core.clone(core.saves.autosave.data)); + callback(core.saves.autosave.data); else { core.getLocalForage("autoSave", null, function(data) { - callback(data); + if (data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + } + callback(core.saves.autosave.data); }, function(err) { main.log(err); callback(null); @@ -1856,8 +2006,8 @@ control.prototype.getStatus = function (name) { ////// 从status中获得属性,如果不存在则从勇士属性中获取 ////// control.prototype.getStatusOrDefault = function (status, name) { if (status && name in status) - return status[name]; - return this.getStatus(name); + return Math.floor(status[name]); + return Math.floor(this.getStatus(name)); } ////// 获得勇士实际属性(增幅后的) ////// @@ -1867,7 +2017,7 @@ control.prototype.getRealStatus = function (name) { ////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 ////// control.prototype.getRealStatusOrDefault = function (status, name) { - return this.getStatusOrDefault(status, name) * this.getBuff(name); + return Math.floor(this.getStatusOrDefault(status, name) * this.getBuff(name)); } ////// 设置某个属性的增幅值 ////// @@ -1961,11 +2111,15 @@ control.prototype.unLockControl = function () { ////// 开启debug模式 ////// control.prototype.debug = function() { core.setFlag('debug', true); - core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n同时,录像将失效,也无法上传成绩。"); + core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n此模式下将无法上传成绩。"); } // ------ 天气,色调,BGM ------ // +control.prototype.getMappedName = function (name) { + return (main.nameMap || {})[name] || name; +} + ////// 更改天气效果 ////// control.prototype.setWeather = function (type, level) { // 非雨雪 @@ -2048,7 +2202,9 @@ control.prototype.setCurtain = function(color, time, callback) { } control.prototype._setCurtain_animate = function (nowColor, color, time, callback) { + time /= Math.max(core.status.replay.speed, 1) var per_time = 10, step = parseInt(time / per_time); + if (step <= 0) step = 1; var animate = setInterval(function() { nowColor = [ (nowColor[0]*(step-1)+color[0])/step, @@ -2088,6 +2244,7 @@ control.prototype.screenFlash = function (color, time, times, callback) { ////// 播放背景音乐 ////// control.prototype.playBgm = function (bgm, startTime) { + bgm = core.getMappedName(bgm); if (main.mode!='play' || !core.material.bgms[bgm]) return; // 如果不允许播放 if (!core.musicStatus.bgmStatus) { @@ -2114,8 +2271,6 @@ 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; @@ -2124,8 +2279,10 @@ control.prototype._playBgm_play = function (bgm, startTime) { if (core.musicStatus.playingBgm) { core.material.bgms[core.musicStatus.playingBgm].pause(); } + // 缓存BGM + core.loader.loadBgm(bgm); // 播放当前BGM - core.material.bgms[bgm].volume = core.musicStatus.volume; + core.material.bgms[bgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; core.material.bgms[bgm].currentTime = startTime || 0; core.material.bgms[bgm].play(); core.musicStatus.playingBgm = bgm; @@ -2152,7 +2309,7 @@ control.prototype.pauseBgm = function () { control.prototype.resumeBgm = function () { if (main.mode!='play')return; try { - core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm); + core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm || main.startBgm); } catch (e) { console.log("无法恢复BGM"); @@ -2182,6 +2339,7 @@ control.prototype.triggerBgm = function () { ////// 播放音频 ////// control.prototype.playSound = function (sound) { + sound = core.getMappedName(sound); if (main.mode!='play' || !core.musicStatus.soundStatus || !core.material.sounds[sound]) return; try { if (core.musicStatus.audioContext != null) { @@ -2197,7 +2355,7 @@ control.prototype.playSound = function (sound) { core.musicStatus.playingSounds[id] = source; } else { - core.material.sounds[sound].volume = core.musicStatus.volume; + core.material.sounds[sound].volume = core.musicStatus.userVolume; core.material.sounds[sound].play(); } } @@ -2241,9 +2399,10 @@ control.prototype.clearStatusBar = function() { } ////// 更新状态栏 ////// -control.prototype.updateStatusBar = function () { +control.prototype.updateStatusBar = function (doNotCheckAutoEvents) { if (!core.isPlaying()) return; this.controldata.updateStatusBar(); + if (!doNotCheckAutoEvents) core.checkAutoEvents(); this._updateStatusBar_setToolboxIcon(); } @@ -2281,6 +2440,7 @@ control.prototype._updateStatusBar_setToolboxIcon = function () { } control.prototype.showStatusBar = function () { + if (main.mode == 'editor') return; if (core.domStyle.showStatusBar) return; var statusItems = core.dom.status; core.domStyle.showStatusBar = true; @@ -2293,9 +2453,12 @@ control.prototype.showStatusBar = function () { } control.prototype.hideStatusBar = function (showToolbox) { + if (main.mode == 'editor') return; + // 如果原本就是隐藏的,则先显示 if (!core.domStyle.showStatusBar) this.showStatusBar(); + if (core.isReplaying()) showToolbox = true; var statusItems = core.dom.status, toolItems = core.dom.tools; core.domStyle.showStatusBar = false; @@ -2318,14 +2481,15 @@ control.prototype.updateHeroIcon = function (name) { var image = core.material.images.hero; // 全身图 - var height = core.material.icons.hero.height; - var ratio = 32 / height, width = 32 * ratio, left = 16-width/2; + var w = core.material.icons.hero.width || 32; + var h = core.material.icons.hero.height || 48; + var ratio = Math.min(w / h, 1), width = 32 * ratio, left = 16 - width/2; var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); canvas.width = 32; canvas.height = 32; - context.drawImage(image, 0, 0, 32, height, left, 0, width, 32); + context.drawImage(image, 0, 0, w, h, left, 0, width, 32); core.statusBar.image.name.src = canvas.toDataURL("image/png"); } @@ -2402,7 +2566,7 @@ control.prototype.setToolbarButton = function (useButton) { } if (useButton == null) useButton = core.domStyle.toolbarBtn; - if (!core.domStyle.isVertical || !core.platform.extendKeyboard) useButton = false; + if (!core.domStyle.isVertical || core.isReplaying()) useButton = false; core.domStyle.toolbarBtn = useButton; if (useButton) { @@ -2585,6 +2749,11 @@ control.prototype._resize_canvas = function (obj) { canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; } + // resize next + main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; + + } control.prototype._resize_statusBar = function (obj) { diff --git a/libs/core.js b/libs/core.js index 3f1165b6..f02616ac 100644 --- a/libs/core.js +++ b/libs/core.js @@ -16,17 +16,15 @@ function core() { 'ground': null, 'items': {}, 'enemys': {}, - 'icons': {} + 'icons': {}, } this.timeout = { - 'tipTimeout': null, 'turnHeroTimeout': null, 'onDownTimeout': null, 'sleepTimeout': null, } this.interval = { 'heroMoveInterval': null, - "tipAnimate": null, 'onDownInterval': null, } this.animateFrame = { @@ -47,6 +45,12 @@ function core() { 'data': null, 'fog': null, }, + "tips": { + 'time': 0, + 'offset': 0, + 'list': [], + 'lastSize': 0, + }, "asyncId": {} } this.musicStatus = { @@ -57,7 +61,8 @@ function core() { 'lastBgm': null, // 上次播放的bgm 'gainNode': null, 'playingSounds': {}, // 正在播放的SE - 'volume': 1.0, // 音量 + 'userVolume': 1.0, // 用户音量 + 'designVolume': 1.0, //设计音量 'cachedBgms': [], // 缓存BGM内容 'cachedBgmCount': 4, // 缓存的bgm数量 } @@ -72,7 +77,6 @@ function core() { 'isChrome': false, // 是否是Chrome 'supportCopy': false, // 是否支持复制到剪切板 'useLocalForage': true, - 'extendKeyboard': false, 'fileInput': null, // FileInput 'fileReader': null, // 是否支持FileReader @@ -102,6 +106,8 @@ function core() { "data": null, "time": 0, "updated": false, + "storage": true, // 是否把自动存档写入文件a + "max": 10, // 自动存档最大回退数 }, "favorite": [], "favoriteName": {} @@ -112,6 +118,7 @@ function core() { // 勇士属性 'hero': {}, + 'heroCenter': {'px': null, 'py': null}, // 当前地图 'floorId': null, @@ -171,6 +178,7 @@ function core() { 'ui': null, 'interval': null, }, + 'autoEvents': [], 'textAttribute': { 'position': "center", "offset": 0, @@ -222,7 +230,9 @@ core.prototype.init = function (coreData, callback) { this._initPlugins(); core.loader._load(function () { - core._afterLoadResources(callback); + core.extensions._load(function () { + core._afterLoadResources(callback); + }); }); } @@ -232,11 +242,39 @@ core.prototype._init_flags = function () { 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; + core.dom.versionLabel.innerText = core.firstData.version; + core.dom.logoLabel.innerText = core.firstData.title; document.title = core.firstData.title + " - HTML5魔塔"; - document.getElementById("startLogo").innerHTML = core.firstData.title; + document.getElementById("startLogo").innerText = core.firstData.title; (core.firstData.shops||[]).forEach(function (t) { core.initStatus.shops[t.id] = t; }); + // 初始化自动事件 + for (var floorId in core.floors) { + var autoEvents = core.floors[floorId].autoEvent || {}; + for (var loc in autoEvents) { + var locs = loc.split(","), x = parseInt(locs[0]), y = parseInt(locs[1]); + for (var index in autoEvents[loc]) { + var autoEvent = core.clone(autoEvents[loc][index]); + if (autoEvent && autoEvent.condition && autoEvent.data) { + autoEvent.floorId = floorId; + autoEvent.x = x; + autoEvent.y = y; + autoEvent.index = index; + autoEvent.symbol = floorId + "@" + x + "@" + y + "@" + index; + autoEvent.condition = core.replaceValue(autoEvent.condition); + autoEvent.data = core.precompile(autoEvent.data); + core.initStatus.autoEvents.push(autoEvent); + } + } + } + } + core.initStatus.autoEvents.sort(function (e1, e2) { + if (e1.priority != e2.priority) return e2.priority - e1.priority; + if (e1.floorId != e2.floorId) return core.floorIds.indexOf(e1.floorId) - core.floorIds.indexOf(e2.floorId); + if (e1.x != e2.x) return e1.x - e2.x; + if (e1.y != e2.y) return e1.y - e2.y; + return e1.index - e2.index; + }) + core.maps._setFloorSize(); // 初始化怪物、道具等 core.material.enemys = core.enemys.getEnemys(); @@ -252,6 +290,8 @@ core.prototype._init_sys_flags = function () { 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.values.moveSpeed = core.getLocalStorage('moveSpeed', core.values.moveSpeed); } core.prototype._init_platform = function () { @@ -268,6 +308,8 @@ core.prototype._init_platform = function () { } core.musicStatus.bgmStatus = core.getLocalStorage('bgmStatus', true); core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); + //新增 userVolume 默认值1.0 + core.musicStatus.userVolume = core.getLocalStorage('userVolume', 1.0); ["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; @@ -276,14 +318,13 @@ core.prototype._init_platform = function () { } }); core.platform.string = core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""; - core.platform.supportCopy = document.queryCommandSupported || document.queryCommandSupported("copy"); + 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); this._init_checkLocalForage(); - core.platform.extendKeyboard = core.getLocalStorage("extendKeyboard", false); if (window.FileReader) { core.platform.fileReader = new FileReader(); core.platform.fileReader.onload = function () { diff --git a/libs/enemys.js b/libs/enemys.js index 51ef8552..e9aaab26 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -19,7 +19,16 @@ enemys.prototype._init = function () { } enemys.prototype.getEnemys = function () { - return core.clone(this.enemys); + var enemys = core.clone(this.enemys); + var enemyInfo = core.getFlag('enemyInfo'); + if (enemyInfo) { + for (var id in enemyInfo) { + for (var name in enemyInfo[id]) { + enemys[id][name] = core.clone(enemyInfo[id][name]); + } + } + } + return enemys; } ////// 判断是否含有某特殊属性 ////// @@ -241,6 +250,11 @@ enemys.prototype._nextCriticals_useBinarySearch = function (enemy, info, number, 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; + // ------ 超大回合数强制使用二分算临界 + // 以避免1攻10e回合,2攻5e回合导致下述循环卡死问题 + if (turn >= 1e6) { // 100w回合以上强制二分计算临界 + return this._nextCriticals_useBinarySearch(enemy, info, number, x, y, floorId); + } var list = [], pre = null; for (var t = turn - 1; t >= 1; t--) { var nextAtk = Math.ceil(mon_hp / t) + mon_def; @@ -293,10 +307,8 @@ enemys.prototype._calDamage = function (enemy, hero, x, y, floorId) { return info.damage; } -////// 更新怪物数据 ////// -enemys.prototype.updateEnemys = function () { - return this.enemydata.updateEnemys(); -} +////// 更新怪物数据。已经不再使用,这里留空进行兼容。 ////// +enemys.prototype.updateEnemys = function () {} ////// 获得当前楼层的怪物列表 ////// enemys.prototype.getCurrentEnemys = function (floorId) { @@ -360,6 +372,14 @@ enemys.prototype._getCurrentEnemys_sort = function (enemys) { } enemys.prototype.hasEnemyLeft = function (enemyId, floorId) { + if (floorId == null) floorId = core.status.floorId; + if (floorId instanceof Array) { + for (var i = 0; i < floorId.length; ++i) { + if (core.hasEnemyLeft(enemyId, floorId[i])) + return true; + } + return false; + } return core.getCurrentEnemys(floorId).filter(function (enemy) { return enemyId == null || enemy.id == enemyId; }).length > 0; diff --git a/libs/events.js b/libs/events.js index dc88d9c9..80c92ccf 100644 --- a/libs/events.js +++ b/libs/events.js @@ -41,7 +41,7 @@ events.prototype.startGame = function (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); + core.resetGame(core.firstData.hero, hard, null, core.clone(core.initStatus.maps)); var nowLoc = core.clone(core.getHeroLoc()); core.setHeroLoc('x', -1); core.setHeroLoc('y', -1); @@ -108,9 +108,9 @@ events.prototype.setInitData = function () { } ////// 游戏获胜事件 ////// -events.prototype.win = function (reason, norank) { - core.status.gameOver = true; - return this.eventdata.win(reason, norank); +events.prototype.win = function (reason, norank, noexit) { + if (!noexit) core.status.gameOver = true; + return this.eventdata.win(reason, norank, noexit); } ////// 游戏失败事件 ////// @@ -121,10 +121,12 @@ events.prototype.lose = function (reason) { ////// 游戏结束 ////// events.prototype.gameOver = function (ending, fromReplay, norank) { - core.clearMap('all'); - core.deleteAllCanvas(); - core.dom.gif2.innerHTML = ""; - core.setWeather(); + if (!core.status.extraEvent) { + core.clearMap('all'); + core.deleteAllCanvas(); + core.dom.gif2.innerHTML = ""; + core.setWeather(); + } core.ui.closePanel(); if (main.isCompetition && ending != null) { @@ -220,12 +222,29 @@ events.prototype._gameOver_confirmDownload = function (ending) { } events.prototype._gameOver_askRate = function (ending) { - if (ending == null) { - core.restart(); + core.ui.closePanel(); + + // 继续接下来的事件 + if (core.status.extraEvent) { + core.status.event = core.status.extraEvent; + delete core.status.extraEvent; + core.lockControl(); + core.doAction(); + return; + } + + if (ending == null) { + core.status.event.selection = 0; + core.ui.drawConfirmBox("你想读取自动存档么?", function () { + core.ui.closePanel(); + core.doSL("autoSave", "load"); + }, function () { + core.ui.closePanel(); + core.restart(); + }); return; } - core.ui.closePanel(); core.ui.drawConfirmBox("恭喜通关本塔,你想进行评分吗?", function () { if (core.platform.isPC) { window.open("/score.php?name=" + core.firstData.name + "&num=10", "_blank"); @@ -297,6 +316,12 @@ events.prototype._trigger = function (x, y) { var block = core.getBlock(x, y); if (block == null) return; block = block.block; + + // 执行该点的脚本 + try { + eval(block.event.script); + } catch (e) { main.log(e); } + if (block.event.trigger) { var noPass = block.event.noPass, trigger = block.event.trigger; if (noPass) core.clearAutomaticRouteNode(x, y); @@ -341,7 +366,7 @@ events.prototype.battle = function (id, x, y, force, callback) { if (!id) return core.clearContinueAutomaticRoute(callback); // 非强制战斗 if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) { - core.drawTip("你打不过此怪物!"); + core.drawTip("你打不过此怪物!", null, true); return core.clearContinueAutomaticRoute(callback); } // 自动存档 @@ -401,8 +426,8 @@ events.prototype._openDoor_check = function (id, x, y, needKey) { var key = id.replace("Door", "Key"); if (!core.hasItem(key)) { if (key != "specialKey") - core.drawTip("你没有" + ((core.material.items[key] || {}).name || "钥匙")); - else core.drawTip("无法开启此门"); + core.drawTip("你没有" + ((core.material.items[key] || {}).name || "钥匙"), null, true); + else core.drawTip("无法开启此门", null, true); core.clearContinueAutomaticRoute(); return false; } @@ -419,20 +444,23 @@ events.prototype._openDoor_animate = function (id, x, y, callback) { var locked = core.status.lockControl; core.lockControl(); core.status.replay.animate = true; + core.removeBlock(x, y); + core.drawImage('event', core.material.images.animates, 0, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32); var state = 0; var animate = window.setInterval(function () { + core.clearMap('event', 32 * x, 32 * y, 32, 32); state++; if (state == 4) { clearInterval(animate); - core.removeBlock(x, y); + delete core.animateFrame.asyncId[animate]; 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); + }, core.status.replay.speed == 24 ? 1 : speed / Math.max(core.status.replay.speed, 1)); + core.animateFrame.asyncId[animate] = true; } ////// 开一个门后触发的事件 ////// @@ -453,8 +481,28 @@ events.prototype.getItem = function (id, num, x, y, callback) { 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); + if (itemCls === 'items' && num == 1) text += core.items.getItemEffectTip(id); core.drawTip(text, id); + + // --- 首次获得道具的提示 + if (!core.hasFlag("__itemHint__")) core.setFlag("__itemHint__", []); + var itemHint = core.getFlag("__itemHint__"); + if (core.flags.itemFirstText && itemHint.indexOf(id) < 0 && itemCls != 'items') { + var hint = core.material.items[id].text || "该道具暂无描述"; + try { + hint = core.replaceText(hint); + } catch (e) {} + if (!core.status.event.id || core.status.event.id=='action') { + core.insertAction("\t["+core.material.items[id].name+","+id+"]" + hint + "\n" + + (itemCls == 'keys' || id == 'greenKey' || id == 'steelKey' ? "(钥匙类道具,遇到对应的门时自动打开)" + : itemCls == 'tools' ? "(消耗类道具,请按T在道具栏使用)" + : itemCls == 'constants' ? "(永久类道具,请按T在道具栏使用)" + : itemCls == 'equips' ? "(装备类道具,请按Q在装备栏进行装备)" : "")); + } + itemHint.push(id); + } + + core.updateStatusBar(); this.afterGetItem(id, x, y, callback); @@ -466,17 +514,31 @@ events.prototype.afterGetItem = function (id, x, y, callback) { ////// 获得面前的物品(轻按) ////// events.prototype.getNextItem = function (noRoute) { - if (core.isMoving() || !core.canMoveHero() || !core.flags.enableGentleClick) return false; + if (core.isMoving() || !core.flags.enableGentleClick) return false; + if (this._canGetNextItem()) return this._getNextItem(null, noRoute); - 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; + var directions = ["up", "down", "left", "right"].filter(function (dir) { + return core.events._canGetNextItem(dir); + }); + return directions.length == 1 ? this._getNextItem(directions[0]) : false; +} + +events.prototype._canGetNextItem = function (direction) { + direction = direction || core.getHeroLoc('direction'); + if (!core.canMoveHero(null, null, direction)) return; + var nx = core.getHeroLoc('x') + core.utils.scan[direction].x; + var ny = core.getHeroLoc('y') + core.utils.scan[direction].y; + var block = core.getBlock(nx, ny); + return block != null && block.block.event.trigger == 'getItem'; +} + +events.prototype._getNextItem = function (direction, noRoute) { + direction = direction || core.getHeroLoc('direction'); + var nx = core.getHeroLoc('x') + core.utils.scan[direction].x; + var ny = core.getHeroLoc('y') + core.utils.scan[direction].y; + if (!noRoute) core.status.route.push("getNext"); + this.getItem(core.getBlockId(nx, ny), 1, nx, ny); + return true; } events.prototype._sys_changeFloor = function (data, callback) { @@ -502,11 +564,13 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback floorId = info.floorId; info.locked = core.status.lockControl; - core.dom.floorNameLabel.innerHTML = core.status.maps[floorId].title; + core.dom.floorNameLabel.innerText = core.status.maps[floorId].title; core.lockControl(); core.stopAutomaticRoute(); core.clearContinueAutomaticRoute(); core.status.replay.animate = true; + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = 'tmp'; this._changeFloor_beforeChange(info, callback); } @@ -545,8 +609,19 @@ events.prototype._changeFloor_getHeroLoc = function (floorId, stair, heroLoc) { if (!heroLoc) heroLoc = core.clone(core.status.hero.loc); if (stair) { + // --- 对称 + if (stair == ':now') + heroLoc = core.clone(core.status.hero.loc); + else if (stair == ':symmetry') { + heroLoc.x = core.bigmap.width - 1 - core.getHeroLoc('x'); + heroLoc.y = core.bigmap.height - 1 - core.getHeroLoc('y'); + } + else if (stair == ':symmetry_x') + heroLoc.x = core.bigmap.width - 1 - core.getHeroLoc('x'); + else if (stair == ':symmetry_y') + heroLoc.y = core.bigmap.height - 1 - core.getHeroLoc('y'); // 检查该层地图的 upFloor & downFloor - if (core.status.maps[floorId][stair]) { + else if (core.status.maps[floorId][stair]) { heroLoc.x = core.status.maps[floorId][stair][0]; heroLoc.y = core.status.maps[floorId][stair][1]; } @@ -627,20 +702,26 @@ events.prototype._sys_passNet = function (data, 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]}); + if (!core.hasItem('shoes')) { + // 血网 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]}); + } } + this.afterPassNet(data.x, data.y, data.event.id); core.updateStatusBar(); } +events.prototype.afterPassNet = function (x, y, id) { + if (this.eventdata.afterPassNet) this.eventdata.afterPassNet(x, y, id); +} + events.prototype._sys_pushBox = function (data, callback) { this.pushBox(data); if (callback) callback(); @@ -715,7 +796,7 @@ events.prototype._sys_action = function (data, callback) { var dir = core.reverseDirection(); var id = data.event.id, toId = (data.event.faceIds || {})[dir]; if (toId && id != toId) { - var number = core.icons.getNumberById(toId); + var number = core.getNumberById(toId); if (number > 0) core.setBlock(number, ex, ey); } @@ -761,11 +842,20 @@ events.prototype.doEvent = function (data, x, y, prefix) { events.prototype.setEvents = function (list, x, y, callback) { var data = core.status.event.data || {}; - if (list) + if (list) { data.list = [{todo: core.clone(list), total: core.clone(list), condition: "false"}]; + // 结束所有正在执行的自动事件 + if (list.length == 0) { + core.status.autoEvents.forEach(function (autoEvent) { + core.autoEventExecuting(autoEvent.symbol, false); + }); + } + } if (x != null) data.x = x; if (y != null) data.y = y; if (callback) data.callback = callback; + if (!data.appendingEvents) data.appendingEvents = []; + if (!data.locStack) data.locStack = []; core.status.event.id = 'action'; core.status.event.data = data; } @@ -785,16 +875,19 @@ events.prototype.startEvents = function (list, x, y, callback) { } ////// 执行当前自定义事件列表中的下一个事件 ////// -events.prototype.doAction = function () { - // 清空boxAnimate和UI层 - core.clearUI(); - clearInterval(core.status.event.interval); - core.status.event.interval = null; +events.prototype.doAction = function (keepUI) { + if (!keepUI) { + // 清空boxAnimate和UI层 + core.clearUI(); + clearInterval(core.status.event.interval); + core.status.event.interval = null; + } // 判定是否执行完毕 if (this._doAction_finishEvents()) return; + var floorId = core.status.event.data.floorId || core.status.floorId; // 当前点坐标和前缀 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 prefix = [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; // 当前要执行的事件 @@ -802,6 +895,7 @@ events.prototype.doAction = function () { core.status.event.data.current = data; if (typeof data == "string") data = {"type": "text", "text": data}; + data.floorId = data.floorId || floorId; core.status.event.data.type = data.type; this.doEvent(data, x, y, prefix); return; @@ -810,6 +904,11 @@ events.prototype.doAction = function () { events.prototype._doAction_finishEvents = function () { // 事件处理完毕 if (core.status.event.data.list.length == 0) { + // 检测并执行延迟自动事件 + if (core.status.event.data.appendingEvents.length > 0) { + this.setEvents(core.status.event.data.appendingEvents.shift()); + return false; + } var callback = core.status.event.data.callback; core.ui.closePanel(); if (callback) callback(); @@ -840,9 +939,14 @@ events.prototype.insertAction = function (action, x, y, callback, addToLast) { // ------ 判定commonEvent var commonEvent = this.getCommonEvent(action); - if (commonEvent instanceof Array) action = commonEvent; + if (commonEvent instanceof Array) { + // 将公共事件视为一个do-while事件插入执行,可被break跳出 + action = [{"type": "dowhile", "condition": "false", "data": commonEvent}]; + } if (!action) return; + action = this.precompile(action); + if (core.status.event.id != 'action') { this.startEvents(action, x, y, callback); } @@ -876,6 +980,183 @@ events.prototype.recoverEvents = function (data) { return false; } +////// 检测自动事件 ////// +events.prototype.checkAutoEvents = function () { + // 只有在无操作或事件流中才能执行自动事件! + if (!core.isPlaying() || (core.status.lockControl && core.status.event.id != 'action')) return; + var todo = [], delay = []; + core.status.autoEvents.forEach(function (autoEvent) { + var symbol = autoEvent.symbol, x = autoEvent.x, y = autoEvent.y, floorId = autoEvent.floorId; + // 不在当前楼层 or 已经执行过 or 正在执行中 + if (autoEvent.currentFloor && floorId != core.status.floorId) return; + if (!autoEvent.multiExecute && core.autoEventExecuted(symbol)) return; + if (core.autoEventExecuting(symbol)) return; + var prefix = floorId + "@" + x + "@" + y; + try { + if (!core.calValue(autoEvent.condition, prefix)) return; + } catch (e) { + return; + } + + core.autoEventExecuting(symbol, true); + core.autoEventExecuted(symbol, true); + + var event = [ + {"type": "function", "function": + "function() { core.pushEventLoc(" + x + ", " + y + ", '" + floorId + "' ); }"}, + // 用do-while(0)包一层防止break影响事件流 + {"type": "dowhile", "condition": "false", "data": autoEvent.data}, + {"type": "function", "function": + "function() { core.popEventLoc(); core.autoEventExecuting('" + symbol + "', false); }"} + ]; + + if (autoEvent.delayExecute) + delay.push(event); + else + core.push(todo, event); + }); + + if (todo.length == 0 && delay.length == 0) return; + + if (core.status.event.id == 'action' || todo.length > 0) { + core.insertAction(todo); + core.push(core.status.event.data.appendingEvents, delay); + } else { + core.insertAction(delay[0]); + if (delay.length > 0) { + core.insertAction(delay.slice(1)); + } + } + +} + +events.prototype.autoEventExecuting = function (symbol, value) { + var name = '_executing_autoEvent_' + symbol; + if (value == null) return core.hasFlag(name); + else core.setFlag(name, value || null); +} + +events.prototype.autoEventExecuted = function (symbol, value) { + var name = '_executed_autoEvent_' + symbol; + if (value == null) return core.hasFlag(name); + else core.setFlag(name, value || null); +} + +events.prototype.pushEventLoc = function (x, y, floorId) { + if (core.status.event.id != 'action') return; + core.status.event.data.locStack.push({ + x: core.status.event.data.x, + y: core.status.event.data.y, + floorId: core.status.event.data.floorId + }); + core.status.event.data.x = x; + core.status.event.data.y = y; + core.status.event.data.floorId = floorId; +} + +events.prototype.popEventLoc = function () { + if (core.status.event.id != 'action') return; + var loc = core.status.event.data.locStack.shift(); + if (loc) { + core.status.event.data.x = loc.x; + core.status.event.data.y = loc.y; + core.status.event.data.floorId = loc.floorId; + } +} + +events.prototype.precompile = function (data) { + var array = this.__precompile_getArray(); + if (typeof data == 'string') { + return this.__precompile_text(data); + } + if (data instanceof Array) { + for (var i = 0; i < data.length; ++i) { + data[i] = this.precompile(data[i]); + } + return data; + } + if (data && data.type) { + if (this["_precompile_" + data.type]) { + data = this["_precompile_" + data.type](data); + } + if (array.texts.indexOf(data.type) >= 0) { + data.text = this.__precompile_text(data.text); + } + if (array.locs.indexOf(data.type) >= 0) { + data.loc = this.__precompile_array(data.loc); + } + if (array.values.indexOf(data.type) >= 0) { + data.value = core.replaceValue(data.value); + } + if (array.uievents.indexOf(data.type) >= 0) { + data.x = core.replaceValue(data.x); + data.y = core.replaceValue(data.y); + data.width = core.replaceValue(data.width); + data.height = core.replaceValue(data.height); + } + if (data.type in array.others) { + array.others[data.type].forEach(function (field) { + data[field] = core.replaceValue(data[field]); + }) + } + } + return data; +} + +events.prototype.__precompile_getArray = function () { + var texts = [ + "text", "autoText", "scrollText", "tip", "textImage", "input", "input2", + "choices", "confirm", "fillText", "fillBoldText", "drawTextContent" + ]; + var locs = [ + "show", "hide", "setBlock", "showFloorImg", "hideFloorImg", "showBgFgMap", + "hideBgFgMap", "setBgFgBlock", "animate", "setViewport", "move", "jumoHero", + "changeFloor", "changePos", "showTextImage", "showGif", "openDoor", + "closeDoor", "battle", "trigger", "insert" + ]; + var values = [ + "setValue", "setValue2", "addValue", "setEnemy", "setFloor", "setGlobalValue", + ]; + var uievents = [ + "clearMap", "fillText", "fillBoldText", "fillRect", "strokeRect", "strokeCircle", + "drawIcon", "drawSelector", "drawBackground", + ]; + var others = { + "strokeCircle": ["r"], + "drawLine": ["x1", "y1", "x2", "y2"], + "drawArrow": ["x1", "y1", "x2", "y2"], + "drawImage": ["x", "y", "w", "h", "x1", "y1", "w1", "h1"], + "drawTextContent": ["left", "top"], + }; + return { + texts: texts, + locs: locs, + values: values, + uievents: uievents, + others: others + }; +} + +events.prototype.__precompile_text = function (text) { + if (typeof text != 'string') return text; + return text.replace(/\${(.*?)}/g, function (word, value) { + return "${" + core.replaceValue(value) + "}"; + }); +} + +events.prototype.__precompile_array = function (value) { + if (typeof value == 'string') { + value = core.replaceValue(value); + return value; + } + if (value instanceof Array) { + for (var i = 0; i < value.length; ++i) { + value[i] = this.__precompile_array(value[i]); + } + } + return value; +} + // ------ 样板提供的的自定义事件 ------ // events.prototype.__action_checkReplaying = function () { @@ -936,7 +1217,7 @@ events.prototype._action_comment = function (data, x, y, prefix) { } events.prototype._action_setText = function (data, x, y, prefix) { - ["position", "offset", "align", "bold", "titlefont", "textfont", "time"].forEach(function (t) { + ["position", "offset", "align", "bold", "titlefont", "textfont", "time", "interval"].forEach(function (t) { if (data[t] != null) core.status.textAttribute[t] = data[t]; }); ["background", "title", "text"].forEach(function (t) { @@ -945,9 +1226,10 @@ events.prototype._action_setText = function (data, x, y, prefix) { core.status.textAttribute[t] = data[t]; } if (t == 'background') { - var img = core.material.images.images[data[t]]; + var name = core.getMappedName(data[t]); + var img = core.material.images.images[name]; if (img && img.width == 192 && img.height == 128) { - core.status.textAttribute[t] = data[t]; + core.status.textAttribute[t] = name; } } }); @@ -962,7 +1244,7 @@ events.prototype._action_tip = function (data, x, y, prefix) { 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)) { + if (data.time > 0 && data.floorId == core.status.floorId) { this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, 'show', data.time); } else { @@ -975,7 +1257,7 @@ events.prototype._action_show = function (data, x, y, prefix) { 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)) { + if (data.time > 0 && data.floorId == core.status.floorId) { data.loc.forEach(function (t) { core.hideBlock(t[0], t[1], data.floorId); }); @@ -990,8 +1272,10 @@ events.prototype._action_hide = function (data, x, y, prefix) { } 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); + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.setBlock(data.number, t[0], t[1], data.floorId); + }); core.doAction(); } @@ -1012,8 +1296,10 @@ events.prototype._action_hideBgFgMap = function (data, x, y, prefix) { } 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); + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.setBgFgBlock(data.name, data.number, t[0], t[1], data.floorId); + }); core.doAction(); } @@ -1028,9 +1314,27 @@ events.prototype._action_unfollow = function (data, x, y, prefix) { } 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]); + if (data.loc == 'hero') { + this.__action_doAsyncFunc(data.async, core.drawHeroAnimate, data.name); + } 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_setViewport = function (data, x, y, prefix) { + if (data.loc == null) { + core.drawHero(); + } + else { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.setViewport(32 * loc[0], 32 * loc[1]); + } + core.doAction(); +} + +events.prototype._action_moveViewport = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.moveViewport, data.steps, data.time); } events.prototype._action_move = function (data, x, y, prefix) { @@ -1048,6 +1352,12 @@ events.prototype._action_jump = function (data, x, y, prefix) { this.__action_doAsyncFunc(data.async, core.jumpBlock, from[0], from[1], to[0], to[1], data.time, data.keep); } +events.prototype._precompile_jump = function (data) { + data.from = this.__precompile_array(data.from); + data.to = this.__precompile_array(data.to); + return data; +} + 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); @@ -1056,7 +1366,7 @@ events.prototype._action_jumpHero = function (data, x, y, prefix) { events.prototype._action_changeFloor = function (data, x, y, prefix) { var loc = this.__action_getHeroLoc(data.loc, prefix); var heroLoc = {x: loc[0], y: loc[1], direction: data.direction}; - core.changeFloor(data.floorId || core.status.floorId, null, heroLoc, data.time, core.doAction); + core.changeFloor(data.floorId, null, heroLoc, data.time, core.doAction); } events.prototype._action_changePos = function (data, x, y, prefix) { @@ -1075,11 +1385,17 @@ events.prototype._action_showImage = function (data, x, y, prefix) { data.code, data.image, data.sloc, data.loc, data.opacity, data.time); } +events.prototype._precompile_showImage = function (data) { + data.sloc = this.__precompile_array(data.sloc); + data.loc = this.__precompile_array(data.loc); + return data; +} + 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, core.showImage, - data.code, core.ui.textImage(data.text), loc[0], loc[1], 100, 100, data.opacity, data.time); + data.code, core.ui.textImage(data.text), null, loc, data.opacity, data.time); } events.prototype._action_hideImage = function (data, x, y, prefix) { @@ -1098,6 +1414,11 @@ events.prototype._action_moveImage = function (data, x, y, prefix) { this.__action_doAsyncFunc(data.async, core.moveImage, data.code, data.to, data.opacity, data.time); } +events.prototype._precompile_moveImage = function (data) { + data.to = this.__precompile_array(data.to); + return data; +} + events.prototype._action_setFg = function (data, x, y, prefix) { return this._action_setCurtain(data, x, y, prefix); } @@ -1130,9 +1451,9 @@ events.prototype._action_setWeather = function (data, x, y, prefix) { 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; + var floorId = data.floorId; if (floorId == core.status.floorId) { - core.openDoor(loc[0], loc[1], data.needKey, core.doAction); + this.__action_doAsyncFunc(data.async, core.openDoor, loc[0], loc[1], data.needKey); } else { core.removeBlock(loc[0], loc[1], floorId); @@ -1142,7 +1463,7 @@ events.prototype._action_openDoor = function (data, x, y, prefix) { 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); + this.__action_doAsyncFunc(data.async, core.closeDoor, loc[0], loc[1], data.id); } events.prototype._action_useItem = function (data, x, y, prefix) { @@ -1156,6 +1477,16 @@ events.prototype._action_useItem = function (data, x, y, prefix) { } } +events.prototype._action_loadEquip = function (data, x, y, prefix) { + core.loadEquip(data.id); + core.doAction(); +} + +events.prototype._action_unloadEquip = function (data, x, y, prefix) { + core.unloadEquip(data.pos); + core.doAction(); +} + events.prototype._action_openShop = function (data, x, y, prefix) { core.status.shops[data.id].visited = true; this.setEvents([]); @@ -1171,7 +1502,17 @@ events.prototype._action_disableShop = function (data, x, y, prefix) { } events.prototype._action_battle = function (data, x, y, prefix) { - this.battle(data.id, null, null, true, core.doAction); + if (data.id) { + this.battle(data.id, null, null, true, core.doAction); + } + else { + if (data.floorId != core.status.floorId) { + core.doAction(); + return; + } + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.battle(null, loc[0], loc[1], true, core.doAction); + } } events.prototype._action_trigger = function (data, x, y, prefix) { @@ -1179,9 +1520,9 @@ events.prototype._action_trigger = function (data, 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); + this.setEvents(data.keep ? null : [], block.x, block.y); if (block.event.trigger == 'action') - this.setEvents(block.event.data); + this.insertAction(block.event.data); else { core.doSystemEvent(block.event.trigger, block, core.doAction); return; @@ -1207,7 +1548,7 @@ events.prototype._action_insert = function (data, x, y, prefix) { else { var loc = this.__action_getLoc(data.loc, x, y, prefix); core.setFlag('arg0', loc); - var floorId = data.floorId || core.status.floorId; + var floorId = data.floorId; var which = data.which || "events"; var event = (core.floors[floorId][which]||[])[loc[0] + "," + loc[1]]; if (event) this.insertAction(event.data || event); @@ -1217,6 +1558,7 @@ events.prototype._action_insert = function (data, x, y, prefix) { events.prototype._action_playBgm = function (data, x, y, prefix) { core.playBgm(data.name); + core.setFlag("__bgm__", data.keep ? data.name : null); core.doAction(); } @@ -1259,6 +1601,15 @@ events.prototype._action_setVolume = function (data, x, y, prefix) { events.prototype._action_setValue = function (data, x, y, prefix) { this.setValue(data.name, data.value, prefix); + if (!data.norefresh) { + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + } else { + core.updateStatusBar(); + } + } core.doAction(); } @@ -1268,6 +1619,20 @@ events.prototype._action_setValue2 = function (data, x, y, prefix) { events.prototype._action_addValue = function (data, x, y, prefix) { this.addValue(data.name, data.value, prefix); + if (!data.norefresh) { + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + } else { + core.updateStatusBar(); + } + } + core.doAction(); +} + +events.prototype._action_setEnemy = function (data, x, y, prefix) { + this.setEnemy(data.id, data.name, data.value, prefix); core.doAction(); } @@ -1326,10 +1691,8 @@ events.prototype.__action_getInput = function (hint, isText, callback) { callback(value); } catch (e) { - main.log(e); - core.stopReplay(); - core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); - core.doAction(); + core.control._replay_error(action); + return; } } else { @@ -1345,6 +1708,13 @@ events.prototype._action_if = function (data, x, y, prefix) { core.doAction(); } +events.prototype._precompile_if = function (data) { + data.condition = core.replaceValue(data.condition); + data["true"] = this.precompile(data["true"]); + data["false"] = this.precompile(data["false"]); + return data; +} + events.prototype._action_switch = function (data, x, y, prefix) { var key = core.calValue(data.condition, prefix) var list = []; @@ -1360,7 +1730,21 @@ events.prototype._action_switch = function (data, x, y, prefix) { core.doAction(); } +events.prototype._precompile_switch = function (data) { + data.condition = core.replaceValue(data.condition); + for (var i = 0; i < data.caseList.length; i++) { + data.caseList[i]["case"] = core.replaceValue(data.caseList[i]["case"]); + data.caseList[i].action = this.precompile(data.caseList[i].action); + } + return data; +} + events.prototype._action_choices = function (data, x, y, prefix) { + data.choices = data.choices.filter(function (x) { + if (x.condition == null || x.condition == '') return true; + try { return core.calValue(x.condition, prefix); } catch (e) { return true; } + }) + if (data.choices.length == 0) return this.doAction(); if (core.isReplaying()) { var action = core.status.replay.toReplay.shift(), index; // --- 忽略可能的turn事件 @@ -1371,19 +1755,26 @@ events.prototype._action_choices = function (data, x, y, prefix) { core.status.route.push("choices:" + index); core.insertAction(data.choices[index].action); core.doAction(); - }, 750 / Math.max(1, core.status.replay.speed)) + }, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed)) } else { - main.log("录像文件出错!当前需要一个 choices: 项,实际为 " + action); - core.stopReplay(); - core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); - core.doAction(); + core.control._replay_error(action); return; } } core.ui.drawChoices(data.text, data.choices); } +events.prototype._precompile_choices = function (data) { + if (!(data.choices instanceof Array)) return data; + for (var i = 0; i < data.choices.length; ++i) { + data.choices[i].condition = core.replaceValue(data.choices[i].condition); + data.choices[i].text = this.__precompile_text(data.choices[i].text); + data.choices[i].action = this.precompile(data.choices[i].action); + } + return data; +} + 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()) { @@ -1397,13 +1788,10 @@ events.prototype._action_confirm = function (data, x, y, prefix) { if (index == 0) core.insertAction(data.yes); else core.insertAction(data.no); core.doAction(); - }, 750 / Math.max(1, core.status.replay.speed)) + }, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed)) } else { - main.log("录像文件出错!当前需要一个 choices: 项,实际为 " + action); - core.stopReplay(); - core.insertAction(["录像文件出错,请在控制台查看报错信息。", {"type": "exit"}]); - core.doAction(); + core.control._replay_error(action); return; } } @@ -1413,6 +1801,12 @@ events.prototype._action_confirm = function (data, x, y, prefix) { core.ui.drawConfirmBox(data.text); } +events.prototype._precompile_confirm = function (data) { + data.yes = this.precompile(data.yes); + data.no = this.precompile(data.no); + return data; +} + events.prototype._action_while = function (data, x, y, prefix) { if (core.calValue(data.condition, prefix)) { core.unshift(core.status.event.data.list, @@ -1422,6 +1816,12 @@ events.prototype._action_while = function (data, x, y, prefix) { core.doAction(); } +events.prototype._precompile_while = function (data) { + data.condition = core.replaceValue(data.condition); + data.data = this.precompile(data.data); + return data; +} + events.prototype._action_dowhile = function (data, x, y, prefix) { core.unshift(core.status.event.data.list, {"todo": core.clone(data.data), "total": core.clone(data.data), "condition": data.condition} @@ -1429,6 +1829,12 @@ events.prototype._action_dowhile = function (data, x, y, prefix) { core.doAction(); } +events.prototype._precompile_dowhile = function (data) { + data.condition = core.replaceValue(data.condition); + data.data = this.precompile(data.data); + return data; +} + events.prototype._action_break = function (data, x, y, prefix) { core.status.event.data.list.shift(); core.doAction(); @@ -1445,11 +1851,11 @@ events.prototype._action_continue = function (data, x, y, prefix) { } events.prototype._action_win = function (data, x, y, prefix) { - this.win(data.reason, data.norank); + this.win(core.replaceText(data.reason), data.norank, data.noexit); } events.prototype._action_lose = function (data, x, y, prefix) { - this.lose(data.reason); + this.lose(core.replaceText(data.reason)); } events.prototype._action_restart = function (data, x, y, prefix) { @@ -1484,9 +1890,15 @@ events.prototype._action_hideStatusBar = function (data, x, y, prefix) { core.doAction(); } -events.prototype._action_updateEnemys = function (data, x, y, prefix) { - core.enemys.updateEnemys(); - core.updateStatusBar(); +events.prototype._action_showHero = function (data, x, y, prefix) { + core.removeFlag('hideHero'); + core.drawHero(); + core.doAction(); +} + +events.prototype._action_hideHero = function (data, x, y, prefix) { + core.setFlag('hideHero', true); + core.drawHero(); core.doAction(); } @@ -1508,6 +1920,7 @@ events.prototype._action_wait = function (data, x, y, prefix) { var value = parseInt(code.substring(6)); core.status.route.push("input:" + value); this.__action_wait_getValue(value); + this.__action_wait_afterGet(data); } else { main.log("录像文件出错!当前需要一个 input: 项,实际为 " + code); @@ -1542,6 +1955,42 @@ events.prototype.__action_wait_getValue = function (value) { } } +events.prototype.__action_wait_afterGet = function (data) { + if (!data.data) return; + var todo = []; + data.data.forEach(function (one) { + if (one["case"] == "keyboard" && core.getFlag("type") == 0) { + if (one["keycode"] == core.getFlag("keycode", 0)) { + core.push(todo, one.action); + } + } + if (one["case"] == "mouse" && one.px instanceof Array + && one.py instanceof Array && core.getFlag("type") == 1) { + var pxmin = core.calValue(one.px[0]); + var pxmax = core.calValue(one.px[1]); + var pymin = core.calValue(one.py[0]); + var pymax = core.calValue(one.py[1]); + var px = core.getFlag("px", 0), py = core.getFlag("py", 0); + if (px >= pxmin && px <= pxmax && py >= pymin && py <= pymax) { + core.push(todo, one.action); + } + } + }) + if (todo.length > 0) + core.insertAction(todo); +} + +events.prototype._precompile_wait = function (data) { + if (data.data) { + data.data.forEach(function (v) { + if (v.px) v.px = this.__precompile_array(v.px); + if (v.py) v.py = this.__precompile_array(v.py); + v.action = this.precompile(v.action); + }, this); + } + return data; +} + events.prototype._action_waitAsync = function (data, x, y, prefix) { var test = window.setInterval(function () { if (!core.hasAsync()) { @@ -1585,7 +2034,7 @@ events.prototype._action_callSave = function (data, x, y, prefix) { events.prototype._action_autoSave = function (data, x, y, prefix) { core.autosave(); - core.drawTip("已自动存档"); + if (!data.nohint) core.drawTip("已自动存档"); core.doAction(); } @@ -1602,6 +2051,114 @@ events.prototype._action_exit = function (data, x, y, prefix) { core.doAction(); } +events.prototype._action_previewUI = function (data, x, y, prefix) { + this.insertAction(data.action); + core.doAction(); +} + +events.prototype._precompile_previewUI = function (data) { + data.action = this.precompile(data.action); + return data; +} + +events.prototype.__action_doUIEvent = function (data) { + this.__action_doUIEvent_doOne(data); + var current = core.status.event.data.list[0]; + while (current.todo.length > 0) { + data = current.todo[0]; + if (this.__action_doUIEvent_doOne(current.todo[0])) + current.todo.shift(); + else break; + } + core.doAction(); +} + +events.prototype.__action_doUIEvent_doOne = function (data) { + if (core.ui['_uievent_' + data.type]) { + core.ui['_uievent_' + data.type](data); + return true; + } + return false; +} + +events.prototype._action_clearMap = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillText = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillBoldText = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillRect = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillPolygon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._precompile_fillPolygon = function (data) { + data.nodes = this.__precompile_array(data.nodes); + return data; +} + +events.prototype._action_strokeRect = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_strokePolygon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._precompile_strokePolygon = function (data) { + data.nodes = this.__precompile_array(data.nodes); + return data; +} + +events.prototype._action_fillCircle = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_strokeCircle = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawLine = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawArrow = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_setAttribute = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawImage = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawIcon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawSelector = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawBackground = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawTextContent = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + // ------ 点击状态栏图标所进行的一些操作 ------ // ////// 判断当前能否进入某个事件 ////// @@ -1688,6 +2245,25 @@ events.prototype.openToolbox = function (fromUserAction) { ////// 点击快捷商店按钮时的打开操作 ////// events.prototype.openQuickShop = function (fromUserAction) { if (core.isReplaying()) return; + + if (Object.keys(core.status.shops).length == 0) { + core.drawTip("本塔没有快捷商店!"); + return; + } + + // --- 如果只有一个商店,则直接打开之 + if (Object.keys(core.status.shops).length == 1) { + var shopId = Object.keys(core.status.shops)[0]; + if (core.status.event.id != null || !this._checkStatus('shop', false)) return; + var reason = core.events.canUseQuickShop(shopId); + if (!core.flags.enableDisabledShop && reason) { + core.drawText(reason); + return; + } + core.events.openShop(shopId, true); + return; + } + if (!this._checkStatus('selectShop', fromUserAction)) return; core.ui.drawQuickShop(); } @@ -1747,8 +2323,8 @@ events.prototype.hasAsync = function () { ////// 跟随 ////// 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) { + name = core.getMappedName(name); + if (core.material.images.images[name]) { core.status.hero.followers.push({"name": name}); core.gatherFollowers(); core.clearMap('hero'); @@ -1763,6 +2339,7 @@ events.prototype.unfollow = function (name) { core.status.hero.followers = []; } else { + name = core.getMappedName(name); 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); @@ -1783,17 +2360,12 @@ events.prototype.setValue = function (name, value, prefix, add) { this._setValue_setItem(name, value); this._setValue_setFlag(name, value); this._setValue_setSwitch(name, value, prefix); - core.updateStatusBar(); + this._setValue_setGlobal(name, value); } 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) { @@ -1813,6 +2385,11 @@ events.prototype._setValue_setSwitch = function (name, value, prefix) { core.setFlag((prefix || ":f@x@y") + "@" + name.substring(7), value); } +events.prototype._setValue_setGlobal = function (name, value) { + if (name.indexOf("global:") !== 0) return; + core.setGlobal(name.substring(7), value); +} + ////// 数值增减 ////// events.prototype.addValue = function (name, value, prefix) { this.setValue(name, value, prefix, true); @@ -1828,9 +2405,22 @@ events.prototype.doEffect = function (effect, need, times) { }); } +////// 设置一个怪物属性 ////// +events.prototype.setEnemy = function (id, name, value, prefix) { + if (!core.hasFlag('enemyInfo')) { + core.setFlag('enemyInfo', {}); + } + var enemyInfo = core.getFlag('enemyInfo'); + if (!enemyInfo[id]) enemyInfo[id] = {}; + value = core.calValue(value, prefix); + enemyInfo[id][name] = value; + (core.material.enemys[id]||{})[name] = core.clone(value); + core.updateStatusBar(); +} + ////// 设置楼层属性 ////// events.prototype.setFloorInfo = function (name, value, floorId, prefix) { - floorId = floorId || data.floorId; + floorId = floorId || core.status.floorId; core.status.maps[floorId][name] = core.calValue(value, prefix); core.updateStatusBar(); } @@ -1857,12 +2447,14 @@ events.prototype.setGlobalFlag = function (name, value) { core.flags[name] = value; core.setFlag("globalFlags", flags); core.resize(); + if (name == 'blurFg') + core.drawMap(); } 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) { + || core.material.icons.animates[id] == null || core.getBlock(x, y) != null) { if (callback) callback(); return; } @@ -1881,13 +2473,16 @@ events.prototype.closeDoor = function (x, y, id, callback) { } core.clearMap('event', 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); + }, core.status.replay.speed == 24 ? 1 : speed / Math.max(core.status.replay.speed, 1)); core.animateFrame.asyncId[animate] = true; } ////// 显示图片 ////// events.prototype.showImage = function (code, image, sloc, loc, opacityVal, time, callback) { - if (typeof image == 'string') image = core.material.images.images[image]; + if (typeof image == 'string') { + image = core.getMappedName(image); + image = core.material.images.images[image]; + } if (!image) { if (callback) callback(); return; @@ -1952,12 +2547,14 @@ events.prototype.moveImage = function (code, to, opacityVal, time, callback) { var opacity = parseFloat(canvas.style.opacity), toOpacity = getOrDefault(opacityVal, opacity); this._moveImage_moving(name, { - fromX: fromX, fromY: fromY, toX: toX, toY: toY, opacity: opacity, toOpacity: toOpacity, time: time + fromX: fromX, fromY: fromY, toX: toX, toY: toY, opacity: opacity, toOpacity: toOpacity, + time: time / Math.max(core.status.replay.speed, 1) }, callback) } events.prototype._moveImage_moving = function (name, moveInfo, callback) { var per_time = 10, step = 0, steps = parseInt(moveInfo.time / 10); + if (steps <= 0) steps = 1; 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; @@ -1980,6 +2577,7 @@ events.prototype._moveImage_moving = function (name, moveInfo, callback) { ////// 绘制或取消一张gif图片 ////// events.prototype.showGif = function (name, x, y) { + name = core.getMappedName(name); var image = core.material.images.images[name]; if (image) { var gif = new Image(); @@ -1999,17 +2597,19 @@ events.prototype.showGif = function (name, x, y) { ////// 淡入淡出音乐 ////// events.prototype.setVolume = function (value, time, callback) { var set = function (value) { - core.musicStatus.volume = value; + core.musicStatus.designVolume = value; if (core.musicStatus.playingBgm) - core.material.bgms[core.musicStatus.playingBgm].volume = value; + core.material.bgms[core.musicStatus.playingBgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; } if (!time || time < 100) { set(value); if (callback) callback(); return; } - var currVolume = core.musicStatus.volume; + var currVolume = core.musicStatus.designVolume; + time /= Math.max(core.status.replay.speed, 1); var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) steps = 1; var fade = setInterval(function () { step++; set(currVolume + (value - currVolume) * step / steps); @@ -2030,6 +2630,7 @@ events.prototype.vibrate = function (time, callback) { } if (!time || time < 1000) time = 1000; // --- 将time调整为500的倍数(上整),不然会出错 + time /= Math.max(core.status.replay.speed, 1) time = Math.ceil(time / 500) * 500; var shakeInfo = {duration: time * 3 / 50, speed: 5, power: 5, direction: 1, shake: 0}; var animate = setInterval(function () { @@ -2082,7 +2683,7 @@ events.prototype.eventMoveHero = function(steps, time, callback) { if (core.events._eventMoveHero_moving(++step, moveSteps)) step = 0; } - }, time / 8 / core.status.replay.speed); + }, core.status.replay.speed == 24 ? 1 : time / 8 / core.status.replay.speed); core.animateFrame.asyncId[animate] = true; } @@ -2120,6 +2721,7 @@ events.prototype.jumpHero = function (ex, ey, time, callback) { 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.width = core.material.icons.hero.width || 32; jumpInfo.height = core.material.icons.hero.height; this._jumpHero_doJump(jumpInfo, callback); @@ -2139,12 +2741,14 @@ events.prototype._jumpHero_doJump = function (jumpInfo, callback) { events.prototype._jumpHero_jumping = function (jumpInfo) { core.clearMap('hero'); core.maps.__updateJumpInfo(jumpInfo); - var nowx = jumpInfo.px, nowy = jumpInfo.py, height = jumpInfo.height; + var nowx = jumpInfo.px, nowy = jumpInfo.py, width = jumpInfo.width || 32, 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); + core.drawImage('hero', core.material.images.hero, jumpInfo.icon.stop, jumpInfo.icon.loc * height, width, height, + nowx + (32 - width) / 2 - core.bigmap.offsetX, nowy + 32-height - core.bigmap.offsetY, width, height); + core.status.heroCenter.px = nowx + 16; + core.status.heroCenter.py = nowy + 32 - height / 2; } events.prototype._jumpHero_finished = function (animate, ex, ey, callback) { @@ -2162,9 +2766,10 @@ events.prototype.openShop = function (shopId, needVisited) { shop.times = shop.times || 0; if (shop.commonTimes) shop.times = core.getFlag('commonTimes', 0); if (needVisited && !shop.visited) { - if (!core.flags.enableDisabledShop || shop.commonEvent) { + if (!core.flags.enableDisabledShop || shop.commonEvent || shop.item) { if (shop.times == 0) core.drawTip("该项尚未开启"); else core.drawTip("该项已失效"); + core.ui.closePanel(); return; } else { @@ -2173,8 +2778,15 @@ events.prototype.openShop = function (shopId, needVisited) { } else shop.visited = true; - // --- 商店 - if (shop.commonEvent) { + if (shop.item) { + core.status.route.push("shop:" + shopId + ":0"); + if (core.openItemShop) { + core.openItemShop(shopId); + } else { + core.insertAction("道具商店插件不存在!请检查是否存在该插件!"); + } + return; + } else if (shop.commonEvent) { core.status.route.push("shop:"+shopId+":0"); core.insertAction({"type": "insert", "name": shop.commonEvent, "args": shop.args}); return; @@ -2230,15 +2842,15 @@ events.prototype.canUseQuickShop = function (shopId) { ////// 设置角色行走图 ////// events.prototype.setHeroIcon = function (name, noDraw) { + name = core.getMappedName(name); var img = core.material.images.images[name]; - if (!img || img.width != 128) return; + if (!img) 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; + core.material.images.hero = img; + core.material.icons.hero.width = img.width / 4; + core.material.icons.hero.height = img.height / 4; + core.control.updateHeroIcon(name); + if (!noDraw) core.drawHero(); } ////// 检查升级事件 ////// diff --git a/libs/extensions.js b/libs/extensions.js new file mode 100644 index 00000000..d7e6fba5 --- /dev/null +++ b/libs/extensions.js @@ -0,0 +1,50 @@ +/* +extensions.js:负责拓展插件 + */ + +"use strict"; + +function extensions() { + +} + +extensions.prototype._load = function (callback) { + if (main.replayChecking) return callback(); + if (!window.fs) { + this._loadJs('_server/fs.js', function () { + core.extensions._listExtensions(callback); + }, callback); + } else this._listExtensions(callback); +} + +extensions.prototype._loadJs = function (file, callback, onerror) { + var script = document.createElement('script'); + script.src = file + '?v=' + main.version; + script.onload = callback; + script.onerror = onerror; + main.dom.body.appendChild(script); +} + +extensions.prototype._listExtensions = function (callback) { + if (!window.fs) return callback(); + fs.readdir('extensions', function (error, data) { + if (error || !(data instanceof Array)) return callback(); + var list = []; + data.forEach(function (name) { + if (/^[\w.-]+\.js$/.test(name)) { + list.push(name); + } + }); + list.sort(); + core.extensions._loadExtensions(list, callback); + }); +} + +extensions.prototype._loadExtensions = function (list, callback) { + var i = 0; + var load = function () { + if (i == list.length) return callback(); + core.extensions._loadJs('extensions/' + list[i++], load, load); + } + load(); +} diff --git a/libs/items.js b/libs/items.js index 6c13e824..4f0fa179 100644 --- a/libs/items.js +++ b/libs/items.js @@ -11,9 +11,12 @@ items.prototype._init = function () { this.itemEffectTip = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.itemEffectTip; this.useItemEffect = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.useItemEffect; this.canUseItemEffect = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.canUseItemEffect; - if (!items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.canEquip) - items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.canEquip = {}; - this.equipCondition = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.canEquip; + if (!items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.equipCondition) + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.equipCondition = {}; + if (!items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.useItemEvent) + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.useItemEvent = {}; + this.equipCondition = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.equipCondition; + this.useItemEvent = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a.useItemEvent; } ////// 获得所有道具 ////// @@ -35,7 +38,7 @@ items.prototype._resetItems = function () { if (core.flags.bombFourDirections) core.material.items.bomb.text = "可以炸掉勇士四周的怪物"; if (core.flags.snowFourDirections) - core.material.items.bomb.text = "可以将四周的熔岩变成平地"; + core.material.items.snow.text = "可以将四周的熔岩变成平地"; if (core.flags.equipment) { core.material.items.sword1.cls = 'equips'; core.material.items.sword2.cls = 'equips'; @@ -59,13 +62,23 @@ items.prototype.getItemEffect = function (itemId, itemNum) { var curr_hp = core.status.hero.hp; if (itemId in this.itemEffect) { try { - eval(this.itemEffect[itemId]); + for (var i = 0; i < itemNum; ++i) + eval(this.itemEffect[itemId]); } catch (e) { main.log(e); } } core.status.hero.statistics.hp += core.status.hero.hp - curr_hp; + + if (this.useItemEvent[itemId]) { + try { + core.insertAction(this.useItemEvent[itemId]); + } + catch (e) { + main.log(e); + } + } } else { core.addItem(itemId, itemNum); @@ -115,6 +128,14 @@ items.prototype._useItemEffect = function (itemId) { main.log(e); } } + if (this.useItemEvent[itemId]) { + try { + core.insertAction(this.useItemEvent[itemId]); + } + catch (e) { + main.log(e); + } + } } items.prototype._afterUseItem = function (itemId) { @@ -125,10 +146,8 @@ items.prototype._afterUseItem = function (itemId) { if (core.status.hero.items[itemCls][itemId] <= 0) delete core.status.hero.items[itemCls][itemId]; - if (!core.status.event.id) { - core.status.event.data = null; + if (!core.status.event.id) core.status.event.ui = null; - } core.updateStatusBar(); } @@ -354,7 +373,7 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { } // --- 音效 - core.playSound('equip.mp3'); + this._realLoadEquip_playSound(); // --- 实际换装 this._loadEquipEffect(loadId, unloadId, loadPercentage == null ? unloadPercentage : loadPercentage); @@ -371,6 +390,12 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { if (callback) callback(); } +items.prototype._realLoadEquip_playSound = function () { + if (core.hasFlag("__quickLoadEquip__")) return; + core.stopSound(); + core.playSound('equip.mp3'); +} + ////// 保存装备 ////// items.prototype.quickSaveEquip = function (index) { var saveEquips = core.getFlag("saveEquips", []); @@ -393,20 +418,41 @@ items.prototype.quickLoadEquip = function (index) { if (v && !this.canEquip(v, true)) return; } + core.setFlag("__quickLoadEquip__", true); // 快速换装 + var toEquip = []; for (var i = 0; i < equipSize; i++) { var now = core.status.hero.equipment[i]; - if (now) { - this.unloadEquip(i); - core.status.route.push("unEquip:" + i); + // --- 只考虑diff的装备 + var to = current[i]; + if (now != to) { + toEquip.push(to || null); + if (now) { + this.unloadEquip(i); + core.status.route.push("unEquip:" + i); + } } } - for (var i = 0; i < equipSize; i++) { - var to = current[i]; + for (var i in toEquip) { + var to = toEquip[i]; if (to) { this.loadEquip(to); core.status.route.push("equip:" + to); } } + core.removeFlag("__quickLoadEquip__"); + this._realLoadEquip_playSound(); + core.drawTip("成功换上" + index + "号套装"); } + +////// 获得装备直接增加的属性数据 ////// +items.prototype.getEquippedStatus = function (name) { + var value = 0; + core.status.hero.equipment.forEach(function (v) { + if (!v || !(core.material.items[v] || {}).equip) return; + if (core.material.items[v].equip.percentage) return; + value += core.material.items[v].equip[name] || 0; + }); + return value; +} diff --git a/libs/loader.js b/libs/loader.js index b2da0c6e..563df8a5 100644 --- a/libs/loader.js +++ b/libs/loader.js @@ -19,7 +19,7 @@ loader.prototype._setStartProgressVal = function (val) { ////// 设置加载进度条提示文字 ////// loader.prototype._setStartLoadTipText = function (text) { - core.dom.startTopLoadTips.innerHTML = text; + core.dom.startTopLoadTips.innerText = text; } loader.prototype._load = function (callback) { @@ -50,7 +50,12 @@ loader.prototype._loadIcons = function () { } loader.prototype._loadMaterialImages = function (callback) { - this.loadImages(core.materials, core.material.images, callback); + this._setStartLoadTipText("正在加载资源文件..."); + if (main.useCompress) { + this.loadImagesFromZip('project/images/materials.h5data', core.materials, core.material.images, callback); + } else { + this.loadImages(core.materials, core.material.images, callback); + } } loader.prototype._loadExtraImages = function (callback) { @@ -60,14 +65,34 @@ loader.prototype._loadExtraImages = function (callback) { if (images.indexOf("hero.png") < 0) images.push("hero.png"); - this.loadImages(images, core.material.images.images, callback); + this._setStartLoadTipText("正在加载图片文件..."); + if (main.useCompress) { + // Check .gif + var gifs = images.filter(function (name) { + return name.toLowerCase().endsWith('.gif'); + }); + images = images.filter(function (name) { + return !name.toLowerCase().endsWith('.gif'); + }); + + this.loadImagesFromZip('project/images/images.h5data', images, core.material.images.images, callback); + gifs.forEach(function (gif) { + this.loadImage(gif, function (id, image) { + if (image != null) { + core.material.images.images[gif] = image; + } + }); + }, this); + } else { + this.loadImages(images, core.material.images.images, callback); + } } loader.prototype._loadAutotiles = function (callback) { core.material.images.autotile = {}; var keys = Object.keys(core.material.icons.autotile); var autotiles = {}; - this.loadImages(keys, autotiles, function () { + var _callback = function () { keys.forEach(function (v) { core.material.images.autotile[v] = autotiles[v]; }); @@ -77,13 +102,19 @@ loader.prototype._loadAutotiles = function (callback) { }); callback(); - }); + } + this._setStartLoadTipText("正在加载自动元件..."); + if (main.useCompress) { + this.loadImagesFromZip('project/images/autotiles.h5data', keys, autotiles, _callback); + } else { + this.loadImages(keys, autotiles, _callback); + } } loader.prototype._loadTilesets = function (callback) { core.material.images.tilesets = {}; core.tilesets = core.tilesets || []; - core.loader.loadImages(core.clone(core.tilesets), core.material.images.tilesets, function () { + var _callback = function () { // 检查宽高是32倍数,如果出错在控制台报错 for (var imgName in core.material.images.tilesets) { var img = core.material.images.tilesets[imgName]; @@ -95,7 +126,13 @@ loader.prototype._loadTilesets = function (callback) { } } callback(); - }); + } + this._setStartLoadTipText("正在加载额外素材..."); + if (main.useCompress) { + this.loadImagesFromZip('project/images/tilesets.h5data', core.tilesets, core.material.images.tilesets, _callback); + } else { + this.loadImages(core.tilesets, core.material.images.tilesets, _callback); + } } loader.prototype.loadImages = function (names, toSave, callback) { @@ -107,6 +144,11 @@ loader.prototype.loadImages = function (names, toSave, callback) { for (var i = 0; i < names.length; i++) { this.loadImage(names[i], function (id, image) { core.loader._setStartLoadTipText('正在加载图片 ' + id + "..."); + if (toSave[id] !== undefined) { + if (image != null) + toSave[id] = image; + return; + } toSave[id] = image; items++; core.loader._setStartProgressVal(items * (100 / names.length)); @@ -117,6 +159,37 @@ loader.prototype.loadImages = function (names, toSave, callback) { } } +loader.prototype.loadImagesFromZip = function (url, names, toSave, callback) { + if (!names || names.length == 0) { + if (callback) callback(); + return; + } + + core.unzip(url + "?v=" + main.version, function (data) { + var cnt = 1; + names.forEach(function (name) { + var imgName = name; + if (imgName.indexOf('.') < 0) imgName += '.png'; + if (imgName in data) { + var img = new Image(); + var url = URL.createObjectURL(data[imgName]); + cnt++; + img.onload = function () { + cnt--; + URL.revokeObjectURL(url); + if (cnt == 0 && callback) callback(); + } + img.src = url; + toSave[name] = img; + } + }); + cnt--; + if (cnt == 0 && callback) callback(); + }, null, false, function (percentage) { + core.loader._setStartProgressVal(percentage * 100); + }); +} + loader.prototype.loadImage = function (imgName, callback) { try { var name = imgName; @@ -126,7 +199,12 @@ loader.prototype.loadImage = function (imgName, callback) { image.onload = function () { callback(imgName, image); } + image.onerror = function () { + callback(imgName, null); + } image.src = 'project/images/' + name + "?v=" + main.version; + if (name.endsWith('.gif')) + callback(imgName, null); } catch (e) { main.log(e); @@ -134,58 +212,75 @@ loader.prototype.loadImage = function (imgName, callback) { } loader.prototype._loadAnimates = function () { - core.animates.forEach(function (t) { - core.http('GET', 'project/animates/' + t + ".animate", null, function (content) { - try { - content = JSON.parse(content); - var data = {}; - data.ratio = content.ratio; - data.se = content.se; - data.images = []; - data.images_rev = []; - content.bitmaps.forEach(function (t2) { - if (!t2) { - data.images.push(null); - } - else { - try { - var image = new Image(); - image.src = t2; - data.images.push(image); - } catch (e) { - main.log(e); - data.images.push(null); - } - } - }) - data.frame = content.frame_max; - data.frames = []; - content.frames.forEach(function (t2) { - var info = []; - t2.forEach(function (t3) { - info.push({ - 'index': t3[0], - 'x': t3[1], - 'y': t3[2], - 'zoom': t3[3], - 'opacity': t3[4], - 'mirror': t3[5] || 0, - 'angle': t3[6] || 0, - }) - }) - data.frames.push(info); - }) - core.material.animates[t] = data; + this._setStartLoadTipText("正在加载动画文件..."); + if (main.useCompress) { + core.unzip('project/animates/animates.h5data?v=' + main.version, function (animates) { + for (var name in animates) { + if (name.endsWith(".animate")) { + var t = name.substring(0, name.length - 8); + if (core.animates.indexOf(t) >= 0) + core.loader._loadAnimate(t, animates[name]); + } } - catch (e) { + }, null, true); + } else { + core.animates.forEach(function (t) { + core.http('GET', 'project/animates/' + t + ".animate?v=" + main.version, null, function (content) { + core.loader._loadAnimate(t, content); + }, function (e) { main.log(e); core.material.animates[t] = null; + }, "text/plain; charset=x-user-defined") + }) + } +} + +loader.prototype._loadAnimate = function (name, content) { + try { + content = JSON.parse(content); + var data = {}; + data.ratio = content.ratio; + data.se = content.se; + data.images = []; + data.images_rev = []; + content.bitmaps.forEach(function (t2) { + if (!t2) { + data.images.push(null); } - }, function (e) { - main.log(e); - core.material.animates[t] = null; - }, "text/plain; charset=x-user-defined") - }) + else { + try { + var image = new Image(); + image.src = t2; + data.images.push(image); + } catch (e) { + main.log(e); + data.images.push(null); + } + } + }) + data.frame = content.frame_max; + data.frames = []; + content.frames.forEach(function (t2) { + var info = []; + t2.forEach(function (t3) { + info.push({ + 'index': t3[0], + 'x': t3[1], + 'y': t3[2], + 'zoom': t3[3], + 'opacity': t3[4], + 'mirror': t3[5] || 0, + 'angle': t3[6] || 0, + }) + }) + data.frames.push(info); + }) + core.material.animates[name] = data; + } + catch (e) { + main.log(e); + core.material.animates[name] = null; + } } ////// 加载音频 ////// @@ -194,9 +289,20 @@ loader.prototype._loadMusic = function () { core.loader.loadOneMusic(t); }); - core.sounds.forEach(function (t) { - core.loader.loadOneSound(t); - }); + this._setStartLoadTipText("正在加载音效文件..."); + if (main.useCompress && core.musicStatus.audioContext) { + core.unzip('project/sounds/sounds.h5data?v=' + main.version, function (data) { + for (var name in data) { + if (core.sounds.indexOf(name) >= 0) { + core.loader._loadOneSound_decodeData(name, data[name]); + } + } + }); + } else { + core.sounds.forEach(function (t) { + core.loader.loadOneSound(t); + }); + } // 直接开始播放 core.playBgm(main.startBgm); } @@ -212,19 +318,8 @@ loader.prototype.loadOneMusic = function (name) { loader.prototype.loadOneSound = function (name) { if (core.musicStatus.audioContext != null) { - core.http('GET', 'project/sounds/' + name, null, function (data) { - try { - core.musicStatus.audioContext.decodeAudioData(data, function (buffer) { - core.material.sounds[name] = buffer; - }, function (e) { - main.log(e); - core.material.sounds[name] = null; - }) - } - catch (e) { - main.log(e); - core.material.sounds[name] = null; - } + core.http('GET', 'project/sounds/' + name + "?v=" + main.version, null, function (data) { + core.loader._loadOneSound_decodeData(name, data); }, function (e) { main.log(e); core.material.sounds[name] = null; @@ -237,7 +332,32 @@ loader.prototype.loadOneSound = function (name) { } } +loader.prototype._loadOneSound_decodeData = function (name, data) { + if (data instanceof Blob) { + var blobReader = new zip.BlobReader(data); + blobReader.init(function () { + blobReader.readUint8Array(0, blobReader.size, function (uint8) { + core.loader._loadOneSound_decodeData(name, uint8.buffer); + }) + }); + return; + } + try { + core.musicStatus.audioContext.decodeAudioData(data, function (buffer) { + core.material.sounds[name] = buffer; + }, function (e) { + main.log(e); + core.material.sounds[name] = null; + }) + } + catch (e) { + main.log(e); + core.material.sounds[name] = null; + } +} + loader.prototype.loadBgm = function (name) { + name = core.getMappedName(name); if (!core.material.bgms[name]) return; // 如果没开启音乐,则不预加载 if (!core.musicStatus.bgmStatus) return; @@ -265,6 +385,7 @@ loader.prototype._preloadBgm = function (bgm) { } loader.prototype.freeBgm = function (name) { + name = core.getMappedName(name); if (!core.material.bgms[name]) return; // 从cachedBgms中删除 core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (t) { diff --git a/libs/maps.js b/libs/maps.js index 52b540b4..a5cb4ada 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -62,6 +62,12 @@ maps.prototype._mapIntoBlocks = function (map, floor, floorId) { ////// 从ID获得数字 ////// maps.prototype.getNumberById = function (id) { + core.status.id2number = core.status.id2number || {}; + if (core.status.id2number[id] != null) return core.status.id2number[id]; + return core.status.id2number[id] = this._getNumberById(id); +} + +maps.prototype._getNumberById = function (id) { for (var number in this.blocksInfo) { if ((this.blocksInfo[number] || {}).id == id) return parseInt(number) || 0; @@ -76,6 +82,12 @@ maps.prototype.getNumberById = function (id) { return 0; } +maps.prototype.getBlockByNumber = function (number) { + core.status.number2Block = core.status.number2Block || {}; + if (core.status.number2Block[number] != null) return core.status.number2Block[number]; + return core.status.number2Block[number] = this.initBlock(null, null, number, true); +} + ////// 数字和ID的对应关系 ////// maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) { var disable = null; @@ -86,11 +98,14 @@ maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) { var block = {'x': x, 'y': y, 'id': id}; 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, "cannotIn":["up", "down", "left", "right"]}; 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 block.event = {'cls': 'terrains', 'id': 'none', 'noPass': false}; + if (typeof block.event.noPass === 'string') + block.event.noPass = JSON.parse(block.event.noPass); + if (addInfo) this._addInfo(block); if (eventFloor) { this._addEvent(block, x, y, (eventFloor.events || {})[x + "," + y]); @@ -238,10 +253,16 @@ maps.prototype.saveMap = function (floorId) { if (!floorId) { var map = {}; for (var id in maps) { - map[id] = this.saveMap(id); + var obj = this.saveMap(id); + if (Object.keys(obj).length > 0) map[id] = obj; } return map; } + // 砍层状态:直接返回 + if (main.mode == 'play' && (flags.__removed__ || []).indexOf(floorId) >= 0) { + return { canFlyTo: false, cannotViewMap: true }; + } + var map = maps[floorId], floor = core.floors[floorId]; var blocks = this._getMapArrayFromBlocks(map.blocks, floor.width, floor.height, true); if (main.mode == 'editor') return blocks; @@ -277,6 +298,23 @@ maps.prototype.loadMap = function (data, floorId) { return this.loadFloor(floorId, data[floorId]); } +////// 删除地图,不计入存档 ////// +maps.prototype.removeMaps = function (fromId, toId) { + if (!core.isPlaying()) return; + toId = toId || fromId; + var fromIndex = core.floorIds.indexOf(fromId), + toIndex = core.floorIds.indexOf(toId); + if (toIndex < 0) toIndex = core.floorIds.length - 1; + flags.__removed__ = flags.__removed__ || []; + for (var i = fromIndex; i <= toIndex; ++i) { + var floorId = core.floorIds[i]; + delete flags.__visited__[floorId]; + flags.__removed__.push(floorId); + core.status.maps[floorId].canFlyTo = false; + core.status.maps[floorId].cannotViewMap = true; + } +} + ////// 更改地图画布的尺寸 maps.prototype.resizeMap = function (floorId) { floorId = floorId || core.status.floorId; @@ -353,7 +391,8 @@ maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { return core.status[name + "maps"][floorId]; var arr = core.clone(core.floors[floorId][name + "map"] || []); - if (main.mode == 'editor') arr = core.clone(editor[name + "map"]) || arr; + if (main.mode == 'editor' && !(window.editor && editor.uievent && editor.uievent.isOpen)) + arr = core.clone(editor[name + "map"]) || arr; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { arr[y] = arr[y] || []; @@ -470,19 +509,44 @@ 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.getBlockByNumber(number).event || {})[name], direction); } ////// 能否瞬间移动 ////// maps.prototype.canMoveDirectly = function (destX, destY) { - if (!this._canMoveDirectly_checkGlobal()) return -1; + return this.canMoveDirectlyArray([[destX,destY]])[0]; +} + +maps.prototype.canMoveDirectlyArray = function (locs) { + var ans = [], number = locs.length; var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); - if (fromX == destX && fromY == destY) return 0; - // 检查起点事件 - if (!this._canMoveDirectly_checkStartPoint(fromX, fromY)) return -1; + if (!this._canMoveDirectly_checkGlobal()) { + for (var i = 0; i < number; ++i) ans.push(-1); + return ans; + } + for (var i = 0; i < number; ++i) { + if (locs[i][0] == fromX && locs[i][1] == fromY) { + ans.push(0); + number--; + } + else if (locs[i][0] < 0 || locs[i][0] >= core.bigmap.width || locs[i][1] < 0 || locs[i][1] >= core.bigmap.height) { + ans.push(-1); + number--; + } + else ans.push(null); + } + if (number == 0) return ans; - return this._canMoveDirectly_bfs(fromX, fromY, destX, destY); + // 检查起点事件 + if (!this._canMoveDirectly_checkStartPoint(fromX, fromY)) { + for (var i in ans) { + if (ans[i] == null) ans[i] = -1; + } + return ans; + } + + return this._canMoveDirectly_bfs(fromX, fromY, locs, number, ans); } maps.prototype._canMoveDirectly_checkGlobal = function () { @@ -508,7 +572,7 @@ maps.prototype._canMoveDirectly_checkStartPoint = function (sx, sy) { return true; } -maps.prototype._canMoveDirectly_bfs = function (sx, sy, ex, ey) { +maps.prototype._canMoveDirectly_bfs = function (sx, sy, locs, number, ans) { var canMoveArray = this.generateMovableArray(); var blocksObj = this.getMapBlocksObj(core.status.floorId); // 滑冰 @@ -527,22 +591,32 @@ maps.prototype._canMoveDirectly_bfs = function (sx, sy, ex, ey) { if (bgMap[ny][nx] == 167) continue; if (!this._canMoveDirectly_checkNextPoint(blocksObj, nx, ny)) continue; visited[nindex] = visited[now] + 1; - if (nx == ex && ny == ey) return visited[nindex]; + // if (nx == ex && ny == ey) return visited[nindex]; + for (var i in ans) { + if (locs[i][0] == nx && locs[i][1] == ny && ans[i] == null) { + ans[i] = visited[nindex]; + number--; + if (number == 0) return ans; + } + } queue.push(nindex); } } - return -1; + for (var i in ans) { + if (ans[i] == null) ans[i] = -1; + } + return ans; } maps.prototype._canMoveDirectly_checkNextPoint = function (blocksObj, x, y) { var index = x + "," + y; // 该点是否有事件 - if (blocksObj[index]) return false; + if (blocksObj[index] && (blocksObj[index].event.trigger || blocksObj[index].event.noPass)) return false; // 是否存在阻激夹域伤害 - if (core.status.checkBlock.damage[x + "," + y]) return false; + if (core.status.checkBlock.damage[index]) return false; // 是否存在捕捉 - if (core.status.checkBlock.ambush[x + "," + y]) return false; + if (core.status.checkBlock.ambush[index]) return false; return true; } @@ -728,6 +802,7 @@ maps.prototype.drawBg = function (floorId, ctx) { if (onMap) { ctx = core.canvas.bg; core.clearMap(ctx); + core.status.floorAnimateObjs = this._getFloorImages(floorId); } core.maps._drawBg_drawBackground(floorId, ctx); // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 @@ -767,7 +842,10 @@ maps.prototype.drawEvents = function (floorId, blocks, ctx) { maps.prototype.drawFg = function (floorId, ctx) { floorId = floorId || core.status.floorId; var onMap = ctx == null; - if (onMap) ctx = core.canvas.fg; + if (onMap) { + ctx = core.canvas.fg; + core.status.floorAnimateObjs = this._getFloorImages(floorId); + } // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 this._drawFloorImages(floorId, ctx, 'fg'); this._drawBgFgMap(floorId, ctx, 'fg', onMap); @@ -784,28 +862,46 @@ maps.prototype._drawBgFgMap = function (floorId, ctx, name, onMap) { core.status[name + "maps"] = {}; var arr = this._getBgFgMapArray(name, floorId, true); + var eventArr = null; + if (name == 'fg' && onMap && this._drawBgFgMap_shouldBlurFg()) { + eventArr = this.getMapArray(floorId); + } + 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; + // --- 前景虚化 + var blur = false, alpha; + if (eventArr != null && eventArr[y][x] != 0) { + blur = true; + alpha = ctx.globalAlpha; + ctx.globalAlpha = 0.6; + } this._drawMap_drawBlockInfo(ctx, block, blockInfo, arr, onMap); + if (blur) ctx.globalAlpha = alpha; } } if (onMap) core.status.autotileAnimateObjs[name + "map"] = core.clone(arr); } +////// 是否应当存在事件时虚化前景层 ////// +maps.prototype._drawBgFgMap_shouldBlurFg = function () { + return main.mode == 'editor' || core.flags.blurFg; +} + ////// 绘制楼层贴图 ////// 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); + imageName = core.getMappedName(imageName); var image = core.material.images.images[imageName]; if (redraw && frame == 1) return; // 不重绘 @@ -875,55 +971,118 @@ maps.prototype._drawFloorImage = function (ctx, name, type, image, offsetX, widt } ////// 绘制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 xx = block.x, yy = block.y; + var autotile = core.material.images['autotile'][block.event.id]; + status = status || 0; + status %= parseInt(autotile.width / 96); + var done = {}; + var isGrass = function(x,y){ + if(core.maps._drawAutotile_getAutotileAroundId(mapArr[yy][xx],x,y,mapArr)){ + return 1; + }else{ + return 0; + } + } + var iG = []; + [-1,0,1].forEach(function(_x){ + iG[_x] = []; + [-1,0,1].forEach(function(_y){ + iG[_x][_y] = isGrass(xx + _x, yy + _y); + })}); + if(iG[-1][-1] + iG[0][-1] + iG[0][0] + iG[-1][0] == 3 && !iG[-1][-1]){ + this._drawAutotile_render(ctx, xx * size + left, yy * size + top, size, autotile, status, 16); + done[0] = true; + } + if(iG[0][-1] + iG[1][-1] + iG[1][0] + iG[0][0] == 3 && !iG[1][-1]){ + this._drawAutotile_render(ctx, xx * size + left + size/2, yy * size + top, size, autotile, status, 17); + done[1] = true; + } + if(iG[0][0] + iG[1][0] + iG[1][1] + iG[0][1] == 3 && !iG[1][1]){ + this._drawAutotile_render(ctx, xx * size + left+size/2, yy * size + top + size/2, size, autotile, status, 18); + done[3] = true; + } + if(iG[0-1][0] + iG[0][0] + iG[0][1] + iG[-1][1] == 3 && !iG[-1][1]){ + this._drawAutotile_render(ctx, xx * size + left, yy * size + top + size/2, size, autotile, status, 19); + done[2] = true; + } + var _id = iG[0][-1] + 2 * iG[-1][0] + 4 * iG[0][1] + 8 * iG[1][0]; + this._drawAutotile_render(ctx, xx * size, yy * size, size, autotile, status, _id, done); +} + + +maps.prototype._drawAutotile_render = function(canvas, x, y, size, autotile, status, index, done) { + var indexData = [[[96 * status, 0, 32, 32, x, y, size, size],], + [[96 * status, 3 * 32, 16, 32, x, y, size / 2, size],[96 * status + 2 * 32 + 16, 3 * 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status + 2 * 32, 32, 32, 16, x, y, size, size / 2],[96 * status + 2 * 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status + 2 * 32, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 16, 32, x, y, size / 2, size],[96 * status + 2 * 32 + 16, 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status, 2 * 32, 16, 32, x, y, size / 2, size],[96 * status + 2 * 32 + 16, 2 * 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status + 2 * 32, 32, 32, 32, x, y, size, size],], + [[96 * status + 2 * 32, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 32, 16, x, y, size, size / 2],[96 * status, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 32, 32, 16, x, y, size, size / 2],[96 * status + 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status + 32, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 32, 32, x, y, size, size],], + [[96 * status, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status + 2 * 32, 0, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32 + 16, 0, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32 + 16, 16, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32, 16, 16, 16, x, y, size / 2, size / 2],], ]; - - // 开始绘制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); + var data = indexData[index]; + if(index>=16){ // 拐角直接绘制 + canvas.drawImage(autotile, data[0][0], data[0][1], data[0][2], data[0][3], data[0][4], data[0][5], size/2, size/2); + }else{ // 非拐角要根据是否已经绘制进行切分后绘制 + this._drawAutotile_renderCut(canvas, autotile, x, y, size, data, done); } } +maps.prototype._drawAutotile_renderCut = function(canvas, autotile, x, y, size, data, done){ + var drawData = []; + done = done || {}; + if(data.length == 2){ + var idx = 0; + var cut = 0; + for(var i in data){ + if(data[i][2] % 32){ // 是否纵切 + cut = 0; + } + else if(data[i][3] % 32){ // 是否横切 + cut = 1; + } + if(data[i][0] % 32 || data[i][1] % 32){ // right down + idx = 1; + }else{ // left top + idx = 0; + } + if(cut){ + idx *= 2; + if(!done[idx])drawData[idx] = [data[i][0], data[i][1]]; + if(!done[idx + 1])drawData[idx + 1] = [parseInt(data[i][0]) + 16, data[i][1]]; + }else{ + if(!done[idx])drawData[idx] = [data[i][0], data[i][1]]; + if(!done[idx + 2])drawData[idx + 2] = [data[i][0], parseInt(data[i][1]) + 16]; + } + } + }else{ + if(!done[0])drawData[0] = [data[0][0], data[0][1]]; + if(!done[1])drawData[1] = [data[0][0] + 16, data[0][1]]; + if(!done[2])drawData[2] = [data[0][0], data[0][1] + 16]; + if(!done[3])drawData[3] = [data[0][0] + 16, data[0][1] + 16]; + } + for(var i = 0; i<4; i++){ + var dt = drawData[i];if(!dt)continue; + canvas.drawImage(autotile, dt[0], dt[1], 16, 16, x + (i % 2) * size / 2, y + parseInt(i / 2) * size / 2, size/2, size/2); + }; +} + + 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)); @@ -1069,6 +1228,7 @@ maps.prototype._drawThumbnail_realDrawTempCanvas = function (floorId, blocks, op // 缩略图:勇士 if (options.heroLoc) { options.heroIcon = options.heroIcon || core.getFlag("heroIcon", "hero.png"); + options.heroIcon = core.getMappedName(options.heroIcon); 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, @@ -1144,9 +1304,12 @@ maps.prototype.terrainExists = function (x, y, id, floorId) { ////// 某个点是否存在楼梯 ////// 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'); + var blockId = this.getBlockId(x, y, floorId); + if (blockId == null) return false; + var ids = ['upFloor','downFloor']; + if (core.flags.flyRecordPosition) + ids = ids.concat(['leftPortal','rightPortal','upPortal','downPortal']); + return ids.indexOf(blockId)>=0; } ////// 当前位置是否在楼梯边 ////// @@ -1196,9 +1359,9 @@ maps.prototype.getBlockInfo = function (block) { } if (typeof block == 'number') { // 参数是数字 if (block == 0) return null; - block = this.initBlock(0, 0, block, true); + block = this.getBlockByNumber(block); } - var number = block.id, id = block.event.id, cls = block.event.cls, + var number = block.id, id = block.event.id, cls = block.event.cls, name = block.event.name, image = null, posX = 0, posY = 0, animate = block.event.animate, height = block.event.height || 32, faceIds = {}; @@ -1206,6 +1369,7 @@ maps.prototype.getBlockInfo = function (block) { else if (id == 'airwall') { if (!core.material.images.airwall) return null; image = core.material.images.airwall; + name = "空气墙"; } else if (cls == 'tileset') { var offset = core.icons.getTilesetOffset(id); @@ -1221,14 +1385,19 @@ maps.prototype.getBlockInfo = function (block) { image = core.material.images[cls]; posY = core.material.icons[cls][id]; faceIds = block.event.faceIds || {}; + if (core.material.enemys[id]) { + name = core.material.enemys[id].name; + } else if (core.material.items[id]) { + name = core.material.items[id].name; + } } - return {number: number, id: id, cls: cls, image: image, posX: posX, posY: posY, height: height, faceIds: faceIds, animate: animate}; + return {number: number, id: id, cls: cls, name: name, image: image, posX: posX, posY: posY, height: height, faceIds: faceIds, animate: animate}; } ////// 搜索某个图块出现的所有位置 ////// maps.prototype.searchBlock = function (id, floorId, showDisable) { - if (typeof id == 'number') id = this.initBlock(0, 0, id).event.id; + if (typeof id == 'number') id = this.getBlockByNumber(id).event.id; floorId = floorId || core.status.floorId; var result = []; if (floorId instanceof Array) { @@ -1431,8 +1600,13 @@ maps.prototype.setBlock = function (number, x, y, floorId) { else number = core.getNumberById(number); } - var originBlock = core.getBlock(x, y, floorId, true); var block = this.initBlock(x, y, number, true, core.floors[floorId]); + if (block.id == 0 && !block.event.trigger) { + // 转变图块为0且该点无事件,视为隐藏 + core.removeBlock(x, y, floorId); + return; + } + var originBlock = core.getBlock(x, y, floorId, true); if (floorId == core.status.floorId) { core.removeGlobalAnimate(x, y); core.clearMap('event', x * 32, y * 32, 32, 32); @@ -1452,7 +1626,8 @@ maps.prototype.setBlock = function (number, x, y, floorId) { } if (floorId == core.status.floorId && !block.disable) { core.drawBlock(block); - core.addGlobalAnimate(block); + if (block.event.cls != 'autotile') + core.addGlobalAnimate(block); core.updateStatusBar(); } } @@ -1466,7 +1641,7 @@ maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) { }); return; } - var toBlock = this.initBlock(0, 0, toNumber, true); + var toBlock = this.getBlockByNumber(toNumber, true); core.status.maps[floorId].blocks.forEach(function (block) { if (block.id == fromNumber) { block.id = toNumber; @@ -1492,8 +1667,11 @@ maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { core.setFlag(vFlag, number); core.status[name + "maps"][floorId] = null; - if (floorId == core.status.floorId) - core.drawMap(floorId); + if (floorId == core.status.floorId){ + core.clearMap(name); + if (name=='bg') core.drawBg(floorId); + else core.drawFg(floorId); + } } ////// 重置地图 ////// @@ -1599,13 +1777,13 @@ maps.prototype.moveBlock = function (x, y, steps, time, keep, callback) { } var block = blockArr[0], blockInfo = blockArr[1]; var moveSteps = (steps||[]).filter(function (t) { - return ['up','down','left','right'].indexOf(t)>=0; + return ['up','down','left','right','forward','backward'].indexOf(t)>=0; }); var canvases = this._initDetachedBlock(blockInfo, x, y, block.event.animate !== false); this._moveDetachedBlock(blockInfo, 32 * x, 32 * y, 1, canvases); var moveInfo = { - x: x, y: y, px: 32 * x, py: 32 * y, opacity: 1, keep: keep, + x: x, y: y, px: 32 * x, py: 32 * y, opacity: 1, keep: keep, lastDirection: null, offset: 1, moveSteps: moveSteps, step: 0, per_time: time / 16 / core.status.replay.speed } this._moveBlock_doMove(blockInfo, canvases, moveInfo, callback); @@ -1630,21 +1808,46 @@ maps.prototype._moveBlock_doMove = function (blockInfo, canvases, moveInfo, call core.animateFrame.asyncId[animate] = true; } -maps.prototype._moveBlock_moving = function (blockInfo, canvases, moveInfo) { +maps.prototype._moveBlock_updateDirection = function (blockInfo, moveInfo) { + moveInfo.offset = 1; 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; + if (moveInfo.lastDirection == null) { + for (var d in blockInfo.faceIds) { + if (blockInfo.faceIds[d] == blockInfo.id) { + moveInfo.lastDirection = d; + break; + } } } + if (direction == 'forward' || direction == 'backward') { + if (moveInfo.lastDirection == null) { + moveInfo.moveSteps.shift(); + return false; + } + if (direction == 'backward') + moveInfo.offset = -1; + direction = moveInfo.lastDirection; + } + moveInfo.lastDirection = moveInfo.moveSteps[0] = direction; + moveInfo.x += core.utils.scan[direction].x * moveInfo.offset; + moveInfo.y += core.utils.scan[direction].y * moveInfo.offset; + // 根据faceIds修改朝向 + var currid = blockInfo.faceIds[direction]; + if (currid) { + var posY = core.material.icons[blockInfo.cls][currid]; + if (posY != null) blockInfo.posY = posY; + } + return true; +} + +maps.prototype._moveBlock_moving = function (blockInfo, canvases, moveInfo) { + if (moveInfo.step == 0) { + if (!this._moveBlock_updateDirection(blockInfo, moveInfo)) return; + } + var direction = moveInfo.moveSteps[0]; moveInfo.step++; - moveInfo.px += core.utils.scan[direction].x * 2; - moveInfo.py += core.utils.scan[direction].y * 2; + moveInfo.px += core.utils.scan[direction].x * 2 * moveInfo.offset; + moveInfo.py += core.utils.scan[direction].y * 2 * moveInfo.offset; this._moveDetachedBlock(blockInfo, moveInfo.px, moveInfo.py, moveInfo.opacity, canvases); if (moveInfo.step == 16) { moveInfo.step = 0; @@ -1673,6 +1876,7 @@ maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, 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; + time /= Math.max(core.status.replay.speed, 1) 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, @@ -1736,6 +1940,7 @@ maps.prototype.animateBlock = function (loc, type, time, callback) { return; } this._animateBlock_drawList(list, isHide ? 1 : 0); + time /= Math.max(core.status.replay.speed, 1) this._animateBlock_doAnimate(loc, list, isHide, 10 / time, callback); } @@ -1844,15 +2049,10 @@ maps.prototype.drawBoxAnimate = function () { ////// 绘制动画 ////// maps.prototype.drawAnimate = function (name, x, y, callback) { + name = core.getMappedName(name); // 正在播放录像:不显示动画 - if (core.isReplaying()) { - if (callback) callback(); - return -1; - } - - // 检测动画是否存在 - if (!core.material.animates[name] || x == null || y == null) { + if (core.isReplaying() || !core.material.animates[name] || x == null || y == null) { if (callback) callback(); return -1; } @@ -1875,6 +2075,33 @@ maps.prototype.drawAnimate = function (name, x, y, callback) { return id; } +////// 绘制一个跟随勇士的动画 ////// +maps.prototype.drawHeroAnimate = function (name, callback) { + name = core.getMappedName(name); + + // 正在播放录像或动画不存在:不显示动画 + if (core.isReplaying() || !core.material.animates[name]) { + if (callback) callback(); + return -1; + } + + // 开始绘制 + var animate = core.material.animates[name]; + // 播放音效 + core.playSound(animate.se); + + var id = setTimeout(null); + core.status.animateObjs.push({ + "id": id, + "animate": animate, + "hero": true, + "index": 0, + "callback": callback + }); + + return id; +} + ////// 绘制动画的某一帧 ////// maps.prototype._drawAnimateFrame = function (animate, centerX, centerY, index) { var frame = animate.frames[index]; diff --git a/libs/thirdparty/LICENSE.md b/libs/thirdparty/LICENSE.md index 33ec4fdc..667f1d31 100644 --- a/libs/thirdparty/LICENSE.md +++ b/libs/thirdparty/LICENSE.md @@ -1,3 +1,238 @@ +lzstring +============ + +MIT License + +Copyright (c) 2013 pieroxy + +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. + +localforage +============ + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Mozilla + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +mid +============ + Copyright (c) 2010, Matt Westcott & Ben Firshman All rights reserved. @@ -21,4 +256,28 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +zip +============ + + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/thirdparty/zip.js b/libs/thirdparty/zip.js new file mode 100644 index 00000000..f7f68f47 --- /dev/null +++ b/libs/thirdparty/zip.js @@ -0,0 +1,2895 @@ +/* + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +(function(obj) { + "use strict"; + + var ERR_BAD_FORMAT = "File format is not recognized."; + var ERR_CRC = "CRC failed."; + var ERR_ENCRYPTED = "File contains encrypted entry."; + var ERR_ZIP64 = "File is using Zip64 (4gb+ file size)."; + var ERR_READ = "Error while reading zip file."; + var ERR_WRITE = "Error while writing zip file."; + var ERR_WRITE_DATA = "Error while writing file data."; + var ERR_READ_DATA = "Error while reading file data."; + var ERR_DUPLICATED_NAME = "File already exists."; + var CHUNK_SIZE = 512 * 1024; + + var TEXT_PLAIN = "text/plain"; + + var appendABViewSupported; + try { + appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0; + } catch (e) { + } + + function Crc32() { + this.crc = -1; + } + Crc32.prototype.append = function append(data) { + var crc = this.crc | 0, table = this.table; + for (var offset = 0, len = data.length | 0; offset < len; offset++) + crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF]; + this.crc = crc; + }; + Crc32.prototype.get = function get() { + return ~this.crc; + }; + Crc32.prototype.table = (function() { + var i, j, t, table = []; // Uint32Array is actually slower than [] + for (i = 0; i < 256; i++) { + t = i; + for (j = 0; j < 8; j++) + if (t & 1) + t = (t >>> 1) ^ 0xEDB88320; + else + t = t >>> 1; + table[i] = t; + } + return table; + })(); + + // "no-op" codec + function NOOP() {} + NOOP.prototype.append = function append(bytes, onprogress) { + return bytes; + }; + NOOP.prototype.flush = function flush() {}; + + function blobSlice(blob, index, length) { + if (index < 0 || length < 0 || index + length > blob.size) + throw new RangeError('offset:' + index + ', length:' + length + ', size:' + blob.size); + if (blob.slice) + return blob.slice(index, index + length); + else if (blob.webkitSlice) + return blob.webkitSlice(index, index + length); + else if (blob.mozSlice) + return blob.mozSlice(index, index + length); + else if (blob.msSlice) + return blob.msSlice(index, index + length); + } + + function getDataHelper(byteLength, bytes) { + var dataBuffer, dataArray; + dataBuffer = new ArrayBuffer(byteLength); + dataArray = new Uint8Array(dataBuffer); + if (bytes) + dataArray.set(bytes, 0); + return { + buffer : dataBuffer, + array : dataArray, + view : new DataView(dataBuffer) + }; + } + + // Readers + function Reader() { + } + + function TextReader(text) { + var that = this, blobReader; + + function init(callback, onerror) { + var blob = new Blob([ text ], { + type : TEXT_PLAIN + }); + blobReader = new BlobReader(blob); + blobReader.init(function() { + that.size = blobReader.size; + callback(); + }, onerror); + } + + function readUint8Array(index, length, callback, onerror) { + blobReader.readUint8Array(index, length, callback, onerror); + } + + that.size = 0; + that.init = init; + that.readUint8Array = readUint8Array; + } + TextReader.prototype = new Reader(); + TextReader.prototype.constructor = TextReader; + + function Data64URIReader(dataURI) { + var that = this, dataStart; + + function init(callback) { + var dataEnd = dataURI.length; + while (dataURI.charAt(dataEnd - 1) == "=") + dataEnd--; + dataStart = dataURI.indexOf(",") + 1; + that.size = Math.floor((dataEnd - dataStart) * 0.75); + callback(); + } + + function readUint8Array(index, length, callback) { + var i, data = getDataHelper(length); + var start = Math.floor(index / 3) * 4; + var end = Math.ceil((index + length) / 3) * 4; + var bytes = obj.atob(dataURI.substring(start + dataStart, end + dataStart)); + var delta = index - Math.floor(start / 4) * 3; + for (i = delta; i < delta + length; i++) + data.array[i - delta] = bytes.charCodeAt(i); + callback(data.array); + } + + that.size = 0; + that.init = init; + that.readUint8Array = readUint8Array; + } + Data64URIReader.prototype = new Reader(); + Data64URIReader.prototype.constructor = Data64URIReader; + + function BlobReader(blob) { + var that = this; + + function init(callback) { + that.size = blob.size; + callback(); + } + + function readUint8Array(index, length, callback, onerror) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(new Uint8Array(e.target.result)); + }; + reader.onerror = onerror; + try { + reader.readAsArrayBuffer(blobSlice(blob, index, length)); + } catch (e) { + onerror(e); + } + } + + that.size = 0; + that.init = init; + that.readUint8Array = readUint8Array; + } + BlobReader.prototype = new Reader(); + BlobReader.prototype.constructor = BlobReader; + + // Writers + + function Writer() { + } + Writer.prototype.getData = function(callback) { + callback(this.data); + }; + + function TextWriter(encoding) { + var that = this, blob; + + function init(callback) { + blob = new Blob([], { + type : TEXT_PLAIN + }); + callback(); + } + + function writeUint8Array(array, callback) { + blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { + type : TEXT_PLAIN + }); + callback(); + } + + function getData(callback, onerror) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(e.target.result); + }; + reader.onerror = onerror; + reader.readAsText(blob, encoding); + } + + that.init = init; + that.writeUint8Array = writeUint8Array; + that.getData = getData; + } + TextWriter.prototype = new Writer(); + TextWriter.prototype.constructor = TextWriter; + + function Data64URIWriter(contentType) { + var that = this, data = "", pending = ""; + + function init(callback) { + data += "data:" + (contentType || "") + ";base64,"; + callback(); + } + + function writeUint8Array(array, callback) { + var i, delta = pending.length, dataString = pending; + pending = ""; + for (i = 0; i < (Math.floor((delta + array.length) / 3) * 3) - delta; i++) + dataString += String.fromCharCode(array[i]); + for (; i < array.length; i++) + pending += String.fromCharCode(array[i]); + if (dataString.length > 2) + data += obj.btoa(dataString); + else + pending = dataString; + callback(); + } + + function getData(callback) { + callback(data + obj.btoa(pending)); + } + + that.init = init; + that.writeUint8Array = writeUint8Array; + that.getData = getData; + } + Data64URIWriter.prototype = new Writer(); + Data64URIWriter.prototype.constructor = Data64URIWriter; + + function BlobWriter(contentType) { + var blob, that = this; + + function init(callback) { + blob = new Blob([], { + type : contentType + }); + callback(); + } + + function writeUint8Array(array, callback) { + blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { + type : contentType + }); + callback(); + } + + function getData(callback) { + callback(blob); + } + + that.init = init; + that.writeUint8Array = writeUint8Array; + that.getData = getData; + } + BlobWriter.prototype = new Writer(); + BlobWriter.prototype.constructor = BlobWriter; + + function launchProcess(process, reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror) { + var chunkIndex = 0, index, outputSize = 0, + crcInput = crcType === 'input', + crcOutput = crcType === 'output', + crc = new Crc32(); + function step() { + var outputData; + index = chunkIndex * CHUNK_SIZE; + if (index < size) + reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(inputData) { + var outputData; + try { + outputData = process.append(inputData, function(loaded) { + if (onprogress) + onprogress(index + loaded, size); + }); + } catch (e) { + onreaderror(e); + return; + } + if (outputData) { + outputSize += outputData.length; + writer.writeUint8Array(outputData, function() { + chunkIndex++; + setTimeout(step, 1); + }, onwriteerror); + if (crcOutput) + crc.append(outputData); + } else { + chunkIndex++; + setTimeout(step, 1); + } + if (crcInput) + crc.append(inputData); + if (onprogress) + onprogress(index, size); + }, onreaderror); + else { + try { + outputData = process.flush(); + } catch (e) { + onreaderror(e); + return; + } + if (outputData) { + if (crcOutput) + crc.append(outputData); + outputSize += outputData.length; + writer.writeUint8Array(outputData, function() { + onend(outputSize, crc.get()); + }, onwriteerror); + } else + onend(outputSize, crc.get()); + } + } + + step(); + } + + function inflate(sn, reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { + var crcType = computeCrc32 ? 'output' : 'none'; + launchProcess(new obj.zip.Inflater(), reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror); + } + + function deflate(sn, reader, writer, level, onend, onprogress, onreaderror, onwriteerror) { + var crcType = 'input'; + launchProcess(new obj.zip.Deflater(), reader, writer, 0, reader.size, crcType, onprogress, onend, onreaderror, onwriteerror); + } + + function copy(sn, reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { + var crcType = 'input'; + launchProcess(new NOOP(), reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror); + } + + // ZipReader + + function decodeASCII(str) { + var i, out = "", charCode, extendedASCII = [ '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4', '\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB', + '\u00E8', '\u00EF', '\u00EE', '\u00EC', '\u00C4', '\u00C5', '\u00C9', '\u00E6', '\u00C6', '\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9', + '\u00FF', '\u00D6', '\u00DC', '\u00F8', '\u00A3', '\u00D8', '\u00D7', '\u0192', '\u00E1', '\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1', + '\u00AA', '\u00BA', '\u00BF', '\u00AE', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB', '\u00BB', '_', '_', '_', '\u00A6', '\u00A6', + '\u00C1', '\u00C2', '\u00C0', '\u00A9', '\u00A6', '\u00A6', '+', '+', '\u00A2', '\u00A5', '+', '+', '-', '-', '+', '-', '+', '\u00E3', + '\u00C3', '+', '+', '-', '-', '\u00A6', '-', '+', '\u00A4', '\u00F0', '\u00D0', '\u00CA', '\u00CB', '\u00C8', 'i', '\u00CD', '\u00CE', + '\u00CF', '+', '+', '_', '_', '\u00A6', '\u00CC', '_', '\u00D3', '\u00DF', '\u00D4', '\u00D2', '\u00F5', '\u00D5', '\u00B5', '\u00FE', + '\u00DE', '\u00DA', '\u00DB', '\u00D9', '\u00FD', '\u00DD', '\u00AF', '\u00B4', '\u00AD', '\u00B1', '_', '\u00BE', '\u00B6', '\u00A7', + '\u00F7', '\u00B8', '\u00B0', '\u00A8', '\u00B7', '\u00B9', '\u00B3', '\u00B2', '_', ' ' ]; + for (i = 0; i < str.length; i++) { + charCode = str.charCodeAt(i) & 0xFF; + if (charCode > 127) + out += extendedASCII[charCode - 128]; + else + out += String.fromCharCode(charCode); + } + return out; + } + + function decodeUTF8(string) { + return decodeURIComponent(escape(string)); + } + + function getString(bytes) { + var i, str = ""; + for (i = 0; i < bytes.length; i++) + str += String.fromCharCode(bytes[i]); + return str; + } + + function getDate(timeRaw) { + var date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & 0x0000ffff; + try { + return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, + (time & 0x001F) * 2, 0); + } catch (e) { + } + } + + function readCommonHeader(entry, data, index, centralDirectory, onerror) { + entry.version = data.view.getUint16(index, true); + entry.bitFlag = data.view.getUint16(index + 2, true); + entry.compressionMethod = data.view.getUint16(index + 4, true); + entry.lastModDateRaw = data.view.getUint32(index + 6, true); + entry.lastModDate = getDate(entry.lastModDateRaw); + if ((entry.bitFlag & 0x01) === 0x01) { + onerror(ERR_ENCRYPTED); + return; + } + if (centralDirectory || (entry.bitFlag & 0x0008) != 0x0008) { + entry.crc32 = data.view.getUint32(index + 10, true); + entry.compressedSize = data.view.getUint32(index + 14, true); + entry.uncompressedSize = data.view.getUint32(index + 18, true); + } + if (entry.compressedSize === 0xFFFFFFFF || entry.uncompressedSize === 0xFFFFFFFF) { + onerror(ERR_ZIP64); + return; + } + entry.filenameLength = data.view.getUint16(index + 22, true); + entry.extraFieldLength = data.view.getUint16(index + 24, true); + } + + function createZipReader(reader, callback, onerror) { + var inflateSN = 0; + + function Entry() { + } + + Entry.prototype.getData = function(writer, onend, onprogress, checkCrc32) { + var that = this; + + function testCrc32(crc32) { + var dataCrc32 = getDataHelper(4); + dataCrc32.view.setUint32(0, crc32); + return that.crc32 == dataCrc32.view.getUint32(0); + } + + function getWriterData(uncompressedSize, crc32) { + if (checkCrc32 && !testCrc32(crc32)) + onerror(ERR_CRC); + else + writer.getData(function(data) { + onend(data); + }); + } + + function onreaderror(err) { + onerror(err || ERR_READ_DATA); + } + + function onwriteerror(err) { + onerror(err || ERR_WRITE_DATA); + } + + reader.readUint8Array(that.offset, 30, function(bytes) { + var data = getDataHelper(bytes.length, bytes), dataOffset; + if (data.view.getUint32(0) != 0x504b0304) { + onerror(ERR_BAD_FORMAT); + return; + } + readCommonHeader(that, data, 4, false, onerror); + dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength; + writer.init(function() { + if (that.compressionMethod === 0) + copy(inflateSN++, reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); + else + inflate(inflateSN++, reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); + }, onwriteerror); + }, onreaderror); + }; + + function seekEOCDR(eocdrCallback) { + // "End of central directory record" is the last part of a zip archive, and is at least 22 bytes long. + // Zip file comment is the last part of EOCDR and has max length of 64KB, + // so we only have to search the last 64K + 22 bytes of a archive for EOCDR signature (0x06054b50). + var EOCDR_MIN = 22; + if (reader.size < EOCDR_MIN) { + onerror(ERR_BAD_FORMAT); + return; + } + var ZIP_COMMENT_MAX = 256 * 256, EOCDR_MAX = EOCDR_MIN + ZIP_COMMENT_MAX; + + // In most cases, the EOCDR is EOCDR_MIN bytes long + doSeek(EOCDR_MIN, function() { + // If not found, try within EOCDR_MAX bytes + doSeek(Math.min(EOCDR_MAX, reader.size), function() { + onerror(ERR_BAD_FORMAT); + }); + }); + + // seek last length bytes of file for EOCDR + function doSeek(length, eocdrNotFoundCallback) { + reader.readUint8Array(reader.size - length, length, function(bytes) { + for (var i = bytes.length - EOCDR_MIN; i >= 0; i--) { + if (bytes[i] === 0x50 && bytes[i + 1] === 0x4b && bytes[i + 2] === 0x05 && bytes[i + 3] === 0x06) { + eocdrCallback(new DataView(bytes.buffer, i, EOCDR_MIN)); + return; + } + } + eocdrNotFoundCallback(); + }, function() { + onerror(ERR_READ); + }); + } + } + + var zipReader = { + getEntries : function(callback) { + // look for End of central directory record + seekEOCDR(function(dataView) { + var datalength, fileslength; + datalength = dataView.getUint32(16, true); + fileslength = dataView.getUint16(8, true); + if (datalength < 0 || datalength >= reader.size) { + onerror(ERR_BAD_FORMAT); + return; + } + reader.readUint8Array(datalength, reader.size - datalength, function(bytes) { + var i, index = 0, entries = [], entry, filename, comment, data = getDataHelper(bytes.length, bytes); + for (i = 0; i < fileslength; i++) { + entry = new Entry(); + if (data.view.getUint32(index) != 0x504b0102) { + onerror(ERR_BAD_FORMAT); + return; + } + readCommonHeader(entry, data, index + 6, true, onerror); + entry.commentLength = data.view.getUint16(index + 32, true); + entry.directory = ((data.view.getUint8(index + 38) & 0x10) == 0x10); + entry.offset = data.view.getUint32(index + 42, true); + filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength)); + entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename); + if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/") + entry.directory = true; + comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46 + + entry.filenameLength + entry.extraFieldLength + entry.commentLength)); + entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment); + entries.push(entry); + index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength; + } + callback(entries); + }, function() { + onerror(ERR_READ); + }); + }); + }, + close : function(callback) { + if (callback) + callback(); + }, + }; + + callback(zipReader); + } + + // ZipWriter + + function encodeUTF8(string) { + return unescape(encodeURIComponent(string)); + } + + function getBytes(str) { + var i, array = []; + for (i = 0; i < str.length; i++) + array.push(str.charCodeAt(i)); + return array; + } + + function createZipWriter(writer, callback, onerror, dontDeflate) { + var files = {}, filenames = [], datalength = 0; + var deflateSN = 0; + + function onwriteerror(err) { + onerror(err || ERR_WRITE); + } + + function onreaderror(err) { + onerror(err || ERR_READ_DATA); + } + + var zipWriter = { + add : function(name, reader, onend, onprogress, options) { + var header, filename, date; + + function writeHeader(callback) { + var data; + date = options.lastModDate || new Date(); + header = getDataHelper(26); + files[name] = { + headerArray : header.array, + directory : options.directory, + filename : filename, + offset : datalength, + comment : getBytes(encodeUTF8(options.comment || "")) + }; + header.view.setUint32(0, 0x14000808); + if (options.version) + header.view.setUint8(0, options.version); + if (!dontDeflate && options.level !== 0 && !options.directory) + header.view.setUint16(4, 0x0800); + header.view.setUint16(6, (((date.getHours() << 6) | date.getMinutes()) << 5) | date.getSeconds() / 2, true); + header.view.setUint16(8, ((((date.getFullYear() - 1980) << 4) | (date.getMonth() + 1)) << 5) | date.getDate(), true); + header.view.setUint16(22, filename.length, true); + data = getDataHelper(30 + filename.length); + data.view.setUint32(0, 0x504b0304); + data.array.set(header.array, 4); + data.array.set(filename, 30); + datalength += data.array.length; + writer.writeUint8Array(data.array, callback, onwriteerror); + } + + function writeFooter(compressedLength, crc32) { + var footer = getDataHelper(16); + datalength += compressedLength || 0; + footer.view.setUint32(0, 0x504b0708); + if (typeof crc32 != "undefined") { + header.view.setUint32(10, crc32, true); + footer.view.setUint32(4, crc32, true); + } + if (reader) { + footer.view.setUint32(8, compressedLength, true); + header.view.setUint32(14, compressedLength, true); + footer.view.setUint32(12, reader.size, true); + header.view.setUint32(18, reader.size, true); + } + writer.writeUint8Array(footer.array, function() { + datalength += 16; + onend(); + }, onwriteerror); + } + + function writeFile() { + options = options || {}; + name = name.trim(); + if (options.directory && name.charAt(name.length - 1) != "/") + name += "/"; + if (files.hasOwnProperty(name)) { + onerror(ERR_DUPLICATED_NAME); + return; + } + filename = getBytes(encodeUTF8(name)); + filenames.push(name); + writeHeader(function() { + if (reader) + if (dontDeflate || options.level === 0) + copy(deflateSN++, reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror); + else + deflate(deflateSN++, reader, writer, options.level, writeFooter, onprogress, onreaderror, onwriteerror); + else + writeFooter(); + }, onwriteerror); + } + + if (reader) + reader.init(writeFile, onreaderror); + else + writeFile(); + }, + close : function(callback) { + + var data, length = 0, index = 0, indexFilename, file; + for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) { + file = files[filenames[indexFilename]]; + length += 46 + file.filename.length + file.comment.length; + } + data = getDataHelper(length + 22); + for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) { + file = files[filenames[indexFilename]]; + data.view.setUint32(index, 0x504b0102); + data.view.setUint16(index + 4, 0x1400); + data.array.set(file.headerArray, index + 6); + data.view.setUint16(index + 32, file.comment.length, true); + if (file.directory) + data.view.setUint8(index + 38, 0x10); + data.view.setUint32(index + 42, file.offset, true); + data.array.set(file.filename, index + 46); + data.array.set(file.comment, index + 46 + file.filename.length); + index += 46 + file.filename.length + file.comment.length; + } + data.view.setUint32(index, 0x504b0506); + data.view.setUint16(index + 8, filenames.length, true); + data.view.setUint16(index + 10, filenames.length, true); + data.view.setUint32(index + 12, length, true); + data.view.setUint32(index + 16, datalength, true); + writer.writeUint8Array(data.array, function() { + writer.getData(callback); + }, onwriteerror); + }, + }; + + callback(zipWriter); + } + + function onerror_default(error) { + console.error(error); + } + obj.zip = { + Reader : Reader, + Writer : Writer, + BlobReader : BlobReader, + Data64URIReader : Data64URIReader, + TextReader : TextReader, + BlobWriter : BlobWriter, + Data64URIWriter : Data64URIWriter, + TextWriter : TextWriter, + createReader : function(reader, callback, onerror) { + onerror = onerror || onerror_default; + + reader.init(function() { + createZipReader(reader, callback, onerror); + }, onerror); + }, + createWriter : function(writer, callback, onerror, dontDeflate) { + onerror = onerror || onerror_default; + dontDeflate = !!dontDeflate; + + writer.init(function() { + createZipWriter(writer, callback, onerror, dontDeflate); + }, onerror); + }, + }; + +})(this); + +// -- Inflate.js + +/* + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc. + * JZlib is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +(function(global) { + "use strict"; + + // Global + var MAX_BITS = 15; + + var Z_OK = 0; + var Z_STREAM_END = 1; + var Z_NEED_DICT = 2; + var Z_STREAM_ERROR = -2; + var Z_DATA_ERROR = -3; + var Z_MEM_ERROR = -4; + var Z_BUF_ERROR = -5; + + var inflate_mask = [ 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, + 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff ]; + + var MANY = 1440; + + // JZlib version : "1.0.2" + var Z_NO_FLUSH = 0; + var Z_FINISH = 4; + + // InfTree + var fixed_bl = 9; + var fixed_bd = 5; + + var fixed_tl = [ 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, + 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, + 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, + 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, + 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, + 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, + 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, + 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, + 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, + 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, + 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, + 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, + 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, + 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, + 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, + 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, + 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, + 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, + 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, + 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, + 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, + 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, + 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, + 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, + 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, + 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, + 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, + 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, + 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, + 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, + 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, + 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, + 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, + 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, + 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, + 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, + 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255 ]; + var fixed_td = [ 80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, + 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, + 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577 ]; + + // Tables for deflate from PKZIP's appnote.txt. + var cplens = [ // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ]; + + // see note #13 above about 258 + var cplext = [ // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + ]; + + var cpdist = [ // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + + var cpdext = [ // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ]; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + var BMAX = 15; // maximum bit length of any code + + function InfTree() { + var that = this; + + var hn; // hufts used in space + var v; // work area for huft_build + var c; // bit length count table + var r; // table entry for structure assignment + var u; // table stack + var x; // bit offsets, then code stack + + function huft_build(b, // code lengths in bits (all assumed <= + // BMAX) + bindex, n, // number of codes (assumed <= 288) + s, // number of simple-valued codes (0..s-1) + d, // list of base values for non-simple codes + e, // list of extra bits for non-simple codes + t, // result: starting table + m, // maximum lookup bits, returns actual + hp,// space for trees + hn,// hufts used in space + v // working area: values in order of bit length + ) { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, + // Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in + // this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set + // of + // lengths), or Z_MEM_ERROR if not enough memory. + + var a; // counter for codes of length k + var f; // i repeats in table every f entries + var g; // maximum code length + var h; // table level + var i; // counter, current code + var j; // counter + var k; // number of bits in current code + var l; // bits per table (returned in m) + var mask; // (1 << w) - 1, to avoid cc -O bug on HP + var p; // pointer into c[], b[], or v[] + var q; // points to current table + var w; // bits before this table == (l * h) + var xp; // pointer into x + var y; // number of dummy codes added + var z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; + i = n; + do { + c[b[bindex + p]]++; + p++; + i--; // assume all entries <= BMAX + } while (i !== 0); + + if (c[0] == n) { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] !== 0) + break; + k = j; // minimum code length + if (l < j) { + l = j; + } + for (i = BMAX; i !== 0; i--) { + if (c[i] !== 0) + break; + } + g = i; // maximum code length + if (l > i) { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) { + if ((y -= c[j]) < 0) { + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) { + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; + xp = 2; + while (--i !== 0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; + p = 0; + do { + if ((j = b[bindex + p]) !== 0) { + v[x[j]++] = i; + } + p++; + } while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) { + a = c[k]; + while (a-- !== 0) { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table + // too few codes for + // k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) { + while (++j < z) { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) { // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /* hp+ */hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h !== 0) { + x[h] = i; // save pattern for backing up + r[0] = /* (byte) */j; // bits in this table + r[1] = /* (byte) */l; // bits to dump before this table + j = i >>> (w - l); + r[2] = /* (int) */(q - u[h - 1] - j); // offset to this table + hp.set(r, (u[h - 1] + j) * 3); + // to + // last + // table + } else { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = /* (byte) */(k - w); + if (p >= n) { + r[0] = 128 + 64; // out of values--invalid code + } else if (v[p] < s) { + r[0] = /* (byte) */(v[p] < 256 ? 0 : 32 + 64); // 256 is + // end-of-block + r[2] = v[p++]; // simple code is just the value + } else { + r[0] = /* (byte) */(e[v[p] - s] + 16 + 64); // non-simple--look + // up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >>> w; j < z; j += f) { + hp.set(r, (q + j) * 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) !== 0; j >>>= 1) { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y !== 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + function initWorkArea(vsize) { + var i; + if (!hn) { + hn = []; // []; //new Array(1); + v = []; // new Array(vsize); + c = new Int32Array(BMAX + 1); // new Array(BMAX + 1); + r = []; // new Array(3); + u = new Int32Array(BMAX); // new Array(BMAX); + x = new Int32Array(BMAX + 1); // new Array(BMAX + 1); + } + if (v.length < vsize) { + v = []; // new Array(vsize); + } + for (i = 0; i < vsize; i++) { + v[i] = 0; + } + for (i = 0; i < BMAX + 1; i++) { + c[i] = 0; + } + for (i = 0; i < 3; i++) { + r[i] = 0; + } + // for(int i=0; i 257)) { + if (result == Z_DATA_ERROR) { + z.msg = "oversubscribed distance tree"; + } else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } else if (result != Z_MEM_ERROR) { + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + }; + + } + + InfTree.inflate_trees_fixed = function(bl, // literal desired/actual bit depth + bd, // distance desired/actual bit depth + tl,// literal/length tree result + td// distance tree result + ) { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return Z_OK; + }; + + // InfCodes + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + var START = 0; // x: set up for LEN + var LEN = 1; // i: get length/literal/eob next + var LENEXT = 2; // i: getting length extra (have base) + var DIST = 3; // i: get distance next + var DISTEXT = 4;// i: getting distance extra + var COPY = 5; // o: copying bytes in window, waiting + // for space + var LIT = 6; // o: got literal, waiting for output + // space + var WASH = 7; // o: got eob, possibly still output + // waiting + var END = 8; // x: got eob and all data flushed + var BADCODE = 9;// x: got error + + function InfCodes() { + var that = this; + + var mode; // current inflate_codes mode + + // mode dependent information + var len = 0; + + var tree; // pointer into tree + var tree_index = 0; + var need = 0; // bits needed + + var lit = 0; + + // if EXT or COPY, where and how much + var get = 0; // bits to get for extra + var dist = 0; // distance back to copy from + + var lbits = 0; // ltree bits decoded per branch + var dbits = 0; // dtree bits decoder per branch + var ltree; // literal/length/eob tree + var ltree_index = 0; // literal/length/eob tree + var dtree; // distance tree + var dtree_index = 0; // distance tree + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + function inflate_fast(bl, bd, tl, tl_index, td, td_index, s, z) { + var t; // temporary pointer + var tp; // temporary pointer + var tp_index; // temporary pointer + var e; // extra bits or operation + var b; // bit buffer + var k; // bits in bit buffer + var p; // input data pointer + var n; // bytes available there + var q; // output window write pointer + var m; // bytes to end of window or read pointer + var ml; // mask for literal/length tree + var md; // mask for distance tree + var c; // bytes to copy + var d; // distance back to copy from + var r; // copy source pointer + + var tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) { // max bits for literal/length code + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) === 0) { + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2]; + m--; + continue; + } + do { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) !== 0) { + e &= 15; + c = tp[tp_index_t_3 + 2] + (/* (int) */b & inflate_mask[e]); + + b >>= e; + k -= e; + + // decode distance base of block to copy + while (k < (15)) { // max bits for distance code + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) !== 0) { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) { // get extra bits (up to 13) + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); + k -= (e); + + // do the copy + m -= c; + if (q >= d) { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) { + s.window[q++] = s.window[r++]; // minimum + // count is + // three, + s.window[q++] = s.window[r++]; // so unroll + // loop a + // little + c -= 2; + } else { + s.window.set(s.window.subarray(r, r + 2), q); + q += 2; + r += 2; + c -= 2; + } + } else { // else offset after destination + r = q - d; + do { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) { + do { + s.window[q++] = s.window[r++]; + } while (--e !== 0); + } else { + s.window.set(s.window.subarray(r, r + e), q); + q += e; + r += e; + e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) { + do { + s.window[q++] = s.window[r++]; + } while (--c !== 0); + } else { + s.window.set(s.window.subarray(r, r + c), q); + q += c; + r += c; + c = 0; + } + break; + } else if ((e & 64) === 0) { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } else { + z.msg = "invalid distance code"; + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_DATA_ERROR; + } + } while (true); + break; + } + + if ((e & 64) === 0) { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) === 0) { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2]; + m--; + break; + } + } else if ((e & 32) !== 0) { + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_STREAM_END; + } else { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_DATA_ERROR; + } + } while (true); + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_OK; + } + + that.init = function(bl, bd, tl, tl_index, td, td_index) { + mode = START; + lbits = /* (byte) */bl; + dbits = /* (byte) */bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + }; + + that.proc = function(s, z, r) { + var j; // temporary storage + var tindex; // temporary pointer + var e; // extra bits or operation + var b = 0; // bit buffer + var k = 0; // bits in bit buffer + var p = 0; // input data pointer + var n; // bytes available there + var q; // output window write pointer + var m; // bytes to end of window or read pointer + var f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) { + switch (mode) { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN + if (m >= 258 && n >= 10) { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z); + + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != Z_OK) { + mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = LEN; + /* falls through */ + case LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) { + if (n !== 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e === 0) { // literal + lit = tree[tindex + 2]; + mode = LIT; + break; + } + if ((e & 16) !== 0) { // length + get = e & 15; + len = tree[tindex + 2]; + mode = LENEXT; + break; + } + if ((e & 64) === 0) { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) !== 0) { // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) { + if (n !== 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = DIST; + /* falls through */ + case DIST: // i: get distance next + j = need; + + while (k < (j)) { + if (n !== 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) !== 0) { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = DISTEXT; + break; + } + if ((e & 64) === 0) { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) { + if (n !== 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = COPY; + /* falls through */ + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len !== 0) { + + if (m === 0) { + if (q == s.end && s.read !== 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m === 0) { + s.write = q; + r = s.inflate_flush(z, r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read !== 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + + if (m === 0) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; + m--; + + if (f == s.end) + f = 0; + len--; + } + mode = START; + break; + case LIT: // o: got literal, waiting for output space + if (m === 0) { + if (q == s.end && s.read !== 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m === 0) { + s.write = q; + r = s.inflate_flush(z, r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read !== 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m === 0) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = Z_OK; + + s.window[q++] = /* (byte) */lit; + m--; + + mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; + r = s.inflate_flush(z, r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = END; + /* falls through */ + case END: + r = Z_STREAM_END; + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case BADCODE: // x: got error + + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = Z_STREAM_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + }; + + that.free = function() { + // ZFREE(z, c); + }; + + } + + // InfBlocks + + // Table for deflate from PKZIP's appnote.txt. + var border = [ // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + + var TYPE = 0; // get type bits (3, including end bit) + var LENS = 1; // get lengths for stored + var STORED = 2;// processing stored block + var TABLE = 3; // get table lengths + var BTREE = 4; // get bit lengths tree for a dynamic + // block + var DTREE = 5; // get length, distance trees for a + // dynamic block + var CODES = 6; // processing fixed or dynamic block + var DRY = 7; // output remaining window bytes + var DONELOCKS = 8; // finished last block, done + var BADBLOCKS = 9; // ot a data error--stuck here + + function InfBlocks(z, w) { + var that = this; + + var mode = TYPE; // current inflate_block mode + + var left = 0; // if STORED, bytes left to copy + + var table = 0; // table lengths (14 bits) + var index = 0; // index into blens (or border) + var blens; // bit lengths of codes + var bb = [ 0 ]; // bit length tree depth + var tb = [ 0 ]; // bit length decoding tree + + var codes = new InfCodes(); // if CODES, current state + + var last = 0; // true if this block is the last block + + var hufts = new Int32Array(MANY * 3); // single malloc for tree space + var check = 0; // check on output + var inftree = new InfTree(); + + that.bitk = 0; // bits in bit buffer + that.bitb = 0; // bit buffer + that.window = new Uint8Array(w); // sliding window + that.end = w; // one byte after sliding window + that.read = 0; // window read pointer + that.write = 0; // window write pointer + + that.reset = function(z, c) { + if (c) + c[0] = check; + // if (mode == BTREE || mode == DTREE) { + // } + if (mode == CODES) { + codes.free(z); + } + mode = TYPE; + that.bitk = 0; + that.bitb = 0; + that.read = that.write = 0; + }; + + that.reset(z, null); + + // copy as much as possible from the sliding window to the output area + that.inflate_flush = function(z, r) { + var n; + var p; + var q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = that.read; + + // compute number of bytes to copy as far as end of window + n = /* (int) */((q <= that.write ? that.write : that.end) - q); + if (n > z.avail_out) + n = z.avail_out; + if (n !== 0 && r == Z_BUF_ERROR) + r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // copy as far as end of window + z.next_out.set(that.window.subarray(q, q + n), p); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == that.end) { + // wrap pointers + q = 0; + if (that.write == that.end) + that.write = 0; + + // compute bytes to copy + n = that.write - q; + if (n > z.avail_out) + n = z.avail_out; + if (n !== 0 && r == Z_BUF_ERROR) + r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // copy + z.next_out.set(that.window.subarray(q, q + n), p); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + that.read = q; + + // done + return r; + }; + + that.proc = function(z, r) { + var t; // temporary storage + var b; // bit buffer + var k; // bits in bit buffer + var p; // input data pointer + var n; // bytes available there + var q; // output window write pointer + var m; // bytes to end of window or read pointer + + var i; + + // copy input/output information to locals (UPDATE macro restores) + // { + p = z.next_in_index; + n = z.avail_in; + b = that.bitb; + k = that.bitk; + // } + // { + q = that.write; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + // } + + // process input based on current state + // DEBUG dtree + while (true) { + switch (mode) { + case TYPE: + + while (k < (3)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + t = /* (int) */(b & 7); + last = t & 1; + + switch (t >>> 1) { + case 0: // stored + // { + b >>>= (3); + k -= (3); + // } + t = k & 7; // go to byte boundary + + // { + b >>>= (t); + k -= (t); + // } + mode = LENS; // get length of stored block + break; + case 1: // fixed + // { + var bl = []; // new Array(1); + var bd = []; // new Array(1); + var tl = [ [] ]; // new Array(1); + var td = [ [] ]; // new Array(1); + + InfTree.inflate_trees_fixed(bl, bd, tl, td); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); + // } + + // { + b >>>= (3); + k -= (3); + // } + + mode = CODES; + break; + case 2: // dynamic + + // { + b >>>= (3); + k -= (3); + // } + + mode = TABLE; + break; + case 3: // illegal + + // { + b >>>= (3); + k -= (3); + // } + mode = BADBLOCKS; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + break; + case LENS: + + while (k < (32)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)) { + mode = BADBLOCKS; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left !== 0 ? STORED : (last !== 0 ? DRY : TYPE); + break; + case STORED: + if (n === 0) { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + + if (m === 0) { + if (q == that.end && that.read !== 0) { + q = 0; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + } + if (m === 0) { + that.write = q; + r = that.inflate_flush(z, r); + q = that.write; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + if (q == that.end && that.read !== 0) { + q = 0; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + } + if (m === 0) { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + } + } + r = Z_OK; + + t = left; + if (t > n) + t = n; + if (t > m) + t = m; + that.window.set(z.read_buf(p, t), q); + p += t; + n -= t; + q += t; + m -= t; + if ((left -= t) !== 0) + break; + mode = last !== 0 ? DRY : TYPE; + break; + case TABLE: + + while (k < (14)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { + mode = BADBLOCKS; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (!blens || blens.length < t) { + blens = []; // new Array(t); + } else { + for (i = 0; i < t; i++) { + blens[i] = 0; + } + } + + // { + b >>>= (14); + k -= (14); + // } + + index = 0; + mode = BTREE; + /* falls through */ + case BTREE: + while (index < 4 + (table >>> 10)) { + while (k < (3)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + + // { + b >>>= (3); + k -= (3); + // } + } + + while (index < 19) { + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK) { + r = t; + if (r == Z_DATA_ERROR) { + blens = null; + mode = BADBLOCKS; + } + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + + index = 0; + mode = DTREE; + /* falls through */ + case DTREE: + while (true) { + t = table; + if (index >= 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { + break; + } + + var j, c; + + t = bb[0]; + + while (k < (t)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + // if (tb[0] == -1) { + // System.err.println("null..."); + // } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) { + b >>>= (t); + k -= (t); + blens[index++] = c; + } else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) { + if (n !== 0) { + r = Z_OK; + } else { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + n--; + b |= (z.read_byte(p++) & 0xff) << k; + k += 8; + } + + b >>>= (t); + k -= (t); + + j += (b & inflate_mask[i]); + + b >>>= (i); + k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { + blens = null; + mode = BADBLOCKS; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do { + blens[i++] = c; + } while (--j !== 0); + index = i; + } + } + + tb[0] = -1; + // { + var bl_ = []; // new Array(1); + var bd_ = []; // new Array(1); + var tl_ = []; // new Array(1); + var td_ = []; // new Array(1); + bl_[0] = 9; // must be <= 9 for lookahead assumptions + bd_[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl_, bd_, tl_, td_, hufts, z); + + if (t != Z_OK) { + if (t == Z_DATA_ERROR) { + blens = null; + mode = BADBLOCKS; + } + r = t; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + codes.init(bl_[0], bd_[0], hufts, tl_[0], hufts, td_[0]); + // } + mode = CODES; + /* falls through */ + case CODES: + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + + if ((r = codes.proc(that, z, r)) != Z_STREAM_END) { + return that.inflate_flush(z, r); + } + r = Z_OK; + codes.free(z); + + p = z.next_in_index; + n = z.avail_in; + b = that.bitb; + k = that.bitk; + q = that.write; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + + if (last === 0) { + mode = TYPE; + break; + } + mode = DRY; + /* falls through */ + case DRY: + that.write = q; + r = that.inflate_flush(z, r); + q = that.write; + m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q); + if (that.read != that.write) { + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + mode = DONELOCKS; + /* falls through */ + case DONELOCKS: + r = Z_STREAM_END; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + case BADBLOCKS: + r = Z_DATA_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + + default: + r = Z_STREAM_ERROR; + + that.bitb = b; + that.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + that.write = q; + return that.inflate_flush(z, r); + } + } + }; + + that.free = function(z) { + that.reset(z, null); + that.window = null; + hufts = null; + // ZFREE(z, s); + }; + + that.set_dictionary = function(d, start, n) { + that.window.set(d.subarray(start, start + n), 0); + that.read = that.write = n; + }; + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. + that.sync_point = function() { + return mode == LENS ? 1 : 0; + }; + + } + + // Inflate + + // preset dictionary flag in zlib header + var PRESET_DICT = 0x20; + + var Z_DEFLATED = 8; + + var METHOD = 0; // waiting for method byte + var FLAG = 1; // waiting for flag byte + var DICT4 = 2; // four dictionary check bytes to go + var DICT3 = 3; // three dictionary check bytes to go + var DICT2 = 4; // two dictionary check bytes to go + var DICT1 = 5; // one dictionary check byte to go + var DICT0 = 6; // waiting for inflateSetDictionary + var BLOCKS = 7; // decompressing blocks + var DONE = 12; // finished check, done + var BAD = 13; // got an error--stay here + + var mark = [ 0, 0, 0xff, 0xff ]; + + function Inflate() { + var that = this; + + that.mode = 0; // current inflate mode + + // mode dependent information + that.method = 0; // if FLAGS, method byte + + // if CHECK, check values to compare + that.was = [ 0 ]; // new Array(1); // computed check value + that.need = 0; // stream check value + + // if BAD, inflateSync's marker bytes count + that.marker = 0; + + // mode independent information + that.wbits = 0; // log2(window size) (8..15, defaults to 15) + + // this.blocks; // current inflate_blocks state + + function inflateReset(z) { + if (!z || !z.istate) + return Z_STREAM_ERROR; + + z.total_in = z.total_out = 0; + z.msg = null; + z.istate.mode = BLOCKS; + z.istate.blocks.reset(z, null); + return Z_OK; + } + + that.inflateEnd = function(z) { + if (that.blocks) + that.blocks.free(z); + that.blocks = null; + // ZFREE(z, z->state); + return Z_OK; + }; + + that.inflateInit = function(z, w) { + z.msg = null; + that.blocks = null; + + // set window size + if (w < 8 || w > 15) { + that.inflateEnd(z); + return Z_STREAM_ERROR; + } + that.wbits = w; + + z.istate.blocks = new InfBlocks(z, 1 << w); + + // reset state + inflateReset(z); + return Z_OK; + }; + + that.inflate = function(z, f) { + var r; + var b; + + if (!z || !z.istate || !z.next_in) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (true) { + // System.out.println("mode: "+z.istate.mode); + switch (z.istate.mode) { + case METHOD: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + if (((z.istate.method = z.read_byte(z.next_in_index++)) & 0xf) != Z_DEFLATED) { + z.istate.mode = BAD; + z.msg = "unknown compression method"; + z.istate.marker = 5; // can't try inflateSync + break; + } + if ((z.istate.method >> 4) + 8 > z.istate.wbits) { + z.istate.mode = BAD; + z.msg = "invalid window size"; + z.istate.marker = 5; // can't try inflateSync + break; + } + z.istate.mode = FLAG; + /* falls through */ + case FLAG: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + b = (z.read_byte(z.next_in_index++)) & 0xff; + + if ((((z.istate.method << 8) + b) % 31) !== 0) { + z.istate.mode = BAD; + z.msg = "incorrect header check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) === 0) { + z.istate.mode = BLOCKS; + break; + } + z.istate.mode = DICT4; + /* falls through */ + case DICT4: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + z.istate.need = ((z.read_byte(z.next_in_index++) & 0xff) << 24) & 0xff000000; + z.istate.mode = DICT3; + /* falls through */ + case DICT3: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 16) & 0xff0000; + z.istate.mode = DICT2; + /* falls through */ + case DICT2: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 8) & 0xff00; + z.istate.mode = DICT1; + /* falls through */ + case DICT1: + + if (z.avail_in === 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + z.istate.need += (z.read_byte(z.next_in_index++) & 0xff); + z.istate.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z.istate.mode = BAD; + z.msg = "need dictionary"; + z.istate.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + + r = z.istate.blocks.proc(z, r); + if (r == Z_DATA_ERROR) { + z.istate.mode = BAD; + z.istate.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) { + r = f; + } + if (r != Z_STREAM_END) { + return r; + } + r = f; + z.istate.blocks.reset(z, z.istate.was); + z.istate.mode = DONE; + /* falls through */ + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + } + }; + + that.inflateSetDictionary = function(z, dictionary, dictLength) { + var index = 0; + var length = dictLength; + if (!z || !z.istate || z.istate.mode != DICT0) + return Z_STREAM_ERROR; + + if (length >= (1 << z.istate.wbits)) { + length = (1 << z.istate.wbits) - 1; + index = dictLength - length; + } + z.istate.blocks.set_dictionary(dictionary, index, length); + z.istate.mode = BLOCKS; + return Z_OK; + }; + + that.inflateSync = function(z) { + var n; // number of bytes to look at + var p; // pointer to bytes + var m; // number of marker bytes found in a row + var r, w; // temporaries to save total_in and total_out + + // set up + if (!z || !z.istate) + return Z_STREAM_ERROR; + if (z.istate.mode != BAD) { + z.istate.mode = BAD; + z.istate.marker = 0; + } + if ((n = z.avail_in) === 0) + return Z_BUF_ERROR; + p = z.next_in_index; + m = z.istate.marker; + + // search + while (n !== 0 && m < 4) { + if (z.read_byte(p) == mark[m]) { + m++; + } else if (z.read_byte(p) !== 0) { + m = 0; + } else { + m = 4 - m; + } + p++; + n--; + } + + // restore + z.total_in += p - z.next_in_index; + z.next_in_index = p; + z.avail_in = n; + z.istate.marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) { + return Z_DATA_ERROR; + } + r = z.total_in; + w = z.total_out; + inflateReset(z); + z.total_in = r; + z.total_out = w; + z.istate.mode = BLOCKS; + return Z_OK; + }; + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses + // Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + that.inflateSyncPoint = function(z) { + if (!z || !z.istate || !z.istate.blocks) + return Z_STREAM_ERROR; + return z.istate.blocks.sync_point(); + }; + } + + // ZStream + + function ZStream() { + } + + ZStream.prototype = { + inflateInit : function(bits) { + var that = this; + that.istate = new Inflate(); + if (!bits) + bits = MAX_BITS; + return that.istate.inflateInit(that, bits); + }, + + inflate : function(f) { + var that = this; + if (!that.istate) + return Z_STREAM_ERROR; + return that.istate.inflate(that, f); + }, + + inflateEnd : function() { + var that = this; + if (!that.istate) + return Z_STREAM_ERROR; + var ret = that.istate.inflateEnd(that); + that.istate = null; + return ret; + }, + + inflateSync : function() { + var that = this; + if (!that.istate) + return Z_STREAM_ERROR; + return that.istate.inflateSync(that); + }, + inflateSetDictionary : function(dictionary, dictLength) { + var that = this; + if (!that.istate) + return Z_STREAM_ERROR; + return that.istate.inflateSetDictionary(that, dictionary, dictLength); + }, + read_byte : function(start) { + var that = this; + return that.next_in.subarray(start, start + 1)[0]; + }, + read_buf : function(start, size) { + var that = this; + return that.next_in.subarray(start, start + size); + } + }; + + // Inflater + + function Inflater() { + var that = this; + var z = new ZStream(); + var bufsize = 512; + var flush = Z_NO_FLUSH; + var buf = new Uint8Array(bufsize); + var nomoreinput = false; + + z.inflateInit(); + z.next_out = buf; + + that.append = function(data, onprogress) { + var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array; + if (data.length === 0) + return; + z.next_in_index = 0; + z.next_in = data; + z.avail_in = data.length; + do { + z.next_out_index = 0; + z.avail_out = bufsize; + if ((z.avail_in === 0) && (!nomoreinput)) { // if buffer is empty and more input is available, refill it + z.next_in_index = 0; + nomoreinput = true; + } + err = z.inflate(flush); + if (nomoreinput && (err === Z_BUF_ERROR)) { + if (z.avail_in !== 0) + throw new Error("inflating: bad input"); + } else if (err !== Z_OK && err !== Z_STREAM_END) + throw new Error("inflating: " + z.msg); + if ((nomoreinput || err === Z_STREAM_END) && (z.avail_in === data.length)) + throw new Error("inflating: bad input"); + if (z.next_out_index) + if (z.next_out_index === bufsize) + buffers.push(new Uint8Array(buf)); + else + buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index))); + bufferSize += z.next_out_index; + if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) { + onprogress(z.next_in_index); + lastIndex = z.next_in_index; + } + } while (z.avail_in > 0 || z.avail_out === 0); + array = new Uint8Array(bufferSize); + buffers.forEach(function(chunk) { + array.set(chunk, bufferIndex); + bufferIndex += chunk.length; + }); + return array; + }; + that.flush = function() { + z.inflateEnd(); + }; + } + + // 'zip' may not be defined in z-worker and some tests + var env = global.zip || global; + env.Inflater = env._jzlib_Inflater = Inflater; +})(this); diff --git a/libs/thirdparty/zip.min.js b/libs/thirdparty/zip.min.js new file mode 100644 index 00000000..ad0d681f --- /dev/null +++ b/libs/thirdparty/zip.min.js @@ -0,0 +1 @@ +!function(g){"use strict";var a,y="File format is not recognized.",o="File contains encrypted entry.",l="File is using Zip64 (4gb+ file size).",d="Error while reading zip file.",e="Error while reading file data.",h=524288,f="text/plain";try{a=0===new Blob([new DataView(new ArrayBuffer(0))]).size}catch(t){}function i(){this.crc=-1}function _(){}function m(t,e){var i,n;return i=new ArrayBuffer(t),n=new Uint8Array(i),e&&n.set(e,0),{buffer:i,array:n,view:new DataView(i)}}function t(){}function n(n){var r,a=this;a.size=0,a.init=function(t,e){var i=new Blob([n],{type:f});(r=new s(i)).init(function(){a.size=r.size,t()},e)},a.readUint8Array=function(t,e,i,n){r.readUint8Array(t,e,i,n)}}function r(s){var _,i=this;i.size=0,i.init=function(t){for(var e=s.length;"="==s.charAt(e-1);)e--;_=s.indexOf(",")+1,i.size=Math.floor(.75*(e-_)),t()},i.readUint8Array=function(t,e,i){var n,r=m(e),a=4*Math.floor(t/3),o=4*Math.ceil((t+e)/3),l=g.atob(s.substring(a+_,o+_)),f=t-3*Math.floor(a/4);for(n=f;nt.size)throw new RangeError("offset:"+e+", length:"+i+", size:"+t.size);return t.slice?t.slice(e,e+i):t.webkitSlice?t.webkitSlice(e,e+i):t.mozSlice?t.mozSlice(e,e+i):t.msSlice?t.msSlice(e,e+i):void 0}(a,t,e))}catch(t){n(t)}}}function u(){}function c(n){var r;this.init=function(t){r=new Blob([],{type:f}),t()},this.writeUint8Array=function(t,e){r=new Blob([r,a?t:t.buffer],{type:f}),e()},this.getData=function(e,t){var i=new FileReader;i.onload=function(t){e(t.target.result)},i.onerror=t,i.readAsText(r,n)}}function b(e){var a="",o="";this.init=function(t){a+="data:"+(e||"")+";base64,",t()},this.writeUint8Array=function(t,e){var i,n=o.length,r=o;for(o="",i=0;i<3*Math.floor((n+t.length)/3)-n;i++)r+=String.fromCharCode(t[i]);for(;i>16,i=65535&t;try{return new Date(1980+((65024&e)>>9),((480&e)>>5)-1,31&e,(63488&i)>>11,(2016&i)>>5,2*(31&i),0)}catch(t){}}(t.lastModDateRaw),1!=(1&t.bitFlag)?(!n&&8==(8&t.bitFlag)||(t.crc32=e.view.getUint32(i+10,!0),t.compressedSize=e.view.getUint32(i+14,!0),t.uncompressedSize=e.view.getUint32(i+18,!0)),4294967295!==t.compressedSize&&4294967295!==t.uncompressedSize?(t.filenameLength=e.view.getUint16(i+22,!0),t.extraFieldLength=e.view.getUint16(i+24,!0)):r(l)):r(o)}function v(h,t,v){function _(){}_.prototype.getData=function(_,r,d,u){var c=this;function b(t,e){var i,n;u&&(i=e,(n=m(4)).view.setUint32(0,i),c.crc32!=n.view.getUint32(0))?v("CRC failed."):_.getData(function(t){r(t)})}function w(t){v(t||e)}function x(t){v(t||"Error while writing file data.")}h.readUint8Array(c.offset,30,function(t){var s,e=m(t.length,t);1347093252==e.view.getUint32(0)?(z(c,e,4,!1,v),s=c.offset+30+c.filenameLength+c.extraFieldLength,_.init(function(){var t,e,i,n,r,a,o,l,f;0===c.compressionMethod?p(0,h,_,s,c.compressedSize,0,b,d,w,x):(t=h,e=_,i=s,n=c.compressedSize,r=b,a=d,o=w,l=x,f=u?"output":"none",k(new g.zip.Inflater,t,e,i,n,f,a,r,o,l))},x)):v(y)},w)},t({getEntries:function(s){!function(n){var r=22;if(h.size=h.size?v(y):h.readUint8Array(e,h.size-e,function(t){var e,i,n,r,a=0,o=[],l=m(t.length,t);for(e=0;e>>8^i[255&(e^t[n])];this.crc=e},i.prototype.get=function(){return~this.crc},i.prototype.table=function(){var t,e,i,n=[];for(t=0;t<256;t++){for(i=t,e=0;e<8;e++)1&i?i=i>>>1^3988292384:i>>>=1;n[t]=i}return n}(),_.prototype.append=function(t){return t},_.prototype.flush=function(){},(n.prototype=new t).constructor=n,(r.prototype=new t).constructor=r,(s.prototype=new t).constructor=s,u.prototype.getData=function(t){t(this.data)},(c.prototype=new u).constructor=c,(b.prototype=new u).constructor=b,(w.prototype=new u).constructor=w,g.zip={Reader:t,Writer:u,BlobReader:s,Data64URIReader:r,TextReader:n,BlobWriter:w,Data64URIWriter:b,TextWriter:c,createReader:function(t,e,i){i=i||C,t.init(function(){v(t,e,i)},i)},createWriter:function(t,e,i,n){i=i||C,n=!!n,t.init(function(){F(t,e,i,n)},i)}}}(this),function(t){"use strict";var I=0,M=1,R=-2,L=-3,b=-4,B=-5,T=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],W=1440,r=[96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,192,80,7,10,0,8,96,0,8,32,0,9,160,0,8,0,0,8,128,0,8,64,0,9,224,80,7,6,0,8,88,0,8,24,0,9,144,83,7,59,0,8,120,0,8,56,0,9,208,81,7,17,0,8,104,0,8,40,0,9,176,0,8,8,0,8,136,0,8,72,0,9,240,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,200,81,7,13,0,8,100,0,8,36,0,9,168,0,8,4,0,8,132,0,8,68,0,9,232,80,7,8,0,8,92,0,8,28,0,9,152,84,7,83,0,8,124,0,8,60,0,9,216,82,7,23,0,8,108,0,8,44,0,9,184,0,8,12,0,8,140,0,8,76,0,9,248,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,196,81,7,11,0,8,98,0,8,34,0,9,164,0,8,2,0,8,130,0,8,66,0,9,228,80,7,7,0,8,90,0,8,26,0,9,148,84,7,67,0,8,122,0,8,58,0,9,212,82,7,19,0,8,106,0,8,42,0,9,180,0,8,10,0,8,138,0,8,74,0,9,244,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,204,81,7,15,0,8,102,0,8,38,0,9,172,0,8,6,0,8,134,0,8,70,0,9,236,80,7,9,0,8,94,0,8,30,0,9,156,84,7,99,0,8,126,0,8,62,0,9,220,82,7,27,0,8,110,0,8,46,0,9,188,0,8,14,0,8,142,0,8,78,0,9,252,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,194,80,7,10,0,8,97,0,8,33,0,9,162,0,8,1,0,8,129,0,8,65,0,9,226,80,7,6,0,8,89,0,8,25,0,9,146,83,7,59,0,8,121,0,8,57,0,9,210,81,7,17,0,8,105,0,8,41,0,9,178,0,8,9,0,8,137,0,8,73,0,9,242,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,202,81,7,13,0,8,101,0,8,37,0,9,170,0,8,5,0,8,133,0,8,69,0,9,234,80,7,8,0,8,93,0,8,29,0,9,154,84,7,83,0,8,125,0,8,61,0,9,218,82,7,23,0,8,109,0,8,45,0,9,186,0,8,13,0,8,141,0,8,77,0,9,250,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,198,81,7,11,0,8,99,0,8,35,0,9,166,0,8,3,0,8,131,0,8,67,0,9,230,80,7,7,0,8,91,0,8,27,0,9,150,84,7,67,0,8,123,0,8,59,0,9,214,82,7,19,0,8,107,0,8,43,0,9,182,0,8,11,0,8,139,0,8,75,0,9,246,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,206,81,7,15,0,8,103,0,8,39,0,9,174,0,8,7,0,8,135,0,8,71,0,9,238,80,7,9,0,8,95,0,8,31,0,9,158,84,7,99,0,8,127,0,8,63,0,9,222,82,7,27,0,8,111,0,8,47,0,9,190,0,8,15,0,8,143,0,8,79,0,9,254,96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,193,80,7,10,0,8,96,0,8,32,0,9,161,0,8,0,0,8,128,0,8,64,0,9,225,80,7,6,0,8,88,0,8,24,0,9,145,83,7,59,0,8,120,0,8,56,0,9,209,81,7,17,0,8,104,0,8,40,0,9,177,0,8,8,0,8,136,0,8,72,0,9,241,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,201,81,7,13,0,8,100,0,8,36,0,9,169,0,8,4,0,8,132,0,8,68,0,9,233,80,7,8,0,8,92,0,8,28,0,9,153,84,7,83,0,8,124,0,8,60,0,9,217,82,7,23,0,8,108,0,8,44,0,9,185,0,8,12,0,8,140,0,8,76,0,9,249,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,197,81,7,11,0,8,98,0,8,34,0,9,165,0,8,2,0,8,130,0,8,66,0,9,229,80,7,7,0,8,90,0,8,26,0,9,149,84,7,67,0,8,122,0,8,58,0,9,213,82,7,19,0,8,106,0,8,42,0,9,181,0,8,10,0,8,138,0,8,74,0,9,245,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,205,81,7,15,0,8,102,0,8,38,0,9,173,0,8,6,0,8,134,0,8,70,0,9,237,80,7,9,0,8,94,0,8,30,0,9,157,84,7,99,0,8,126,0,8,62,0,9,221,82,7,27,0,8,110,0,8,46,0,9,189,0,8,14,0,8,142,0,8,78,0,9,253,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,195,80,7,10,0,8,97,0,8,33,0,9,163,0,8,1,0,8,129,0,8,65,0,9,227,80,7,6,0,8,89,0,8,25,0,9,147,83,7,59,0,8,121,0,8,57,0,9,211,81,7,17,0,8,105,0,8,41,0,9,179,0,8,9,0,8,137,0,8,73,0,9,243,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,203,81,7,13,0,8,101,0,8,37,0,9,171,0,8,5,0,8,133,0,8,69,0,9,235,80,7,8,0,8,93,0,8,29,0,9,155,84,7,83,0,8,125,0,8,61,0,9,219,82,7,23,0,8,109,0,8,45,0,9,187,0,8,13,0,8,141,0,8,77,0,9,251,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,199,81,7,11,0,8,99,0,8,35,0,9,167,0,8,3,0,8,131,0,8,67,0,9,231,80,7,7,0,8,91,0,8,27,0,9,151,84,7,67,0,8,123,0,8,59,0,9,215,82,7,19,0,8,107,0,8,43,0,9,183,0,8,11,0,8,139,0,8,75,0,9,247,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,207,81,7,15,0,8,103,0,8,39,0,9,175,0,8,7,0,8,135,0,8,71,0,9,239,80,7,9,0,8,95,0,8,31,0,9,159,84,7,99,0,8,127,0,8,63,0,9,223,82,7,27,0,8,111,0,8,47,0,9,191,0,8,15,0,8,143,0,8,79,0,9,255],a=[80,5,1,87,5,257,83,5,17,91,5,4097,81,5,5,89,5,1025,85,5,65,93,5,16385,80,5,3,88,5,513,84,5,33,92,5,8193,82,5,9,90,5,2049,86,5,129,192,5,24577,80,5,2,87,5,385,83,5,25,91,5,6145,81,5,7,89,5,1537,85,5,97,93,5,24577,80,5,4,88,5,769,84,5,49,92,5,12289,82,5,13,90,5,3073,86,5,193,192,5,24577],w=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],x=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,112,112],h=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],v=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],C=15;function V(){var _,d,z,S,D,F;function u(t,e,i,n,r,a,o,l,f,s,_){var d,u,c,b,w,x,h,v,g,y,m,k,p,U,A;for(y=0,w=i;z[t[e+y]]++,y++,0!==--w;);if(z[0]==i)return o[0]=-1,l[0]=0,I;for(v=l[0],x=1;x<=C&&0===z[x];x++);for(v<(h=x)&&(v=x),w=C;0!==w&&0===z[w];w--);for((c=w)d+1&&(u-=d+1,p=h,xW)return L;D[b]=m=s[0],s[0]+=A,0!==b?(F[b]=w,S[0]=x,x=w>>>k-(S[1]=v),S[2]=m-D[b-1]-x,f.set(S,3*(D[b-1]+x))):o[0]=m}for(S[1]=h-k,i<=y?S[0]=192:_[y]>>k;x>>=1)w^=x;for(w^=x,g=(1<>=s[p+1],c-=s[p+1],0!=(16&d)){for(d&=15,y=s[p+2]+(u&T[d]),u>>=d,c-=d;c<15;)w--,u|=(255&l.read_byte(b++))<>=s[p+1],c-=s[p+1],0!=(16&d)){for(d&=15;c>=d,c-=d,h-=y,m<=x)0>3<(y=l.avail_in-w)?c>>3:y,b-=y,c-=y<<3,o.bitb=u,o.bitk=c,l.avail_in=w,l.total_in+=b-l.next_in_index,l.next_in_index=b,o.write=x,L;f+=s[p+2],d=s[p=3*(_+(f+=u&T[d]))]}break}if(0!=(64&d))return 0!=(32&d)?(w+=y=c>>3<(y=l.avail_in-w)?c>>3:y,b-=y,c-=y<<3,o.bitb=u,o.bitk=c,l.avail_in=w,l.total_in+=b-l.next_in_index,l.next_in_index=b,o.write=x,M):(l.msg="invalid literal/length code",w+=y=c>>3<(y=l.avail_in-w)?c>>3:y,b-=y,c-=y<<3,o.bitb=u,o.bitk=c,l.avail_in=w,l.total_in+=b-l.next_in_index,l.next_in_index=b,o.write=x,L);if(f+=s[p+2],0===(d=s[p=3*(_+(f+=u&T[d]))])){u>>=s[p+1],c-=s[p+1],o.window[x++]=s[p+2],h--;break}}else u>>=s[p+1],c-=s[p+1],o.window[x++]=s[p+2],h--}while(258<=h&&10<=w);return w+=y=c>>3<(y=l.avail_in-w)?c>>3:y,b-=y,c-=y<<3,o.bitb=u,o.bitk=c,l.avail_in=w,l.total_in+=b-l.next_in_index,l.next_in_index=b,o.write=x,I}this.init=function(t,e,i,n,r,a){c=D,p=t,U=e,w=i,A=n,x=r,z=a,b=null},this.proc=function(t,e,i){var n,r,a,o,l,f,s,_=0,d=0,u=0;for(u=e.next_in_index,o=e.avail_in,_=t.bitb,d=t.bitk,f=(l=t.write)>>=b[r+1],d-=b[r+1],0===(a=b[r])){y=b[r+2],c=H;break}if(0!=(16&a)){m=15&a,h=b[r+2],c=E;break}if(0==(64&a)){g=a,v=r/3+b[r+2];break}if(0==(32&a))return c=q,e.msg="invalid literal/length code",i=L,t.bitb=_,t.bitk=d,e.avail_in=o,e.total_in+=u-e.next_in_index,e.next_in_index=u,t.write=l,t.inflate_flush(e,i);c=Y;break;case E:for(n=m;d>=n,d-=n,g=U,b=x,v=z,c=O;case O:for(n=g;d>=b[r+1],d-=b[r+1],0!=(16&(a=b[r]))){m=15&a,k=b[r+2],c=P;break}if(0!=(64&a))return c=q,e.msg="invalid distance code",i=L,t.bitb=_,t.bitk=d,e.avail_in=o,e.total_in+=u-e.next_in_index,e.next_in_index=u,t.write=l,t.inflate_flush(e,i);g=a,v=r/3+b[r+2];break;case P:for(n=m;d>=n,d-=n,c=j;case j:for(s=l-k;s<0;)s+=t.end;for(;0!==h;){if(0===f&&(l==t.end&&0!==t.read&&(f=(l=0)t.avail_out&&(i=t.avail_out),0!==i&&e==B&&(e=I),t.avail_out-=i,t.total_out+=i,t.next_out.set(m.window.subarray(r,r+i),n),n+=i,(r+=i)==m.end&&(r=0,m.write==m.end&&(m.write=0),(i=m.write-r)>t.avail_out&&(i=t.avail_out),0!==i&&e==B&&(e=I),t.avail_out-=i,t.total_out+=i,t.next_out.set(m.window.subarray(r,r+i),n),n+=i,r+=i),t.next_out_index=n,m.read=r,e},m.proc=function(t,e){var i,n,r,a,o,l,f,s;for(a=t.next_in_index,o=t.avail_in,n=m.bitb,r=m.bitk,f=(l=m.write)>>1){case 0:n>>>=3,n>>>=i=7&(r-=3),r-=i,k=K;break;case 1:var _=[],d=[],u=[[]],c=[[]];V.inflate_trees_fixed(_,d,u,c),D.init(_[0],d[0],u[0],0,c[0],0),n>>>=3,r-=3,k=tt;break;case 2:n>>>=3,r-=3,k=Q;break;case 3:return n>>>=3,r-=3,k=nt,t.msg="invalid block type",e=L,m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e)}break;case K:for(;r<32;){if(0===o)return m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);e=I,o--,n|=(255&t.read_byte(a++))<>>16&65535)!=(65535&n))return k=nt,t.msg="invalid stored block lengths",e=L,m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);p=65535&n,n=r=0,k=0!==p?N:0!==F?et:J;break;case N:if(0===o)return m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);if(0===f&&(l==m.end&&0!==m.read&&(f=(l=0)>5&31))return k=nt,t.msg="too many length or distance symbols",e=L,m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);if(i=258+(31&i)+(i>>5&31),!y||y.length>>=14,r-=14,A=0,k=X;case X:for(;A<4+(U>>>10);){for(;r<3;){if(0===o)return m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);e=I,o--,n|=(255&t.read_byte(a++))<>>=3,r-=3}for(;A<19;)y[G[A++]]=0;if(z[0]=7,(i=E.inflate_trees_bits(y,z,S,C,t))!=I)return(e=i)==L&&(y=null,k=nt),m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);A=0,k=$;case $:for(;!(258+(31&(i=U))+(i>>5&31)<=A);){var b,w;for(i=z[0];r>>=i,r-=i,y[A++]=w;else{for(s=18==w?7:w-14,b=18==w?11:3;r>>=i)&T[s],n>>>=s,r-=s,258+(31&(i=U))+(i>>5&31)<(s=A)+b||16==w&&s<1)return y=null,k=nt,t.msg="invalid bit length repeat",e=L,m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);for(w=16==w?y[s-1]:0;y[s++]=w,0!=--b;);A=s}}S[0]=-1;var x=[],h=[],v=[],g=[];if(x[0]=9,h[0]=6,i=U,(i=E.inflate_trees_dynamic(257+(31&i),1+(i>>5&31),y,x,h,v,g,C,t))!=I)return i==L&&(y=null,k=nt),e=i,m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,m.inflate_flush(t,e);D.init(x[0],h[0],C,v[0],C,g[0]),k=tt;case tt:if(m.bitb=n,m.bitk=r,t.avail_in=o,t.total_in+=a-t.next_in_index,t.next_in_index=a,m.write=l,(e=D.proc(m,t,e))!=M)return m.inflate_flush(t,e);if(e=I,D.free(t),a=t.next_in_index,o=t.avail_in,n=m.bitb,r=m.bitk,f=(l=m.write)>4)>t.istate.wbits){t.istate.mode=13,t.msg="invalid window size",t.istate.marker=5;break}t.istate.mode=1;case 1:if(0===t.avail_in)return i;if(i=e,t.avail_in--,t.total_in++,n=255&t.read_byte(t.next_in_index++),((t.istate.method<<8)+n)%31!=0){t.istate.mode=13,t.msg="incorrect header check",t.istate.marker=5;break}if(0==(32&n)){t.istate.mode=7;break}t.istate.mode=2;case 2:if(0===t.avail_in)return i;i=e,t.avail_in--,t.total_in++,t.istate.need=(255&t.read_byte(t.next_in_index++))<<24&4278190080,t.istate.mode=3;case 3:if(0===t.avail_in)return i;i=e,t.avail_in--,t.total_in++,t.istate.need+=(255&t.read_byte(t.next_in_index++))<<16&16711680,t.istate.mode=4;case 4:if(0===t.avail_in)return i;i=e,t.avail_in--,t.total_in++,t.istate.need+=(255&t.read_byte(t.next_in_index++))<<8&65280,t.istate.mode=5;case 5:return 0===t.avail_in?i:(i=e,t.avail_in--,t.total_in++,t.istate.need+=255&t.read_byte(t.next_in_index++),t.istate.mode=6,2);case 6:return t.istate.mode=13,t.msg="need dictionary",t.istate.marker=0,R;case 7:if((i=t.istate.blocks.proc(t,i))==L){t.istate.mode=13,t.istate.marker=0;break}if(i==I&&(i=e),i!=M)return i;i=e,t.istate.blocks.reset(t,t.istate.was),t.istate.mode=12;case 12:return M;case 13:return L;default:return R}},i.inflateSetDictionary=function(t,e,i){var n=0,r=i;return t&&t.istate&&6==t.istate.mode?(r>=1<= 8; font_size--) { + ctx.font = font.replace(/(\d+)px/, font_size+"px"); + if (ctx.measureText(text).width <= maxWidth) break; + } + ctx.fillText(text, x, y); + ctx.font = font; } ////// 在某个canvas上绘制粗体 ////// @@ -66,6 +106,7 @@ ui.prototype.fillBoldText = function (name, text, x, y, style, font) { if (!ctx) return; if (font) ctx.font = font; if (!style) style = ctx.fillStyle; + if (style instanceof Array) style = core.arrayToRGBA(style); ctx.fillStyle = '#000000'; ctx.fillText(text, x-1, y-1); ctx.fillText(text, x-1, y+1); @@ -75,6 +116,11 @@ ui.prototype.fillBoldText = function (name, text, x, y, style, font) { ctx.fillText(text, x, y); } +ui.prototype._uievent_fillBoldText = function (data) { + this._createUIEvent(); + this.fillBoldText('uievent', core.replaceText(data.text), core.calValue(data.x), core.calValue(data.y), data.style, data.font); +} + ////// 在某个canvas上绘制一个矩形 ////// ui.prototype.fillRect = function (name, x, y, width, height, style) { if (style) core.setFillStyle(name, style); @@ -82,6 +128,32 @@ ui.prototype.fillRect = function (name, x, y, width, height, style) { if (ctx) ctx.fillRect(x, y, width, height); } +ui.prototype._uievent_fillRect = function (data) { + this._createUIEvent(); + this.fillRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), data.style); +} + +////// 在某个canvas上绘制一个多边形 ////// +ui.prototype.fillPolygon = function (name, nodes, style) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (!ctx) return; + if (!nodes || nodes.length<3) return; + ctx.beginPath(); + for (var i = 0; i < nodes.length; ++i) { + var x = core.calValue(nodes[i][0]), y = core.calValue(nodes[i][1]); + if (i == 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + } + ctx.closePath(); + ctx.fill(); +} + +ui.prototype._uievent_fillPolygon = function (data) { + this._createUIEvent(); + this.fillPolygon('uievent', data.nodes, data.style); +} + ////// 在某个canvas上绘制一个矩形的边框 ////// ui.prototype.strokeRect = function (name, x, y, width, height, style, lineWidth) { if (style) core.setStrokeStyle(name, style); @@ -90,6 +162,64 @@ ui.prototype.strokeRect = function (name, x, y, width, height, style, lineWidth) if (ctx) ctx.strokeRect(x, y, width, height); } +ui.prototype._uievent_strokeRect = function (data) { + this._createUIEvent(); + this.strokeRect('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height), data.style, data.lineWidth); +} + +////// 在某个canvas上绘制一个多边形的边框 ////// +ui.prototype.strokePolygon = function (name, nodes, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + if (!nodes || nodes.length<3) return; + ctx.beginPath(); + for (var i = 0; i < nodes.length; ++i) { + var x = core.calValue(nodes[i][0]), y = core.calValue(nodes[i][1]); + if (i == 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + } + ctx.closePath(); + ctx.stroke(); +} + +ui.prototype._uievent_strokePolygon = function (data) { + this._createUIEvent(); + this.strokePolygon('uievent', data.nodes, data.style, data.lineWidth); +} + +////// 在某个canvas上绘制一个圆 ////// +ui.prototype.fillCircle = function (name, x, y, r, style) { + if (style) core.setFillStyle(name, style); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.arc(x, y, r, 0, 2*Math.PI); + ctx.fill(); +} + +ui.prototype._uievent_fillCircle = function (data) { + this._createUIEvent(); + this.fillCircle('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.r), data.style); +} + +////// 在某个canvas上绘制一个圆的边框 ////// +ui.prototype.strokeCircle = function (name, x, y, r, style, lineWidth) { + if (style) core.setStrokeStyle(name, style); + if (lineWidth) core.setLineWidth(name, lineWidth); + var ctx = this.getContextByName(name); + if (!ctx) return; + ctx.beginPath(); + ctx.arc(x, y, r, 0, 2*Math.PI); + ctx.stroke(); +} + +ui.prototype._uievent_strokeCircle = function (data) { + this._createUIEvent(); + this.strokeCircle('uievent', core.calValue(data.x), core.calValue(data.y), core.calValue(data.r), data.style, data.lineWidth); +} + ////// 在某个canvas上绘制一条线 ////// ui.prototype.drawLine = function (name, x1, y1, x2, y2, style, lineWidth) { if (style) core.setStrokeStyle(name, style); @@ -102,6 +232,11 @@ ui.prototype.drawLine = function (name, x1, y1, x2, y2, style, lineWidth) { ctx.stroke(); } +ui.prototype._uievent_drawLine = function (data) { + this._createUIEvent(); + this.drawLine('uievent', core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.x2), core.calValue(data.y2), data.style, data.lineWidth); +} + ////// 在某个canvas上绘制一个箭头 ////// ui.prototype.drawArrow = function (name, x1, y1, x2, y2, style, lineWidth) { if (x1==x2 && y1==y2) return; @@ -121,6 +256,11 @@ ui.prototype.drawArrow = function (name, x1, y1, x2, y2, style, lineWidth) { ctx.stroke(); } +ui.prototype._uievent_drawArrow = function (data) { + this._createUIEvent(); + this.drawArrow('uievent', core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.x2), core.calValue(data.y2), data.style, data.lineWidth); +} + ////// 设置某个canvas的文字字体 ////// ui.prototype.setFont = function (name, font) { var ctx = this.getContextByName(name); @@ -160,12 +300,14 @@ ui.prototype.setOpacity = function (name, opacity) { ////// 设置某个canvas的绘制属性(如颜色等) ////// ui.prototype.setFillStyle = function (name, style) { var ctx = this.getContextByName(name); + if (style instanceof Array) style = core.arrayToRGBA(style); if (ctx) ctx.fillStyle = style; } ////// 设置某个canvas边框属性 ////// ui.prototype.setStrokeStyle = function (name, style) { var ctx = this.getContextByName(name); + if (style instanceof Array) style = core.arrayToRGBA(style); if (ctx) ctx.strokeStyle = style; } @@ -181,6 +323,23 @@ ui.prototype.setTextBaseline = function (name, baseline) { if (ctx) ctx.textBaseline = baseline; } +ui.prototype._uievent_setAttribute = function (data) { + this._createUIEvent(); + if (data.font) this.setFont('uievent', data.font); + if (data.lineWidth) this.setLineWidth('uievent', data.lineWidth); + if (data.alpha != null) this.setAlpha('uievent', data.alpha); + if (data.fillStyle) this.setFillStyle('uievent', data.fillStyle); + if (data.strokeStyle) this.setStrokeStyle('uievent', data.strokeStyle); + if (data.align) this.setTextAlign('uievent', data.align); + if (data.baseline) this.setTextBaseline('uievent', data.baseline); + if (data.z != null && main.mode != 'editor') { + var z = parseInt(data.z) || 135; + core.dymCanvas.uievent.canvas.style.zIndex = z; + if (core.dymCanvas._uievent_selector) + core.dymCanvas._uievent_selector.canvas.style.zIndex = z + 1; + } +} + ////// 计算某段文字的宽度 ////// ui.prototype.calWidth = function (name, text, font) { var ctx = this.getContextByName(name); @@ -194,7 +353,7 @@ ui.prototype.calWidth = function (name, text, font) { ////// 字符串自动换行的分割 ////// ui.prototype.splitLines = function (name, text, maxWidth, font) { var ctx = this.getContextByName(name); - if (!ctx) return; + if (!ctx) return [text]; if (font) core.setFont(name, font); var contents = []; @@ -226,14 +385,15 @@ 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.getMappedName(image); image = core.material.images.images[image]; if (!image) return; } // 只能接受2, 4, 8个参数 - if (core.isset(x) && core.isset(y)) { - if (core.isset(w) && core.isset(h)) { - if (core.isset(x1) && core.isset(y1) && core.isset(w1) && core.isset(h1)) { + if (x != null && y != null) { + if (w != null && h != null) { + if (x1 != null && y1 != null && w1 != null && h1 != null) { ctx.drawImage(image, x, y, w, h, x1, y1, w1, h1); return; } @@ -245,13 +405,42 @@ ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1) { } } +ui.prototype._uievent_drawImage = function (data) { + this._createUIEvent(); + this.drawImage('uievent', data.image, core.calValue(data.x), core.calValue(data.y), core.calValue(data.w), core.calValue(data.h), + core.calValue(data.x1), core.calValue(data.y1), core.calValue(data.w1), core.calValue(data.h1)); +} + +ui.prototype.drawIcon = function (name, id, x, y, w, h) { + var ctx = this.getContextByName(name); + if (!ctx) return; + var info = core.getBlockInfo(id); + if (!info) { + // 检查状态栏图标 + if (core.statusBar.icons[id] instanceof Image) + info = {image: core.statusBar.icons[id], posX: 0, posY: 0, height: 32}; + else return; + } + ctx.drawImage(info.image, 32 * info.posX, info.height * info.posY, 32, info.height, x, y, w || 32, h || info.height); +} + +ui.prototype._uievent_drawIcon = function (data) { + this._createUIEvent(); + var id; + try { + id = core.calValue(data.id); + if (typeof id !== 'string') id = data.id; + } catch (e) { id = data.id; } + this.drawIcon('uievent', id, core.calValue(data.x), core.calValue(data.y), core.calValue(data.width), core.calValue(data.height)); +} + ///////////////// UI绘制 ////// 结束一切事件和绘制,关闭UI窗口,返回游戏进程 ////// ui.prototype.closePanel = function () { this.clearUI(); core.maps.generateGroundPattern(); - core.updateStatusBar(); + core.updateStatusBar(true); core.unLockControl(); core.status.event.data = null; core.status.event.id = null; @@ -263,71 +452,59 @@ ui.prototype.closePanel = function () { ui.prototype.clearUI = function () { core.status.boxAnimateObjs = []; if (core.dymCanvas._selector) core.deleteCanvas("_selector"); + main.dom.next.style.display = 'none'; core.clearMap('ui'); core.setAlpha('ui', 1); } ////// 左上角绘制一段提示 ////// -ui.prototype.drawTip = function (text, id) { - var textX, textY, width, height; - clearInterval(core.interval.tipAnimate); - core.setFont('data', "16px Arial"); - core.setTextAlign('data', 'left'); +ui.prototype.drawTip = function (text, id, clear) { + this.clearTip(); + var one = { + text: text, + textX: 21, + width: 26 + core.calWidth('data', text, "16px Arial"), + opacity: 0.1, + stage: 1, + time: 0 + }; if (id != null) { var info = core.getBlockInfo(id); - if (info == null || !info.image || info.height != 32) { + if (info == null || !info.image) { // 检查状态栏图标 if (core.statusBar.icons[id] instanceof Image) { - id = {image: core.statusBar.icons[id], posX: 0, posY: 0}; + id = {image: core.statusBar.icons[id], posX: 0, posY: 0, height: 32}; } - else id = null; + else info = null; + } + if (info != null) { + one.image = info.image; + one.posX = info.posX; + one.posY = info.posY; + one.height = info.height; + one.textX += 24; + one.width += 24; } - else id = info; } - if (!id) { - textX = 16; - textY = 18; - width = textX + core.calWidth('data', text) + 16; - height = 42; + core.animateFrame.tips.list.push(one); + if (core.animateFrame.tips.list.length > 3) { + core.animateFrame.tips.list.shift(); } - else { - textX = 44; - textY = 18; - 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, core.ui.PIXEL, height); - core.setAlpha('data', alpha); - core.fillRect('data', 5, 5, width, height, '#000'); - 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, core.ui.PIXEL, height); - clearInterval(core.interval.tipAnimate); - return; - } - else { - if (!core.timeout.tipTimeout) { - core.timeout.tipTimeout = window.setTimeout(function () { - hide = true; - core.timeout.tipTimeout = null; - }, 750); - } - alpha = 0.6; - } - } - }, 30); +ui.prototype._drawTip_drawOne = function (one, offset) { + core.setAlpha('data', one.opacity); + core.fillRect('data', 5, offset+ 5, one.width, 42, '#000000'); + if (one.image) + core.drawImage('data', one.image, one.posX * 32, one.posY * one.height, 32, 32, 10, offset + 10, 32, 32); + core.fillText('data', one.text, one.textX, offset + 33, '#FFFFFF'); + core.setAlpha('data', 1); +} + +ui.prototype.clearTip = function () { + core.animateFrame.tips.list = []; + core.animateFrame.tips.offset = 0; + core.animateFrame.tips.lastSize = 0; } ////// 地图中间绘制一段文字 ////// @@ -350,7 +527,8 @@ ui.prototype.drawText = function (contents, callback) { ui.prototype._drawText_setContent = function (contents, callback) { // 合并进 insertAction - if ((core.status.event && core.status.event.id=='action') || core.isReplaying()) { + if ((core.status.event && core.status.event.id=='action') + || (!core.hasFlag('__replayText__') && core.isReplaying())) { core.insertAction(contents,null,null,callback); return; } @@ -372,15 +550,17 @@ ui.prototype._getTitleAndIcon = function (content) { title = core.status.hero.name; image = core.material.images.hero; icon = 0; - height = core.material.icons.hero.height; + var w = core.material.icons.hero.width || 32; + height = 32 * core.material.icons.hero.height / w; } - else if (/^[-\w.]+\.png$/.test(s4)) + else if (s4.endsWith(".png")) { + s4 = core.getMappedName(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; - else title = s4; + if (blockInfo.name) title = blockInfo.name; image = blockInfo.image; icon = blockInfo.posY; height = blockInfo.height; @@ -389,7 +569,10 @@ ui.prototype._getTitleAndIcon = function (content) { else title = s4; } } - if (s3) title = s3; + if (s3 != null) { + title = s3; + if (title == 'null') title = null; + } return ""; }); return { @@ -404,7 +587,7 @@ ui.prototype._getTitleAndIcon = function (content) { ////// 正则处理 \b[up,xxx] 问题 ui.prototype._getPosition = function (content) { - var pos = null, px = null, py = null; + var pos = null, px = null, py = null, noPeak = false; if (core.status.event.id=='action') { px = core.status.event.data.x; py = core.status.event.data.y; @@ -431,6 +614,7 @@ ui.prototype._getPosition = function (content) { } else{ px = parseInt(str[0]); py = parseInt(str[1]); + noPeak = core.getBlockId(px, py) == null; } } if(pos=='hero' || pos=='null'){ @@ -438,32 +622,64 @@ ui.prototype._getPosition = function (content) { } return ""; }); - return {content: content, position: pos, px: px, py: py}; + return {content: content, position: pos, px: px, py: py, noPeak: noPeak}; } ////// 绘制选择光标 ui.prototype.drawWindowSelector = function(background, x, y, w, h) { + w = Math.round(w), h = Math.round(h); + var ctx = core.ui.createCanvas("_selector", x, y, w, h, 165); + this._drawSelector(ctx, background, w, h); +} + +ui.prototype._uievent_drawSelector = function (data) { + var canvasName = '_uievent_selector_' + (data.code || 0); + if (data.image == null) return core.deleteCanvas(canvasName); + + var background = data.image || core.status.textAttribute.background; + if (typeof background != 'string') return; + var x = core.calValue(data.x), y = core.calValue(data.y), w = core.calValue(data.width), h = core.calValue(data.height); + w = Math.round(w); h = Math.round(h); + if (main.mode == 'editor') { + this._drawSelector('uievent', background, w, h, x, y); + return; + } + var z = 136; + if (core.dymCanvas.uievent) z = (parseInt(core.dymCanvas.uievent.canvas.style.zIndex) || 135) + 1; + var ctx = core.createCanvas(canvasName, x, y, w, h, z); + ctx.canvas.classList.add('_uievent_selector'); + this._drawSelector(ctx, background, w, h); +} + +ui.prototype._drawSelector = function (ctx, background, w, h, left, top) { + left = left || 0; + top = top || 0; + ctx = this.getContextByName(ctx); + if (!ctx) return; 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); + if (!(background instanceof Image)) return; // back - dstImage.drawImage(background, 130, 66, 28, 28, 2, 2,w-4,h-4); + ctx.drawImage(background, 130, 66, 28, 28, left+2, top+2, w-4, h-4); // corner - dstImage.drawImage(background, 128, 64, 2, 2, 0, 0, 2, 2); - dstImage.drawImage(background, 158, 64, 2, 2,w-2, 0, 2, 2); - dstImage.drawImage(background, 128, 94, 2, 2, 0,h-2, 2, 2); - dstImage.drawImage(background, 158, 94, 2, 2,w-2,h-2, 2, 2); + ctx.drawImage(background, 128, 64, 2, 2, left, top, 2, 2); + ctx.drawImage(background, 158, 64, 2, 2, left+w-2, top, 2, 2); + ctx.drawImage(background, 128, 94, 2, 2, left, top+h-2, 2, 2); + ctx.drawImage(background, 158, 94, 2, 2, left+w-2, top+h-2, 2, 2); // border - dstImage.drawImage(background, 130, 64, 28, 2, 2, 0,w-4, 2); - dstImage.drawImage(background, 130, 94, 28, 2, 2,h-2,w-4, 2); - dstImage.drawImage(background, 128, 66, 2, 28, 0, 2, 2,h-4); - dstImage.drawImage(background, 158, 66, 2, 28,w-2, 2, 2,h-4); + ctx.drawImage(background, 130, 64, 28, 2, left+2, top, w-4, 2); + ctx.drawImage(background, 130, 94, 28, 2, left+2, top+h-2, w-4, 2); + ctx.drawImage(background, 128, 66, 2, 28, left, top+2, 2,h-4); + ctx.drawImage(background, 158, 66, 2, 28, left+w-2, top+2, 2,h-4); } ////// 绘制 WindowSkin ui.prototype.drawWindowSkin = function(background, ctx, x, y, w, h, direction, px, py) { + background = background || core.status.textAttribute.background; + if (typeof background == 'string') { + background = core.getMappedName(background); + background = core.material.images.images[background]; + } // 仿RM窗口皮肤 ↓ var dstImage = core.getContextByName(ctx); if (!dstImage) return; @@ -504,8 +720,8 @@ ui.prototype.drawWindowSkin = function(background, ctx, x, y, w, h, direction, p ////// 绘制一个背景图,可绘制 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 px = posInfo.px == null || posInfo.noPeak ? null : posInfo.px * 32 - core.bigmap.offsetX; + var py = posInfo.py == null || posInfo.noPeak ? null : posInfo.py * 32 - core.bigmap.offsetY; var xoffset = posInfo.xoffset || 0, yoffset = posInfo.yoffset || 0; var background = core.status.textAttribute.background; @@ -516,6 +732,19 @@ ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) { return false; } +ui.prototype._uievent_drawBackground = function (data) { + this._createUIEvent(); + var background = data.background || core.status.textAttribute.background; + var x = core.calValue(data.x), y = core.calValue(data.y), w = core.calValue(data.width), h = core.calValue(data.height); + if (typeof background == 'string') { + this.drawWindowSkin(background, 'uievent', x, y, w, h); + } + else if (background instanceof Array) { + this.fillRect('uievent', x, y, w, h, core.arrayToRGBA(background)); + this.strokeRect('uievent', x, y, w, h); + } +} + ui.prototype._drawWindowSkin_getOpacity = function () { return core.getFlag("__winskin_opacity__", 0.85); } @@ -603,52 +832,63 @@ ui.prototype._getDrawableIconInfo = function (id) { return [image,icon]; } -ui.prototype._buildFont = function (fontSize, bold) { +ui.prototype._buildFont = function (fontSize, bold, italic) { var textAttribute = core.status.textAttribute || core.initStatus.textAttribute, globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; if (bold == null) bold = textAttribute.bold; - return (bold?"bold ":"") + (fontSize || textAttribute.textfont) + "px " + globalAttribute.font; + return (bold?"bold ":"") + (italic?"italic ":"") + (fontSize || textAttribute.textfont) + "px " + globalAttribute.font; } ////// 绘制一段文字到某个画布上面 // ctx:要绘制到的画布 -// content:要绘制的内容;转义字符目前只允许留 \n, \r[...] 和 \i[...] +// content:要绘制的内容;转义字符目前只允许留 \n, \r[...], \i[...], \c[...], \d, \e // config:绘制配置项,目前暂时包含如下内容(均为可选) // left, top:起始点位置;maxWidth:单行最大宽度;color:默认颜色;align:左中右 // fontSize:字体大小;lineHeight:行高;time:打字机间隔 ui.prototype.drawTextContent = function (ctx, content, config) { ctx = core.getContextByName(ctx); - if (!ctx) return; // 设置默认配置项 var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; config = core.clone(config || {}); config.left = config.left || 0; - config.right = config.left + (config.maxWidth == null ? ctx.canvas.width : config.maxWidth); + config.right = config.left + (config.maxWidth == null ? (ctx != null ? ctx.canvas.width : core.__PIXELS__) : config.maxWidth) config.top = config.top || 0; - config.color = config.color || core.arrayToRGBA(textAttribute.text); + config.color = config.color || textAttribute.text; + if (config.color instanceof Array) config.color = core.arrayToRGBA(config.color); if (config.bold == null) config.bold = textAttribute.bold; + config.italic = false; config.align = config.align || textAttribute.align || "left"; config.fontSize = config.fontSize || textAttribute.textfont; config.lineHeight = config.lineHeight || (config.fontSize * 1.3); config.time = config.time || 0; + config.interval = config.interval == null ? (textAttribute.interval || 0) : config.interval; config.index = 0; config.currcolor = config.color; + config.currfont = config.fontSize; + config.lineMargin = Math.max(0, config.lineHeight - config.fontSize); + config.topMargin = parseInt(config.lineMargin / 2); + config.lineMaxHeight = config.lineMargin + config.fontSize; config.offsetX = 0; config.offsetY = 0; config.line = 0; config.blocks = []; // 创建一个新的临时画布 - var tempCtx = core.bigmap.tempCanvas; - tempCtx.canvas.height = ctx.canvas.height; - tempCtx.canvas.width = ctx.canvas.width; - var _textBaseLine = tempCtx.textBaseline; + var tempCtx = core.createCanvas('__temp__', 0, 0, ctx==null?1:ctx.canvas.width, ctx==null?1:ctx.canvas.height, -1); tempCtx.textBaseline = 'top'; - tempCtx.font = this._buildFont(config.fontSize, config.bold); + tempCtx.font = this._buildFont(config.fontSize, config.bold, config.italic); tempCtx.fillStyle = config.color; - this._drawTextContent_draw(ctx, tempCtx, content, config); - tempCtx.textBaseline = _textBaseLine; + config = this._drawTextContent_draw(ctx, tempCtx, content, config); + core.deleteCanvas('__temp__'); + return config; +} + +ui.prototype._uievent_drawTextContent = function (data) { + this._createUIEvent(); + data.left = core.calValue(data.left); + data.top = core.calValue(data.top); + this.drawTextContent('uievent', core.replaceText(data.text), data); } // 绘制的基本逻辑: @@ -659,13 +899,16 @@ ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) { // Step 1: 绘制到tempCtx上,并记录下图块信息 while (this._drawTextContent_next(tempCtx, content, config)); + if (ctx == null) return 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); + config.left + block.left + block.marginLeft, config.top + block.top + block.marginTop, + block.width, block.height); return true; } if (config.time == 0) { @@ -679,6 +922,8 @@ ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) { } }, config.time); } + + return config; } ui.prototype._drawTextContent_next = function (tempCtx, content, config) { @@ -704,22 +949,36 @@ ui.prototype._drawTextContent_drawChar = function (tempCtx, content, config, ch) 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); + if (ch == '\\') { + var c = content.charAt(config.index); + if (c == 'i') return this._drawTextContent_drawIcon(tempCtx, content, config); + if (c == 'c') return this._drawTextContent_changeFont(tempCtx, content, config); + if (c == 'd' || c == 'e') { + config.index++; + if (c == 'd') config.bold = !config.bold; + if (c == 'e') config.italic = !config.italic; + tempCtx.font = this._buildFont(config.currfont, config.bold, config.italic); + return true; + } + } + // \\e 斜体切换 + if (ch == '\\' && content.charAt(config.index)=='e') { + config.italic = !config.italic; + tempCtx.font = this._buildFont(config.fontSize, config.bold, config.italic); } // 检查是不是自动换行 - var charwidth = core.calWidth(tempCtx, ch); + var charwidth = core.calWidth(tempCtx, ch) + config.interval; 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; + var left = config.offsetX, top = config.offsetY + config.topMargin; 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}); + width: charwidth, height: config.currfont + config.lineMargin, + line: config.line, marginLeft: 0}); config.offsetX += charwidth; return true; } @@ -734,12 +993,17 @@ ui.prototype._drawTextContent_newLine = function (tempCtx, config) { marginLeft = totalWidth - width; config.blocks.forEach(function (b) { - if (b.line == config.line) + if (b.line == config.line) { b.marginLeft = marginLeft; + // b.marginTop = 0; // 上对齐 + b.marginTop = (config.lineMaxHeight - b.height) / 2; // 居中对齐 + // b.marginTop = config.lineMaxHeight - b.height; // 下对齐 + } }); config.offsetX = 0; - config.offsetY += config.lineHeight; + config.offsetY += config.lineMaxHeight; + config.lineMaxHeight = config.currfont + config.lineMargin; config.line++; } @@ -757,6 +1021,22 @@ ui.prototype._drawTextContent_changeColor = function (tempCtx, content, config) return this._drawTextContent_next(tempCtx, content, config); } +ui.prototype._drawTextContent_changeFont = function (tempCtx, content, config) { + config.index++; + // 检查是不是 [] + var index = config.index, index2; + if (content.charAt(index) == '[' && ((index2=content.indexOf(']', index))>=0)) { + var str = content.substring(index+1, index2); + if (!/^\d+$/.test(str)) config.currfont = config.fontSize; + else config.currfont = parseInt(str); + config.index = index2 + 1; + } + else config.currfont = config.fontSize; + config.lineMaxHeight = Math.max(config.lineMaxHeight, config.currfont + config.lineMargin); + tempCtx.font = this._buildFont(config.currfont, config.bold, config.italic); + return this._drawTextContent_next(tempCtx, content, config); +} + ui.prototype._drawTextContent_drawIcon = function (tempCtx, content, config) { // 绘制一个 \i 效果 var index = config.index, index2; @@ -766,17 +1046,18 @@ ui.prototype._drawTextContent_drawIcon = function (tempCtx, content, config) { 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; + var width = config.currfont + 2, left = config.offsetX + 2, top = config.offsetY + config.topMargin - 1; if (config.maxWidth != null && left + width > config.maxWidth) { this._drawTextContent_newLine(tempCtx, config); config.index--; - this._drawTextContent_next(tempCtx, content, config); + return 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}); + width: width, height: width + config.lineMargin, + line: config.line, marginLeft: 0}); config.offsetX += width + 6; config.index = index2 + 1; @@ -785,8 +1066,12 @@ ui.prototype._drawTextContent_drawIcon = function (tempCtx, content, config) { return this._drawTextContent_next(tempCtx, content, config); } +ui.prototype.getTextContentHeight = function (content, config) { + return this.drawTextContent(null, content, config).offsetY; +} + ui.prototype._getRealContent = function (content) { - return content.replace(/(\r|\\r)(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "占1"); + return content.replace(/(\r|\\(r|c|d|e))(\[.*?])?/g, "").replace(/(\\i)(\[.*?])?/g, "占1"); } ////// 绘制一个对话框 ////// @@ -820,16 +1105,27 @@ ui.prototype.drawTextBox = function(content, showAll) { var content_top = this._drawTextBox_drawTitleAndIcon(titleInfo, hPos, vPos, alpha); // Step 5: 绘制正文 - this.drawTextContent('ui', content, { + var config = 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 }); + + // Step 6: 绘制光标 + main.dom.next.style.display = 'block'; + main.dom.next.style.borderRightColor = main.dom.next.style.borderBottomColor = core.arrayToRGB(textAttribute.text); + main.dom.next.style.top = (vPos.bottom - 20) * core.domStyle.scale + "px"; + var left = (hPos.left + hPos.right) / 2; + if (pInfo.position == 'up' && !pInfo.noPeak && pInfo.px != null && Math.abs(pInfo.px * 32 + 16 - left) < 50) + left = hPos.right - 64; + main.dom.next.style.left = left * core.domStyle.scale + "px"; + return config; } 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 && ss.length!=9) return ""; + ss[0] = core.getMappedName(ss[0]); var img = core.material.images.images[ss[0]]; if (!img) return ""; // 绘制 @@ -837,8 +1133,11 @@ ui.prototype._drawTextBox_drawImages = function (content) { core.drawImage('ui', img, parseFloat(ss[1]), parseFloat(ss[2])); 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) + else if (ss.length==9 || ss.length==10) { + if (ss.length==10) core.setAlpha('ui', parseFloat(ss[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])); + core.setAlpha('ui', 1); + } return ""; }); } @@ -866,12 +1165,15 @@ ui.prototype._drawTextBox_getHorizontalPosition = function (content, titleInfo, 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; + var lineHeight = textAttribute.textfont + 6; + var height = 45 + this.getTextContentHeight(content, { + lineHeight: lineHeight, maxWidth: validWidth + }); if (titleInfo.title) height += textAttribute.titlefont + 5; - if (titleInfo.icon != null) - height = Math.max(height, titleInfo.height+50); + if (titleInfo.icon != null) { + if (titleInfo.title) height = Math.max(height, titleInfo.height+50); + else height = Math.max(height, titleInfo.height + 30); + } else if (titleInfo.image) height = Math.max(height, 90); @@ -900,9 +1202,11 @@ ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, al core.setTextAlign('ui', 'left'); var textAttribute = core.status.textAttribute; var content_top = vPos.top + 15; + var image_top = vPos.top + 15; if (titleInfo.title != null) { var titlefont = textAttribute.titlefont; content_top += titlefont + 5; + image_top = vPos.top + 40; core.setFillStyle('ui', core.arrayToRGB(textAttribute.title)); core.setStrokeStyle('ui', core.arrayToRGB(textAttribute.title)); @@ -915,19 +1219,27 @@ ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, al 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 = []; + } + if (titleInfo.icon != null) { + core.setAlpha('ui', alpha); + core.strokeRect('ui', hPos.left + 15 - 1, image_top-1, 34, titleInfo.height + 2, null, 2); + core.setAlpha('ui', 1); + core.status.boxAnimateObjs = []; + // --- 勇士 + if (titleInfo.image == core.material.images.hero) { + core.clearMap('ui', hPos.left + 15, image_top, 32, titleInfo.height); + core.fillRect('ui', hPos.left + 15, image_top, 32, titleInfo.height, core.material.groundPattern); + core.drawImage('ui', titleInfo.image, 0, 0, core.material.icons.hero.width || 32, core.material.icons.hero.height, + hPos.left + 15, image_top, 32, titleInfo.height); + } + else { 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, + 'bgx': hPos.left + 15, 'bgy': image_top, 'bgWidth': 32, 'bgHeight': titleInfo.height, + 'x': hPos.left + 15, 'y': image_top, 'height': titleInfo.height, 'animate': titleInfo.animate, 'image': titleInfo.image, 'pos': titleInfo.icon * titleInfo.height }); - core.drawBoxAnimate(); } + core.drawBoxAnimate(); } if (titleInfo.image != null && titleInfo.icon == null) { // 头像图 core.drawImage('ui', titleInfo.image, 0, 0, titleInfo.image.width, titleInfo.image.height, @@ -937,9 +1249,7 @@ ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, al } 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 width = this.PIXEL, height = 30 + this.getTextContentHeight(content, {lineHeight: lineHeight}); var ctx = document.createElement('canvas').getContext('2d'); ctx.canvas.width = width; ctx.canvas.height = height; @@ -965,6 +1275,7 @@ ui.prototype.drawScrollText = function (content, time, lineHeight, callback) { ui.prototype._drawScrollText_animate = function (ctx, time, callback) { // 开始绘制到UI上 + time /= Math.max(core.status.replay.speed, 1) 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); @@ -974,7 +1285,7 @@ ui.prototype._drawScrollText_animate = function (ctx, time, callback) { if (currH < -height) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); - if (core.isset(callback)) callback(); + if (callback) callback(); return; } core.drawImage('ui', ctx.canvas, 0, currH); @@ -1038,10 +1349,10 @@ ui.prototype._drawChoices_getVerticalPosition = function (titleInfo, choices, hP var choice_top = bottom - height + 56; if (titleInfo.content) { var headHeight = 0; - var realContent = this._getRealContent(titleInfo.content); - var lines = core.splitLines('ui', realContent, hPos.validWidth, this._buildFont(15, true)); if (titleInfo.title) headHeight += 25; - headHeight += lines.length * 20; + headHeight += this.getTextContentHeight(titleInfo.content, { + lineHeight: 20, maxWidth: hPos.validWidth, fontSize: 15, bold: true + }); height += headHeight; if (bottom - height <= 32) { offset = Math.floor(headHeight / 64); @@ -1094,7 +1405,7 @@ ui.prototype._drawChoices_drawChoices = function (choices, isWindowSkin, hPos, v if (color instanceof Array) color = core.arrayToRGBA(color); core.setFillStyle('ui', color); var offset = this.HPIXEL; - if (core.isset(choices[i].icon)) { + if (choices[i].icon) { 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, @@ -1186,12 +1497,15 @@ ui.prototype.drawSwitchs = function() { var choices = [ "背景音乐: "+(core.musicStatus.bgmStatus ? "[ON]" : "[OFF]"), "背景音效: "+(core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), + //显示为 0~10 十挡 + " < 音量:" + Math.round(Math.sqrt(100 * core.musicStatus.userVolume)) + " > ", + //数值越大耗时越长 + " < 步时:" + core.values.moveSpeed + " > ", "怪物显伤: "+(core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"), "临界显伤: "+(core.flags.displayCritical ? "[ON]" : "[OFF]"), "领域显伤: "+(core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), "新版存档: "+(core.platform.useLocalForage ? "[ON]":"[OFF]"), "单击瞬移: "+(!core.hasFlag("__noClickMove__") ? "[ON]":"[OFF]"), - "拓展键盘: "+(core.platform.extendKeyboard ? "[ON]":"[OFF]"), "返回主菜单" ]; this.drawChoices(null, choices); @@ -1222,7 +1536,7 @@ ui.prototype.drawQuickShop = function () { ui.prototype.drawSyncSave = function () { core.status.event.id = 'syncSave'; this.drawChoices(null, [ - "同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "回放当前录像", "下载当前录像", "清空本地存档", "返回主菜单" + "同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "回放和下载录像", "清空本地存档", "返回主菜单" ]); } @@ -1254,7 +1568,7 @@ ui.prototype.drawReplay = function () { core.lockControl(); core.status.event.id = 'replay'; this.drawChoices(null, [ - "从头回放录像", "从存档开始回放", "选择录像文件", "下载当前录像", "返回游戏" + "从头回放录像", "从存档开始回放", "接续播放剩余录像", "选择录像文件", "下载当前录像", "返回游戏" ]); } @@ -1387,13 +1701,13 @@ 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)); + top + 35, '#DDDDDD', this._buildFont(17, true), width); } else { core.fillText('ui', enemy.name, left + width / 2, - top + 28, '#DDDDDD', this._buildFont(17, true)); + top + 28, '#DDDDDD', this._buildFont(17, true), width); core.fillText('ui', enemy.specialText, left + width / 2, - top + 50, '#FF6A6A', this._buildFont(15, true)); + top + 50, '#FF6A6A', this._buildFont(15, true), width); } } @@ -1485,7 +1799,7 @@ ui.prototype.drawBookDetail = function (index) { if (!enemy) return; var content = info[1].join("\n"); core.status.event.id = 'book-detail'; - clearInterval(core.interval.tipAnimate); + core.clearTip(); core.clearMap('data'); var left = 10, width = this.PIXEL - 2 * left, right = left + width; @@ -1600,11 +1914,19 @@ ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) var damageInfo = core.getDamageInfo(enemy, null, null, null, floorId); texts.push("战斗回合数:"+((damageInfo||{}).turn||0)); // 临界表 - var criticals = core.enemys.nextCriticals(enemy, 10, null, null, floorId).map(function (v) { + var criticals = core.enemys.nextCriticals(enemy, 8, null, null, floorId).map(function (v) { return core.formatBigNumber(v[0])+":"+core.formatBigNumber(v[1]); }); while (criticals[0]=='0:0') criticals.shift(); - texts.push("临界表:"+JSON.stringify(criticals)) + texts.push("临界表:"+JSON.stringify(criticals)); + var prevInfo = core.getDamageInfo(enemy, {atk: core.status.hero.atk-1}, null, null, floorId); + if (prevInfo != null && damageInfo != null) { + if (damageInfo.damage != null) damageInfo = damageInfo.damage; + if (prevInfo.damage != null) prevInfo = prevInfo.damage; + if (prevInfo > damageInfo) { + texts.push("(当前攻击力正位于临界点上)") + } + } } ui.prototype._drawBookDetail_drawContent = function (enemy, contents, pos) { @@ -1641,8 +1963,19 @@ ui.prototype.drawFly = function(page) { core.setTextAlign('ui', 'center'); core.fillText('ui', '楼层跳跃', this.HPIXEL, 60, '#FFFFFF', this._buildFont(28, true)); core.fillText('ui', '返回游戏', this.HPIXEL, this.PIXEL - 13, null, this._buildFont(15, true)) - core.fillText('ui', title, this.PIXEL - 60, this.HPIXEL + 39, null, this._buildFont(19, true)); + var middle = this.HPIXEL + 39; + + // 换行 + var lines = core.splitLines('ui', title, 120, this._buildFont(19, true)); + var start_y = middle - (lines.length - 1) * 11; + for (var i in lines) { + core.fillText('ui', lines[i], this.PIXEL - 60, start_y); + start_y += 22; + } + + // core.fillText('ui', title, this.PIXEL - 60, this.HPIXEL + 39, null, this._buildFont(19, true)); + 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); @@ -1694,7 +2027,7 @@ ui.prototype.drawShop = function (shopId) { for (var i=0;i= 14; font_size -= 3) { + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(font_size, false)); + var line_height = parseInt(font_size * 1.4), curr = 37 + line_height; + if (curr + lines.length * line_height < max_height) break; + } core.setFillStyle('ui', '#FFFFFF'); for (var i=0;i= 11; font_size -= 3) { + var lines = core.splitLines('ui', text, this.PIXEL - 15, this._buildFont(font_size, false)); + var line_height = parseInt(font_size * 1.4), curr = 37 + line_height; + if (curr + lines.length * line_height < max_height) break; + } core.setFillStyle('ui', '#FFFFFF'); for (var i = 0; i < lines.length; ++i) { core.fillText('ui', lines[i], 10, curr); @@ -2009,7 +2349,7 @@ ui.prototype._drawEquipbox_description = function (info, max_height) { this._drawEquipbox_drawStatusChanged(info, curr, equip, equipType); } -ui.prototype._drawEquipbox_getStatusChanged = function (info, equip, equipType) { +ui.prototype._drawEquipbox_getStatusChanged = function (info, equip, equipType, y) { var compare, differentMode = null; if (info.index < this.LAST) compare = core.compareEquipment(null, info.selectId); else { @@ -2030,23 +2370,23 @@ ui.prototype._drawEquipbox_getStatusChanged = function (info, equip, equipType) } ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipType) { - var compare = this._drawEquipbox_getStatusChanged(info, equip, equipType); + var compare = this._drawEquipbox_getStatusChanged(info, equip, equipType, y); if (compare == null) return; - var drawOffset = 10; + var obj = { drawOffset: 10, y: y }; + // --- 变化值... core.setFont('ui', this._buildFont(14, true)); for (var name in compare) { var img = core.statusBar.icons[name]; var text = core.getStatusName(name); if (img && core.flags.iconInEquipbox) { // 绘制图标 - core.drawImage('ui', img, 0, 0, 32, 32, drawOffset, y - 13, 16, 16); - drawOffset += 20; + core.drawImage('ui', img, 0, 0, 32, 32, obj.drawOffset, obj.y - 13, 16, 16); + obj.drawOffset += 20; } else { // 绘制文字 - core.fillText('ui', text + " ", drawOffset, y, '#CCCCCC'); - drawOffset += core.calWidth('ui', text + " "); + this._drawEquipbox_drawStatusChanged_draw(text + " ", '#CCCCCC', obj); } - var nowValue = core.getStatus(name) * core.getBuff(name), newValue = (nowValue + compare[name]) * core.getBuff(name); + var nowValue = core.getStatus(name) * core.getBuff(name), newValue = (core.getStatus(name) + 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)); @@ -2054,13 +2394,22 @@ ui.prototype._drawEquipbox_drawStatusChanged = function (info, y, equip, equipTy } 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; + this._drawEquipbox_drawStatusChanged_draw(nowValue+"->", '#CCCCCC', obj); + this._drawEquipbox_drawStatusChanged_draw(newValue, compare[name]>0?'#00FF00':'#FF0000', obj); + obj.drawOffset += 8; } } +ui.prototype._drawEquipbox_drawStatusChanged_draw = function (text, color, obj) { + var len = core.calWidth('ui', text); + if (obj.drawOffset + len >= core.__PIXELS__) { // 换行 + obj.y += 19; + obj.drawOffset = 10; + } + core.fillText('ui', text, obj.drawOffset, obj.y, color); + obj.drawOffset += len; +} + ui.prototype._drawEquipbox_drawEquiped = function (info, line) { core.setTextAlign('ui', 'center'); var per_line = this.HSIZE - 3, width = Math.floor(this.PIXEL / (per_line + 0.25)); @@ -2153,9 +2502,9 @@ ui.prototype._drawSLPanel_loadSave = function(page, callback) { ids.push(id); } core.getSaves(ids, function (data) { - for (var i = 0; i < ids.length; ++i) + for (var i = 1; i < ids.length; ++i) core.status.event.ui[i] = data[i]; - core.saves.autosave.data = data[0]; + core.status.event.ui[0] = data[0] == null ? null : data[0][data[0].length-1]; callback(); }); } @@ -2174,11 +2523,15 @@ ui.prototype._drawSLPanel_drawRecord = function(title, data, x, y, size, cho, hi }, { ctx: 'ui', x: x-size/2, y: y+15, size: size, centerX: data.hero.loc.x, centerY: data.hero.loc.y }); + if (core.isPlaying() && core.getFlag("hard") != data.hero.flags.hard) { + core.fillRect('ui', x-size/2, y+15, size, size, [0, 0, 0, 0.4], 2); + core.fillText('ui', data.hard, x, parseInt(y+22+size/2), core.dom.hard.style.color, this._buildFont(30,true)); + } 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'); + core.fillText('ui', core.formatDate(new Date(data.time)), x, y+43+size, data.hero.flags.__consoleOpened__||data.hero.flags.debug?'#FF6A6A':'#FFFFFF'); } else { core.fillRect('ui', x-size/2, y+15, size, size, '#333333', 2); @@ -2190,7 +2543,7 @@ 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'?"回放":""; + var name=core.status.event.id=='save'?"存档":core.status.event.id=='load'?"读档":"回放"; for (var i = 0; i < (n||6); i++){ var data = core.status.event.ui[i]; @@ -2271,6 +2624,7 @@ ui.prototype.drawStatistics = function (floorIds) { core.ui._drawStatistics_floorId(floorId, obj); }); var statistics = core.status.hero.statistics; + core.setFlag("__replayText__", true); core.drawText([ this._drawStatistics_generateText(obj, "全塔", obj.total), this._drawStatistics_generateText(obj, "当前", obj.current), @@ -2288,6 +2642,7 @@ ui.prototype.drawStatistics = function (floorIds) { "4. 在自定义道具(例如其他宝石)后,需在脚本编辑的drawStatistics中注册,不然不会进行统计。\n"+ "5. 所有统计信息仅供参考,如有错误,概不负责。" ]) + core.removeFlag("__replayText__"); } ui.prototype._drawStatistics_buildObj = function () { @@ -2457,7 +2812,7 @@ ui.prototype._drawPaint_draw = function () { // 将已有的内容绘制到route上 var value = core.paint[core.status.floorId]; - if (core.isset(value)) value = lzw_decode(value).split(","); + 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); @@ -2518,7 +2873,7 @@ ui.prototype.drawHelp = function () { ////// canvas创建 ////// ui.prototype.createCanvas = function (name, x, y, width, height, z) { // 如果画布已存在则直接调用 - if (core.isset(core.dymCanvas[name])) { + if (core.dymCanvas[name]) { this.relocateCanvas(name, x, y); this.resizeCanvas(name, width, height); core.dymCanvas[name].canvas.style.zIndex = z; @@ -2544,13 +2899,13 @@ ui.prototype.createCanvas = function (name, x, y, width, height, z) { ////// canvas重定位 ////// ui.prototype.relocateCanvas = function (name, x, y) { - var ctx = core.dymCanvas[name]; - if (!core.isset(ctx)) return null; - if (core.isset(x)) { + var ctx = core.getContextByName(name); + if (!ctx) return null; + if (x != null) { ctx.canvas.style.left = x * core.domStyle.scale + 'px'; ctx.canvas.setAttribute("_left", x); } - if (core.isset(y)) { + if (y != null) { ctx.canvas.style.top = y * core.domStyle.scale + 'px'; ctx.canvas.setAttribute("_top", y); } @@ -2558,22 +2913,22 @@ ui.prototype.relocateCanvas = function (name, x, y) { } ////// canvas重置 ////// -ui.prototype.resizeCanvas = function (name, width, height) { - var ctx = core.dymCanvas[name]; - if (!core.isset(ctx)) return null; - if (core.isset(width)) { - ctx.canvas.width = width; +ui.prototype.resizeCanvas = function (name, width, height, styleOnly) { + var ctx = core.getContextByName(name); + if (!ctx) return null; + if (width != null) { + if (!styleOnly) ctx.canvas.width = width; ctx.canvas.style.width = width * core.domStyle.scale + 'px'; } - if (core.isset(height)) { - ctx.canvas.height = height; + if (height != null) { + if (!styleOnly) ctx.canvas.height = height; ctx.canvas.style.height = height * core.domStyle.scale + 'px'; } return ctx; } ////// canvas删除 ////// ui.prototype.deleteCanvas = function (name) { - if (!core.isset(core.dymCanvas[name])) return null; + if (!core.dymCanvas[name]) return null; core.dom.gameDraw.removeChild(core.dymCanvas[name].canvas); delete core.dymCanvas[name]; } diff --git a/libs/utils.js b/libs/utils.js index 9260a9c7..009625ff 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -48,7 +48,14 @@ utils.prototype._init = function () { return this.substring(this_len - search.length, this_len) === search; }; } - + if (typeof String.prototype.startsWith != "function") { + String.prototype.startsWith = function (search, this_len) { + if (this_len === undefined || this_len > this.length) { + this_len = this.length; + } + return this.substring(0, search.length) === search; + } + } } @@ -59,14 +66,39 @@ utils.prototype.replaceText = function (text, need, times) { }); } +utils.prototype.replaceValue = function (value) { + if (typeof value == "string" && value.indexOf(":") >= 0) { + if (value.indexOf('status:') >= 0) + value = value.replace(/status:([a-zA-Z0-9_]+)/g, "core.getStatus('$1')"); + if (value.indexOf('item:') >= 0) + value = value.replace(/item:([a-zA-Z0-9_]+)/g, "core.itemCount('$1')"); + if (value.indexOf('flag:') >= 0) + value = value.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC]+)/g, "core.getFlag('$1', 0)"); + //if (value.indexOf('switch:' >= 0)) + // value = value.replace(/switch:([a-zA-Z0-9_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)"); + if (value.indexOf('global:') >= 0) + value = value.replace(/global:([a-zA-Z0-9_\u4E00-\u9FCC]+)/g, "core.getGlobal('$1', 0)"); + if (value.indexOf('enemy:')>=0) + value = value.replace(/enemy:([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)/g, "core.material.enemys['$1'].$2"); + if (value.indexOf('blockId:')>=0) + value = value.replace(/blockId:(\d+),(\d+)/g, "core.getBlockId($1, $2)"); + if (value.indexOf('blockCls:')>=0) + value = value.replace(/blockCls:(\d+),(\d+)/g, "core.getBlockCls($1, $2)"); + if (value.indexOf('equip:')>=0) + value = value.replace(/equip:(\d)/g, "core.getEquip($1)"); + } + return value; +} + ////// 计算表达式的值 ////// utils.prototype.calValue = function (value, prefix, need, times) { 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 || ":f@x@y") + "@$1', 0)"); + if (value.indexOf(':') >= 0) { + if (value.indexOf('switch:' >= 0)) + value = value.replace(/switch:([a-zA-Z0-9_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)"); + value = this.replaceValue(value); + } return eval(value); } if (value instanceof Function) { @@ -246,6 +278,31 @@ utils.prototype.removeLocalForage = function (key, successCallback, errorCallbac }) } +utils.prototype.setGlobal = function (key, value) { + if (core.isReplaying()) return; + core.setLocalStorage(key, value); +} + +utils.prototype.getGlobal = function (key, defaultValue) { + var value; + if (core.isReplaying()) { + // 不考虑key不一致的情况 + var action = core.status.replay.toReplay.shift(); + if (action.indexOf("input2:") == 0) { + value = JSON.parse(core.decodeBase64(action.substring(7))); + } + else { + core.control._replay_error(action); + return core.getLocalStorage(key, defaultValue); + } + } + else { + value = core.getLocalStorage(key, defaultValue); + } + core.status.route.push("input2:" + core.encodeBase64(JSON.stringify(value))); + return value; +} + ////// 深拷贝一个对象 ////// utils.prototype.clone = function (data, filter, recursion) { if (!core.isset(data)) return null; @@ -282,23 +339,24 @@ utils.prototype.clone = function (data, filter, recursion) { ////// 裁剪图片 ////// utils.prototype.splitImage = function (image, width, height) { - if (typeof image == "string") + if (typeof image == "string") { + image = core.getMappedName(image); image = core.material.images.images[image]; + } if (!image) return []; width = width || 32; height = height || width; var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); - canvas.width = width; - canvas.height = height; var ans = []; for (var j = 0; j < image.height; j += height) { for (var i = 0; i < image.width; i += width) { - context.drawImage(image, i, j, width, height, 0, 0, width, height); + var w = Math.min(width, image.width - i), h = Math.min(height, image.height - j); + canvas.width = w; canvas.height = h; + context.drawImage(image, i, j, w, h, 0, 0, w, h); var img = new Image(); img.src = canvas.toDataURL("image/png"); ans.push(img); - context.clearRect(0, 0, width, height); } } return ans; @@ -599,15 +657,25 @@ utils.prototype.getCookie = function (name) { ////// 设置statusBar的innerHTML,会自动斜体和放缩,也可以增加自定义css ////// utils.prototype.setStatusBarInnerHTML = function (name, value, css) { if (!core.statusBar[name]) return; - if (typeof value == 'number') value = this.formatBigNumber(value); + var isNumber = false; + if (typeof value == 'number') { + value = this.formatBigNumber(value); + isNumber = true; + } // 判定是否斜体 var italic = /^[-a-zA-Z0-9`~!@#$%^&*()_=+\[{\]}\\|;:'",<.>\/?]*$/.test(value); var style = 'font-style: ' + (italic ? 'italic' : 'normal') + '; '; + style += 'text-shadow: #000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0; '; // 判定是否需要缩放 var length = this.strlen(value) || 1; style += 'font-size: ' + Math.min(1, 7 / length) + 'em; '; if (css) style += css; - core.statusBar[name].innerHTML = "" + value + ""; + if (isNumber) { + core.statusBar[name].innerHTML = "" + value + ""; + } else { + core.statusBar[name].innerHTML = ""; + core.statusBar[name].children[0].innerText = value; + } } utils.prototype.strlen = function (str) { @@ -684,9 +752,8 @@ utils.prototype.rand2 = function (num) { value = parseInt(action.substring(7)); } else { - core.stopReplay(); - core.drawTip("录像文件出错"); - return; + core.control._replay_error(action); + return 0; } } else { @@ -712,7 +779,7 @@ utils.prototype.__next_rand = function (_rand) { } ////// 读取一个本地文件内容 ////// -utils.prototype.readFile = function (success, error, readType) { +utils.prototype.readFile = function (success, error, accept, readType) { core.platform.successCallback = success; core.platform.errorCallback = error; @@ -751,6 +818,7 @@ utils.prototype.readFile = function (success, error, readType) { else core.platform.fileReader.readAsDataURL(core.platform.fileInput.files[0]); core.platform.fileInput.value = ''; } + if (accept) core.platform.fileInput.accept = accept; } core.platform.fileInput.click(); @@ -888,7 +956,9 @@ 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(); + main.dom.inputYes.blur(); + main.dom.inputNo.blur(); + core.status.holdingKeys = []; core.platform.successCallback = yesCallback; core.platform.errorCallback = noCallback; @@ -900,9 +970,12 @@ utils.prototype.myprompt = function (hint, value, callback) { main.dom.inputMessage.innerHTML = hint.replace(/\n/g, '
'); main.dom.inputBox.style.display = 'block'; main.dom.inputBox.value = value==null?"":value; + main.dom.inputYes.blur(); + main.dom.inputNo.blur(); setTimeout(function () { main.dom.inputBox.focus(); }); + core.status.holdingKeys = []; core.platform.successCallback = core.platform.errorCallback = callback; } @@ -910,7 +983,7 @@ utils.prototype.myprompt = function (hint, value, callback) { ////// 动画显示某对象 ////// utils.prototype.showWithAnimate = function (obj, speed, callback) { obj.style.display = 'block'; - if (!speed && main.mode != 'play') { + if (!speed || main.mode != 'play') { obj.style.opacity = 1; if (callback) callback(); return; @@ -1058,11 +1131,11 @@ utils.prototype._export = function (floorIds) { // map var content = floorIds.length + "\n" + core.__SIZE__ + " " + core.__SIZE__ + "\n\n"; floorIds.forEach(function (floorId) { - var arr = core.maps._getMapArrayFromBlocks(core.status.maps[floorId].blocks); + var arr = core.maps._getMapArrayFromBlocks(core.status.maps[floorId].blocks, core.__SIZE__, core.__SIZE__); content += arr.map(function (x) { // check monster x.forEach(function (t) { - var block = core.maps.initBlock(null, null, t); + var block = core.maps.getBlockByNumber(t); if (block.event.cls.indexOf("enemy") == 0) { monsterMap[t] = block.event.id; } @@ -1074,7 +1147,7 @@ utils.prototype._export = function (floorIds) { // values content += ["redJewel", "blueJewel", "greenJewel", "redPotion", "bluePotion", "yellowPotion", "greenPotion", "sword1", "shield1"].map(function (x) { - return core.values[x] + return core.values[x] || 0; }).join(" ") + "\n\n"; // monster @@ -1093,7 +1166,55 @@ utils.prototype._export = function (floorIds) { console.log(content); } -utils.prototype.http = function (type, url, formData, success, error, mimeType, responseType) { +utils.prototype.unzip = function (blobOrUrl, success, error, convertToText, onprogress) { + var _error = function (msg) { + main.log(msg); + if (error) error(msg); + } + + if (!window.zip) { + return _error("zip.js not exists!"); + } + + if (typeof blobOrUrl == 'string') { + return core.http('GET', blobOrUrl, null, function (data) { + core.unzip(data, success, error, convertToText); + }, _error, null, 'blob', onprogress); + } + + if (!(blobOrUrl instanceof Blob)) { + return _error("Should use Blob or URL as input"); + } + + zip.createReader(new zip.BlobReader(blobOrUrl), function (reader) { + reader.getEntries(function (entries) { + core.utils._unzip_readEntries(entries, function (data) { + reader.close(function () { + if (success) success(data); + }); + }, convertToText); + }); + }, _error); +} + +utils.prototype._unzip_readEntries = function (entries, success, convertToText) { + var results = {}; + if (entries == null) { + return success(results); + } + var length = entries.length; + entries.forEach(function (entry) { + entry.getData(convertToText ? new zip.TextWriter('utf8') : new zip.BlobWriter(), function (data) { + results[entry.filename] = data; + length--; + if (length == 0) { + success(results); + } + }); + }); +} + +utils.prototype.http = function (type, url, formData, success, error, mimeType, responseType, onprogress) { var xhr = new XMLHttpRequest(); xhr.open(type, url, true); if (mimeType) xhr.overrideMimeType(mimeType); @@ -1106,6 +1227,11 @@ utils.prototype.http = function (type, url, formData, success, error, mimeType, if (error) error("HTTP " + xhr.status); } }; + xhr.onprogress = function (e) { + if (e.lengthComputable) { + if (onprogress) onprogress(e.loaded / e.total); + } + } xhr.onabort = function () { if (error) error("Abort"); } diff --git a/logo.png b/logo.png new file mode 100644 index 00000000..96da1025 Binary files /dev/null and b/logo.png differ diff --git a/main.js b/main.js index 4743655b..d9e3534a 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,7 @@ function main() { //------------------------ 用户修改内容 ------------------------// - this.version = "2.6"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 + this.version = "2.6.6"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 this.useCompress = false; // 是否使用压缩文件 // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。 @@ -74,11 +74,12 @@ function main() { 'inputMessage': document.getElementById('inputMessage'), 'inputBox': document.getElementById('inputBox'), 'inputYes': document.getElementById('inputYes'), - 'inputNo': document.getElementById('inputNo') + 'inputNo': document.getElementById('inputNo'), + 'next': document.getElementById('next') }; this.mode = 'play'; this.loadList = [ - 'loader', 'control', 'utils', 'items', 'icons', 'maps', 'enemys', 'events', 'actions', 'data', 'ui', 'core' + 'loader', 'control', 'utils', 'items', 'icons', 'maps', 'enemys', 'events', 'actions', 'data', 'ui', 'extensions', 'core' ]; this.pureData = [ 'data', 'enemys', 'icons', 'maps', 'items', 'functions', 'events', 'plugins' @@ -188,8 +189,8 @@ function main() { this.floors = {} this.canvas = {}; - this.__VERSION__ = "2.6"; - this.__VERSION_CODE__ = 30; + this.__VERSION__ = "2.6.6"; + this.__VERSION_CODE__ = 99; } main.prototype.init = function (mode, callback) { @@ -204,6 +205,7 @@ main.prototype.init = function (mode, callback) { main.dom.startBackground.src="project/images/"+main.startBackground; main.dom.startLogo.style=main.startLogoStyle; + main.dom.startButtonGroup.style = main.startButtonsStyle; main.levelChoose.forEach(function(value){ var span = document.createElement('span'); span.setAttribute('class','startButton'); @@ -215,6 +217,7 @@ main.prototype.init = function (mode, callback) { })(span,value[1]); main.dom.levelChooseButtons.appendChild(span); }); + main.createOnChoiceAnimation(); main.loadJs('libs', main.loadList, function () { main.core = core; @@ -228,7 +231,7 @@ main.prototype.init = function (mode, callback) { main.loadFloors(function() { var coreData = {}; ["dom", "statusBar", "canvas", "images", "tilesets", "materials", - "animates", "bgms", "sounds", "floorIds", "floors"].forEach(function (t) { + "animates", "bgms", "sounds", "floorIds", "floors"].forEach(function (t) { coreData[t] = main[t]; }) main.core.init(coreData, callback); @@ -264,14 +267,14 @@ main.prototype.loadJs = function (dir, loadList, callback) { } ////// 加载某一个JS文件 ////// -main.prototype.loadMod = function (dir, modName, callback) { +main.prototype.loadMod = function (dir, modName, callback, onerror) { var script = document.createElement('script'); var name = modName; script.src = dir + '/' + modName + (this.useCompress?".min":"") + '.js?v=' + this.version; - main.dom.body.appendChild(script); script.onload = function () { callback(name); } + main.dom.body.appendChild(script); } ////// 动态加载所有楼层(剧本) ////// @@ -327,6 +330,49 @@ main.prototype.log = function (e) { } } +main.prototype.createOnChoiceAnimation = function () { + var borderColor = main.dom.startButtonGroup.style.caretColor || "rgb(255, 215, 0)"; + // get rgb value + var rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*\d+\s*)?\)$/.exec(borderColor); + if (rgb != null) { + var value = rgb[1] + ", " + rgb[2] + ", " + rgb[3]; + var style = document.createElement("style"); + style.type = 'text/css'; + var keyFrames = "onChoice { " + + "0% { border-color: rgba("+value+", 0.9); } " + + "50% { border-color: rgba("+value+", 0.3); } " + + "100% { border-color: rgba("+value+", 0.9); } " + + "}"; + style.innerHTML = "@-webkit-keyframes " + keyFrames + " @keyframes " + keyFrames; + document.body.appendChild(style); + } +} + +////// 选项 ////// +main.prototype.selectButton = function (index) { + var select = function (children) { + index = (index + children.length) % children.length; + for (var i = 0;i < children.length; ++i) { + children[i].classList.remove("onChoiceAnimate"); + } + children[index].classList.add("onChoiceAnimate"); + if (main.selectedButton == index) { + children[index].click(); + } + else { + main.selectedButton = index; + } + } + + if (core.dom.startPanel.style.display != 'block') return; + + if (main.dom.startButtons.style.display == 'block') { + select(main.dom.startButtons.children); + } + else if (main.dom.levelChooseButtons.style.display == 'block') { + select(main.dom.levelChooseButtons.children); + } +} main.prototype.listen = function () { @@ -349,11 +395,48 @@ main.dom.body.onkeydown = function(e) { ////// 在界面上放开某按键时 ////// main.dom.body.onkeyup = function(e) { try { - if (main.dom.inputDiv.style.display == 'block') return; - if (main.core && (main.core.isPlaying() || main.core.status.lockControl)) + if (main.dom.startPanel.style.display == 'block' && + (main.dom.startButtons.style.display == 'block' || main.dom.levelChooseButtons.style.display == 'block')) { + if (e.keyCode == 38 || e.keyCode == 33) // up/pgup + main.selectButton((main.selectedButton||0) - 1); + else if (e.keyCode == 40 || e.keyCode == 34) // down/pgdn + main.selectButton((main.selectedButton||0) + 1); + else if (e.keyCode == 67 || e.keyCode == 13 || e.keyCode == 32) // C/Enter/Space + main.selectButton(main.selectedButton); + else if (e.keyCode == 27 && main.dom.levelChooseButtons.style.display == 'block') { // ESC + main.core.showStartAnimate(true); + } + e.stopPropagation(); + return; + } + if (main.dom.inputDiv.style.display == 'block') { + if (e.keyCode == 13) { + setTimeout(function () { + main.dom.inputYes.click(); + }, 50); + } + else if (e.keyCode == 27) { + setTimeout(function () { + main.dom.inputNo.click(); + }, 50); + } + return; + } + if (main.core && main.core.isPlaying && main.core.status && + (main.core.isPlaying() || main.core.status.lockControl)) main.core.onkeyUp(e); } catch (ee) { main.log(ee); } -} +}; + +[main.dom.startButtons, main.dom.levelChooseButtons].forEach(function (dom) { + dom.onmousemove = function (e) { + for (var i = 0; i < dom.children.length; ++i) { + if (dom.children[i] == e.target && i != (main.selectedButton || 0)) { + main.selectButton(i); + } + } + } +}); ////// 开始选择时 ////// main.dom.body.onselectstart = function () { @@ -381,9 +464,12 @@ main.dom.data.onmousemove = function (e) { } ////// 鼠标放开时 ////// -main.dom.data.onmouseup = function () { +main.dom.data.onmouseup = function (e) { try { - main.core.onup(); + e.stopPropagation(); + var loc = main.core.actions._getClickLoc(e.clientX, e.clientY); + if (loc == null) return; + main.core.onup(loc); }catch (e) { main.log(e); } } @@ -403,6 +489,7 @@ main.dom.data.ontouchstart = function (e) { e.preventDefault(); var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; + main.lastTouchLoc = loc; main.core.ondown(loc); }catch (ee) { main.log(ee); } } @@ -413,6 +500,7 @@ main.dom.data.ontouchmove = function (e) { e.preventDefault(); var loc = main.core.actions._getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; + main.lastTouchLoc = loc; main.core.onmove(loc); }catch (ee) { main.log(ee); } } @@ -421,7 +509,19 @@ main.dom.data.ontouchmove = function (e) { main.dom.data.ontouchend = function (e) { try { e.preventDefault(); - main.core.onup(); + if (main.lastTouchLoc == null) return; + var loc = main.lastTouchLoc; + delete main.lastTouchLoc; + main.core.onup(loc); + } catch (e) { + main.log(e); + } +} + +main.dom.statusCanvas.onclick = function (e) { + try { + e.preventDefault(); + main.core.onStatusBarClick(e); } catch (e) { main.log(e); } @@ -592,7 +692,7 @@ main.statusBar.image.settings.onclick = function (e) { } ////// 点击工具栏时 ////// -main.dom.toolBar.onclick = function () { +main.dom.hard.onclick = function () { if (core.isReplaying()) return; main.core.control.setToolbarButton(!core.domStyle.toolbarBtn); @@ -649,6 +749,8 @@ main.dom.playGame.onclick = function () { } else { main.dom.levelChooseButtons.style.display='block'; + main.selectedButton = null; + main.selectButton(0); } } @@ -693,19 +795,6 @@ main.dom.inputNo.onclick = function () { if (func) func(null); } -main.dom.inputDiv.onkeyup = function (e) { - if (e.keyCode == 13) { - setTimeout(function () { - main.dom.inputYes.click(); - }, 50); - } - else if (e.keyCode == 27) { - setTimeout(function () { - main.dom.inputNo.click(); - }, 50); - } -} - }//listen end var main = new main(); \ No newline at end of file diff --git a/project/animates/hand.animate b/project/animates/hand.animate index bffc26d1..09c95147 100644 --- a/project/animates/hand.animate +++ b/project/animates/hand.animate @@ -1 +1 @@ -{"ratio":2,"bitmaps":["data:image/png;base64,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","data:image/png;base64,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","data:image/png;base64,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","","","","","","",""],"frame_max":8,"frames":[[[0,0,0,30,120]],[[0,0,0,50,255]],[[0,0,0,70,255]],[[1,0,0,80,255]],[[1,0,0,90,255]],[[2,0,0,90,120]],[],[]]} \ No newline at end of file +{"ratio":2,"se":"attack.mp3","bitmaps":["data:image/png;base64,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","data:image/png;base64,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","data:image/png;base64,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","","","","","","",""],"frame_max":8,"frames":[[[0,0,0,30,120]],[[0,0,0,50,255]],[[0,0,0,70,255]],[[1,0,0,80,255]],[[1,0,0,90,255]],[[2,0,0,90,120]],[],[]]} \ No newline at end of file diff --git a/project/animates/zone.animate b/project/animates/zone.animate index f13a6b20..8c2b604c 100644 --- a/project/animates/zone.animate +++ b/project/animates/zone.animate @@ -1 +1 @@ -{"ratio":2,"bitmaps":["data:image/png;base64,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","data:image/png;base64,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","","","","","","","",""],"frame_max":6,"frames":[[[0,0,32,30,100]],[[0,0,16,50,130]],[[0,0,0,80,150]],[[0,0,-8,90,180],[1,24,24,100,255]],[[0,0,-8,100,200],[1,24,8,100,255]],[[0,0,-8,100,255]]]} \ No newline at end of file +{"ratio":2,"se":"zone.mp3","bitmaps":["data:image/png;base64,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","data:image/png;base64,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","","","","","","","",""],"frame_max":6,"frames":[[[0,0,32,30,100]],[[0,0,16,50,130]],[[0,0,0,80,150]],[[0,0,-8,90,180],[1,24,24,100,255]],[[0,0,-8,100,200],[1,24,8,100,255]],[[0,0,-8,100,255]]]} \ No newline at end of file diff --git a/project/data.js b/project/data.js index ff821cd8..f5c220f7 100644 --- a/project/data.js +++ b/project/data.js @@ -35,6 +35,10 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "bomb.mp3", "centerFly.mp3" ], + "nameMap": { + "背景图.jpg": "bg.jpg", + "背景音乐.mp3": "bgm.mp3" + }, "startBackground": "bg.jpg", "startLogoStyle": "color: black", "levelChoose": [ @@ -59,8 +63,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "武器", "盾牌" ], - "startBgm": "bgm.mp3", - "statusLeftBackground": "url(project/images/ground.png) repeat", + "startBgm": null, + "statusLeftBackground": null, "statusTopBackground": "url(project/images/ground.png) repeat", "toolsBackground": "url(project/images/ground.png) repeat", "borderColor": "#CCCCCC", @@ -68,12 +72,13 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "hardLabelColor": "red", "floorChangingBackground": "black", "floorChangingTextColor": "white", - "font": "Verdana" + "font": "Verdana", + "startButtonsStyle": "background-color: #32369F; opacity: 0.85; color: #FFFFFF; border: #FFFFFF 2px solid; caret-color: #FFD700;" }, "firstData": { "title": "魔塔样板", "name": "template", - "version": "Ver 2.6", + "version": "Ver 2.6.6", "floorId": "sample0", "hero": { "name": "阳光", @@ -261,18 +266,6 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = { "text": "生命+800", "effect": "status:hp+=800" - }, - { - "text": "攻击+4", - "effect": "status:atk+=4" - }, - { - "text": "防御+4", - "effect": "status:def+=4" - }, - { - "text": "魔防+10", - "effect": "status:mdef+=10" } ] }, @@ -290,17 +283,20 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = { "text": "等级+1", "need": "100", - "effect": "status:lv+=1;status:hp+=1000;status:atk+=7;status:def+=7" - }, + "effect": "status:hp+=1000" + } + ] + }, + { + "id": "itemShop", + "item": true, + "textInList": "道具商店", + "mustEnable": false, + "choices": [ { - "text": "攻击+5", - "need": "30", - "effect": "status:atk+=5" - }, - { - "text": "防御+5", - "need": "30", - "effect": "status:def+=5" + "id": "yellowKey", + "number": 10, + "money": 10 } ] }, @@ -385,11 +381,13 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableDebuff": false, "enableSkill": false, "flyNearStair": true, + "flyRecordPosition": false, "pickaxeFourDirections": false, "bombFourDirections": false, "snowFourDirections": false, "bigKeyIsBox": false, "steelDoorWithoutKey": false, + "itemFirstText": false, "equipment": false, "equipboxButton": false, "iconInEquipbox": false, @@ -413,6 +411,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableMoveDirectly": true, "enableDisabledShop": true, "disableShopOnDamage": false, + "blurFg": false, "checkConsole": false } } \ No newline at end of file diff --git a/project/events.js b/project/events.js index d8559846..57aec84c 100644 --- a/project/events.js +++ b/project/events.js @@ -4,7 +4,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "加点事件": [ { "type": "comment", - "text": "通过传参,flag:arg1表示当前应该的加点数值" + "text": "通过传参,flag:arg1 表示当前应该的加点数值" }, { "type": "choices", @@ -45,7 +45,7 @@ var events_c12a15a8_c380_4b28_8144_256cba95f760 = "毒衰咒处理": [ { "type": "comment", - "text": "获得毒衰咒效果,flag:arg1为要获得的类型" + "text": "获得毒衰咒效果,flag:arg1 为要获得的类型" }, { "type": "switch", diff --git a/project/floors/sample1.js b/project/floors/sample1.js index 92b3a0e8..8b5c8646 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -728,20 +728,6 @@ main.floors.sample1= "type": "setValue", "name": "flag:door", "value": "flag:door+1" - }, - { - "type": "if", - "condition": "flag:door==2", - "true": [ - { - "type": "openDoor", - "loc": [ - 10, - 5 - ] - } - ], - "false": [] } ], "11,6": [ @@ -749,20 +735,6 @@ main.floors.sample1= "type": "setValue", "name": "flag:door", "value": "flag:door+1" - }, - { - "type": "if", - "condition": "flag:door==2", - "true": [ - { - "type": "openDoor", - "loc": [ - 10, - 5 - ] - } - ], - "false": [] } ] }, @@ -775,4 +747,21 @@ main.floors.sample1= "fgmap": [ ], +"autoEvent": { + "10,5": { + "0": { + "condition": "flag:door==2", + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + { + "type": "openDoor" + } + + ] + } + } +} } \ No newline at end of file diff --git a/project/functions.js b/project/functions.js index 1cee5278..c93f4dd3 100644 --- a/project/functions.js +++ b/project/functions.js @@ -9,10 +9,13 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 这一步会清空状态栏和全部画布内容,并删除所有动态创建的画布 core.clearStatus(); // 初始化status - core.status = core.clone(core.initStatus); + core.status = core.clone(core.initStatus, function (name) { + return name != 'hero' && name != 'maps'; + }); core.status.played = true; // 初始化人物,图标,统计信息 core.status.hero = core.clone(hero); + window.flags = core.status.hero.flags; core.events.setHeroIcon(core.getFlag('heroIcon', 'hero.png'), true); core.control._initStatistics(core.animateFrame.totalTime); core.status.hero.statistics.totalTime = core.animateFrame.totalTime = @@ -22,7 +25,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.status.hard = hard || ""; // 初始化地图 core.status.floorId = floorId; - core.status.maps = core.clone(maps); + core.status.maps = maps; // 初始化怪物和道具 core.material.enemys = core.enemys.getEnemys(); core.material.items = core.items.getItems(); @@ -68,26 +71,33 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 设置已经到过的楼层 core.setFlag("__visited__", {}); - - core.updateEnemys(); }, - "win": function(reason, norank) { + "win": function (reason, norank, noexit) { + // 游戏获胜事件 + // 请注意,成绩统计时是按照hp进行上传并排名 + // 可以先在这里对最终分数进行计算,比如将2倍攻击和5倍黄钥匙数量加到分数上 + // core.status.hero.hp += 2 * core.getRealStatus('atk') + 5 * core.itemCount('yellowKey'); + + // 如果不退出,则临时存储数据 + if (noexit) { + core.status.extraEvent = core.clone(core.status.event); + } + // 游戏获胜事件 core.ui.closePanel(); var replaying = core.isReplaying(); if (replaying) core.stopReplay(); - core.waitHeroToStop(function() { - core.clearMap('all'); // 清空全地图 - core.deleteAllCanvas(); // 删除所有创建的画布 - core.dom.gif2.innerHTML = ""; - // 请注意: - // 成绩统计时是按照hp进行上传并排名,因此光在这里改${status:hp}是无效的 - // 如需按照其他的的分数统计方式,请先将hp设置为你的得分 - // core.setStatus('hp', ...); + core.waitHeroToStop(function () { + if (!noexit) { + core.clearMap('all'); // 清空全地图 + core.deleteAllCanvas(); // 删除所有创建的画布 + core.dom.gif2.innerHTML = ""; + } + reason = core.replaceText(reason); core.drawText([ - "\t[" + (reason||"恭喜通关") + "]你的分数是${status:hp}。" + "\t[" + (reason || "恭喜通关") + "]你的分数是${status:hp}。" ], function () { - core.events.gameOver(reason||'', replaying, norank); + core.events.gameOver(reason || '', replaying, norank); }) }); }, @@ -110,16 +120,19 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // var currentId = core.status.floorId || null; // 获得当前的floorId,可能为null + if (!core.hasFlag("__leaveLoc__")) core.setFlag("__leaveLoc__", {}); + if (currentId != null) core.getFlag("__leaveLoc__")[currentId] = core.status.hero.loc; + // 可以对currentId进行判定,比如删除某些自定义图层等 // if (currentId == 'MT0') { // core.deleteAllCanvas(); // } - + // 重置画布尺寸 core.maps.resizeMap(floorId); // 检查重生怪并重置 if (!fromLoad) { - core.status.maps[floorId].blocks.forEach(function(block) { + core.status.maps[floorId].blocks.forEach(function (block) { if (block.disable && core.enemys.hasSpecial(block.event.id, 23)) { block.disable = false; } @@ -131,12 +144,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // ---------- 重绘新地图;这一步将会设置core.status.floorId ---------- // core.drawMap(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); + if (!core.hasFlag("__bgm__")) core.playBgm(bgm); } // 更改画面色调 var color = core.getFlag('__color__', null); @@ -182,22 +195,31 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var fromId = core.status.floorId; // 检查能否飞行 - if (!core.status.maps[fromId].canFlyTo || !core.status.maps[toId].canFlyTo) { + if (!core.status.maps[fromId].canFlyTo || !core.status.maps[toId].canFlyTo || !core.hasVisitedFloor(toId)) { 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"; + // 平面塔模式 + var stair = null, + loc = null; + if (core.flags.flyRecordPosition) { + loc = core.getFlag("__leaveLoc__", {})[toId] || null; + } + if (loc == null) { + // 获得两个楼层的索引,以决定是上楼梯还是下楼梯 + 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); + core.changeFloor(toId, stair, loc, null, callback); return true; }, @@ -245,7 +267,10 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (!(core.material.animates[equipAnimate] || {}).se) core.playSound('attack.mp3'); // 强制战斗的战斗动画 - core.drawAnimate(equipAnimate, x != null ? x : core.getHeroLoc('x'), y != null ? y : core.getHeroLoc('y')); + if (x != null && y != null) + core.drawAnimate(equipAnimate, x, y); + else + core.drawHeroAnimate(equipAnimate); var damage = core.enemys.getDamage(enemyId, x, y); if (damage == null) damage = core.status.hero.hp + 1; @@ -291,7 +316,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = var hint = "打败 " + enemy.name; if (core.flags.enableMoney) hint += ",金币+" + money; if (core.flags.enableExperience) hint += ",经验+" + experience; - core.drawTip(hint); + core.drawTip(hint, enemy.id); // 事件的处理 var todo = []; @@ -315,6 +340,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 自爆 if (core.enemys.hasSpecial(special, 19)) { + core.status.hero.statistics.battleDamage += core.status.hero.hp - 1; core.status.hero.hp = 1; } // 退化 @@ -338,7 +364,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.setFlag('skill', 0); core.setFlag('skillName', '无'); } - core.updateStatusBar(); // 如果有加点 var point = core.material.enemys[enemyId].point; @@ -360,6 +385,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 如果事件不为空,将其插入 if (todo.length > 0) core.insertAction(todo, x, y); + core.updateStatusBar(); // 如果已有事件正在处理中 if (core.status.event.id == null) @@ -429,6 +455,13 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } */ +}, + "afterPassNet": function (x, y, id) { + // 经过特殊地形后的事件;x和y为当前坐标,id为当前的图块id + + // 这是个一次性血网的例子 + // if (id == 'lavaNet') core.removeBlock(x, y); + }, "canUseQuickShop": function(shopId) { // 当前能否使用某个快捷商店 @@ -463,7 +496,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = [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) + "点"; }], + [15, "领域", function (enemy) { return "经过怪物周围" + (enemy.zoneSquare ? "九宫格" : "十字") + "范围内" + (enemy.range || 1) + "格时自动减生命" + (enemy.value || 0) + "点"; }], [16, "夹击", "经过两只相同的怪物中间,勇士生命值变成一半"], [17, "仇恨", "战斗前,怪物附加之前积累的仇恨值作为伤害" + (core.flags.hatredDecrease ? ";战斗后,释放一半的仇恨值" : "") + "。(每杀死一个怪物获得" + (core.values.hatred || 0) + "点仇恨值)"], [18, "阻击", function (enemy) { return "经过怪物的十字领域时自动减生命" + (enemy.value || 0) + "点,同时怪物后退一格"; }], @@ -511,64 +544,72 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = mon_def = hero_atk - 1; } - // 光环检查 - // 从V2.5.4开始,对光环效果增加缓存,以解决多次重复计算的问题,从而大幅提升运行效率。 - // 检查当前楼层所有光环怪物(数字25) - var hp_buff = 0, - atk_buff = 0, - def_buff = 0, - cnt = 0; // ------ 支援 ------ var guards = []; - // 检查光环缓存 - var index = x != null && y != null ? (x + "," + y) : "floor"; - if (!core.status.checkBlock) core.status.checkBlock = {}; - if (!core.status.checkBlock.cache) core.status.checkBlock.cache = {}; - var cache = core.status.checkBlock.cache[index]; - if (!cache) { - // 没有该点的缓存,则遍历每个图块 - core.status.maps[floorId].blocks.forEach(function (block) { - if (!block.disable) { - // 获得该图块的ID - var id = block.event.id, - enemy = core.material.enemys[id]; - // 检查是不是怪物,且是否拥有该特殊属性 - if (enemy && core.hasSpecial(enemy.special, 25)) { - // 检查是否可叠加 - if (enemy.add || cnt == 0) { - hp_buff += enemy.value || 0; - atk_buff += enemy.atkValue || 0; - def_buff += enemy.defValue || 0; - cnt++; - } - } - // 检查【支援】技能 - if (enemy && core.hasSpecial(enemy.special, 26) && - // 检查支援条件,坐标存在,距离为1,且不能是自己 - // 其他类型的支援怪,比如十字之类的话.... 看着做是一样的 - x != null && y != null && Math.abs(block.x - x) <= 1 && Math.abs(block.y - y) <= 1 && !(x == block.x && y == block.y)) { - // 记录怪物的x,y,ID - guards.push([block.x, block.y, id]); - } - // TODO:如果有其他类型光环怪物在这里仿照添加检查 - } - }); - - core.status.checkBlock.cache[index] = { "hp_buff": hp_buff, "atk_buff": atk_buff, "def_buff": def_buff, "guards": guards }; - } else { - // 直接使用缓存数据 - hp_buff = cache.hp_buff; - atk_buff = cache.atk_buff; - def_buff = cache.def_buff; - guards = cache.guards; + // 光环检查 + // 在这里判定是否需要遍历全图(由于光环需要遍历全图,应尽可能不需要以减少计算量,尤其是大地图) + var query = function () { + var floorIds = ["MTx"]; // 在这里给出所有需要遍历的楼层(即有光环或支援等) + return core.inArray(floorIds, floorId); // 也可以写其他的判定条件 } - // 增加比例;如果要增加数值可以直接在这里修改 - mon_hp *= (1 + hp_buff / 100); - mon_atk *= (1 + atk_buff / 100); - mon_def *= (1 + def_buff / 100); + if (query()) { + // 从V2.5.4开始,对光环效果增加缓存,以解决多次重复计算的问题,从而大幅提升运行效率。 + // 检查当前楼层所有光环怪物(数字25) + var hp_buff = 0, + atk_buff = 0, + def_buff = 0, + cnt = 0; + // 检查光环缓存 + var index = x != null && y != null ? (x + "," + y) : "floor"; + if (!core.status.checkBlock) core.status.checkBlock = {}; + if (!core.status.checkBlock.cache) core.status.checkBlock.cache = {}; + var cache = core.status.checkBlock.cache[index]; + if (!cache) { + // 没有该点的缓存,则遍历每个图块 + core.status.maps[floorId].blocks.forEach(function (block) { + if (!block.disable) { + // 获得该图块的ID + var id = block.event.id, + enemy = core.material.enemys[id]; + // 检查是不是怪物,且是否拥有该特殊属性 + if (enemy && core.hasSpecial(enemy.special, 25)) { + // 检查是否可叠加 + if (enemy.add || cnt == 0) { + hp_buff += enemy.value || 0; + atk_buff += enemy.atkValue || 0; + def_buff += enemy.defValue || 0; + cnt++; + } + } + // 检查【支援】技能 + if (enemy && core.hasSpecial(enemy.special, 26) && + // 检查支援条件,坐标存在,距离为1,且不能是自己 + // 其他类型的支援怪,比如十字之类的话.... 看着做是一样的 + x != null && y != null && Math.abs(block.x - x) <= 1 && Math.abs(block.y - y) <= 1 && !(x == block.x && y == block.y)) { + // 记录怪物的x,y,ID + guards.push([block.x, block.y, id]); + } + // TODO:如果有其他类型光环怪物在这里仿照添加检查 + } + }); + + core.status.checkBlock.cache[index] = { "hp_buff": hp_buff, "atk_buff": atk_buff, "def_buff": def_buff, "guards": guards }; + } else { + // 直接使用缓存数据 + hp_buff = cache.hp_buff; + atk_buff = cache.atk_buff; + def_buff = cache.def_buff; + guards = cache.guards; + } + + // 增加比例;如果要增加数值可以直接在这里修改 + mon_hp *= (1 + hp_buff / 100); + mon_atk *= (1 + atk_buff / 100); + mon_def *= (1 + def_buff / 100); + } // TODO:可以在这里新增其他的怪物数据变化 // 比如仿攻(怪物攻击不低于勇士攻击): @@ -603,9 +644,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = hero_atk = core.getRealStatusOrDefault(hero, 'atk'), hero_def = core.getRealStatusOrDefault(hero, 'def'), hero_mdef = core.getRealStatusOrDefault(hero, 'mdef'), - origin_hero_hp = hero_hp, - origin_hero_atk = hero_atk, - origin_hero_def = hero_def; + origin_hero_hp = core.getStatusOrDefault(hero, 'hp'), + origin_hero_atk = core.getStatusOrDefault(hero, 'atk'), + origin_hero_def = core.getStatusOrDefault(hero, 'def'); // 勇士的负属性都按0计算 hero_hp = Math.max(0, hero_hp); @@ -735,18 +776,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = "turn": Math.floor(turn), "damage": Math.floor(damage) }; -}, - "updateEnemys": function () { - // 更新怪物数据,可以在这里对怪物属性和数据进行动态更新,详见文档——事件——怪物数据的动态修改 - // 此函数执行时间:重新开始游戏、读档后、通过事件调用“更新怪物数据”时 - - // 比如下面这个例子,如果flag:xxx为真,则将绿头怪的攻击设为100,金币设为20 - /* - if (core.hasFlag('xxx')) { - core.material.enemys.greenSlime.atk = 100; - core.material.enemys.greenSlime.money = 20; - } - */ } }, "actions": { @@ -905,15 +934,29 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ } +}, + "onStatusBarClick": function (px, py) { + // 点击状态栏时触发的事件,仅在自绘状态栏开启时生效 + // px和py为点击的像素坐标 + // + // 横屏模式下状态栏的画布大小是 129*416 + // 竖屏模式下状态栏的画布大小是 416*(32*rows+9) 其中rows为状态栏行数,即全塔属性中statusCanvasRowsOnMobile值 + // 可以使用 core.domStyle.isVertical 来判定当前是否是竖屏模式 + + // 如果正在执行事件,则忽略 + if (core.status.lockControl) return; + // 如果当前正在行走,则忽略;也可以使用 core.waitHeroToStop(callback) 来停止行走再回调执行脚本 + if (core.isMoving()) return; + + // 判定px和py来执行自己的脚本内容.... 注意横竖屏 + // 这里是直接打出点击坐标的例子。 + // console.log("onStatusBarClick:", px, py); } }, "control": { "saveData": function () { // 存档操作,此函数应该返回“具体要存档的内容” - // 勇士和hash值(防改存档文件来作弊) - var hero = core.clone(core.status.hero), - hashCode = core.utils.hashCode(hero); // 差异化存储values var values = {}; for (var key in core.values) { @@ -924,16 +967,18 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 要存档的内容 var data = { 'floorId': core.status.floorId, - 'hero': hero, + 'hero': core.clone(core.status.hero), 'hard': core.status.hard, 'maps': core.maps.saveMap(), 'route': core.encodeRoute(core.status.route), 'values': values, 'shops': {}, 'version': core.firstData.version, - "time": new Date().getTime(), - "hashCode": hashCode + "time": new Date().getTime() }; + if (core.flags.checkConsole) { + data.hashCode = core.utils.hashCode(data.hero); + } // 设置商店次数 for (var shopId in core.status.shops) { data.shops[shopId] = { @@ -968,17 +1013,24 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.events.setVolume(core.getFlag("__volume__", 1), 0); // 加载勇士图标 var icon = core.getFlag("heroIcon", "hero.png"); + icon = core.getMappedName(icon); if (core.material.images.images[icon]) { - core.material.images.hero.src = core.material.images.images[icon].src; + core.material.images.hero = core.material.images.images[icon]; + core.material.icons.hero.width = core.material.images.images[icon].width / 4; core.material.icons.hero.height = core.material.images.images[icon].height / 4; } - // 刷新怪物数据 - core.updateEnemys(); // TODO:增加自己的一些读档处理 // 切换到对应的楼层 - core.changeFloor(data.floorId, null, data.hero.loc, 0, callback, true); + core.changeFloor(data.floorId, null, data.hero.loc, 0, function () { + // TODO:可以在这里设置读档后播放BGM + if (core.hasFlag("__bgm__")) { // 持续播放 + core.playBgm(core.getFlag("__bgm__")); + } + + if (callback) callback(); + }, true); }, "updateStatusBar": function () { // 更新状态栏 @@ -1013,9 +1065,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 设置魔力值 if (core.flags.enableMana) { // 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); + if (core.status.hero.manamax != null && core.getRealStatus('manamax') >= 0) { + core.status.hero.mana = Math.min(core.status.hero.mana, core.getRealStatus('manamax')); + core.setStatusBarInnerHTML('mana', core.status.hero.mana + "/" + core.getRealStatus('manamax')); } else { core.setStatusBarInnerHTML("mana", core.status.hero.mana); @@ -1060,7 +1112,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 难度 - core.statusBar.hard.innerHTML = core.status.hard; + core.statusBar.hard.innerText = core.status.hard; // 自定义状态栏绘制 core.drawStatusBar(); @@ -1213,8 +1265,10 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (enemyDamage != null && enemyDamage < value) value = enemyDamage; } - damage[loc] = (damage[loc] || 0) + value; - type[loc] = "夹击伤害"; + if (value > 0) { + damage[loc] = (damage[loc] || 0) + value; + type[loc] = "夹击伤害"; + } } } } @@ -1257,15 +1311,13 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 如需强行终止行走可以在这里条件判定: // core.stopAutomaticRoute(); - - core.updateStatusBar(); }, - "moveDirectly": function (x, y) { - // 瞬间移动;x,y为要瞬间移动的点 + "moveDirectly": function (x, y, ignoreSteps) { + // 瞬间移动;x,y为要瞬间移动的点;ignoreSteps为减少的步数,可能之前已经被计算过 // 返回true代表成功瞬移,false代表没有成功瞬移 // 判定能否瞬移到该点 - var ignoreSteps = core.canMoveDirectly(x, y); + if (ignoreSteps == null) ignoreSteps = core.canMoveDirectly(x, y); if (ignoreSteps >= 0) { core.clearMap('hero'); // 获得勇士最后的朝向 @@ -1301,20 +1353,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = main.log(e); } } - - // 下面是一个并行事件开门的样例 - /* - // 如果某个flag为真 - if (core.hasFlag("xxx")) { - // 千万别忘了将该flag清空!否则下次仍然会执行这段代码。 - core.removeFlag("xxx"); - // 使用insertAction来插入若干自定义事件执行 - core.insertAction([ - {"type":"openDoor", "loc":[0,0], "floorId": "MT0"} - ]) - // 也可以写任意其他的脚本代码 - } - */ } }, "ui": { diff --git a/project/icons.js b/project/icons.js index 0fe339c2..4c84391c 100644 --- a/project/icons.js +++ b/project/icons.js @@ -25,6 +25,7 @@ var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = "leftFoot": 1, "rightFoot": 3 }, + "width": 32, "height": 48 }, "terrains": { @@ -137,8 +138,7 @@ var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = "npc0": 0, "npc1": 1, "npc2": 2, - "npc3": 3, - "npc4": 4 + "npc3": 3 }, "enemys": { "greenSlime": 0, diff --git a/project/images/bg.jpg b/project/images/bg.jpg index 55cf862c..3fbf4d1c 100644 Binary files a/project/images/bg.jpg and b/project/images/bg.jpg differ diff --git a/project/images/npc48.png b/project/images/npc48.png index 43c3a376..5b5b0fec 100644 Binary files a/project/images/npc48.png and b/project/images/npc48.png differ diff --git a/project/items.js b/project/items.js index efda5b71..598488b7 100644 --- a/project/items.js +++ b/project/items.js @@ -3,30 +3,33 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "items": { "yellowKey": { "cls": "keys", - "name": "黄钥匙" + "name": "黄钥匙", + "text": "可以打开一扇黄门" }, "blueKey": { "cls": "keys", - "name": "蓝钥匙" + "name": "蓝钥匙", + "text": "可以打开一扇蓝门" }, "redKey": { "cls": "keys", - "name": "红钥匙" + "name": "红钥匙", + "text": "可以打开一扇红门" }, "redJewel": { "cls": "items", "name": "红宝石", - "text": "',攻击+'+core.values.redJewel" + "text": "',攻击+${core.values.redJewel}'" }, "blueJewel": { "cls": "items", "name": "蓝宝石", - "text": "',防御+'+core.values.blueJewel" + "text": "',防御+${core.values.blueJewel}'" }, "greenJewel": { "cls": "items", "name": "绿宝石", - "text": "',魔防+'+core.values.greenJewel" + "text": "',魔防+${core.values.greenJewel}'" }, "yellowJewel": { "cls": "items", @@ -36,22 +39,22 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "redPotion": { "cls": "items", "name": "红血瓶", - "text": "',生命+'+core.values.redPotion" + "text": "',生命+${core.values.redPotion}'" }, "bluePotion": { "cls": "items", "name": "蓝血瓶", - "text": "',生命+'+core.values.bluePotion" + "text": "',生命+${core.values.bluePotion}'" }, "yellowPotion": { "cls": "items", "name": "黄血瓶", - "text": "'生命+'+core.values.yellowPotion" + "text": "',生命+${core.values.yellowPotion'}" }, "greenPotion": { "cls": "items", "name": "绿血瓶", - "text": "',生命+'+core.values.greenPotion" + "text": "',生命+${core.values.greenPotion}'" }, "sword0": { "cls": "items", @@ -365,7 +368,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "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});", "snow": "core.removeBlockByIds(core.status.floorId, core.status.event.ui);\ncore.drawMap(core.status.floorId, function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "bigKey": "core.removeBlockByIds(core.status.floorId, core.status.event.ui);\ncore.drawMap(core.status.floorId, function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "bigKey": "(function () {\n\tvar actions = core.status.event.ui.map(function (id) {\n\t\tvar block = core.status.thisMap.blocks[id];\n\t\treturn { \"type\": \"openDoor\", \"loc\": [block.x, block.y], \"async\": true };\n\t});\n\tactions.push({ \"type\": \"waitAsync\" });\n\tactions.push({ \"type\": \"tip\", \"text\": core.material.items[itemId].name + \"使用成功\" });\n\tcore.insertAction(actions);\n})();", "bomb": "core.playSound('bomb.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\tcore.events.afterUseBomb();\n});", "hammer": "core.playSound('bomb.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\tcore.events.afterUseBomb();\n});", "centerFly": "core.playSound('centerFly.mp3');\ncore.clearMap('hero');\ncore.setHeroLoc('x', core.bigmap.width-1-core.getHeroLoc('x'));\ncore.setHeroLoc('y', core.bigmap.height-1-core.getHeroLoc('y'));\ncore.drawHero();\ncore.drawTip(core.material.items[itemId].name + '使用成功');", @@ -399,7 +402,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "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 (!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})();", + "bigKey": "(function () {\n\tvar able = false,\n\t\tids = [];\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');", @@ -416,5 +419,5 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "yellowJewel": "true", "skill1": "true" }, - "canEquip": {} + "equipCondition": {} } \ No newline at end of file diff --git a/project/maps.js b/project/maps.js index d631d0ba..fc9bd45f 100644 --- a/project/maps.js +++ b/project/maps.js @@ -98,11 +98,10 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = "130": {"cls":"npcs","id":"pinkShop"}, "131": {"cls":"npcs","id":"blueShop"}, "132": {"cls":"npcs","id":"princess"}, - "133": {"cls":"npc48","id":"npc0"}, - "134": {"cls":"npc48","id":"npc1"}, - "135": {"cls":"npc48","id":"npc2"}, - "136": {"cls":"npc48","id":"npc3"}, - "137": {"cls":"npc48","id":"npc4"}, + "133": {"cls":"npc48","id":"npc0","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"}}, + "134": {"cls":"npc48","id":"npc1","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"}}, + "135": {"cls":"npc48","id":"npc2","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"}}, + "136": {"cls":"npc48","id":"npc3","faceIds":{"down":"npc0","left":"npc1","right":"npc2","up":"npc3"}}, "151": {"cls":"autotile","id":"autotile1","noPass":true}, "152": {"cls":"autotile","id":"autotile2","noPass":true}, "153": {"cls":"autotile","id":"autotile3","noPass":true}, diff --git a/project/plugins.js b/project/plugins.js index d09e9944..63637164 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -84,5 +84,480 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = ctx.globalCompositeOperation = 'source-over'; // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); } +}, + "itemShop": function () { + // 道具商店相关的插件 + // 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑(如果找不到可以在入口方块中找) + + var shopId = null; // 当前商店ID + var type = 0; // 当前正在选中的类型,0买入1卖出 + var selectItem = 0; // 当前正在选中的道具 + var selectCount = 0; // 当前已经选中的数量 + var page = 0; + var totalPage = 0; + var totalMoney = 0; + var list = []; + + var bigFont = core.ui._buildFont(20, false), + middleFont = core.ui._buildFont(18, false); + + this.drawItemShop = function () { + // 绘制道具商店 + + // Step 1: 背景和固定的几个文字 + core.ui._createUIEvent(); + core.clearMap('uievent'); + core.ui._uievent_drawSelector({ "code": 1 }); + core.ui._uievent_drawSelector({ "code": 2 }); + core.setTextAlign('uievent', 'left'); + core.setTextBaseline('uievent', 'top'); + core.fillRect('uievent', 0, 0, 416, 416, 'black'); + core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 0, width: 416, height: 56 }); + core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 56, width: 312, height: 56 }); + core.ui._uievent_drawBackground({ background: 'winskin.png', x: 0, y: 112, width: 312, height: 304 }); + core.ui._uievent_drawBackground({ background: 'winskin.png', x: 312, y: 56, width: 104, height: 56 }); + core.ui._uievent_drawBackground({ background: 'winskin.png', x: 312, y: 112, width: 104, height: 304 }); + core.setFillStyle('uievent', 'white'); + core.setStrokeStyle('uievent', 'white'); + core.fillText("uievent", "购买", 32, 74, 'white', bigFont); + core.fillText("uievent", "卖出", 132, 74); + core.fillText("uievent", "离开", 232, 74); + core.fillText("uievent", "当前金币", 324, 66, null, middleFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", core.formatBigNumber(core.status.hero.money), 405, 89); + core.setTextAlign("uievent", "left"); + core.ui._uievent_drawSelector({ + "type": "drawSelector", + "image": "winskin.png", + "code": 2, + "x": 22 + 100 * type, + "y": 66, + "width": 60, + "height": 33 + }); + if (selectItem != null) { + core.setTextAlign('uievent', 'center'); + core.fillText("uievent", type == 0 ? "买入个数" : "卖出个数", 364, 320, null, bigFont); + core.fillText("uievent", "◀ " + selectCount + " ▶", 364, 350); + core.fillText("uievent", "确定", 364, 380); + } + + // Step 2:获得列表并展示 + var choices = core.status.shops[shopId].choices; + list = choices.filter(function (one) { + if (one.condition != null) { + try { if (!core.calValue(one.condition)) return false; } catch (e) {} + } + return (type == 0 && one.money != null) || (type == 1 && one.sell != null); + }); + var per_page = 6; + totalPage = Math.ceil(list.length / per_page); + page = Math.floor((selectItem || 0) / per_page) + 1; + + // 绘制分页 + if (totalPage > 1) { + var half = 156; + core.setTextAlign('uievent', 'center'); + core.fillText('uievent', page + " / " + totalPage, half, 388, null, middleFont); + if (page > 1) core.fillText('uievent', '上一页', half - 80, 388); + if (page < totalPage) core.fillText('uievent', '下一页', half + 80, 388); + } + core.setTextAlign('uievent', 'left'); + + // 绘制每一项 + var start = (page - 1) * per_page; + for (var i = 0; i < per_page; ++i) { + var curr = start + i; + if (curr >= list.length) break; + var item = list[curr]; + core.drawIcon('uievent', item.id, 10, 125 + i * 40); + core.setTextAlign('uievent', 'left'); + core.fillText('uievent', core.material.items[item.id].name, 50, 132 + i * 40, null, bigFont); + core.setTextAlign('uievent', 'right'); + core.fillText('uievent', (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) + "金币/个", 300, 133 + i * 40, null, middleFont); + core.setTextAlign("uievent", "left"); + if (curr == selectItem) { + // 绘制描述,文字自动放缩 + var text = core.material.items[item.id].text || "该道具暂无描述"; + try { text = core.replaceText(text); } catch (e) {} + for (var fontSize = 20; fontSize >= 8; fontSize -= 2) { + var config = { left: 10, fontSize: fontSize, maxWidth: 403, lineHeight: 1.4 }; + var height = core.getTextContentHeight(text, config); + if (height <= 50) { + config.top = (56 - height) / 2; + core.drawTextContent("uievent", text, config); + break; + } + } + core.ui._uievent_drawSelector({ "type": "drawSelector", "image": "winskin.png", "code": 1, "x": 8, "y": 120 + i * 40, "width": 295, "height": 40 }); + if (type == 0 && item.number != null) { + core.fillText("uievent", "存货", 324, 132, null, bigFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", item.number, 406, 132, null, null, 40); + } else if (type == 1) { + core.fillText("uievent", "数量", 324, 132, null, bigFont); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", core.itemCount(item.id), 406, 132, null, null, 40); + } + core.setTextAlign("uievent", "left"); + core.fillText("uievent", "预计金额", 324, 250); + core.setTextAlign("uievent", "right"); + totalMoney = selectCount * (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)); + core.fillText("uievent", core.formatBigNumber(totalMoney), 405, 280); + + core.setTextAlign("uievent", "left"); + core.fillText("uievent", type == 0 ? "已购次数" : "已卖次数", 324, 170); + core.setTextAlign("uievent", "right"); + core.fillText("uievent", (type == 0 ? item.money_count : item.sell_count) || 0, 405, 200); + } + } + + core.setTextAlign('uievent', 'left'); + core.setTextBaseline('uievent', 'alphabetic'); + } + + var _add = function (item, delta) { + if (item == null) return; + selectCount = core.clamp( + selectCount + delta, 0, + Math.min(type == 0 ? Math.floor(core.status.hero.money / core.calValue(item.money)) : core.itemCount(item.id), + type == 0 && item.number != null ? item.number : Number.MAX_SAFE_INTEGER) + ); + } + + var _confirm = function (item) { + if (item == null || selectCount == 0) return; + if (type == 0) { + core.status.hero.money -= totalMoney; + core.getItem(item.id, selectCount); + if (item.number != null) item.number -= selectCount; + item.money_count = (item.money_count || 0) + selectCount; + } else { + core.status.hero.money += totalMoney; + core.removeItem(item.id, selectCount); + core.drawTip("成功卖出" + selectCount + "个" + core.material.items[item.id].name, item.id); + if (item.number != null) item.number += selectCount; + item.sell_count = (item.sell_count || 0) + selectCount; + } + selectCount = 0; + } + + this._performItemShopKeyBoard = function (keycode) { + var item = list[selectItem] || null; + // 键盘操作 + switch (keycode) { + case 38: // up + if (selectItem == null) break; + if (selectItem == 0) selectItem = null; + else selectItem--; + selectCount = 0; + break; + case 37: // left + if (selectItem == null) { + if (type > 0) type--; + break; + } + _add(item, -1); + break; + case 39: // right + if (selectItem == null) { + if (type < 2) type++; + break; + } + _add(item, 1); + break; + case 40: // down + if (selectItem == null) { + if (list.length > 0) selectItem = 0; + break; + } + if (list.length == 0) break; + selectItem = Math.min(selectItem + 1, list.length - 1); + selectCount = 0; + break; + case 13: + case 32: // Enter/Space + if (selectItem == null) { + if (type == 2) + core.insertAction({ "type": "break" }); + else if (list.length > 0) + selectItem = 0; + break; + } + _confirm(item); + break; + case 27: // ESC + if (selectItem == null) { + core.insertAction({ "type": "break" }); + break; + } + selectItem = null; + break; + } + } + + this._performItemShopClick = function (px, py) { + var item = list[selectItem] || null; + // 鼠标操作 + if (px >= 22 && px <= 82 && py >= 71 && py <= 102) { + // 买 + if (type != 0) { + type = 0; + selectItem = null; + selectCount = 0; + } + return; + } + if (px >= 122 && px <= 182 && py >= 71 && py <= 102) { + // 卖 + if (type != 1) { + type = 1; + selectItem = null; + selectCount = 0; + } + return; + } + if (px >= 222 && px <= 282 && py >= 71 && py <= 102) // 离开 + return core.insertAction({ "type": "break" }); + // ◀,▶ + if (px >= 318 && px <= 341 && py >= 348 && py <= 376) + return _add(item, -1); + if (px >= 388 && px <= 416 && py >= 348 && py <= 376) + return _add(item, 1); + // 确定 + if (px >= 341 && px <= 387 && py >= 380 && py <= 407) + return _confirm(item); + + // 上一页/下一页 + if (px >= 45 && px <= 105 && py >= 388) { + if (page > 1) selectItem -= 6; + return; + } + if (px >= 208 && px <= 268 && py >= 388) { + if (page < totalPage) selectItem = Math.min(selectItem + 6, list.length - 1); + return; + } + + // 实际区域 + if (px >= 9 && px <= 300 && py >= 120 && py < 360) { + if (list.length == 0) return; + var index = parseInt((py - 120) / 40); + var newItem = 6 * (page - 1) + index; + if (newItem >= list.length) newItem = list.length - 1; + if (newItem != selectItem) { + selectItem = newItem; + selectCount = 0; + } + return; + } + } + + this.performItemShopAction = function () { + if (flags.type == 0) return this._performItemShopKeyBoard(flags.keycode); + else return this._performItemShopClick(flags.px, flags.py); + } + + this.openItemShop = function (itemShopId) { + shopId = itemShopId; + type = 0; + page = 0; + selectItem = null; + selectCount = 0; + core.insertAction([{ + "type": "while", + "condition": "true", + "data": [ + { "type": "function", "function": "function () { core.drawItemShop(); }" }, + { "type": "wait" }, + { "type": "function", "function": "function() { core.performItemShopAction(); }" } + ] + }, + { "type": "function", "function": "function () { " + + "core.deleteCanvas('uievent'); " + + "core.ui._uievent_drawSelector({ \"code\": 1 }); " + + "core.ui._uievent_drawSelector({ \"code\": 2 }); " + + "}" } + ]); + } + + // Write item number to save + core.control.saveData = function () { + var data = this.controldata.saveData(); + for (var shopId in core.status.shops) { + if (core.status.shops[shopId].item) { + data.shops[shopId].choices = core.status.shops[shopId].choices.map(function (t) { + return { + number: t.number, + money_count: t.money_count || 0, + sell_count: t.sell_count || 0 + } + }); + } + } + return data; + } + + core.control.loadData = function (data, callback) { + this.controldata.loadData(data, callback); + for (var shopId in data.shops) { + if (data.shops[shopId].choices) { + for (var i = 0; i < data.shops[shopId].choices.length; ++i) { + core.status.shops[shopId].choices[i].number = data.shops[shopId].choices[i].number; + core.status.shops[shopId].choices[i].money_count = data.shops[shopId].choices[i].money_count; + core.status.shops[shopId].choices[i].sell_count = data.shops[shopId].choices[i].sell_count; + } + } + } + } + +}, + "smoothCamera": function () { + + // 是否启用本插件,默认不启用 + this.__enableSmoothCamera = false; + if (!this.__enableSmoothCamera) return; + + this.Camera = function () { + + // 下面这个变量决定本插件的开关 + // 你可以在游戏中使用core.setFlag('smoothCamera',false)来关闭本插件的功能 + // 同时也可以core.setFlag('smoothCamera',true)重新开启 + // 此项默认为true + // + this.__switchName = 'smoothCamera'; + + // 初始化成员变量 + this._cameraNeedRefresh = true; + this._nowOffsetX = 0; + this._nowOffsetY = 0; + this._targetOffsetX = 0; + this._targetOffsetY = 0; + this._currentFloorId = null; + + // 重置镜头,在楼层变更时使用 + this.resetCamera = function () { + this._targetOffsetX = core.bigmap.offsetX; + this._targetOffsetY = core.bigmap.offsetY; + this._nowOffsetX = this._targetOffsetX; + this._nowOffsetY = this._targetOffsetY; + this._cameraNeedRefresh = true; + }; + + // 设置焦点坐标,目前没有用 + this.setTarget = function (x, y) { + this._targetOffsetX = x; + this._targetOffsetY = y; + }; + + // 请求镜头更新 + this.requestCameraUpdate = function () { + this._cameraNeedRefresh = true; + }; + + // 更新焦点坐标,目前仅根据大地图偏移决定 + this.updateTargetPosition = function () { + this._targetOffsetX = core.bigmap.offsetX; + this._targetOffsetY = core.bigmap.offsetY; + }; + + // 更新额外的刷新条件,即镜头未指向焦点时 + this.updateRefreshFlag = function () { + if (this._nowOffsetX != this._targetOffsetX || this._nowOffsetY != this._targetOffsetY) { + this._cameraNeedRefresh = true; + } + }; + + // 判断是否禁止了弹性滚动 + this.canDirectMove = function () { + return !core.getFlag(this.__switchName, true); + }; + + // 更新镜头坐标 + this.updateCameraPosition = function () { + if (this._cameraNeedRefresh) { + this._cameraNeedRefresh = false; + var disX = this._targetOffsetX - this._nowOffsetX; + var disY = this._targetOffsetY - this._nowOffsetY; + if (Math.abs(disX) <= 2 && Math.abs(disY) <= 2 || this.canDirectMove()) { + this._nowOffsetX = this._targetOffsetX; + this._nowOffsetY = this._targetOffsetY; + } else { + this._nowOffsetX += disX / 10; + this._nowOffsetY += disY / 10; + } + var x = -Math.floor(this._nowOffsetX); + var y = -Math.floor(this._nowOffsetY); + core.bigmap.canvas.forEach(function (cn) { + core.control.setGameCanvasTranslate(cn, x, y); + }); + core.relocateCanvas('route', core.status.automaticRoute.offsetX + x, core.status.automaticRoute.offsetY + y); + core.setGameCanvasTranslate('hero', x + this._targetOffsetX, y + this._targetOffsetY); + } + }; + + // 更新逻辑主体 + this.update = function () { + this.updateTargetPosition(); + this.updateRefreshFlag(); + this.updateCameraPosition(); + }; + }; + + // 其实只注释了最后一行,只能这样了 + control.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'); + + if (!core.hasFlag('hideHero')) { + this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (block) { + core.drawImage('hero', block.img, block.heroIcon[block.status] * block.width, + block.heroIcon.loc * block.height, block.width, block.height, + block.posx + (32 - block.width) / 2, block.posy + 32 - block.height, block.width, block.height); + }); + } + + core.control.updateViewport(); + //core.setGameCanvasTranslate('hero', 0, 0); + }; + + // 复写转发 + core.drawHero = function (status, offset) { + return core.control.drawHero(status, offset); + }; + + // 创建摄像机对象 + this.camera = new this.Camera(); + + // 帧事件 更新摄像机 + this.updateCameraEx = function () { + this.camera.update(); + }; + + // 代理原本的镜头事件 + control.updateViewport = function () { + core.plugin.camera.requestCameraUpdate(); + }; + + // 更变楼层的行为追加,重置镜头 + events.prototype.changingFloor = function (floorId, heroLoc, fromLoad) { + this.eventdata.changingFloor(floorId, heroLoc, fromLoad); + core.plugin.camera.resetCamera(); + }; + + // 注册帧事件 + core.registerAnimationFrame('smoothCameraFlash', true, this.updateCameraEx.bind(this)); } } \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 00000000..7302d2ad --- /dev/null +++ b/server.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- + +# HTML5魔塔样板,启动服务Python版 +# 需要安装Python环境,并 pip install flask 安装Flask库 +# 运行方式:python server.py 或 python3 server.py + +import sys +import json +import os +import base64 + +isPy3 = sys.version_info > (3, 0) + +def p(s): # s is unicode in py2 and str in py3 + if isPy3: print(s) + else: print(s.decode('utf-8')) +p("") + +try: + from flask import Flask, request, Response, abort + import mimetypes + import socket +except: + p("需要flask才可使用本服务。\n安装方式:%s install flask" % ("pip3" if isPy3 else "pip")) + exit(1) + +app = Flask(__name__, static_folder='') +app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 + +@app.after_request +def add_header(r): + r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" + r.headers["Pragma"] = "no-cache" + r.headers["Expires"] = "0" + r.headers['Cache-Control'] = 'public, max-age=0' + return r + +def is_sub(filename): + try: + return (os.path.realpath(filename) + os.sep).startswith(os.path.realpath(".") + os.sep) + except: + return True + +def get_mimetype(path): + return mimetypes.guess_type(path)[0] or 'application/octet-stream' + +def get_file(path): + if not os.path.exists(path): + abort(404) + return None + if not is_sub(path): + abort(403) + return None + with open(path, 'rb') as f: + content = f.read() # str in py2 and bytes in py3 + return content + +@app.route('/', methods=['GET']) +def root(): + return static_file('index.html') + +@app.route('/', methods=['GET']) +def static_file(path): + return Response(get_file(path), mimetype = get_mimetype(path)) + +def process_request(): + data = request.get_data() # str in py2 and bytes in py3 + if isPy3: data = str(data, encoding = 'utf-8') + params = data.split("&") + d = {} + for one in params: + index = one.find("=") + if index >= 0: + d[one[:index]] = one[index+1:] + return d # str in py2 & py3 + +@app.route('/readFile', methods=['POST']) +def readFile(): + data = process_request() + tp = data.get('type', 'base64') + filename = data.get('name', None) + content = get_file(filename) + return content if tp == 'utf8' or content is None else base64.b64encode(content) + +@app.route('/writeFile', methods=['POST']) +def writeFile(): + data = process_request() + tp = data.get('type', 'base64') + filename = data.get('name', None) + if not is_sub(filename): + abort(403) + return + value = data.get('value', '') + if isPy3: value = value.encode('utf-8') + if tp == 'base64': value = base64.b64decode(value) + with open(filename, 'wb') as f: + f.write(value) # str in py2 and bytes in py3 + return str(len(value)) + +@app.route('/writeMultiFiles', methods=['POST']) +def writeMultiFiles(): + data = process_request() + filenames = data.get('name', '').split(';') + values = data.get('value', '').split(';') + l = 0 + for i in range(len(filenames)): + if i >= len(values): + break + filename = filenames[i] + value = values[i].encode('utf-8') if isPy3 else values[i] + value = base64.b64decode(value) + if not is_sub(filename): + abort(403) + return + with open(filename, 'wb') as f: + f.write(value) + l += len(value) + return str(l) + +@app.route('/listFile', methods=['POST']) +def listFile(): + data = process_request() + filename = data.get('name', None) + if filename is None or not os.path.isdir(filename): + abort(404) + return + if not is_sub(filename): + abort(403) + return + files = [f + for f in os.listdir(filename) + if os.path.isfile(os.path.join(filename, f))] + return "[" + ", ".join(['"'+f+'"' for f in files]) + "]" + +def port_used(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = True + try: + sock.bind(("0.0.0.0", port)) + result = False + except: + pass + sock.close() + return result + +if __name__ == '__main__': + port = 1055 + while port_used(port): + port += 1 + if port > 1055: + p("默认的1055端口已被占用,自动选择%d端口。请注意,不同端口下的存档等信息都是不共用的。\n" % port) + p("服务已启动...\n游戏地址:http://127.0.0.1:%d/\n编辑器地址:http://127.0.0.1:%d/editor.html\n" % (port, port)) + app.run(host = '0.0.0.0', port = port, debug = False) diff --git a/styles.css b/styles.css index a1d6649e..9fe80446 100644 --- a/styles.css +++ b/styles.css @@ -117,16 +117,25 @@ } #startButtonGroup { - width: 100%; + width: auto; position: absolute; text-align: center; font-size: 1.4rem; - background-color: #000; - opacity: 0.85; display: none; z-index: 310; bottom: 0; - margin-bottom: 7%; + margin-bottom: 5%; + left: 50%; + transform: translateX(-50%); + padding: 15px 25px; + min-width: 20%; + /* default value */ + background-color: #32369F; + opacity: 0.85; + color: #FFFFFF; + border: #FFFFFF 2px solid; + caret-color: #FFD700; + border-radius: 10px; } #startButtons { @@ -139,15 +148,19 @@ .startButton { width: 100%; - margin: 20px 0; - color: #fff; + margin: 0; font-weight: bold; display: block; cursor: pointer; + padding: 4px 0; + border-color: transparent; + border-width: 2px; + border-style: solid; + border-radius: 6px; } -.startButton:hover { - color: #ff0000; +.onChoiceAnimate { + animation: onChoice 2s ease-in-out 0s infinite normal none running; } #floorMsgGroup { @@ -334,11 +347,10 @@ p#name { position: fixed; top: 50%; left: 50%; - transform: translate(-50%, -60%); + transform: translate(-50%, -55%); background: white; width: 250px; min-height: 50px; - max-height: 250px; } #inputMessage { @@ -365,4 +377,107 @@ p#name { #inputNo { float:right; margin-right: 10%; -} \ No newline at end of file +} + +#_selector, ._uievent_selector { + animation: selector 2s ease-in-out 0s infinite normal none running; +} + +@-webkit-keyframes selector { + 0% { opacity: 0.95; } + 50% { opacity: 0.55; } + 100% { opacity: 0.95; } +} + +@keyframes selector { + 0% { opacity: 0.95; } + 50% { opacity: 0.55; } + 100% { opacity: 0.95; } +} + +#next { + width: 5px; + height: 5px; + display: none; + position: absolute; + transform: rotate(45deg); + border-bottom-width: 4px; + border-bottom-style: solid; + border-right-width: 4px; + border-right-style: solid; + -webkit-animation: next .5s ease-in-out alternate infinite; + animation: next .5s ease-in-out alternate infinite; + left: 0; + top: 0; + opacity: 0.7; + z-index: 169; +} + +@-webkit-keyframes next { + 100% { + transform: rotate(45deg) translate(-3px, -3px); + } +} +@keyframes next { + 100% { + transform: rotate(45deg) translate(-3px, -3px); + } +} + +#startImageBackgroundDiv { + display: none; + width: 100%; + height: 100%; + position: fixed; + z-index: 10000; +} + +#startImageDiv { + width: 100%; + height: 100%; + position: fixed; + background: black; + opacity: 1; +} + +#startImageLogo { + opacity: 0; + max-width: 60%; + max-height: 60%; + position: fixed; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); +} + +.startImageAnimation { + -webkit-animation: startImage 4s ease-in-out 1s alternate 1; + animation: startImage 4s ease-in-out 1s alternate 1; +} + +@-webkit-keyframes startImage { + 0% { opacity: 0; } + 60% { opacity: 1; } + 100% { opacity: 0; } +} + +@keyframes startImage { + 0% { opacity: 0; } + 60% { opacity: 1; } + 100% { opacity: 0; } +} + +.startImageDivAnimation { + -webkit-animation: startImageDivDisappear 2s ease-in-out 5s alternate 1; + animation: startImageDivDisappear 2s ease-in-out 5s alternate 1; +} + +@-webkit-keyframes startImageDivDisappear { + 0% { opacity: 1 } + 100% { opacity: 0 } +} + +@keyframes startImageDivDisappear { + 0% { opacity: 1 } + 100% { opacity: 0 } +} diff --git a/常用工具/ICSharpCode.SharpZipLib.dll b/常用工具/ICSharpCode.SharpZipLib.dll new file mode 100644 index 00000000..bcdb00ea Binary files /dev/null and b/常用工具/ICSharpCode.SharpZipLib.dll differ diff --git a/常用工具/JS代码压缩工具.exe b/常用工具/JS代码压缩工具.exe index f0447910..c5f5e89c 100644 Binary files a/常用工具/JS代码压缩工具.exe and b/常用工具/JS代码压缩工具.exe differ diff --git a/常用工具/RM动画导出器.exe b/常用工具/RM动画导出器.exe index b894a786..632441a2 100644 Binary files a/常用工具/RM动画导出器.exe and b/常用工具/RM动画导出器.exe differ diff --git a/常用工具/便捷PS工具.exe b/常用工具/便捷PS工具.exe index 90959cf0..893fbfc8 100644 Binary files a/常用工具/便捷PS工具.exe and b/常用工具/便捷PS工具.exe differ diff --git a/常用工具/动画编辑器.exe b/常用工具/动画编辑器.exe new file mode 100644 index 00000000..f6423db6 Binary files /dev/null and b/常用工具/动画编辑器.exe differ diff --git a/常用工具/额外素材合并工具.exe b/常用工具/额外素材合并工具.exe new file mode 100644 index 00000000..169828b1 Binary files /dev/null and b/常用工具/额外素材合并工具.exe differ diff --git a/更新说明.txt b/更新说明.txt index ec9833e3..1cd892d5 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,4 +1,118 @@ -HTML5魔塔样板V2.6 +HTML5魔塔样板V2.6.6 + +编辑器增加【最近使用的图块】的区域 +编辑器拉伸到铺满全屏幕,还可以Ctrl+滚轮放缩 +编辑器支持连续Ctrl+Z的撤销,Ctrl+Y的重做 +新增tileset右键绑定宽高,以替代贴图模式 +多重自动存档,可以连续A键读档 +高层塔分区域支持 +自绘状态栏点击事件 +绘制的锁定模式 +等待用户操作增设分歧选项 +增设压缩模式,会对图片等进行zip压缩 +追加素材现在可以同时进行自动注册 +可以复制和粘贴怪物或道具的属性 +折叠素材时设置每一列个数 +标题界面和显示选择项时光标跟随鼠标 +修复所有已知的bug,大量细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6.5 + +事件:设置怪物属性;穿脱装备 +新值块:enemy:xxx:atk可获得怪物数据 +新值块:blockId:x,y获得某点图块ID +部分事件预编译,加快执行速度 +在系统设置中可以设置bgm的播放音量 +通关事件可以不退出游戏 +失败时允许直接读取自动存档 +NPC48自动注册可以自动绑定faceIds +编辑器Alt+1-9存图块,1-9读取图块 +编辑器现在可以跨楼层复制粘贴图块了 +可以对flags.进行自动补全 +部分Bug修复,大量细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6.4 + +自动事件,多事件页 +增加开场logo动画 +拓展:游戏时动态修改地图和怪物数据 +插件:道具商店,支持买入和卖出道具 +编辑器可以搜索变量出现位置 +变量的中文替换 +可以给图块绑定自定义脚本,碰触时触发 +编辑器右键可以绑定机关门和出生点 +支持多个drawTip同时出现 +闪烁光标同时支持多个同时存在 +插件:镜头平滑移动,默认禁用 +素材的快速追加 +批量导出动画 +部分Bug修复,大量细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6.3 + +标题界面大幅美化,增加闪烁光标,支持键盘开始游戏 +事件编辑器支持自动补全,能对flag和API列表等进行补全 +剧情文本中\\c修改字体大小,\\d和\\e切换粗体和斜体 +事件:设置视角&移动视角 +可以指定显示选择项的出现条件并动态生成 +楼层传送器的平面传送模式(哪里离开飞回到哪里) +UI绘制事件增添绘制圆和绘制圆边框 +所有的UI绘制事件均可以双击预览 +播放BGM事件可以一直持续播放直到下次调用 +\f立绘支持alpha值 +支持在脚本编辑中直接flags.xxx调用自定义变量 +首次获得道具将给予提示 +等待用户操作支持滚轮,视为PgUp和PgDn +脚本编辑器语法错误将禁止保存 +录像播放时B键查看数据统计 +所有已知bug的修复,大量细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6.2 + +可以拖动地图上的图块和事件,复制剪切和跨楼层粘贴 +新增事件的地图选点功能,可以在地图上选择落点 +现在素材区可以进行折叠与自动换列了 +新增UI绘制系列事件,并且可以进行预览 +显示文本事件的标题解析 +新增常用工具:额外素材合并工具 +进一步提升24倍速的播放速度 +楼层转换增加对称点 +增加编辑器快捷键说明,H键查看 +文档-事件增加事件编辑器截图 +大量细节优化,所有已知的Bug修复 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6.1 + +区域优化的录像播放功能,R键使用 +强制战斗可以指定怪物坐标,将自动隐藏并执行该点战后事件 +flag:xxx也支持中文,例如 flag:2楼机关门 +增加文件名映射,可以用中文映射到某个图片或bgm文件并使用 +勇士宽度可以超过32(例如48x48的勇士行走图) +现在允许修改floorId和图块ID了(在表格下方) +增加事件:自动存档,返回标题界面;部分事件优化 +商店长按空格可以连续加点 +增设global:xxx使用全局存储,可被录像支持 +支持\b[hero]和\b[null,x,y]来自动调整上下方向 +支持\t[yellowKey]等只显示图标而没有标题 +编辑器中前景层对于有事件的点半透明显示 +存档改成1000页,长按上下页可快速翻页 +录像播放初始默认暂停,N键可以单步执行 +增设本地API文档,部分API和事件的优化 +所有已知的bug修复,大量细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.6 拆分整个项目,大幅重构代码,新增大量API 重写文档,尤其是脚本和API列表