From 379dbd0348ff179a91da46427a2097d615e7e096 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 00:19:44 +0800 Subject: [PATCH 01/16] startUsingCanvas --- _server/data.comment.js | 13 +++++++ libs/control.js | 31 +++++++++++++++-- libs/events.js | 58 +++++++++++++++++++++---------- main.js | 34 +++---------------- project/data.js | 75 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 159 insertions(+), 52 deletions(-) diff --git a/_server/data.comment.js b/_server/data.comment.js index 681abdf8..06ba1016 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -276,6 +276,13 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = } } }, + "startCanvas": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_range": "thiseval==null || thiseval instanceof Array", + "_data": "使用画布绘制开始菜单。可以在这里自定义需要的标题界面等内容。" + }, "startText": { "_leaf": true, "_type": "event", @@ -560,6 +567,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否循环计算临界;如果此项为true则使用循环法(而不是回合数计算法)来算临界" }, + "startUsingCanvas": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否开始菜单canvas化;如果此项为true,则将使用canvas来绘制开始菜单" + }, "startDirectly": { "_leaf": true, "_type": "checkbox", diff --git a/libs/control.js b/libs/control.js index c1f03d4b..7a282c86 100644 --- a/libs/control.js +++ b/libs/control.js @@ -222,6 +222,14 @@ control.prototype.showStartAnimate = function (noAnimate, callback) { core.clearStatus(); core.clearMap('all'); + if (core.flags.startUsingCanvas) { + core.dom.startTop.style.display = 'none'; + core.dom.startButtonGroup.style.display = 'block'; + core.events.startGame(''); + if (core.isset(callback)) callback(); + return; + } + if(noAnimate) { core.dom.startTop.style.display = 'none'; // core.playGame(); @@ -1655,15 +1663,22 @@ control.prototype.chooseReplayFile = function () { } if (core.isset(obj.version) && obj.version!=core.firstData.version) { // alert("游戏版本不一致!"); - if (!confirm("游戏版本不一致!\n你仍然想播放录像吗?")) + if (!confirm("游戏版本不一致!\n你仍然想播放录像吗?")) { return; + } } - if (!core.isset(obj.route) || !core.isset(obj.hard)) { + if (!core.isset(obj.route)) { alert("无效的录像!"); return; } - core.startGame(obj.hard, obj.seed, core.decodeRoute(obj.route)); + if (core.flags.startUsingCanvas) { + core.status.isStarting = false; + core.startGame('', obj.seed, core.decodeRoute(obj.route)); + } + else { + core.startGame(obj.hard, obj.seed, core.decodeRoute(obj.route)); + } }, function () { }) @@ -2672,6 +2687,16 @@ control.prototype.playSound = function (sound) { } } +control.prototype.checkBgm = function() { + if (core.musicStatus.startDirectly && core.musicStatus.bgmStatus) { + if (core.musicStatus.playingBgm==null + || core.material.bgms[core.musicStatus.playingBgm].paused) { + core.musicStatus.playingBgm=null; + core.playBgm(core.bgms[0]); + } + } +} + ////// 清空状态栏 ////// control.prototype.clearStatusBar = function() { diff --git a/libs/events.js b/libs/events.js index a02099bd..8ffd23df 100644 --- a/libs/events.js +++ b/libs/events.js @@ -106,7 +106,9 @@ events.prototype.startGame = function (hard, seed, route, callback) { } else core.utils.__init_seed(); - core.events.setInitData(hard); + if (!core.flags.startUsingCanvas) + core.events.setInitData(hard); + core.clearMap('all'); core.clearStatusBar(); @@ -114,6 +116,8 @@ events.prototype.startGame = function (hard, seed, route, callback) { core.status.isStarting = false; + core.control.triggerStatusBar('show'); + core.changeFloor(core.status.floorId, null, core.status.hero.loc, null, function() { if (core.isset(callback)) callback(); }, true); @@ -125,7 +129,7 @@ events.prototype.startGame = function (hard, seed, route, callback) { formData.append('name', core.firstData.name); formData.append('version', core.firstData.version); formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); - formData.append('hard', core.encodeBase64(hard)); + formData.append('hard', core.encodeBase64(core.status.hard)); formData.append('hardCode', core.getFlag('hard', 0)); formData.append('base64', 1); @@ -133,23 +137,35 @@ events.prototype.startGame = function (hard, seed, route, callback) { }) } - core.insertAction(core.clone(core.firstData.startText), null, null, function() { - if (!core.status.replay.replaying && core.flags.showBattleAnimateConfirm) { // 是否提供“开启战斗动画”的选择项 - core.status.event.selection = core.flags.battleAnimate ? 0 : 1; - core.ui.drawConfirmBox("你想开启战斗动画吗?\n之后可以在菜单栏中开启或关闭。\n(强烈建议新手开启此项)", function () { - core.flags.battleAnimate = true; - core.setLocalStorage('battleAnimate', true); + var real_start = function () { + core.insertAction(core.clone(core.firstData.startText), null, null, function() { + if (!core.flags.startUsingCanvas && !core.status.replay.replaying && core.flags.showBattleAnimateConfirm) { // 是否提供“开启战斗动画”的选择项 + core.status.event.selection = core.flags.battleAnimate ? 0 : 1; + core.ui.drawConfirmBox("你想开启战斗动画吗?\n之后可以在菜单栏中开启或关闭。\n(强烈建议新手开启此项)", function () { + core.flags.battleAnimate = true; + core.setLocalStorage('battleAnimate', true); + post_start(); + }, function () { + core.flags.battleAnimate = false; + core.setLocalStorage('battleAnimate', false); + post_start(); + }); + } + else { post_start(); - }, function () { - core.flags.battleAnimate = false; - core.setLocalStorage('battleAnimate', false); - post_start(); - }); - } - else { - post_start(); - } - }); + } + }); + } + + if (core.flags.startUsingCanvas) { + core.control.triggerStatusBar('hide'); + core.insertAction(core.clone(core.firstData.startCanvas), null, null, function() { + real_start(); + }); + } + else { + real_start(); + } if (core.isset(route)) { core.startReplay(route); @@ -157,6 +173,12 @@ events.prototype.startGame = function (hard, seed, route, callback) { } + if (core.flags.startUsingCanvas) { + core.dom.startPanel.style.display = 'none'; + start(); + return; + } + if (core.isset(route)) { core.dom.startPanel.style.display = 'none'; start(); diff --git a/main.js b/main.js index 9285f889..592e56f7 100644 --- a/main.js +++ b/main.js @@ -523,15 +523,7 @@ main.statusBar.image.settings.onclick = function () { ////// 点击“开始游戏”时 ////// main.dom.playGame.onclick = function () { main.dom.startButtons.style.display='none'; - - if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly - && main.core.musicStatus.bgmStatus) { - if (main.core.musicStatus.playingBgm==null - || core.material.bgms[main.core.musicStatus.playingBgm].paused) { - main.core.musicStatus.playingBgm=null; - main.core.playBgm(main.core.bgms[0]); - } - } + main.core.control.checkBgm(); if (main.core.isset(main.core.flags.startDirectly) && main.core.flags.startDirectly) { core.events.startGame(""); @@ -543,32 +535,14 @@ main.dom.playGame.onclick = function () { ////// 点击“载入游戏”时 ////// main.dom.loadGame.onclick = function() { - - if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly - && main.core.musicStatus.bgmStatus) { - if (main.core.musicStatus.playingBgm==null - || core.material.bgms[main.core.musicStatus.playingBgm].paused) { - main.core.musicStatus.playingBgm=null; - main.core.playBgm(main.core.bgms[0]); - } - } - + main.core.control.checkBgm(); main.core.load(); } ////// 点击“录像回放”时 ////// main.dom.replayGame.onclick = function () { - - if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly - && main.core.musicStatus.bgmStatus) { - if (main.core.musicStatus.playingBgm==null - || core.material.bgms[main.core.musicStatus.playingBgm].paused) { - main.core.musicStatus.playingBgm=null; - main.core.playBgm(main.core.bgms[0]); - } - } - - core.chooseReplayFile(); + main.core.control.checkBgm(); + main.core.chooseReplayFile(); } diff --git a/project/data.js b/project/data.js index d2697561..28a1ff15 100644 --- a/project/data.js +++ b/project/data.js @@ -1,4 +1,4 @@ -var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = +var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = { "main": { "floorIds": [ @@ -103,6 +103,78 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "flags": {}, "steps": 0 }, + "startCanvas": [ + { + "type": "showImage", + "name": "bg.jpg", + "loc": [ + 0, + 0 + ] + }, + { + "type": "choices", + "choices": [ + { + "text": "开始游戏", + "action": [ + { + "type": "function", + "function": "function(){\ncore.control.checkBgm()\n}" + }, + { + "type": "if", + "condition": "core.flags.startDirectly", + "true": [ + { + "type": "function", + "function": "function(){\ncore.events.setInitData('')\n}" + } + ], + "false": [ + { + "type": "function", + "function": "function(){\n// 动态生成难度选择项\nvar choices = [];\nmain.levelChoose.forEach(function (one) {\n\tchoices.push({\"text\": one[0], \"action\": [\n\t\t{\"type\": \"function\", \"function\": \"function() { core.status.hard = '\"+one[1]+\"'; core.events.setInitData('\"+one[1]+\"'); }\"}\n\t]});\n})\ncore.insertAction({\"type\": \"choices\", \"choices\": choices});\n}" + } + ] + }, + { + "type": "showImage" + } + ] + }, + { + "text": "读取存档", + "action": [ + { + "type": "function", + "function": "function(){\ncore.control.checkBgm()\n}" + }, + { + "type": "showImage" + }, + { + "type": "function", + "function": "function(){\ncore.insertAction([{\"type\": \"exit\"}], null, null, function() {\n\tcore.status.played = false;\n\tcore.status.isStarting = false;\n\tcore.load();\n})\n}" + } + ] + }, + { + "text": "回放录像", + "action": [ + { + "type": "function", + "function": "function(){\ncore.control.checkBgm()\n}" + }, + { + "type": "function", + "function": "function(){\ncore.insertAction([{\"type\": \"exit\"}], null, null, function() {\n\tcore.restart();\n\tcore.chooseReplayFile();\n})\n}" + } + ] + } + ] + } + ], "startText": [ "Hi,欢迎来到 HTML5 魔塔样板!\n\n本样板由艾之葵制作,可以让你在不会写任何代码\n的情况下也能做出属于自己的H5魔塔!", "这里游戏开始时的剧情。\n定义在data.js的startText处。\n\n你可以在这里写上自己的内容。", @@ -248,6 +320,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "hatredDecrease": true, "betweenAttackCeil": false, "useLoop": false, + "startUsingCanvas": true, "startDirectly": false, "canOpenBattleAnimate": true, "showBattleAnimateConfirm": false, From f8ead89d909b3e0a623fc58b6b8c08fc93b38d8b Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 01:41:35 +0800 Subject: [PATCH 02/16] Update startGame --- _server/data.comment.js | 2 +- docs/event.md | 2 +- docs/personalization.md | 19 ++++++++++++++- libs/control.js | 11 +++------ libs/events.js | 26 ++++++++++---------- libs/maps.js | 42 ++++++++++++++++++++++++-------- project/data.js | 54 ++++++++++++++++++++++++++++++++++++++--- project/functions.js | 2 +- 8 files changed, 121 insertions(+), 37 deletions(-) diff --git a/_server/data.comment.js b/_server/data.comment.js index 06ba1016..c3262c4d 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -281,7 +281,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "event", "_event": "firstArrive", "_range": "thiseval==null || thiseval instanceof Array", - "_data": "使用画布绘制开始菜单。可以在这里自定义需要的标题界面等内容。" + "_data": "标题界面事件化,可以使用事件流的形式来绘制开始界面等。\n需要开启startUsingCanvas这个开关。\n详见文档-个性化-标题界面事件化。" }, "startText": { "_leaf": true, diff --git a/docs/event.md b/docs/event.md index 041d9547..91ddc83f 100644 --- a/docs/event.md +++ b/docs/event.md @@ -2113,7 +2113,7 @@ if (core.getFlag("door",0)==2) { core.stopReplay(); core.waitHeroToStop(function() { core.removeGlobalAnimate(0,0,true); - core.clearMap('all'); // 清空全地图 + core.clearMap('all'); core.clearMap('curtain'); // 清空全地图 core.drawText([ "\t[恭喜通关]你的分数是${status:hp}。" ], function () { diff --git a/docs/personalization.md b/docs/personalization.md index e8b6f810..30b65102 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -17,10 +17,10 @@ HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们 - fg:前景层;绘制前景图层素材fgmap,和前景贴图 - damage:显伤层;主要用来绘制怪物显伤和领域显伤 - animate:动画层;主要用来绘制动画。showImage事件绘制的图片也是在这一层。 -- image:图片层;主要用来绘制显示图片 - weather:天气层;主要用来绘制天气(雨/雪) - route:路线层;主要用来绘制勇士的行走路线图,也用来绘制图块的淡入/淡出效果,图块的移动等。 - curtain:色调层;用来控制当前楼层的画面色调 +- image:图片层;主要用来绘制显示图片;该层之所以在curtain层上是为了可以在全黑时贴大头像图 - ui:UI层;用来绘制一切UI窗口,如剧情文本、怪物手册、楼传器、系统菜单等等 - data:数据层;用来绘制一些顶层的或更新比较快的数据,如左上角的提示,战斗界面中数据的变化等等。 @@ -560,6 +560,23 @@ this.myfunc = function(x) { 通过这种,将脚本和自定义事件混用的方式,可以达到和RM中公共事件类似的效果,即一个调用触发一系列事件。 --> + +## 标题界面事件化 + +从V2.5.3开始,我们可以将标题界面的绘制和游戏开始用事件来完成。可以通过绘制画布、 + +全塔属性,flags中的startUsingCanvas可以决定是否开启标题界面事件化。 + +然后就可以使用“事件流”的形式来绘制标题界面、提供选项等等。 + +在这里可以调用任意事件。例如,可以贴若干个图,可以事件切换楼层到某个剧情层再执行若干事件,等等。 + +关于选项,样板默认给出的是最简单的choices事件;你也可以使用贴按钮图,循环处理+等待操作来定制自己的按钮点击效果。 + +!> 开始游戏、读取存档、录像回放的效果已经默认给出,请不要修改或删减这些内容,以免出现问题。 + +标题界面事件全部处理完后,将再继续执行startText事件。 + ## 自定义状态栏(新增显示项) 在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground)等等。 diff --git a/libs/control.js b/libs/control.js index 7a282c86..893c7e08 100644 --- a/libs/control.js +++ b/libs/control.js @@ -221,6 +221,7 @@ control.prototype.showStartAnimate = function (noAnimate, callback) { core.status.played = false; core.clearStatus(); core.clearMap('all'); + core.clearMap('curtain'); if (core.flags.startUsingCanvas) { core.dom.startTop.style.display = 'none'; @@ -1033,7 +1034,7 @@ control.prototype.updateViewport = function() { ////// 绘制勇士 ////// control.prototype.drawHero = function (direction, x, y, status, offset) { - if (!core.isPlaying() || core.status.isStarting) return; + if (!core.isPlaying()) return; var scan = { 'up': {'x': 0, 'y': -1}, @@ -1510,16 +1511,13 @@ control.prototype.setFg = function(color, time, callback) { ////// 更新全地图显伤 ////// control.prototype.updateDamage = function (floorId, canvas) { - - if (!core.isset(floorId)) floorId = core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; if (!core.isset(canvas)) { canvas = core.canvas.damage; core.clearMap('damage'); } - // 正在开始游戏中 - if (core.status.isStarting) return; - // 更新显伤 var mapBlocks = core.status.maps[floorId].blocks; // 没有怪物手册 @@ -1673,7 +1671,6 @@ control.prototype.chooseReplayFile = function () { } if (core.flags.startUsingCanvas) { - core.status.isStarting = false; core.startGame('', obj.seed, core.decodeRoute(obj.route)); } else { diff --git a/libs/events.js b/libs/events.js index 8ffd23df..a2bc4e68 100644 --- a/libs/events.js +++ b/libs/events.js @@ -91,14 +91,12 @@ events.prototype.initGame = function () { ////// 游戏开始事件 ////// events.prototype.startGame = function (hard, seed, route, callback) { - if (core.status.isStarting) return; - core.status.isStarting = true; - var start = function () { console.log('开始游戏'); - core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, null, core.initStatus.maps); - - core.status.isStarting = true; + core.resetStatus(core.firstData.hero, hard, null, null, core.initStatus.maps); + var nowLoc = core.clone(core.getHeroLoc()); + core.setHeroLoc('x', -1); + core.setHeroLoc('y', -1); if (core.isset(seed)) { core.setFlag('__seed__', seed); @@ -106,19 +104,15 @@ events.prototype.startGame = function (hard, seed, route, callback) { } else core.utils.__init_seed(); - if (!core.flags.startUsingCanvas) - core.events.setInitData(hard); - core.clearMap('all'); + core.clearMap('curtain'); core.clearStatusBar(); var post_start = function () { - core.status.isStarting = false; - core.control.triggerStatusBar('show'); - core.changeFloor(core.status.floorId, null, core.status.hero.loc, null, function() { + core.changeFloor(core.firstData.floorId, null, nowLoc, null, function() { if (core.isset(callback)) callback(); }, true); @@ -164,6 +158,7 @@ events.prototype.startGame = function (hard, seed, route, callback) { }); } else { + core.events.setInitData(hard); real_start(); } @@ -1334,6 +1329,7 @@ events.prototype.trigger = function (x, y) { events.prototype.setFloorName = function (floorId) { floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; // 根据文字判断是否斜体 var floorName = core.status.maps[floorId].name || ""; if (typeof floorName == 'number') floorName = ""+floorName; @@ -1358,7 +1354,11 @@ events.prototype.setFloorName = function (floorId) { ////// 楼层切换 ////// events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback, fromLoad) { - if (!core.isset(floorId)) floorId = core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) { + if (core.isset(callback)) callback(); + return; + } if (floorId == ':before') { var index=core.floorIds.indexOf(core.status.floorId); diff --git a/libs/maps.js b/libs/maps.js index 229d6ebd..6caa54bd 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -211,6 +211,7 @@ maps.prototype.save = function(maps, floorId) { ////// 更改地图画布的尺寸 maps.prototype.resizeMap = function(floorId) { floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; core.bigmap.width = core.floors[floorId].width || 13; core.bigmap.height = core.floors[floorId].height || 13; var cwidth = core.bigmap.width * 32; @@ -229,7 +230,7 @@ maps.prototype.resizeMap = function(floorId) { ////// 将存档中的地图信息重新读取出来 ////// maps.prototype.load = function (data, floorId) { - if (floorId == undefined) { + if (!core.isset(floorId)) { var map = {}; core.floorIds.forEach(function (id) { map[id] = core.maps.loadFloor(id, data[id]); @@ -266,7 +267,8 @@ maps.prototype.canMoveHero = function(x,y,direction,floorId) { if (!core.isset(x)) x=core.getHeroLoc('x'); if (!core.isset(y)) y=core.getHeroLoc('y'); if (!core.isset(direction)) direction=core.getHeroLoc('direction'); - if (!core.isset(floorId)) floorId=core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return false; // 检查当前块的cannotMove if (core.isset(core.floors[floorId].cannotMove)) { @@ -394,6 +396,7 @@ maps.prototype.drawBlock = function (block, animate, dx, dy) { maps.prototype.getBgFgMapArray = function (floorId, name) { floorId = floorId||core.status.floorId; + if (!core.isset(floorId)) return []; var width = core.floors[floorId].width || 13; var height = core.floors[floorId].height || 13; @@ -417,6 +420,7 @@ maps.prototype.getBgFgMapArray = function (floorId, name) { ////// 背景/前景图块的绘制 ////// maps.prototype.drawBgFgMap = function (floorId, canvas, name) { floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; var width = core.floors[floorId].width || 13; var height = core.floors[floorId].height || 13; @@ -455,6 +459,11 @@ maps.prototype.drawBgFgMap = function (floorId, canvas, name) { ////// 绘制某张地图 ////// maps.prototype.drawMap = function (mapName, callback) { mapName = mapName || core.status.floorId; + if (!core.isset(mapName)) { + if (core.isset(callback)) + callback(); + return; + } core.clearMap('all'); core.removeGlobalAnimate(null, null, true); @@ -707,7 +716,8 @@ maps.prototype.enemyExists = function (x, y, id,floorId) { ////// 获得某个点的block ////// maps.prototype.getBlock = function (x, y, floorId, showDisable) { - if (!core.isset(floorId)) floorId=core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return null; var blocks = core.status.maps[floorId].blocks; for (var n=0;n=core.bigmap.width || y<0 || y>=core.bigmap.height) return; @@ -1187,6 +1205,7 @@ maps.prototype.setBlock = function (number, x, y, floorId) { ////// 改变图层块 ////// maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; if (!core.isset(number) || !core.isset(x) || !core.isset(y)) return; if (x<0 || x>=core.bigmap.width || y<0 || y>=core.bigmap.height) return; if (name!='bg' && name!='fg') return; @@ -1338,7 +1357,8 @@ maps.prototype.setFloorImage = function (type, loc, floorId, callback) { if (type!='show') type='hide'; if (typeof loc[0] == 'number' && typeof loc[1] == 'number') loc = [loc]; - floorId = floorId||core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; if (loc.length==0) return; loc.forEach(function (t) { @@ -1360,7 +1380,8 @@ maps.prototype.setBgFgMap = function (type, name, loc, floorId, callback) { if (name!='fg') name='bg'; if (typeof loc[0] == 'number' && typeof loc[1] == 'number') loc = [loc]; - floorId = floorId||core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; if (loc.length==0) return; loc.forEach(function (t) { @@ -1379,7 +1400,8 @@ maps.prototype.setBgFgMap = function (type, name, loc, floorId, callback) { } maps.prototype.resetMap = function(floorId) { - var floorId = floorId||core.status.floorId; + floorId = floorId || core.status.floorId; + if (!core.isset(floorId)) return; core.status.maps[floorId] = this.loadFloor(floorId); if (floorId==core.status.floorId) { this.drawMap(floorId, function () { diff --git a/project/data.js b/project/data.js index 28a1ff15..107c1140 100644 --- a/project/data.js +++ b/project/data.js @@ -104,6 +104,10 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "steps": 0 }, "startCanvas": [ + { + "type": "comment", + "text": "在这里可以用事件来自定义绘制标题界面的背景图等" + }, { "type": "showImage", "name": "bg.jpg", @@ -112,12 +116,24 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = 0 ] }, + { + "type": "comment", + "text": "给用户提供选择项,这里简单的使用了choices事件" + }, + { + "type": "comment", + "text": "也可以贴按钮图然后使用循环处理+等待操作来完成" + }, { "type": "choices", "choices": [ { "text": "开始游戏", "action": [ + { + "type": "comment", + "text": "检查bgm状态,下同" + }, { "type": "function", "function": "function(){\ncore.control.checkBgm()\n}" @@ -126,20 +142,32 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "type": "if", "condition": "core.flags.startDirectly", "true": [ + { + "type": "comment", + "text": "直接开始游戏,设置初始化数据" + }, { "type": "function", "function": "function(){\ncore.events.setInitData('')\n}" } ], "false": [ + { + "type": "comment", + "text": "动态生成难度选择项" + }, { "type": "function", - "function": "function(){\n// 动态生成难度选择项\nvar choices = [];\nmain.levelChoose.forEach(function (one) {\n\tchoices.push({\"text\": one[0], \"action\": [\n\t\t{\"type\": \"function\", \"function\": \"function() { core.status.hard = '\"+one[1]+\"'; core.events.setInitData('\"+one[1]+\"'); }\"}\n\t]});\n})\ncore.insertAction({\"type\": \"choices\", \"choices\": choices});\n}" + "function": "function(){\nvar choices = [];\nmain.levelChoose.forEach(function (one) {\n\tchoices.push({\"text\": one[0], \"action\": [\n\t\t{\"type\": \"function\", \"function\": \"function() { core.status.hard = '\"+one[1]+\"'; core.events.setInitData('\"+one[1]+\"'); }\"}\n\t]});\n})\ncore.insertAction({\"type\": \"choices\", \"choices\": choices});\n}" } ] }, { "type": "showImage" + }, + { + "type": "comment", + "text": "成功选择难度" } ] }, @@ -153,9 +181,17 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = { "type": "showImage" }, + { + "type": "comment", + "text": "这段代码会结束事件,打开读档页面,读取存档或重新开始" + }, { "type": "function", - "function": "function(){\ncore.insertAction([{\"type\": \"exit\"}], null, null, function() {\n\tcore.status.played = false;\n\tcore.status.isStarting = false;\n\tcore.load();\n})\n}" + "function": "function(){\ncore.insertAction([{\"type\": \"exit\"}], null, null, function() {\n\tcore.status.played = false;\n\tcore.load();\n})\n}" + }, + { + "type": "comment", + "text": "不管读档有没有成功,都会重新开始,这个地方不会被执行到" } ] }, @@ -166,13 +202,25 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "type": "function", "function": "function(){\ncore.control.checkBgm()\n}" }, + { + "type": "comment", + "text": "这段代码会结束事件,选择录像文件,播放录像或重新开始" + }, { "type": "function", "function": "function(){\ncore.insertAction([{\"type\": \"exit\"}], null, null, function() {\n\tcore.restart();\n\tcore.chooseReplayFile();\n})\n}" + }, + { + "type": "comment", + "text": "不管录像有没有成功,都会重新开始,这个地方不会被执行到" } ] } ] + }, + { + "type": "comment", + "text": "接下来会执行startText中的事件" } ], "startText": [ @@ -320,7 +368,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "hatredDecrease": true, "betweenAttackCeil": false, "useLoop": false, - "startUsingCanvas": true, + "startUsingCanvas": false, "startDirectly": false, "canOpenBattleAnimate": true, "showBattleAnimateConfirm": false, diff --git a/project/functions.js b/project/functions.js index c6ee7084..822856b8 100644 --- a/project/functions.js +++ b/project/functions.js @@ -59,7 +59,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.stopReplay(); core.waitHeroToStop(function() { core.removeGlobalAnimate(0,0,true); - core.clearMap('all'); // 清空全地图 + core.clearMap('all'); core.clearMap('curtain'); // 清空全地图 // 请注意: // 成绩统计时是按照hp进行上传并排名,因此光在这里改${status:hp}是无效的 // 如需按照其他的的分数统计方式,请先将hp设置为你的得分 From e3969012c62ff0d9166c5a3eb90b907080de0c52 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 21:19:08 +0800 Subject: [PATCH 03/16] Fix \b Position Bug --- libs/ui.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/libs/ui.js b/libs/ui.js index 948e9912..40855e7e 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -519,9 +519,13 @@ ui.prototype.drawTextBox = function(content, showAll) { width = validWidth + leftSpace + rightSpace; // left必须在7~416-7-width区间内,以保证left>=7,right<=416-7 left = core.clamp(32*px+16-width/2, 7, 416-7-width); + + left -= core.bigmap.offsetX; + right = left + width; } + var content_left = left + leftSpace; var height = 30 + (textfont+5)*core.splitLines("ui", realContent, validWidth, font).length; if (core.isset(name)) height += titlefont + 5; @@ -541,20 +545,24 @@ ui.prototype.drawTextBox = function(content, showAll) { else if (position=='up') { if (px==null || py==null) top = 5 + offset; - else + else { top = 32 * py - height - ydelta - yoffset; + top -= core.bigmap.offsetY; + } } else if (position=='down') { if (px==null || py==null) top = 416 - height - 5 - offset; - else + else { top = 32 * py + 32 + yoffset; + top -= core.bigmap.offsetY; + } } var bottom = top + height; if (isWindowSkin) { core.setAlpha('ui', alpha); - this.drawWindowSkin(background,'ui',left,top,width,height,position,px==null?null:px*32,py==null?null:py*32); + this.drawWindowSkin(background,'ui',left,top,width,height,position,px==null?null:px*32-core.bigmap.offsetX,py==null?null:py*32-core.bigmap.offsetY); core.setAlpha('ui', 1); } else { @@ -569,17 +577,17 @@ ui.prototype.drawTextBox = function(content, showAll) { canvas.moveTo(left,top); // 上边缘 if (position=='down' && core.isset(px) && core.isset(py)) { - canvas.lineTo(32*px+xoffset, top); - canvas.lineTo(32*px+16, top-yoffset); - canvas.lineTo(32*(px+1)-xoffset, top); + canvas.lineTo(32*px+xoffset - core.bigmap.offsetX, top); + canvas.lineTo(32*px+16 - core.bigmap.offsetX, top-yoffset); + canvas.lineTo(32*(px+1)-xoffset - core.bigmap.offsetX, top); } canvas.lineTo(right, top); canvas.lineTo(right, bottom); // 下边缘 if (position=='up' && core.isset(px) && core.isset(py)) { - canvas.lineTo(32*(px+1)-xoffset, bottom); - canvas.lineTo(32*px+16, bottom+yoffset); - canvas.lineTo(32*px+xoffset, bottom); + canvas.lineTo(32*(px+1)-xoffset - core.bigmap.offsetX, bottom); + canvas.lineTo(32*px+16 - core.bigmap.offsetX, bottom+yoffset); + canvas.lineTo(32*px+xoffset - core.bigmap.offsetX, bottom); } canvas.lineTo(left, bottom); canvas.closePath(); From a830e1461413bd9a2fcdf19ff3efe6a99c506b00 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 21:47:03 +0800 Subject: [PATCH 04/16] waitAsync --- _server/blockly/MotaAction.g4 | 36 +++++++++++++++++++++++++++-------- _server/editor_blockly.js | 3 ++- docs/event.md | 17 +++++++++++++++-- libs/control.js | 12 +++++++++--- libs/core.js | 3 ++- libs/events.js | 26 ++++++++++++++++++++++--- libs/maps.js | 7 +++++++ 7 files changed, 86 insertions(+), 18 deletions(-) diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 6fecc669..8eb700d1 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -235,6 +235,7 @@ action | updateEnemys_s | sleep_s | wait_s + | waitAsync_s | battle_s | openDoor_s | changeFloor_s @@ -245,7 +246,7 @@ action | follow_s | unfollow_s | animate_s - | viberate_s + | vibrate_s | showImage_0_s | showImage_1_s | animateImage_0_s @@ -819,6 +820,7 @@ var code = '{"type": "sleep", "time": '+Int_0+'},\n'; return code; */; + battle_s : '强制战斗' IdString Newline @@ -949,18 +951,18 @@ var code = '{"type": "unfollow"' + EvalString_0 + '},\n'; return code; */; -viberate_s +vibrate_s : '画面震动' '时间' Int '不等待执行完毕' Bool Newline -/* viberate_s -tooltip : viberate: 画面震动 -helpUrl : https://h5mota.com/games/template/docs/#/event?id=viberate%ef%bc%9a%e7%94%bb%e9%9d%a2%e9%9c%87%e5%8a%a8 +/* vibrate_s +tooltip : vibrate: 画面震动 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=vibrate%ef%bc%9a%e7%94%bb%e9%9d%a2%e9%9c%87%e5%8a%a8 default : [2000,false] colour : this.soundColor Int_0 = Int_0 ?(', "time": '+Int_0):''; var async = Bool_0?', "async": true':'' -var code = '{"type": "viberate"' + Int_0 + async + '},\n'; +var code = '{"type": "vibrate"' + Int_0 + async + '},\n'; return code; */; @@ -1459,6 +1461,20 @@ var code = '{"type": "wait"},\n'; return code; */; + +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 +colour : this.soundColor +var code = '{"type": "waitAsync"},\n'; +return code; +*/; + + function_s : '自定义JS脚本' BGNL? Newline RawEvalString Newline BEND Newline @@ -2023,8 +2039,8 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['animate_s'].xmlText([ data.name,animate_loc,data.async||false,this.next]); break; - case "viberate": // 画面震动 - this.next = MotaActionBlocks['viberate_s'].xmlText([data.time||0, data.async||false, this.next]); + case "vibrate": // 画面震动 + this.next = MotaActionBlocks['vibrate_s'].xmlText([data.time||0, data.async||false, this.next]); break; case "showImage": // 显示图片 if(this.isset(data.name)){ @@ -2221,6 +2237,10 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['wait_s'].xmlText([ this.next]); break; + case "waitAsync": // 等待所有异步事件执行完毕 + this.next = MotaActionBlocks['waitAsync_s'].xmlText([ + this.next]); + break; case "revisit": // 立刻重新执行该事件 this.next = MotaActionBlocks['revisit_s'].xmlText([ this.next]); diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index f36cb09e..2ea9e2bb 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -131,7 +131,8 @@ editor_blockly = function () { '特效/声音':[ MotaActionBlocks['sleep_s'].xmlText(), MotaActionBlocks['wait_s'].xmlText(), - MotaActionBlocks['viberate_s'].xmlText(), + MotaActionBlocks['waitAsync_s'].xmlText(), + MotaActionBlocks['vibrate_s'].xmlText(), MotaActionBlocks['animate_s'].xmlText(), MotaActionBlocks['showStatusBar_s'].xmlText(), MotaActionBlocks['hideStatusBar_s'].xmlText(), diff --git a/docs/event.md b/docs/event.md index 041d9547..3b5cba46 100644 --- a/docs/event.md +++ b/docs/event.md @@ -863,9 +863,9 @@ name为可选的,是要取消跟随的行走图文件名。 如果name省略,则会取消所有的跟随效果。 -### viberate:画面震动 +### vibrate:画面震动 -使用 `{"type": "viberate", "time": 2000, "async": true}` 可以造成画面震动效果。 +使用 `{"type": "vibrate", "time": 2000, "async": true}` 可以造成画面震动效果。 time可以指定震动时间,默认是2000毫秒。 @@ -1548,6 +1548,19 @@ choices为一个数组,其中每一项都是一个选项列表。 ``` +### waitAsync:等待所有异步事件执行完毕 + +上面有很多很多的异步事件(也就执行时不等待执行完毕)。 + +由于录像是加速播放,且会跳过`{"type":"sleep"}`(等待X毫秒)事件;因此异步行为很有可能导致录像播放出错。 + +例如,异步移动一个NPC去某格,然后等待X毫秒,再勇士走过去对话; +但是录像播放中,等待X毫秒的行为会被跳过,因此勇士可能走过去时异步还未执行完成,导致录像出错。 + +我们可以使用`{"type":"waitAsync"}`来等待所有异步事件执行完毕。 + +该事件会进行等待,直到所有可能的异步事件(异步动画除外)执行完毕。 + ### function: 自定义JS脚本 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 diff --git a/libs/control.js b/libs/control.js index c1f03d4b..aca04d4d 100644 --- a/libs/control.js +++ b/libs/control.js @@ -867,6 +867,7 @@ control.prototype.eventMoveHero = function(steps, time, callback) { var animate=window.setInterval(function() { var x=core.getHeroLoc('x'), y=core.getHeroLoc('y'); if (moveSteps.length==0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); core.drawHero(null, x, y); if (core.isset(callback)) callback(); @@ -889,7 +890,9 @@ control.prototype.eventMoveHero = function(steps, time, callback) { moveSteps.shift(); } } - }, time / 8 / core.status.replay.speed) + }, time / 8 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; } ////// 勇士跳跃事件 ////// @@ -944,6 +947,7 @@ control.prototype.jumpHero = function (ex, ey, time, callback) { nowx - core.bigmap.offsetX, nowy + 32-height - core.bigmap.offsetY, 32, height); } else { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); core.setHeroLoc('x', ex); core.setHeroLoc('y', ey); @@ -953,8 +957,7 @@ control.prototype.jumpHero = function (ex, ey, time, callback) { }, time / 16 / core.status.replay.speed); - - + core.animateFrame.asyncId[animate] = true; } ////// 每移动一格后执行的事件 ////// @@ -1492,12 +1495,15 @@ control.prototype.setFg = function(color, time, callback) { core.fillRect('curtain', 0, 0, 416, 416, core.arrayToRGBA([nowR,nowG,nowB,nowA])); if (step>=25) { + delete core.animateFrame.asyncId[changeAnimate]; clearInterval(changeAnimate); core.status.curtainColor = color; // core.status.replay.animate=false; if (core.isset(callback)) callback(); } }, time/25/core.status.replay.speed); + + core.animateFrame.asyncId[changeAnimate] = true; } ////// 更新全地图显伤 ////// diff --git a/libs/core.js b/libs/core.js index f14206fe..4e87bdd9 100644 --- a/libs/core.js +++ b/libs/core.js @@ -41,7 +41,8 @@ function core() { 'level': 0, 'nodes': [], 'data': null, - } + }, + "asyncId": {} } this.musicStatus = { 'audioContext': null, // WebAudioContext diff --git a/libs/events.js b/libs/events.js index a02099bd..091ab206 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1059,7 +1059,7 @@ events.prototype.doAction = function() { core.updateStatusBar(); this.doAction(); break; - case "viberate": + case "vibrate": if (data.async) { core.events.vibrate(data.time); this.doAction(); @@ -1102,6 +1102,16 @@ events.prototype.doAction = function() { } } break; + case "waitAsync": // 等待所有异步事件执行完毕 + { + var test = window.setInterval(function () { + if (Object.keys(core.animateFrame.asyncId)==0) { + clearInterval(test); + core.events.doAction(); + } + }, 50); + break; + } case "revisit": // 立刻重新执行该事件 { var block=core.getBlock(x,y); // 重新获得事件 @@ -1513,6 +1523,7 @@ events.prototype.animateImage = function (type, image, loc, time, keep, callback else opacityVal -= 0.1; core.setOpacity('data', opacityVal); if (opacityVal >=1 || opacityVal<=0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); if (type == 'show' && keep) core.canvas.image.drawImage(image, x, y); @@ -1522,6 +1533,8 @@ events.prototype.animateImage = function (type, image, loc, time, keep, callback if (core.isset(callback)) callback(); } }, time / 10); + + core.animateFrame.asyncId[animate] = true; } ////// 移动图片 ////// @@ -1554,13 +1567,14 @@ events.prototype.moveImage = function (image, from, to, time, keep, callback) { if (step <= steps) drawImage(); else { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); - // score.clearMap('data'); - // core.status.replay.animate=false; if (keep) core.canvas.image.drawImage(image, toX, toY); if (core.isset(callback)) callback(); } }, per_time); + + core.animateFrame.asyncId[animate] = true; } ////// 淡入淡出音乐 ////// @@ -1587,12 +1601,15 @@ events.prototype.setVolume = function (value, time, callback) { var nowVolume = currVolume+(value-currVolume)*step/32; set(nowVolume); if (step>=32) { + delete core.animateFrame.asyncId[fade]; clearInterval(fade); // core.status.replay.animate=false; if (core.isset(callback)) callback(); } }, time / 32); + + core.animateFrame.asyncId[fade] = true; } ////// 画面震动 ////// @@ -1650,11 +1667,14 @@ events.prototype.vibrate = function(time, callback) { update(); addGameCanvasTranslate(shake, 0); if(shake_duration===0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); // core.status.replay.animate=false; if (core.isset(callback)) callback(); } }, 50/3); + + core.animateFrame.asyncId[animate] = true; } ////// 打开一个全局商店 ////// diff --git a/libs/maps.js b/libs/maps.js index 229d6ebd..ac08d2dd 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -831,6 +831,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { else alpha -= 0.06; core.clearMap('route', nowX, nowY-height+32, 32, height); if (alpha<=0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); // 不消失 if (keep) { @@ -872,6 +873,9 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { } } }, time / 16 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; + } ////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// @@ -970,6 +974,7 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { else alpha -= 0.06; core.clearMap('route', drawX(), drawY()-height+32, 32, height); if (alpha<=0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); core.clearMap('route'); core.setOpacity('route', 1); @@ -987,6 +992,8 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { } }, time / 16 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; } ////// 显示/隐藏某个块时的动画效果 ////// From 09d95cc7776cfe537365ce7e3511f453d1311b82 Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 22:05:24 +0800 Subject: [PATCH 05/16] Merge Settings --- docs/event.md | 2 +- libs/actions.js | 107 +++++++++++++++++++++++++++++++++++++++--------- libs/ui.js | 9 +++- 3 files changed, 96 insertions(+), 22 deletions(-) diff --git a/docs/event.md b/docs/event.md index 3b5cba46..b17c45e8 100644 --- a/docs/event.md +++ b/docs/event.md @@ -1550,7 +1550,7 @@ choices为一个数组,其中每一项都是一个选项列表。 ### waitAsync:等待所有异步事件执行完毕 -上面有很多很多的异步事件(也就执行时不等待执行完毕)。 +上面有很多很多的异步事件(也就是执行时不等待执行完毕)。 由于录像是加速播放,且会跳过`{"type":"sleep"}`(等待X毫秒)事件;因此异步行为很有可能导致录像播放出错。 diff --git a/libs/actions.js b/libs/actions.js index 6439e095..d4d21634 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -166,6 +166,9 @@ actions.prototype.keyDown = function(keyCode) { if (core.status.event.id=='replay') { this.keyDownReplay(keyCode); } + if (core.status.event.id=='gameInfo') { + this.keyDownGameInfo(keyCode); + } return; } if(!core.status.played) { @@ -280,6 +283,10 @@ actions.prototype.keyUp = function(keyCode, altKey) { this.keyUpReplay(keyCode); return; } + if (core.status.event.id=='gameInfo') { + this.keyUpGameInfo(keyCode); + return; + } if (core.status.event.id=='centerFly') { this.keyUpCenterFly(keyCode); return; @@ -590,6 +597,10 @@ actions.prototype.onclick = function (x, y, stepPostfix) { return; } + if (core.status.event.id=='gameInfo') { + this.clickGameInfo(x,y); + } + } ////// 滑动鼠标滚轮时的操作 ////// @@ -1903,25 +1914,10 @@ actions.prototype.clickSettings = function (x,y) { core.ui.drawSyncSave(); break; case 5: - core.ui.drawStatistics(); + core.status.event.selection=0; + core.ui.drawGameInfo(); break; case 6: - if (core.platform.isPC) { - window.open("/score.php?name="+core.firstData.name+"&num=10", "_blank"); - } - else { - if (confirm("即将离开本塔,跳转至本塔评论页面,确认?")) { - window.location.href = "/score.php?name="+core.firstData.name+"&num=10"; - } - } - break; - case 7: - core.ui.drawHelp(); - break; - case 8: - core.ui.drawAbout(); - break; - case 9: core.status.event.selection=1; core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { core.ui.closePanel(); @@ -1931,7 +1927,7 @@ actions.prototype.clickSettings = function (x,y) { core.ui.drawSettings(); }); break; - case 10: + case 7: core.ui.closePanel(); break; } @@ -2057,7 +2053,7 @@ actions.prototype.clickSyncSave = function (x,y) { core.ui.drawStorageRemove(); break; case 7: - core.status.event.selection=3; + core.status.event.selection=4; core.ui.drawSettings(); break; @@ -2291,7 +2287,7 @@ actions.prototype.clickStorageRemove = function (x, y) { } break; case 2: - core.status.event.selection=5; + core.status.event.selection=6; core.ui.drawSyncSave(); break; } @@ -2414,6 +2410,77 @@ actions.prototype.keyUpReplay = function (keycode) { } } + +////// 游戏信息界面时的点击操作 ////// +actions.prototype.clickGameInfo = function (x, y) { + if (x<5 || x>7) return; + var choices = core.status.event.ui.choices; + + var topIndex = 6 - parseInt((choices.length - 1) / 2); + + if (y>=topIndex && y=49 && keycode<=57) { + var index = keycode-49; + if (index=1 && x<=11) { diff --git a/libs/ui.js b/libs/ui.js index 40855e7e..ac5e4c93 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -919,7 +919,7 @@ ui.prototype.drawSettings = function () { core.status.event.id = 'settings'; this.drawChoices(null, [ - "系统设置", "虚拟键盘", "浏览地图", "绘图模式", "同步存档", "数据统计", "查看评论", "操作帮助", "关于本塔", "返回标题", "返回游戏" + "系统设置", "虚拟键盘", "浏览地图", "绘图模式", "同步存档", "游戏信息", "返回标题", "返回游戏" ]); } @@ -1315,6 +1315,13 @@ ui.prototype.drawReplay = function () { ]); } +ui.prototype.drawGameInfo = function () { + core.status.event.id = 'gameInfo'; + this.drawChoices(null, [ + "数据统计", "查看评论", "操作帮助", "关于本塔", "返回上级菜单" + ]); +} + ////// 绘制分页 ////// ui.prototype.drawPagination = function (page, totalPage, top) { // if (totalPage Date: Sun, 2 Dec 2018 22:15:22 +0800 Subject: [PATCH 06/16] eachArrive --- _server/blockly/MotaAction.g4 | 12 ++++++++++++ _server/comment.js | 6 ++++++ _server/editor.js | 1 + _server/editor_blockly.js | 1 + _server/editor_file.js | 1 + docs/event.md | 4 +--- editor-mobile.html | 1 + editor.html | 1 + project/functions.js | 6 +++++- 9 files changed, 29 insertions(+), 4 deletions(-) diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 8eb700d1..5e58167f 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -183,6 +183,18 @@ var code = '[\n'+action_0+']\n'; return code; */; +//eachArrive 事件编辑器入口之一 +eachArrive_m + : '每次到达楼层' BGNL? Newline action+ BEND + + +/* 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 +var code = '[\n'+action_0+']\n'; +return code; +*/; + //changeFloor 事件编辑器入口之一 changeFloor_m : '楼梯, 传送门' BGNL? Newline Floor_List IdString? Stair_List 'x' Number ',' 'y' Number '朝向' DirectionEx_List '动画时间' Int? '允许穿透' Bool BEND diff --git a/_server/comment.js b/_server/comment.js index 2cdc9364..2b7fcc33 100644 --- a/_server/comment.js +++ b/_server/comment.js @@ -330,6 +330,12 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_event": "firstArrive", "_data": "第一次到该楼层触发的事件,可以双击进入事件编辑器。" }, + "eachArrive": { + "_leaf": true, + "_type": "event", + "_event": "eachArrive", + "_data": "每次到该楼层触发的事件,可以双击进入事件编辑器;该事件会比firstArrive先执行。" + }, "parallelDo": { "_leaf": true, "_type": "textarea", diff --git a/_server/editor.js b/_server/editor.js index 7b418d1d..9bd90184 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -183,6 +183,7 @@ editor.prototype.mapInit = function () { editor.currentFloorData.fgmap = editor.fgmap; editor.currentFloorData.bgmap = editor.bgmap; editor.currentFloorData.firstArrive = []; + editor.currentFloorData.eachArrive = []; editor.currentFloorData.events = {}; editor.currentFloorData.changeFloor = {}; editor.currentFloorData.afterBattle = {}; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 2ea9e2bb..2d81a4cc 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -52,6 +52,7 @@ editor_blockly = function () { MotaActionBlocks['afterGetItem_m'].xmlText(), MotaActionBlocks['afterOpenDoor_m'].xmlText(), MotaActionBlocks['firstArrive_m'].xmlText(), + MotaActionBlocks['eachArrive_m'].xmlText(), MotaActionBlocks['level_m'].xmlText(), ], '显示文字':[ diff --git a/_server/editor_file.js b/_server/editor_file.js index 68d9c8ec..3e938917 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -156,6 +156,7 @@ editor_file = function (editor, callback) { color: saveStatus?currData.color:null, weather: saveStatus?currData.weather:null, firstArrive: [], + eachArrive: [], parallelDo: "", events: {}, changeFloor: {}, diff --git a/docs/event.md b/docs/event.md index b17c45e8..686891a6 100644 --- a/docs/event.md +++ b/docs/event.md @@ -228,7 +228,7 @@ 除此以外,我们还能实现“对话框效果”,只要有`\b[...]`就可以。 - `\b[up]` 直接显示在当前点上方。同样把这里的up换成down则为下方。 - - 如果不存在当前点(如在firstArrive中调用),则显示在屏幕最上方(最下方) + - 如果不存在当前点(如在firstArrive或eachArrive中调用),则显示在屏幕最上方(最下方) - `\b[up,hero]` 显示在勇士上方。同样把这里的up换成down则为下方。 - `\b[up,x,y]` 显示在(x,y)点的上方(下方);x和y都为整数且在0到12之间。 @@ -807,8 +807,6 @@ time为可选的,指定的话将作为楼层切换动画的时间。 **如果time指定为小于100,则视为没有楼层切换动画。** -!> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。** - ### changePos:当前位置切换/勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 diff --git a/editor-mobile.html b/editor-mobile.html index 9141fe2d..04682f39 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -174,6 +174,7 @@ + diff --git a/editor.html b/editor.html index 41e3e25c..d85a793c 100644 --- a/editor.html +++ b/editor.html @@ -173,6 +173,7 @@ + diff --git a/project/functions.js b/project/functions.js index c6ee7084..350d5a04 100644 --- a/project/functions.js +++ b/project/functions.js @@ -1,4 +1,4 @@ -var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = +var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { "events": { "initGame": function() { @@ -93,6 +93,10 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = visited.push(floorId); core.setFlag("__visited__", visited); } + // 每次抵达楼层时执行的事件 + if (!fromLoad) { + core.insertAction(core.floors[floorId].eachArrive); + } }, "addPoint": function (enemy) { // 加点事件 From f709a61013a7200896b083e88d5c88d4f95c501f Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 22:17:26 +0800 Subject: [PATCH 07/16] eachArrive --- editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor.html b/editor.html index d85a793c..58fe020e 100644 --- a/editor.html +++ b/editor.html @@ -173,7 +173,7 @@ - + From dff67938292ff81276a89333b539b9791fe3d77c Mon Sep 17 00:00:00 2001 From: oc Date: Sun, 2 Dec 2018 23:10:06 +0800 Subject: [PATCH 08/16] Enable Animate block & autotile in Bg/Fg --- _server/editor.js | 1 + docs/personalization.md | 2 +- libs/control.js | 16 ++++++++++++-- libs/core.js | 2 +- libs/maps.js | 47 +++++++++++++++++++++++++++++------------ 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/_server/editor.js b/_server/editor.js index 9bd90184..3da2558c 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -11,6 +11,7 @@ function editor() { if (string.indexOf(substring) > -1){ message = 'Script Error: See Browser Console for Detail'; } else { + if (url) url = url.substring(url.lastIndexOf('/')+1); message = [ 'Message: ' + msg, 'URL: ' + url, diff --git a/docs/personalization.md b/docs/personalization.md index e8b6f810..ded9701f 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -51,7 +51,7 @@ HTML5魔塔是使用画布(canvas)来绘制,存在若干个图层,它们 在地图编辑器中绘图时,下拉框选中“背景层”或“前景层”即可在对应的图层上绘图。 -其中背景层和前景层仅为静态绘图(不支持动画),但可以使用自动元件(autotile)。 +其中背景层和前景层可以使用任何素材,以及使用自动元件(autotile)。 可以使用`showBgFgMap`, `hideBgFgMap`, `setBgFgBlock`等事件对背景和前景图层进行操作。 diff --git a/libs/control.js b/libs/control.js index aca04d4d..f8a49d01 100644 --- a/libs/control.js +++ b/libs/control.js @@ -70,7 +70,7 @@ control.prototype.setRequestAnimationFrame = function () { } // Global Animate - if (core.animateFrame.globalAnimate && core.isPlaying()) { + if (core.animateFrame.globalAnimate && core.isPlaying() && core.isset(core.status.floorId)) { if (timestamp-core.animateFrame.globalTime>core.animateFrame.speed && core.isset(core.status.globalAnimateObjs)) { @@ -81,9 +81,21 @@ control.prototype.setRequestAnimationFrame = function () { } if ((core.status.autotileAnimateObjs.blocks||[]).length>0) { + var groundId = (core.status.maps||core.floors)[core.status.floorId].defaultGround || "ground"; + var blockIcon = core.material.icons.terrains[groundId]; core.status.autotileAnimateObjs.status++; core.status.autotileAnimateObjs.blocks.forEach(function (block) { - core.drawAutotile(core.canvas.event, core.status.autotileAnimateObjs.map, block, 32, 0, 0, core.status.autotileAnimateObjs.status); + var cv = core.isset(block.name)?core.canvas[block.name]:core.canvas.event; + cv.clearRect(block.x * 32, block.y * 32, 32, 32); + if (core.isset(block.name)) { + if (block.name == 'bg') { + core.canvas.bg.drawImage(core.material.images.terrains, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + } + core.drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, core.status.autotileAnimateObjs.status); + } + else { + core.drawAutotile(cv, core.status.autotileAnimateObjs.map, block, 32, 0, 0, core.status.autotileAnimateObjs.status); + } }) } diff --git a/libs/core.js b/libs/core.js index 4e87bdd9..41153ef0 100644 --- a/libs/core.js +++ b/libs/core.js @@ -177,7 +177,7 @@ function core() { // 动画 'globalAnimateObjs': [], 'boxAnimateObjs': [], - 'autotileAnimateObjs': {}, + 'autotileAnimateObjs': {"status": 0, "blocks": [], "map": null, "bgmap": null, "fgmap": null}, 'animateObjs': [], }; this.status = {}; diff --git a/libs/maps.js b/libs/maps.js index ac08d2dd..dca3c6b4 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -384,6 +384,17 @@ maps.prototype.drawBlock = function (block, animate, dx, dy) { dx = dx || 0; dy = dy || 0; + if (core.isset(block.name)) { + core.canvas[block.name].clearRect(block.x * 32, block.y * 32, 32, 32); + if (block.name == 'bg') { + var groundId = (core.status.maps||core.floors)[core.status.floorId].defaultGround || "ground"; + var blockIcon = core.material.icons.terrains[groundId]; + core.canvas.bg.drawImage(core.material.images.terrains, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + } + core.canvas[block.name].drawImage(image, x * 32, y * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + return; + } + core.canvas.event.clearRect(block.x * 32 + dx, block.y * 32 + dy, 32, 32); core.canvas.event.drawImage(image, x * 32, y * height + height-32, 32, 32, block.x * 32 + dx, block.y * 32 + dy, 32, 32); if (height>32) { @@ -415,7 +426,7 @@ maps.prototype.getBgFgMapArray = function (floorId, name) { } ////// 背景/前景图块的绘制 ////// -maps.prototype.drawBgFgMap = function (floorId, canvas, name) { +maps.prototype.drawBgFgMap = function (floorId, canvas, name, animate) { floorId = floorId || core.status.floorId; var width = core.floors[floorId].width || 13; var height = core.floors[floorId].height || 13; @@ -428,10 +439,15 @@ maps.prototype.drawBgFgMap = function (floorId, canvas, name) { for (var y = 0; y < height; y++) { if (arr[y][x]>0) { var block = core.maps.initBlock(x, y, arr[y][x]); + this.addInfo(block); + block.name = name; if (core.isset(block.event)) { var id = block.event.id, cls = block.event.cls; - if (cls == 'autotile') + if (cls == 'autotile') { core.drawAutotile(canvas, arr, block, 32, 0, 0); + if (animate) + core.status.autotileAnimateObjs.blocks.push(core.clone(block)); + } else if (cls == 'tileset') { var offset = core.icons.getTilesetOffset(id); if (offset!=null) { @@ -443,13 +459,20 @@ maps.prototype.drawBgFgMap = function (floorId, canvas, name) { canvas.drawImage(core.material.images.airwall, 32*x, 32*y); } } - else - canvas.drawImage(core.material.images[cls], 0, core.material.icons[cls][id] * 32, 32, 32, x * 32, y * 32, 32, 32); + else { + if (animate) { + this.drawBlock(block); + this.addGlobalAnimate(block); + } + else { + canvas.drawImage(core.material.images[cls], 0, core.material.icons[cls][id] * 32, 32, 32, x * 32, y * 32, 32, 32); + } + } } } } } - + core.status.autotileAnimateObjs[name+"map"] = core.clone(arr); } ////// 绘制某张地图 ////// @@ -511,8 +534,8 @@ maps.prototype.drawMap = function (mapName, callback) { } }); - core.maps.drawBgFgMap(mapName, core.canvas.bg, "bg"); - core.maps.drawBgFgMap(mapName, core.canvas.fg, "fg"); + core.maps.drawBgFgMap(mapName, core.canvas.bg, "bg", true); + core.maps.drawBgFgMap(mapName, core.canvas.fg, "fg", true); } if (main.mode=='editor'){ @@ -531,7 +554,6 @@ maps.prototype.drawMap = function (mapName, callback) { core.status.floorId = mapName; core.status.thisMap = core.status.maps[mapName]; var drawEvent = function(){ - core.status.autotileAnimateObjs = {"status": 0, "blocks": [], "map": null}; var mapData = core.status.maps[core.status.floorId]; var mapBlocks = mapData.blocks; @@ -1214,24 +1236,23 @@ maps.prototype.addGlobalAnimate = function (b) { block.status = 0; core.status.globalAnimateObjs.push(block); - } ////// 删除一个或所有全局动画 ////// -maps.prototype.removeGlobalAnimate = function (x, y, all) { +maps.prototype.removeGlobalAnimate = function (x, y, all, name) { if (main.mode=='editor' && main.editor.disableGlobalAnimate) return; if (all) { core.status.globalAnimateObjs = []; - core.status.autotileAnimateObjs = {}; + core.status.autotileAnimateObjs = {"status": 0, "blocks": [], "map": null, "bgmap": null, "fgmap": null}; return; } - core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) {return block.x!=x || block.y!=y;}); + core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) {return block.x!=x || block.y!=y || block.name!=name;}); // 检查Autotile if (core.isset(core.status.autotileAnimateObjs.blocks)) { - core.status.autotileAnimateObjs.blocks = core.status.autotileAnimateObjs.blocks.filter(function (block) {return block.x!=x || block.y!=y;}); + core.status.autotileAnimateObjs.blocks = core.status.autotileAnimateObjs.blocks.filter(function (block) {return block.x!=x || block.y!=y || block.name!=name;}); core.status.autotileAnimateObjs.map[y][x] = 0; } From 2a0568ae6cefd1ba8158e57ebc553084e8b15da2 Mon Sep 17 00:00:00 2001 From: ckcz123 Date: Mon, 3 Dec 2018 14:22:40 +0800 Subject: [PATCH 09/16] moveSpeed --- _server/blockly/MotaAction.g4 | 4 ++-- _server/data.comment.js | 5 ++--- docs/event.md | 2 +- libs/control.js | 6 +++--- libs/events.js | 7 +------ project/data.js | 2 +- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 5e58167f..9d663fff 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -1672,8 +1672,8 @@ Global_Attribute_List /*Global_Attribute_List ['font','statusLeftBackground','statusTopBackground', 'toolsBackground', 'borderColor', 'statusBarColor', 'hardLabelColor', 'floorChangingBackground', 'floorChangingTextColor']*/; Global_Value_List - : '血网伤害'|'中毒伤害'|'衰弱效果'|'红宝石效果'|'蓝宝石效果'|'绿宝石效果'|'红血瓶效果'|'蓝血瓶效果'|'黄血瓶效果'|'绿血瓶效果'|'破甲比例'|'反击比例'|'净化比例'|'仇恨增加值'|'最大合法HP'|'动画时间' - /*Global_Value_List ['lavaDamage','poisonDamage','weakValue', 'redJewel', 'blueJewel', 'greenJewel', 'redPotion', 'bluePotion', 'yellowPotion', 'greenPotion', 'breakArmor', 'counterAttack', 'purify', 'hatred', 'maxValidHp', 'animateSpeed']*/; + : '血网伤害'|'中毒伤害'|'衰弱效果'|'红宝石效果'|'蓝宝石效果'|'绿宝石效果'|'红血瓶效果'|'蓝血瓶效果'|'黄血瓶效果'|'绿血瓶效果'|'破甲比例'|'反击比例'|'净化比例'|'仇恨增加值'|'行走速度'|'动画时间' + /*Global_Value_List ['lavaDamage','poisonDamage','weakValue', 'redJewel', 'blueJewel', 'greenJewel', 'redPotion', 'bluePotion', 'yellowPotion', 'greenPotion', 'breakArmor', 'counterAttack', 'purify', 'hatred', 'moveSpeed', 'animateSpeed']*/; Bool: 'TRUE' | 'FALSE' diff --git a/_server/data.comment.js b/_server/data.comment.js index c3262c4d..300a5fc0 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -380,11 +380,10 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "textarea", "_data": "仇恨属性中,每杀死一个怪物获得的仇恨值" }, - "maxValidHp": { + "moveSpeed": { "_leaf": true, "_type": "textarea", - "_range": "thiseval==null||thiseval>0", - "_data": "最大合法生命值;如果此项不为null且用户通关血量超过本值,则视为作弊,不上传成绩" + "_data": "行走速度,即勇士每走一格的时间,一般100比较合适" }, "animateSpeed": { "_leaf": true, diff --git a/docs/event.md b/docs/event.md index 0b376eb2..8fa1798f 100644 --- a/docs/event.md +++ b/docs/event.md @@ -441,7 +441,7 @@ value为必填项,代表要修改到的结果。此项无需再手动加单引 name必填项,代表要修改的全局数值,其和全塔属性中的values一一对应。目前只能为`"lavaDamage", "poisonDamage", "weakValue", "redJewel", "blueJewel", "greenJewel", "redPotion", "bluePotion", "yellowPotion", "greenPotion", "breakArmor", "counterAttack", -"purify", "hatred", "maxValidHp", "animateSpeed"`。 +"purify", "hatred", "moveSpeed", "animateSpeed"`。 value为必填项,代表要修改到的结果。该项必须是个数值。 diff --git a/libs/control.js b/libs/control.js index 72758e6d..bd4de898 100644 --- a/libs/control.js +++ b/libs/control.js @@ -465,7 +465,7 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { core.control.tryMoveDirectly(destX, destY); } core.status.automaticRoute.moveDirectly = false; - }, 100); + }, core.values.moveSpeed || 100); } return; } @@ -714,7 +714,7 @@ control.prototype.setHeroMoveInterval = function (direction, x, y, callback) { core.status.heroMoving = 0; if (core.isset(callback)) callback(); } - }, 12.5 * toAdd / core.status.replay.speed); + }, (core.values.moveSpeed||100) / 8 * toAdd / core.status.replay.speed); } ////// 实际每一步的行走过程 ////// @@ -857,7 +857,7 @@ control.prototype.moveHero = function (direction, callback) { /////// 使用事件让勇士移动。这个函数将不会触发任何事件 ////// control.prototype.eventMoveHero = function(steps, time, callback) { - time = time || 100; + time = time || core.values.moveSpeed || 100; // 要运行的轨迹:将steps展开 var moveSteps=[]; diff --git a/libs/events.js b/libs/events.js index 30d82948..86978527 100644 --- a/libs/events.js +++ b/libs/events.js @@ -307,12 +307,7 @@ events.prototype.gameOver = function (ending, fromReplay, norank) { } else { - if (core.isset(core.values.maxValidHp) && core.status.hero.hp>core.values.maxValidHp) { - core.drawText("作弊可耻!", function () { - core.restart(); - }); - } - else if (core.hasFlag('debug')) { + if (core.hasFlag('debug')) { core.drawText("\t[系统提示]调试模式下无法上传成绩", function () { core.restart(); }) diff --git a/project/data.js b/project/data.js index 107c1140..9b4ac12e 100644 --- a/project/data.js +++ b/project/data.js @@ -337,7 +337,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "counterAttack": 0.1, "purify": 3, "hatred": 2, - "maxValidHp": null, + "moveSpeed": 100, "animateSpeed": 300 }, "flags": { From 717efe78826a9caf5d2e3aa8352b77e36d4bb090 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 3 Dec 2018 21:22:40 +0800 Subject: [PATCH 10/16] show/hide Async --- _server/blockly/MotaAction.g4 | 18 ++++++++------- docs/event.md | 9 +++++--- libs/events.js | 30 +++++++++++++++---------- libs/maps.js | 42 +++++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 9d663fff..c4c827c9 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -483,13 +483,13 @@ return code; show_s - : '显示事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' Int? Newline + : '显示事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' Int? '不等待执行完毕' Bool? Newline /* 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 -default : ["","","",500] +default : ["","","",500,false] colour : this.eventColor var floorstr = ''; if (EvalString_0 && EvalString_1) { @@ -510,18 +510,19 @@ if (EvalString_0 && EvalString_1) { } IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); Int_0 = Int_0 ?(', "time": '+Int_0):''; -var code = '{"type": "show"'+floorstr+IdString_0+''+Int_0+'},\n'; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "show"'+floorstr+IdString_0+''+Int_0+Bool_0+'},\n'; return code; */; hide_s - : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' Int? Newline + : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' Int? '不等待执行完毕' Bool? Newline /* 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 -default : ["","","",500] +default : ["","","",500,false] colour : this.eventColor var floorstr = ''; if (EvalString_0 && EvalString_1) { @@ -542,7 +543,8 @@ if (EvalString_0 && EvalString_1) { } IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); Int_0 = Int_0 ?(', "time": '+Int_0):''; -var code = '{"type": "hide"'+floorstr+IdString_0+''+Int_0+'},\n'; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "hide"'+floorstr+IdString_0+''+Int_0+Bool_0+'},\n'; return code; */; @@ -1929,7 +1931,7 @@ ActionParser.prototype.parseAction = function() { y_str.push(t[1]); }) this.next = MotaActionBlocks['show_s'].xmlText([ - x_str.join(','),y_str.join(','),data.floorId||'',data.time||0,this.next]); + x_str.join(','),y_str.join(','),data.floorId||'',data.time||0,data.async||false,this.next]); break; case "hide": // 消失 data.loc=data.loc||[]; @@ -1941,7 +1943,7 @@ ActionParser.prototype.parseAction = function() { y_str.push(t[1]); }) this.next = MotaActionBlocks['hide_s'].xmlText([ - x_str.join(','),y_str.join(','),data.floorId||'',data.time||0,this.next]); + x_str.join(','),y_str.join(','),data.floorId||'',data.time||0,data.async||false,this.next]); break; case "setBlock": // 设置图块 data.loc=data.loc||['','']; diff --git a/docs/event.md b/docs/event.md index 8fa1798f..f9d4e64a 100644 --- a/docs/event.md +++ b/docs/event.md @@ -456,7 +456,8 @@ value为必填项,代表要修改到的结果。该项必须是个数值。 {"type": "show", "loc": [3,6], "floorId": "MT1", "time": 500}, // 启用MT1层[3,6]位置事件,动画500ms {"type": "show", "loc": [3,6], "time": 500}, // 如果启用目标是当前层,则可以省略floorId项 {"type": "show", "loc": [3,6]}, // 如果不指定动画时间,则立刻显示,否则动画效果逐渐显示,time为动画时间 - {"type": "show", "loc": [[3,6],[2,9],[1,2]], "time": 500} // 我们也可以同时动画显示多个点。 + {"type": "show", "loc": [[3,6],[2,9],[1,2]], "time": 500}, // 我们也可以同时动画显示多个点。 + {"type": "show", "loc": [3,6], "time": 500, "async": true} // 可以使用异步动画效果 ] ``` @@ -468,13 +469,15 @@ floorId为目标点的楼层,如果不是该楼层的事件(比如4楼小偷 time为动画效果时间,如果指定了某个大于0的数,则会以动画效果慢慢从无到有显示,动画时间为该数值;如果不指定该选项则无动画直接立刻显示。 +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + !> **要注意的是,调用show事件后只是让该事件从禁用状态变成启用,从不可见不可交互变成可见可交互,但本身不会去执行该点的事件。** ### hide:将一个启用事件禁用 `{"type":"hide"}`和show刚好相反,它会让一个已经启用的事件被禁用。 -其参数和show也完全相同,loc指定事件的位置,floorId为楼层(同层可忽略),time指定的话事件会以动画效果从有到无慢慢消失。 +其参数和show也完全相同,loc指定事件的位置,floorId为楼层(同层可忽略),time指定的话事件会以动画效果从有到无慢慢消失,async代表是否是异步效果。 loc同样可以简单的写[x,y]表示单个点,或二维数组[[x1,y1],[x2,y2],...]表示多个点。 @@ -492,7 +495,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` {"type": "hide", "loc": [[3,6],[2,9],[1,2]], "time": 500}, // 也可以同时指定多个点消失 {"type": "hide", "time": 500}, // 如果不指定loc选项则默认为当前点, 例如这个就是500ms消失当前对话的NPC {"type": "hide"}, // 无动画将当前事件禁用,常常适用于某个空地点(触发陷阱事件、触发机关门这种) - + {"type": "hide", "loc": [3,6], "time": 500, "async": true} // 可以使用异步动画效果 ] ``` diff --git a/libs/events.js b/libs/events.js index 86978527..14e1fc5d 100644 --- a/libs/events.js +++ b/libs/events.js @@ -450,12 +450,15 @@ events.prototype.doAction = function() { && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) data.loc = [[core.calValue(data.loc[0]), core.calValue(data.loc[1])]]; if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { - core.animateBlock(data.loc,'show', data.time, function () { - data.loc.forEach(function (t) { - core.showBlock(t[0],t[1],data.floorId); - }) - core.events.doAction(); - }); + if (data.async) { + core.animateBlock(data.loc, 'show', data.time); + this.doAction(); + } + else { + core.animateBlock(data.loc,'show', data.time, function () { + core.events.doAction(); + }); + } } else { data.loc.forEach(function (t) { @@ -473,13 +476,16 @@ events.prototype.doAction = function() { if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { data.loc.forEach(function (t) { core.hideBlock(t[0],t[1],data.floorId); - }) - core.animateBlock(data.loc,'hide',data.time, function () { - data.loc.forEach(function (t) { - core.removeBlock(t[0],t[1],data.floorId) - }) - core.events.doAction(); }); + if (data.async) { + core.animateBlock(data.loc, 'hide', data.time); + this.doAction(); + } + else { + core.animateBlock(data.loc,'hide', data.time, function () { + core.events.doAction(); + }); + } } else { data.loc.forEach(function (t) { diff --git a/libs/maps.js b/libs/maps.js index 3c1d91d8..7f0375f4 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1032,8 +1032,6 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { maps.prototype.animateBlock = function (loc,type,time,callback) { if (type!='hide') type='show'; - core.clearMap('route'); - if (typeof loc[0] == 'number' && typeof loc[1] == 'number') loc = [loc]; @@ -1076,30 +1074,50 @@ maps.prototype.animateBlock = function (loc,type,time,callback) { } // core.status.replay.animate=true; + var clear = function () { + list.forEach(function (t) { + core.clearMap('route', t.x*32, t.y*32+32-t.height, 32, t.height); + }) + } var draw = function () { list.forEach(function (t) { core.canvas.route.drawImage(t.image, t.bx*32, t.by*t.height, 32, t.height, t.x*32, t.y*32+32-t.height, 32, t.height); }) } - var opacityVal = 0; - if (type=='hide') opacityVal=1; + var alpha = 0; + if (type=='hide') alpha=1; - core.setOpacity('route', opacityVal); + core.setAlpha('route', alpha); draw(); var animate = window.setInterval(function () { - if (type=='show') opacityVal += 0.1; - else opacityVal -= 0.1; - core.setOpacity('route', opacityVal); - if (opacityVal >=1 || opacityVal<=0) { + if (type=='show') alpha += 0.1; + else alpha -= 0.1; + clear(); + if (alpha >=1 || alpha<=0) { + delete core.animateFrame.asyncId[animate]; clearInterval(animate); - core.clearMap('route'); - core.setOpacity('route', 1); - // core.status.replay.animate=false; + core.setAlpha('curtain', 1); + if (type == 'show') { + loc.forEach(function (t) { + core.showBlock(t[0],t[1],data.floorId); + }); + } + else { + loc.forEach(function (t) { + core.removeBlock(t[0],t[1],data.floorId); + }); + } if (core.isset(callback)) callback(); } + else { + core.setAlpha('route', alpha); + draw(); + } }, time / 10 / core.status.replay.speed); + + core.animateFrame.asyncId[animate] = true; } ////// 将某个块从禁用变成启用状态 ////// From b970002b4fd1f7bf819344cf926a5caf2bb71ab9 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 3 Dec 2018 21:56:17 +0800 Subject: [PATCH 11/16] \f drawImage in TextBox --- _server/editor_blockly.js | 2 +- docs/element.md | 1 + docs/event.md | 14 ++++++++++++++ libs/ui.js | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 2d81a4cc..c16c0a12 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -422,7 +422,7 @@ document.getElementById('blocklyDiv').onmousewheel = function(e){ var type = args.type; if (!type) return false; editor_blockly.id = id_; - codeAreaHL.setValue(input.value.replace(/\\r/g,'\\\\r')); + codeAreaHL.setValue(input.value.replace(/\\r/g,'\\\\r').replace(/\\f/,'\\\\f')); document.getElementById('entryType').value = type; editor_blockly.parse(); editor_blockly.show(); diff --git a/docs/element.md b/docs/element.md index fc633a07..254f8ad9 100644 --- a/docs/element.md +++ b/docs/element.md @@ -313,6 +313,7 @@ floorId指定的是目标楼层的唯一标识符(ID)。 - 使用`\b[...]`来制作对话框效果,如`\b[up,3,2]`。 - 使用`\r[...]`来动态修改局部文本的颜色,如`\r[red]`。 - 使用`${}`来计算一个表达式的值,如`${status:atk+status:def}`。 +- 使用`\f[...]`来同时插入一张立绘图,如`\f[1.png,100,200]`。 从V2.5.2开始,也允许绘制一张头像图在对话框中,只要通过`\t[1.png]`或`\t[标题,1.png]`的写法。 diff --git a/docs/event.md b/docs/event.md index f9d4e64a..7720332f 100644 --- a/docs/event.md +++ b/docs/event.md @@ -253,6 +253,20 @@ ] ``` +从V2.5.3以后,也可以使用`\f[...]`来同时绘制一张图片。 + +其基本写法是`\f[图片名,起始x像素,起始y像素]`,或者`\f[图片名,起始x像素,起始y像素,绘制宽度,绘制高度]`。 + +需要注意的是,这个图片是绘制在UI层上的,下一个事件执行时即会擦除;同时如果使用了\t的图标动画效果,重叠的地方也会被图标动画给覆盖掉。 + +``` js +"x,y": [ // 实际执行的事件列表 + "\t[勇士]\b[up,hero]\f[1.png,100,100]以(100,100)为左上角绘制1.png图片", + "\t[hero]\f[1.png,100,100]\f[2.png,300,300]同时绘制了两张图片", + "\f[1.png,100,100,300,300]也可以填写宽高,这样会把图片强制进行放缩到指定的宽高值" +] +``` + 另外值得一提的是,我们是可以在文字中计算一个表达式的值的。只需要将表达式用 `${ }`整个括起来就可以。 ``` js diff --git a/libs/ui.js b/libs/ui.js index ac5e4c93..938b29b7 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -500,6 +500,20 @@ ui.prototype.drawTextBox = function(content, showAll) { core.status.boxAnimateObjs = []; core.clearMap('ui'); + // drawImage + content = content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + var ss = str.split(","); + if (ss.length!=3 && ss.length!=5) return ""; + var img = core.material.images.images[ss[0]]; + if (!core.isset(img)) return ""; + // 绘制 + if (ss.length==3) + core.canvas.ui.drawImage(img, parseFloat(ss[1]), parseFloat(ss[2])); + else + core.canvas.ui.drawImage(img, 0, 0, img.width, img.height, parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4])); + return ""; + }); + var globalFont = core.status.globalAttribute.font; var font = textfont + 'px '+globalFont; if (textAttribute.bold) font = "bold "+font; From 6d052efba54ef29dcc4b8b7010b3e8b583ab4204 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 3 Dec 2018 22:52:42 +0800 Subject: [PATCH 12/16] Toolbar button 1-7 --- docs/personalization.md | 2 +- index.html | 7 ++++ libs/control.js | 40 ++++++++++++++++--- libs/core.js | 2 + libs/events.js | 3 ++ main.js | 84 ++++++++++++++++++++++++++++++++++----- project/images/icons.png | Bin 22870 -> 25694 bytes 7 files changed, 122 insertions(+), 16 deletions(-) diff --git a/docs/personalization.md b/docs/personalization.md index 5a957c5b..6529ed3b 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -594,7 +594,7 @@ this.myfunc = function(x) { ``` 3. 在editor.html中的statusBar(323行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。editor-mobile.html同理。 -4. 使用便捷PS工具,打开icons.png,新增一行并将魔力的图标P上去;记下其索引比如30(从0开始数)。 +4. 使用便捷PS工具,打开icons.png,新增一行并将魔力的图标P上去;记下其索引比如37(从0开始数)。 5. 在main.js的this.statusBar中增加图片、图标和内容的定义。 ``` js this.statusBar = { diff --git a/index.html b/index.html index 2a05eb95..4e65f98a 100644 --- a/index.html +++ b/index.html @@ -118,6 +118,13 @@ + + + + + + +

diff --git a/libs/control.js b/libs/control.js index bd4de898..d81ae7bd 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2778,7 +2778,8 @@ control.prototype.triggerStatusBar = function (name) { if (name!='hide') name='show'; var statusItems = core.dom.status; var toolItems = core.dom.tools; - if (name == 'hide') { + core.domStyle.showStatusBar = name == 'show'; + if (!core.domStyle.showStatusBar) { for (var i = 0; i < statusItems.length; ++i) statusItems[i].style.opacity = 0; for (var i = 0; i < toolItems.length; ++i) @@ -2787,10 +2788,7 @@ control.prototype.triggerStatusBar = function (name) { else { for (var i = 0; i < statusItems.length; ++i) statusItems[i].style.opacity = 1; - for (var i = 0; i < toolItems.length; ++i) - toolItems[i].style.display = 'block'; - if (core.domStyle.screenMode != 'vertical') - core.statusBar.image.shop.style.display = 'none'; + this.setToolbarButton(false); } } @@ -2872,6 +2870,33 @@ control.prototype.updateGlobalAttribute = function (name) { } } +////// 改变工具栏为按钮1-7 ////// +control.prototype.setToolbarButton = function (useButton) { + if (!core.domStyle.showStatusBar) return; + + if (!core.isset(useButton)) useButton = core.domStyle.toolbarBtn; + if (core.domStyle.screenMode != 'vertical') useButton = false; + + core.domStyle.toolbarBtn = useButton; + if (useButton) { + ["book","fly","toolbox","shop","save","load","settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["btn1","btn2","btn3","btn4","btn5","btn6","btn7"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }) + } + else { + ["btn1","btn2","btn3","btn4","btn5","btn6","btn7"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["book","fly","toolbox","shop","save","load","settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }); + core.statusBar.image.shop.style.display = core.domStyle.screenMode != 'vertical' ? "none":"block"; + } +} + ////// 屏幕分辨率改变后重新自适应 ////// control.prototype.resize = function(clientWidth, clientHeight) { if (main.mode=='editor')return; @@ -2934,6 +2959,8 @@ control.prototype.resize = function(clientWidth, clientHeight) { var zoom = (ADAPT_WIDTH - width) / 4.22; var aScale = 1 - zoom / 100; + core.domStyle.toolbarBtn = false; + // 移动端 if (width < CHANGE_WIDTH) { if(width < ADAPT_WIDTH){ @@ -3172,7 +3199,7 @@ control.prototype.resize = function(clientWidth, clientHeight) { { imgId: 'shop', rules:{ - display: shopDisplay + display: shopDisplay && core.domStyle.showStatusBar } }, { @@ -3262,6 +3289,7 @@ control.prototype.resize = function(clientWidth, clientHeight) { }, ] core.domRenderer(); + this.setToolbarButton(); } ////// 渲染DOM ////// diff --git a/libs/core.js b/libs/core.js index 41153ef0..f6cfe375 100644 --- a/libs/core.js +++ b/libs/core.js @@ -74,6 +74,8 @@ function core() { this.domStyle = { styles: [], scale: 1.0, + toolbarBtn: false, + showStatusBar: true, } this.bigmap = { canvas: ["bg", "event", "event2", "fg", "damage", "route"], diff --git a/libs/events.js b/libs/events.js index 14e1fc5d..70c9a013 100644 --- a/libs/events.js +++ b/libs/events.js @@ -91,6 +91,9 @@ events.prototype.initGame = function () { ////// 游戏开始事件 ////// events.prototype.startGame = function (hard, seed, route, callback) { + main.dom.levelChooseButtons.style.display='none'; + main.dom.startButtonGroup.style.display='none'; + var start = function () { console.log('开始游戏'); core.resetStatus(core.firstData.hero, hard, null, null, core.initStatus.maps); diff --git a/main.js b/main.js index 592e56f7..2ff39470 100644 --- a/main.js +++ b/main.js @@ -98,7 +98,14 @@ function main() { 'shop': document.getElementById("img-shop"), 'save': document.getElementById("img-save"), 'load': document.getElementById("img-load"), - 'settings': document.getElementById("img-settings") + 'settings': document.getElementById("img-settings"), + 'btn1': document.getElementById("img-btn1"), + 'btn2': document.getElementById("img-btn2"), + 'btn3': document.getElementById("img-btn3"), + 'btn4': document.getElementById("img-btn4"), + 'btn5': document.getElementById("img-btn5"), + 'btn6': document.getElementById("img-btn6"), + 'btn7': document.getElementById("img-btn7") }, 'icons': { 'floor': 0, @@ -132,6 +139,13 @@ function main() { 'erase': 27, 'empty': 28, 'exit': 29, + 'btn1': 30, + 'btn2': 31, + 'btn3': 32, + 'btn4': 33, + 'btn5': 34, + 'btn6': 35, + 'btn7': 36 }, 'floor': document.getElementById('floor'), 'name': document.getElementById('name'), @@ -386,7 +400,9 @@ main.dom.data.ontouchend = function (e) { } ////// 点击状态栏中的怪物手册时 ////// -main.statusBar.image.book.onclick = function () { +main.statusBar.image.book.onclick = function (e) { + e.stopPropagation(); + if (core.isset(core.status.replay) && core.status.replay.replaying) { core.triggerReplay(); return; @@ -402,7 +418,8 @@ main.statusBar.image.book.onclick = function () { } ////// 点击状态栏中的楼层传送器/装备栏时 ////// -main.statusBar.image.fly.onclick = function () { +main.statusBar.image.fly.onclick = function (e) { + e.stopPropagation(); // 播放录像时 if (core.isset(core.status.replay) && core.status.replay.replaying) { @@ -427,7 +444,8 @@ main.statusBar.image.fly.onclick = function () { } ////// 点击状态栏中的工具箱时 ////// -main.statusBar.image.toolbox.onclick = function () { +main.statusBar.image.toolbox.onclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.rewindReplay(); @@ -445,7 +463,8 @@ main.statusBar.image.toolbox.onclick = function () { } ////// 双击状态栏中的工具箱时 ////// -main.statusBar.image.toolbox.ondblclick = function () { +main.statusBar.image.toolbox.ondblclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.rewindReplay(); @@ -458,7 +477,8 @@ main.statusBar.image.toolbox.ondblclick = function () { } ////// 点击状态栏中的快捷商店时 ////// -main.statusBar.image.shop.onclick = function () { +main.statusBar.image.shop.onclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.bookReplay(); @@ -470,7 +490,8 @@ main.statusBar.image.shop.onclick = function () { } ////// 点击状态栏中的存档按钮时 ////// -main.statusBar.image.save.onclick = function () { +main.statusBar.image.save.onclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.speedDownReplay(); @@ -487,7 +508,8 @@ main.statusBar.image.save.onclick = function () { } ////// 点击状态栏中的读档按钮时 ////// -main.statusBar.image.load.onclick = function () { +main.statusBar.image.load.onclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.speedUpReplay(); @@ -504,7 +526,8 @@ main.statusBar.image.load.onclick = function () { } ////// 点击状态栏中的系统菜单时 ////// -main.statusBar.image.settings.onclick = function () { +main.statusBar.image.settings.onclick = function (e) { + e.stopPropagation(); if (core.isset(core.status.replay) && core.status.replay.replaying) { core.saveReplay(); @@ -520,6 +543,49 @@ main.statusBar.image.settings.onclick = function () { main.core.openSettings(true); } +////// 点击工具栏时 ////// +main.dom.toolBar.onclick = function () { + if (core.isset(core.status.replay) && core.status.replay.replaying) + return; + main.core.control.setToolbarButton(!core.domStyle.toolbarBtn); +} + +////// 手机端的按钮1-7 ////// +main.statusBar.image.btn1.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 49}); +}; + +main.statusBar.image.btn2.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 50}); +}; + +main.statusBar.image.btn3.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 51}); +}; + +main.statusBar.image.btn4.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 52}); +}; + +main.statusBar.image.btn5.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 53}); +}; + +main.statusBar.image.btn6.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 54}); +}; + +main.statusBar.image.btn7.onclick = function (e) { + e.stopPropagation(); + main.core.onkeyUp({"keyCode": 55}); +}; + ////// 点击“开始游戏”时 ////// main.dom.playGame.onclick = function () { main.dom.startButtons.style.display='none'; diff --git a/project/images/icons.png b/project/images/icons.png index 75301f33464638828c1bfcdd538f2b523f784721..c5a383c617a6b21df8fb0b916fc229b5579c1da3 100644 GIT binary patch literal 25694 zcmaI7byOTdvo{PZEWuqCUEJN>VS_FbB)Cg(cXwSR5FCOh1W9mr4X(j8xVr>ip8I_F zocq^z=IrU|?dh7DlHXKMRmVV86);dqP~qUzo|4Szhblw?=Cw})X_>X`h$ToXqD7Z@4PQapXD8*ML z`=!_oyubqpSB`%Kbrp$emPX@ch({2kmswtCpjmGv`v0n zQ$#b}aE))X5L0Gy+&5e^ID?0eio_ig^erri z;ozidjvVnIczKRh)YOJNtKt)$Jq~F_#aL6Ijn)ht&n-7TMotgS%Sx{??sk%;JPGG6 z_blfI2u@B%1}^bX#5 zAV3_smUXFVw5F{ReMHc2#`=GKyp9(aJ^rzrV(E#Y_ zUE(DV1NWZ^2?@1RZtKlu0e+&QnNVhm=2^HYTELF~+F4Lfc!hCS-+^(D_j3-qA^7A5z6xOZU1=of&8mfByP4#tVbN(ayC7tqcFz4$KabhdldoD=Z8*0t%8Bn>SwaWfvm4JV0wB&r{?Bk3RSh%H} zVGF%`Z>gaHn!6CRlW|~*68>y!qZk9`M%?K3;OljQ&$&Sm?8MqYU~td`&BeGZMlNY! zdN9vS{-vfMSP?}YiJ&Q1@uW@T0HMkC>Pue?H9$oV-}AX-kCZ2xF{Up;^+yU-ElHM) z`l=#$PN6?du<m5HopX$m37Iy;lI4hGDC;>E!xQKkRrBgisTI zsEa=EzmEYh>~-?F!(m5U<_*3dR+D+|W1w=j;emLaAwXS`K?tBG@^H%wyye>eGPlPG z;*G19?@&VdSF1#$U~;$If%-sXjUY)z;~?>69iCd^6C!?Z|N5@OSQZM32kdva0@kJ| zibazb-~;`5Al=A-=pO&FEg z6-!2L-V|DDZ&mz1_fa3z)TjMhV8E5V_KM;5s=!uAsWRQrjs?Yu-xGi<*k`^(z2uhM zq&vR6Dj4a`*8GV^QJ_4He2C6PHm~3R&%eX%Q3QKclN9L01^iDp!u>~C#<*vS5wi>#y?C;Va&sNYg8=;YJE7YyWyTgRr0Iqr zgmtVyE5h9^A&fjcn;KrJj^JaQzt^6wYcW!bc(+i(dflF&0#;uX&l_sSW)VC^>9P$b zT?`=wWVm|U<+ea$`&Zlv%SpJjEiQ|IonJv}Hm?qUJ0=Q5=g1Xl2*Izi?4i>T{hTN( znTy;CC>b}B1VbqI7+|j?S<<2A#BB&DnlWtSK2!C}@8xsz;uuTePPadPZj{1v?#KIE z+8qnNunVpJCBU02*)Qwn_K5PT=v{fbxO}0(Uw=i3bzG91`f&lp7ln^?RFhThaoAy7 z=@7y+uTkLqk7s+J@^F4V!ke)4HNRtUFh`IRfbI~+t`lSFb^If?)i;d1dDGjQ*In|` z7pYEu8%Mih{~eh!6m-5Ofr>OB-CZ&P4$#5U<@?dALH!?e`M)mRTb^)c$llRO7%S5m z(Q{Pf9F7(4Ja|`RY@-~4N(S6p#6b3N^+Qbe zMf_5a7K5AGp;OJ{zW`w7gH~*7vtFv+O5AvNInxk?ev#d3g>kmzD@r({K6BE*-tRog z|G8a?3t!2BI#--^W0yW`r8Dd}p}43tCPuzt#i!(d;bH!oLUJ-$9l^Jzc)}MFkcv8| z9$%MGS5|pXDY;nu9-6(w-SYM>PWk_DUTGLd#yd2Pj3~mYyGROtPCq~Ui7GR)FvV5qj*1d;?XV1f z$PWSsTv_=$3w^_(IjJS-UY2~R|AZ~(^7Yt+nub;qNYMF@o;3Bm7wdQByF4R_zM`_o zmOeR+3EG#-IQv`(H^XhXlzFHeb-BncZkDvL7GLq@5{rbRU>xUZltVHrDDKV$IhBm*CoLQCh=G_15VM_9kGwx}vf=)cO-Xz-hTV zGV;A^10_a|2DQpKYKY6+0*_Q$7%q!J_WYVpMz}FGro7hN66u=j<*1sEo+=BaSNuLW z{Tw~aU6?&4(F$cZd{X`3|nZf)Svl{tG7|D&<$500|etPIUC23o=dZwrpH>E=}pjxt`n0f}!Jn!@0` zc|n^MJY|0&zQ_NMTlw~!t&^NGF09gy9Qu!kyIRV$Vsx~OcdmA`4h#6tEJmtvnwoD^ zwRKG+rn*{%qpxohxu5@Z;ci^b6>)XdgD6_yt46^DL5#pA_OK5t%;Pe4SOyv0%#K_a z1T3oX$!FSt1>(`%IAkmNHH<9{M~P~G^U~)r85>~xYSc2Ke9M^n~4?j33*J)o1zt%9o0oOTta8N zWQZ7GM&fsssXfA;~x)MChl z>UPqJ^rVyhiY_=S;RcnE)VBbhj1Qf763vQqQoO8B5%1%cYb_xkSVg&~rLV+=o)o*W z9xrYy@=GTW>In_9S=KobZ&UJz=6Gp(u_tuxM5ee&$n6l7@zX|@ z@nB3_EK$48Chpq)4l{5Kz~A<98!e=#si&{RP={oDtq`10msGx}l@syVQ z_~Fp5vQWLyP7+kVSy6s82EYY;G*oU`j1;%cA1Uc6)AmP_U81)+BMli0A5LZHjHX9_ z(Wj!t-%U`Hf~f1h=@@-*wnB5AMBKkgDu_p(8Mx4H*_go3WjkH$qIX>yH15Vwm=K=2I}(I&`GC5`V#0=m(*7rJ$6K3{#1MlRCgP!Ck{Fw87F`5QCqJ z1e-JlV`pd!@+EGxAG@TvPmrAft2VjAgey|KWV!7lz^uLFiyb!J)v6tKYOD(%9ntoE z)dMV6vC+rlq}Q%%8WbbwQFBnSfX4WvjchW$}R5mn|JiQQh0M_VW_4GX>TYa>9nT z!GBLmEo<$q_q&tOWlxB|SY!R{QL}BA*RU0j)o<^VGrNsAjt)q|zgkD=n1 zoTD=M*wsFUbxk-9rWaWYa&aWdc!YNz>f`U!tjT0uPQs_NlgRo7ZAFSyApQN}(>q%o zIW@Wrj@cH{%fVluqRbUs$x9y7{MWWM@Bv$OY#NmdItt5XVgV%oxuA(nr7CHOzPQsvg22RP(ussP3^t@-MLf-BvJx&c5#}{eI4Fm zk!Q~E7Byo?4vt|OYBn2tL<7#7P-^&P;SG08wRP2x3c5>^>QVO_XJt5;B7bSa@rWUG zml_KUWb)hH9^Eq2EYI_rxkzrflqurzI6T@C!?Fr=J@7`uGM!P;WoZU8QPfus1k=S> z0U?88U><7hLWTm1dyGM|t)c=3`5 zCEjZtqu|ne$}EAI5@)`etR8RjYKkqn7~`jt3DIQ2()18N7v%x-6H<|Rs8SB+Oh21g zTBJB-M<}|qtl7@a6yUwTIpoy)Vj0Xw?{X0-a~I$;tEmPhPntn0!idJ0?GY~h$jor5 zYgk^rQ1p|2XSzX&eV)R(TgCh*ssOsJpYjt+cQJn z9I)sZ(q;NaLf!`&*Jby-y}OR0=7P93?i&5yw_V2r(sDn-TMlMO7M}U@Uf8A_l|Npe zWzSONdE!()!)a^L4DE3$X)=7nBy{dVyTJ#f2^3D%!F!a2Ho<`?ikYY zj3Ty1=e7pbpBdB19|;r#f+D4>Gi|oQXzyGSp3Y1EhV?o93G^0w-8h4Kx`K#aNR#|= z(kLDPNfiB9FX$!W5|RfUdeGw=q&{{Zk%z@fhO#UC(lGLewuOKztlx4|{RWHL0kaB$ zgEaZbC4mP1uM5x?FJPLlN6we*BR-=i#7o2{GGrYvs`mDl<0P+K# z=RYq-akRHPwU>)D#5hIE9Oq9gqV4!^cJ3Qf|*xQ}>oRJZ5CE792EoUYx{P7s2 z(-`(koj7S}kCajDoFFzRL?LO*i1E}EK-VfSIXq4Uttk=U+2m_?kjPsC&x+mn#2ulw zQow3mI4uM*LeTf9V7uJJNh3hHi)btpuhjEE(Zhh5iy;Q{g`}vh+%T$%1F@mCb9Rd# zL1;hcJ1{f98;4CcvuTLZgTy(rpC1crt{brs2OlWkk;Zfe(b_+s&~($CI!n{aMp|<0D1)?z;Tiob9BXOpDUcp5EQ&caFZ0TZBozdUOAidAN2WRC=&` zHa__SX1U0U&xu~%E|_&gj|~Er_(e)LY>RC0%YR{*X&^`mEE;63i+Mc~5*^%jDM;v7 z!=;Gnp@D^6v|-5toI)q?Ltf_YK1yYKFIzi@Z%>_4B$5H?PX85H_+c@wEn&nOu7}u1 zS$A3cvXMY^uW8fl<)q3Q3%&m-K`jV8d|sK7zMVH5#$ zUUsr_Ao-Hi({>bhVN2N>Lk5UYqGIcbYWWh2;F&eE1Q+iMLOGhC%mQy}^(}oT3D%jNz7s zsYA;Pr*0X_)VqvH47-Bw%W_o0W?aoj0{Pmv?*cRy;yn5aWuml6z^))4C?H_Z06#fh zozxIm*ME}-q<8GH$w*n6ESv3uQOPi4V3%ywP3-bY5k$tejz4P^Ez8+aih+`2y-wyo ztOavz(NZ%gM?xU@MDHVIUGxB21!p;*H1W&SDqv(4$PoYYnr;)Gap2$IxxFLJb(;$8 z1SK>CNad<4^QGQ9w5+k8h)txPpRw!ZN@m6Fn2RQyI%~h<&z9zR^8wsd(^-uWj00~< zjA(fG`}*qCdajw$ZBywNs_T z(TfjY7QFwHo~EV6$h%F-vQeo&MoXT{-R>J`akt|;H*`6^0mqIZP?0G%)rTwUOOh&A zzg8?>`NqszzBUPrhL0W(pN3D`p`^WQ{9&m3^Ol)D_2&>H0lx#XiYcr;w5Boh>> z&<04TVtcIOluYAszTsg^dUNX=pvuHUK~q~>8v8ub%Y+A>GGOas?IZ&tSsH3v!krJ= zv*IHxBjh8aqcoqHcm0j7&x5xf2J3M#XbGtA|4y_t(}XNv$A8wLjNzew!i`8m){v=~0a4gBAU98crtF=ddMs zIMGN2p$b+%udm!cGfG4D3!!=KuT_dAB*VRIX!uUhDrS&&nisPIXiY`az(Pxcaa06H zzl!-x!OBOoH+X!#Pu|cSClymeJ1vykla=BGysG>KslUJf&zI_K1BztbSLO_Dz{F91 ze0)5vrJgXO5+1K!dgvZefBmQjH_rCxg=XQ0MAnJ$wzf8DGLENacLK#Ux{Ndwp6_04 zWeZx`p7FBAH|BsarF|fy*9z`@(O+L_76 z#sKNUIFx%7t$Hu%8v}p1`@uAwat8Owf;&M$tJTQQ*)YX^jt}q67&i9X5l4`#bw1Ax z?Qyke7+>6s9ki14WYU?Q?%{& zd?+tgZjwdMHq=3J*D?}MRd4A~*WXxoNXCxRy)Nl^EE)>5Tt?dtoY0i_NAG9y)LUe^ zeRB@@Bwr#-#jJIm3u+ctEGN=J)p-3U;05LcfBYSF;3VhEAu`4XKA0IJzrW$&qsjM& zs6RKMp)=LjJ{;Q``4PWgjK!JM|90Mu<*O)Aky&qx}(*lzxj$SE<@Y)|JA>*(Dd^)=R<H`iZ3`$Web$ z{5#w6{ie_&Pc==`>m|4SzfDW$e=i@L^VdP5gaL4QaT4$oLB7;}70pe^-Y+*?$B*qS z_Wx?}?4RJx_u5cf_dLTL)(-wq)PrV#b}b{tUme`l6`#SP^o@Jft#_*CO;;mZ84d>SL6w9ASCV{CSjRU3cpxQSgo zfg5$)_MVK(CG}8~h{v6f{-z7n!P2DDSstl|I`M-TzT7`JxhV;0b@U5{(UePSH1#6R zOXQ(89y(F`yHWD+aLv`AQM6oCB&?3m&~rT~_5&X@yIRxGWg?W{kMUCNY6-F{9+!a-gqVU(rxwGJE~D};IZiS9(GO5=tF^*sv4^aba*fsf1pC#{=JqCzTmsl}fL*Gr@?sQC!0YW#&H@B5_5ngBWEWI&PF| zyd*x1CnUrCmVqkqyo~Ss+CYIRdFpAST7KSI>)K>*ntlB$zMW(OH5O7qy#<#KDR)4&zoriUF=Va9GZ`6G>5RsqEQpG8DbEi!AXAFn8 zJeqd>G3Br7fWX5GM_Ax3-cZ{T-p?>!sgXGf=QV5gK81d1X>U&1HE}>A;MsKjRLptN zja*bvjO3@|HA4SI9!%644lii|SAy>!Mw8nHpbcd+fw6|j!sry<)>=F=t!Nee`)Idv zMk(8!DDh?WxSjECZ{{t@89AG}3f}ICT2!GdT29*hIT*FE+kh_Z2$V69u+Uslg0TMF;}7H|Ymtc~Rm2IAkuT7A6mZ zT#7pq%eFm++RClwOBSeppIJJ4?e{8^i8;f0s%0S#<^`gx95sfLzXsqYzAWN|-nE5d zfe-mXvcHo9tMEoM{dYb<7MF3%R`a8>N%I9yIOS~^7u|6W4eLMsC2jph-se{zMcawF zUq_P{n^C*T0+SLo?&n-5GC7Jhs_{z_SzH$B$~Rm?u3mUSTkar5TWFJrtoy|w_4gt1 z(A!=~h*9V8B5|>o96Vkm)@L7t`m7^RmI1oi)`Q8Qy`_%kv0IO9AuF$Bd1bdYTS#pN zu(B8q?$T{f*7*m=eAMx5@@~gn?lxx6UQcAd#a45M@pRJ3J_y;vD%B=21!8?aS;e5S zi4fkc7&4bge&qfShm8i!IEgQUo6|q7))iVCg7+O9z>aDn5Ycn|+rF7a%ZUxTBCEq% zYw*&ZtzfM+^YO|7Iw_$i62meU^I#tn*%unydCRfIWRPNSF@BU2TuDm?FW6u*tz;axV1o3Uh;)f*!~UTHe1F(P0&9GBsw;p)e0BnZJ1a$Hbd92&$ z;}U@|F7DWSg&Ok+s@7*!TpquvA8#UgP-Fhn-V0es@(7z(^;z+I9DL1OFRwbsJe+Ek z+AR`tfT}^M{+T{!=+6?Y`L=x677t>596dUs3|xHH8jYk8slycyIQkpQtTVncuC51lJ6XhTb~H0T zfFJKcC4r1zxx*llMj}FfKp8cmm)uCx76%YBlp`nJs31R&2Roy_dX9+%}ylTo~X z{+)@Pq(C5GhY!`We|>@R28kC72}@5;NQ9YPT0mY-MIc5saQKU87c*tsnl3?vqykpx zpWfhrh~;R;ngL?fX$K({Z&P@LQB@o)g8gRbht$-f{38&!)xiPhCPQ7}K7#9e))tZt z8AK+mB5Z&1@?iOyC_bB;u#iyv>}DIB9R@n7z>g#r4vSr8Gf`M-@5%X(&DqOh;zI!0 zbXXu)Rszy8m0VOIZ|hMO=AONmWtecFXfb9VQEcZ+=*qGE5DRN~WxHR9S&ZJjx4dPu zo*t~jnJP{tTM%cxQhHA4bR~rMm@T%S%QL#J$({fg8U3EUPLrVop_P-MRS+%Wr>iS? zYzzY*aJ~x|oiQ-f195r$^M!8sl(e`Xhg7bt*zMZ--Ik9|>3$0Xj7pId_gl_&Sw9NE z)a`5H$AKz|N0c82?NE`6PA*Kz~p_J!fq6< zE9%A7?kjbCz6lF|UoJ|=M}X@*J9xd{{Z@}Muw>8*tQCyZC4E+I@z-0W7Xsm?vzop! z$*Zpa?r1GwK8!*Pm8R7zsvL&~yg5PuHL0Oy0-0T0#y;=h4cL$o#h9Av<{_dUXMR}^ ziW*9iu!`@#s_LRCY3W180+UQMF0x~;Q|gLyJ8vS$7BWp!t4tH7AY-&-^4eh2suLCU zDj%;YACLS_vw&ni@z9!k;c61H2mik2tsJ%R7evtNj(uA8@1lJ>{$of z?bt*UTLLn)StF0S8#l!}Xn;rt5tGK9aQ*x0Fbi`4sG|cG+>VRmNl6_xF0=A&9c&5- zg2)cAWdf34%BT?BG*08gkb5C#v(Bo>IsGNysz7S_exHv!ou0v$~)XLm?< zCw|?lW2B7%8^`H%U#_@~^n@fyisiD3$ADj>LqieDzHXTfp|-;NHCBZo>CBjR*P~kq zKl|_{TzIi&`_Up+**}n?Uj8h^-bQo5Wc)oGYEoTabErth+VSDt>k4z1ni&<@+6jW* z9iX7R2@w)D`avNuutPIYippXI3&Clj{(QLv|Nfr__crBFGf|4^R*VJl838(xz&|7( zJweT_DZHzn+#98PB)T3Tyl2yQCi&a^eIyC zLOS315fakxz2{qZ>UhyKxB!1#)8Bcd&hQvJ7R3k+>Ue7131Z((cl#OdMcze({k-5I z4QxvRh!_e;`wkHltQ2?eexnf)S!NFWg%r-rxLT<#nb@+`)rAF2vC@%Jn|rV3RIA1}KfWot=7y zvgo{n%X-)TR$XftN-4$>viI0YC3KHV8vZ}5vayXZ0aBSlReP&E;!L;l;D|eVjl+69 zfn8~AgBv}M##C(LSt_IPHh;&ZbQU*Hf!|j|H}Ze@5fjk_9Pfp0%_9v-{`wzEAQnv* z=@`Klas;xyE};y$#knleD8nEej&A*N?^mo21Y5!8;w`Q0Z_!aK%jw;fy{4S^y7cJc zS>uBHHb6lq1GJch2{U^jEd#?5SMX;@j@`Bt4iVtJFvKJFUKgUMA<4Za5Y2>A>pbrP zieL4@0+OpnbVq3t+b$2g@bw|*4Lr$Z1p)-~Md&vD9dWn6ZeIVTGx;Te52TRZlo^f( zB$F;I%NTSqK@?RB{URL!D6#m1vPf&tcsdeDpOH|rElT`#2U@^Cjlz6mRCD88MVK=GXRoK_#ZX0gs4%9&F0R@MbSiGkJOWXf9_h&+(6wAK{*V8?vkTB8Je}NobCj zm-P6u2>5C21hzVi@zZz}^$)or)l)wk?B==jYo)b8!`}wa(h`!H0^-93#qCJy(t`0dD(&1r5f>;^YW5}?IH7}L=$ z8W!O|Dhp*L^IovCn4Mt;66+*UZvm%pEEv3E)B(5 zf6HhAvy8ulfFGyf-V(69Q+6~w*qT8q%>xxfltDvm69ZU7lG;yWtPoykT_2@Z#BSDa zwun$CHB#$fOiT{*^wt*=NcFF!&oS12a_sWNddj|_@7ykb%7( zgW$#thMn7PB@Z~%t*$ZB$k6-;=`|ZsFm5{WkU~Nw|00?Vc-!Tv$KBLYlPYK{Ic5YA9mIxZgLE-6L;nd<1x-`wOu@x`obFmxDf5bjVxKCkaYU;ahoI4$`;)f}xb@A;&}lJb;QMp)_${z{57CHL(Y(7Bi%s zq?^A4I34;`s-)6nnHpUfsT95EOZ^_We_OsJBz(J(%5{#(NFND+TxgqNnzC*AF8c%! z(@7RNM8M!(LBE5p)rx+_K%NIC;q;`|5Ht#D+g-Rf@)!b5=0s~J5pdI!JEZfxKyk8T zFW~!#0epp7@Ut-0{)*6|$GC)t?dOIRc+?SJc&BV!fIAN}2kYDfwNIqIXrU>XsG2uD z8+}%ZNrd3C<~1Z%$cB#C^}Gb!N%&>3cZxm^3T!$HsP-vs2kx7>rIIDl%SCKN=*sdXwXXqc2EIx&r zBkDg$ukY!1g*)zHoFn4R}|ceaD`$Tnk4$iaY@gU=22lI%2-lO#G|8L-j8bkuJ60y;ZY zRti|I<-JiEoQ_v@wAHJ2yqGxLZ(-Vt7&F&gJ_n7bKpz_IOGRhA&H6J3szzp1LSxky zCbaJPSTyhXI9Ps7CVB~xcJevy#C+_;B0844f^BK5*W$_t)cPbqkSWMT~RL{&IF}tL?dsFFh1o#$rW5m?Ca$a?) zAp(f>L(h%( zjLYlt?c%`;epkoAzWo*jTN*Ul6%LX$>DP!Ju&;t-yXI)Tu<0{^X38|L9tK4QLVU$tV0}zQv z{_N3WWa7+uL!J(s0*kZMY(z_wRWNrKa}9Jk_;Lih{GIV|c`UdJ?gOp$tSeLwFC&CG zlE;$?j?0m$-0`l$D!6XM--Qeo5^(YyvDMX*`5P`zKnOt47_>$1C@QV$PxmRfin%@{ z-8QtR+7U!B4VQ8PA4Hlr?!6)|2$_d7954RdQmtY;1{@FQn*k*)8#&F}7tq$cS=ym6 z((d5WmHPmVg-%?Snv_Zy%n*t0k0_m_{BOBv;VV4Z{9KHwz4I&43x(*8bDAA`!@ufq zC2}C_(~ur+ATO}+nwwT#2vDL|$XfC3D;aYdT|6`Br)xabPVq`;IA9K=yo%R?;Spp(quo^rd zTX#2HdtbxEJBm`S)5q+x1v?2TcVMmHif-OSL@K*h>)%+F|95SG19f6KqCjVs(Eewo z^;d%M>vOK4fV7q*K@oubJ8@lM%F9}q3vkYjXX(yj?lJiAV|&Jhr-7=}HEmxw9gsCZ zM4Qa}XXU*l=~za3?01ioFYxGAs%-X>Md0G)etYiUa+gVE^YCMOpt+L$9Gi1F zpGP?Yw&*(sba|vaeb*m2^VH`t)452mJ+1jOKt2g)Pf`yskUxdnn`j7|dAGwUuqfzG zT7cqw+*xANTQH`-aJy>t_}Eoi{Y>JJ+cda&7Q^znYTK(f-KF&n&-QZ}JKCp%udGG7 zY@7PJHh$EY!dNOPH$HZhu3L#jVZ}ecbR{xTRNH5_?VUEf_cJ-_Ta^St7C}k)tt9+g zQz_t>R21$yke}r*43geFDw-9z%A-!C`P*gx!NOO<-g)E(l{^0hhggNjhG7axpuymo zw3M>oCjj{_}=&!ygkF9oAjp6*sQi*LL{EPPjz z@Pekmlldzf@YV#Kn!a=Hgokf+l-@YauEDw+hI2sn0rq-Wzy{f@HYUH~Ih&u6H#K85 zOCTn`xV22fC96g(1uMM;eT(9&GY*4$-q$u=E<@V72~uC-gYYx9x`ZXHBtH~x(ZTIn z?$)>$h#{Uf4=c?Iw%o{;gphyn`!&Ws1-E$Tu2GHU9kjdZzelvzZ(+jX3G!6?0b0Ch z7N{K5{Kju$+;mB^P})-kGQyjJ)Ap_Ht9J+3U`&Fi_&3Cs zv2Lryh9Fy>`PG)dDtk1_D(XhjFmYDDd#!1=#x}f zDq5kV=Ue{Q+$8>(rTtO)tepr7Si$(h}`Z-7%o3F-)VD0 zp9E;y#>^e93R*Si9=lEto{T)R|7x>)iL43i!F+w?rmyWJ%*A*l8~0_h6S*Fn$xMOI zgcMOURSdBEm@U#k_bT2E{{GZFj7<1tqXx_GzMuX<-3IsaJnJ7MrBC{haifgJ=N6Fn zxmFyqbRa)OQ%ps7&49j5h|9OL7r%CLdD?^>^LO&(6BcEdZu@-k`R9UfWe&cn@t0Pf zYWAv4t(N+DdCK5T54s8?m}5XQuWS83tS2yGw&!B#Q!aA<`M#Y#OyM;YJP?P1n$pA$ zkX>2`JI1_Ze0brk6u(_1^aRjeqJ4 zu|Uw?rpUB2Jqw*M#(Lgg-r2Erm;DA6?`v}wYeAOEnjY5jaH0rQQ@p@&B1xNWB}!`M za6CrVmyEEdxGD61bf2RTOR%ZA3SKy57&qH5(UZQ(4S}#AT3@1`a7) z`YCKjev!Z{y$ycH&hgQvG0_8jaFPP{x|W$7^-3W5NK?H8A)(IXzi{J}5|`taN)%ht z$Me9#p3(kfH8#MZb3s`xwqIX+Xi6n2txWfM-nxbR{>8h2!rB5RFvH`nFRmY_d%cyh6`6qQ>1EYGl<5Gd6HuOd`^#5ftcQ&aIsKt=4ASy zAlm1xwXw}hkK|zXz)hin!aRfXsfR`gnSbpY&4J>7|7iUjMU3?w{{I{yL17^ZPm-Z1 z`^l_u64h~i|Gu|S44132@OM=zaQ{c*s4Oo|GIZ~ZGthd(EYdRDkVDT2)IJH;Y#Q1} z3-Dc4;DdkX?tBsdl&Cc1Fwv|DU6)v%9M(09>fRZSTJ-&HIx48}+|b?WBqJ z&I1i|XjWS7V{10$e(oRQii4Vqj!Mt-+J=O++>xRf5y2L4@hL)Cz`uIRjFb*YQ-&I zcU_<5czVS87kwWMdv{XITHDvv%8t|CT9pLJNZ^@+%e7X9Rp&yBZ3^O)?w@bjadus~ zWQ#Bsy=8K%#&KBoMMvo>cS>tIc)^xOw7esdjUJn0YaS>fj2Y!57S-d0B6mz zo+rny5=QJXLcBw8c@y34!$WkO8GIxK!Ho+m%;}%(QRZzn#0I~366=0loz!@~%SXUD zYYFe1onv}VG{AMUse$jIX-6-4lf=V^g?=1MR}{I9J>2r$=)Jb4D7Y zYQJ|U@%kLoS4s9ro1gH~3kf(A?MGx3jM=)bJcobDZf{#vai)H{{3QloyT{oEyS+5n z#nd|vo;frvppL3($MGz{^%nVqlcoJ>{U(IWSUFOV#8Nir>hR3{-inC+N9&d4py39N z8^z~ez2n6a#yP*^ERvu1(9H3OJe+k{{c9;XxGB#+WVCR(E$=a~9m2`>_h2S!4SZ)w zZrgYu3M2A>sw?k*#Z>J7ig_i4O|d;!Dr5Sj_({DjC8)!j#}gI?574*1T*koRUm<{%%GcFY5Fq>0Axgme%f+NE9O%m9$@S2Vuxu1AC0BZx zXs{-HKsp~jnfDym!T6X(han{)DT1R4jUu$0(6-Ts$i4G7lOn~67g9K;rU_pX= zfZz^;6GCtc?gV$Y;I6^l-3jgz2yVkp-rN1RXV0Fq`>*@n?w(s!x4!DGd+VEqcR5e+ zHKTg#><=o_f5@(1j6KUU{R7DW@Uo?1Aaa=G>7RVo1IxvACXU6Eyq1HFhkVhM z>y`LddX2}gBG?Qt*8bkG>f~*EeKR3tOY4N}m$>su(7sd{W^@p7NOZJ) zh;)mZw9P4TC&x7Th@E;gwstt6YUFXl+x^;|(Ynvq7|-SYDLFaW>HgoTs8gzZ3f6oS zB8?^F{H4TC`aOEwr!7);>_*Je1*_V`*UDZ{sPTS$e1PRWVQjsnuW#eQCqBlEm}r{$ z8bl<7*{&MAoE$2ql*MIUN~D3l^MHT=-EyrNpwM459Cz9t5+wxs7FHbl4o#pNZO>xl zQ=!?zKU{oT?+e2#%BKH2DLdF_85cbNy19ROCnq5$e2N~FFmQd00E*LXc;zHkFT<*X zTD>(JR%z(HjukNtDE!f)-+O<#&pgs>GuMpE+^_;0myh2lYNoI2D}e7^(MHb2_~I_E zxVj=Jo2NgIpaSiV&n%YFjhM3XwS+1WQnU6SLPWV6z_0NLOsu&@x$D3Y*kEECUoABy zLS{woVFVRTrDSSF?m=Id3>PKk40My;P-8n`vLU5D<2Nera21qO&`tV6|1&yTahZCB zWM;*k8jTptX&y?|0CnAkxt#F-bmk()w)6rs!1+5hbM{iI@ald^Tvf-z&Uk$?)!~G2 zers;ZwZxl%Iz=cFGk33ql8loVg86T5O4O#}ddAallEJSxp0uu&>M4 zPWb||iqu-lR^m){lZ@H6{<8sEnz)S3d|<;KS!lRgS+R}@C}cNC6)K+Jz^5|1I}^Jn z&+sYL*!QFHa(ozWvkhK;0_gkyG6ys0ka1Fy{4+)~q3z$vRH_B7G@&{iCl0vzq5tH^j#ejMJlUs%P{pKm~>wIPevm}z^@jR+FEi32b_OUFkvw$nt zi1BH4GUZe!k5Z$oW9%%vjkAJ(eM{k8@N?I_?R&d?v$FS&jpDY{_(F|O>SHLJEB4pE z-`u!Hy1=v^&GsL$Y?^+kzPsrTGRaw{ckuTIn9}Q-3H9VEM&>iL++Vy;*S{GcBR5fDsI@{@VcM&+#*zJ;wA5kA@ z$-|h)?Fq+;B|da}Po)W80gq-rJj4PUBQ213=+y0*o=CA4VLWb!PL#jL$D;%1Mw2KG z^uSiaIQcjA7H~A$sg}Y)>qBn-BZEB`^3IQQ;O*R9l-D^UZjum3Vq9UWX0@gOJDV2o zUuQ?SQ?X8=kvSu%s3+)(3Klnw?&g5P7{!6iVNf|%ynQ?3al?Ah zR}XQcHNUDO+d26y_)WMGB-RepSb_dVJ%2Gb?k=eo`R0&$6qjuYJI;{??gtEi^_Xex zyHyScnAS{(LI#W#%f4YD4cL3_#HEQ2+)Ep+ohfG%#f=>FKj0DI?EXj|WArc_i(1jw zmEt8WI%P;wFhs0)xh@ug*P#Yi-VzK#c#@)Kgt}mjxUJ0*c4ZG!f1=jZm9!o+*lg0@ zWsMRFC|>$PL>{_+VzhR7-|vdRYo!-o9J{@DW6DK!Aua@qK3@^$vs12XZ)z_Yz|?dY z6_5RBpL~4aBn0-C8T(ZiU5sI#F>`je**Wo!`-sY^%Jop!1gg(h$X za6HgFGQXtg#HPLNQo5WKz>d!BBjhY4m_rseHD#3)ZibcBtdY^szQTOsEnxaP%e^Yi z$8-E=_0f!2{>}}}>TIs1)QNelX@KI$Pb=Yj7fhwLiJ9bJU;{Wy|GwTnSv@3lh_H(6 zNZnnc|8F6CoM-;;dUOvs0BXMA2> z;;OgdCDKY&KC;fz5*VivmfpS{XA%#XF%`|{RBsB)nouM2`Y9gBuZ2H&r1VPJQJHU? zcphbgg|^4vb;nuKY1sq_q`x&im;C z3M@W*wKj5w9tQPSJ;Ej^xx%ivi1$~{WETu_7fleZwCa0S6l9Q*TpZbV{R%-qkIsp` zy?Fa>pzGVZmSe0d6U-DtBUFSXo17scEfq5-+9-EG6z{S^jOMF+maVV$ z<(LHrF`LncaX?ggi*kehWa5fBNijMb^gA;F`9hSdJBP*Aw(#<3?By+r_G=nX5vcrE z)|sLtsb5CSi_*d#gf16 z`wX(S#F(X3>}*}-#<4&?DOSGsI5MX>Sa{+kD?(wY>wEM(p8yF3_(dfH^!hfBUar!I znmBl0%9$_2roZpu`E56es_4%B>G5gbrXubX1MAq~Jna*-kzL8D075BVgz#LjLJYwu8&BG9rfxtWWSHab06K(Vq%7$li|AUYO{5 z&Qz`Zq*43PzDEV|C*Oxpq3*u{sucRBg#LSYZ6tiH3?u4}mGRTOhZs;L_aRhH!!+gi zT6Gmqxih0eV!Khs6Ke1oFT@HTRDyj+dY^JfDF^69)7Fdrl7OD9xp=+YmTO~s@j*@1 z{O_CZ8&PcRrtt3=8)hP{G6o>cg+^#WhTF%lDC3Ntm8zaChg+(ILV)5Gwr8#QJQi=$ zpIdU|#yAHB{d5Iw6Yi*O{oy;Y=bJdIg+a(7v}Coz&(@9^(*EuI`07zE^! z?kYXaVzHmf_hYH7)DXIm$digiUUyTd-!2NR0`w&*QDEtKrz3nlo>$oAs1MrDEY@VR zg-%p#z%FSQJ2vD9FNXxHOplbcc&%S^+b?dAN0;KY{VrC?ds_P7(Rs$NmfSSxHK-{l zY!G5%%o<()?r5QMz0~cJzRxF;1cCgr&?*Es*u^6+U*>y@1^aAM$Fe#4GPkY3ZV8W4 ze0l%n$@TFSbZZ9CuJ=1%?D@^>`X{_|=Mi(C_8S|Wg3Ge0jpr4Dxn2UpXl|=JSD_$i z8HYB?7%@ca@o2_2A+3W5)59lAMPU^-5<6L@|I7KcS-2*?WRLsxs)2V)q|?j-Pt8`E zPclOJ(ZWQ(@~@5}OB@Gw+k?f5dc_O9FGXEkRJkwKbS?-qXW1`Fc;97S6T4^-_)55cqv? zC+=7(lUZLDG=4o>pX{NipXJtk^ul?&KN3?a@|n0j>lcc5s)QDFUJ)7j%x$IGj#RW}kqM1kDKqN% z>}FweKFW2Pr&A9tC+75YDrg#vZUw2%d$mQ?n8ne%zbKHHh?T-qHJ21d;;k-9_dbu$ zHhgaL6bDZ}JP(($7gk(*ow!PMr@?T<}J$NPo6=u@IuMVup7nppjiiA?8xXOtKTwy}Q{+d3>ykzY` z$2YYb2U&4O5=A$=|#r;8PMEUm<_B7KEU%!xXv`=6A=$h<7;8QO|9+(_zWZhO*m z)62pvlZ@Stc;#b0T}}zURF%^A^wHn%?J+$5{tfVIrX(F=emh=5J`Tl1n6+|l%C4;v zsaHQALfiCz6(Hw3%0|HYQ~q&fH)u;td|1awWvGDHcZY(M|r?SPV$?+p88&jFbo{f0u5`;C;5QBe3wLA zrH?HB$-Oo}WQp+9NxBMi4vF4EF?G+vPtAey2f}}iL!u_O9_iBv2hDc;^h);! zk*Q5)?%OfwSs6;(rL!aLvGy0AT;9ntf)%@sQ{3p|HALPg`S+5Pg#-^If&CxLr=df+ zxHX~zXvwBqTJ5q;`NTtXrIcYW7TVP4H+afEz|OOc8tBj{$JDv35qa3ii*D|r0Z2W! z$uL?mwlFpOv}wnG*frKjMiQ+#9semz*v&01SDwi(GxW&wO-7FFN4CWKw42~?cXYuY z!-t8>D@C*lE54S-6Q`*P$_oQ@yPDxN7rnDv_D#7c(&6r}9^c9yK0n3%Dq!48o-_w; z$q;W*e@(IUr57?)F#TYos$cK4wiSTv<|zM^d(#t;+MhaE=WUKhRN&gOjGC0_v#O?`Ctqw>uV8 zXBzcsFT+_0-^y}45N4^&M;1Y_LK!6~`it;E+(|=2OveTXb=}<`n=cb%aGX%?MbWCU zOrStUD(S#@Q85(tAh1$tO3C2e3EI6tUihB85gtS>pRxOh+eHGRBE_b+r>^b3YU zJ!vy_X^!Qu)Z1r-=O3aLA2UA|ll_KlBJ zh|ab1q3&M}ok|}1)aX=3_L#RcfGR)PCX)H@v7-NmKz$bIR!w?Ko_v1Y8FLO3w?v^_ zbHhlK)HO8w)-x;8fDyr9M5_#E@rnqUtg00naL_kK5zk%6x0CjNL+bb=3BAcDB4I5| z`Nbp+7E2d_@{fBC+fk4+J^}Shj}#SUVTJ?qtG;ggOB}H#qIv^8{1RBXzzYMn@Be(L zj*8XM9U-na+VyJlKwx+d6BT>Q5sAIn)LHknWCwTAP25C;0h>Y$l{THQ+8Hw z^Yc6Oh1^WjM@^IYn&(oEBYSOwa2B{|DJ_c6eF?rz3v-VuDmLr1x}k#M637@wIwuK> zW;QQQs}o1hO*m3W|Lj=xoctSpRtmo?J^K0$YyWo#VZ~>q0lg4|!5g;z5^!Sp1zm&< z$q1Y!5q*EV|L00S@$Ie)Fg@G=oUVkhsq6o+FS5VAUwpfr^z#;1mJs6~8DHM-m&}3G z3$B3~3?Fn095KF(VnuiQ3zGJZ)IUk=IK}^@IR(G_G_X}P#7Fc};P5IgU0(U+Y$>$& z@aS2Bpuy)=U+8}nPn90_?b85L`#+f_w%9kDkOE9zUE5y8Z@Fz28|OR3gJge#|NB4( zA;*pu+gzgc3jWMh1-?|2?#+DMwq6HP#g^Wc)Ej#}&O6!;N>W)s7cXOS5@FQ$A)~VL zEb*7dYb6E>vKukG(h2Z`Vptwmxn+`o#~#~`}EC!3iwdnmU5rG^W$bVpT%l?o40p2 z=MACwf4CZpILK!l&No$9QmW{FDJ%BAQPmk1Yj*wI!#>7RzZTD81#^;7Fi92J-8sBA z$(0*UQ)|el9Rza*Gh)Q2jSPP9+Nq~v<)jm{P?~hZX?>P!bmsfUOHdKCOVk^F>$J(- zs7@rtKYbWOgv+hfn{6O8z3FaB?%B*ZPpOYN5}4)E+k>+ImW9LZ2Y!;qcUFQ!OE7ZM zpZ)G^JZ!*)t|it}-FtFCWd!#iij|dauJf2IvOGj|B`B3rfnKpT@rKr<%#RN3ExU`J z`ffWF{l~bmWbUs}O=4@&v;jM_u4Kp*B$d*Z)U$+j8E=nR>kBQBoz(uErGK5()l8&h zUbil?#0UA92450q&6(n=0K{BG`tBiwHAM*1&g%&0kO1I-YUf3h;E!6zSrq zEse0YIR7;-uUH7Ws$Ts;AG9o$u*AnCyvg}M<;77Q5Sr5vi<-AU*WNQj0yr#9DWMNq z1e8iu%AFILj0^bPISEWN7IZ4(I;TYHl6?D`Yoh9r+3z`7266G~F2gR}w!>-+m+ATP zSzNpq){rBaRUV}tp-bZ9c-3;(oW;_*5XM_e3Lw|h^jXDj72^p$(e1xmG9dI=6!tvX z)VPN(2mhC?@UQuv$xij0QjRYdCkO-Z!#JaaMae{{Fr(I%{U&A&M_!#l#^xO+Big@Z zvF~G>8mBf-C(mX$sS?AI{BV9W!Y{p>W7TMJj2GnVw}-D@lrrsz9U&LUb=8amP43wz ztx|E*kSC&@Sv=A*b-zU(bl~o^D{@MJOnV}i{(S)gUW=eEbFU+0BAEbkc*Y=|fSl^2 zocKLr!=xNW+}Mv_fK{P`>2SIeh&?N)YU8in65)Gwr9PBZjze?q>pOu#W)}a5Eur+x zh2j1Kqq)ZFv1YOvFC%JI_>vyPejq^lK~&szC)yN#pv<%tE1VcknUp9*7GK&=vjE5d z3$s(eN>koG;dtG#6)k6LhZicQcH9F`Hrq18T}=VxON_4h-_K+9gXXUhmxAzVtp=ko zgf~n{w9|{>1uq*(%P->(ov9?jl(aNhHFB>gJIV;p%xSAMztSwA%j;0^VS9v&1R<#x zt7ofgoWJ)?UR5_CpNKG^h!IB33hh9iiK?^!6{6)KxiNM|weFj)49C_*x^wX3cx8m< z6YLtB1P?ncvv#V9q05HOTINOP+s>9Q?FoW&%EU29zDHZd%K_8n(C3P0q^EfvcAdLp z=jMjz*MvrUF{~YRTJ=F=Iuw-CB(bhn+~|=WVjN$*d|3x-!kp+8?WNZjY8Qu1E4uo;IG0>zE5nm_ z*M1_ECw_~o+zjv7V(HeB5A>-PJ_RLeQ?SRvwR`;F>m4H}mx(p3u^nsueH@n}8Quy= zt+zbNzfW85EIUSiu!Q~JnBylUMN=I4$UNGsVOh#|a$WkWJI2?Ob=Qa&z!ZJigCVUJ z;i_X8>JpED<6&!H%MmXK(p43;G4R~eN$8rI)6bR@h&8`PQjaB&v_2nWxZn%zqmI^ePAQ6rrO?6ylUkW(Gbo=W}#I>Joj)kE}JDq`oJHjrWthxU@`e3?lo6y)3%=hEP z5oLP!t#mUX8MmF6Z@ZwAAPb;fq_yC4MlNL>QAp1>=~Dqc)9;hXBQd=gh(numN5zjA zjqFmbiUdn$mZK7N&u7~7;#yuk(OwDR!&Z)}C}SAQr_Qko68-Xh+tGZTzeZ75QBN=x zrr98C@{twXW(W$8Fa(yN6U>hOPjZrzH!dfroD2(6Xk!Kx$$m-c9CDmEpBK>nsb7qh z#Z7=g6Hed)6j1Usgtrn}&gYiI?b&-H*H(ZI^6rZ^VzIiV-HB>K2P^UEsYk6AA4F~c z&p0JdetH#e+M7o>)pm`@=p)dGR}*%*RN4}2FZFL4${1*6Q>cb!gUy=x$hf_|MAHax z&YY|u>2=;e^>JlZcG!602*p}tjmr?Hc@Cu;JJP>>$1x+qg>C<}yU9ROdmYm-#a86} z8}~0~GUc;BazY9e!RqOuv-$1~y_85DKHK{lAYrqxC^IFBJ9a@5PW7rha!`gp#d$*d zxS-KYM%?-3y=}r<0tU;W{y~ael6Lr|)Iq-OI#3gT#866egj&bkT^`(&A<8hC8|31F^A zF)4ki)I~!U5Pii7?;7N(qx|0nf$6JjM0ZGD7I3<;`0zMGb^1@<%*31B<)MErk+cF{ zL@s7^4b)4t1%H^CN9%?79i+cq#Bq}Sel@3W(ypVpEL91LVebNSiZ=Wv{L!FtjC^In zf1zOcTuyUhGW!{=^8Qk7_FI9 z)X|M-RW>}RmFOZtj$3lKfc>~G+(yTZ7_R+wkPPE!khP2v@j714pZIY4@%~`Tx+x`0 zxMmo<_Yst__Fhlcl}SL;CPOAcZsqEY1b@$wx_KG$>&`Tqx3{SOGSnZRg?bwfC} z6OTltnMY40@ncd7Ja*h|mRaw90~{PCw@WLAm^a37!@=%;@`t?PmyZ93GGf%|DEc+AezAG1^u54hXb{^EU^3Xd)PzjB)< zdJpHGqDNX6lhUoa{C}ezMw_LcW1jbygC;G(llJrROmp2=F0}1LQ}$Yfjh~Ho;3&zj zjD0nuTK)LavZsB2xs6-hHMe|N7<-KDH-s%8AZq===_{kFw(N*?Hhfe26$u`7A;o&O ziZm{+Uw=T(`cLD3h^726z&gJ!l&d1mT!2EH3?JZst)$SkH7Z?s$jz*JFPrWu;VR7T zS}=QejYwUN8jc*LKh{@y^~SDffVfr_`f!VuxG^iY@at}0#L_4XcIF#vV8{VC%qK+! zUAp=Tkk$g))%X8rzkxl&g&6!wWH5064E0tl9(%l#kdNL6zz+K1<8mkBKmovBFTwrC z9ry+F{fIYeQ`r6@MK3ksrM~F0l#h>SR0UO0GT*NkBcofZ8Bq^9QD Ws&$Lm2>(wI1Q|(1i3)K;|Nj9$A)18% literal 22870 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 From e6b620d2022bc98832c25e0dbd6147228ff77207 Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 3 Dec 2018 22:55:29 +0800 Subject: [PATCH 13/16] Fix route bug --- libs/maps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/maps.js b/libs/maps.js index 7f0375f4..45ca5c7b 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -1098,7 +1098,7 @@ maps.prototype.animateBlock = function (loc,type,time,callback) { if (alpha >=1 || alpha<=0) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); - core.setAlpha('curtain', 1); + core.setAlpha('route', 1); if (type == 'show') { loc.forEach(function (t) { core.showBlock(t[0],t[1],data.floorId); From 813087f852a9bd9296f851f139206fa5e49adeff Mon Sep 17 00:00:00 2001 From: oc Date: Mon, 3 Dec 2018 23:01:52 +0800 Subject: [PATCH 14/16] Toolbar button 1-7 --- docs/personalization.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/personalization.md b/docs/personalization.md index 6529ed3b..153ee029 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -577,6 +577,22 @@ this.myfunc = function(x) { 标题界面事件全部处理完后,将再继续执行startText事件。 +## 手机端按键模式 + +从V2.5.3以后,我们可以给手机端增加按键了,这样将非常有利于技能的释放。 + +当用户在竖屏模式下点击工具栏,就会在工具栏按钮和快捷键模式之间进行切换。 + +切换到快捷键模式后,可以点1-7,分别等价于在电脑端按键1-7。 + +可以在脚本编辑的onKeyUp中定义每个快捷键的使用效果,比如使用道具或释放技能等。 + +默认值下,1使用破,2使用炸,3使用飞,4使用其他存在的道具,5-7未定义。可以相应修改成自己的效果。 + +也可以替换icons.png中的对应图标,以及修改main.js中`main.statusBar.image.btn1~7`中的onclick事件来自定义按钮和对应按键。 + +非竖屏模式下、回放录像中、隐藏状态栏中,将不允许进行切换。 + ## 自定义状态栏(新增显示项) 在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground)等等。 From 6b371c305f84ecbdb57b7aaf948b095ae04f091d Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 4 Dec 2018 01:07:55 +0800 Subject: [PATCH 15/16] Fix Bug --- editor-mobile.html | 7 +++++++ editor.html | 7 +++++++ libs/control.js | 1 + 常用工具/地图生成器.exe | Bin 20480 -> 20480 bytes 4 files changed, 15 insertions(+) diff --git a/editor-mobile.html b/editor-mobile.html index 04682f39..89b98714 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -413,6 +413,13 @@ + + + + + + +

diff --git a/editor.html b/editor.html index 58fe020e..b5f76989 100644 --- a/editor.html +++ b/editor.html @@ -399,6 +399,13 @@ + + + + + + +

diff --git a/libs/control.js b/libs/control.js index d81ae7bd..56bdff04 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2789,6 +2789,7 @@ control.prototype.triggerStatusBar = function (name) { for (var i = 0; i < statusItems.length; ++i) statusItems[i].style.opacity = 1; this.setToolbarButton(false); + core.dom.tools.hard.style.display = 'block'; } } diff --git a/常用工具/地图生成器.exe b/常用工具/地图生成器.exe index 9851040459ed0e01f1a304547016044d53a0f538..a17d95e4699dab935627077902c645c458d8a0f1 100644 GIT binary patch delta 4298 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 K_TM;jd-%U(gY-`T From 5b34fbe47ef18adc25ea3e339bd3778716d595c7 Mon Sep 17 00:00:00 2001 From: oc Date: Tue, 4 Dec 2018 03:02:02 +0800 Subject: [PATCH 16/16] setOpacity -> setAlpha --- libs/actions.js | 2 -- libs/events.js | 33 ++++++++++++++++++--------------- libs/maps.js | 5 +++-- libs/ui.js | 41 ++++++++++++++--------------------------- 4 files changed, 35 insertions(+), 46 deletions(-) diff --git a/libs/actions.js b/libs/actions.js index d4d21634..5b8a3ede 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1002,7 +1002,6 @@ actions.prototype.clickViewMaps = function (x,y) { } else if (x>=2 && x<=10 && y>=5 && y<=7) { core.clearMap('data'); - core.setOpacity('data', 1); core.ui.closePanel(); } } @@ -1031,7 +1030,6 @@ actions.prototype.keyUpViewMaps = function (keycode) { if (keycode==27 || keycode==13 || keycode==32 || (!core.status.replay.replaying && keycode==67)) { core.clearMap('data'); - core.setOpacity('data', 1); core.ui.closePanel(); return; } diff --git a/libs/events.js b/libs/events.js index 70c9a013..a1a3184a 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1531,33 +1531,37 @@ events.prototype.animateImage = function (type, image, loc, time, keep, callback } clearInterval(core.interval.tipAnimate); - core.setAlpha('data', 1); - var opacityVal = 0; - if (type == 'hide') opacityVal = 1; + var alpha = 0; + if (type == 'hide') alpha = 1; + + var x = core.calValue(loc[0]), y = core.calValue(loc[1]); if (type == 'hide' && keep) { - core.clearMap('image'); + core.clearMap('image', x, y, image.width, image.height); } - - core.setOpacity('data', opacityVal); - var x = core.calValue(loc[0]), y = core.calValue(loc[1]); + core.setAlpha('data', alpha); core.canvas.data.drawImage(image, x, y); + core.setAlpha('data', 1); + // core.status.replay.animate=true; var animate = setInterval(function () { - if (type=='show') opacityVal += 0.1; - else opacityVal -= 0.1; - core.setOpacity('data', opacityVal); - if (opacityVal >=1 || opacityVal<=0) { + if (type=='show') alpha += 0.1; + else alpha -= 0.1; + core.clearMap('data', x, y, image.width, image.height); + if (alpha >=1 || alpha<=0) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); if (type == 'show' && keep) core.canvas.image.drawImage(image, x, y); - core.clearMap('data'); - core.setOpacity('data', 1); - // core.status.replay.animate=false; + core.setAlpha('data', 1); if (core.isset(callback)) callback(); } + else { + core.setAlpha('data', alpha); + core.canvas.data.drawImage(image, x, y); + core.setAlpha('data', 1); + } }, time / 10); core.animateFrame.asyncId[animate] = true; @@ -1568,7 +1572,6 @@ events.prototype.moveImage = function (image, from, to, time, keep, callback) { time = time || 1000; clearInterval(core.interval.tipAnimate); core.setAlpha('data', 1); - core.setOpacity('data', 1); var width = image.width, height = image.height; diff --git a/libs/maps.js b/libs/maps.js index 45ca5c7b..2c9c5f83 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -870,7 +870,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { core.setBlock(id, nowX/32, nowY/32); core.showBlock(nowX/32, nowY/32); } - // core.status.replay.animate=false; + core.setAlpha('route',1); if (core.isset(callback)) callback(); } else { @@ -1009,7 +1009,7 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); core.clearMap('route'); - core.setOpacity('route', 1); + core.setAlpha('route', 1); if (keep) { core.setBlock(id, ex, ey); core.showBlock(ex, ey); @@ -1351,6 +1351,7 @@ maps.prototype.drawAnimateFrame = function (animate, centerX, centerY, index) { core.canvas.animate.drawImage(image, -realWidth/2 - core.bigmap.offsetX, -realHeight/2 - core.bigmap.offsetY, realWidth, realHeight); core.loadCanvas('animate'); } + core.setAlpha('animate', 1); }) } diff --git a/libs/ui.js b/libs/ui.js index 938b29b7..635d0221 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -144,7 +144,7 @@ ui.prototype.setAlpha = function (map, alpha) { else core.canvas[map].globalAlpha = alpha; } -////// 设置某个canvas的透明度 ////// +////// 设置某个canvas的透明度;尽量不要使用本函数,而是全部换成setAlpha实现 ////// ui.prototype.setOpacity = function (map, opacity) { if (map == 'all') { for (var m in core.canvas) { @@ -186,10 +186,9 @@ ui.prototype.closePanel = function () { ////// 左上角绘制一段提示 ////// ui.prototype.drawTip = function (text, itemIcon) { - var textX, textY, width, height, hide = false, opacityVal = 0; + var textX, textY, width, height, hide = false, alpha = 0; clearInterval(core.interval.tipAnimate); core.setFont('data', "16px Arial"); - core.setOpacity('data', 0); core.canvas.data.textAlign = 'left'; if (!core.isset(itemIcon)) { textX = 16; @@ -205,22 +204,22 @@ ui.prototype.drawTip = function (text, itemIcon) { } core.interval.tipAnimate = window.setInterval(function () { if (hide) { - opacityVal -= 0.1; + alpha -= 0.1; } else { - opacityVal += 0.1; + alpha += 0.1; } - core.setOpacity('data', opacityVal); - core.clearMap('data', 5, 5, 400, height); + core.clearMap('data', 5, 5, 416, height); + core.setAlpha('data', alpha); core.fillRect('data', 5, 5, width, height, '#000'); if (core.isset(itemIcon)) { core.canvas.data.drawImage(core.material.images.items, 0, itemIcon * 32, 32, 32, 10, 8, 32, 32); } core.fillText('data', text, textX + 5, textY + 15, '#fff'); - if (opacityVal > 0.6 || opacityVal < 0) { + core.setAlpha('data', 1); + if (alpha > 0.6 || alpha < 0) { if (hide) { - core.clearMap('data', 5, 5, 400, height); - core.setOpacity('data', 1); + core.clearMap('data', 5, 5, 416, height); clearInterval(core.interval.tipAnimate); return; } @@ -231,8 +230,7 @@ ui.prototype.drawTip = function (text, itemIcon) { core.timeout.getItemTipTimeout = null; }, 750); } - opacityVal = 0.6; - core.setOpacity('data', opacityVal); + alpha = 0.6; } } }, 30); @@ -1032,16 +1030,13 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { var top = (416-height)/2, bottom = height; - // var left = 97, top = 64, right = 416 - 2 * left, bottom = 416 - 2 * top; - core.setAlpha('ui', 0.85); - core.fillRect('ui', left, top, right, bottom, '#000000'); + core.fillRect('ui', left, top, right, bottom, 'rgba(0,0,0,0.85)'); core.setAlpha('ui', 1); core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); core.clearMap('data'); clearInterval(core.interval.tipAnimate); core.setAlpha('data', 1); - core.setOpacity('data', 1); core.status.boxAnimateObjs = []; var globalFont = core.status.globalAttribute.font; @@ -1388,7 +1383,6 @@ ui.prototype.drawBook = function (index) { clearInterval(core.interval.tipAnimate); core.clearMap('data'); - core.setOpacity('data', 1); core.clearMap('ui'); core.setAlpha('ui', 1); @@ -1624,7 +1618,6 @@ ui.prototype.drawBookDetail = function (index) { clearInterval(core.interval.tipAnimate); core.clearMap('data'); - core.setOpacity('data', 1); var left=10, right=416-2*left; var content_left = left + 25; @@ -1710,8 +1703,7 @@ ui.prototype.drawMaps = function (index, x, y) { core.setAlpha('ui', 1); core.clearMap('animate'); - core.setOpacity('animate', 0.4); - core.fillRect('animate', 0, 0, 416, 416, '#000000'); + core.fillRect('animate', 0, 0, 416, 416, 'rgba(0,0,0,0.4)'); core.strokeRect('ui', 66, 2, 284, 60, "#FFD700", 4); core.strokeRect('ui', 2, 66, 60, 284); @@ -1752,7 +1744,6 @@ ui.prototype.drawMaps = function (index, x, y) { } core.clearMap('animate'); - core.setOpacity('animate', 1); var damage = (core.status.event.data||{}).damage, paint = (core.status.event.data||{}).paint; var all = (core.status.event.data||{}).all; @@ -1793,17 +1784,14 @@ ui.prototype.drawMaps = function (index, x, y) { } core.clearMap('data'); - core.setOpacity('data', 0.2); core.canvas.data.textAlign = 'left'; core.setFont('data', '16px Arial'); var text = core.status.maps[floorId].title; if (!all && (mw>13 || mh>13)) text+=" ["+(x-6)+","+(y-6)+"]"; var textX = 16, textY = 18, width = textX + core.canvas.data.measureText(text).width + 16, height = 42; - core.fillRect('data', 5, 5, width, height, '#000'); - core.setOpacity('data', 0.4); - core.fillText('data', text, textX + 5, textY + 15, '#fff'); - + core.fillRect('data', 5, 5, width, height, 'rgba(0,0,0,0.4)'); + core.fillText('data', text, textX + 5, textY + 15, 'rgba(255,255,255,0.6)'); } ////// 绘制道具栏 ////// @@ -2603,7 +2591,6 @@ ui.prototype.drawPaint = function () { core.clearMap('route'); core.setAlpha('route', 1); - core.setOpacity('route', 1); // 将已有的内容绘制到route上 var value = core.paint[core.status.floorId];