diff --git a/README.md b/README.md index 54f5ff23..1882e388 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! **即使完全不会编程的用户,按照模板和说明文档也能很快做出一个魔塔游戏!** -* [List / HTML5魔塔游戏列表](http://mota.pw/) +* [List / HTML5魔塔游戏列表](https://h5mota.com/) * [Demo / 样板效果](https://ckcz123.com/games/template/) * [Docs / 使用文档说明](https://ckcz123.github.io/mota-js/) * [Video / 视频教程](http://www.bilibili.com/video/av17608025/) @@ -35,18 +35,12 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! │ ├─ /animates/ # 动画目录 │ ├─ /floors/ # 剧本文件,记录了每个地图的数据和事件 │ ├─ /images/ # 所有图片素材目录 -│ │ ├─ /常用素材/ # 可以被直接替换的素材 -│ │ └─ *.png # 对应的某个具体的图片素材 │ ├─ /sounds/ # 音效目录 -│ ├─ comments.js # 对怪物、道具、楼层等的注释 -│ ├─ data.comment.js # 对全局变量的注释 │ ├─ data.js # 全局变量信息 │ ├─ enemys.js # 怪物属性数据 -│ ├─ functions.comment.js # 脚本编辑的注释 │ ├─ functions.js # 可能会被修改的脚本代码 │ ├─ icons.js # 素材和ID的对应关系定义 │ ├─ items.js # 道具的定义,获得道具的效果 -│ ├─ maps.commment.js # 地图信息的注释 │ └─ maps.js # 地图和数字的对应关系 ├── /常用工具/ # 一些常用工具,可以辅助造塔 │ ├─ RM动画导出器.exe # 能从RMXP中导出动画,以供H5使用。 http://github.com/ckcz123/animate_export/ @@ -224,4 +218,4 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! HTML5魔塔交流群群号: `539113091` -如有其它意见或建议,也可以通过发[issues](https://github.com/ckcz123/mota-js/issues)、或邮件至[ckcz123.com](mailto:ckcz123.com)联系我。 +如有其它意见或建议,也可以通过发[issues](https://github.com/ckcz123/mota-js/issues)、或邮件至[ckcz123@126.com](mailto:ckcz123@126.com)联系我。 diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index c80796fc..48e10939 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -236,7 +236,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 +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 : ["欢迎使用事件编辑器(双击方块进入多行编辑)"] var code = '"'+EvalString_0+'",\n'; return code; @@ -248,7 +248,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 +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","","欢迎使用事件编辑器(双击方块进入多行编辑)"] var title=''; if (EvalString_0==''){ @@ -272,7 +272,7 @@ autoText_s /* 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 +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==''){ @@ -296,7 +296,7 @@ setText_s /* setText_s 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 +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,"","","",null,''] 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)?$/; @@ -327,7 +327,7 @@ tip_s /* tip_s tooltip : tip:显示一段提示文字 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=tip%ef%bc%9a%e6%98%be%e7%a4%ba%e4%b8%80%e6%ae%b5%e6%8f%90%e7%a4%ba%e6%96%87%e5%ad%97 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=tip%EF%BC%9A%E6%98%BE%E7%A4%BA%E4%B8%80%E6%AE%B5%E6%8F%90%E7%A4%BA%E6%96%87%E5%AD%97 default : ["这段话将在左上角以气泡形式显示"] var code = '{"type": "tip", "text": "'+EvalString_0+'"},\n'; return code; @@ -339,37 +339,41 @@ setValue_s /* setValue_s tooltip : setValue:设置勇士的某个属性、道具个数, 或某个变量/Flag的值 -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 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=setvalue%EF%BC%9A%E8%AE%BE%E7%BD%AE%E5%8B%87%E5%A3%AB%E7%9A%84%E6%9F%90%E4%B8%AA%E5%B1%9E%E6%80%A7%E3%80%81%E9%81%93%E5%85%B7%E4%B8%AA%E6%95%B0%EF%BC%8C%E6%88%96%E6%9F%90%E4%B8%AA%E5%8F%98%E9%87%8Fflag%E7%9A%84%E5%80%BC colour : this.dataColor var code = '{"type": "setValue", "name": "'+idString_e_0+'", "value": "'+expression_0+'"},\n'; return code; */; show_s - : '显示事件' 'x' EvalString ',' 'y' EvalString '楼层' IdString? '动画时间' Int? Newline + : '显示事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' Int? Newline /* show_s tooltip : show: 将禁用事件启用,楼层和动画时间可不填,xy可用逗号分隔表示多个点 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=show-%e5%b0%86%e4%b8%80%e4%b8%aa%e7%a6%81%e7%94%a8%e4%ba%8b%e4%bb%b6%e5%90%af%e7%94%a8 -default : ["0","0","",500] +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=show%EF%BC%9A%E5%B0%86%E4%B8%80%E4%B8%AA%E7%A6%81%E7%94%A8%E4%BA%8B%E4%BB%B6%E5%90%AF%E7%94%A8 +default : ["","","",500] colour : this.eventColor -IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); -var pattern1 = /^flag:[0-9a-zA-Z_][0-9a-zA-Z_\-:]*$/; -if(pattern1.test(EvalString_0) || pattern1.test(EvalString_1)){ - EvalString_0=MotaActionFunctions.PosString_pre(EvalString_0); - EvalString_1=MotaActionFunctions.PosString_pre(EvalString_1); - EvalString_0=[EvalString_0,EvalString_1] -} else { - var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; - if(!pattern2.test(EvalString_0) || !pattern2.test(EvalString_1))throw new Error('坐标格式错误,请右键点击帮助查看格式'); - EvalString_0=EvalString_0.split(','); - EvalString_1=EvalString_1.split(','); - if(EvalString_0.length!==EvalString_1.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); - for(var ii=0;ii10) throw new Error('天气的强度等级, 在1-10之间'); @@ -777,7 +769,7 @@ move_s /* move_s tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=move-%e8%ae%a9%e6%9f%90%e4%b8%aanpc%e6%80%aa%e7%89%a9%e7%a7%bb%e5%8a%a8 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=move%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AAnpc%E6%80%AA%E7%89%A9%E7%A7%BB%E5%8A%A8 default : ["","",500,null,"上右3下2左上左2"] colour : this.eventColor var floorstr = ''; @@ -795,7 +787,7 @@ moveHero_s /* moveHero_s tooltip : moveHero:移动勇士,用这种方式移动勇士的过程中将无视一切地形, 无视一切事件, 中毒状态也不会扣血 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=movehero%ef%bc%9a%e7%a7%bb%e5%8a%a8%e5%8b%87%e5%a3%ab +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=movehero%EF%BC%9A%E7%A7%BB%E5%8A%A8%E5%8B%87%E5%A3%AB default : [500,"上右3下2左上左2"] colour : this.dataColor Int_0 = Int_0 ?(', "time": '+Int_0):''; @@ -809,7 +801,7 @@ playBgm_s /* playBgm_s tooltip : playBgm: 播放背景音乐 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=playbgm-%e6%92%ad%e6%94%be%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=playbgm%EF%BC%9A%E6%92%AD%E6%94%BE%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 default : ["bgm.mp3"] colour : this.soundColor var code = '{"type": "playBgm", "name": "'+EvalString_0+'"},\n'; @@ -822,7 +814,7 @@ pauseBgm_s /* pauseBgm_s tooltip : pauseBgm: 暂停背景音乐 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=pausebgm-%e6%9a%82%e5%81%9c%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=pausebgm%EF%BC%9A%E6%9A%82%E5%81%9C%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 colour : this.soundColor var code = '{"type": "pauseBgm"},\n'; return code; @@ -834,7 +826,7 @@ resumeBgm_s /* resumeBgm_s tooltip : resumeBgm: 恢复背景音乐 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=resumebgm-%e6%81%a2%e5%a4%8d%e8%83%8c%e6%99%af%e9%9f%b3%e4%b9%90 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=resumebgm%EF%BC%9A%E6%81%A2%E5%A4%8D%E8%83%8C%E6%99%AF%E9%9F%B3%E4%B9%90 colour : this.soundColor var code = '{"type": "resumeBgm"},\n'; return code; @@ -846,8 +838,8 @@ playSound_s /* playSound_s tooltip : playSound: 播放音效 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=playsound-%e6%92%ad%e6%94%be%e9%9f%b3%e6%95%88 -default : ["item.ogg"] +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=playsound%EF%BC%9A%E6%92%AD%E6%94%BE%E9%9F%B3%E6%95%88 +default : ["item.mp3"] colour : this.soundColor var code = '{"type": "playSound", "name": "'+EvalString_0+'"},\n'; return code; @@ -859,7 +851,7 @@ setVolume_s /* setVolume_s tooltip : setVolume: 设置音量 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=setVolume-%e8%ae%be%e7%bd%ae%e9%9f%b3%e9%87%8f +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=setvolume%EF%BC%9A%E8%AE%BE%E7%BD%AE%E9%9F%B3%E9%87%8F default : [90] colour : this.soundColor var code = '{"type": "setVolume", "value": '+Int_0+'},\n'; @@ -872,7 +864,7 @@ win_s /* win_s tooltip : win: 获得胜利, 该事件会显示获胜页面, 并重新游戏 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=win-%e8%8e%b7%e5%be%97%e8%83%9c%e5%88%a9 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=win%EF%BC%9A%E8%8E%B7%E5%BE%97%E8%83%9C%E5%88%A9 default : [""] var code = '{"type": "win", "reason": "'+EvalString_0+'"},\n'; return code; @@ -884,7 +876,7 @@ lose_s /* lose_s tooltip : lose: 游戏失败, 该事件会显示失败页面, 并重新开始游戏 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=lose-%e6%b8%b8%e6%88%8f%e5%a4%b1%e8%b4%a5 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=lose%EF%BC%9A%E6%B8%B8%E6%88%8F%E5%A4%B1%E8%B4%A5 default : [""] var code = '{"type": "lose", "reason": "'+EvalString_0+'"},\n'; return code; @@ -896,7 +888,7 @@ input_s /* input_s tooltip : input:接受用户输入, 事件只能接受非负整数输入, 所有非法的输入将全部变成0 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=input%ef%bc%9a%e6%8e%a5%e5%8f%97%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=input%EF%BC%9A%E6%8E%A5%E5%8F%97%E7%94%A8%E6%88%B7%E8%BE%93%E5%85%A5 default : ["请输入一个数"] colour : this.dataColor var code = '{"type": "input", "text": "'+EvalString_0+'"},\n'; @@ -909,7 +901,7 @@ if_s /* if_s tooltip : if: 条件判断 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=if-%e6%9d%a1%e4%bb%b6%e5%88%a4%e6%96%ad +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=if%EF%BC%9A%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD colour : this.eventColor var code = ['{"type": "if", "condition": "',expression_0,'",\n', '"true": [\n',action_0,'],\n', @@ -918,6 +910,43 @@ var code = ['{"type": "if", "condition": "',expression_0,'",\n', return code; */; +choices_s + : '选项' ':' EvalString? BGNL? '标题' EvalString? '图像' IdString? BGNL? Newline choicesContext+ BEND Newline + + +/* choices_s +tooltip : choices: 给用户提供选项 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 +default : ["","流浪者","woman"] +var title=''; +if (EvalString_1==''){ + if (IdString_0=='')title=''; + else title='\\t['+IdString_0+']'; +} else { + if (IdString_0=='')title='\\t['+EvalString_1+']'; + else title='\\t['+EvalString_1+','+IdString_0+']'; +} +EvalString_0 = title+EvalString_0; +EvalString_0 = EvalString_0 ?(', "text": "'+EvalString_0+'"'):''; +var code = ['{"type": "choices"',EvalString_0,', "choices": [\n', + choicesContext_0, +']},\n'].join(''); +return code; +*/; + +choicesContext + : '子选项' EvalString BGNL? Newline action+ + + +/* choicesContext +tooltip : 选项的选择 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=choices%EF%BC%9A%E7%BB%99%E7%94%A8%E6%88%B7%E6%8F%90%E4%BE%9B%E9%80%89%E9%A1%B9 +default : ["提示文字:红钥匙"] +colour : this.subColor +var code = '{"text": "'+EvalString_0+'", "action": [\n'+action_0+']},\n'; +return code; +*/; + while_s : '循环处理' ':' '当' expression '时' BGNL? Newline action+ BEND Newline @@ -953,40 +982,16 @@ var code = '{"type": "continue"},\n'; return code; */; -choices_s - : '选项' ':' EvalString? BGNL? '标题' EvalString? '图像' IdString? BGNL? Newline choicesContext+ BEND Newline - -/* choices_s -tooltip : choices: 给用户提供选项 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=choices-%e7%bb%99%e7%94%a8%e6%88%b7%e6%8f%90%e4%be%9b%e9%80%89%e9%a1%b9 -default : ["","流浪者","woman"] -var title=''; -if (EvalString_1==''){ - if (IdString_0=='')title=''; - else title='\\t['+IdString_0+']'; -} else { - if (IdString_0=='')title='\\t['+EvalString_1+']'; - else title='\\t['+EvalString_1+','+IdString_0+']'; -} -EvalString_0 = title+EvalString_0; -EvalString_0 = EvalString_0 ?(', "text": "'+EvalString_0+'"'):''; -var code = ['{"type": "choices"',EvalString_0,', "choices": [\n', - choicesContext_0, -']},\n'].join(''); -return code; -*/; +wait_s + : '等待用户操作并获得按键或点击信息' -choicesContext - : '子选项' EvalString BGNL? Newline action+ - -/* choicesContext -tooltip : 选项的选择 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=choices-%e7%bb%99%e7%94%a8%e6%88%b7%e6%8f%90%e4%be%9b%e9%80%89%e9%a1%b9 -default : ["提示文字:红钥匙"] -colour : this.subColor -var code = '{"text": "'+EvalString_0+'", "action": [\n'+action_0+']},\n'; +/* wait_s +tooltip : wait: 等待用户操作并获得按键或点击信息(具体用法看文档) +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=wait%EF%BC%9A%E7%AD%89%E5%BE%85%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C +colour : this.soundColor +var code = '{"type": "wait"},\n'; return code; */; @@ -995,8 +1000,8 @@ function_s /* function_s -tooltip : function: 自定义JS脚本 -helpUrl : https://ckcz123.github.io/mota-js/#/event?id=function-%e8%87%aa%e5%ae%9a%e4%b9%89js%e8%84%9a%e6%9c%ac +tooltip : 可双击多行编辑,请勿使用异步代码。常见API参见文档附录。 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=function%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89js%E8%84%9A%E6%9C%AC default : ["alert(core.getStatus(\"atk\"));"] colour : this.dataColor var code = '{"type": "function", "function": "function(){\\n'+JSON.stringify(RawEvalString_0).slice(1,-1).split('\\\\n').join('\\n')+'\\n}"},\n'; @@ -1380,8 +1385,7 @@ ActionParser.prototype.parseAction = function() { break; case "tip": this.next = MotaActionBlocks['tip_s'].xmlText([ - data.data,this.next]); - this.parseAction(); + data.text,this.next]); break; case "show": // 显示 if (!(data.loc[0] instanceof Array)) diff --git a/_server/data.comment.js b/_server/data.comment.js index 65db31e4..d431715b 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -415,6 +415,12 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否允许等级提升(进阶);如果上面enableExperience为false,则此项恒视为false" }, + "enableKeys": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否在状态栏显示三色钥匙数量" + }, "enableDebuff": { "_leaf": true, "_type": "checkbox", diff --git a/_server/editor.js b/_server/editor.js index 7f9d1fca..d2a6b517 100644 --- a/_server/editor.js +++ b/_server/editor.js @@ -542,6 +542,29 @@ editor.prototype.listen = function () { currDrawData = JSON.parse(JSON.stringify(reDo)); reDo = null; } + // PGUP和PGDOWN切换楼层 + if (e.keyCode==33) { + e.preventDefault(); + var index=editor.core.floorIds.indexOf(editor.currentFloorId); + if (index>0) { + var toId = editor.core.floorIds[index-1]; + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + document.getElementById('selectFloor').value = toId; + editor.changeFloor(toId); + } + } + if (e.keyCode==34) { + e.preventDefault(); + var index=editor.core.floorIds.indexOf(editor.currentFloorId); + if (index', + MotaActionFunctions.actionParser.parse([ + {"type": "setValue", "name": "flag:y", "value": "core.status.event.data.y-2"}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x-1"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x+1"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:y", "value": "core.status.event.data.y-1"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x-1"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:y", "value": "core.status.event.data.y"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + {"type": "setValue", "name": "flag:x", "value": "core.status.event.data.x+1"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]}, + ],'afterBattle'), '', MotaActionFunctions.actionParser.parse({ "trigger": "action", diff --git a/_server/editor_file.js b/_server/editor_file.js index ae7dac9e..711a1bb1 100644 --- a/_server/editor_file.js +++ b/_server/editor_file.js @@ -161,11 +161,17 @@ editor_file = function (editor, callback) { var image = info.images; - if (image!='items' && image.indexOf('enemy')!=0) { - callback('只有怪物和道具才能自动注册!'); + if (image=='autotile') { + callback('不能对自动元件进行自动注册!'); return; } - var c=image=='items'?'I':'M'; + var c=image.toUpperCase().charAt(0); + + // terrains id + var terrainsId = []; + Object.keys(core.material.icons.terrains).forEach(function (id) { + terrainsId[core.material.icons.terrains[id]]=id; + }) var allIds = []; editor.ids.forEach(function (v) { @@ -180,17 +186,25 @@ editor_file = function (editor, callback) { for (var y=0; y0) + saveSetting('icons', iconActions, tempcallback); + else tempcallback(null); + saveSetting('maps', mapActions, tempcallback); + if (image=='items') saveSetting('items', templateActions, tempcallback); - else + else if (image.indexOf('enemy')==0) saveSetting('enemys', templateActions, tempcallback); + else tempcallback(null); } editor_file.changeIdAndIdnum = function (id, idnum, info, callback) { diff --git a/_server/fs.js b/_server/fs.js index 751b7f30..59bd1441 100644 --- a/_server/fs.js +++ b/_server/fs.js @@ -1,32 +1,57 @@ (function () { fs = {}; - var postsomething = function (data, _ip, callback) { - //callback:function(err, data) - //data:字符串 + + + var _http = function (type, url, formData, success, error, mimeType, responseType) { var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () { - switch (xhr.readyState) { - case 4 : - if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { - if (Boolean(callback)) { - if (xhr.responseText.slice(0, 6) == 'error:') { - callback(xhr.responseText, null); - } else { - callback(null, xhr.responseText); - } - } - //printf(xhr.responseText) - } else { - if (Boolean(callback)) callback(xhr.status, null); - //printf('error:' + xhr.status+'
'+(xhr.responseText||'')); - } - break; + xhr.open(type, url, true); + if (core.isset(mimeType)) + xhr.overrideMimeType(mimeType); + if (core.isset(responseType)) + xhr.responseType = responseType; + xhr.onload = function(e) { + if (xhr.status==200) { + if (core.isset(success)) { + success(xhr.response); + } } + else { + if (core.isset(error)) + error("HTTP "+xhr.status); + } + }; + xhr.onabort = function () { + if (core.isset(error)) + error("Abort"); } - xhr.open('post', _ip); - xhr.setRequestHeader('Content-Type', 'text/plain'); + xhr.ontimeout = function() { + if (core.isset(error)) + error("Timeout"); + } + xhr.onerror = function() { + if (core.isset(error)) + error("Error on Connection"); + } + if (core.isset(formData)) + xhr.send(formData); + else xhr.send(); + } + + + var postsomething = function (data, _ip, callback) { if (typeof(data) == typeof([][0]) || data == null) data = JSON.stringify({1: 2}); - xhr.send(data); + + _http("POST", _ip, data, function (data) { + if (data.slice(0, 6) == 'error:') { + callback(data, null); + } + else { + callback(null, data); + } + }, function (e) { + console.log(e); + callback(e+":请检查启动服务是否处于正常运行状态。"); + }, "text/plain; charset=x-user-defined"); } fs.readFile = function (filename, encoding, callback) { diff --git a/docs/_api.md b/docs/_api.md new file mode 100644 index 00000000..1939aa2c --- /dev/null +++ b/docs/_api.md @@ -0,0 +1,367 @@ +# 附录:API列表 + +?> 目前版本**v2.2.1**,上次更新时间:* {docsify-updated} * + +所有系统支持的API都列在了这里。所有可能被用到的API都在前面用\*标记。 + +可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 + +!> **`main.js`:游戏入口,所有其他JS文件都是被此文件加载。** + +``` js +main.init // 初始化 +main.loaderJs // 动态加载所有核心JS文件 +main,loaderFloors // 动态加载所有楼层(剧本) +main.loadMod // 加载某一个JS文件 +main.loadFloor // 加载某一个楼层 +main.setMainTipsText // 加载过程提示 +window.onresize // 窗口大小变化时 +main.dom.body.onkeydown // 在界面上按下某按键时 +main.dom.body.onkeydown // 在界面上放开某按键时 +main.dom.body.onselectstart // 开始选择时 +main.dom.data.onmousedown // 鼠标按下时 +main.dom.data.onmousemove // 鼠标移动时 +main.dom.data.onmouseup // 鼠标放开时 +main.dom.data.onmousewheel // 鼠标滑轮滚动时 +main.dom.data.ontouchstart // 手指在触摸屏开始触摸时 +main.dom.data.ontouchmove // 手指在触摸屏上移动时 +main.dom.data.ontouchend // 手指离开触摸屏时 +main.statusBar.image.book.onclick // 点击状态栏中的怪物手册时 +main.statusBar.image.fly.onclick // 点击状态栏中的楼层传送器时 +main.statusBar.image.toolbox.onclick // 点击状态栏中的工具箱时 +main.statusBar.image.shop.onclick // 点击状态栏中的快捷商店时 +main.statusBar.image.save.onclick // 点击状态栏中的存档按钮时 +main.statusBar.image.load.onclick // 点击状态栏中的读档按钮时 +main.statusBar.image.settings.onclick // 点击状态栏中的系统菜单时 +main.dom.playGame.onclick // 点击“开始游戏”时 +main.dom.loadGame.onclick // 点击“载入游戏”时 +main.dom.replayGame.onclick // 点击“录像回放”时 +main.dom.easyLevel.onclick // 点击“简单难度”时 +main.dom.normalLevel.onclick // 点击“普通难度”时 +main.dom.hardLevel.onclick // 点击“困难难度”时 +``` + +!> **`core.js`:系统核心文件。所有核心逻辑处理都在此文件完成。** + +``` js +* core.status.floorId // 获得当前层floorId +* core.status.thisMap // 获得当前层的地图信息 +* core.status.maps // 获得所有楼层的地图信息 +* core.floors // 获得所有楼层的剧本 + +// ------ 初始化部分 ------ +core.init // 初始化 +core.showStartAnimate // 显示游戏开始界面 +core.hideStartAnimate // 隐藏游戏开始界面 +core.setStartProgressVal // 设置加载进度条进度 +core.setStartLoadTipText // 设置加载进度条提示文字 +core.loader // 加载图片和音频 +core.loadAutotile // 加载Autotile +core.loadImage // 加载图片 +core.loadMusic // 加载音频 +core.isPlaying // 游戏是否已经开始 +core.clearStatus // 清除游戏状态和数据 +core.resetStatus // 重置游戏状态和初始数据 +core.startGame // 开始游戏 +* core.restart // 重新开始游戏;此函数将回到标题页面 + +// ------ 键盘、鼠标事件 ------ +core.onKeyDown // 按下某个键时 +core.onKeyUp // 放开某个键时 +core.pressKey // 按住某个键时 +core.keyDown // 根据按下键的code来执行一系列操作 +core.keyUp // 根据放开键的code来执行一系列操作 +core.ondown // 点击(触摸)事件按下时 +core.onmove // 当在触摸屏上滑动时 +core.onup // 当点击(触摸)事件放开时 +core.getClickLoc // 获得点击事件相对左上角的坐标(0到12之间) +core.onclick // 具体点击屏幕上(x,y)点时,执行的操作 +core.onmousewheel // 滑动鼠标滚轮时的操作 + +// ------ 自动寻路代码相关 ------ +core.clearAutomaticRouteNode // 清除自动寻路路线 +core.stopAutomaticRoute // 停止自动寻路操作 +core.continueAutomaticRoute // 继续剩下的自动寻路操作 +core.clearContinueAutomaticRoute // 清空剩下的自动寻路列表 +core.setAutomaticRoute // 设置自动寻路路线 +core.automaticRoute // 自动寻路算法,找寻最优路径 +core.fillPosWithPoint // 显示离散的寻路点 +core.clearStepPostfix // 清除已经寻路过的部分 + +// ------ 自动行走,行走控制 ------ +core.stopAutoHeroMove // 停止勇士的自动行走 +core.setAutoHeroMove // 设置勇士的自动行走路线 +core.autoHeroMove // 让勇士开始自动行走 +core.setHeroMoveInterval // 设置行走的效果动画 +core.setHeroMoveTriggerInterval // 设置勇士行走过程中对事件的触发检测 +core.moveAction // 实际每一步的行走过程 +* core.turnHero(direction) // 设置勇士的方向(转向) +core.canMoveHero // 勇士能否前往某方向 +core.moveHero // 让勇士开始移动 +core.eventMoveHero // 使用事件让勇士移动。这个函数将不会触发任何事件。 +core.moveOneStep // 每移动一格后执行的事件。中毒时在这里进行扣血判断。 +core.waitHeroToStop(callback) // 停止勇士的一切行动,等待勇士行动结束后,再执行callback回调函数。 +core.stopHero // 停止勇士的移动状态。 +core.drawHero // 绘制勇士。 +* core.setHeroLoc(name, value) // 设置勇士的位置。name为”direction”,”x”,”y” +* core.getHeroLoc(name) // 获得勇士的位置。 +* core.nextX // 获得勇士面对位置的x坐标 +* core.nextY // 获得勇士面对位置的y坐标 + +// ------ 地图和事件处理 ------ +* core.openDoor(id, x, y, needKey, callback) // 打开一扇位于 (x,y) 的门 +* core.battle(id, x, y, force, callback) // 进行战斗;force表示是否强制战斗 +core.afterBattle // 战斗完毕 +core.trigger(x,y) // 触发x,y点的事件 +* core.changeFloor(floorId, stair, heroLoc, time, callback) // 楼层切换。floorId为目标楼层Id,stair可指定为上/下楼梯,time动画时间 +core.mapChangeAnimate // 地图切换动画效果 +core.clearMap // 清除地图 +core.fillText // 在某个canvas上绘制一段文字 +core.fillRect // 在某个canvas上绘制一个矩形 +core.strokeRect // 在某个canvas上绘制一个矩形的边框 +core.drawLine // 在某个canvas上绘制一条线 +core.setFont // 设置某个canvas的文字字体 +core.setLineWidth // 设置某个canvas的线宽度 +core.saveCanvas // 保存某个canvas状态 +core.loadCanvas // 加载某个canvas状态 +core.setStrokeStyle // 设置某个canvas边框属性 +core.setAlpha // 设置某个canvas的alpha值 +core.setOpacity // 设置某个canvas的透明度 +core.setFillStyle // 设置某个canvas的绘制属性(如颜色等) +* core.drawMap(mapId, callback) // 绘制某张地图。mapId为地图Id,绘制完毕将执行callback回调函数。 +core.drawAutotile // 绘制Autotile +* core.noPassExists(x,y) // 某个点是否不可通行 +core.noPass // 某个点是否在区域内且不可通行 +* core.npcExists(x,y) // 某个点是否存在NPC +* core.terrainExists(x,y) // 某个点是否存在(指定的)地形 +* core.stairExists(x,y) // 某个点是否存在楼梯 +* core.nearStair // 当前位置是否在楼梯边 +* core.enemyExists(x,y) // 某个点是否存在(指定的)怪物 +* core.getBlock(x, y, floorId, needEnable) // 获得某个点的block。floorId指定目标楼层,needEnable如果为false则即使该点的事件处于禁用状态也将被返回(否则只有事件启用的点才被返回) +core.moveBlock // 显示移动某块的动画,达到{“type”:”move”}的效果 +core.animateBlock // 显示/隐藏某个块时的动画效果 +core.showBlock // 将某个块从禁用变成启用状态 +core.removeBlock // 将某个块从启用变成禁用状态 +core.removeBlockById // 根据block的索引删除该块 +core.removeBlockByIds // 一次性删除多个block +core.addGlobalAnimate // 添加一个全局动画 +core.removeGlobalAnimate // 删除一个或所有全局动画 +core.setGlobalAnimate // 设置全局动画的显示效果 +core.syncGlobalAnimate // 同步所有的全局动画效果 +core.drawBoxAnimate // 绘制UI层的box动画 +core.updateCheckBlock // 更新领域、夹击、阻击的伤害地图 +core.checkBlock // 检查并执行领域、夹击、阻击事件 +core.snipe // 阻击事件(动画效果) +core.setFg // 更改画面色调 +* core.updateFg // 更新全地图显伤 +* core.itemCount // 获得某个物品的个数 +* core.hasItem // 是否存在某个物品 +* core.setItem // 设置某个物品的个数 +* core.removeItem // 删除某个物品 +* core.useItem // 使用某个物品;直接调用items.js中的useItem函数。 +* core.canUseItem // 能否使用某个物品。直接调用items.js中的canUseItem函数。 +* core.addItem // 增加某个物品的个数 +core.getNextItem // 获得面前的物品(轻按) +* core.getItem // 获得某个物品 +* core.drawTip // 左上角绘制一段提示 +* core.drawText // 地图中间绘制一段文字 + +// ------ 系统机制 ------ +core.replaceText // 将文字中的${和}(表达式)进行替换 +core.calValue // 计算表达式的值 +core.doEffect // 执行一个表达式的effect操作 +core.splitLines // 字符串自动换行的分割 +core.unshift // 向某个数组前插入另一个数组或元素 +core.setLocalStorage // 设置本地存储 +core.getLocalStorage // 获得本地存储 +core.removeLocalStorage // 移除本地存储 +core.clone // 深拷贝一个对象 +core.formatDate // 格式化时间为字符串 +core.formatDate2 // 格式化时间为最简字符串 +core.setTwoDigits // 两位数显示 +core.debug // 进入Debug模式,攻防血和钥匙都调成很高的数值 +core.replay // 开始回放 +core.checkStatus // 判断当前能否进入某个事件 +core.openBook // 点击怪物手册时的打开操作 +core.useFly // 点击楼层传送器时的打开操作 +core.openToolbox // 点击工具栏时的打开操作 +core.openQuickShop // 点击快捷商店时的打开操作 +core.save // 点击保存按钮时的打开操作 +core.load // 点击读取按钮时的打开操作 +core.openSettings // 点击设置按钮时的打开操作 +core.autosave // 自动存档 +core.doSL // 实际进行存读档事件 +core.syncSave // 存档同步操作 +core.saveData // 存档到本地 +core.loadData // 从本地读档 +core.encodeRoute // 将路线压缩 +core.decodeRoute // 将路线解压缩 +* core.setStatus // 设置勇士属性 +* core.getStatus // 获得勇士属性 +core.getLvName // 获得某个等级的名称 +* core.setFlag // 设置某个自定义变量或flag +* core.getFlag // 获得某个自定义变量或flag +* core.hasFlag // 是否存在某个自定义变量或flag,且值为true +core.insertAction // 往当前事件列表之前插入一系列事件 +* core.lockControl // 锁定状态栏,常常用于事件处理 +* core.unlockControl // 解锁状态栏 +* core.isset // 判断某对象是否不为undefined也不会null +core.readFile // 读取一个本地文件内容 +core.download // 下载文件到本地 +core.copy // 复制一段文字到剪切板 +* core.playBgm // 播放背景音乐 +* core.pauseBgm // 暂停背景音乐的播放 +* core.resumeBgm // 恢复背景音乐的播放 +* core.playSound // 播放音频 +core.show // 动画显示某对象 +core.hide // 动画使某对象消失 +core.clearStatusBar // 清空状态栏 +core.updateStatusBar // 更新状态栏 +core.resize // 屏幕分辨率改变后重新自适应 +core.domRenderer // 渲染DOM + +// ------ core.js 结束 ------ +``` + +!> **`data.js` 定义了一些初始化的数据信息。** + +!> **`enemys.js` 定义了怪物信息。** + +``` js +core.enemys.init // 初始化 +* core.enemys.getEnemys // 获得一个或所有怪物数据 +* core.enemys.hasSpecial // 判断是否含有某特殊属性 +* core.enemys.getSpecialText // 获得所有特殊属性的名称 +* core.enemys.getSpecialHint // 获得每个特殊属性的说明 +* core.enemys.getDamage // 获得某个怪物的伤害 +* core.enemys.getExtraDamage // 获得某个怪物的额外伤害 +* core.enemys.getCritical // 临界值计算 +* core.enemys.getCriticalDamage // 临界减伤计算 +* core.enemys.getDefDamage // 1防减伤计算 +* core.enemys.calDamage // 具体的伤害计算公式 +core.enemys.getCurrentEnemys // 获得当前楼层的怪物列表 +``` + +!> **`events.js` 定义了各个事件的处理流程。** + +``` js +core.events.init // 初始化 +core.events.getEvents // 获得一个或所有系统事件类型 +core.events.startGame // 游戏开始事件 +* core.events.setInitData // 不同难度分别设置初始属性 +* core.events.win // 游戏获胜事件 +* core.events.lose // 游戏失败事件 +core.evens.gameOver // 游戏结束 +core.events.afterChangeFloor // 转换楼层结束的事件 +core.events.doEvents // 开始执行一系列自定义事件 +core.events.doAction // 执行当前自定义事件列表中的下一个事件 +core.events.insertAction // 往当前事件列表之前添加一个或多个事件 +core.events.openShop // 打开一个全局商店 +core.events.disableQuickShop // 禁用一个全局商店 +* core.events.canUseQuickShop // 当前能否使用快捷商店 +* core.events.checkLvUp // 检查升级事件 +* core.events.useItem // 尝试使用道具 +core.events.addPoint // 加点事件 +core.events.afterBattle // 战斗结束后触发的事件 +core.events.afterOpenDoor // 开一个门后触发的事件 +core.events.passNet // 经过一个路障 +core.events.changeLight // 改变亮灯(感叹号)的事件 +* core.events.afterChangeLight // 改变亮灯之后,可以触发的事件 +* core.events.afterUseBomb // 使用炸弹/圣锤后的事件 +* core.events.beforeSaveData // 即将存档前可以执行的操作 +* core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作 + +// ------ 点击事件和键盘事件的处理 ------ +core.events.longClick // 长按 +core.events.keyDownCtrl // 按下Ctrl键时(快捷跳过对话) +core.events.clickConfirmBox // 确认框界面时的点击操作 +core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作 +core.events.clickAction // 自定义事件时的点击操作 +core.events.keyDownAction // 自定义事件时,按下某个键的操作 +core.events.keyUpAction // 自定义事件时,放开某个键的操作 +core.events.clickBook // 怪物手册界面的点击操作 +core.events.keyDownBook // 怪物手册界面时,按下某个键的操作 +core.events.keyUpBook // 怪物手册界面时,放开某个键的操作 +core.events.clickBookDetail // 怪物手册属性显示界面时的点击操作 +core.events.clickFly // 楼层传送器界面时的点击操作 +core.events.keyDownFly // 楼层传送器界面时,按下某个键的操作 +core.events.keyUpFly // 楼层传送器界面时,放开某个键的操作 +core.events.clickViewMaps // 浏览地图界面时的点击操作 +core.events.keyDownViewMaps // 浏览地图界面时,按下某个键的操作 +core.events.keyUpViewMaps // 浏览地图界面时,放开某个键的操作 +core.events.clickShop // 商店界面时的点击操作 +core.events.keyDownShop // 商店界面时,按下某个键的操作 +core.events.keyUpShop // 商店界面时,放开某个键的操作 +core.events.clickQuickShop // 快捷商店界面时的点击操作 +core.events.keyDownQuickShop // 快捷商店界面时,按下某个键的操作 +core.events.keyUpQuickShop // 快捷商店界面时,放开某个键的操作 +core.events.clickToolbox // 工具栏界面时的点击操作 +core.events.clickToolboxIndex // 选择工具栏界面中某个Index后的操作 +core.events.keyDownToolbox // 工具栏界面时,按下某个键的操作 +core.events.keyUpToolbox // 工具栏界面时,放开某个键的操作 +core.events.clickSL // 存读档界面时的点击操作 +core.events.keyDownSL // 存读档界面时,按下某个键的操作 +core.events.keyUpSL // 存读档界面时,放开某个键的操作 +core.events.clickSwitchs // 系统设置界面时的点击操作 +core.events.keyDownSwitchs // 系统设置界面时,按下某个键的操作 +core.events.keyUpSwitchs // 系统设置界面时,放开某个键的操作 +core.events.clickSettings // 系统菜单栏界面时的点击事件 +core.events.keyDownSettings // 系统菜单栏界面时,按下某个键的操作 +core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作 +core.events.clickSyncSave // 同步存档界面时的点击操作 +core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作 +core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作 +core.events.clickKeyBoard // 虚拟键盘界面时的点击操作 +core.events.clickAbout // “关于”界面时的点击操作 +``` + +!> `icons.js` 定义了素材ID和它在图片上的索引的对应关系。 + +!> `items.js` 定义了每个道具的名称,以及使用效果。 + +``` js +core.items.init // 初始化 +core.items.getItems // 获得所有道具 +core.items.getItemEffect // “即捡即用类”道具的使用效果 +core.items.getItemEffectTip // “即捡即用类”道具的文字提示 +* core.items.useItem // 使用道具 +* core.items.cauUseItem // 当前能否使用道具 +``` + +!> `maps.js` 定义了数字-ID的对应关系。 + +``` js +core.maps.loadFloor // 加载某个楼层(从剧本或存档中) +core.maps.getBlock // 数字和ID的对应关系 +core.maps.addEvent // 向该楼层添加剧本的自定义事件 +core.maps.addChangeFloor // 向该楼层添加剧本的楼层转换事件 +core.maps.initMaps // 初始化所有地图 +core.maps.save // 将当前地图重新变成数字,以便于存档 +core.maps.load // 将存档中的地图信息重新读取出来 +core.maps.getMapArray // 将当前地图重新变成二维数组形式 +``` + +!> `ui.js` 定义了各种界面的绘制。 + +``` js +core.ui.closePanel // 结束一切事件和绘制,关闭UI窗口,返回游戏进程 +core.ui.drawTextBox // 绘制一个对话框 +core.ui.drawChoices // 绘制一个选项界面 +core.ui.drawConfirmBox // 绘制一个确认/取消的警告页面 +core.ui.drawSwitchs // 绘制系统设置界面 +core.ui.drawSettings // 绘制系统菜单栏 +core.ui.drawQuickShop // 绘制快捷商店选择栏 +core.ui.drawBattleAnimate // 绘制战斗动画 +core.ui.drawWaiting // 绘制等待界面 +core.ui.drawSyncSave // 绘制存档同步界面 +core.ui.drawPagination // 绘制分页 +core.ui.drawEnemyBook // 绘制怪物手册 +core.ui.drawBookDetail // 绘制怪物属性的详细信息 +core.ui.drawFly // 绘制楼层传送器 +core.ui.drawMaps // 绘制浏览地图界面 +core.ui.drawToolbox // 绘制道具栏 +core.ui.drawSLPanel // 绘制存档/读档界面 +core.ui.drawThumbnail // 绘制一个缩略图 +core.ui.drawAbout // 绘制“关于”界面 +core.ui.drawHelp // 绘制帮助界面 +``` diff --git a/docs/api.md b/docs/api.md index 1939aa2c..92bc289b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,367 +1,467 @@ -# 附录:API列表 +# 附录: API列表 ?> 目前版本**v2.2.1**,上次更新时间:* {docsify-updated} * -所有系统支持的API都列在了这里。所有可能被用到的API都在前面用\*标记。 +**这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** + +如有任何疑问,请联系小艾寻求帮助。 可以在chrome浏览器的控制台中(`ctrl+shift+I`,找到Console)中直接进行调用,以查看效果。 -!> **`main.js`:游戏入口,所有其他JS文件都是被此文件加载。** +**以下所有异步API都会加上[异步]的说明,存在此说明的请勿在事件处理的自定义脚本中使用。** + +!> 最常用的新手向命令,强烈建议每个人了解 + +``` text + +core.status.floorId +获得当前层的floorId。 + + +core.status.maps +获得所有楼层的地图信息。 + + +core.status.thisMap +获得当前楼层信息,其等价于core.status.maps[core.status.floorId]。 + + +core.floors +获得所有楼层的信息。例如core.floors[core.status.floorId].events可获得本楼层的所有自定义事件。 + + +core.status.hero +获得当前勇士状态信息。例如core.status.hero.atk就是当前勇士的攻击力数值。 + + +core.material.enemys +获得所有怪物信息。例如core.material.enemys.greenSlime就是获得绿色史莱姆的属性数据。 + + +core.material.items +获得所有道具的信息。 + + +core.debug() +开启调试模式。此模式下可以按Ctrl键进行穿墙,并忽略一切事件。 +此模式下不可回放录像和上传成绩。 + + +core.updateStatusBar() +立刻刷新状态栏和地图显伤。 + + +core.setStatus('atk', 1000) +将攻击力设置为1000;这里把atk可以改成hp, def, mdef, money, experience等等。 +本句等价于 core.status.hero.atk = 1000 + + +core.getStatus('atk') +返回当前攻击力数值。本句等价于 core.status.hero.atk。 + + +core.setHeroLoc('x', 5) +设置勇士位置。这句话的意思是将勇士当前位置的横坐标设置为5。 +同理可以设置勇士纵坐标 core.setHeroLoc('y', 3)。 +值得注意的是,这句话虽然会使勇士改变位置,但并不会使界面重新绘制;如需立刻重新绘制地图还需调用: +core.clearMap('hero'); core.drawHero(); +来对界面进行更新。 + + +core.setItem('pickaxe', 10) +将破墙镐个数设置为10个。这里可以写任何道具的ID。 + + +core.getItem('pickaxe', 4) +另勇士获得四个破墙镐。这里可以写任何道具的ID。 + + +core.itemCount('pickaxe') +返回当前破墙镐的个数。这里可以写任何道具的ID。 + + +core.hasItem('pickaxe') +返回当前是否存在某个道具。等价于 core.itemCount('pickaxe')>0 。 + + +core.setFlag('xyz', 2) +设置某个flag/变量的值为2。这里可以写任何的flag变量名。 + + +core.getFlag('xyz', 7) +获得某个flag/变量的值;如果该变量不存在,则返回第二个参数。 +比如 core.getFlag('point', 2) 则获得变量point的值;如果该变量从未定义过则返回2。 + + +core.hasFlag('xyz') +返回是否存在某个变量且不为0。等价于 core.getFlag('xyz', 0)!=0 。 + + +core.insertAction(list, x, y, callback) +插入并执行一段自定义事件。在这里你可以写任意的自定义事件列表,有关详细写法请参见文档-事件。 +x和y如果设置则覆盖"当前事件点"的坐标,callback如果设置则覆盖事件执行完毕后的回调函数。 +例如: core.insertAction(["楼层切换", {"type":"changeFloor", "floorId": "MT3"}]) +将依次显示剧情文本,并执行一个楼层切换的自定义事件。 + + +core.changeFloor(floorId, stair, heroLoc, time, callback) [异步] +立刻切换到指定楼层。 +floorId为目标楼层ID,stair为到达的目标楼梯,heroLoc为到达的指定点,time为动画时间,callback为切换完毕后的回调。 +例如: +core.changeFloor('MT2', 'upFloor', null, 600) 切换到MT2层的上楼点,动画事件600ms +core.changeFloor('MT5', null, {'x': 3, 'y': 6}, 0) 无动画切换到MT5层的(3,6)位置。 + + +core.resetMap() +重置当前楼层地图。 +当我们修改某一层地图后,进游戏读档,会发现修改的内容并没有被更新上去。 +这是因为,H5的存档是会存下来每一个楼层的地图的,读档会从档里面获得地图信息。 +此时,如果我们在某一层地图执行 core.resetMap() ,则可以立刻从剧本中读取并重置当前楼层地图。 +已经被修改过的内容也会相应出现。 + + +R +录像回放的快捷键;这不是一个控制台命令,但是也把它放在这里供使用。 +录像回放在修改地图或新增数据后会很有用。 + + +localStorage +获得所有的存档数据。可以用 core.getLocalStorage('save1') 来具体获得某个存档。 -``` js -main.init // 初始化 -main.loaderJs // 动态加载所有核心JS文件 -main,loaderFloors // 动态加载所有楼层(剧本) -main.loadMod // 加载某一个JS文件 -main.loadFloor // 加载某一个楼层 -main.setMainTipsText // 加载过程提示 -window.onresize // 窗口大小变化时 -main.dom.body.onkeydown // 在界面上按下某按键时 -main.dom.body.onkeydown // 在界面上放开某按键时 -main.dom.body.onselectstart // 开始选择时 -main.dom.data.onmousedown // 鼠标按下时 -main.dom.data.onmousemove // 鼠标移动时 -main.dom.data.onmouseup // 鼠标放开时 -main.dom.data.onmousewheel // 鼠标滑轮滚动时 -main.dom.data.ontouchstart // 手指在触摸屏开始触摸时 -main.dom.data.ontouchmove // 手指在触摸屏上移动时 -main.dom.data.ontouchend // 手指离开触摸屏时 -main.statusBar.image.book.onclick // 点击状态栏中的怪物手册时 -main.statusBar.image.fly.onclick // 点击状态栏中的楼层传送器时 -main.statusBar.image.toolbox.onclick // 点击状态栏中的工具箱时 -main.statusBar.image.shop.onclick // 点击状态栏中的快捷商店时 -main.statusBar.image.save.onclick // 点击状态栏中的存档按钮时 -main.statusBar.image.load.onclick // 点击状态栏中的读档按钮时 -main.statusBar.image.settings.onclick // 点击状态栏中的系统菜单时 -main.dom.playGame.onclick // 点击“开始游戏”时 -main.dom.loadGame.onclick // 点击“载入游戏”时 -main.dom.replayGame.onclick // 点击“录像回放”时 -main.dom.easyLevel.onclick // 点击“简单难度”时 -main.dom.normalLevel.onclick // 点击“普通难度”时 -main.dom.hardLevel.onclick // 点击“困难难度”时 ``` -!> **`core.js`:系统核心文件。所有核心逻辑处理都在此文件完成。** +!> 一些相对高级的命令,针对有一定脚本经验的人 -``` js -* core.status.floorId // 获得当前层floorId -* core.status.thisMap // 获得当前层的地图信息 -* core.status.maps // 获得所有楼层的地图信息 -* core.floors // 获得所有楼层的剧本 +``` text -// ------ 初始化部分 ------ -core.init // 初始化 -core.showStartAnimate // 显示游戏开始界面 -core.hideStartAnimate // 隐藏游戏开始界面 -core.setStartProgressVal // 设置加载进度条进度 -core.setStartLoadTipText // 设置加载进度条提示文字 -core.loader // 加载图片和音频 -core.loadAutotile // 加载Autotile -core.loadImage // 加载图片 -core.loadMusic // 加载音频 -core.isPlaying // 游戏是否已经开始 -core.clearStatus // 清除游戏状态和数据 -core.resetStatus // 重置游戏状态和初始数据 -core.startGame // 开始游戏 -* core.restart // 重新开始游戏;此函数将回到标题页面 +========== 可直接从core中调用的,最常被使用的函数 ========== +core.js实际上是所有API的入口(路由),核心API的实现在其他几个文件中,core.js主要进行转发操作。 -// ------ 键盘、鼠标事件 ------ -core.onKeyDown // 按下某个键时 -core.onKeyUp // 放开某个键时 -core.pressKey // 按住某个键时 -core.keyDown // 根据按下键的code来执行一系列操作 -core.keyUp // 根据放开键的code来执行一系列操作 -core.ondown // 点击(触摸)事件按下时 -core.onmove // 当在触摸屏上滑动时 -core.onup // 当点击(触摸)事件放开时 -core.getClickLoc // 获得点击事件相对左上角的坐标(0到12之间) -core.onclick // 具体点击屏幕上(x,y)点时,执行的操作 -core.onmousewheel // 滑动鼠标滚轮时的操作 -// ------ 自动寻路代码相关 ------ -core.clearAutomaticRouteNode // 清除自动寻路路线 -core.stopAutomaticRoute // 停止自动寻路操作 -core.continueAutomaticRoute // 继续剩下的自动寻路操作 -core.clearContinueAutomaticRoute // 清空剩下的自动寻路列表 -core.setAutomaticRoute // 设置自动寻路路线 -core.automaticRoute // 自动寻路算法,找寻最优路径 -core.fillPosWithPoint // 显示离散的寻路点 -core.clearStepPostfix // 清除已经寻路过的部分 +core.nextX() +获得勇士面向的下一个位置的x坐标 -// ------ 自动行走,行走控制 ------ -core.stopAutoHeroMove // 停止勇士的自动行走 -core.setAutoHeroMove // 设置勇士的自动行走路线 -core.autoHeroMove // 让勇士开始自动行走 -core.setHeroMoveInterval // 设置行走的效果动画 -core.setHeroMoveTriggerInterval // 设置勇士行走过程中对事件的触发检测 -core.moveAction // 实际每一步的行走过程 -* core.turnHero(direction) // 设置勇士的方向(转向) -core.canMoveHero // 勇士能否前往某方向 -core.moveHero // 让勇士开始移动 -core.eventMoveHero // 使用事件让勇士移动。这个函数将不会触发任何事件。 -core.moveOneStep // 每移动一格后执行的事件。中毒时在这里进行扣血判断。 -core.waitHeroToStop(callback) // 停止勇士的一切行动,等待勇士行动结束后,再执行callback回调函数。 -core.stopHero // 停止勇士的移动状态。 -core.drawHero // 绘制勇士。 -* core.setHeroLoc(name, value) // 设置勇士的位置。name为”direction”,”x”,”y” -* core.getHeroLoc(name) // 获得勇士的位置。 -* core.nextX // 获得勇士面对位置的x坐标 -* core.nextY // 获得勇士面对位置的y坐标 +core.nextY() +获得勇士面向的下一个位置的y坐标 -// ------ 地图和事件处理 ------ -* core.openDoor(id, x, y, needKey, callback) // 打开一扇位于 (x,y) 的门 -* core.battle(id, x, y, force, callback) // 进行战斗;force表示是否强制战斗 -core.afterBattle // 战斗完毕 -core.trigger(x,y) // 触发x,y点的事件 -* core.changeFloor(floorId, stair, heroLoc, time, callback) // 楼层切换。floorId为目标楼层Id,stair可指定为上/下楼梯,time动画时间 -core.mapChangeAnimate // 地图切换动画效果 -core.clearMap // 清除地图 -core.fillText // 在某个canvas上绘制一段文字 -core.fillRect // 在某个canvas上绘制一个矩形 -core.strokeRect // 在某个canvas上绘制一个矩形的边框 -core.drawLine // 在某个canvas上绘制一条线 -core.setFont // 设置某个canvas的文字字体 -core.setLineWidth // 设置某个canvas的线宽度 -core.saveCanvas // 保存某个canvas状态 -core.loadCanvas // 加载某个canvas状态 -core.setStrokeStyle // 设置某个canvas边框属性 -core.setAlpha // 设置某个canvas的alpha值 -core.setOpacity // 设置某个canvas的透明度 -core.setFillStyle // 设置某个canvas的绘制属性(如颜色等) -* core.drawMap(mapId, callback) // 绘制某张地图。mapId为地图Id,绘制完毕将执行callback回调函数。 -core.drawAutotile // 绘制Autotile -* core.noPassExists(x,y) // 某个点是否不可通行 -core.noPass // 某个点是否在区域内且不可通行 -* core.npcExists(x,y) // 某个点是否存在NPC -* core.terrainExists(x,y) // 某个点是否存在(指定的)地形 -* core.stairExists(x,y) // 某个点是否存在楼梯 -* core.nearStair // 当前位置是否在楼梯边 -* core.enemyExists(x,y) // 某个点是否存在(指定的)怪物 -* core.getBlock(x, y, floorId, needEnable) // 获得某个点的block。floorId指定目标楼层,needEnable如果为false则即使该点的事件处于禁用状态也将被返回(否则只有事件启用的点才被返回) -core.moveBlock // 显示移动某块的动画,达到{“type”:”move”}的效果 -core.animateBlock // 显示/隐藏某个块时的动画效果 -core.showBlock // 将某个块从禁用变成启用状态 -core.removeBlock // 将某个块从启用变成禁用状态 -core.removeBlockById // 根据block的索引删除该块 -core.removeBlockByIds // 一次性删除多个block -core.addGlobalAnimate // 添加一个全局动画 -core.removeGlobalAnimate // 删除一个或所有全局动画 -core.setGlobalAnimate // 设置全局动画的显示效果 -core.syncGlobalAnimate // 同步所有的全局动画效果 -core.drawBoxAnimate // 绘制UI层的box动画 -core.updateCheckBlock // 更新领域、夹击、阻击的伤害地图 -core.checkBlock // 检查并执行领域、夹击、阻击事件 -core.snipe // 阻击事件(动画效果) -core.setFg // 更改画面色调 -* core.updateFg // 更新全地图显伤 -* core.itemCount // 获得某个物品的个数 -* core.hasItem // 是否存在某个物品 -* core.setItem // 设置某个物品的个数 -* core.removeItem // 删除某个物品 -* core.useItem // 使用某个物品;直接调用items.js中的useItem函数。 -* core.canUseItem // 能否使用某个物品。直接调用items.js中的canUseItem函数。 -* core.addItem // 增加某个物品的个数 -core.getNextItem // 获得面前的物品(轻按) -* core.getItem // 获得某个物品 -* core.drawTip // 左上角绘制一段提示 -* core.drawText // 地图中间绘制一段文字 +core.openDoor(id, x, y, needKey, callback) [异步] +尝试开门操作。id为目标点的ID,x和y为坐标,needKey表示是否需要使用钥匙,callback为开门完毕后的回调函数。 +例如:core.openDoor('yellowDoor', 10, 3, false, function() {console.log("1")}) -// ------ 系统机制 ------ -core.replaceText // 将文字中的${和}(表达式)进行替换 -core.calValue // 计算表达式的值 -core.doEffect // 执行一个表达式的effect操作 -core.splitLines // 字符串自动换行的分割 -core.unshift // 向某个数组前插入另一个数组或元素 -core.setLocalStorage // 设置本地存储 -core.getLocalStorage // 获得本地存储 -core.removeLocalStorage // 移除本地存储 -core.clone // 深拷贝一个对象 -core.formatDate // 格式化时间为字符串 -core.formatDate2 // 格式化时间为最简字符串 -core.setTwoDigits // 两位数显示 -core.debug // 进入Debug模式,攻防血和钥匙都调成很高的数值 -core.replay // 开始回放 -core.checkStatus // 判断当前能否进入某个事件 -core.openBook // 点击怪物手册时的打开操作 -core.useFly // 点击楼层传送器时的打开操作 -core.openToolbox // 点击工具栏时的打开操作 -core.openQuickShop // 点击快捷商店时的打开操作 -core.save // 点击保存按钮时的打开操作 -core.load // 点击读取按钮时的打开操作 -core.openSettings // 点击设置按钮时的打开操作 -core.autosave // 自动存档 -core.doSL // 实际进行存读档事件 -core.syncSave // 存档同步操作 -core.saveData // 存档到本地 -core.loadData // 从本地读档 -core.encodeRoute // 将路线压缩 -core.decodeRoute // 将路线解压缩 -* core.setStatus // 设置勇士属性 -* core.getStatus // 获得勇士属性 -core.getLvName // 获得某个等级的名称 -* core.setFlag // 设置某个自定义变量或flag -* core.getFlag // 获得某个自定义变量或flag -* core.hasFlag // 是否存在某个自定义变量或flag,且值为true -core.insertAction // 往当前事件列表之前插入一系列事件 -* core.lockControl // 锁定状态栏,常常用于事件处理 -* core.unlockControl // 解锁状态栏 -* core.isset // 判断某对象是否不为undefined也不会null -core.readFile // 读取一个本地文件内容 -core.download // 下载文件到本地 -core.copy // 复制一段文字到剪切板 -* core.playBgm // 播放背景音乐 -* core.pauseBgm // 暂停背景音乐的播放 -* core.resumeBgm // 恢复背景音乐的播放 -* core.playSound // 播放音频 -core.show // 动画显示某对象 -core.hide // 动画使某对象消失 -core.clearStatusBar // 清空状态栏 -core.updateStatusBar // 更新状态栏 -core.resize // 屏幕分辨率改变后重新自适应 -core.domRenderer // 渲染DOM -// ------ core.js 结束 ------ -``` - -!> **`data.js` 定义了一些初始化的数据信息。** - -!> **`enemys.js` 定义了怪物信息。** - -``` js -core.enemys.init // 初始化 -* core.enemys.getEnemys // 获得一个或所有怪物数据 -* core.enemys.hasSpecial // 判断是否含有某特殊属性 -* core.enemys.getSpecialText // 获得所有特殊属性的名称 -* core.enemys.getSpecialHint // 获得每个特殊属性的说明 -* core.enemys.getDamage // 获得某个怪物的伤害 -* core.enemys.getExtraDamage // 获得某个怪物的额外伤害 -* core.enemys.getCritical // 临界值计算 -* core.enemys.getCriticalDamage // 临界减伤计算 -* core.enemys.getDefDamage // 1防减伤计算 -* core.enemys.calDamage // 具体的伤害计算公式 -core.enemys.getCurrentEnemys // 获得当前楼层的怪物列表 -``` - -!> **`events.js` 定义了各个事件的处理流程。** - -``` js -core.events.init // 初始化 -core.events.getEvents // 获得一个或所有系统事件类型 -core.events.startGame // 游戏开始事件 -* core.events.setInitData // 不同难度分别设置初始属性 -* core.events.win // 游戏获胜事件 -* core.events.lose // 游戏失败事件 -core.evens.gameOver // 游戏结束 -core.events.afterChangeFloor // 转换楼层结束的事件 -core.events.doEvents // 开始执行一系列自定义事件 -core.events.doAction // 执行当前自定义事件列表中的下一个事件 -core.events.insertAction // 往当前事件列表之前添加一个或多个事件 -core.events.openShop // 打开一个全局商店 -core.events.disableQuickShop // 禁用一个全局商店 -* core.events.canUseQuickShop // 当前能否使用快捷商店 -* core.events.checkLvUp // 检查升级事件 -* core.events.useItem // 尝试使用道具 -core.events.addPoint // 加点事件 -core.events.afterBattle // 战斗结束后触发的事件 -core.events.afterOpenDoor // 开一个门后触发的事件 -core.events.passNet // 经过一个路障 -core.events.changeLight // 改变亮灯(感叹号)的事件 -* core.events.afterChangeLight // 改变亮灯之后,可以触发的事件 -* core.events.afterUseBomb // 使用炸弹/圣锤后的事件 -* core.events.beforeSaveData // 即将存档前可以执行的操作 -* core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作 - -// ------ 点击事件和键盘事件的处理 ------ -core.events.longClick // 长按 -core.events.keyDownCtrl // 按下Ctrl键时(快捷跳过对话) -core.events.clickConfirmBox // 确认框界面时的点击操作 -core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作 -core.events.clickAction // 自定义事件时的点击操作 -core.events.keyDownAction // 自定义事件时,按下某个键的操作 -core.events.keyUpAction // 自定义事件时,放开某个键的操作 -core.events.clickBook // 怪物手册界面的点击操作 -core.events.keyDownBook // 怪物手册界面时,按下某个键的操作 -core.events.keyUpBook // 怪物手册界面时,放开某个键的操作 -core.events.clickBookDetail // 怪物手册属性显示界面时的点击操作 -core.events.clickFly // 楼层传送器界面时的点击操作 -core.events.keyDownFly // 楼层传送器界面时,按下某个键的操作 -core.events.keyUpFly // 楼层传送器界面时,放开某个键的操作 -core.events.clickViewMaps // 浏览地图界面时的点击操作 -core.events.keyDownViewMaps // 浏览地图界面时,按下某个键的操作 -core.events.keyUpViewMaps // 浏览地图界面时,放开某个键的操作 -core.events.clickShop // 商店界面时的点击操作 -core.events.keyDownShop // 商店界面时,按下某个键的操作 -core.events.keyUpShop // 商店界面时,放开某个键的操作 -core.events.clickQuickShop // 快捷商店界面时的点击操作 -core.events.keyDownQuickShop // 快捷商店界面时,按下某个键的操作 -core.events.keyUpQuickShop // 快捷商店界面时,放开某个键的操作 -core.events.clickToolbox // 工具栏界面时的点击操作 -core.events.clickToolboxIndex // 选择工具栏界面中某个Index后的操作 -core.events.keyDownToolbox // 工具栏界面时,按下某个键的操作 -core.events.keyUpToolbox // 工具栏界面时,放开某个键的操作 -core.events.clickSL // 存读档界面时的点击操作 -core.events.keyDownSL // 存读档界面时,按下某个键的操作 -core.events.keyUpSL // 存读档界面时,放开某个键的操作 -core.events.clickSwitchs // 系统设置界面时的点击操作 -core.events.keyDownSwitchs // 系统设置界面时,按下某个键的操作 -core.events.keyUpSwitchs // 系统设置界面时,放开某个键的操作 -core.events.clickSettings // 系统菜单栏界面时的点击事件 -core.events.keyDownSettings // 系统菜单栏界面时,按下某个键的操作 -core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作 -core.events.clickSyncSave // 同步存档界面时的点击操作 -core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作 -core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作 -core.events.clickKeyBoard // 虚拟键盘界面时的点击操作 -core.events.clickAbout // “关于”界面时的点击操作 -``` - -!> `icons.js` 定义了素材ID和它在图片上的索引的对应关系。 - -!> `items.js` 定义了每个道具的名称,以及使用效果。 - -``` js -core.items.init // 初始化 -core.items.getItems // 获得所有道具 -core.items.getItemEffect // “即捡即用类”道具的使用效果 -core.items.getItemEffectTip // “即捡即用类”道具的文字提示 -* core.items.useItem // 使用道具 -* core.items.cauUseItem // 当前能否使用道具 -``` - -!> `maps.js` 定义了数字-ID的对应关系。 - -``` js -core.maps.loadFloor // 加载某个楼层(从剧本或存档中) -core.maps.getBlock // 数字和ID的对应关系 -core.maps.addEvent // 向该楼层添加剧本的自定义事件 -core.maps.addChangeFloor // 向该楼层添加剧本的楼层转换事件 -core.maps.initMaps // 初始化所有地图 -core.maps.save // 将当前地图重新变成数字,以便于存档 -core.maps.load // 将存档中的地图信息重新读取出来 -core.maps.getMapArray // 将当前地图重新变成二维数组形式 -``` - -!> `ui.js` 定义了各种界面的绘制。 - -``` js -core.ui.closePanel // 结束一切事件和绘制,关闭UI窗口,返回游戏进程 -core.ui.drawTextBox // 绘制一个对话框 -core.ui.drawChoices // 绘制一个选项界面 -core.ui.drawConfirmBox // 绘制一个确认/取消的警告页面 -core.ui.drawSwitchs // 绘制系统设置界面 -core.ui.drawSettings // 绘制系统菜单栏 -core.ui.drawQuickShop // 绘制快捷商店选择栏 -core.ui.drawBattleAnimate // 绘制战斗动画 -core.ui.drawWaiting // 绘制等待界面 -core.ui.drawSyncSave // 绘制存档同步界面 -core.ui.drawPagination // 绘制分页 -core.ui.drawEnemyBook // 绘制怪物手册 -core.ui.drawBookDetail // 绘制怪物属性的详细信息 -core.ui.drawFly // 绘制楼层传送器 -core.ui.drawMaps // 绘制浏览地图界面 -core.ui.drawToolbox // 绘制道具栏 -core.ui.drawSLPanel // 绘制存档/读档界面 -core.ui.drawThumbnail // 绘制一个缩略图 -core.ui.drawAbout // 绘制“关于”界面 -core.ui.drawHelp // 绘制帮助界面 +core.battle(id, x, y, force, callback) [异步] +执行战斗事件。id为怪物的id,x和y为坐标,force为bool值表示是否是强制战斗,callback为战斗完毕后的回调函数。 +例如:core.battle('greenSlime', null, null, true) + + +core.trigger(x, y) +触发某个地点的事件。 + + +core.clearMap(mapName) +清空某个画布图层。 +mapName可为'bg', 'event', 'fg', 'event2', 'hero', 'animate', 'weather', 'ui', 'data', 'all'之一。 +如果mapName为'all',则为清空所有画布;否则只清空对应的画布。 + + +core.drawBlock(block) +重绘某个图块。block应为core.status.thisMap.blocks中的一项。 + + +core.drawMap(floorId, callback) +重绘某一层的地图。floorId为要绘制楼层的floorId,callback为绘制完毕后的回调函数。 + + +core.terrainExists(x, y, id, floorId) +检测某个点是否存在(指定的)地形。 +x和y为坐标;id为地形ID,可为null表示任意地形;floorId为楼层ID,可忽略表示当前楼层。 + + +core.enemyExists(x, y, id, floorId) +检测某个点是否存在(指定的)怪物。 +x和y为坐标;id为怪物ID,可为null表示任意怪物;floorId为楼层ID,可忽略表示当前楼层。 + + +core.getBlock(x, y, floorId, needEnable) +获得某个点的当前图块信息。 +x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +needEnable表示该点是否启用时才返回,其值不设置则默认为true。 +如果该点不存在图块,则返回null。 +否则,返回值如下: {"index": xxx, "block": xxx} +其中index为该点在该楼层blocks数组中的索引,block为该图块实际内容。 + + +core.getBlockId(x, y, floorId, needEnable) +获得某个点的图块ID。 +x和y为坐标;floorId为楼层ID,可忽略或null表示当前楼层。 +needEnable表示是否需要该点处于启用状态才返回,其值不设置则默认为true。 +如果该点不存在图块,则返回null,否则返回该点的图块ID。 + + +core.showBlock(x, y, floorId) +将某个点从禁用变成启用状态 + + +core.removeBlock(x, y, floorId) +将某个点删除或从禁用变成启用状态。 +如果该点不存在自定义事件(比如普通的怪物),则将直接从地图中删除。 +否则将该点设置为禁用,以供以后可能的启用事件。 + + +core.useItem(itemId, callback) +尝试使用某个道具。itemId为道具ID,callback为成功或失败后的回调。 + + +core.canUseItem(itemId) +返回当前能否使用某个道具。 + + +core.getNextItem() +轻按。 + + +core.drawTip(text, itemIcon) +在左上角绘制一段提示信息,2秒后消失。itemIcon为道具图标的索引。 + + +core.drawText(contents, callback) [异步] +绘制一段文字。 +不推荐使用此函数,尽量使用core.insertAction(contents)来显示剧情文本。 + + +core.closePanel() +结束一切事件和绘制,关闭UI窗口,返回游戏进程。 + + +core.replaceText(text) +将一段文字中的${}进行计算并替换。 + + +core.calValue(value) +计算表达式的实际值。这个函数可以传入status:atk等这样的参数。 + + +core.getLocalStorage(key, defaultValue) +从localStorage中获得某个数据(已被parse);如果对应的key不存在则返回defaultValue。 + + +core.clone(data) +深拷贝某个对象。 + + +core.isset(x) +测试x是否不为null,不为undefined也不为NaN。 + + +core.rand(num) +使用伪种子生成伪随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到1之间的浮点数。 +此函数为伪随机算法,SL大法无效。(即多次SL后调用的该函数返回的值都是相同的。) + + +core.rand2(num) +使用系统的随机数算法得到的随机数。该随机函数能被录像支持。 +num如果设置大于0,则生成一个[0, num-1]之间的数;否则生成一个0到2147483647之间的整数。 +此函数使用了系统的Math.random()函数,支持SL大法。 +但是,此函数会将生成的随机数值存入录像,因此如果调用次数太多则会导致录像文件过大。 + + +core.restart() [异步] +返回标题界面。 + + +core.updateFg() +更新全地图显伤。包括怪物显伤、临界显示和领域显伤等。 + + +========== core.actions.XXX 和游戏控制相关的函数 ========== +actions.js主要用来进行用户交互行为的处理。 +所有用户行为,比如按键、点击、滑动等等,都会被此文件接收并进行操作。 + + +========== core.control.XXX 和游戏控制相关的函数 ========== +control.js主要用来进行游戏控制,比如行走控制、自动寻路、存读档等等游戏核心内容。 + + +========== core.enemys.XXX 和怪物相关的函数 ========== +enemys.js主要用来进行怪物相关的内容,比如怪物的特殊属性,伤害和临界计算等。 + + +core.enemys.hasSpecial(special, test) +测试怪物是否含有某个特殊属性。 +常见用法: core.enemys.hasSpecial(monster.special, 3) ## 测试是否拥有坚固 + + +core.enemys.getSpecialText(enemyId) +返回一个列表,包含该怪物ID对应的所有特殊属性。 + + +core.enemys.getSpecialHint(enemy, special) +获得怪物某个(或全部)特殊属性的文字说明。 + + +core.enemys.canBattle(enemyId) +返回当前能否战胜某个怪物。 + + +core.enemys.getDamage(enemyId) +返回当前对某个怪物的战斗伤害。如果无法战斗,返回null。 + + +core.enemys.getExtraDamage(enemyId) +返回某个怪物会对勇士造成的额外伤害(不可被魔防抵消),例如仇恨、固伤等等。 + + +core.enemys.nextCriticals(enemyId, number) +返回一个列表,为接下来number(可忽略,默认为1)个该怪物的临界值和临界减伤。 +列表每一项类似 [x,y] 表示临界值为x,且临界减伤为y。 +如果无临界值,则返回空列表。 + + +core.enemys.getDefDamage(enemyId, k) +获得k(可忽略,默认为1)防减伤值。 + + +core.enemys.getDamageInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef) +获得实际战斗信息,比如伤害,回合数,每回合伤害等等。 +此函数是实际战斗过程的计算。 + + +core.enemys.calDamage(enemy, hero_hp, hero_atk, hero_def, hero_mdef) +计算战斗伤害;实际返回的是上面getDamageInfo中伤害的数值。 + + +core.enemys.getCurrentEnemys(floorId) +获得某一层楼剩余所有怪物的信息(供怪物手册使用) + + +========== core.events.XXX 和事件相关的函数 ========== +events.js主要用来进行事件处理,比如自定义事件,以及某些条件下可能会被触发的事件。 +大多数事件API都在脚本编辑中存在,这里只列出部分比较重要的脚本编辑中不存在的API。 + + +core.events.gameOver(ending, fromReplay) +游戏结束并上传的事件。 +该函数将提问是否上传和是否下载录像,并返回标题界面。 + + +core.events.doEvents(list, x, y, callback) [异步] +开始执行某个事件。 +请不要执行此函数,尽量使用 core.insertAction(list, x, y, callback) 来开始执行一段事件。 + + +core.events.doAction() +执行下一个事件。此函数中将对所有自定义事件类型分别处理。 + + +core.events.openShop(shopId, needVisited) [异步] +打开一个全局商店。needVisited表示是否需要该商店已被打开过。 + + +core.events.disableQuickShop(shopId) +禁用一个全局商店 + + +core.events.canUseQuickShop(shopId) +当前能否使用某个快捷商店 + + +core.events.setHeroIcon(name) +设置勇士行走图 + + +========== core.items.XXX 和道具相关的函数 ========== +items.js将处理和道具相关的内容,比如道具的使用,获取和删除等等。 + + +========== core.loader.XXX 和游戏加载相关的函数 ========== +loader.js将主要用来进行资源的加载,比如加载音乐、图片、动画等等。 + + +========== core.maps.XXX 和地图处理相关的函数 ========== +maps.js主要用来进行地图相关的的操作。包括绘制地图,获取地图上的点等等。 + + +core.maps.canMoveHero(x,y,direction,floorId) +判断能否前往某个方向。x,y为坐标,可忽略为当前点;direction为方向,可忽略为当前方向。 +floorId为楼层ID,可忽略为当前楼层。 + + +core.maps.canMoveDirectly(destX, destY) +判断当前能否瞬间移动到某个点。 + + +core.maps.removeBlockById(index, floorId) +根据索引删除或禁用某块。 + + +core.maps.removeBlockByIds(floorId, ids) +根据索引删除或禁用若干块。 + + +========== core.ui.XXX 和对话框绘制相关的函数 ========== +ui.js主要用来进行UI窗口的绘制,比如对话框、怪物手册、楼传器、存读档界面等等。 + + +core.ui.drawThumbnail(floorId, canvas, blocks, x, y, size, heroLoc, heroIcon) +绘制一个缩略图,比如楼传器界面,存读档界面等情况。 +floorId为目标楼层ID,canvas为要绘制到的图层,blocks为要绘制的所有图块。 +x,y为该图层开始绘制的起始点坐标,size为每一格的像素,heroLoc为勇士坐标,heroIcon为勇士图标。 + + +========== core.utils.XXX 工具类的辅助函数 ========== +utils.js主要用来进行一些辅助函数的计算。 + + +core.utils.splitLines(canvas, text, maxLength, font) +自动切分长文本的换行。 +canvas为图层,text为要自动换行的内容,maxLength为每行最长像素,font为文本的字体。 + + +core.utils.cropImage(image, size) +纵向对图片进行切分(裁剪)。 + + +core.utils.unshift(a, b) +向某个数组前插入另一个数组或元素 + + +core.utils.formatBigNumber(x) +大数据的格式化 + + +core.utils.arrayToRGB(color) +将形如[255,0,0]之类的数组转成#FF0000这样的RGB形式。 + + +core.utils.encodeRoute(list) +压缩加密路线。可以使用core.encodeRoute(core.status.route)来压缩当前路线。 + + +core.utils.decodeRoute(route) +解压缩(解密)路线。 + + +core.utils.readFile(success, error, readType) [异步] +尝试请求读取一个本地文件内容。 +success和error为成功/失败后的回调,readType不设置则以文本读取,否则以DataUrl形式读取。 + + +core.utils.readFileContent(content) [异步] +文件读取完毕后的内容处理。 + + +core.utils.download(filename, content) +尝试生成并下载一个文件。 + + +core.utils.copy(data) +尝试复制一段文本到剪切板。 + + +core.utils.http(type, url, formData, success, error) [异步] +发送一个异步HTTP请求。 +type为'GET'或者'POST';url为目标地址;formData如果是POST请求则为表单数据。 +success为成功后的回调,error为失败后的回调。 + ``` diff --git a/docs/element.md b/docs/element.md index 44e3bd97..11816adf 100644 --- a/docs/element.md +++ b/docs/element.md @@ -36,7 +36,8 @@ ## 怪物 -本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。 +本塔支持的怪物列表参见`project/enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。 + 如有自己的怪物素材需求请参见[自定义素材](personalization#自定义素材)的内容。 怪物可以有特殊属性,每个怪物可以有多个自定义属性。 @@ -89,27 +90,23 @@ enemys.prototype.getSpecialText = function (enemyId) { 下面的`getSpecialHint`函数则给定了每个特殊属性的详细描述。这个描述将在怪物手册中看到。 -**打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。** +打败怪物后可以进行加点操作。有关加点塔的制作可参见[加点事件](event#加点事件)。 如果`data.js`中的enableExperience为false,即不启用经验的话,怪物手册里将不显示怪物的经验值,打败怪物也不获得任何经验。 拿到幸运金币后,打怪获得的金币将翻倍。 +如果怪物有`"notBomb": true`,则该系列诖怪物均不可被炸。 + N连击怪物的special是6,且我们可以为它定义n代表实际连击数。参见样板中剑王的写法。 -![N连击](./img/nattack.png) - -吸血怪需要在怪物后添加value,代表吸血的比例。 +吸血怪需要给怪物设置value,代表吸血的比例。 可以给吸血怪添加`'add': true`来将吸血的数值加到自身上。 -![怪物吸血](./img/blood.png) - 中毒怪让勇士中毒后,每步扣减的生命值由`data.js`中的values定义。 -衰弱怪让勇士衰弱后,攻防会暂时下降一定的数值(直到衰弱状态解除恢复);这个下降的数值同在`data.js`中的values定义。 - -![debuff](./img/debuff.png) +衰弱怪让勇士衰弱后,攻防会下降一定比例或固定数值(直到衰弱状态解除恢复);其在`data.js`中的values定义。 诅咒怪将让勇士陷入诅咒状态,诅咒状态下杀怪不获得金币和经验值。 @@ -117,21 +114,17 @@ N连击怪物的special是6,且我们可以为它定义n代表实际连击数 领域是十字伤害还是九宫格伤害由`zoneSquare`设定,如设置为true则为九宫格伤害,不指定或为false则为十字伤害。 -`range`选项可选,代表该领域怪的范围,不写则默认为1。 +领域怪还可以设置`range`选项代表该领域怪的范围,不写则默认为1。 -![怪物领域](./img/zone.png) - -阻击怪同样需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 +阻击怪同样需要设置value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。 !> 阻击怪后退的地点不能有任何事件存在,即使是已经被禁用的自定义事件! 请注意如果吸血、领域、阻击中任何两个同时存在,则value会冲突。**因此请勿将吸血、领域或阻击放置在同一个怪物身上。** -退化怪需要在后面增加'atkValue'和'defValue'表示退化的数值。 +退化怪需要设置'atkValue'和'defValue'表示退化的数值;也可以不设置默认为0。 -![怪物退化](./img/tuihua.png) - -固伤怪则需要在后面增加`damage`选项,代表战前扣血数值。 +固伤怪则需要设置`damage`选项,代表战前扣血数值。 如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。 @@ -197,16 +190,16 @@ floorId指定的是目标楼层的唯一标识符(ID)。 本塔支持BGM和SE的播放。 -要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在main.js中进行定义 +要播放音乐和音效,你需要将对应的文件放在sounds目录下,然后在全塔属性中进行定义 ``` js "bgms": [ // 在此存放所有的bgm,和文件名一致。第一项为默认播放项 // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'bgm.mp3', 'qianjin.mid', 'star.mid', + 'bgm.mp3' ]; "sounds": [ // 在此存放所有的SE,和文件名一致 // 音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 - 'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg', 'zone.ogg' + 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3' ] ``` diff --git a/docs/event.md b/docs/event.md index 1aeaaeb4..b1559bcc 100644 --- a/docs/event.md +++ b/docs/event.md @@ -26,7 +26,7 @@ 它能通过拖动、复制粘贴等方式帮助你快速生成事件列表,而不用手动打大量字符。 -但是,仍然强烈建议要对每个事件的写法进行了解。 +但是,仍然强烈建议要对每个事件的写法进行了解,因为在脚本编辑,`insertAction`等地方需要插入自定义事件时,还是很有必要的。 ## 自定义事件 @@ -252,18 +252,19 @@ ``` js "x,y": [ // 实际执行的事件列表 - "你当前的攻击力是${status:atk}, 防御是${status:def}", + "你当前的攻击力是${status:atk}, 防御是${status:def},坐标是(${status:x},${status:y})", "你的攻防和的十倍是${10*(status:atk+status:def)}", "你的红黄蓝钥匙总数为${item:yellowKey+item:blueKey+item:redKey}", "你访问某个老人的次数为${flag:man_times}", ] ``` -- `status:xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(魔防值),money(金币),experience(经验)。 +- `status:xxx` 获取勇士属性时只能使用如下几个:hp(生命值),atk(攻击力),def(防御力),mdef(魔防值),money(金币),experience(经验),x(勇士的横坐标),y(勇士的纵坐标),direction(勇士的方向)。 - `item:xxx` 中的xxx为道具ID。所有道具的ID定义在items.js中,请自行查看。例如,`item:centerFly` 代表中心对称飞行器的个数。 - `flag:xxx` 中的xxx为一个自定义的变量/Flag;如果没有对其进行赋值则默认值为false。 另外,有个小`trick`。是否想立刻知道显示效果? + 你可以用Chrome浏览器打开游戏,按Ctrl+Shift+I打开开发者工具,找到Console(控制台),并中输入`core.drawText("...")` 即可立刻看到文字显示的效果。适当调整文字,使得显示效果满意后,再复制粘贴到你的剧情文本中。 ![调试](./img/eventdebug.png) @@ -322,7 +323,7 @@ time为可选项,表示文字添加的速度。若此项设置为0将直接全 ] ``` -值得注意的是,提示的text内容是可以使用`${ }`来计算表达式的值的。 +值得注意的是,提示的text内容也是可以使用`${ }`来计算表达式的值的。 ### setValue:设置勇士的某个属性、道具个数,或某个变量/Flag的值 @@ -356,7 +357,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam 另外注意一点的是,如果hp被设置成了0或以下,将触发lose事件,直接死亡。 -### show: 将一个禁用事件启用 +### show:将一个禁用事件启用 我们上面提到了,所有事件都必须靠其他事件驱动来完成,不存在当某个flag为true时自动执行的说法。那么,我们自然要有启用事件的写法。 @@ -381,7 +382,7 @@ time为动画效果时间,如果指定了某个大于0的数,则会以动画 !> **要注意的是,调用show事件后只是让该事件从禁用状态变成启用,从不可见不可交互变成可见可交互,但本身不会去执行该点的事件。** -### hide: 将一个启用事件禁用 +### hide:将一个启用事件禁用 `{"type":"hide"}`和show刚好相反,它会让一个已经启用的事件被禁用。 @@ -407,7 +408,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` ] ``` -### trigger: 立即触发另一个地点的事件 +### trigger:立即触发另一个地点的事件 `{"type":"trigger"}` 会立刻触发当层另一个地点的自定义事件。 @@ -428,7 +429,7 @@ NPC对话事件结束后如果需要NPC消失也需要调用 `{"type": "hide"}` 例如上面这个例子,下面的文字将不会再被显示,而是直接跳转到`"3,6"`对应的事件列表从头执行。 -### revisit: 立即重启当前事件 +### revisit:立即重启当前事件 revisit和trigger完全相同,只不过是立刻触发的还是本地点的事件 @@ -443,7 +444,7 @@ revisit其实是trigger的简写,只不过是loc固定为当前点。 revisit常常使用在一些商人之类的地方,当用户购买物品后不是离开,而是立刻重新访问重新进入购买页面。 -### exit: 立刻结束当前事件 +### exit:立刻结束当前事件 上面说到像商人一类,购买物品后可以立刻revisit重新访问,但是这样就相当于陷入了死循环导致无法离开。 @@ -504,11 +505,11 @@ name是可选的,代表目标行走图的文件名。 如果你需要同时修改勇士的名称,可以使用`setValue`事件来修改`status:name`,但请注意value必须加单引号,不然会报错。 -### update: 立刻更新状态栏和地图显伤 +### update:立刻更新状态栏和地图显伤 如果你需要刷新状态栏和地图显伤,只需要简单地调用 `{"type": "update"}` 即可。 -### sleep: 等待多少毫秒 +### sleep:等待多少毫秒 等价于RMXP中的"等待x帧",不过是以毫秒来计算。 @@ -521,11 +522,7 @@ name是可选的,代表目标行走图的文件名。 ] ``` -### wait:等待用户操作 - -使用 `{"type": "wait"}` 可以等待用户进行操作(如点击、回车等)。 - -### battle: 强制战斗 +### battle:强制战斗 调用battle可强制与某怪物进行战斗(而无需去触碰到它)。 @@ -556,7 +553,7 @@ name是可选的,代表目标行走图的文件名。 强制战斗没有指定loc的选项,因此战斗后需要调用hide使怪物消失(如果有必要)。 -### openDoor: 开门 +### openDoor:开门 调用`{"type":"openDoor"}`可以打开一扇门。 @@ -573,7 +570,7 @@ loc指定门的坐标,floorId指定门所在的楼层ID。如果是当前层 如果loc所在的点既不是门也不是墙壁,则忽略本事件。 -### changeFloor: 楼层切换 +### changeFloor:楼层切换 在事件中也可以对楼层进行切换。一个比较典型的例子就是TSW中,勇士在三楼的陷阱被扔到了二楼,就是一个楼层切换事件。 @@ -598,7 +595,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。 !> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。** -### changePos: 当前位置切换/勇士转向 +### changePos:当前位置切换/勇士转向 有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。 @@ -612,11 +609,11 @@ time为可选的,指定的话将作为楼层切换动画的时间。 ] ``` -### openShop: 打开一个全局商店 +### openShop:打开一个全局商店 使用openShop可以打开一个全局商店。有关全局商店的说明可参见[全局商店](#全局商店)。 -### disableShop: 禁用一个全局商店 +### disableShop:禁用一个全局商店 使用disableShop可以永久禁用全局商店直到再次被openShop打开为止。有关全局商店的说明可参见[全局商店](#全局商店)。 @@ -726,7 +723,7 @@ time为总移动的时间。 !> 移动图片只是会在顶层绘制“移动”效果,动画结束即消失,并不会实际对图片的显示造成影响。请与showImage事件合用。 -### setFg: 更改画面色调 +### setFg:更改画面色调 我们可以使用 `{"type": "setFg"}` 来更改画面色调。 @@ -766,7 +763,7 @@ level为天气的强度等级,在1-10之间。1级为最弱,10级为最强 !> 使用setWeather更改的天气在切换地图后会被目标地图的默认天气覆盖。 -### move: 让某个NPC/怪物移动 +### move:让某个NPC/怪物移动 如果我们需要移动某个NPC或怪物,可以使用`{"type": "move"}`。 @@ -836,7 +833,7 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate 不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。 -### playBgm: 播放背景音乐 +### playBgm:播放背景音乐 使用playBgm可以播放一个背景音乐。 @@ -848,19 +845,19 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate 有关BGM播放的详细说明参见[背景音乐](element#背景音乐) -### pauseBgm: 暂停背景音乐 +### pauseBgm:暂停背景音乐 使用`{"type": "pauseBgm"}`可以暂停背景音乐的播放。 -### resumeBgm: 恢复背景音乐 +### resumeBgm:恢复背景音乐 使用`{"type": "resumeBgm"}`可以恢复背景音乐的播放。 -### playSound: 播放音效 +### playSound:播放音效 使用playSound可以立刻播放一个音效。 -使用方法:`{"type": "playSound", "name": "item.ogg"}` +使用方法:`{"type": "playSound", "name": "item.mp3"}` 值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。 @@ -872,7 +869,7 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate value为音量大小,在0到100之间,默认为100。设置后,BGM和SE都将使用该音量进行播放。 -### win: 获得胜利 +### win:获得胜利 `{"type": "win", "reason": "xxx"}` 将会直接调用events.js中的win函数,并将reason作为结局传入。 @@ -880,7 +877,7 @@ value为音量大小,在0到100之间,默认为100。设置后,BGM和SE都 !> 如果`reason`不为空,则会以reason作为获胜的结局! -### lose: 游戏失败 +### lose:游戏失败 `{"type": "lose", "reason": "xxx"}` 将会直接调用`events.js`中的lose函数,并将reason作为参数传入。 @@ -905,7 +902,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 输入得到的结果将被赋值给flag:input,可以供后续if来进行判断。 -### if: 条件判断 +### if:条件判断 使用`{"type": "if"}`可以对条件进行判断,根据判断结果将会选择不同的分支执行。 @@ -956,7 +953,7 @@ text为提示文字,可以在这里给输入提示文字。这里同样可以 - if可以不断进行嵌套,一层套一层;如成立的场合再进行另一个if判断等。 - if语句内的内容执行完毕后将接着其后面的语句继续执行。 -### choices: 给用户提供选项 +### choices:给用户提供选项 choices是一个很麻烦的事件,它将弹出一个列表供用户进行选择。 @@ -1118,6 +1115,45 @@ choices为一个数组,其中每一项都是一个选项列表。 !> 如果continue事件不在任何循环中被执行,则和exit等价,即会立刻结束当前事件! +### wait:等待用户操作 + +使用 `{"type": "wait"}` 可以等待用户进行操作(如点击、按键等)。 + +当用户执行操作后: +- 如果是键盘的按键操作,则会将flag:type置为0,并且把flag:keycode置为刚刚按键的keycode。 +- 如果是屏幕的点击操作,则会将flag:type置为1,并且设置flag:x和flag:y为刚刚的点击坐标。 + +下面是一个while事件和wait合并使用的例子,这个例子将不断接收用户的点击或按键行为,并输出该信息。 +如果用户按下了ESC或者点击了屏幕正中心,则退出循环。 + + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "while", "condition": "true", // 永久循环 + "data": [ + {"type": "wait"}, // 等待用户操作 + {"type": "if", "condition": "flag:type==0", // flag:type==0,键盘按键 + "true": [ + "你当前按键了,keycode是${flag:keycode}", + {"type": "if", "condition": "flag:keycode==27", // ESC的keycode是27 + "true": [{"type": "break"}], // 跳出循环 + "false": [] + } + ], + "false": [ // flag:type==1,鼠标点击 + "你当前点击屏幕了,坐标是[${flag:x},${flag:y}]", + {"type": "if", "condition": "flag:x==6 && flag:y==6", // 点击(6,6) + "true": [{"type": "break"}], // 跳出循环 + "false": [] + } + ] + } + ] + } +] + +``` + ### function: 自定义JS脚本 上述给出了这么多事件,但有时候往往不能满足需求,这时候就需要执行自定义脚本了。 @@ -1133,22 +1169,23 @@ choices为一个数组,其中每一项都是一个选项列表。 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统所有支持的API都在[附录](api)中给出。 +系统常见可能会被造塔所用到的的API都在[附录:API列表](api)中给出,请进行参照。 -这里只简单列出给一些最常见的API: +**警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** + +[附录:API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 + +如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 + +举个例子,如果我们想随机切换到某个楼层的某个点,我们可以这么写自定义脚本: ``` js -core.getStatus(name) //获得勇士的某个属性(hp/atk/def/…) -core.setStatus(name, value) //设置勇士某个属性值为value -core.itemCount(name) //获得某个道具的个数 -core.getItem(name, count) //获得某个道具count个 -core.setItem(name, value) //设置某个道具为value个 -core.getFlag(name, defaultValue) //获得某个自定义变量flag;如果未定义则返回defaultValue -core.setFlag(name, value) //将某个自定义变量/flag设置为value -core.hasFlag(name) //判断某个自定义flag是否成立。只有被被赋值过,且不为0或false时才会返回true。 -core.updateStatusBar() //立刻更新状态栏和地图显伤。(上面各种get和set均不会对状态栏和地图显伤更新,需要手动调用这个函数。) -core.insertAction(list) //往当前事件列表中插入一系列事件。使用这个函数插入的事件将在这段自定义JS脚本执行完毕后立刻执行。 -// …… +var toFloor = core.floorIds[core.rand(core.floorIds.length)]; // 随机一个楼层ID +var toX = core.rand(13), toY = core.rand(13); // 随机一个点 +core.insertAction([ + {"type": "changeFloor", "floorId": toFloor, "loc": [toX, toY]} // 插入一个changeFloor事件,并在该脚本结束后执行。 +]) +// 请勿直接调用 core.changeFloor(toFloor, ...),这个代码是异步的,会导致事件处理和录像出问题! ``` ## 同一个点的多事件处理 @@ -1161,7 +1198,7 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 下面以几个具体例子来进行详细说明。 -### 打怪掉宝(怪物->道具) +### 打怪掉宝 我们注意到怪物和道具都是系统默认事件,因此无需写events,而是直接在afterBattle中setBlock即可。 @@ -1173,22 +1210,24 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 } ``` -### 打怪变成可对话的NPC(怪物->NPC) +### 打怪变成楼梯 -由于NPC是自定义事件,因此我们需要写events。注意到events中不覆盖trigger,则还是怪物时,存在系统trigger因此会战斗;变成NPC后没有系统trigger因此会触发自定义事件。 +因为涉及到多事件处理,因此我们不能写changeFloor那一项,而是使用events(自定义事件里写楼层转换)。 + +注意到events中不覆盖trigger,则还是怪物时,存在系统trigger因此会战斗并触发afterBattle;变成NPC后没有系统trigger因此会触发自定义事件(楼层转换)。 请注意打死怪物时默认会禁用该点,因此替换后需要手动进行show来启用。 ``` js "events": { "x,y": [ - "可对话的NPC" + {"type": "changeFloor", "loc": [0,0], "floorId": "MT1"} ] }, "afterBattle": { "x,y": [ - {"type": "setBlock", "number": 121}, // 变成老人 - {"type": "show", "loc": [x,y]} // 启用该点 + {"type": "setBlock", "number": 87}, // 变成上楼梯 + {"type": "show"} // 启用该点 ] } ``` @@ -1228,6 +1267,7 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 - 触发器(trigger)亦采用覆盖原则,即**首先取该图块的默认触发器(例如怪物是battle,道具是getItem,门是openDoor),如果剧本的events中定义了该点的trigger则覆盖**。 - 可以通过if语句和flag来控制自定义事件具体走向哪个分支。 - 如果弄不清楚系统trigger和自定义事件等的区别,也可以全部覆盖为自定义事件,然后通过type:battle,type:openDoor等来具体进行控制。 + - 多事件处理时请不要使用`changeFloor`那一项,而是使用`events`或者`afterXXX`来处理。 ## 加点事件 @@ -1237,13 +1277,7 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 如果要对某个怪物进行加点操作,则首先需要修改该怪物的`point`数值,代表怪物本身的加点数值。 -``` js -... 'def': 0, 'money': 1, 'experience': 1, 'point': 1, 'special': 0}, // 在怪物后面添加point代表怪物的加点数 -``` - -然后在`functions.js`文件中找到`addPoint`函数。它将返回一个choices事件。修改此函数为我们需要的加点项即可。 - -!> V2.0版本可以直接在“脚本编辑 - 加点事件”中双击进行修改! +然后在脚本编辑中找到加点事件,双击进行修改。它将返回一个choices事件。修改此函数为我们需要的加点项即可。 ``` js ////// 加点事件 ////// @@ -1279,6 +1313,8 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 全局商店定义在`data.js`中,找到shops一项。 +从V2.2以后,全局商店也可以使用图块进行编辑,但仍需知道每一项的使用。 + ``` js "shops": [ // 定义全局商店(即快捷商店) { @@ -1477,7 +1513,7 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 而在我们的存档中,是不会对怪物数据进行存储的,只会存各个变量和Flag,因此我们需要在读档后根据变量或Flag来调整怪物数据。 -我们可以在functions.js中的`afterLoadData`进行处理。 +我们可以在脚本编辑中的`afterLoadData`进行处理。 ``` js ////// 读档事件后,载入事件前,可以执行的操作 ////// @@ -1540,7 +1576,7 @@ core.insertAction(list) //往当前事件列表中插入一系列事件。使用 // effect也允许写一个function,代表本次升级将会执行的操作,比如可以显示一段提示文字,或者触发一个事件 {"need": 40, "effect": function () { - core.drawText("恭喜升级!"); + core.drawTip("恭喜升级!"); core.status.hero.hp *= 2; core.status.hero.atk += 100; core.status.hero.def += 100; diff --git a/docs/img/console.png b/docs/img/console.png new file mode 100644 index 00000000..68f55019 Binary files /dev/null and b/docs/img/console.png differ diff --git a/docs/img/plugin.png b/docs/img/plugin.png new file mode 100644 index 00000000..4e87fa84 Binary files /dev/null and b/docs/img/plugin.png differ diff --git a/docs/img/sample0.png b/docs/img/sample0.png index a218190b..6598e611 100644 Binary files a/docs/img/sample0.png and b/docs/img/sample0.png differ diff --git a/docs/index.html b/docs/index.html index f29c2402..cb5df153 100644 --- a/docs/index.html +++ b/docs/index.html @@ -44,8 +44,13 @@ mergeNavbar: true, formatUpdated: '{YYYY}-{MM}-{DD} {HH}:{mm}:{ss}', } - if (typeof navigator.serviceWorker !== 'undefined') { - navigator.serviceWorker.register('serviceWorker.js') + if (navigator.serviceWorker) { + navigator.serviceWorker.getRegistrations() + .then(function(registrations) { + for(let registration of registrations) { + registration.unregister(); + } + }); } diff --git a/docs/personalization.md b/docs/personalization.md index c287e4e7..91993245 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -75,7 +75,7 @@ images为一个数组,代表当前层所有作为背景素材的图片信息 如果第四项为true,则会在前景层(event2)上绘制,能覆盖勇士,常常用来作为柱子的上半部分等情况。 -**如果你需要让某些点不可通行(比如你建了个房子,墙壁和家具等位置不让通行),则需在`events`中指定`{"noPass": false}`,参见[自定义事件](event#自定义事件)的写法。 +**如果你需要让某些点不可通行(比如你建了个房子,墙壁和家具等位置不让通行),则需在`events`中指定`{"noPass": false}`,参见[自定义事件](event#自定义事件)的写法。** ``` js "events": { @@ -114,8 +114,35 @@ images为一个数组,代表当前层所有作为背景素材的图片信息 **`ID-数字` 对应关系定义在maps.js文件中。该文件将唯一确定一个ID对应的数字是多少。** -如果需要添加一个素材到游戏,则必须为其分配一个唯一标识符,并同时修改`icons.js`和`maps.js`两个文件。 +在V2.0中,我们可以在地图编辑器中很方便查看每个图块的三个属性信息。 +#### 注册素材 + +在V2.0的地图编辑器中,要注册新素材,我们只需要在图块属性一栏输入新素材的ID和数字。 + +![素材注册](./img/register.png) + +ID必须由数字字母下划线组成,数字在1000以内,且均不能和已有的进行重复。 + +之后刷新编辑器即可。 + +对于怪物和道具,我们也可以进行自动注册,只需要点击“自动注册”按钮,将对该栏下所有未注册的素材进行自动注册(自动分配ID和数字)。 + +素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 + +#### Autotile的注册 + +但是,通过上面这种方式,我们是没办法新增并注册Autotile的。 + +除了替换样板现有的几个外,如果我们还需要新添加Autotile,则: + +1. 将新的Autotile图片复制到images目录下。文件名必须是字母数字和下划线组成。 +2. 进入icons.js,在autotile分类下进行添加该文件的名称,索引简单的写0。 +3. 指定一个数字,在maps.js中类似进行添加。 + +!> Autotile的ID和文件名应确保完全相同! + + + ### 地图生成器使用自定义素材 地图生成器是直接从js文件中读取数字-图标对应关系的。 @@ -196,7 +225,7 @@ images为一个数组,代表当前层所有作为背景素材的图片信息 如果你想要同种宝石在不同层效果不同的话,可以进行如下操作: 1. 在楼层的item_ratio中定义宝石的比率(比如1-10的写1,11-20层写2等) -2. 修改获得道具的itemEffect函数(在items.js中的itemEffect中编辑,V2.0中也可以使用编辑器) +2. 修改获得道具的itemEffect函数(编辑器中双击进行编辑) ``` js // ratio为楼层的item_ratio值,可以进行翻倍宝石属性 @@ -278,7 +307,53 @@ control.prototype.checkBlock = function () { } // ... 下略 ``` -4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。 +4. 如果有更高的需求,例如想让吸血效果变成一半,则还是在上面这些地方进行对应的修改即可。 + +## 新增门和对应的钥匙 + +如果要新增一个门和对应的钥匙,需要进行如下几步: + +1. 在terrains.png中添加新的门的素材,并在地图编辑器中注册门的ID。该ID必须是以`Door`结尾,例如`abcDoor`。 +2. 在animates.png中添加开门的四格动画,然后直接打开icons.js文件,在animates下直接添加ID和索引信息,例如`'abcDoor': 34`。 +3. 在items.png中添加钥匙的素材,并在地图编辑器中注册钥匙的ID。该ID必须是和门对应且以`Key`结尾,例如`abcKey`。 +4. 该道具的cls应为`tools`,可以自行写道具描述,最下面几项均留`null`即可。 + +!> **请勿在animates中对门的动画素材进行注册!而是请直接打开icons.js文件并添加ID和索引信息!!!** + +!> terrains和animates的门ID必须完全一致,且以`Door`结尾;所对应的钥匙ID应当是把`Door`换成`Key`,这样才能对应的上! + +## 覆盖楼传事件 + +对于特殊的塔,我们可以考虑修改楼传事件来完成一些特殊的要求,比如镜子可以按楼传来切换表里。 + +要修改楼传事件,需要进行如下几步: + +1. 截获楼传的点击事件。在control.js中找到useFly函数,并将其替换成如下内容: +``` js +////// 点击楼层传送器时的打开操作 ////// +control.prototype.useFly = function (need) { + if (!core.status.heroStop) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` +2. 让录像记下楼传的使用。在items.js的useItem函数中找到记录路线的那几行,修改为: +``` js + // 记录路线 + if (itemId!='book') { // 把 `&& itemId!='fly'` 给删除 + core.status.route.push("item:"+itemId); + } +``` +3. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如: +``` js +"useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本 +"canUseItemEffect": "true" // 任何时候可用 +``` +修改时,请先把`null`改成空字符串`""`,然后再双击进行编辑。 + ## 自定义装备 @@ -305,7 +380,7 @@ control.prototype.checkBlock = function () { "shield1": {"atk": 0, "def": 10, "mdef": 10}, // 铁盾加10防和10魔防 ``` -通过这种方式,当穿上装备时,将会给你的三围分别加上对应项的数值。 +通过这种方式,当穿上装备时,将会给你的三围分别加上对应项的数值(支持负数,比如装剑减防御)。 ### 新增剑盾 @@ -359,7 +434,7 @@ this.useEquipment = function (itemId) { // 使用装备 你需自己指定一个special数字,修改getSpecialText函数(属性名)和getSpecialHint函数(属性提示文字)。 -如果要修改伤害计算公式,请修改下面的calDamage函数。请注意,如果无法战斗,该函数必须返回`null`。 +如果要修改伤害计算公式,请修改下面的getDamageInfo函数。请注意,如果无法战斗,该函数必须返回`null`。 对于毒衰弱怪物的战斗后结算在`functions.js`中的afterBattle函数中。 @@ -367,6 +442,295 @@ this.useEquipment = function (itemId) { // 使用装备 `getCritical`, `getCriticalDamage`和`getDefDamage`三个函数依次计算的是该怪物的临界值、临界减伤和1防减伤。也可以适当进行修改。 +## 自定义快捷键 + +如果需要绑定某个快捷键为处理一段事件,也是可行的。 + +要修改按键,我们可以在`actions.js`的`keyUp`进行处理: + +比如,我们设置一个快捷键进行绑定,比如`W`,其keycode是87。(有关每个键的keycode搜一下就能得到) + +然后在`actions.js`的`keyUp`函数的`switch`中进行处理。 + +``` js +case 87: // W + if (core.status.heroStop) { + // ... 在这里写你要执行脚本 + // 请使用同步脚本,请勿执行任何异步代码,否则可能导致游戏过程或录像出现问题。 + core.insertAction([...]) // 例如,插入一段自定义事件并执行。 + + core.status.route.push("key:"+keyCode); // 录像的支持!这句话必须要加,不然录像回放会出错! + } + break; +``` + + +在勇士处于停止的条件下,按下W键时,将执行你写的脚本代码。请只使用同步脚本而不要使用异步代码,不然可能导致游戏出现问题。 + +`core.status.route.push("key:"+keyCode);` 这句话是对录像的支持,一定要加(这样录像播放时也会模拟该按键)。 + +!> H5不支持组合快捷键,所以不存在`W+1`这种组合快捷键的说法! + +!> 手机端可以通过长按任何位置调出虚拟键盘,再进行按键,和键盘按键是等价的效果! + +## 公共事件 + +在RM中,存在公共事件的说法;也就是通过某个指令来调用一系列事件的触发。 + +在H5中,我们可以使用“插件”的形式来达成这个效果。具体参见“脚本编辑 - 插件编写”。 + +![插件编写](./img/plugin.png) + +当我们在这上面定义了自己需要的函数(插件后),就可以通过任何方式进行调用。 + +在这个插件编写的过程中,我们可以使用任何[常见API](api)里面的代码调用;也可以通过`core.insertAction`来插入自定义事件执行。 + +下面是一个很简单的例子,我编写一个公共事件(插件),其效果是让勇士生命值变成原来的x倍,并令面前的图块消失。 + +``` js +this.myfunc = function(x) { + core.status.hero.hp *= x; // 勇士生命翻若干倍 + core.insertAction([ // 自定义事件:令面前的图块消失。 + {"type": "setValue", "name": "flag:x", "value": "core.nextX()"}, + {"type": "setValue", "name": "flag:y", "value": "core.nextY()"}, + {"type": "hide", "loc": ["flag:x", "flag:y"]} + ]); +} +``` + +然后比如我们在某个道具的使用效果 `useItemEffect` 中写 `core.plugin.myfunc(2)` 即可调用此公共事件(插件)。也可以在战后事件或自定义脚本等位置来写。 + +通过这种,将脚本和自定义事件混用的方式,可以达到和RM中公共事件类似的效果,即一个调用触发一系列事件。 + +## 自定义状态栏(新增显示项) + +在V2.2以后,我们可以自定义状态栏背景图(全塔属性 - statusLeftBackground)等等。 + +但是,如果我们还想新增其他项目的显示,比如技能塔所需要的魔力值(气息),该怎么办? + +需要进行如下几个操作: + +1. 定义图标ID;比如魔力我就定义mana,气息可以简单的定义qixi;你也可以定义其他的ID,但是不能和已有的重复。这里以mana为例。 +2. 在index.html的statusBar中(44行起),进行该状态栏项的定义。仿照其他几项,插在其应当显示的位置,注意替换掉相应的ID。 +``` html +
+ +

+
+``` +3. 在editor.html中的statusBar(305行起),仿照第二点同样添加;这一项如果不进行则会地图编辑器报错。 +4. 使用便捷PS工具,打开icons.png,新增一行并将魔力的图标P上去;记下其索引比如23(减速播放图标的下方)。 +5. 在main.js的this.statusBar中增加图片、图标和内容的定义。 +``` js +this.statusBar = { + 'images': { + // ...其他略 + 'mana': document.getElementById("img-mana"), // 图片的定义 + }, + 'icons': { + // ...其他略 + 'mana': 23, // 图标的定义,这里对应的是icons.png中的索引 + }, + // ...其他略 + 'mana': document.getElementById('mana'), // 显示内容(数据)的定义 +} +``` +6. 显示内容的设置。在control.js的updateStatusBar函数,可以对该状态栏显示内容进行设置,下面是几个例子。 +``` js +core.statusBar.mana.innerHTML = core.getFlag('mana', 0); // 设置其显示内容为flag:mana值。 +core.statusBar.mana.innerHTML = core.getFlag('mana', 0) + '/' + core.getFlag('manaMax', 0); // 显示内容将类似 "32/60" 这样。 +core.statusBar.mana.style.fontStyle = 'normal'; // 这一行会取消斜体。如果是汉字(比如技能名)的话,斜体起来会非常难看,可以通过这一句取消。 +``` +7. 在control.js的clearStatusBar函数,`statusList`里面也要增加mana项,这样清空状态栏时也会对其清空。 + +## 技能塔的支持 + +其实,在HTML5上制作技能塔是完全可行的。 + +要支持技能塔,可能需要如下几个方面: + +- 魔力(和上限)的定义添加 +- 状态栏的显示 +- 技能的触发(按键与录像问题) +- 技能的效果 + +下面依次进行描述。 + +### 魔力的定义添加 + +当我们定义了魔力的ID,比如`mana`后,要使用它,一般有两种方式:属性获取`status:mana`或者flag标记`flag:mana`。 + +如果要属性获取,则需要打开`data.js`文件,并在`hero`中添加定义。 + +通过这种方式定义的,可以通过`core.setStatus('mana', 0)`以及`core.getStatus('mana')`来设置或获取。 + +``` js +'hero': { + // ... 上略 + 'mana': 0, // 增添mana定义,可以放在experience之后。同理可定义manaMax表示当前最大魔力值。 +} +``` + +如果要flag标记,则无需额外在任何地方进行定义。只需要在设置或取用的时候使用 `core.setFlag('mana', 0)` 或 `core.getFlag('mana', 0)` 即可。 + +下面我都使用属性获取的方式来进行说明。 + +### 状态栏的显示 + +首先我们需要额外新增一个状态栏;请参见[自定义状态栏(新增显示项)](#自定义状态栏(新增显示项))。 + +我们可以在魔力那一行显示当前值和最大值: + +``` js +core.setStatus('mana', Math.min(core.getStatus('mana'), core.getStatus('manaMax'))); // 如果魔力存在上限,则不能超过其上限值 +core.statusBar.mana.innerHTML = core.getStatus('mana') + '/' + core.getStatus('manaMax', 0); // 显示比如 6/30 这样 +``` + +如果我们还需要显示当前使用的技能名,也是可以的;定义一个ID为skill,然后按照上面的做法新增一行。 + +请注意,如果是中文字符,需要取消斜体(不然会非常难看的)! + +``` js +core.statusBar.skill.style.fontStyle = 'normal'; // 取消斜体显示 +core.statusBar.skill.innerHTML = core.getFlag('skillName', '无'); // 使用flag:skillName表示当前激活的技能名。 +``` + +### 技能的触发 + +我们可以按键触发技能。有关绑定按键请参见[自定义快捷键](#自定义快捷键)。 + +下面是一个很简单的例子,当勇士按下W后,如果魔力不小于5点则允许开启技能"二倍斩",再次按W则关闭技能。 + +``` js +case 87: // W + if (core.status.heroStop) { // 当前停止状态;这个if需要加,不能在行走过程中触发不然容易出错。 + if (core.getFlag('skill', 0)==0) { // 判断当前是否已经开了技能 + if (core.getStatus('mana')>=5) { // 这里要写当前能否开技能的条件判断,比如魔力值至少要多少 + core.setFlag('skill', 1); // 开技能1 + core.setFlag('skillName', '二倍斩'); // 设置技能名 + } + else { + core.drawTip("魔力不足,无法开技能"); + } + } + else { // 关闭技能 + core.setFlag('skill', 0); // 关闭技能状态 + core.setFlag('skillName', '无'); + } + core.updateStatusBar(); // 立刻更新状态栏和地图显伤 + core.status.route.push("key:"+keyCode); // 录像的支持!这句话必须要加,不然录像回放会出错! + } + break; +``` + +简单的说,用flag:skill判断当前开启的技能,flag:skillName表示该技能名。(可在状态栏显示) + +在勇士处于停止的条件下,按下W键时,判断当前是否开启了技能,如果开启则关闭,没开则再判断是否允许开启(魔力值够不够等)。 + +`core.status.route.push("key:"+keyCode);` 这句话是对录像的支持,一定要加(这样录像播放时也会模拟该按键)。 + +!> 1,2,3这三个键被默认绑定到了破炸飞;如果想用的话也是一样,只不过是把已有的实现进行替换。 + +!> 手机端可以通过长按任何位置调出虚拟键盘,再进行按键,和键盘按键是等价的效果! + +### 技能的效果 + +最后一点就是技能的效果;其实到了这里就和RM差不多了。 + +技能的效果要分的话有地图类技能,战斗效果类技能,后续影响类技能什么的,这里只介绍最简单的战斗效果类技能。 +其他的几类技能根据需求可能更为麻烦,有兴趣可自行进行研究。 + +战斗效果内技能要改两个地方:战斗伤害计算,战后扣除魔力值。 + +战斗伤害计算在`enenmys.js`的`getDamageInfo`函数,有需求直接修改这个函数即可。 + +战后扣除魔力值则在脚本编辑的`afterBattle`中进行编辑即可。 + +举个例子,我设置一个勇士的技能:二倍斩,开启技能消耗5点魔力,下一场战斗攻击力翻倍。 + +那么,直接在`getDamageInfo`中进行判断: + +``` js +if (core.getFlag('skill', 0)==1) { // 开启了技能1 + hero_atk *= 2; // 计算时攻击力翻倍 +} +``` + +然后在脚本编辑的战后事件中进行魔力值的扣除: + +``` js +if (core.getFlag('skill', 0)==1) { // 开启了技能1 + core.status.hero.mana -= 5; // 扣除5点魔力值 + core.setFlag('skill', 0); // 自动关闭技能 + core.setFlag('skillName', '无'); +} +``` + +  + +通过上述这几种方式,我们就能成功的让H5支持技能啦! + +## 多角色的支持 + +其实,我们的样板还能支持多角色的制作。比如《黑·白·间》之类的塔也是完全可以刻的。 + +你只需要如下几步来达到多角色的效果。 + +1. 每个角色弄一张行走图。相关信息参见[自定义事件:setHeroIcon](event#setHeroIcon:更改角色行走图)。 +2. [覆盖楼传事件](#覆盖楼传事件),这样可以通过点工具栏的楼层传送按钮来切换角色。当然你也完全可以自己写一个道具,或[自定义快捷键](#自定义快捷键)来进行绑定。 +3. 在脚本编辑的setInitData中初始化新角色的属性值。 + ```js + // 所有需要保存的内容;这些保存的内容不会多角色共用,在切换时会进行恢复。 + // 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化, + // 多角色共用hp的话则删除hp,等等。总之,不共用的属性都在这里进行定义就好。 + var initData = { + "floorId": "MT0", // 该角色楼层ID + "icon": "hero2.png", // 角色的行走图名称 + "name": "2号角色", + "lv": 1, + "hp": 1000, + "atk": 10, + "def": 10, + "mdef": 0, + "loc": {"x": 0, "y": 0, "direction": "up"}, + // 不共用的数据都可以在这里加上定义 + } + core.setFlag("hero1", initData); // 将属性值存到变量中 + ``` +4. 道具(或快捷键)的脚本如下。 + ```js + // 这个saveList和上面的初始化定义中的的key,除了不要icon(行走图名称)其他应完全相同。 + var saveList = ["floorId", "name", "lv", "hp", "atk", "def", "mdef", "loc"]; + + // 保存当前内容 + var toSave = {}; + saveList.forEach(function(name) { + if (name=='floorId') toSave[name] = core.status.floorId; // 楼层单独设置 + else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象 + }) + + var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID + var toHeroId = (currHeroId+1)%2; // 获得要切换到的角色ID,比如 0->1,1->0 + + core.setFlag("hero"+currHeroId, toSave); // 将当前角色信息进行保存 + + var data = core.getFlag("hero"+toHeroId); // 获得要切换的角色保存内容 + + // 设置角色的属性值 + saveList.forEach(function(name) { + if (core.isset(core.status.hero[name]) && core.isset(data[name])) + core.status.hero[name] = core.clone(data[name]); + }) + + // 插入事件:改变角色行走图并进行楼层切换 + core.insertAction([ + {"type": "setHeroIcon", "name": data.icon||"hero.png"}, // 改变行走图 + {"type": "changeFloor", "floorId": data.floorId, "loc": [data.loc.x, data.loc.y], + "direction": data.loc.direction, "time": 0} // 楼层切换事件 + ]) + core.setFlag("heroId", toHeroId); // 保存切换到的角色ID + ``` + ## 根据难度分歧来自定义地图 遗憾的是,所有地图数据必须在剧本的map中指定,换句话说,我们无法在游戏进行中动态修改地图,比如为简单难度增加一个血瓶。 diff --git a/docs/serviceWorker.js b/docs/serviceWorker.js deleted file mode 100644 index 34634e1e..00000000 --- a/docs/serviceWorker.js +++ /dev/null @@ -1,75 +0,0 @@ -const RUNTIME = 'docsify' -const HOSTNAME_WHITELIST = [ - self.location.hostname, - 'fonts.gstatic.com', - 'fonts.googleapis.com', - 'cdn.bootcss.com' -] - -// The Util Function to hack URLs of intercepted requests -const getFixedUrl = (req) => { - var now = Date.now() - var url = new URL(req.url) - - // 1. fixed http URL - // Just keep syncing with location.protocol - // fetch(httpURL) belongs to active mixed content. - // And fetch(httpRequest) is not supported yet. - url.protocol = self.location.protocol - - // 2. add query for caching-busting. - // Github Pages served with Cache-Control: max-age=600 - // max-age on mutable content is error-prone, with SW life of bugs can even extend. - // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. - // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 - if (url.hostname === self.location.hostname) { - url.search += (url.search ? '&' : '?') + 'cache-bust=' + now - } - return url.href -} - -/** - * @Lifecycle Activate - * New one activated when old isnt being used. - * - * waitUntil(): activating ====> activated - */ -self.addEventListener('activate', event => { - event.waitUntil(self.clients.claim()) -}) - -/** - * @Functional Fetch - * All network requests are being intercepted here. - * - * void respondWith(Promise r) - */ -self.addEventListener('fetch', event => { - // Skip some of cross-origin requests, like those for Google Analytics. - if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { - // Stale-while-revalidate - // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale - // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 - const cached = caches.match(event.request) - const fixedUrl = getFixedUrl(event.request) - const fetched = fetch(fixedUrl, { cache: 'no-store' }) - const fetchedCopy = fetched.then(resp => resp.clone()) - - // Call respondWith() with whatever we get first. - // If the fetch fails (e.g disconnected), wait for the cache. - // If there’s nothing in cache, wait for the fetch. - // If neither yields a response, return offline pages. - event.respondWith( - Promise.race([fetched.catch(_ => cached), cached]) -.then(resp => resp || fetched) -.catch(_ => { /* eat any errors */ }) -) - - // Update the cache with the version we fetched (only for ok status) - event.waitUntil( - Promise.all([fetchedCopy, caches.open(RUNTIME)]) - .then(([response, cache]) => response.ok && cache.put(event.request, response)) - .catch(_ => { /* eat any errors */ }) -) -} -}) \ No newline at end of file diff --git a/docs/start.md b/docs/start.md index 195767ae..180d2002 100644 --- a/docs/start.md +++ b/docs/start.md @@ -30,6 +30,8 @@ * “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。 * “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。 +!> **整个造塔过程中,启动服务必须全程处于开启状态!切不可手滑关闭,否则做的都是无用功!** + ## 绘制地图 有两种绘制地图的方式:从头绘制地图;从RMXP中导入已有的地图。 @@ -44,6 +46,8 @@ 如果提示“该素材未被定义”或有红色问号框,请参见[素材注册](#素材注册)。 +绘制地图时可以右键弹出菜单,移动图块和事件。 + ### 从RMXP导入已有的地图 如果我们想复刻一个现有的,已经被RMXP所制作的塔,也有很便捷的方式,那就是用到我们的“地图生成器”。 @@ -168,9 +172,47 @@ 素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 +## 控制台调试 + +HTML5的塔都是可以进行控制台调试的。 + +当我们使用Chrome进入游戏后,可以按 `Ctrl+Shift+I` ,并找到 `Console` 控制台。 + +![控制台](./img/console.png) + +在控制台中,我们可以输入一些命令对游戏进行调试,常见的命令有: + +- `core.status.floorId` 获得当前层的floorId。 +- `core.status.thisMap` 获得当前地图信息。例如`core.status.thisMap.blocks`可以获得当前层所有图块。 +- `core.floors` 获得所有剧本的信息。例如`core.floors[core.status.floorId].events`可以获得当前层所有事件。 +- `core.status.hero` 获得当前勇士状态信息。例如`core.status.hero.atk`就是当前勇士的攻击力数值。 +- `core.material.enemys` 获得所有怪物信息。例如`core.material.enemys.greenSlime`就是获得绿色史莱姆的属性数据。 +- `core.material.items` 获得所有道具的信息。例如`core.material.items.pickaxe`就是获得破墙镐的信息。 +- `core.debug()` 开启调试模式;此模式下可以按住Ctrl键进行穿墙。 +- `core.updateStatusBar()` 立刻更新状态栏和地图显伤。 +- `core.setStatus('atk', 1000)` 直接设置勇士的某项属性。本句等价于 `core.status.hero.atk = 1000`。 +- `core.getStatus('atk')` 返回勇士当前某项属性数值。本句等价于 `core.status.hero.atk`。 +- `core.setItem('pickaxe', 10)` 直接设置勇士某个道具的个数。这里可以需要写道具的ID。 +- `core.getItem('pickaxe', 2)` 令勇士获得两个破墙镐。 +- `core.itemCount('pickaxe')` 返回勇士某个道具的个数。 +- `core.hasItem('pickaxe')` 返回勇士是否拥有某个道具。等价于`core.itemCount('pickaxe')!=0`。 +- `core.setFlag('xxx', 1)` 设置某个flag/自定义变量的值。 +- `core.getFlag('xxx', 10)` 获得某个flag/自定义变量的值;如果该项不存在(未被定义),则返回第二个参数的值。 +- `core.hasFlag('xxx')` 返回是否存在某个变量且不为0。等价于`core.getFlag('xxx', 0)!=0`。 +- `core.insertAction(list)` 执行一段自定义事件。比如 `core.insertAction(["剧情文本"])` 将执行一个剧情文本显示事件。 +- `core.changeFloor('MT2', 'downFloor')` 立刻执行楼层切换到MT2层的下楼点位置。 +- `core.changeFloor('MT5', null, {'x': 4, 'y': 7})` 立刻切换楼层到MT5层的(4,7)点。 +- `core.getBlock(3, 5, 'MT1')` 获得当前地图上某一个块的信息。第三个参数为floorId,可省略表示当前楼层。 +- `core.resetMap()` 重置当前层地图。**当修改地图后再读档,修改的地图不会立刻生效,此时可以使用resetMap来重置当前楼层的地图。** +- `localStorage` 获得所有的存档数据。可以用 `core.getLocalStorage('save1')` 来具体获得某个存档。 +- …… + +更多API和详细参数介绍可参见[API列表](api)。 + + ## 报错处理 -有时候刷新后可能页面变成空白,即无法正确加载,游戏也无法正常进入。 +有时候刷新后可能地图编辑器页面变成空白,即无法正确加载,游戏也无法正常进入。 出现这种问题的原因往往是如下几种: - 手动直接打开并错误编辑了文件 diff --git a/index.html b/index.html index 32cb7037..e03830c3 100644 --- a/index.html +++ b/index.html @@ -82,7 +82,7 @@

-
+
diff --git a/libs/actions.js b/libs/actions.js index 0bf91aa6..1fa43e52 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -25,6 +25,7 @@ actions.prototype.onkeyDown = function (e) { core.status.holdingKeys.push(e.keyCode); this.pressKey(e.keyCode); } else { + if (e.keyCode==17) core.status.ctrlDown = true; this.keyDown(e.keyCode); } } @@ -60,6 +61,7 @@ actions.prototype.onkeyUp = function(e) { } this.keyUp(e.keyCode); } else { + if (e.keyCode==17) core.status.ctrlDown = false; this.keyUp(e.keyCode); } } @@ -337,8 +339,14 @@ actions.prototype.keyUp = function(keyCode, fromReplay) { core.ui.drawHelp(); break; case 82: // R - if (core.status.heroStop) - core.ui.drawReplay(); + if (core.status.heroStop) { + if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法回放录像"); + } + else { + core.ui.drawReplay(); + } + } break; case 33: case 34: // PAGEUP/PAGEDOWN if (core.status.heroStop) { @@ -409,7 +417,7 @@ actions.prototype.ondown = function (x ,y) { core.timeout.onDownTimeout = setTimeout(function () { if (core.interval.onDownInterval == null) { core.interval.onDownInterval = setInterval(function () { - if (!core.actions.longClick()) { + if (!core.actions.longClick(x, y, true)) { clearInterval(core.interval.onDownInterval); core.interval.onDownInterval = null; } @@ -480,10 +488,7 @@ actions.prototype.onup = function () { // 长按 if (!core.status.lockControl && stepPostfix.length==0 && core.status.downTime!=null && new Date()-core.status.downTime>=1000) { - core.waitHeroToStop(function () { - // 绘制快捷键 - core.ui.drawKeyBoard(); - }); + this.longClick(posx, posy); } else { //posx,posy是寻路的目标点,stepPostfix是后续的移动 @@ -709,15 +714,23 @@ actions.prototype.onmousewheel = function (direct) { /////////////////// 在某个界面时的按键点击效果 /////////////////// ////// 长按 ////// -actions.prototype.longClick = function () { +actions.prototype.longClick = function (x, y, fromEvent) { if (!core.isPlaying()) return false; - if (core.status.event.id=='text') { - core.drawText(); - return true; + if (core.status.lockControl) { + if (core.status.event.id=='text') { + core.drawText(); + return true; + } + if (core.status.event.id=='action' && core.status.event.data.type=='text') { + core.doAction(); + return true; + } } - if (core.status.event.id=='action' && (core.status.event.data.type=='text' || core.status.event.data.type=='wait')) { - core.doAction(); - return true; + else if (!fromEvent) { + core.waitHeroToStop(function () { + // 绘制快捷键 + core.ui.drawKeyBoard(); + }); } return false; } @@ -728,7 +741,7 @@ actions.prototype.keyDownCtrl = function () { core.drawText(); return; } - if (core.status.event.id=='action' && (core.status.event.data.type=='text' || core.status.event.data.type=='wait')) { + if (core.status.event.id=='action' && core.status.event.data.type=='text') { core.doAction(); return; } @@ -769,11 +782,20 @@ actions.prototype.keyUpConfirmBox = function (keycode) { ////// 自定义事件时的点击操作 ////// actions.prototype.clickAction = function (x,y) { - if (core.status.event.data.type=='text' || core.status.event.data.type=='wait') { + if (core.status.event.data.type=='text') { // 文字 core.doAction(); return; } + if (core.status.event.data.type=='wait') { + core.setFlag('type', 1); + core.setFlag('x', x); + core.setFlag('y', y); + core.status.route.push("input:"+(10000+100*x+y)); + core.doAction(); + return; + } + if (core.status.event.data.type=='choices') { // 选项 var data = core.status.event.data.current; @@ -811,7 +833,14 @@ actions.prototype.keyDownAction = function (keycode) { ////// 自定义事件时,放开某个键的操作 ////// actions.prototype.keyUpAction = function (keycode) { - if ((core.status.event.data.type=='text' || core.status.event.data.type=='wait') && (keycode==13 || keycode==32 || keycode==67)) { + if (core.status.event.data.type=='text' && (keycode==13 || keycode==32 || keycode==67)) { + core.doAction(); + return; + } + if (core.status.event.data.type=='wait') { + core.setFlag('type', 0); + core.setFlag('keycode', keycode); + core.status.route.push("input:"+keycode); core.doAction(); return; } @@ -1533,6 +1562,8 @@ actions.prototype.clickSettings = function (x,y) { }); break; case 5: + core.ui.drawStatistics(); + /* core.ui.drawWaiting("正在拉取统计信息,请稍后..."); var formData = new FormData(); @@ -1580,6 +1611,7 @@ actions.prototype.clickSettings = function (x,y) { core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:XHR Error"); } xhr.send(formData); + */ break; case 6: core.ui.drawHelp(); @@ -1687,6 +1719,10 @@ actions.prototype.clickSyncSave = function (x,y) { }); break; case 4: + if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法下载录像"); + break; + } core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify({ 'name': core.firstData.name, 'hard': core.status.hard, diff --git a/libs/control.js b/libs/control.js index a7047e7a..e345f6c4 100644 --- a/libs/control.js +++ b/libs/control.js @@ -59,6 +59,13 @@ control.prototype.setRequestAnimationFrame = function () { core.animateFrame.moveTime = core.animateFrame.moveTime||timestamp; core.animateFrame.weather.time = core.animateFrame.weather.time||timestamp; + // move time + if (core.isPlaying() && core.isset(core.status) && core.isset(core.status.hero) + && core.isset(core.status.hero.statistics)) { + core.status.hero.statistics.totalTime += timestamp-(core.status.hero.statistics.start||timestamp); + core.status.hero.statistics.start=timestamp; + } + // Global Animate if (core.animateFrame.globalAnimate && core.isPlaying()) { @@ -237,6 +244,12 @@ control.prototype.clearStatus = function() { ////// 重置游戏状态和初始数据 ////// control.prototype.resetStatus = function(hero, hard, floorId, route, maps) { + var totalTime=0; + if (core.isset(core.status) && core.isset(core.status.hero) + && core.isset(core.status.hero.statistics) && core.isset(route)) { + totalTime=core.status.hero.statistics.totalTime; + } + this.clearStatus(); // 初始化status @@ -249,6 +262,20 @@ control.prototype.resetStatus = function(hero, hard, floorId, route, maps) { core.material.enemys = core.clone(core.enemys.getEnemys()); // 初始化人物属性 core.status.hero = core.clone(hero); + // 统计数据 + if (!core.isset(core.status.hero.statistics)) + core.status.hero.statistics = { + 'totalTime': totalTime, + 'hp': 0, + 'battleDamage': 0, + 'poisonDamage': 0, + 'extraDamage': 0, + 'moveDirectly': 0, + 'ignoreSteps': 0, + } + core.status.hero.statistics.totalTime = Math.max(core.status.hero.statistics.totalTime, totalTime); + core.status.hero.statistics.start = null; + core.status.hard = hard; // 初始化路线 if (core.isset(route)) @@ -349,12 +376,15 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { core.status.automaticRoute.moveDirectly = true; setTimeout(function () { if (core.status.automaticRoute.moveDirectly && core.status.heroMoving==0) { - if (core.canMoveDirectly(destX, destY)) { + var ignoreSteps = core.canMoveDirectly(destX, destY); + if (ignoreSteps>0) { core.clearMap('hero', 0, 0, 416, 416); core.setHeroLoc('x', destX); core.setHeroLoc('y', destY); core.drawHero(); core.status.route.push("move:"+destX+":"+destY); + core.status.hero.statistics.moveDirectly++; + core.status.hero.statistics.ignoreSteps+=ignoreSteps; } } core.status.automaticRoute.moveDirectly = false; @@ -621,6 +651,7 @@ control.prototype.moveAction = function (callback) { if (core.status.event.id!='ski') core.status.route.push(direction); core.status.automaticRoute.moveStepBeforeStop = []; + core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); if (canMove) // 非箭头:触发 core.trigger(x + scan[direction].x, y + scan[direction].y); core.drawHero(direction, x, y); @@ -676,7 +707,7 @@ control.prototype.turnHero = function() { ////// 让勇士开始移动 ////// control.prototype.moveHero = function (direction, callback) { // 如果正在移动,直接return - if (core.status.heroMoving>0) return; + if (core.status.heroMoving!=0) return; if (core.isset(direction)) core.setHeroLoc('direction', direction); if (!core.isset(callback)) { // 如果不存在回调函数,则使用heroMoveTrigger @@ -685,8 +716,18 @@ control.prototype.moveHero = function (direction, callback) { var doAction = function () { if (!core.status.heroStop) { - core.moveAction(); - setTimeout(doAction, 50); + if (core.hasFlag('debug') && core.status.ctrlDown) { + if (core.status.heroMoving!=0) return; + core.status.heroMoving=-1; + core.eventMoveHero([core.getHeroLoc('direction')], 100, function () { + core.status.heroMoving=0; + doAction(); + }); + } + else { + core.moveAction(); + setTimeout(doAction, 50); + } } else { core.stopHero(); @@ -770,6 +811,7 @@ control.prototype.moveOneStep = function() { core.status.hero.steps++; // 中毒状态 if (core.hasFlag('poison')) { + core.status.hero.statistics.poisonDamage += core.values.poisonDamage; core.status.hero.hp -= core.values.poisonDamage; if (core.status.hero.hp<=0) { core.status.hero.hp=0; @@ -783,6 +825,7 @@ control.prototype.moveOneStep = function() { ////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback ////// control.prototype.waitHeroToStop = function(callback) { + var lastDirection = core.status.automaticRoute.lastDirection; core.stopAutomaticRoute(); core.clearContinueAutomaticRoute(); if (core.isset(callback)) { @@ -791,6 +834,8 @@ control.prototype.waitHeroToStop = function(callback) { core.status.automaticRoute.moveDirectly = false; setTimeout(function(){ core.status.replay.animate=false; + if (core.isset(lastDirection)) + core.setHeroLoc('direction', lastDirection); core.drawHero(); callback(); }, 30); @@ -1007,9 +1052,10 @@ control.prototype.checkBlock = function () { } if (damage>0) { - core.playSound('zone.ogg'); + core.playSound('zone.mp3'); core.drawAnimate("zone", x, y); } + core.status.hero.statistics.extraDamage += damage; if (core.status.hero.hp<=0) { core.status.hero.hp=0; @@ -1317,7 +1363,9 @@ control.prototype.updateFg = function () { // 临界显伤 if (core.flags.displayCritical) { - var critical = core.formatBigNumber(core.enemys.getCritical(id)); + var critical = core.enemys.nextCriticals(id); + if (critical.length>0) critical=critical[0]; + critical = core.formatBigNumber(critical[0]); if (critical == '???') critical = '?'; core.setFillStyle('fg', '#000000'); core.canvas.fg.fillText(critical, 32 * x + 2, 32 * (y + 1) - 2 - 10); @@ -1338,6 +1386,7 @@ control.prototype.updateFg = function () { for (var y=0;y<13;y++) { var damage = core.status.checkBlock.damage[13*x+y]; if (damage>0) { + damage = core.formatBigNumber(damage); core.setFillStyle('fg', '#000000'); core.canvas.fg.fillText(damage, 32 * x + 17, 32 * (y + 1) - 13); core.canvas.fg.fillText(damage, 32 * x + 15, 32 * (y + 1) - 15); @@ -1368,8 +1417,11 @@ control.prototype.doEffect = function (expression) { } } -////// 作弊 ////// +////// 开启debug模式 ////// control.prototype.debug = function() { + core.setFlag('debug', true); + core.insertAction(["\t[调试模式开启]此模式下按住Ctrl键可以穿墙并忽略一切事件。\n同时,录像将失效,也无法上传成绩。"]); + /* core.setStatus('hp', 999999); core.setStatus('atk', 10000); core.setStatus('def', 10000); @@ -1386,6 +1438,7 @@ control.prototype.debug = function() { core.status.hero.flyRange.push(i); core.updateStatusBar(); core.drawTip("作弊成功"); + */ } ////// 开始播放 ////// @@ -1660,12 +1713,16 @@ control.prototype.replay = function () { else if (action.indexOf('move:')==0) { var pos=action.substring(5).split(":"); var x=parseInt(pos[0]), y=parseInt(pos[1]); - if (core.canMoveDirectly(x,y)) { + + var ignoreSteps = core.canMoveDirectly(x, y); + if (ignoreSteps>0) { core.clearMap('hero', 0, 0, 416, 416); core.setHeroLoc('x', x); core.setHeroLoc('y', y); core.drawHero(); core.status.route.push("move:"+x+":"+y); + core.status.hero.statistics.moveDirectly++; + core.status.hero.statistics.ignoreSteps+=ignoreSteps; core.replay(); return; } @@ -1807,6 +1864,8 @@ control.prototype.openSettings = function (need) { ////// 自动存档 ////// control.prototype.autosave = function (removeLast) { + if (core.status.event.id!=null) + return; var x=null; if (removeLast) x=core.status.route.pop(); @@ -2061,11 +2120,17 @@ control.prototype.loadData = function (data, callback) { ////// 设置勇士属性 ////// control.prototype.setStatus = function (statusName, statusVal) { - core.status.hero[statusName] = statusVal; + if (core.isset(core.status.hero.loc[statusName])) + core.status.hero.loc[statusName] = statusVal; + else + core.status.hero[statusName] = statusVal; } ////// 获得勇士属性 ////// control.prototype.getStatus = function (statusName) { + // support status:x + if (core.isset(core.status.hero.loc[statusName])) + return core.status.hero.loc[statusName]; return core.status.hero[statusName]; } @@ -2129,11 +2194,10 @@ control.prototype.playBgm = function (bgm) { core.material.bgms[core.musicStatus.playingBgm].pause(); } // 播放当前BGM - core.musicStatus.playingBgm = bgm; core.material.bgms[bgm].volume = core.musicStatus.volume; core.material.bgms[bgm].play(); + core.musicStatus.playingBgm = bgm; core.musicStatus.isPlaying = true; - } catch (e) { console.log("无法播放BGM "+bgm); @@ -2355,6 +2419,7 @@ control.prototype.resize = function(clientWidth, clientHeight) { if (!core.flags.enableExperience) count--; if (!core.flags.enableLevelUp) count--; if (!core.flags.enableDebuff) count--; + if (core.isset(core.flags.enableKeys) && !core.flags.enableKeys) count--; var statusLineHeight = BASE_LINEHEIGHT * 9 / count; var statusLineFontSize = DEFAULT_FONT_SIZE; @@ -2639,6 +2704,12 @@ control.prototype.resize = function(clientWidth, clientHeight) { display: core.flags.enableLevelUp ? 'block': 'none' } }, + { + id: 'keyCol', + rules: { + display: !core.isset(core.flags.enableKeys)||core.flags.enableKeys?'block':'none' + } + }, { 'id': 'debuffCol', rules: { diff --git a/libs/core.js b/libs/core.js index 525d3685..769249b6 100644 --- a/libs/core.js +++ b/libs/core.js @@ -107,6 +107,7 @@ function core() { // 按下键的时间:为了判定双击 'downTime': null, + 'ctrlDown': false, // 路线&回放 'route': [], @@ -651,6 +652,11 @@ core.prototype.getBlock = function (x, y, floorId, needEnable) { return core.maps.getBlock(x,y,floorId,needEnable); } +////// 获得某个点的blockId ////// +core.prototype.getBlockId = function (x, y, floorId, needEnable) { + return core.maps.getBlockId(x, y, floorId, needEnable); +} + ////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { core.maps.moveBlock(x,y,steps,time,immediateHide,callback) @@ -883,9 +889,19 @@ core.prototype.debug = function() { core.control.debug(); } +////// 存档前 ////// +core.prototype.beforeSaveData = function (data) { + return core.events.beforeSaveData(data); +} + +////// 读档后 ////// +core.prototype.afterLoadData = function (data) { + return core.events.afterLoadData(data); +} + ////// 重置当前地图 ////// -core.prototype.resetMap = function() { - core.maps.resetMap(); +core.prototype.resetMap = function(floorId) { + core.maps.resetMap(floorId); } ////// 开始播放 ////// @@ -893,6 +909,11 @@ core.prototype.startReplay = function (list) { core.control.startReplay(list); } +////// 关闭UI窗口 ////// +core.prototype.closePanel = function () { + core.ui.closePanel(); +} + ////// 更改播放状态 ////// core.prototype.triggerReplay = function () { core.control.triggerReplay(); @@ -1023,8 +1044,8 @@ core.prototype.decodeRoute = function (route) { } ////// 发送HTTP ////// -core.prototype.http = function (type, url, formData, success, error, header) { - core.utils.http(type, url, formData, success, error, header) +core.prototype.http = function (type, url, formData, success, error, mimeType, responseType) { + core.utils.http(type, url, formData, success, error, mimeType, responseType) } ////// 设置勇士属性 ////// diff --git a/libs/enemys.js b/libs/enemys.js index 1b4158ff..bdc5e7a0 100644 --- a/libs/enemys.js +++ b/libs/enemys.js @@ -132,92 +132,63 @@ enemys.prototype.getExtraDamage = function (monster) { return extra_damage; } -////// 接下来若干个临界值计算 ///// +////// 接下来N个临界值和临界减伤计算 ////// enemys.prototype.nextCriticals = function (monsterId, number) { - number = number||1; + var useTurn = true; // 是否使用回合法计算临界值;如果要用循环法,则直接改为false。 + number = number||1; var monster = core.material.enemys[monsterId]; // 坚固、模仿怪物没有临界! if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return []; - var info = this.getDamageInfo(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); if (info == null) { if (core.status.hero.atk<=monster.def) { - return [(monster.def+1-core.status.hero.atk)+":?"]; + return [[monster.def+1-core.status.hero.atk,'?']]; } return []; } - if (info.damage <= 0) return []; - - var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn; - - if (turn<=1) return []; var list = [], pre = null; + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn; - for (var t = turn-1;t>=1;t--) { - var nextAtk = Math.ceil(mon_hp/t) + mon_def; - if (nextAtk<=hero_atk) break; - if (nextAtk!=pre) { - var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, nextAtk, core.status.hero.def, core.status.hero.mdef); - if (nextInfo==null) break; - list.push((nextAtk-hero_atk)+":"+(info.damage-nextInfo.damage)); - if (nextInfo.damage<=0) break; - pre = nextAtk; + if (useTurn) { // 回合数计算法 + for (var t = turn-1;t>=1;t--) { + var nextAtk = Math.ceil(mon_hp/t) + mon_def; + if (nextAtk<=hero_atk) break; + if (nextAtk!=pre) { + var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, nextAtk, core.status.hero.def, core.status.hero.mdef); + if (nextInfo==null) break; + list.push([nextAtk-hero_atk,info.damage-nextInfo.damage]); + pre = nextAtk; + } + if (list.length>=number) + break; } - if (list.length>=number) - break; } + else { // 暴力for循环法 + pre = info.damage; + for (var atk=hero_atk+1;atk<=mon_hp+mon_def;atk++) { + var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, atk, core.status.hero.def, core.status.hero.mdef); + if (nextInfo==null) break; + if (pre>nextInfo.damage) { + pre = nextInfo.damage; + list.push([atk-hero_atk, info.damage-nextInfo.damage]); + if (list.length>=number) break; + } + } + } + if (list.length==0) list.push([0,0]); return list; } -////// 临界值计算 ////// -enemys.prototype.getCritical = function (monsterId) { - var monster = core.material.enemys[monsterId]; - // 坚固、模仿怪物没有临界! - if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return "???"; - - var info = this.getDamageInfo(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); - - if (info == null) { - if (core.status.hero.atk<=monster.def) - return monster.def+1-core.status.hero.atk; - return '???'; - } - if (info.damage <= 0) return 0; - - var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn; - - // turn 是勇士攻击次数 - if (turn<=1) return 0; // 攻杀 - - // 每回合最小伤害 = ⎡怪物生命/勇士攻击次数⎤ - var nextAtk = Math.ceil(mon_hp/(turn-1)) + mon_def; - - if (nextAtk <= hero_atk) return 0; - return nextAtk - hero_atk; - -} - -////// 临界减伤计算 ////// -enemys.prototype.getCriticalDamage = function (monsterId) { - var c = this.getCritical(monsterId); - if (c == '???') return '???'; - if (c <= 0) return 0; - var monster = core.material.enemys[monsterId]; - var last = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); - var now = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk+c, core.status.hero.def, core.status.hero.mdef); - if (last == null || now==null) return '???'; - return last - now; -} - -////// 1防减伤计算 ////// -enemys.prototype.getDefDamage = function (monsterId) { +////// N防减伤计算 ////// +enemys.prototype.getDefDamage = function (monsterId, k) { + k = k || 1; var monster = core.material.enemys[monsterId]; var nowDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef); - var nextDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def + 1, core.status.hero.mdef); + var nextDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def + k, core.status.hero.mdef); if (nowDamage == null || nextDamage ==null) return "???"; return nowDamage - nextDamage; } @@ -225,6 +196,10 @@ enemys.prototype.getDefDamage = function (monsterId) { ////// 获得战斗伤害信息 ////// enemys.prototype.getDamageInfo = function(monster, hero_hp, hero_atk, hero_def, hero_mdef) { + if (typeof monster == 'string') { + monster = core.material.enemys[monster]; + } + var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_special = monster.special; hero_hp=Math.max(0, hero_hp); hero_atk=Math.max(0, hero_atk); @@ -310,6 +285,10 @@ enemys.prototype.getDamageInfo = function(monster, hero_hp, hero_atk, hero_def, ////// 具体的伤害计算公式 ////// enemys.prototype.calDamage = function (monster, hero_hp, hero_atk, hero_def, hero_mdef) { + if (typeof monster == 'string') { + monster = core.material.enemys[monsterId]; + } + var info = this.getDamageInfo(monster, hero_hp, hero_atk, hero_def, hero_mdef); if (info == null) return null; return info.damage; @@ -341,6 +320,9 @@ enemys.prototype.getCurrentEnemys = function (floorId) { if (specialText.length>=3) specialText = "多属性..."; else specialText = specialText.join(" "); + var critical = this.nextCriticals(monsterId); + if (critical.length>0) critical=critical[0]; + enemys.push({ 'id': monsterId, 'name': monster.name, @@ -352,8 +334,8 @@ enemys.prototype.getCurrentEnemys = function (floorId) { 'point': monster.point||0, // 加点 'special': specialText, 'damage': this.getDamage(monsterId), - 'critical': this.getCritical(monsterId), - 'criticalDamage': this.getCriticalDamage(monsterId), + 'critical': critical[0], + 'criticalDamage': critical[1], 'defDamage': this.getDefDamage(monsterId) }); diff --git a/libs/events.js b/libs/events.js index 3158b818..0835161a 100644 --- a/libs/events.js +++ b/libs/events.js @@ -228,6 +228,11 @@ events.prototype.gameOver = function (ending, fromReplay) { core.restart(); }); } + else if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法上传成绩", function () { + core.restart(); + }) + } else { confirmUpload(); } @@ -349,6 +354,8 @@ events.prototype.doAction = function() { core.events.doAction(); break; case "show": // 显示 + if (!core.isset(data.loc)) + data.loc = [x,y]; if ((typeof data.loc[0] == 'number' || typeof data.loc[0] == 'string') && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) data.loc = [[core.calValue(data.loc[0]), core.calValue(data.loc[1])]]; @@ -405,8 +412,6 @@ events.prototype.doAction = function() { } if (floorId==core.status.floorId) { core.drawMap(floorId); - core.drawHero(); - core.updateStatusBar(); } } this.doAction(); @@ -638,13 +643,7 @@ events.prototype.doAction = function() { break; case "setHeroIcon": { - var name = "hero.png"; - if (core.isset(core.material.images.images[data.name]) && core.material.images.images[data.name].width==128) - name = data.name; - core.setFlag("heroIcon", name); - core.material.images.hero.src = core.material.images.images[name].src; - core.material.icons.hero.height = core.material.images.images[name].height/4; - core.drawHero(); + this.setHeroIcon(data.name); this.doAction(); break; } @@ -761,8 +760,27 @@ events.prototype.doAction = function() { } break; case "wait": - if (core.status.replay.replaying) - core.events.doAction(); + if (core.status.replay.replaying) { + var code = core.status.replay.toReplay.shift(); + if (code.indexOf("input:")==0) { + var value = parseInt(code.substring(6)); + core.status.route.push("input:"+value); + if (value>=10000) { + core.setFlag('type', 1); + core.setFlag('x', parseInt((value-10000)/100)); + core.setFlag('y', value%100); + } + else { + core.setFlag('type', 0); + core.setFlag('keycode', value); + } + core.events.doAction(); + } + else { + core.stopReplay(); + core.drawTip("录像文件出错"); + } + } break; case "revisit": // 立刻重新执行该事件 { @@ -817,7 +835,7 @@ events.prototype.getNextItem = function() { ////// 获得某个物品 ////// events.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) { // core.getItemAnimate(itemId, itemNum, itemX, itemY); - core.playSound('item.ogg'); + core.playSound('item.mp3'); var itemCls = core.material.items[itemId].cls; core.items.getItemEffect(itemId, itemNum); core.removeBlock(itemX, itemY); @@ -867,7 +885,7 @@ events.prototype.openDoor = function (id, x, y, needKey, callback) { } // open - core.playSound("door.ogg"); + core.playSound("door.mp3"); var state = 0; var doorId = id; if (!(doorId.substring(doorId.length-4)=="Door")) { @@ -921,11 +939,11 @@ events.prototype.battle = function (id, x, y, force, callback) { else { if (core.flags.equipment && core.getFlag('sword', 'sword0')!='sword0') { - core.playSound('zone.ogg'); + core.playSound('zone.mp3'); core.drawAnimate('sword', x, y); } else { - core.playSound('attack.ogg'); + core.playSound('attack.mp3'); core.drawAnimate('hand', x, y); } @@ -1012,10 +1030,10 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback } } if (core.status.maps[floorId].canFlyTo && core.status.hero.flyRange.indexOf(floorId)<0) { - if (core.floorIds.indexOf(floorId)>core.floorIds.indexOf(core.status.floorId)) - core.status.hero.flyRange.push(floorId); - else - core.status.hero.flyRange.unshift(floorId); + core.status.hero.flyRange.push(floorId); + core.status.hero.flyRange.sort(function (a, b) { + return core.floorIds.indexOf(a) - core.floorIds.indexOf(b); + }) } window.setTimeout(function () { @@ -1073,29 +1091,27 @@ events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback }) } core.drawMap(floorId, function () { - setTimeout(function() { - if (core.isset(heroLoc.direction)) - core.setHeroLoc('direction', heroLoc.direction); - core.setHeroLoc('x', heroLoc.x); - core.setHeroLoc('y', heroLoc.y); - core.drawHero(); - core.updateStatusBar(); + if (core.isset(heroLoc.direction)) + core.setHeroLoc('direction', heroLoc.direction); + core.setHeroLoc('x', heroLoc.x); + core.setHeroLoc('y', heroLoc.y); + core.clearMap('hero', 0, 0, 416, 416); + core.drawHero(); - var changed = function () { - core.unLockControl(); - core.status.replay.animate=false; - core.events.afterChangeFloor(floorId); - if (core.isset(callback)) callback(); - } - if (displayAnimate) { - core.hide(core.dom.floorMsgGroup, time/4, function () { - changed(); - }); - } - else { + var changed = function () { + core.unLockControl(); + core.status.replay.animate=false; + core.events.afterChangeFloor(floorId); + if (core.isset(callback)) callback(); + } + if (displayAnimate) { + core.hide(core.dom.floorMsgGroup, time/4, function () { changed(); - } - }, 25) + }); + } + else { + changed(); + } }); } core.playSound('floor.mp3'); @@ -1234,6 +1250,16 @@ events.prototype.canUseQuickShop = function(shopId) { return null; } +////// 设置角色行走图 ////// +events.prototype.setHeroIcon = function (name) { + if (core.isset(core.material.images.images[name]) && core.material.images.images[name].width==128) { + core.setFlag("heroIcon", name); + core.material.images.hero.src = core.material.images.images[name].src; + core.material.icons.hero.height = core.material.images.images[name].height/4; + core.drawHero(); + } +} + ////// 检查升级事件 ////// events.prototype.checkLvUp = function () { if (!core.flags.enableLevelUp || core.status.hero.lv>=core.firstData.levelUp.length) return; @@ -1325,7 +1351,7 @@ events.prototype.passNet = function (data) { if (data.event.id=='weakNet') { // 衰网 if (core.hasFlag('weak')) return; core.setFlag('weak', true); - var weakValue = core.status.weakValue; + var weakValue = core.values.weakValue; var weakAtk = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.atk); var weakDef = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.def); core.setFlag('weakAtk', weakAtk); diff --git a/libs/items.js b/libs/items.js index 437c7ff4..2b712d38 100644 --- a/libs/items.js +++ b/libs/items.js @@ -23,7 +23,9 @@ items.prototype.getItemEffect = function(itemId, itemNum) { // 消耗品 if (itemCls === 'items') { var ratio = parseInt(core.floors[core.status.floorId].item_ratio) || 1; + var curr_hp = core.status.hero.hp; if (itemId in this.itemEffect)eval(this.itemEffect[itemId]); + core.status.hero.statistics.hp += core.status.hero.hp - curr_hp; } else { core.addItem(itemId, itemNum); diff --git a/libs/loader.js b/libs/loader.js index 969f4f54..c74f36b0 100644 --- a/libs/loader.js +++ b/libs/loader.js @@ -204,12 +204,10 @@ loader.prototype.loadMusic = function () { core.sounds.forEach(function (t) { if (core.musicStatus.audioContext != null) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', 'project/sounds/'+t, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = function(e) { //下载完成 + + core.http('GET', 'project/sounds/'+t, null, function (data) { try { - core.musicStatus.audioContext.decodeAudioData(this.response, function (buffer) { + core.musicStatus.audioContext.decodeAudioData(data, function (buffer) { core.material.sounds[t] = buffer; }, function (e) { console.log(e); @@ -220,17 +218,10 @@ loader.prototype.loadMusic = function () { console.log(ee); core.material.sounds[t] = null; } - }; - - xhr.ontimeout = function(e) { + }, function () { console.log(e); core.material.sounds[t] = null; - } - xhr.onerror = function(e) { - console.log(e); - core.material.sounds[t] = null; - } - xhr.send(); + }, null, 'arraybuffer'); } else { var music = new Audio(); diff --git a/libs/maps.js b/libs/maps.js index fa80da9f..2ab284db 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -255,20 +255,20 @@ maps.prototype.canMoveHero = function(x,y,direction,floorId) { ////// 能否瞬间移动 ////// maps.prototype.canMoveDirectly = function (destX,destY) { - if (!core.flags.enableMoveDirectly) return false; + if (!core.flags.enableMoveDirectly) return -1; // 中毒状态:不能 - if (core.hasFlag('poison')) return false; + if (core.hasFlag('poison')) return -1; var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); - if (fromX==destX&&fromY==destY) return false; + if (fromX==destX&&fromY==destY) return -1; if (core.getBlock(fromX,fromY)!=null||core.status.checkBlock.damage[13*fromX+fromY]>0) - return false; + return -1; // BFS var visited=[], queue=[]; - visited[13*fromX+fromY]=true; + visited[13*fromX+fromY]=0; queue.push(13*fromX+fromY); var directions = [[-1,0],[1,0],[0,1],[0,-1]]; @@ -278,12 +278,12 @@ maps.prototype.canMoveDirectly = function (destX,destY) { for (var dir in directions) { var nx=nowX+directions[dir][0], ny=nowY+directions[dir][1]; if (nx<0||nx>=13||ny<0||ny>=13||visited[13*nx+ny]||core.getBlock(nx,ny)!=null||core.status.checkBlock.damage[13*nx+ny]>0) continue; - if (nx==destX&&ny==destY) return true; - visited[13*nx+ny]=true; + visited[13*nx+ny]=visited[13*nowX+nowY]+1; + if (nx==destX&&ny==destY) return visited[13*nx+ny]; queue.push(13*nx+ny); } } - return false; + return -1; } maps.prototype.drawBlock = function (block, animate, dx, dy) { @@ -389,8 +389,10 @@ maps.prototype.drawMap = function (mapName, callback) { } } else { drawEvent(); + core.setGlobalAnimate(core.values.animateSpeed); + core.drawHero(); + core.updateStatusBar(); } - core.setGlobalAnimate(core.values.animateSpeed); if (core.isset(callback)) callback(); } @@ -398,7 +400,7 @@ maps.prototype.drawMap = function (mapName, callback) { ////// 绘制Autotile ////// maps.prototype.drawAutotile = function(ctx, mapArr, block, size, left, top){ var indexArrs = [ //16种组合的图块索引数组; // 将autotile分割成48块16*16的小块; 数组索引即对应各个小块 - // +----+----+----+----+----+----+ + // +----+----+----+----+----+----+ [10, 9, 4, 3 ], //0 bin:0000 | 1 | 2 | 3 | 4 | 5 | 6 | [10, 9, 4, 13], //1 bin:0001 +----+----+----+----+----+----+ [10, 9, 18, 3 ], //2 bin:0010 | 7 | 8 | 9 | 10 | 11 | 12 | @@ -537,6 +539,14 @@ maps.prototype.getBlock = function (x, y, floorId, needEnable) { return null; } +////// 获得某个点的blockId ////// +maps.prototype.getBlockId = function (x, y, floorId, needEnable) { + var block = core.getBlock(x, y, floorId, needEnable); + if (block == null) return null; + if (core.isset(block.block.event)) return block.block.event.id; + return null; +} + ////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { time = time || 500; @@ -729,7 +739,7 @@ maps.prototype.removeBlock = function (x, y, floorId) { // 删除Index core.removeBlockById(index, floorId); - core.updateFg(); + core.updateStatusBar(); } ////// 根据block的索引删除该块 ////// @@ -888,11 +898,15 @@ maps.prototype.drawAnimate = function (name, x, y, callback) { }, 50); } -maps.prototype.resetMap = function() { - var floorId = core.status.floorId; +maps.prototype.resetMap = function(floorId) { + var floorId = floorId||core.status.floorId; core.status.maps[floorId] = this.loadFloor(floorId); - this.drawMap(floorId, function() { - core.drawHero(); - core.updateStatusBar(); - }) + if (floorId==core.status.floorId) { + this.drawMap(floorId, function () { + core.drawTip("地图重置成功"); + }) + } + else { + core.drawTip(floorId+"地图重置成功"); + } } \ No newline at end of file diff --git a/libs/ui.js b/libs/ui.js index b539d278..87118e65 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -668,8 +668,8 @@ ui.prototype.drawChoices = function(content, choices) { if (choices.length>0) { if (!core.isset(core.status.event.selection)) core.status.event.selection=0; - if (core.status.event.selection<0) core.status.event.selection=0; - if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1; + while (core.status.event.selection<0) core.status.event.selection+=choices.length; + while (core.status.event.selection>=choices.length) core.status.event.selection-=choices.length; var len = core.canvas.ui.measureText(core.replaceText(choices[core.status.event.selection].text || choices[core.status.event.selection])).width; core.strokeRect('ui', 208-len/2-5, choice_top + 32 * core.status.event.selection - 20, len+10, 28, "#FFD700", 2); } @@ -981,7 +981,7 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) { core.fillText("ui", "S", right_start-8, 208+15, "#FFFFFF", "italic bold 40px Verdana"); var battleInterval = setInterval(function() { - core.playSound("attack.ogg"); + core.playSound("attack.mp3"); if (turn==0) { // 勇士攻击 @@ -1341,7 +1341,11 @@ ui.prototype.drawBookDetail = function (index) { hints.push("该怪物无特殊属性。"); hints.push(""); - hints.push("临界表:"+JSON.stringify(core.enemys.nextCriticals(enemyId,10))) + var criticals = core.enemys.nextCriticals(enemyId, 10).map(function (v) { + return v[0]+":"+v[1]; + }); + while (criticals[0]=='0:0') criticals.shift(); + hints.push("临界表:"+JSON.stringify(criticals)) var content=hints.join("\n"); @@ -1513,8 +1517,19 @@ ui.prototype.drawToolbox = function(index) { if (core.isset(selectId)) { var item=core.material.items[selectId]; core.fillText('ui', item.name, 10, 32, '#FFD700', "bold 20px Verdana") - core.fillText('ui', item.text||"该道具暂无描述。", 10, 62, '#FFFFFF', '17px Verdana'); - core.fillText('ui', '<继续点击该道具即可进行使用>', 10, 89, '#CCCCCC', '14px Verdana'); + + var text = item.text||"该道具暂无描述。"; + var lines = core.splitLines('ui', text, 406, '17px Verdana'); + + core.fillText('ui', lines[0], 10, 62, '#FFFFFF', '17px Verdana'); + + if (lines.length==1) { + core.fillText('ui', '<继续点击该道具即可进行使用>', 10, 89, '#CCCCCC', '14px Verdana'); + } + else { + var leftText = text.substring(lines[0].length); + core.fillText('ui', leftText, 10, 89, '#FFFFFF', '17px Verdana'); + } } core.canvas.ui.textAlign = 'right'; @@ -1734,6 +1749,167 @@ ui.prototype.drawKeyBoard = function () { core.fillText("ui", "返回游戏", 416-80, offset-3, '#FFFFFF', 'bold 15px Verdana'); } +////// 绘制“数据统计”界面 ////// +ui.prototype.drawStatistics = function () { + + // 数据统计要统计如下方面: + // 1. 当前全塔剩余下的怪物数量,总金币数,总经验数,总加点数 + // 2. 当前全塔剩余的黄蓝红铁门数量,和对应的钥匙数量 + // 3. 当前全塔剩余的三种宝石数量,血瓶数量,装备数量;总共增加的攻防生命值 + // 4. 当前层的上述信息 + // 5. 当前已走的步数;瞬间移动的步数,瞬间移动的次数(和少走的步数);游戏时长 + // 6. 当前已恢复的生命值;当前总伤害、战斗伤害、阻激夹域血网伤害、中毒伤害。 + + + var total = { + 'monster': { + 'count': 0, 'money': 0, 'experience': 0, 'point': 0, + }, + 'count': { + 'yellowDoor': 0, 'blueDoor': 0, 'redDoor': 0, 'steelDoor': 0, + 'yellowKey': 0, 'blueKey': 0, 'redKey': 0, 'steelKey': 0, + 'redJewel': 0, 'blueJewel': 0, 'greenJewel': 0, 'yellowJewel': 0, + 'redPotion': 0, 'bluePotion': 0, 'greenPotion': 0, 'yellowPotion': 0, 'superPotion': 0, + 'pickaxe': 0, 'bomb': 0, 'centerFly': 0, + 'poisonWine': 0, 'weakWine': 0, 'curseWine': 0, 'superWine': 0, + 'sword1': 0, 'sword2': 0, 'sword3': 0, 'sword4': 0, 'sword5': 0, + 'shield1': 0, 'shield2': 0, 'shield3': 0, 'shield4': 0, 'shield5': 0, + }, + 'add': { + 'hp': 0, 'atk': 0, 'def': 0, 'mdef': 0 + } + }; + var current = core.clone(total); + + core.floorIds.forEach(function (floorId) { + var floor=core.floors[floorId]; + var blocks=core.status.maps[floorId].blocks; + // 隐藏层不给看 + if (floor.cannotViewMap && floorId!=core.status.floorId) return; + + blocks.forEach(function (block) { + if (!core.isset(block.event) || (core.isset(block.enable) && !block.enable)) + return; + var event = block.event; + if (event.cls.indexOf("enemy")==0) { + var enemyId = event.id, enemy = core.material.enemys[enemyId]; + total.monster.money+=enemy.money||0; + total.monster.experience+=enemy.experience||0; + total.monster.point+=enemy.point||0; + total.monster.count++; + if (floorId==core.status.floorId) { + current.monster.money+=enemy.money||0; + current.monster.experience+=enemy.experience||0; + current.monster.point+=enemy.point||0; + current.monster.count++; + } + } + else { + var id = event.id; + + var temp = core.clone(core.status.hero); + + if (core.isset(total.count[id])) { + var hp=0, atk=0, def=0, mdef=0; + + if (core.isset(core.material.items[id]) && core.material.items[id].cls=='items' && id!='superPotion') { + var ratio = floor.item_ratio||1; + if (core.isset(core.items.itemEffect[id])) { + eval(core.items.itemEffect[id]); + } + hp = core.status.hero.hp - temp.hp; + atk = core.status.hero.atk - temp.atk; + def = core.status.hero.def - temp.def; + mdef = core.status.hero.mdef - temp.mdef; + } + else { + if (id.indexOf('sword')==0 && core.isset(core.values[id])) { + var x = core.values[id]; + if (typeof x == 'number') x = {'atk': x}; + atk += x.atk||0; + def += x.def||0; + mdef += x.mdef||0; + } + if (id.indexOf('shield')==0 && core.isset(core.values[id])) { + var x = core.values[id]; + if (typeof x == 'number') x = {'def': x}; + atk += x.atk||0; + def += x.def||0; + mdef += x.mdef||0; + } + } + core.status.hero = core.clone(temp); + total.count[id]++; + total.add.hp+=hp; + total.add.atk+=atk; + total.add.def+=def; + total.add.mdef+=mdef; + if (floorId==core.status.floorId) { + current.count[id]++; + current.add.hp+=hp; + current.add.atk+=atk; + current.add.def+=def; + current.add.mdef+=mdef; + } + } + } + }) + }) + + var getText = function (type, data) { + var text = type+"地图中:\n"; + text += "共有怪物"+data.monster.count+"个"; + if (core.flags.enableMoney) text+=",总金币数"+data.monster.money; + if (core.flags.enableExperience) text+=",总经验数"+data.monster.experience; + if (core.flags.enableAddPoint) text+=",总加点数"+data.monster.point; + text+="。\n\n"; + Object.keys(data.count).forEach(function (key) { + var value=data.count[key]; + if (value>0) { + var name=""; + if (key=='yellowDoor') name="黄门"; + else if (key=='blueDoor') name="蓝门"; + else if (key=='redDoor') name="红门"; + else if (key=='steelDoor') name="铁门"; + else name=core.material.items[key].name; + if (core.isset(name)) { + text+=name+value+"个;"; + } + } + }) + text+="\n\n"; + text+="共加生命值"+core.formatBigNumber(data.add.hp)+"点,攻击" + +core.formatBigNumber(data.add.atk)+"点,防御" + +core.formatBigNumber(data.add.def)+"点,魔防" + +core.formatBigNumber(data.add.mdef)+"点。"; + return text; + } + + var formatTime = function (time) { + return core.setTwoDigits(parseInt(time/3600000)) + +":"+core.setTwoDigits(parseInt(time/60000)%60) + +":"+core.setTwoDigits(parseInt(time/1000)%60); + } + + var statistics = core.status.hero.statistics; + core.drawText([ + getText("全塔", total), + getText("当前", current), + "当前总步数:"+core.status.hero.steps+",游戏时长:"+formatTime(statistics.totalTime) + +"。\n瞬间移动次数:"+statistics.moveDirectly+",共计少走"+statistics.ignoreSteps+"步。" + +"\n\n总计通过血瓶恢复生命值为"+core.formatBigNumber(statistics.hp)+"点。\n\n" + +"总计受到的伤害为"+core.formatBigNumber(statistics.battleDamage+statistics.poisonDamage+statistics.extraDamage) + +",其中战斗伤害"+core.formatBigNumber(statistics.battleDamage)+"点" + +(core.flags.enableDebuff?(",中毒伤害"+core.formatBigNumber(statistics.poisonDamage)+"点"):"") + +",领域/夹击/阻击/血网伤害"+core.formatBigNumber(statistics.extraDamage)+"点。", + "\t[说明]1. 地图数据统计的效果仅模拟当前立刻获得该道具的效果。\n2. 不会计算“不可被浏览地图”的隐藏层的数据。\n" + + "3. 不会计算任何通过事件得到的道具(显示事件、改变图块、或直接增加道具等)。\n"+ + "4. 在自定义道具(例如其他宝石)后,需在ui.js的drawStatistics中注册,不然不会进行统计。\n"+ + "5. 所有统计信息仅供参考,如有错误,概不负责。" + ]) + +} + ////// 绘制“关于”界面 ////// ui.prototype.drawAbout = function () { return this.uidata.drawAbout(); diff --git a/libs/utils.js b/libs/utils.js index 8ad8e03b..b650904b 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -590,11 +590,13 @@ utils.prototype.hide = function (obj, speed, callback) { }, speed); } -utils.prototype.http = function (type, url, formData, success, error, mimeType) { +utils.prototype.http = function (type, url, formData, success, error, mimeType, responseType) { var xhr = new XMLHttpRequest(); xhr.open(type, url, true); if (core.isset(mimeType)) xhr.overrideMimeType(mimeType); + if (core.isset(responseType)) + xhr.responseType = responseType; xhr.onload = function(e) { if (xhr.status==200) { if (core.isset(success)) { diff --git a/main.js b/main.js index 3dddfd0d..6a89dd6d 100644 --- a/main.js +++ b/main.js @@ -57,6 +57,7 @@ function main() { 'moneyCol': document.getElementById('moneyCol'), 'expCol': document.getElementById('expCol'), 'upCol': document.getElementById('upCol'), + 'keyCol': document.getElementById('keyCol'), 'debuffCol': document.getElementById('debuffCol'), 'hard': document.getElementById('hard'), }; @@ -457,6 +458,15 @@ main.statusBar.image.settings.onclick = function () { main.dom.playGame.onclick = function () { main.dom.startButtons.style.display='none'; + if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly + && main.core.musicStatus.bgmStatus) { + if (main.core.musicStatus.playingBgm==null + || core.material.bgms[main.core.musicStatus.playingBgm].paused) { + main.core.musicStatus.playingBgm=null; + main.core.playBgm(main.core.bgms[0]); + } + } + if (main.core.isset(main.core.flags.startDirectly) && main.core.flags.startDirectly) { core.events.startGame(""); } @@ -467,12 +477,31 @@ main.dom.playGame.onclick = function () { ////// 点击“载入游戏”时 ////// main.dom.loadGame.onclick = function() { + + if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly + && main.core.musicStatus.bgmStatus) { + if (main.core.musicStatus.playingBgm==null + || core.material.bgms[main.core.musicStatus.playingBgm].paused) { + main.core.musicStatus.playingBgm=null; + main.core.playBgm(main.core.bgms[0]); + } + } + main.core.load(); } ////// 点击“录像回放”时 ////// main.dom.replayGame.onclick = function () { + if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly + && main.core.musicStatus.bgmStatus) { + if (main.core.musicStatus.playingBgm==null + || core.material.bgms[main.core.musicStatus.playingBgm].paused) { + main.core.musicStatus.playingBgm=null; + main.core.playBgm(main.core.bgms[0]); + } + } + core.readFile(function (obj) { if (obj.name!=core.firstData.name) { alert("存档和游戏不一致!"); @@ -490,9 +519,9 @@ main.dom.replayGame.onclick = function () { core.dom.startPanel.style.display = 'none'; core.resetStatus(core.firstData.hero, obj.hard, core.firstData.floorId, null, core.initStatus.maps); - core.events.setInitData(obj.hard); 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); diff --git a/project/data.js b/project/data.js index 257d7b4f..e03c802b 100644 --- a/project/data.js +++ b/project/data.js @@ -14,7 +14,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = 'bgm.mp3' ], "sounds" : [ - 'floor.mp3', 'attack.ogg', 'door.ogg', 'item.ogg', 'zone.ogg' + 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3' ], "startBackground" : "bg.jpg", "startLogoStyle" : "color: black", @@ -49,11 +49,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = }, "flyRange": [], "loc": {"direction": "up", "x": 6, "y": 10}, - "flags": { - "poison": false, - "weak": false, - "curse": false, - }, + "flags": {}, "steps": 0, }, "startText": [ @@ -144,6 +140,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableMoney": true, "enableExperience": false, "enableLevelUp": false, + "enableKeys": true, "enableDebuff": false, "flyNearStair": true, "pickaxeFourDirections": false, diff --git a/project/floors/sample1.js b/project/floors/sample1.js index 54098420..87bbb9b2 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -56,7 +56,7 @@ main.floors.sample1 = "\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下,我就与你一对一的决斗。\n现在你必须接受我的安排。", {"type": "show", "loc": [[1,6],[0,7],[1,8],[2,7]], "time": 500}, // 显示四个白衣武士,每个动画效果500ms "\t[hero]什么?", - {"type": "playSound", "name": "attack.ogg"}, // 播放战斗音频 + {"type": "playSound", "name": "attack.mp3"}, // 播放战斗音频 {"type": "setValue", "name": "status:atk", "value": "status:atk/10"}, // 勇士的攻防变成原来的十分之一 {"type": "setValue", "name": "status:def", "value": "status:def/10"}, {"type": "hide", "loc": [[1,6],[0,7],[2,7],[1,8]]}, // 直接隐藏四个白衣武士,没有动画效果 diff --git a/project/floors/sample2.js b/project/floors/sample2.js index 666e5f64..5db5310c 100644 --- a/project/floors/sample2.js +++ b/project/floors/sample2.js @@ -36,22 +36,22 @@ main.floors.sample2 = "6,11": {"enable": false}, // 下楼梯口的机关门,初始处于关闭状态 "6,10": [ // 进入陷阱后关门 - {"type": "playSound", "name": "door.ogg"}, + {"type": "playSound", "name": "door.mp3"}, {"type": "show", "loc": [6,11]}, // 显示机关门 {"type": "hide"}, // 隐藏该事件 {"type": "trigger", "loc": [6,7]}, // 直接引发"6,7"处的事件,即下面的杰克 // 请再次注意"trigger"会立刻结束当前事件,因此"type":"hide"需要在trigger前调用 ], "6,7": [ // 杰克事件 - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]杰克,你究竟是什么人?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[杰克,thief]……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]我们……是朋友对吧?\n是朋友就应该相互信任对吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[杰克,thief]……事到如今也没有什么好隐瞒的了。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[杰克,thief]没错,我就是这一切的背后主谋。", {"type": "move", "steps": [ // 移动到黑暗大法师的位置;使用move会自动调用hide进行隐藏,无需再手动调用 {"direction": "up", "value": 3} @@ -59,105 +59,105 @@ main.floors.sample2 = {"type": "show", "loc": [6,4], "time": 1000}, // 显示黑暗大法师 {"type": "sleep", "time": 500}, // 等待500毫秒 // 下面是黑暗大法师的事件 - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我的真名为——黑暗大法师,第四区域的头目。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]呵呵,不知道为什么,我竟然对事情走到现在这一步毫不感觉意外。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]以杰克的名义利用了你这么久,真是抱歉啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]真正的杰克现在在哪里?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]盗贼杰克这个人类从未存在过,他只是我用来接近你的一副皮囊而已。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……这样啊,呵呵。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]为什么你看上去丝毫不生气?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]多亏了鬼帝,我现在的脾气好得连我自己都害怕。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]说起来我还得好好感谢你呢,如果没有杰克……你的帮助,我早就死在第一区域了。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]不论你的目的如何,你的所作所为都是对我有利的。不是吗?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]能够如此淡定的面对背叛,看来跟五年前相比,你确实成长了很多啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]五年前?……黑暗大法师,在这之前,我们好像素未谋面吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]五年前那场屠城你应该这一生都不会忘记吧。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]很不巧,那场屠城的主谋,也是我。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]这么说,击中我双亲的那道紫色闪电,也就是你释放的吧……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]你的双亲?这种事情我怎么可能会记得?\n你难道在踩死蚂蚁的时候还会一只只记下他们的样子吗?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]老 子 要 你 的 命", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]你应该对我心怀感激才对,如果不是那时的我看出了你隐藏的稀有勇者体质,你绝对不可能活到今天。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]在暗中动手脚让你通过勇者选拔的人也是我,我一直一直在暗中引导你走到今天这一步。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]是我救赎了一无是处的你。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]为什么只有我一个人活了下来!!!!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]为什么偏偏是我!!!!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我刚才不是说过了吗?因为我看出了你有稀有勇者体质啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]你刚刚跟鬼帝交过手,应该已经很清楚这稀有勇者体质意味着什么了吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……就因为我有这种体质,就不得不背负如此残酷的宿命吗?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]愚蠢!这意味着只要我对你加以引导跟培养,你就能成为这世间实力最强的存在!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……所以,你究竟想利用我干什么?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我利用你干的事情,你不是已经完成了吗?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……你说什么?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]不知不觉间,你已经在我的指引下跟鬼帝正面交手并且杀掉了他啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]就连我跟鬼帝的对决……也是被你安排好了的?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]你们两个一个是人类勇者,一个是魔物勇者,迟早会有交手的一天。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我只不过是操纵了一系列的连锁事件让这一天提早了数十年到来而已。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……你这样做对谁有好处?他可是你们魔物世界的救世主啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]一个惧怕征战,爱好和平的懦夫,也配叫救世主?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]获得了力量,却只会被动挨打而不主动向人类世界出击,龟缩在第二区域惶惶度日,他根本就不配拥有稀有勇者体质。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]为了不让这种人霸占着积累多年的庞大灵魂能量无作为,我设计让你杀掉了他。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]你没有辜负我的期待,成功战胜了那个废物,现在你体内累积的灵魂能量……也就是魔力,已经达到了能跟魔王匹敌的地步。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……是吗?现在的我能与魔王匹敌?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]不止如此,你现在的力量之强就算是统治世界也是绰绰有余!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]怎么样?要不要加入我的麾下,跟随我去征战人类世界?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]能与魔王匹敌的话,也就是说。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]我 现 在 对 付 你 这 种 杂 碎 也 绰 绰 有 余 吧 ?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]……什么?!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]等一下!别冲动!你先等我把这利害关系理一理——", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]你给老子闭嘴。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]老子什么都不想听。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]老子现在想做的事情只有一件——", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]剁掉你的头,把它放回我双亲的墓前。", {"type": "update"} // 本事件剧情结束,更新地图显伤 ], @@ -175,7 +175,7 @@ main.floors.sample2 = "data": [ "\t[blackMagician]听不进去人话的蠢货,就要用疼痛来管教!", {"type": "changePos", "direction": "up"}, - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]出来吧!禁忌——紫电凶杀阵!", {"type": "show", "loc": [[4,3],[4,6],[8,6],[8,3]], "time": 500}, // 依次显示四个角的法师 {"type": "sleep", "time": 500}, @@ -195,13 +195,13 @@ main.floors.sample2 = {"type": "animate", "name": "yongchang", "loc": [8,6]}, {"type": "animate", "name": "yongchang", "loc": [8,3]}, {"type": "sleep", "time": 200}, - {"type": "playSound", "name": "attack.ogg"}, // 播放攻击音效 + {"type": "playSound", "name": "attack.mp3"}, // 播放攻击音效 {"type": "animate", "name": "thunder", "loc": "hero"}, {"type": "sleep", "time": 200}, "\t[hero]唔……!!(吐血)", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我的魔力可是充足的很啊!我会一直折磨到你屈服于我为止!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]人类!好好感受吧!当初你们施加于我的痛苦!如今我要百倍奉还!", {"type": "show", "loc": [6,6], "time": 1000}, // 显示妖精 {"type": "sleep", "time": 700}, @@ -232,45 +232,45 @@ main.floors.sample2 = "6,6": { // 妖精 "enable":false, // 初始时禁用状态 "data": [ // 妖精事件 - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]…妖精…小姐……是你吗?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]不要绝望,也不要悲伤。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]你从来都不是独自一人在前进。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]咱一直,一直都在注视着你。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]耍小聪明的你、笨笨的你呆呆的你、胆小的你、勇敢的你帅气的你……全部全部都是你。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]所以放心吧,无论发生什么,咱都会陪伴在你身边的。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]因为你要是离开我的话,立刻就会死掉吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]…妖精…小姐……其实一直以来,我都非常感激你……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]笨蛋!都这种时候了就不要作出像是临终遗言的发言了啊!!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]喂!那边穿衣品味差到极点的黑暗大法师,别左顾右盼说的就是你!你应该知道咱的身份吧?\n还不速速退下!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]可恶…多管闲事的妖精族…明明只要再让他承受一点疼痛来瓦解他的意志力,我的计划就成功了!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]哼哼哼~抱歉哦,这个笨蛋的意志力可不像你想象的那么薄弱哦!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]不甘心!我不甘心!妖精公主又如何!\n只要是阻挡我的,不管是谁我都要铲除!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]终于露出狐狸尾巴了,其实咱早就看出你有谋反的念头。你的计划就是拉拢这家伙入伙然后推翻魔王对魔塔的统治对吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]呵呵呵……那个昏庸的魔王,掌握着那么庞大的魔物军队却只知道固守魔塔,而不主动侵略人类世界扩张领土!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]我实在是看不过眼,所以我才决定把这个具备稀有勇者体质的家伙培养成新一任魔王!\n来让这个世界的势力重新洗牌!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]你觉得一个满脑子想着回家种田的废柴勇者会成为改变世界的魔王?你晃晃脑袋试试,是不是能听到大海的声音?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]恼人至极的妖精族!呵呵呵……我干脆一不做二不休,连你也一块收拾了吧!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]别小瞧咱!咱好歹也是妖精族里实力数一数二的存在!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]只会耍嘴皮子的恼人苍蝇!我倒要看看一块焦炭会不会说话!\n——招雷弹!!", /* {"type": "hide", "loc": [4,3], "time": 150}, // 由于没有动画效果,暂时使用“闪一下”表示 @@ -286,7 +286,7 @@ main.floors.sample2 = {"type": "animate", "name": "yongchang", "loc": [4,6]}, {"type": "animate", "name": "yongchang", "loc": [8,6]}, {"type": "animate", "name": "yongchang", "loc": [8,3]}, - {"type": "playSound", "name": "attack.ogg"}, // 播放攻击音效 + {"type": "playSound", "name": "attack.mp3"}, // 播放攻击音效 /* {"type": "hide", "loc": [6,6], "time": 150}, // 妖精也闪一下表示收到了伤害 {"type": "show", "loc": [6,6], "time": 150}, // 妖精也闪一下表示收到了伤害 @@ -294,38 +294,38 @@ main.floors.sample2 = {"type": "animate", "name": "thunder", "loc": [6,6]}, {"type": "sleep", "time": 500}, // 等待500毫秒 "\t[小妖精,fairy]切,这点伤痛跟他刚才经历的身心地狱相比根本就不算什么。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]哼!翅膀都被烧焦了还要嘴硬?你难不成真以为我不会对你动真格?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……你这混蛋!给我离她远点!!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]!…你现在受了很严重的致命伤,乱动什么?\n乖。别怕,这里有咱顶着!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]对了,咱再问你一遍,你是很珍惜自己性命的对吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]!…等等…妖精小姐,你不会是……?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]喂,黑暗大法师,你作为魔塔里最博学多识的蠢货,应该对咱妖精族的特殊能力再清楚不过吧?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]什么?!难不成你是想!!不可能……\n就为了一个渺小的人类,不可理喻!!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]哼哼哼!你害怕的表情可真美味!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]不过比起这个,咱更期待你吃到“妖精自灭冲击”之后的死状哦!~", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[blackMagician]不!!不应该是这样的!我完美的计划竟然会被一只小小的妖精破坏!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]不要!……千万不要!……为了我这种人……唔!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]笨蛋,动都动不了了就不要强撑着站起来了啊。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]真是的,都到最后一刻了,你这家伙好歹也让咱省点心吧。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[小妖精,fairy]那么,再见了……我的勇者大人。", {"type": "move", "time": 700, "steps": [ // 向上移动三个,撞上黑暗大大法师;本事件的hide会自动被调用 {"direction": "up", "value": 3} ]}, - {"type": "playSound", "name": "attack.ogg"}, // 播放攻击音效 + {"type": "playSound", "name": "attack.mp3"}, // 播放攻击音效 {"type": "sleep", "time": 200}, "\t[blackMagician]不可能!!!!!", {"type": "hide", "loc": [6,3]}, // 法师消失 @@ -336,24 +336,24 @@ main.floors.sample2 = {"type": "changeFloor", "floorId": "sample2", "loc": [6,6], "direction": "up", "time": 1000}, // 更换勇士地点,合计1秒 {"type": "show", "loc": [6,5]}, // 显示黄宝石 {"type": "sleep", "time": 200}, // 等待200毫秒 - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, {"type": "sleep", "time": 200}, // 等待200毫秒 "\t[hero]…妖精…小姐……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……妖精小姐!", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]是梦吗?……不对,为什么我在流泪?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]这颗漂亮的宝石是……?", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]我全都想起来了……妖精小姐为了我……\n牺牲了自己的性命。", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]在这颗宝石上,我能感受到你的温度……\n熟悉而又令人安心,这就是你最后留给我的东西吗……", - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]好温暖……", {"type": "setValue", "name": "item:yellowJewel", "value": "1"}, // 获得1个黄宝石 {"type": "hide", "loc": [6,5]}, // 隐藏黄宝石 - {"type": "playSound", "name": "item.ogg"}, + {"type": "playSound", "name": "item.mp3"}, "\t[hero]……", {"type": "openDoor", "loc": [6,2]}, // 开门 {"type": "openDoor", "loc": [6,11]} diff --git a/project/functions.js b/project/functions.js index 975d9aab..f8004acd 100644 --- a/project/functions.js +++ b/project/functions.js @@ -119,6 +119,10 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 扣减体力值 core.status.hero.hp -= damage; + + // 记录 + core.status.hero.statistics.battleDamage += damage; + if (core.status.hero.hp<=0) { core.status.hero.hp=0; core.updateStatusBar(); @@ -155,7 +159,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 衰弱 if (core.enemys.hasSpecial(special, 13) && !core.hasFlag('weak')) { core.setFlag('weak', true); - var weakValue = core.status.weakValue; + var weakValue = core.values.weakValue; var weakAtk = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.atk); var weakDef = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.def); core.setFlag('weakAtk', weakAtk); @@ -346,7 +350,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = console.log("插件函数执行测试"); } - var _useEquipment = function (itemId, name, type) { + var _useEquipment = function (itemId, name, type) { // 具体的装备使用效果 if (itemId.indexOf(name)==0) { var now=core.getFlag(name, name+"0"); @@ -356,7 +360,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = else { core.status.hero.atk -= core.values[now].atk || 0; core.status.hero.def -= core.values[now].def || 0; - core.status.hero.mdef -= core.values[now].mdef || 0; + core.status.hero.mdef -= core.values[now].mdef || 0; } if (typeof core.values[itemId] == 'number') { diff --git a/project/icons.js b/project/icons.js index 1b6208fe..b7461654 100644 --- a/project/icons.js +++ b/project/icons.js @@ -27,8 +27,8 @@ icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = 'white': 17, 'ground6': 18, 'soil': 19, - 'star': 20, - 'lava': 21, + 'ground7': 20, + 'ground8': 21, 'ice': 22, 'downFloor': 23, 'upFloor': 24, @@ -70,9 +70,9 @@ icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = 'lavaDoor': 13, 'starDoor': 14, 'starPortal': 15, - //'exclamation': 16, + 'fire': 16, 'portal': 17, - //'switch': 18, + 'switch': 18, 'lavaNet': 19, 'poisonNet': 20, 'weakNet': 21, diff --git a/project/images/animates.png b/project/images/animates.png index 0bfad0df..f6245285 100644 Binary files a/project/images/animates.png and b/project/images/animates.png differ diff --git a/project/images/terrains.png b/project/images/terrains.png index 246de0cb..55239f66 100644 Binary files a/project/images/terrains.png and b/project/images/terrains.png differ diff --git a/project/items.js b/project/items.js index 475f59cb..94a57ff9 100644 --- a/project/items.js +++ b/project/items.js @@ -278,13 +278,13 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "useItemEffect": { "book": "core.ui.drawBook(0);", "fly": "core.ui.drawFly(core.status.hero.flyRange.indexOf(core.status.floorId));", - "earthquake": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "pickaxe": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "icePickaxe": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero(), 'stop');\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "snow": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "bigKey": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", - "bomb": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.events.afterUseBomb();\n});", - "hammer": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawHero();\n core.updateFg();\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.events.afterUseBomb();\n});", + "earthquake": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "pickaxe": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "icePickaxe": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "snow": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "bigKey": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n});", + "bomb": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.events.afterUseBomb();\n});", + "hammer": "core.removeBlockByIds(core.status.floorId, core.status.event.data);\ncore.drawMap(core.status.floorId, function () {\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.events.afterUseBomb();\n});", "centerFly": "core.clearMap('hero', 0, 0, 416, 416);\ncore.setHeroLoc('x', core.status.event.data.x);\ncore.setHeroLoc('y', core.status.event.data.y);\ncore.drawHero();\ncore.drawTip(core.material.items[itemId].name + '使用成功');", "upFly": "var loc = {'direction': core.status.hero.loc.direction, 'x': core.status.event.data.x, 'y': core.status.event.data.y};\ncore.changeFloor(core.status.event.data.id, null, loc, null, function (){\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.replay();\n});", "downFly": "var loc = {'direction': core.status.hero.loc.direction, 'x': core.status.event.data.x, 'y': core.status.event.data.y};\ncore.changeFloor(core.status.event.data.id, null, loc, null, function (){\n core.drawTip(core.material.items[itemId].name + '使用成功');\n core.replay();\n});", diff --git a/project/sounds/attack.mp3 b/project/sounds/attack.mp3 new file mode 100644 index 00000000..259b759e Binary files /dev/null and b/project/sounds/attack.mp3 differ diff --git a/project/sounds/attack.ogg b/project/sounds/attack.ogg deleted file mode 100644 index ec692100..00000000 Binary files a/project/sounds/attack.ogg and /dev/null differ diff --git a/project/sounds/door.mp3 b/project/sounds/door.mp3 new file mode 100644 index 00000000..ea6706d2 Binary files /dev/null and b/project/sounds/door.mp3 differ diff --git a/project/sounds/door.ogg b/project/sounds/door.ogg deleted file mode 100644 index 65f75ce2..00000000 Binary files a/project/sounds/door.ogg and /dev/null differ diff --git a/project/sounds/item.mp3 b/project/sounds/item.mp3 new file mode 100644 index 00000000..5d821780 Binary files /dev/null and b/project/sounds/item.mp3 differ diff --git a/project/sounds/item.ogg b/project/sounds/item.ogg deleted file mode 100644 index 469399d5..00000000 Binary files a/project/sounds/item.ogg and /dev/null differ diff --git a/project/sounds/zone.mp3 b/project/sounds/zone.mp3 new file mode 100644 index 00000000..eccf53c8 Binary files /dev/null and b/project/sounds/zone.mp3 differ diff --git a/project/sounds/zone.ogg b/project/sounds/zone.ogg deleted file mode 100644 index aef5fcbc..00000000 Binary files a/project/sounds/zone.ogg and /dev/null differ diff --git a/常用工具/便捷PS工具.exe b/常用工具/便捷PS工具.exe index 78a9c25e..3f5deca0 100644 Binary files a/常用工具/便捷PS工具.exe and b/常用工具/便捷PS工具.exe differ diff --git a/更新说明.txt b/更新说明.txt index d9e15cda..55da88f0 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,12 +1,19 @@ HTML5魔塔样板V2.2.1 -衰弱减少攻防的比例 √ -while循环事件 √ -地图数据统计 +地图编辑器可以右键复制或移动图块 √ +事件:while循环处理 √ +事件:等待用户操作并获得按键或点击信息 √ +衰弱可以减少攻防的比例 √ +地图数据统计 √ +支持 status:x 获得当前坐标 √ +core.debug()改成调试模式,可以Ctrl穿墙 √ 最大存档个数提到main处理 √ 新建地图可以保留楼层属性 √ -自定义装备的教程(个性化 - 自定义装备) √ -部分Bug修复 √ +地图编辑器可用PageUp和PageDown切换楼层 √ +道具描述过长时可以自动换行 √ +除Autotile外均可自动注册 √ +重写大部分教程,新增大量拓展描述 √ +大量细节进行优化,所有已知的bug进行了修复 √ -----------------------------------------------------------------------