diff --git a/README.md b/README.md index aa6b6e24..ec5f01ec 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,12 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! * [x] 同一个点的多事件处理(做法详见文档)。 * [x] 增加新地图后可以接档而不用重新开始。 * [x] 增加可以接收用户输入的事件(type:input)。 +* [x] 滚动字幕;自动剧情文本。 * [x] 可以同时show/hide多个事件。 * [x] 现在可以支持滑冰和推箱子事件了。 * [x] 地图中每个块的可通行方向控制(悬崖效果)。 * [x] 动画支持带旋转和翻转的帧。 +* [x] 长按屏幕可跳过对话。 * [x] 现在可以允许用户丢弃道具了(例如不会再使用的装备)。 * [x] 修复行走时按键会发生动画抖动问题。 * [x] 修复无法打开战斗动画的Bug。 diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index efc92009..385f166e 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -155,6 +155,7 @@ return code; action : text_0_s | text_1_s + | autoText_s | setText_s | tip_s | setValue_s @@ -201,7 +202,7 @@ text_0_s /* text_0_s tooltip : text:显示一段文字(剧情) helpUrl : https://ckcz123.github.io/mota-js/#/event?id=text%ef%bc%9a%e6%98%be%e7%a4%ba%e4%b8%80%e6%ae%b5%e6%96%87%e5%ad%97%ef%bc%88%e5%89%a7%e6%83%85%ef%bc%89 -default : ["欢迎使用事件编辑器"] +default : ["欢迎使用事件编辑器(双击方块进入多行编辑)"] var code = '"'+EvalString_0+'",\n'; return code; */ @@ -213,7 +214,7 @@ text_1_s /* text_1_s tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=text%ef%bc%9a%e6%98%be%e7%a4%ba%e4%b8%80%e6%ae%b5%e6%96%87%e5%ad%97%ef%bc%88%e5%89%a7%e6%83%85%ef%bc%89 -default : ["小妖精","fairy","","欢迎使用事件编辑器"] +default : ["小妖精","fairy","","欢迎使用事件编辑器(双击方块进入多行编辑)"] var title=''; if (EvalString_0==''){ if (IdString_0=='')title=''; @@ -230,14 +231,38 @@ var code = '"'+title+EvalString_1+EvalString_2+'",\n'; return code; */ +autoText_s + : '自动剧情文本: 标题' EvalString? '图像' IdString? '对话框效果' EvalString? '时间' Int BGNL? EvalString Newline + ; + +/* autoText_s +tooltip : autoText:自动剧情文本,用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=autotext%ef%bc%9a%e8%87%aa%e5%8a%a8%e5%89%a7%e6%83%85%e6%96%87%e6%9c%ac +default : ["小妖精","fairy","",3000,"双击方块进入多行编辑\\n自动剧情文本\\n自动剧情文本\\n自动剧情文本"] +var title=''; +if (EvalString_0==''){ + if (IdString_0=='')title=''; + else title='\\t['+IdString_0+']'; +} else { + if (IdString_0=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+IdString_0+']'; +} +if(EvalString_1 && !(/^(up|down)(,hero)?(,([+-]?\d+),([+-]?\d+))?$/.test(EvalString_1))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_1 = EvalString_1 && ('\\b['+EvalString_1+']'); +var code = '{"type": "autoText", "text": "'+title+EvalString_1+EvalString_2+'", "time" :'+Int_0+'},\n'; +return code; +*/ + setText_s - : '设置剧情文本的属性' '位置' SetTextPosition_List BGNL? '标题颜色' EvalString? '正文颜色' EvalString? '背景色' EvalString? '粗体' B_List Newline + : '设置剧情文本的属性' '位置' SetTextPosition_List BGNL? '标题颜色' EvalString? '正文颜色' EvalString? '背景色' EvalString? BGNL? '粗体' B_List '打字间隔' EvalString? Newline ; /* setText_s -tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组 +tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=settext%ef%bc%9a%e8%ae%be%e7%bd%ae%e5%89%a7%e6%83%85%e6%96%87%e6%9c%ac%e7%9a%84%e5%b1%9e%e6%80%a7 -default : [[['不改变','null'],['上','up'],['中','center'],['下','down']],"255,215,0,1","255,255,255,1","0,0,0,0.85",[['不改变','null'],['设为粗体','true'],['取消粗体','false']]] +default : [[['不改变','null'],['上','up'],['中','center'],['下','down']],"","","",[['不改变','null'],['设为粗体','true'],['取消粗体','false']],''] SetTextPosition_List_0 =SetTextPosition_List_0==='null'?'': ', "position": "'+SetTextPosition_List_0+'"'; var colorRe = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d),(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(,0(\.\d+)?|,1)?$/; if (EvalString_0) { @@ -252,8 +277,12 @@ if (EvalString_2) { if (!colorRe.test(EvalString_2))throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); EvalString_2 = ', "background": ['+EvalString_2+']'; } +if (EvalString_3) { + if (!/^\d+$/.test(EvalString_3))throw new Error('打字时间间隔必须是整数或不填'); + EvalString_3 = ', "time": '+EvalString_3; +} B_List_0 = ', "bold": '+B_List_0; -var code = '{"type": "setText"'+SetTextPosition_List_0+EvalString_0+EvalString_1+EvalString_2+B_List_0+'},\n'; +var code = '{"type": "setText"'+SetTextPosition_List_0+EvalString_0+EvalString_1+EvalString_2+B_List_0+EvalString_3+'},\n'; return code; */ @@ -1117,7 +1146,7 @@ ActionParser.prototype.parseAction = function() { // 如果是文字:显示 if (typeof data == "string") { - data={"type": "text", "data": data} + data={"type": "text", "text": data} } this.event.data.type=data.type; switch (data.type) { @@ -1127,7 +1156,12 @@ ActionParser.prototype.parseAction = function() { return; case "text": // 文字/对话 this.next = MotaActionBlocks['text_0_s'].xmlText([ - this.EvalString(data.data),this.next]); + this.EvalString(data.text),this.next]); + break; + case "autoText": // 自动剧情文本 + data.time=this.isset(data.time)?data.time:MotaActionBlocks['autoText_s'].fieldDefault[3]; + this.next = MotaActionBlocks['autoText_s'].xmlText([ + '','','',data.time,this.EvalString(data.text),this.next]); break; case "setText": // 设置剧情文本的属性 var setTextfunc = function(a){return a?JSON.stringify(a).slice(1,-1):null;} @@ -1135,7 +1169,7 @@ ActionParser.prototype.parseAction = function() { data.text=setTextfunc(data.text); data.background=setTextfunc(data.background); this.next = MotaActionBlocks['setText_s'].xmlText([ - data.position,data.title,data.text,data.background,data.bold,this.next]); + data.position,data.title,data.text,data.background,data.bold,data.time,this.next]); break; case "tip": this.next = MotaActionBlocks['tip_s'].xmlText([ diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index 7d6da9ea..71f83494 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -53,6 +53,13 @@ initscript=String.raw` '', MotaActionBlocks['text_0_s'].xmlText(), MotaActionBlocks['text_1_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "choices", "text": "是否跳过剧情", "choices": [ + {"text": "是", "action": []}, + {"text": "否", "action": [ + {"type": "autoText", "text": "\\t[小妖精,fairy]双击方块进入多行编辑\\n用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示\\n自动剧情文本\\n自动剧情文本\\n自动剧情文本", "time" :3000}, + {"type": "autoText", "text": "(可以右键方块后点复制)", "time" :3000}, + ]}, + ]}), MotaActionBlocks['setText_s'].xmlText(), MotaActionBlocks['showImage_0_s'].xmlText(), MotaActionBlocks['showImage_1_s'].xmlText(), @@ -372,6 +379,7 @@ editor_blockly.doubleClickBlock = function (blockId){ var textStringDict = { 'text_0_s':'EvalString_0', 'text_1_s':'EvalString_2', + 'autoText_s':'EvalString_2', 'choices_s':'EvalString_0', 'function_s':'RawEvalString_0', } diff --git a/docs/event.md b/docs/event.md index b2482c59..879ac5d1 100644 --- a/docs/event.md +++ b/docs/event.md @@ -150,14 +150,14 @@ ### text:显示一段文字(剧情) -使用`{"type": "text"}`可以显示一段文字。后面`"data"`可以指定文字内容。 +使用`{"type": "text"}`可以显示一段文字。后面`"text"`可以指定文字内容。 ``` js "events": { // 该楼的所有可能事件列表 // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 "x,y": [ // 实际执行的事件列表 - {"type": "text", "data": "在界面上的一段文字"}, // 显示文字事件 - {"type": "text", "data": "这是第二段文字"}, // 显示第二个文字事件 + {"type": "text", "text": "在界面上的一段文字"}, // 显示文字事件 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 // ... // 按顺序写事件,直到结束 ] @@ -171,7 +171,7 @@ // 如果大括号里只有"data"项(没有"action"或"enable"),则可以省略到只剩下中括号 "x,y": [ // 实际执行的事件列表 "在界面上的一段文字",// 直接简写,和下面写法完全等价 - {"type": "text", "data": "这是第二段文字"}, // 显示第二个文字事件 + {"type": "text", "text": "这是第二段文字"}, // 显示第二个文字事件 // ... // 按顺序写事件,直到结束 ] @@ -261,26 +261,49 @@ ![调试](./img/eventdebug.png) +### autoText:自动剧情文本 + +使用`{"type": "autoText"}`可以使用剧情文本。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "autoText", "text": "一段自动显示的剧情文字", "time": 5000} +] +``` + +text为文本正文内容,和上面的写法完全一致。 + +time为可选项,代表该自动文本的时间。可以不指定,不指定默认为3000毫秒。 + +用户无法跳过自动剧情文本,只能等待time时间结束后自动过。 + +回放录像时将忽略自动剧情文本的显示。 + +!> 由于用户无法跳过自动剧情文本,因此对于大段剧情文本请自行添加“是否跳过剧情”的提示,否则可能会非常不友好。 + ### setText:设置剧情文本的属性 使用`{"type": "setText"}`可以设置剧情文本的各项属性。 ``` js "x,y": [ // 实际执行的事件列表 - {"type": "setText", "position": "up", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3], "bold": true}, - "这段话将显示在上方,标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色" + {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3]}, + {"type": "setText", "position": "up", "bold": true, "time": 70}, + "这段话将显示在上方,标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色,70毫秒速度打字机效果" ] ``` -position为可选项,表示设置文字显示位置。只能为up(上),center(中)和down(下)三者。 +title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 默认值:`[255,215,0,1]` -title为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示标题(名字)颜色。 +text为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示正文颜色。 默认值:`[255,255,255,1]` -text为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示正文颜色。 +background为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示背景色。 默认值:`[0,0,0,0.85]` -background为可选项,如果设置则为一个RGB三元组或RGBA四元组,表示背景色。 +position为可选项,表示设置文字显示位置。只能为up(上),center(中)和down(下)三者。 默认值: `center` -bold为可选项,如果设置则为true或false,表示正文是否使用粗体。 +bold为可选项,如果设置则为true或false,表示正文是否使用粗体。 默认值:`false` + +time为可选项,表示文字添加的速度。若此项设置为0将直接全部显示,若大于0则会设置为相邻字符依次显示的时间间隔。 默认值:`0` ### tip:显示一段提示文字 diff --git a/docs/index.html b/docs/index.html index 79028c98..f29c2402 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,7 +6,9 @@ + + diff --git a/editor.html b/editor.html index 1617c843..ecc3947b 100644 --- a/editor.html +++ b/editor.html @@ -192,20 +192,15 @@

资源即将开始加载

- - - + +
开始游戏 载入游戏 录像回放
-
- 简单 - 普通 - 困难 -
+
diff --git a/index.html b/index.html index 5c84fe0c..3d8ecc60 100644 --- a/index.html +++ b/index.html @@ -25,20 +25,15 @@

资源即将开始加载

- - - + +
开始游戏 载入游戏 录像回放
-
- 简单 - 普通 - 困难 -
+
diff --git a/libs/core.js b/libs/core.js index 91412644..e61bb02a 100644 --- a/libs/core.js +++ b/libs/core.js @@ -17,12 +17,14 @@ function core() { this.timeout = { 'getItemTipTimeout': null, 'turnHeroTimeout': null, + 'onDownTimeout': null, } this.interval = { 'heroMoveInterval': null, "tipAnimate": null, 'openDoorAnimate': null, 'animateInterval': null, + 'onDownInterval': null, } this.animateFrame = { 'background': null, @@ -124,6 +126,7 @@ function core() { 'data': null, 'selection': null, 'ui': null, + 'interval': null, }, 'textAttribute': { 'position': "center", @@ -131,6 +134,7 @@ function core() { "background": [0,0,0,0.85], "text": [255,255,255,1], "bold": false, + "time": 0, }, 'curtainColor': null, 'usingCenterFly':false, @@ -775,8 +779,13 @@ core.prototype.clearStatus = function() { core.prototype.resetStatus = function(hero, hard, floorId, route, maps) { // 停止各个Timeout和Interval + for (var i in core.timeout) { + clearTimeout(core.timeout[i]); + core.timeout[i] = null; + } for (var i in core.interval) { clearInterval(core.interval[i]); + core.interval[i] = null; } // 初始化status @@ -1229,6 +1238,18 @@ core.prototype.ondown = function (x ,y) { if (core.isset(core.status.replay)&&core.status.replay.replaying) return; if (!core.status.played || core.status.lockControl) { core.onclick(x, y, []); + if (core.timeout.onDownTimeout==null) { + core.timeout.onDownTimeout = setTimeout(function () { + if (core.interval.onDownInterval == null) { + core.interval.onDownInterval = setInterval(function () { + if (!core.events.longClick()) { + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + } + }, 40) + } + }, 500); + } return; } @@ -1271,6 +1292,12 @@ core.prototype.onmove = function (x ,y) { ////// 当点击(触摸)事件放开时 ////// core.prototype.onup = function () { if (core.isset(core.status.replay)&&core.status.replay.replaying) return; + + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + // core.status.holdingPath=0; if(core.status.stepPostfix.length>0){ var stepPostfix = []; @@ -1290,7 +1317,10 @@ core.prototype.onup = function () { // 长按 if (!core.status.lockControl && stepPostfix.length==0 && core.status.downTime!=null && new Date()-core.status.downTime>=1000) { - core.events.longClick(); + core.waitHeroToStop(function () { + // 绘制快捷键 + core.ui.drawKeyBoard(); + }); } else { //posx,posy是寻路的目标点,stepPostfix是后续的移动 diff --git a/libs/events.js b/libs/events.js index 95a8283d..cc1b05a1 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1,7 +1,7 @@ function events() { } - +var eventdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.events; ////// 初始化 ////// events.prototype.init = function () { this.events = { @@ -105,52 +105,16 @@ events.prototype.startGame = function (hard) { } ////// 不同难度分别设置初始属性 ////// -events.prototype.setInitData = function (hard) { - if (hard=='Easy') { // 简单难度 - core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度 - // 可以在此设置一些初始福利,比如设置初始生命值可以调用: - // core.setStatus("hp", 10000); - // 赠送一把黄钥匙可以调用 - // core.setItem("yellowKey", 1); - } - if (hard=='Normal') { // 普通难度 - core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度 - } - if (hard=='Hard') { // 困难难度 - core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度 - } - this.afterLoadData(); -} +events.prototype.setInitData = eventdata.setInitData +// function (hard) ////// 游戏获胜事件 ////// -events.prototype.win = function(reason) { - core.ui.closePanel(); - var replaying = core.status.replay.replaying; - core.stopReplay(); - core.waitHeroToStop(function() { - core.removeGlobalAnimate(0,0,true); - core.clearMap('all'); // 清空全地图 - core.drawText([ - "\t[恭喜通关]你的分数是${status:hp}。" - ], function () { - core.events.gameOver('', replaying); - }) - }); -} +events.prototype.win = eventdata.win +// function(reason) ////// 游戏失败事件 ////// -events.prototype.lose = function(reason) { - core.ui.closePanel(); - var replaying = core.status.replay.replaying; - core.stopReplay(); - core.waitHeroToStop(function() { - core.drawText([ - "\t[结局1]你死了。\n如题。" - ], function () { - core.events.gameOver(null, replaying); - }); - }) -} +events.prototype.lose = eventdata.lose +// function(reason) ////// 游戏结束 ////// events.prototype.gameOver = function (ending, fromReplay) { @@ -230,20 +194,8 @@ events.prototype.gameOver = function (ending, fromReplay) { } ////// 转换楼层结束的事件 ////// -events.prototype.afterChangeFloor = function (floorId) { - if (core.isset(core.status.event.id)) return; // 当前存在事件 - - if (!core.hasFlag("visited_"+floorId)) { - this.doEvents(core.floors[floorId].firstArrive, null, null, function () { - //core.autosave(); - }); - core.setFlag("visited_"+floorId, true); - return; - } - - // 自动存档 - //core.autosave(); -} +events.prototype.afterChangeFloor = eventdata.afterChangeFloor +// function (floorId) ////// 开始执行一系列自定义事件 ////// events.prototype.doEvents = function (list, x, y, callback) { @@ -266,6 +218,8 @@ events.prototype.doEvents = function (list, x, y, callback) { events.prototype.doAction = function() { // 清空boxAnimate和UI层 core.status.boxAnimateObjs = []; + clearInterval(core.status.event.interval); + core.clearMap('ui', 0, 0, 416, 416); core.setAlpha('ui', 1.0); @@ -301,7 +255,17 @@ events.prototype.doAction = function() { if (core.status.replay.replaying) core.events.doAction(); else - core.ui.drawTextBox(data.data); + core.ui.drawTextBox(data.text); + break; + case "autoText": + if (core.status.replay.replaying) + core.events.doAction(); + else { + core.ui.drawTextBox(data.text); + setTimeout(function () { + core.events.doAction(); + }, data.time || 3000); + } break; case "setText": // 设置文本状态 if (data.position=='up'||data.position=='down'||data.position=='center') { @@ -316,6 +280,9 @@ events.prototype.doAction = function() { if (core.isset(data.bold)) { core.status.textAttribute.bold=data.bold; } + if (core.isset(data.time)) { + core.status.textAttribute.time=data.time; + } core.events.doAction(); break; case "tip": @@ -777,124 +744,16 @@ events.prototype.useItem = function(itemId) { } ////// 加点事件 ////// -events.prototype.addPoint = function (enemy) { - var point = enemy.point; - if (!core.isset(point) || point<=0) return []; - - // 加点,返回一个choices事件 - return [ - {"type": "choices", - "choices": [ - {"text": "攻击+"+(1*point), "action": [ - {"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)} - ]}, - {"text": "防御+"+(2*point), "action": [ - {"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)} - ]}, - {"text": "生命+"+(200*point), "action": [ - {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)} - ]}, - ] - } - ]; -} +events.prototype.addPoint = eventdata.addPoint +// function (enemy) ////// 战斗结束后触发的事件 ////// -events.prototype.afterBattle = function(enemyId,x,y,callback) { - - var enemy = core.material.enemys[enemyId]; - - // 毒衰咒的处理 - var special = enemy.special; - // 中毒 - if (core.enemys.hasSpecial(special, 12) && !core.hasFlag('poison')) { - core.setFlag('poison', true); - } - // 衰弱 - if (core.enemys.hasSpecial(special, 13) && !core.hasFlag('weak')) { - core.setFlag('weak', true); - core.status.hero.atk-=core.values.weakValue; - core.status.hero.def-=core.values.weakValue; - } - // 诅咒 - if (core.enemys.hasSpecial(special, 14) && !core.hasFlag('curse')) { - core.setFlag('curse', true); - } - // 仇恨属性:减半 - if (core.flags.hatredDecrease && core.enemys.hasSpecial(special, 17)) { - core.setFlag('hatred', parseInt(core.getFlag('hatred', 0)/2)); - } - // 自爆 - if (core.enemys.hasSpecial(special, 19)) { - core.status.hero.hp = 1; - } - // 退化 - if (core.enemys.hasSpecial(special, 21)) { - core.status.hero.atk -= (enemy.atkValue||0); - core.status.hero.def -= (enemy.defValue||0); - if (core.status.hero.atk<0) core.status.hero.atk=0; - if (core.status.hero.def<0) core.status.hero.def=0; - } - // 增加仇恨值 - core.setFlag('hatred', core.getFlag('hatred',0)+core.values.hatred); - core.updateStatusBar(); - - - // 事件的处理 - var todo = []; - // 如果不为阻击,且该点存在,且有事件 - if (!core.enemys.hasSpecial(special, 18) && core.isset(x) && core.isset(y)) { - var event = core.floors[core.status.floorId].afterBattle[x+","+y]; - if (core.isset(event)) { - // 插入事件 - core.unshift(todo, event); - } - } - // 如果有加点 - var point = core.material.enemys[enemyId].point; - if (core.isset(point) && point>0) { - core.unshift(todo, core.events.addPoint(core.material.enemys[enemyId])); - } - - // 如果事件不为空,将其插入 - if (todo.length>0) { - this.insertAction(todo,x,y); - } - - // 如果已有事件正在处理中 - if (core.status.event.id == null) { - core.continueAutomaticRoute(); - } - else { - core.clearContinueAutomaticRoute(); - } - if (core.isset(callback)) callback(); - -} +events.prototype.afterBattle = eventdata.afterBattle +// function(enemyId,x,y,callback) ////// 开一个门后触发的事件 ////// -events.prototype.afterOpenDoor = function(doorId,x,y,callback) { - - var todo = []; - if (core.isset(x) && core.isset(y)) { - var event = core.floors[core.status.floorId].afterOpenDoor[x+","+y]; - if (core.isset(event)) { - core.unshift(todo, event); - } - } - - if (todo.length>0) { - this.insertAction(todo,x,y); - } - - if (core.status.event.id == null) { - core.continueAutomaticRoute(); - } - else { - core.clearContinueAutomaticRoute(); - } - if (core.isset(callback)) callback(); -} +events.prototype.afterOpenDoor = eventdata.afterOpenDoor +// function(doorId,x,y,callback) ////// 经过一个路障 ////// events.prototype.passNet = function (data) { @@ -945,9 +804,8 @@ events.prototype.changeLight = function(x, y) { } ////// 改变亮灯之后,可以触发的事件 ////// -events.prototype.afterChangeLight = function(x,y) { - -} +events.prototype.afterChangeLight = eventdata.afterChangeLight +// function(x,y) ////// 滑冰 ////// events.prototype.ski = function (direction) { @@ -1024,54 +882,20 @@ events.prototype.pushBox = function (data) { } ////// 推箱子后的事件 ////// -events.prototype.afterPushBox = function () { - - var noBoxLeft = function () { - // 地图上是否还存在未推到的箱子,如果不存在则返回true,存在则返回false - for (var i=0;i', 270, top+height-13, '#CCCCCC', '13px Verdana'); } ////// 绘制一个选项界面 ////// @@ -400,7 +419,7 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) { max_length = Math.max(max_length, core.canvas.ui.measureText(contents[i]).width); } - var left = Math.min(208 - 40 - max_length / 2, 100); + var left = Math.min(208 - 40 - parseInt(max_length / 2), 100); var top = 140 - (lines-1)*30; var right = 416 - 2 * left, bottom = 416 - 140 - top; @@ -418,10 +437,10 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) { var len=core.canvas.ui.measureText("确定").width; if (core.status.event.selection==0) { - core.strokeRect('ui', 208-38-len/2-5, top+bottom-35-20, len+10, 28, "#FFD700", 2); + core.strokeRect('ui', 208-38-parseInt(len/2)-5, top+bottom-35-20, len+10, 28, "#FFD700", 2); } if (core.status.event.selection==1) { - core.strokeRect('ui', 208+38-len/2-5, top+bottom-35-20, len+10, 28, "#FFD700", 2); + core.strokeRect('ui', 208+38-parseInt(len/2)-5, top+bottom-35-20, len+10, 28, "#FFD700", 2); } } @@ -776,7 +795,7 @@ ui.prototype.drawWaiting = function(text) { var text_length = core.canvas.ui.measureText(text).width; var right = Math.max(text_length+50, 220); - var left = 208-right/2, top = 208 - 32 - 16, bottom = 416 - 2 * top; + var left = 208-parseInt(right/2), top = 208 - 32 - 16, bottom = 416 - 2 * top; core.fillRect('ui', left, top, right, bottom, background); core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); @@ -822,7 +841,7 @@ ui.prototype.drawPagination = function (page, totalPage) { var length = core.canvas.ui.measureText(page + " / " + page).width; core.canvas.ui.textAlign = 'left'; - core.fillText('ui', page + " / " + totalPage, (416 - length) / 2, 403); + core.fillText('ui', page + " / " + totalPage, parseInt((416 - length) / 2), 403); core.canvas.ui.textAlign = 'center'; if (page > 1) @@ -1231,7 +1250,7 @@ ui.prototype.drawSLPanel = function(index) { core.setAlpha('ui', 1); core.canvas.ui.textAlign = 'center'; - var u=416/6, size=117; + var u=416/6, size=118; var name=core.status.event.id=='save'?"存档":"读档"; for (var i=0;i<6;i++) { @@ -1371,33 +1390,7 @@ ui.prototype.drawKeyBoard = function () { } ////// 绘制“关于”界面 ////// -ui.prototype.drawAbout = function() { - - if (!core.isPlaying()) { - core.status.event = {'id': null, 'data': null}; - core.dom.startPanel.style.display = 'none'; - } - core.lockControl(); - core.status.event.id = 'about'; - - core.clearMap('ui', 0, 0, 416, 416); - var left = 48, top = 36, right = 416 - 2 * left, bottom = 416 - 2 * top; - - core.setAlpha('ui', 0.85); - core.fillRect('ui', left, top, right, bottom, '#000000'); - core.setAlpha('ui', 1); - core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2); - - var text_start = left + 24; - - // 名称 - core.canvas.ui.textAlign = "left"; - core.fillText('ui', "HTML5 魔塔样板", text_start, top+35, "#FFD700", "bold 22px Verdana"); - core.fillText('ui', "版本: "+core.firstData.version, text_start, top + 80, "#FFFFFF", "bold 17px Verdana"); - core.fillText('ui', "作者: 艾之葵", text_start, top + 112); - core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top+112+32); - // TODO: 写自己的“关于”页面,每次增加32像素即可 -} +ui.prototype.drawAbout = uidata.drawAbout ////// 绘制帮助页面 ////// ui.prototype.drawHelp = function () { @@ -1424,6 +1417,8 @@ ui.prototype.drawHelp = function () { "点任意块并拖动: 指定寻路路线\n"+ "单击勇士: 转向\n"+ "双击勇士: 轻按(仅在轻按开关打开时有效)\n"+ - "长按任意位置:打开虚拟键盘" + "长按任意位置:跳过剧情对话或打开虚拟键盘\n" ]); -} \ No newline at end of file +} + +delete(uidata) \ No newline at end of file diff --git a/main.js b/main.js index bb18530f..c2fb77c8 100644 --- a/main.js +++ b/main.js @@ -17,6 +17,7 @@ function main() { 'startTopProgress': document.getElementById('startTopProgress'), 'startTopLoadTips': document.getElementById('startTopLoadTips'), 'startBackground': document.getElementById('startBackground'), + 'startLogo': document.getElementById('startLogo'), 'startButtonGroup': document.getElementById('startButtonGroup'), 'floorMsgGroup': document.getElementById('floorMsgGroup'), 'logoLabel': document.getElementById('logoLabel'), @@ -33,9 +34,6 @@ function main() { 'loadGame': document.getElementById('loadGame'), 'replayGame': document.getElementById('replayGame'), 'levelChooseButtons': document.getElementById('levelChooseButtons'), - 'easyLevel': document.getElementById('easyLevel'), - 'normalLevel': document.getElementById('normalLevel'), - 'hardLevel': document.getElementById('hardLevel'), 'data': document.getElementById('data'), 'statusLabels': document.getElementsByClassName('statusLabel'), 'floorCol': document.getElementById('floorCol'), @@ -52,7 +50,7 @@ function main() { 'items', 'icons', 'maps', 'enemys', 'events', 'data', 'ui', 'core' ]; this.pureData = [ - "data","enemys","icons","maps","items" + "data","enemys","icons","maps","items","functions" ]; this.images = [ 'animates', 'enemys', 'hero', 'items', 'npcs', 'terrains' @@ -128,6 +126,21 @@ main.prototype.init = function (mode) { main.loadPureData(function(){ var mainData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main; for(var ii in mainData)main[ii]=mainData[ii]; + + main.dom.startBackground.src="project/images/"+main.startBackground; + main.dom.startLogo.style=main.startLogoStyle; + main.levelChoose.forEach(function(value){ + var span = document.createElement('span'); + span.setAttribute('class','startButton'); + span.innerText=value[0]; + (function(span,str_){ + span.onclick = function () { + core.events.startGame(str_); + } + })(span,value[1]); + main.dom.levelChooseButtons.appendChild(span); + }); + main.loaderJs(function () { var coreData = {}; for (i = 0; i < main.loadList.length; i++) { @@ -454,20 +467,6 @@ main.dom.replayGame.onclick = function () { }) } -////// 点击“简单难度”时 ////// -main.dom.easyLevel.onclick = function() { - core.events.startGame('Easy'); -} - -////// 点击“普通难度”时 ////// -main.dom.normalLevel.onclick = function () { - core.events.startGame('Normal'); -} - -////// 点击“困难难度”时 ////// -main.dom.hardLevel.onclick = function () { - core.events.startGame('Hard'); -} }//listen end diff --git a/project/data.comment.js b/project/data.comment.js index 7dad7ce9..7a901303 100644 --- a/project/data.comment.js +++ b/project/data.comment.js @@ -7,7 +7,10 @@ data_comment_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "animates": " 在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n 动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" \n 根据需求自行添加 \n$leaf(true)$end", "bgms": " 在此存放所有的bgm,和文件名一致。第一项为默认播放项 \n 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n$leaf(true)$end", "sounds": " 在此存放所有的SE,和文件名一致 \n 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n$leaf(true)$end", - "bgmRemote" : " 是否使用远程的背景音乐;此项一般不要开启 \n$select({\"values\":[false]})$end" + "bgmRemote" : " 是否使用远程的背景音乐;此项一般不要开启 \n$select({\"values\":[false]})$end", + "startBackground" : "标题界面的背景,建议使用jpg格式以压缩背景图空间", + "startLogoStyle" : "标题样式:可以改变颜色,也可以隐藏标题(如果背景图自带)", + "levelChoose" : " 难度选择:每个数组的第一个是其在标题界面显示的难度,第二个是在游戏内部传输的字符串,会显示在状态栏,修改此处后需要在project/functions中作相应更改 \n$leaf(true)$end" }, "firstData": { "title": " 游戏名,将显示在标题页面以及切换楼层的界面中 ", diff --git a/project/data.js b/project/data.js index d47d1f2c..d8988833 100644 --- a/project/data.js +++ b/project/data.js @@ -33,6 +33,10 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 ], "bgmRemote" : false, // 是否使用远程的背景音乐;此项一般不要开启 + "startBackground" : "bg.png",// 标题界面的背景,建议使用jpg格式以压缩背景图空间 + "startLogoStyle" : "color: black",// 标题样式:可以改变颜色,也可以隐藏标题(如果背景图自带) + "levelChoose" : [["简单","Easy"],["普通","Normal"],["困难","Hard"],["噩梦","Hell"]], + //难度选择:每个数组的第一个是其在标题界面显示的难度,第二个是在游戏内部传输的字符串,会显示在状态栏,修改此处后需要在project/functions中作相应更改 }, "firstData" : { "title": "魔塔样板", // 游戏名,将显示在标题页面以及切换楼层的界面中 diff --git a/project/functions.comment.js b/project/functions.comment.js new file mode 100644 index 00000000..1540f623 --- /dev/null +++ b/project/functions.comment.js @@ -0,0 +1,21 @@ +functions_comment_d6ad677b_427a_4623_b50f_a445a3b0ef8a = +{ + "events" : { + "setInitData" : "不同难度分别设置初始属性", + "win" : "游戏获胜事件", + "lose" : "游戏失败事件", + "afterChangeFloor":"转换楼层结束的事件", + "addPoint":"加点事件", + "afterBattle" : "战斗结束后触发的事件", + "afterOpenDoor" : "开一个门后触发的事件", + "afterChangeLight" : "改变亮灯之后,可以触发的事件", + "afterPushBox" : "推箱子后的事件", + "afterUseBomb" : "使用炸弹/圣锤后的事件", + "beforeSaveData" : "即将存档前可以执行的操作", + "afterLoadData" : "读档事件后,载入事件前,可以执行的操作" + + }, + "ui" : { + "drawAbout" : "绘制“关于”界面" + } +} \ No newline at end of file diff --git a/project/functions.js b/project/functions.js new file mode 100644 index 00000000..f93652ee --- /dev/null +++ b/project/functions.js @@ -0,0 +1,267 @@ +functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = +{ +"events":{ +////// 不同难度分别设置初始属性 ////// +"setInitData":function (hard) { + if (hard=='Easy') { // 简单难度 + core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度 + // 可以在此设置一些初始福利,比如设置初始生命值可以调用: + // core.setStatus("hp", 10000); + // 赠送一把黄钥匙可以调用 + // core.setItem("yellowKey", 1); + } + if (hard=='Normal') { // 普通难度 + core.setFlag('hard', 2); // 可以用flag:hard来获得当前难度 + } + if (hard=='Hard') { // 困难难度 + core.setFlag('hard', 3); // 可以用flag:hard来获得当前难度 + } + if (hard=='Hell') { // 噩梦难度 + core.setFlag('hard', 4); // 可以用flag:hard来获得当前难度 + } + this.afterLoadData(); +}, +////// 游戏获胜事件 ////// +"win" : function(reason) { + core.ui.closePanel(); + var replaying = core.status.replay.replaying; + core.stopReplay(); + core.waitHeroToStop(function() { + core.removeGlobalAnimate(0,0,true); + core.clearMap('all'); // 清空全地图 + core.drawText([ + "\t[恭喜通关]你的分数是${status:hp}。" + ], function () { + core.events.gameOver('', replaying); + }) + }); +}, +////// 游戏失败事件 ////// +"lose" : function(reason) { + core.ui.closePanel(); + var replaying = core.status.replay.replaying; + core.stopReplay(); + core.waitHeroToStop(function() { + core.drawText([ + "\t[结局1]你死了。\n如题。" + ], function () { + core.events.gameOver(null, replaying); + }); + }) +}, +////// 转换楼层结束的事件 ////// +"afterChangeFloor" : function (floorId) { + if (core.isset(core.status.event.id)) return; // 当前存在事件 + + if (!core.hasFlag("visited_"+floorId)) { + this.doEvents(core.floors[floorId].firstArrive, null, null, function () { + //core.autosave(); + }); + core.setFlag("visited_"+floorId, true); + return; + } + + // 自动存档 + //core.autosave(); +}, +////// 加点事件 ////// +"addPoint" : function (enemy) { + var point = enemy.point; + if (!core.isset(point) || point<=0) return []; + + // 加点,返回一个choices事件 + return [ + {"type": "choices", + "choices": [ + {"text": "攻击+"+(1*point), "action": [ + {"type": "setValue", "name": "status:atk", "value": "status:atk+"+(1*point)} + ]}, + {"text": "防御+"+(2*point), "action": [ + {"type": "setValue", "name": "status:def", "value": "status:def+"+(2*point)} + ]}, + {"text": "生命+"+(200*point), "action": [ + {"type": "setValue", "name": "status:hp", "value": "status:hp+"+(200*point)} + ]}, + ] + } + ]; +}, +////// 战斗结束后触发的事件 ////// +"afterBattle" : function(enemyId,x,y,callback) { + + var enemy = core.material.enemys[enemyId]; + + // 毒衰咒的处理 + var special = enemy.special; + // 中毒 + if (core.enemys.hasSpecial(special, 12) && !core.hasFlag('poison')) { + core.setFlag('poison', true); + } + // 衰弱 + if (core.enemys.hasSpecial(special, 13) && !core.hasFlag('weak')) { + core.setFlag('weak', true); + core.status.hero.atk-=core.values.weakValue; + core.status.hero.def-=core.values.weakValue; + } + // 诅咒 + if (core.enemys.hasSpecial(special, 14) && !core.hasFlag('curse')) { + core.setFlag('curse', true); + } + // 仇恨属性:减半 + if (core.flags.hatredDecrease && core.enemys.hasSpecial(special, 17)) { + core.setFlag('hatred', parseInt(core.getFlag('hatred', 0)/2)); + } + // 自爆 + if (core.enemys.hasSpecial(special, 19)) { + core.status.hero.hp = 1; + } + // 退化 + if (core.enemys.hasSpecial(special, 21)) { + core.status.hero.atk -= (enemy.atkValue||0); + core.status.hero.def -= (enemy.defValue||0); + if (core.status.hero.atk<0) core.status.hero.atk=0; + if (core.status.hero.def<0) core.status.hero.def=0; + } + // 增加仇恨值 + core.setFlag('hatred', core.getFlag('hatred',0)+core.values.hatred); + core.updateStatusBar(); + + + // 事件的处理 + var todo = []; + // 如果不为阻击,且该点存在,且有事件 + if (!core.enemys.hasSpecial(special, 18) && core.isset(x) && core.isset(y)) { + var event = core.floors[core.status.floorId].afterBattle[x+","+y]; + if (core.isset(event)) { + // 插入事件 + core.unshift(todo, event); + } + } + // 如果有加点 + var point = core.material.enemys[enemyId].point; + if (core.isset(point) && point>0) { + core.unshift(todo, core.events.addPoint(core.material.enemys[enemyId])); + } + + // 如果事件不为空,将其插入 + if (todo.length>0) { + this.insertAction(todo,x,y); + } + + // 如果已有事件正在处理中 + if (core.status.event.id == null) { + core.continueAutomaticRoute(); + } + else { + core.clearContinueAutomaticRoute(); + } + if (core.isset(callback)) callback(); + +}, +////// 开一个门后触发的事件 ////// +"afterOpenDoor" : function(doorId,x,y,callback) { + + var todo = []; + if (core.isset(x) && core.isset(y)) { + var event = core.floors[core.status.floorId].afterOpenDoor[x+","+y]; + if (core.isset(event)) { + core.unshift(todo, event); + } + } + + if (todo.length>0) { + this.insertAction(todo,x,y); + } + + if (core.status.event.id == null) { + core.continueAutomaticRoute(); + } + else { + core.clearContinueAutomaticRoute(); + } + if (core.isset(callback)) callback(); +}, +////// 改变亮灯之后,可以触发的事件 ////// +"afterChangeLight" : function(x,y) { + +}, +////// 推箱子后的事件 ////// +"afterPushBox" : function () { + + var noBoxLeft = function () { + // 地图上是否还存在未推到的箱子,如果不存在则返回true,存在则返回false + for (var i=0;i