diff --git a/README.md b/README.md index 13b94127..406abacc 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,31 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 +### 2018.10.31 V2.5 + +* [x] 添加绘图模式支持;可以用户手动绘图和保存 +* [x] 内置主动技能:二倍斩的支持,可以仿照制作其他主动技能 +* [x] 将按键处理移动到脚本编辑中 +* [x] Alt+0\~9保存和读取当前套装 +* [x] 图块属性的cannotOut和cannotIn控制可通行方向(来造成悬崖效果) +* [x] 支持动态Autotile自动元件(仅在事件层有效) +* [x] 允许快捷商店使用共用的times +* [x] 未启用的快捷商店可以隐藏或预览 +* [x] 开始剧情startText可以执行任意事件 +* [x] 对话窗口可以任意调节位置(上中下、距离顶部/底部的像素值) +* [x] 楼层转换界面可以设置背景图片文字颜色等 +* [x] 数据统计进行分段描写,剑盾显示数值 +* [x] 现在可以在事件编辑器中注释内容了 +* [x] 存读档界面显示该存档的属性 +* [x] F7键可以开启debug模式 +* [x] R键可以从本地选取录像文件从头播放 +* [x] 吸血属性的显伤增加^;仇恨怪显示仇恨伤害 +* [x] 4键默认使用破冰稿或冰冻徽章或地震卷轴或上下楼器(依次判断是否存在) +* [x] 血瓶的道具化选项;黄宝石增加加点选项 +* [x] 破炸飞增加默认音效 +* [x] 修复单击瞬移的拖动打怪问题 +* [x] 其他细节优化 + ### 2018.10.27 V2.4.4 * [x] tilesets可以设置图块属性(如可通行状态) diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 892bd536..add43d86 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -63,19 +63,20 @@ return code; */; shopsub - : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND + : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '未开启状态则不显示在列表中' Bool BGNL? NewLine '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND /* shopsub tooltip : 全局商店,消耗填-1表示每个选项的消耗不同,正数表示消耗数值 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 -default : ["shop1","贪婪之神","blueShop","1F金币商店",false,null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] +default : ["shop1","贪婪之神","blueShop","1F金币商店",false,false,null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] var code = { 'id': IdString_0, 'name': EvalString_0, 'icon': IdString_1, 'textInList': EvalString_1, 'commonTimes': Bool_0, + 'mustEnable': Bool_1, 'use': ShopUse_List_0, 'need': EvalString_2, 'text': EvalString_3, @@ -182,6 +183,7 @@ return code; action : text_0_s | text_1_s + | comment_s | autoText_s | setText_s | tip_s @@ -282,6 +284,19 @@ var code = '"'+title+EvalString_1+EvalString_2+'",\n'; return code; */; +comment_s + : '添加注释' ':' EvalString Newline + + +/* comment_s +tooltip : comment:添加一段会被游戏跳过的注释内容 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=comment%ef%bc%9a%e6%b7%bb%e5%8a%a0%e6%b3%a8%e9%87%8a +default : ["可以在这里写添加任何注释内容"] +colour : this.commentColor +var code = '{"type": "comment", "text": "'+EvalString_0+'"},\n'; +return code; +*/; + autoText_s : '自动剧情文本: 标题' EvalString? '图像' IdString? '对话框效果' EvalString? '时间' Int BGNL? EvalString Newline @@ -1575,6 +1590,7 @@ this.evisitor.printColor=70; this.evisitor.dataColor=130; this.evisitor.eventColor=220; this.evisitor.soundColor=20; +this.evisitor.commentColor=285; */ /* Function_1 @@ -1639,7 +1655,7 @@ ActionParser.prototype.parse = function (obj,type) { choice.text,choice.need||'',text_effect,text_choices]); } return MotaActionBlocks['shopsub'].xmlText([ - obj.id,obj.name,obj.icon,obj.textInList,obj.commonTimes,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next + obj.id,obj.name,obj.icon,obj.textInList,obj.commonTimes,obj.mustEnable,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next ]); } var next=null; @@ -1704,6 +1720,9 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['autoText_s'].xmlText([ '','','',data.time,this.EvalString(data.text),this.next]); break; + case "comment": // 注释 + this.next = MotaActionBlocks['comment_s'].xmlText([data.text,this.next]); + break; case "setText": // 设置剧情文本的属性 var setTextfunc = function(a){return a?JSON.stringify(a).slice(1,-1):null;} data.title=setTextfunc(data.title); diff --git a/_server/data.comment.js b/_server/data.comment.js index 43080322..af1c0b4b 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -257,7 +257,7 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "event", "_event": "firstArrive", "_range": "thiseval==null || thiseval instanceof Array", - "_data": "游戏开始前剧情。\n可以双击进入事件编辑器。\n如果无剧情直接留一个空数组即可。" + "_data": "游戏开始前剧情,可以执行任意自定义事件。\n双击进入事件编辑器。\n如果无剧情直接留一个空数组即可。" }, "shops": { "_leaf": true, @@ -606,6 +606,12 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否允许瞬间移动" }, + "enableDisabledShop": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否允许查看未开启状态的快捷商店内容;如果此项为真,则对于未开启状态的商店允许查看其内容(但不能购买)" + }, } } } diff --git a/_server/editor.js b/_server/editor.js index e7cd0523..63ca9f47 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -390,7 +390,7 @@ editor.prototype.drawInitData = function (icons) { if (img == 'autotile') { var autotiles = images[img]; for (var im in autotiles) { - dc.drawImage(autotiles[im], nowx, nowy); + dc.drawImage(autotiles[im], 0, 0, 96, 128, nowx, nowy, 96, 128); nowy += autotiles[im].height; } nowx += 3 * 32; diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 4a12dd87..1c4b538b 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -56,6 +56,7 @@ editor_blockly = function () { '显示文字':[ MotaActionBlocks['text_0_s'].xmlText(), MotaActionBlocks['text_1_s'].xmlText(), + MotaActionBlocks['comment_s'].xmlText(), MotaActionFunctions.actionParser.parseList({"type": "choices", "text": "是否跳过剧情", "choices": [ {"text": "是", "action": []}, {"text": "否", "action": [ @@ -484,6 +485,7 @@ document.getElementById('blocklyDiv').onmousewheel = function(e){ 'text_0_s': 'EvalString_0', 'text_1_s': 'EvalString_2', 'autoText_s': 'EvalString_2', + 'comment_s': 'EvalString_0', 'choices_s': 'EvalString_0', 'function_s': 'RawEvalString_0', 'shopsub': 'EvalString_3', diff --git a/_server/editor_mode.js b/_server/editor_mode.js index 0c231b0a..6693d42e 100644 --- a/_server/editor_mode.js +++ b/_server/editor_mode.js @@ -805,10 +805,18 @@ editor_mode = function (editor) { var confirmAutotile = function () { if (sprite.width != 96 || sprite.height != 128) { - printe("不合法的Autotile图片!"); + if (sprite.height==128 && sprite.width%96==0) { + printe("这里只能导入单帧的自动元件,多帧的动画请先导入单帧自动元件再同名替换素材即可。"); + } + else { + printe("不合法的Autotile图片!"); + } + return; } - var imgbase64 = source.toDataURL().split(',')[1]; + var imgData = source_ctx.getImageData(0,0,sprite.width,sprite.height); + sprite_ctx.putImageData(imgData, 0, 0); + var imgbase64 = sprite.toDataURL().split(',')[1]; // Step 1: List文件名 fs.readdir('./project/images', function (err, data) { diff --git a/docs/V2.0.md b/docs/V2.0.md index 617af9f2..6c1039cf 100644 --- a/docs/V2.0.md +++ b/docs/V2.0.md @@ -1,6 +1,6 @@ # V2.0版本介绍 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 目前样板已经更新到V2.0版本以上,本章将对V2.0的一些内容进行介绍。 diff --git a/docs/api.md b/docs/api.md index 72335c95..03a5d666 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ # 附录: API列表 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * **这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** diff --git a/docs/element.md b/docs/element.md index 75633a11..087937c0 100644 --- a/docs/element.md +++ b/docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 diff --git a/docs/event.md b/docs/event.md index 6c475c36..915ca3a8 100644 --- a/docs/event.md +++ b/docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 @@ -327,6 +327,18 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 值得注意的是,提示的text内容也是可以使用`${ }`来计算表达式的值的。 +### comment:添加注释 + +使用`{"type": "comment"}`可以添加一段注释 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "comment", "text": "这是一段会被跳过的注释内容"} +] +``` + +这个事件将在运行时被游戏跳过。 + ### setValue:设置勇士的某个属性、道具个数,或某个变量/Flag的值 `{"type": "setValue"}` 能修改勇士的某个属性、道具个数、或某个自定义变量或`Flag`的值。 @@ -1587,6 +1599,8 @@ core.insertAction([ "icon": "blueShop", // 商店图标,blueShop为蓝色商店,pinkShop为粉色商店 "textInList": "1F金币商店", // 在快捷商店栏中显示的名称 "use": "money", // 商店所要使用的。只能是"money"或"experience"。 + "commonTimes": true, // 是否使用全局次数 + "mustEnable": true, // 如果未开启则不显示在状态栏中 "need": "20+10*times*(times+1)", // 商店需要的金币/经验数值;可以是一个表达式,以times作为参数计算。 // 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。 // 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式,只要以times作为参数即可。 @@ -1634,6 +1648,8 @@ core.insertAction([ - icon 为商店的图标,在icons.js的npcs中定义。如woman可代表一个商人。 - textInList 为其在快捷商店栏中显示的名称,如"3楼金币商店"等 - use 为消耗的类型,是金币(money)还是经验(experience)。 +- commonTimes 是否使用全局次数;如果为true则可以多个快捷商店共享相同的次数 +- mustEnable 是否必须是只在开启状态才在列表显示;如果此项为true则未开启的快捷商店不予显示 - need 是一个表达式,计算商店所需要用到的数值。 - 可以将times作为参数,times为该商店已经访问过的次数,第一次访问时times是0。 - 如果对于每个选项都需要不同的数值,这里设为"-1";可参见下面经验商店的例子。 @@ -1873,8 +1889,6 @@ core.insertAction([ 它将显示全塔属性中的startText内容(可以修改成自己的),提供战斗动画开启选择,设置初始福利,并正式开始游戏。 -全塔属性的startText只能使用纯文本类型,其他的事件均无效。 - 我们可以修改脚本编辑`setInitData`函数来对于不同难度分别设置初始属性。 其参数hard分为对应全塔属性中levelChooseButtons中的第二项,分别对应不同的难度,并会在游戏中传输,在状态栏显示。 diff --git a/docs/index.md b/docs/index.md index 3d12213c..842078dd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # HTML5 魔塔样板说明文档 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 diff --git a/docs/personalization.md b/docs/personalization.md index 2a6a15b0..5d4211f3 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -1,6 +1,6 @@ # 个性化 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 diff --git a/docs/start.md b/docs/start.md index ca8d110a..ac2bc064 100644 --- a/docs/start.md +++ b/docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 目前版本**v2.4.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.5**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! diff --git a/libs/actions.js b/libs/actions.js index 9c70ab75..1724c529 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -1028,6 +1028,18 @@ actions.prototype.clickShop = function(x,y) { var topIndex = 6 - parseInt(choices.length / 2); if (y>=topIndex && y= 5 && x <= 7) { var topIndex = 6 - parseInt(keys.length / 2); if (y>=topIndex && y=10 && x<=12 && y==12) { core.ui.closePanel(); if (!core.isPlaying()) { - core.showStartAnimate(); + core.showStartAnimate(true); } return; } @@ -1666,7 +1678,7 @@ actions.prototype.keyUpSL = function (keycode) { if (keycode==27 || keycode==88 || (core.status.event.id == 'save' && keycode==83) || (core.status.event.id == 'load' && keycode==68)) { core.ui.closePanel(); if (!core.isPlaying()) { - core.showStartAnimate(); + core.showStartAnimate(true); } return; } @@ -1760,7 +1772,7 @@ actions.prototype.clickSwitchs = function (x,y) { core.ui.drawSwitchs(); break; case 7: - core.setFlag('bigmapMoveDirectly', !core.getFlag('bigmapMoveDirectly', false)); + core.setFlag('clickMove', !core.getFlag('clickMove', true)); core.ui.drawSwitchs(); break; case 8: @@ -2141,11 +2153,15 @@ actions.prototype.clickStorageRemove = function (x, y) { localforage.clear(function () { core.ui.closePanel(); core.drawText("\t[操作成功]你的所有存档已被清空。"); + core.status.saveIndex = 1; + core.setLocalStorage('saveIndex2', 1); }); } else { localStorage.clear(); core.drawText("\t[操作成功]你的所有存档已被清空。"); + core.status.saveIndex = 1; + core.setLocalStorage('saveIndex2', 1); } break; case 1: @@ -2158,6 +2174,8 @@ actions.prototype.clickStorageRemove = function (x, y) { core.removeLocalForage("autoSave", function() { core.ui.closePanel(); core.drawText("\t[操作成功]当前塔的存档已被清空。"); + core.status.saveIndex = 1; + core.setLocalStorage('saveIndex2', 1); }); } else { @@ -2167,6 +2185,8 @@ actions.prototype.clickStorageRemove = function (x, y) { } core.removeLocalStorage("autoSave"); core.drawText("\t[操作成功]当前塔的存档已被清空。"); + core.status.saveIndex = 1; + core.setLocalStorage('saveIndex2', 1); } break; case 2: @@ -2216,15 +2236,8 @@ actions.prototype.clickReplay = function (x, y) { case 0: { core.ui.closePanel(); - var hard=core.status.hard, route=core.clone(core.status.route); - var seed = core.getFlag('seed'); - core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, null, core.initStatus.maps); - core.events.setInitData(hard); - core.setFlag('seed', seed); - core.setFlag('rand', seed); - core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() { - core.startReplay(route); - }, true); + var hard=core.status.hard, seed = core.getFlag('seed'); + core.startGame(hard, seed, core.clone(core.status.route)); break; } case 1: @@ -2290,6 +2303,15 @@ actions.prototype.clickKeyBoard = function (x, y) { core.ui.closePanel(); core.keyUp(112+x-1); // F1-F12: 112-122 } + if (y==3 && x==12) { + var val = prompt(); + if (val!=null) { + try { + eval(val); + } + catch (e) {} + } + } if (y==4 && x>=1 && x<=10) { core.ui.closePanel(); core.keyUp(x==10?48:48+x); // 1-9: 49-57; 0: 48 @@ -2456,6 +2478,12 @@ actions.prototype.setPaintMode = function (mode) { core.drawTip("进入"+(core.status.event.data.erase?"擦除":"绘图")+"模式"); } +actions.prototype.clearPaint = function () { + core.clearMap('route'); + core.paint[core.status.floorId] = null; + core.drawTip("已清空绘图内容"); +} + actions.prototype.savePaint = function () { var data = {}; for (var floorId in core.paint) { @@ -2498,7 +2526,6 @@ 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("退出绘图模式"); } diff --git a/libs/control.js b/libs/control.js index d69826b6..f7a8f576 100644 --- a/libs/control.js +++ b/libs/control.js @@ -79,6 +79,13 @@ control.prototype.setRequestAnimationFrame = function () { core.drawBlock(obj, obj.status); } + if ((core.status.autotileAnimateObjs.blocks||[]).length>0) { + 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); + }) + } + core.animateFrame.globalTime = timestamp; } } @@ -201,7 +208,7 @@ control.prototype.setRequestAnimationFrame = function () { } ////// 显示游戏开始界面 ////// -control.prototype.showStartAnimate = function (callback) { +control.prototype.showStartAnimate = function (noAnimate, callback) { core.dom.startPanel.style.opacity=1; core.dom.startPanel.style.display="block"; core.dom.startTop.style.opacity=1; @@ -213,18 +220,27 @@ control.prototype.showStartAnimate = function (callback) { core.clearStatus(); core.clearMap('all'); - var opacityVal = 1; - var startAnimate = window.setInterval(function () { - opacityVal -= 0.03; - if (opacityVal < 0) { - clearInterval(startAnimate); - core.dom.startTop.style.display = 'none'; - // core.playGame(); - core.dom.startButtonGroup.style.display = 'block'; - if (core.isset(callback)) callback(); - } - core.dom.startTop.style.opacity = opacityVal; - }, 20); + if(noAnimate) { + core.dom.startTop.style.display = 'none'; + // core.playGame(); + core.dom.startButtonGroup.style.display = 'block'; + if (core.isset(callback)) callback(); + } + else { + var opacityVal = 1; + var startAnimate = window.setInterval(function () { + opacityVal -= 0.03; + if (opacityVal < 0) { + clearInterval(startAnimate); + core.dom.startTop.style.display = 'none'; + // core.playGame(); + core.dom.startButtonGroup.style.display = 'block'; + if (core.isset(callback)) callback(); + } + core.dom.startTop.style.opacity = opacityVal; + }, 20); + } + } ////// 隐藏游戏开始界面 ////// @@ -318,35 +334,9 @@ control.prototype.resetStatus = function(hero, hard, floorId, route, maps, value core.status.played = true; } -////// 开始游戏 ////// -control.prototype.startGame = function (hard, callback) { - console.log('开始游戏'); - - this.resetStatus(core.firstData.hero, hard, core.firstData.floorId, null, core.initStatus.maps); - - core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() { - if (core.isset(callback)) callback(); - }, true); - - setTimeout(function () { - // Upload - var formData = new FormData(); - formData.append('type', 'people'); - formData.append('name', core.firstData.name); - formData.append('version', core.firstData.version); - formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); - formData.append('hard', core.encodeBase64(hard)); - formData.append('hardCode', core.getFlag('hard', 0)); - formData.append('base64', 1); - - core.utils.http("POST", "/games/upload.php", formData); - }) - -} - ////// 重新开始游戏;此函数将回到标题页面 ////// control.prototype.restart = function() { - this.showStartAnimate(); + this.showStartAnimate(true); if (core.bgms.length>0) core.playBgm(core.bgms[0]); } @@ -473,7 +463,7 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { // 单击瞬间移动 if (core.status.heroStop) { - if (core.control.tryMoveDirectly(destX, destY)) + if (stepPostfix.length<=1 && core.getFlag('clickMove', true) && core.control.tryMoveDirectly(destX, destY)) return; } @@ -1041,7 +1031,7 @@ control.prototype.updateViewport = function() { ////// 绘制勇士 ////// control.prototype.drawHero = function (direction, x, y, status, offset) { - if (!core.isPlaying()) return; + if (!core.isPlaying() || core.status.isStarting) return; var scan = { 'up': {'x': 0, 'y': -1}, @@ -1679,14 +1669,7 @@ control.prototype.chooseReplayFile = function () { return; } - core.dom.startPanel.style.display = 'none'; - core.resetStatus(core.firstData.hero, obj.hard, core.firstData.floorId, null, core.initStatus.maps); - core.setFlag('seed', obj.seed); - core.setFlag('rand', obj.seed); - core.events.setInitData(obj.hard); - core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() { - core.startReplay(core.decodeRoute(obj.route)); - }, true); + core.startGame(obj.hard, obj.seed, core.decode(obj.route)); }, function () { }) @@ -2263,15 +2246,7 @@ control.prototype.doSL = function (id, type) { if (data.version != core.firstData.version) { // core.drawTip("存档版本不匹配"); if (confirm("存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。")) { - core.dom.startPanel.style.display = 'none'; - var seed = data.hero.flags.seed; - core.resetStatus(core.firstData.hero, data.hard, core.firstData.floorId, null, core.initStatus.maps); - core.events.setInitData(data.hard); - core.setFlag('seed', seed); - core.setFlag('rand', seed); - core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() { - core.startReplay(core.decodeRoute(data.route)); - }, true); + core.startGame(data.hard, data.hero.flags.seed, core.decodeRoute(data.route)); } return; } diff --git a/libs/core.js b/libs/core.js index 38f0f4d8..77ad01e9 100644 --- a/libs/core.js +++ b/libs/core.js @@ -164,6 +164,7 @@ function core() { // 动画 'globalAnimateObjs': [], 'boxAnimateObjs': [], + 'autotileAnimateObjs': {}, 'animateObjs': [], }; this.status = {}; @@ -331,8 +332,8 @@ core.prototype.setRequestAnimationFrame = function () { } ////// 显示游戏开始界面 ////// -core.prototype.showStartAnimate = function (callback) { - core.control.showStartAnimate(callback); +core.prototype.showStartAnimate = function (noAnimate, callback) { + core.control.showStartAnimate(noAnimate, callback); } ////// 隐藏游戏开始界面 ////// @@ -356,8 +357,8 @@ core.prototype.resetStatus = function(hero, hard, floorId, route, maps, values) } ////// 开始游戏 ////// -core.prototype.startGame = function (hard, callback) { - core.control.startGame(hard, callback); +core.prototype.startGame = function (hard, seed, route, callback) { + core.events.startGame(hard, seed, route, callback); } ////// 重新开始游戏;此函数将回到标题页面 ////// @@ -661,8 +662,8 @@ core.prototype.drawMap = function (mapName, callback) { } ////// 绘制Autotile ////// -core.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top){ - core.maps.drawAutotile(ctx, mapArr, block, size, left, top); +core.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top, status){ + core.maps.drawAutotile(ctx, mapArr, block, size, left, top, status); } ////// 某个点是否不可通行 ////// diff --git a/libs/events.js b/libs/events.js index 73397f40..220b6ba8 100644 --- a/libs/events.js +++ b/libs/events.js @@ -76,38 +76,83 @@ events.prototype.initGame = function () { } ////// 游戏开始事件 ////// -events.prototype.startGame = function (hard) { +events.prototype.startGame = function (hard, seed, route, callback) { if (core.status.isStarting) return; core.status.isStarting = true; - core.hideStartAnimate(function() { - core.drawText(core.clone(core.firstData.startText), function() { - if (core.flags.showBattleAnimateConfirm) { // 是否提供“开启战斗动画”的选择项 + var start = function () { + console.log('开始游戏'); + core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, null, core.initStatus.maps); + + core.status.isStarting = true; + + if (core.isset(seed)) { + core.setFlag('seed', seed); + core.setFlag('rand', seed); + } + else core.utils.__init_seed(); + + core.events.setInitData(hard); + core.clearMap('all'); + core.clearStatusBar(); + + var post_start = function () { + + core.status.isStarting = false; + + core.changeFloor(core.status.floorId, null, core.status.hero.loc, null, function() { + if (core.isset(callback)) callback(); + }, true); + + setTimeout(function () { + // Upload + var formData = new FormData(); + formData.append('type', 'people'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":""); + formData.append('hard', core.encodeBase64(hard)); + formData.append('hardCode', core.getFlag('hard', 0)); + formData.append('base64', 1); + + core.utils.http("POST", "/games/upload.php", formData); + }) + } + + 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.data.flags.battleAnimate = true; core.flags.battleAnimate = true; core.setLocalStorage('battleAnimate', true); - core.startGame(hard); - core.utils.__init_seed(); - core.events.setInitData(hard); + post_start(); }, function () { - core.data.flags.battleAnimate = false; core.flags.battleAnimate = false; core.setLocalStorage('battleAnimate', false); - core.startGame(hard); - core.utils.__init_seed(); - core.events.setInitData(hard); + post_start(); }); } else { - core.startGame(hard); - core.utils.__init_seed(); - core.events.setInitData(hard); + post_start(); } }); - }) + + if (core.isset(route)) { + core.startReplay(route); + } + + } + + if (core.isset(route)) { + core.dom.startPanel.style.display = 'none'; + start(); + } + else { + core.hideStartAnimate(function() { + start(); + }) + } } ////// 不同难度分别设置初始属性 ////// @@ -347,6 +392,9 @@ events.prototype.doAction = function() { }, data.time || 3000); } break; + case "comment": + this.doAction(); + break; case "setText": // 设置文本状态 ["position", "offset", "bold", "titlefont", "textfont", "time"].forEach(function (t) { if (core.isset(data[t])) core.status.textAttribute[t]=data[t]; @@ -589,11 +637,8 @@ events.prototype.doAction = function() { this.doAction(); break; case "showImage": // 显示图片 - if (core.isset(data.loc) && core.isset(core.material.images.images[data.name])) { - core.canvas.image.drawImage(core.material.images.images[data.name], - core.calValue(data.loc[0]), core.calValue(data.loc[1])); - } - else core.clearMap('image'); + if (!core.isset(data.loc)) data.loc=[]; + core.events.showImage(data.name, data.loc[0], data.loc[1]); this.doAction(); break; case "animateImage": // 淡入淡出图片 @@ -1361,6 +1406,14 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback }, 25); } +////// 绘制图片 ////// +events.prototype.showImage = function (name, x, y) { + if (core.isset(name) && core.isset(x) && core.isset(y) && core.isset(core.material.images.images[name])) { + core.canvas.image.drawImage(core.material.images.images[name], x, y); + } + else core.clearMap('image'); +} + ////// 图片淡入/淡出 ////// events.prototype.animateImage = function (type, image, loc, time, keep, callback) { time = time||0; @@ -1535,12 +1588,18 @@ events.prototype.openShop = function(shopId, needVisited) { if (shop.commonTimes) shop.times = core.getFlag('commonTimes', 0); shop.visited = shop.visited || false; + if (needVisited && !shop.visited) { - if (shop.times==0) core.drawTip("该商店尚未开启"); - else core.drawTip("该商店已失效"); - return; + if (!core.flags.enableDisabledShop) { + if (shop.times==0) core.drawTip("该商店尚未开启"); + else core.drawTip("该商店已失效"); + return; + } + else { + core.drawTip("该商店尚未开启,只能浏览不可使用"); + } } - shop.visited = true; + else shop.visited = true; var selection = core.status.event.selection; var actions = []; diff --git a/libs/maps.js b/libs/maps.js index 9a319ca6..859faee7 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -310,12 +310,6 @@ maps.prototype.canMoveDirectly = function (destX,destY) { var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); if (fromX==destX&&fromY==destY) return 0; - // 大地图且会改变左上角坐标,不能 - var sx = core.clamp(fromX-6,0,core.bigmap.width-13), sy = core.clamp(fromY-6,0,core.bigmap.height-13), - ex = core.clamp(destX-6,0,core.bigmap.width-13), ey = core.clamp(destY-6,0,core.bigmap.height-13); - - if (!core.hasFlag('bigmapMoveDirectly') && (sx!=ex || sy!=ey)) return -1; - // 无视起点事件 var nowBlockId = core.getBlockId(fromX, fromY); if ((nowBlockId!=null&&nowBlockId!='upFloor'&&nowBlockId!='downFloor'&&nowBlockId!='portal' @@ -458,6 +452,7 @@ maps.prototype.drawMap = function (mapName, callback) { mapName = mapName || core.status.floorId; core.clearMap('all'); core.removeGlobalAnimate(null, null, true); + var drawBg = function(){ core.maps.drawBgFgMap(mapName, core.canvas.bg, "bg"); @@ -520,6 +515,8 @@ 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; @@ -530,6 +527,7 @@ maps.prototype.drawMap = function (mapName, callback) { if (core.isset(block.event) && !block.disable) { if (block.event.cls == 'autotile') { core.drawAutotile(core.canvas.event, mapArray, block, 32, 0, 0); + core.status.autotileAnimateObjs.blocks.push(core.clone(block)); } else { core.drawBlock(block); @@ -537,6 +535,7 @@ maps.prototype.drawMap = function (mapName, callback) { } } } + core.status.autotileAnimateObjs.map = core.clone(mapArray); } if (main.mode=='editor'){ @@ -561,7 +560,7 @@ maps.prototype.drawMap = function (mapName, callback) { } ////// 绘制Autotile ////// -maps.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top){ +maps.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top, status){ var indexArrs = [ //16种组合的图块索引数组; // 将autotile分割成48块16*16的小块; 数组索引即对应各个小块 // +----+----+----+----+----+----+ [10, 9, 4, 3 ], //0 bin:0000 | 1 | 2 | 3 | 4 | 5 | 6 | @@ -584,7 +583,9 @@ maps.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top){ var drawBlockByIndex = function(ctx, dx, dy, autotileImg, index, size){ //index为autotile的图块索引1-48 var sx = 16*((index-1)%6), sy = 16*(~~((index-1)/6)); - ctx.drawImage(autotileImg, sx, sy, 16, 16, dx, dy, size/2, size/2); + status = status || 0; + status %= parseInt(autotileImg.width/96); + ctx.drawImage(autotileImg, sx + 96*status, sy, 16, 16, dx, dy, size/2, size/2); } var getAutotileAroundId = function(currId, x, y) { if(x<0 || y<0 || x>=mapArr[0].length || y>=mapArr.length) return 1; @@ -1195,15 +1196,18 @@ maps.prototype.removeGlobalAnimate = function (x, y, all) { if (all) { core.status.globalAnimateObjs = []; + core.status.autotileAnimateObjs = {}; return; } - for (var t = 0; t < core.status.globalAnimateObjs.length; t++) { - if (core.status.globalAnimateObjs[t].x == x && core.status.globalAnimateObjs[t].y == y) { - core.status.globalAnimateObjs.splice(t, 1); - return; - } + core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) {return block.x!=x || block.y!=y;}); + + // 检查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.map[y][x] = 0; } + } ////// 设置全局动画的显示效果 ////// @@ -1219,6 +1223,9 @@ maps.prototype.syncGlobalAnimate = function () { core.status.globalAnimateObjs.forEach(function (t) { t.status=0; }) + if (core.isset(core.status.autotileAnimateObjs.status)) { + core.status.autotileAnimateObjs.status = 0; + } } ////// 绘制UI层的box动画 ////// diff --git a/libs/ui.js b/libs/ui.js index 9636e447..7049a4f2 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -823,7 +823,7 @@ ui.prototype.drawSwitchs = function() { "临界显伤: "+(core.flags.displayCritical ? "[ON]" : "[OFF]"), "领域显伤: "+(core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), "新版存档: "+(core.platform.useLocalForage ? "[ON]":"[OFF]"), - "大地图瞬移:"+(core.hasFlag('bigmapMoveDirectly') ? "[ON]":"[OFF]"), + "单击瞬移: "+(core.getFlag('clickMove', true) ? "[ON]":"[OFF]"), "查看工程", "下载离线版本", "返回主菜单" @@ -845,12 +845,9 @@ ui.prototype.drawQuickShop = function () { core.status.event.id = 'selectShop'; - var shopList = core.status.shops, keys = Object.keys(shopList); + var shopList = core.status.shops, keys = Object.keys(shopList).filter(function (shopId) {return shopList[shopId].visited || !shopList[shopId].mustEnable}); + var choices = keys.map(function (shopId) {return shopList[shopId].textInList}); - var choices = []; - for (var i=0;i0) t+=atk+"攻"; if (def>0) t+=def+"防"; @@ -2460,10 +2465,10 @@ ui.prototype.drawPaint = function () { 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.toolbox.src = core.statusBar.icons.delete.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; diff --git a/libs/utils.js b/libs/utils.js index b347f524..24c1eaf9 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -20,6 +20,7 @@ utils.prototype.replaceText = function (text) { ////// 计算表达式的值 ////// utils.prototype.calValue = function (value) { + if (!core.isset(value)) return value; if (typeof value == 'number') { return value; } diff --git a/main.js b/main.js index cee1293a..2def3d70 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,7 @@ function main() { //------------------------ 用户修改内容 ------------------------// - this.version = "2.4.4"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 + this.version = "2.5"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 this.useCompress = false; // 是否使用压缩文件 // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。 @@ -130,7 +130,8 @@ function main() { 'skill': 25, 'paint': 26, 'erase': 27, - 'exit': 28, + 'delete': 28, + 'exit': 29, }, 'floor': document.getElementById('floor'), 'name': document.getElementById('name'), @@ -451,6 +452,11 @@ main.statusBar.image.toolbox.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.clearPaint(); + return; + } + if (main.core.isPlaying()) { main.core.openToolbox(core.status.event.id != 'equipbox'); } diff --git a/project/data.js b/project/data.js index 15eba994..22b396ea 100644 --- a/project/data.js +++ b/project/data.js @@ -68,7 +68,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "firstData": { "title": "魔塔样板", "name": "template", - "version": "Ver 2.4.4", + "version": "Ver 2.5", "floorId": "sample0", "hero": { "name": "阳光", @@ -231,6 +231,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "potionWhileRouting": false, "portalWithoutTrigger": true, "canGoDeadZone": false, - "enableMoveDirectly": true + "enableMoveDirectly": true, + "enableDisabledShop": true } } \ No newline at end of file diff --git a/project/images/icons.png b/project/images/icons.png index 538a2c8f..75301f33 100644 Binary files a/project/images/icons.png and b/project/images/icons.png differ diff --git a/更新说明.txt b/更新说明.txt index 1e1ad136..806a3184 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,4 +1,31 @@ -HTML5魔塔样板V2.4.4 +HTML5魔塔样板V2.5 + +添加绘图模式支持;可以用户手动绘图和保存 +内置主动技能:二倍斩的支持,可以仿照制作其他主动技能 +将按键处理移动到脚本编辑中 +Alt+0\~9保存和读取当前套装 +图块属性的cannotOut和cannotIn控制可通行方向(来造成悬崖效果) +支持动态Autotile自动元件(仅在事件层有效) +允许快捷商店使用共用的times +未启用的快捷商店可以隐藏或预览 +开始剧情startText可以执行任意事件 +对话窗口可以任意调节位置(上中下、距离顶部/底部的像素值) +楼层转换界面可以设置背景图片文字颜色等 +数据统计进行分段描写,剑盾显示数值 +现在可以在事件编辑器中注释内容了 +存读档界面显示该存档的属性 +F7键可以开启debug模式 +R键可以从本地选取录像文件从头播放 +吸血属性的显伤增加^;仇恨怪显示仇恨伤害 +4键默认使用破冰稿或冰冻徽章或地震卷轴或上下楼器等等 +血瓶的道具化选项;黄宝石增加加点选项 +破炸飞增加默认音效 +修复单击瞬移的拖动打怪问题 +其他细节优化 + +----------------------------------------------------------------------- + +HTML5魔塔样板V2.4.4 tilesets可以设置图块属性(如可通行状态) 追加素材时可以更改图片色调