diff --git a/docs/element.md b/docs/element.md index bd743bef..75633a11 100644 --- a/docs/element.md +++ b/docs/element.md @@ -369,6 +369,20 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 如果录像出现问题,请加群539113091找小艾反馈Bug。 +## 绘图模式 + +从V2.5开始,样板提供了绘图模式,可以让玩家在画布上任意进行绘制,标记等。 + +使用M键,或在菜单栏中可以进入绘图模式。 + +**绘图的内容会自动保存,且以页面为生命周期,和存读档无关,返回标题并重新开始游戏后绘制的内容仍有效,但刷新页面就会消失。** + +你可以将绘制内容保存到文件,也可以从文件读取保存的绘制内容。 + +绘图模式下,状态栏的图标也会相应改变,铅笔为绘制模式,橡皮为擦除模式,存读档为保存和读取绘图文件,退出为返回默认值。 + +在浏览地图页面中也可以按楼传按钮或M键来开启/关闭该层的绘图显示。 + ## 操作说明 本塔主要支持鼠标(触摸屏)操作和键盘操作。 @@ -397,10 +411,15 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 - **[B]** 打开数据统计 - **[H]** 打开帮助页面 - **[R]** 回放录像 +- **[E]** 显示光标 - **[SPACE]** 轻按(仅在轻按开关打开时有效) +- **[M]** 绘图模式 +- **[PgUp/PgDn]** 浏览地图 - **[1]** 快捷使用破墙镐 - **[2]** 快捷使用炸弹/圣锤 - **[3]** 快捷使用中心对称飞行器 +- **[4]** 快捷使用其他道具 +- **[Alt+0~9]** 快捷换装 以上快捷键也能在游戏菜单中的操作说明中看到。 diff --git a/libs/actions.js b/libs/actions.js index 0546b0a4..5b4fff35 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -282,6 +282,9 @@ actions.prototype.keyUp = function(keyCode, altKey) { this.keyUpCenterFly(keyCode); return; } + if (core.status.event.id=='paint') { + this.keyUpPaint(keyCode); + } return; } @@ -1137,10 +1140,17 @@ actions.prototype.keyUpViewMaps = function (keycode) { core.clearMap('data'); core.setOpacity('data', 1); core.ui.closePanel(); + return; } if (keycode==86) { core.status.event.data.damage = !core.status.event.data.damage; - core.ui.drawMaps(core.status.event.data.index, core.status.event.data.x, core.status.event.data.y); + core.ui.drawMaps(core.status.event.data); + return; + } + if (keycode==77) { + core.status.event.data.paint = !core.status.event.data.paint; + core.ui.drawMaps(core.status.event.data); + return; } if (keycode==88 || (core.status.replay.replaying && keycode==67)) { if (core.isset(core.status.replay)&&core.status.replay.replaying) { @@ -1148,6 +1158,7 @@ actions.prototype.keyUpViewMaps = function (keycode) { } else { core.openBook(false); } + return; } return; } @@ -2531,17 +2542,28 @@ actions.prototype.clickAbout = function () { core.restart(); } -actions.prototype.ondownPaint = function (x, y) { - console.log("ondown: ("+x+","+y+")"); - core.canvas.route.beginPath(); - core.canvas.route.moveTo(x+core.bigmap.offsetX, y+core.bigmap.offsetY); +////// 绘图相关 ////// + +actions.prototype.ondownPaint = function (x, y) { + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (!core.status.event.data.erase) { + core.canvas.route.beginPath(); + core.canvas.route.moveTo(x, y); + } core.status.event.data.x = x; core.status.event.data.y = y; } actions.prototype.onmovePaint = function (x, y) { if (core.status.event.data.x==null) return; + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (core.status.event.data.erase) { + core.clearMap('route', x-10, y-10, 20, 20); + return; + } var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; core.canvas.route.quadraticCurveTo(midx, midy, x, y); core.canvas.route.stroke(); @@ -2550,13 +2572,83 @@ actions.prototype.onmovePaint = function (x, y) { } actions.prototype.onupPaint = function (x,y) { - console.log("onup: ("+x+","+y+")"); - var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; - core.canvas.route.quadraticCurveTo(midx, midy, x, y); - core.canvas.route.stroke(); + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (core.status.event.data.erase) { + core.clearMap('route', x-5, y-5, 10, 10); + } + else if (core.status.event.data.x!=null) { + var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; + core.canvas.route.quadraticCurveTo(midx, midy, x, y); + core.canvas.route.stroke(); + } core.status.event.data.x = null; core.status.event.data.y = null; // 保存 core.paint[core.status.floorId] = LZString.compress(core.utils.encodeCanvas(core.canvas.route).join(",")); } + +actions.prototype.setPaintMode = function (mode) { + if (mode == 'paint') core.status.event.data.erase = false; + else if (mode == 'erase') core.status.event.data.erase = true; + else return; + + core.drawTip("进入"+(core.status.event.data.erase?"擦除":"绘图")+"模式"); +} + +actions.prototype.savePaint = function () { + var data = {}; + for (var floorId in core.paint) { + if (core.isset(core.paint[floorId])) + data[floorId] = LZString.decompress(core.paint[floorId]); + } + core.download(core.firstData.name+".h5paint", JSON.stringify({ + 'name': core.firstData.name, + 'paint': data + })); +} + +actions.prototype.loadPaint = function () { + core.readFile(function (obj) { + if (obj.name!=core.firstData.name) { + alert("绘图文件和游戏不一致!"); + return; + } + if (!core.isset(obj.paint)) { + alert("无效的绘图文件!"); + return; + } + core.paint = {}; + for (var floorId in obj.paint) { + if (core.isset(obj.paint[floorId])) + core.paint[floorId] = LZString.compress(obj.paint[floorId]); + } + + core.clearMap('route'); + var value = core.paint[core.status.floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); + core.canvas.route.drawImage(core.bigmap.tempCanvas.canvas, 0, 0); + + core.drawTip("读取绘图文件成功"); + }) +} + +actions.prototype.exitPaint = function () { + core.clearMap('route'); + core.ui.closePanel(); + core.statusBar.image.shop.style.opacity = 1; + core.statusBar.image.toolbox.style.opacity = 1; + core.updateStatusBar(); + core.drawTip("退出绘图模式"); +} + +actions.prototype.keyUpPaint = function (keycode) { + if (keycode==27 || keycode==88 || keycode==77 || keycode==13 || keycode==32 || keycode==67) { + this.exitPaint(); + return; + } +} + +////// 绘图相关 END ////// diff --git a/libs/ui.js b/libs/ui.js index 2a620b81..9636e447 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1645,6 +1645,10 @@ 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; + if (core.isset(index.damage)) damage=index.damage; + if (core.isset(index.paint)) paint=index.paint; + if (core.isset(index.index)) { x=index.x; y=index.y; @@ -1661,19 +1665,27 @@ ui.prototype.drawMaps = function (index, x, y) { if (y<6) y=6; if (y>mh-7) y=mh-7; - core.status.event.data = {"index": index, "x": x, "y": y, "damage": (core.status.event.data||{"damage":false}).damage}; + core.status.event.data = {"index": index, "x": x, "y": y, "damage": damage, "paint": paint}; clearTimeout(core.interval.tipAnimate); core.clearMap('ui'); core.setAlpha('ui', 1); this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 0, 0, 416, x, y); + // 绘图 + if (core.status.event.data.paint) { + var offsetX = core.clamp(x-6, 0, mw-13), offsetY = core.clamp(y-6, 0, mh-13); + var value = core.paint[floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*mw, 32*mh); + core.canvas.ui.drawImage(core.bigmap.tempCanvas.canvas, offsetX*32, offsetY*32, 416, 416, 0, 0, 416, 416); + } + 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 (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; @@ -2420,52 +2432,61 @@ ui.prototype.drawAbout = function () { ////// 绘制“画图”界面 ////// ui.prototype.drawPaint = function () { - console.log("drawPaint"); + core.drawText( + "\t[进入绘图模式]你可以在此页面上任意进行绘图和标记操作。\nM键可以进入或退出此模式。\n\n"+ + "绘图的内容会自动保存,且以页面为生命周期,和存读档无关,重新开始游戏或读档后绘制的内容仍有效,但刷新页面就会消失。\n"+ + "你可以将绘制内容保存到文件,也可以从文件读取保存的绘制内容。\n"+ + "浏览地图页面可以按楼传按钮或M键来开启/关闭该层的绘图显示。\n\n更多功能请详见文档-元件-绘图模式。", + function () { + core.drawTip("打开绘图模式,现在可以任意在界面上绘图标记"); - core.drawTip("打开绘图模式,现在可以任意在界面上绘图标记"); + core.lockControl(); + core.status.event.id = 'paint'; + core.status.event.data = {"x": null, "y": null, "erase": false}; - core.lockControl(); - core.status.event.id = 'paint'; - core.status.event.data = {"x": null, "y": null, "erase": false}; + core.clearMap('ui'); + core.clearMap('route'); - core.clearMap('ui'); - core.clearMap('route'); + core.setAlpha('route', 1); + core.setOpacity('route', 1); - core.setAlpha('route', 1); - core.setOpacity('route', 1); + // 将已有的内容绘制到route上 + var value = core.paint[core.status.floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); + core.canvas.route.drawImage(core.bigmap.tempCanvas.canvas, 0, 0); - // 将已有的内容绘制到route上 - var value = core.paint[core.status.floorId]; - if (core.isset(value) && typeof value == 'string') value = LZString.decompress(value).split(","); - core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); - core.canvas.route.drawImage(core.bigmap.tempCanvas.canvas, 0, 0); + core.setLineWidth('route', 3); + core.setStrokeStyle('route', '#FF0000'); - core.setLineWidth('route', 3); - core.setStrokeStyle('route', '#FF0000'); + core.statusBar.image.shop.style.opacity = 0; + core.statusBar.image.toolbox.style.opacity = 0; + + core.statusBar.image.book.src = core.statusBar.icons.paint.src; + core.statusBar.image.fly.src = core.statusBar.icons.erase.src; + core.statusBar.image.settings.src = core.statusBar.icons.exit.src; + core.statusBar.image.book.style.opacity = 1; + core.statusBar.image.fly.style.opacity = 1; + } + ); } ////// 绘制帮助页面 ////// ui.prototype.drawHelp = function () { core.drawText([ "\t[键盘快捷键列表]"+ - "[CTRL] 跳过对话\n" + - "[Z] 转向\n" + - "[X] 打开/关闭怪物手册\n" + - "[G] 打开/关闭楼层传送器\n" + - "[A] 读取自动存档(回退)\n" + + "[CTRL] 跳过对话 [Z] 转向\n" + + "[X] 怪物手册 [G] 楼层传送\n" + + "[A] 读取自动存档 [S/D] 存读档页面\n" + "[S/D] 打开/关闭存/读档页面\n" + - "[K/V] 打开/关闭快捷商店选择列表\n" + - "[T] 打开/关闭工具栏\n" + - "[ESC] 打开/关闭系统菜单\n" + - "[B] 打开数据统计\n" + - // "[E] 显示光标\n" + - "[H] 打开帮助页面\n"+ - "[R] 回放\n"+ - "[SPACE] 轻按(仅在轻按开关打开时有效)\n" + + "[K/V] 快捷商店 [ESC] 系统菜单\n" + + "[T] 道具页面 [Q] 装备页面\n" + + "[B] 数据统计 [H] 帮助页面\n" + + "[R] 回放录像 [E] 显示光标\n" + + "[SPACE] 轻按 [M] 绘图模式\n" + "[PgUp/PgDn] 浏览地图\n"+ - "[1] 快捷使用破墙镐\n" + - "[2] 快捷使用炸弹/圣锤\n" + - "[3] 快捷使用中心对称飞行器", + "[1~4] 快捷使用破炸飞和其他道具\n"+ + "[Alt+0~9] 快捷换装", "\t[鼠标操作]"+ "点状态栏中图标: 进行对应的操作\n"+ "点任意块: 寻路并移动\n"+ @@ -2473,7 +2494,7 @@ ui.prototype.drawHelp = function () { "双击空地: 瞬间移动\n"+ "单击勇士: 转向\n"+ "双击勇士: 轻按(仅在轻按开关打开时有效)\n"+ - "长按任意位置:跳过剧情对话或打开虚拟键盘\n" + "长按任意位置:跳过剧情对话或打开虚拟键盘" ]); } diff --git a/libs/utils.js b/libs/utils.js index f5bd1ffd..b347f524 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -253,14 +253,14 @@ utils.prototype.cropImage = function (image, size) { ////// 格式化时间为字符串 ////// utils.prototype.formatDate = function(date) { if (!core.isset(date)) return ""; - return date.getFullYear()+"-"+core.setTwoDigits(date.getMonth()+1)+"-"+core.setTwoDigits(date.getDate())+" " + return ""+date.getFullYear()+"-"+core.setTwoDigits(date.getMonth()+1)+"-"+core.setTwoDigits(date.getDate())+" " +core.setTwoDigits(date.getHours())+":"+core.setTwoDigits(date.getMinutes())+":"+core.setTwoDigits(date.getSeconds()); } ////// 格式化时间为最简字符串 ////// utils.prototype.formatDate2 = function (date) { if (!core.isset(date)) return ""; - return date.getFullYear()+core.setTwoDigits(date.getMonth()+1)+core.setTwoDigits(date.getDate()) + return ""+date.getFullYear()+core.setTwoDigits(date.getMonth()+1)+core.setTwoDigits(date.getDate()) +core.setTwoDigits(date.getHours())+core.setTwoDigits(date.getMinutes())+core.setTwoDigits(date.getSeconds()); } @@ -460,6 +460,26 @@ utils.prototype.decodeBase64 = function (str) { }).join('')); } +////// 任意进制转换 ////// +utils.prototype.convertBase = function (str, fromBase, toBase) { + var map = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]\\|:;<>,.?/"; + if (fromBase==toBase) return str; + var len = str.length, ans=""; + var t = []; + for (var i=0;i0) { + for (var i=len; i>=1; i--) { + t[i-1]+=t[i]%toBase*fromBase; + t[i]=parseInt(t[i]/toBase); + } + ans+=map.charAt(t[0]%toBase); + t[0]=parseInt(t[0]/toBase); + while (len>0 && t[len-1]==0) len--; + } + return ans; +} + utils.prototype.__init_seed = function () { var rand = new Date().getTime()%34834795 + 3534; rand = this.__next_rand(rand); diff --git a/main.js b/main.js index 792cca37..1abb2b91 100644 --- a/main.js +++ b/main.js @@ -122,6 +122,11 @@ function main() { 'speedUp': 21, 'rewind': 22, 'equipbox': 23, + 'mana': 24, + 'skill': 25, + 'paint': 26, + 'erase': 27, + 'exit': 28, }, 'floor': document.getElementById('floor'), 'name': document.getElementById('name'), @@ -389,6 +394,11 @@ main.statusBar.image.book.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.setPaintMode('paint'); + return; + } + if (main.core.isPlaying()) main.core.openBook(true); } @@ -396,11 +406,27 @@ main.statusBar.image.book.onclick = function () { ////// 点击状态栏中的楼层传送器/装备栏时 ////// main.statusBar.image.fly.onclick = function () { + // 播放录像时 if (core.isset(core.status.replay) && core.status.replay.replaying) { core.stopReplay(); return; } + // 绘图模式 + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.setPaintMode('erase'); + return; + } + + // 浏览地图时 + if (main.core.isPlaying() && (core.status.event||{}).id=='viewMaps') { + if (core.isset(core.status.event.data)) { + core.status.event.data.paint = !core.status.event.data.paint; + core.ui.drawMaps(core.status.event.data); + } + return; + } + if (main.core.isPlaying()) { if (!main.core.flags.equipboxButton) { main.core.useFly(true); @@ -457,6 +483,11 @@ main.statusBar.image.save.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.savePaint(); + return; + } + if (main.core.isPlaying()) main.core.save(true); } @@ -469,6 +500,11 @@ main.statusBar.image.load.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.loadPaint(); + return; + } + if (main.core.isPlaying()) main.core.load(true); } @@ -481,6 +517,11 @@ main.statusBar.image.settings.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.exitPaint(); + return; + } + if (main.core.isPlaying()) main.core.openSettings(true); } diff --git a/project/images/icons.png b/project/images/icons.png index cbf4aee4..538a2c8f 100644 Binary files a/project/images/icons.png and b/project/images/icons.png differ