diff --git a/README.md b/README.md index 8721c47f..690e8ee4 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! * [x] \r[red]可以动态调整剧情文本的颜色 * [x] 升级事件改用事件编辑器完成 * [x] 每层楼都增添该层的并行事件处理 -* [x] 新增快捷键:N返回标题;P查看评论;O打开工程 +* [x] 新增快捷键:N返回标题;P游戏主页;O打开工程 * [x] 新增事件:设置全局属性或全局数值 * [x] 新增事件:隐藏/显示状态栏 * [x] 道具可以设置是否在回放时绘制道具栏或直接使用 diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index efa8695b..09610c7d 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -270,6 +270,7 @@ action | showGif_1_s | setFg_0_s | setFg_1_s + | screenFlash_s | setWeather_s | move_s | moveHero_s @@ -1155,6 +1156,29 @@ var code = '{"type": "setFg"'+Int_0 +async+'},\n'; return code; */; +screenFlash_s + : '画面闪烁' Number ',' Number ',' Number '强度' Number '单次时间' Int '执行次数' Int? '不等待执行完毕' Bool Newline + +/* screenFlash_s +tooltip : screenFlash: 画面闪烁,动画时间可不填 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=screenFlash%EF%BC%9A%E7%94%BB%E9%9D%A2%E9%97%AA%E7%83%81 +default : [255,255,255,1,500,1,false] +colour : this.soundColor +var limit = function(v,min,max) { + if(v>max) return max; + if(v0) { - 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) { var cv = core.isset(block.name)?core.canvas[block.name]:core.canvas.event; cv.clearRect(block.x * 32, block.y * 32, 32, 32); if (core.isset(block.name)) { if (block.name == 'bg') { - core.drawImage('bg', core.material.images.terrains, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + core.drawImage('bg', core.material.groundCanvas.canvas, block.x * 32, block.y * 32); } core.drawAutotile(cv, core.status.autotileAnimateObjs[block.name+"map"], block, 32, 0, 0, core.status.autotileAnimateObjs.status); } @@ -1335,7 +1333,7 @@ control.prototype.setFg = function(color, time, callback) { core.status.curtainColor = [0,0,0,0]; } - var fromColor = core.status.curtainColor; + var nowColor = core.status.curtainColor; if (!core.isset(color)) color = [0,0,0,0]; @@ -1351,19 +1349,20 @@ control.prototype.setFg = function(color, time, callback) { return; } - var per_time = 10, step=0, steps = parseInt(time / per_time); + var per_time = 10, step = parseInt(time / per_time); var changeAnimate = setInterval(function() { - step++; - - var nowA = fromColor[3]+(color[3]-fromColor[3])*step/steps; - var nowR = parseInt(fromColor[0]+(color[0]-fromColor[0])*step/steps); - var nowG = parseInt(fromColor[1]+(color[1]-fromColor[1])*step/steps); - var nowB = parseInt(fromColor[2]+(color[2]-fromColor[2])*step/steps); + nowColor = [ + parseInt(nowColor[0]*(step-1)+color[0])/step, + parseInt(nowColor[1]*(step-1)+color[1])/step, + parseInt(nowColor[2]*(step-1)+color[2])/step, + (nowColor[3]*(step-1)+color[3])/step, + ]; core.clearMap('curtain'); - core.fillRect('curtain', 0, 0, 416, 416, core.arrayToRGBA([nowR,nowG,nowB,nowA])); + core.fillRect('curtain', 0, 0, 416, 416, core.arrayToRGBA(nowColor)); + step--; - if (step>=steps) { + if (step <= 0) { delete core.animateFrame.asyncId[changeAnimate]; clearInterval(changeAnimate); core.status.curtainColor = color; @@ -1375,6 +1374,22 @@ control.prototype.setFg = function(color, time, callback) { core.animateFrame.asyncId[changeAnimate] = true; } +////// 画面闪烁 ////// +control.prototype.screenFlash = function (color, time, times, callback) { + times = times || 1; + time = time/3; + var nowColor = core.clone(core.status.curtainColor); + core.setFg(color, time, function() { + core.setFg(nowColor, time * 2, function() { + if (times > 1) + core.screenFlash(color, time * 3, times - 1, callback); + else { + if (core.isset(callback)) callback(); + } + }); + }); +} + ////// 更新全地图显伤 ////// control.prototype.updateDamage = function (floorId, canvas) { floorId = floorId || core.status.floorId; @@ -2077,10 +2092,10 @@ control.prototype.doSL = function (id, type) { }, function(err) { console.info(err); if (core.platform.useLocalForage) { - alert("存档失败,请将控制台的报错信息反馈给管理员。"); + alert("存档失败,错误信息:\n"+err); } else { - alert("存档空间不足,请先使用垃圾存档清理工具进行清理!"); + alert("存档失败,错误信息:\n"+err+"\n建议使用垃圾存档清理工具进行清理!"); } }) return; @@ -2094,7 +2109,9 @@ control.prototype.doSL = function (id, type) { return; } if (core.isset(data.hashCode) && data.hashCode != core.utils.hashCode(data.hero)) { - alert("存档校验失败,请勿修改存档文件!"); + if (confirm("存档校验失败,请勿修改存档文件!\n你想回放此存档的录像吗?")) { + core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); + } return; } if (data.version != core.firstData.version) { diff --git a/libs/core.js b/libs/core.js index 98037f67..a77b8c06 100644 --- a/libs/core.js +++ b/libs/core.js @@ -333,11 +333,9 @@ core.prototype.init = function (coreData, callback) { core.flags.displayCritical = core.getLocalStorage('critical', core.flags.displayCritical); core.flags.displayExtraDamage = core.getLocalStorage('extraDamage', core.flags.displayExtraDamage); - core.material.ground = new Image(); - core.material.ground.onload = function () { - core.material.groundPattern = core.canvas.ui.createPattern(core.material.ground, "repeat"); - } - core.material.ground.src = "project/images/ground.png"; + core.material.groundCanvas = document.createElement('canvas').getContext('2d'); + core.material.groundCanvas.canvas.width = core.material.groundCanvas.canvas.height = 32; + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); core.animateFrame.weather.fog = new Image(); core.animateFrame.weather.fog.onerror = function () { @@ -345,6 +343,12 @@ core.prototype.init = function (coreData, callback) { } core.animateFrame.weather.fog.src = "project/images/fog.png"; + core.material.images.keyboard = new Image(); + core.material.images.keyboard.onerror = function () { + core.material.images.keyboard = null; + } + core.material.images.keyboard.src = "project/images/keyboard.png"; + core.bigmap.tempCanvas = document.createElement('canvas').getContext('2d'); core.loader.load(function () { @@ -632,8 +636,8 @@ core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback, } ////// 从名字获得画布 ////// -core.prototype.getContextByName = function (name) { - return core.ui.getContextByName(name); +core.prototype.getContextByName = function (canvas) { + return core.ui.getContextByName(canvas); } ////// 清除地图 ////// @@ -647,8 +651,8 @@ core.prototype.fillText = function (name, text, x, y, style, font) { } ////// 在某个canvas上绘制一段描边文字 ////// -core.prototype.fillBoldText = function (canvas, text, style, x, y, font) { - core.ui.fillBoldText(canvas, text, style , x, y, font); +core.prototype.fillBoldText = function (name, text, style, x, y, font) { + core.ui.fillBoldText(name, text, style , x, y, font); } ////// 在某个canvas上绘制一个矩形 ////// @@ -923,6 +927,11 @@ core.prototype.setFg = function(color, time, callback) { core.control.setFg(color, time, callback); } +////// 画面闪烁 ////// +core.prototype.screenFlash = function (color, time, times, callback) { + core.control.screenFlash(color, time, times, callback); +} + ////// 更新全地图显伤 ////// core.prototype.updateDamage = function () { core.control.updateDamage(); diff --git a/libs/events.js b/libs/events.js index f0aa8603..16e5e82e 100644 --- a/libs/events.js +++ b/libs/events.js @@ -822,6 +822,17 @@ events.prototype.doAction = function() { }); } break; + case "screenFlash": // 画面闪烁 + if (data.async) { + core.screenFlash(data.color, data.time, data.times); + this.doAction(); + } + else { + core.screenFlash(data.color, data.time, data.times, function() { + core.events.doAction(); + }); + } + break; case "setWeather": // 更改天气 core.setWeather(data.name, data.level); if (core.isset(data.name)) diff --git a/libs/maps.js b/libs/maps.js index 4da72796..cc2fdfb8 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -377,9 +377,7 @@ maps.prototype.drawBlock = function (block, animate, dx, dy) { 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.drawImage('bg', core.material.images.terrains, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); + core.drawImage('bg', core.material.groundCanvas.canvas, block.x * 32, block.y * 32); } core.canvas[block.name].drawImage(image, x * 32, y * 32, 32, 32, block.x * 32, block.y * 32, 32, 32); return; @@ -477,18 +475,22 @@ maps.prototype.drawMap = function (floorId, callback) { } core.clearMap('all'); + var groundId = (core.status.maps||core.floors)[floorId].defaultGround || "ground"; + core.material.groundCanvas.clearRect(0, 0, 32, 32); + core.material.groundCanvas.drawImage(core.material.images.terrains, 0, 32*core.material.icons.terrains[groundId], 32, 32, 0, 0, 32, 32); + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); + var drawBg = function(){ var width = core.floors[floorId].width || 13; var height = core.floors[floorId].height || 13; - var groundId = (core.status.maps||core.floors)[floorId].defaultGround || "ground"; - var blockIcon = core.material.icons.terrains[groundId]; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { - core.drawImage('bg', core.material.images.terrains, 0, blockIcon * 32, 32, 32, x * 32, y * 32, 32, 32); + core.drawImage('bg', core.material.groundCanvas.canvas, 32*x, 32*y); } } + // 获得image var images = []; if (core.isset(core.status.maps[floorId].images)) { images = core.status.maps[floorId].images; diff --git a/libs/ui.js b/libs/ui.js index 98b1512b..60d5303a 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -17,11 +17,16 @@ ui.prototype.init = function () { ////////////////// 地图设置 -ui.prototype.getContextByName = function (name) { - if (core.isset(core.canvas[name])) - return core.canvas[name]; - if (core.isset(core.dymCanvas[name])) - return core.dymCanvas[name]; +ui.prototype.getContextByName = function (canvas) { + if (typeof canvas == 'string') { + if (core.isset(core.canvas[canvas])) + canvas = core.canvas[canvas]; + else if (core.isset(core.dymCanvas[canvas])) + canvas = core.dymCanvas[canvas]; + } + if (core.isset(canvas) && core.isset(canvas.canvas)) { + return canvas; + } return null; } @@ -53,15 +58,17 @@ ui.prototype.fillText = function (name, text, x, y, style, font) { } ////// 在某个canvas上绘制粗体 ////// -ui.prototype.fillBoldText = function (canvas, text, style, x, y, font) { - if (core.isset(font)) canvas.font = font; - canvas.fillStyle = '#000000'; - canvas.fillText(text, x-1, y-1); - canvas.fillText(text, x-1, y+1); - canvas.fillText(text, x+1, y-1); - canvas.fillText(text, x+1, y+1); - canvas.fillStyle = style; - canvas.fillText(text, x, y); +ui.prototype.fillBoldText = function (name, text, style, x, y, font) { + var ctx = this.getContextByName(name); + if (!ctx) return; + if (core.isset(font)) ctx.font = font; + ctx.fillStyle = '#000000'; + ctx.fillText(text, x-1, y-1); + ctx.fillText(text, x-1, y+1); + ctx.fillText(text, x+1, y-1); + ctx.fillText(text, x+1, y+1); + ctx.fillStyle = style; + ctx.fillText(text, x, y); } ////// 在某个canvas上绘制一个矩形 ////// @@ -94,12 +101,11 @@ ui.prototype.drawLine = function (name, x1, y1, x2, y2, style, lineWidth) { core.setLineWidth(name, lineWidth); } var ctx = this.getContextByName(name); - if (ctx) { - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - } + if (!ctx) return; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); } ////// 在某个canvas上绘制一个箭头 ////// @@ -112,18 +118,17 @@ ui.prototype.drawArrow = function (name, x1, y1, x2, y2, style, lineWidth) { core.setLineWidth(name, lineWidth); } var ctx = this.getContextByName(name); - if (ctx) { - var head = 10; - var dx = x2-x1, dy=y2-y1; - var angle = Math.atan2(dy,dx); - ctx.beginPath(); - ctx.moveTo(x1,y1); - ctx.lineTo(x2, y2); - ctx.lineTo(x2-head*Math.cos(angle-Math.PI/6),y2-head*Math.sin(angle-Math.PI/6)); - ctx.moveTo(x2, y2); - ctx.lineTo(x2-head*Math.cos(angle+Math.PI/6),y2-head*Math.sin(angle+Math.PI/6)); - ctx.stroke(); - } + if (!ctx) return; + var head = 10; + var dx = x2-x1, dy=y2-y1; + var angle = Math.atan2(dy,dx); + ctx.beginPath(); + ctx.moveTo(x1,y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x2-head*Math.cos(angle-Math.PI/6),y2-head*Math.sin(angle-Math.PI/6)); + ctx.moveTo(x2, y2); + ctx.lineTo(x2-head*Math.cos(angle+Math.PI/6),y2-head*Math.sin(angle+Math.PI/6)); + ctx.stroke(); } ////// 设置某个canvas的文字字体 ////// @@ -200,14 +205,17 @@ ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1) { } // 只能接受2, 4, 8个参数 - if (core.isset(x) && core.isset(y) && core.isset(w) && core.isset(h) && core.isset(x1) && core.isset(y1) && core.isset(w1) && core.isset(h1)) { - ctx.drawImage(image, x, y, w, h, x1, y1, w1, h1); - } - else if (core.isset(x) && core.isset(y) && core.isset(w) && core.isset(h)) { - ctx.drawImage(image, x, y, w, h); - } - else if (core.isset(x) && core.isset(y)) { + if (core.isset(x) && core.isset(y)) { + if (core.isset(w) && core.isset(h)) { + if (core.isset(x1) && core.isset(y1) && core.isset(w1) && core.isset(h1)) { + ctx.drawImage(image, x, y, w, h, x1, y1, w1, h1); + return; + } + ctx.drawImage(image, x, y, w, h); + return; + } ctx.drawImage(image, x, y); + return; } } @@ -424,13 +432,12 @@ ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py // 仿RM窗口皮肤 ↓ var dstImage = core.getContextByName(canvas); if (!dstImage) return; - var dx = 0, dy = 0; // 绘制背景 dstImage.drawImage(background, 0, 0, 128, 128, x+2, y+2, w-4, h-4); // 绘制边框 // 上方 dstImage.drawImage(background, 128, 0, 16, 16, x, y, 16, 16); - for (dx = 0; dx < w - 64; dx += 32) { + for (var dx = 0; dx < w - 64; dx += 32) { dstImage.drawImage(background, 144, 0, 32, 16,x+dx+16, y, 32, 16); dstImage.drawImage(background, 144,48, 32, 16,x+dx+16, y+h-16, 32, 16); } @@ -438,7 +445,7 @@ ui.prototype.drawWindowSkin = function(background,canvas,x,y,w,h,direction,px,py dstImage.drawImage(background, 144,48,w-dx-32, 16,x+dx+16, y+h-16,w-dx-32, 16); dstImage.drawImage(background, 176, 0, 16, 16, x+w-16, y, 16, 16); // 左右 - for (dy = 0; dy < h - 64; dy += 32) { + for (var dy = 0; dy < h - 64; dy += 32) { dstImage.drawImage(background, 128,16, 16, 32, x,y+dy+16, 16, 32); dstImage.drawImage(background, 176,16, 16, 32, x+w-16,y+dy+16, 16, 32); } @@ -1478,7 +1485,7 @@ ui.prototype.drawReplay = function () { ui.prototype.drawGameInfo = function () { core.status.event.id = 'gameInfo'; this.drawChoices(null, [ - "数据统计", "查看工程", "查看评论", "操作帮助", "关于本塔","下载离线版本", "返回主菜单" + "数据统计", "查看工程", "游戏主页", "操作帮助", "关于本塔","下载离线版本", "返回主菜单" ]); } @@ -2499,16 +2506,37 @@ ui.prototype.drawKeyBoard = function () { core.clearLastEvent(); - var left = 16, top = 48, right = 416 - 2 * left, bottom = 416 - 2 * top; - core.fillRect('ui', left, top, right, bottom, core.material.groundPattern); - core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); + var left = 16, top = 48, width = 416 - 2 * left, height = 416 - 2 * top; + + var background = core.status.textAttribute.background; + var isWindowSkin = false; + if (typeof background == 'string') { + background = core.material.images.images[background]; + if (core.isset(background) && background.width==192 && background.height==128) isWindowSkin = true; + else background = core.initStatus.textAttribute.background; + } + if (!isWindowSkin) background = core.arrayToRGBA(background); + var borderColor = core.status.globalAttribute.borderColor; + var titleColor = core.arrayToRGBA(core.status.textAttribute.title); + var textColor = core.arrayToRGBA(core.status.textAttribute.text); + + core.clearMap('ui'); + if (isWindowSkin) { + core.setAlpha('ui', 0.85); + this.drawWindowSkin(background,'ui',left,top,width,height); + } + else { + core.fillRect('ui', left, top, width, height, background); + core.strokeRect('ui', left - 1, top - 1, width + 1, height + 1, borderColor, 2); + } + core.setAlpha('ui', 1); core.setTextAlign('ui', 'center'); var globalFont = core.status.globalAttribute.font; - core.fillText('ui', "虚拟键盘", 208, top+35, "#FFD700", "bold 22px "+globalFont); + core.fillText('ui', "虚拟键盘", 208, top+35, titleColor, "bold 22px "+globalFont); core.setFont('ui', '17px '+globalFont); - core.setFillStyle('ui', '#FFFFFF'); + core.setFillStyle('ui', textColor); var offset = 128-9; var lines = [ @@ -2519,7 +2547,7 @@ ui.prototype.drawKeyBoard = function () { ["Z","X","C","V","B","N","M"], ["-","=","[","]","\\",";","'",",",".","/","`"], ["ES","TA","CA","SH","CT","AL","SP","BS","EN","DE"] - ] + ]; lines.forEach(function (line) { for (var i=0;i