diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 18cd0c30..892bd536 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -63,18 +63,19 @@ return code; */; shopsub - : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString BGNL? Newline '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND + : '商店 id' IdString '标题' EvalString '图标' IdString BGNL? Newline '快捷商店栏中名称' EvalString '共用times' Bool BGNL? Newline '使用' ShopUse_List '消耗' EvalString BGNL? Newline '显示文字' EvalString BGNL? Newline shopChoices+ BEND /* shopsub tooltip : 全局商店,消耗填-1表示每个选项的消耗不同,正数表示消耗数值 helpUrl : https://ckcz123.github.io/mota-js/#/event?id=%e5%85%a8%e5%b1%80%e5%95%86%e5%ba%97 -default : ["shop1","贪婪之神","blueShop","1F金币商店",null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] +default : ["shop1","贪婪之神","blueShop","1F金币商店",false,null,"20+10*times*(times+1)","勇敢的武士啊, 给我${need}金币就可以:"] var code = { 'id': IdString_0, 'name': EvalString_0, 'icon': IdString_1, 'textInList': EvalString_1, + 'commonTimes': Bool_0, 'use': ShopUse_List_0, 'need': EvalString_2, 'text': EvalString_3, @@ -1638,7 +1639,7 @@ ActionParser.prototype.parse = function (obj,type) { choice.text,choice.need||'',text_effect,text_choices]); } return MotaActionBlocks['shopsub'].xmlText([ - obj.id,obj.name,obj.icon,obj.textInList,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next + obj.id,obj.name,obj.icon,obj.textInList,obj.commonTimes,obj.use,obj.need,parser.EvalString(obj.text),text_choices,next ]); } var next=null; diff --git a/_server/data.comment.js b/_server/data.comment.js index 7afe0f36..43080322 100644 --- a/_server/data.comment.js +++ b/_server/data.comment.js @@ -153,6 +153,11 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_type": "textarea", "_data": "初始生命值" }, + "mana": { + "_leaf": true, + "_type": "textarea", + "_data": "初始魔力值,只在enableMana开启时才有效" + }, "atk": { "_leaf": true, "_type": "textarea", @@ -389,6 +394,12 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否是否启用生命上限" }, + "enableMana": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否开启魔力值" + }, "enableMDef": { "_leaf": true, "_type": "checkbox", @@ -431,6 +442,12 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = "_bool": "bool", "_data": "是否涉及毒衰咒;如果此项为false则不会在状态栏中显示毒衰咒的debuff" }, + "enableSkill": { + "_leaf": true, + "_type": "checkbox", + "_bool": "bool", + "_data": "是否启用技能栏" + }, "flyNearStair": { "_leaf": true, "_type": "checkbox", diff --git a/_server/functions.comment.js b/_server/functions.comment.js index 92080dd7..9bd96f61 100644 --- a/_server/functions.comment.js +++ b/_server/functions.comment.js @@ -129,6 +129,18 @@ functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = } } }, + "actions": { + "_leaf": false, + "_type": "object", + "_data": { + "onKeyUp": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "按键处理;可以在这里自定义快捷键,详见文档-个性化-自定义快捷键" + } + } + }, "control": { "_leaf": false, "_type": "object", diff --git a/docs/element.md b/docs/element.md index bd743bef..75633a11 100644 --- a/docs/element.md +++ b/docs/element.md @@ -369,6 +369,20 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 如果录像出现问题,请加群539113091找小艾反馈Bug。 +## 绘图模式 + +从V2.5开始,样板提供了绘图模式,可以让玩家在画布上任意进行绘制,标记等。 + +使用M键,或在菜单栏中可以进入绘图模式。 + +**绘图的内容会自动保存,且以页面为生命周期,和存读档无关,返回标题并重新开始游戏后绘制的内容仍有效,但刷新页面就会消失。** + +你可以将绘制内容保存到文件,也可以从文件读取保存的绘制内容。 + +绘图模式下,状态栏的图标也会相应改变,铅笔为绘制模式,橡皮为擦除模式,存读档为保存和读取绘图文件,退出为返回默认值。 + +在浏览地图页面中也可以按楼传按钮或M键来开启/关闭该层的绘图显示。 + ## 操作说明 本塔主要支持鼠标(触摸屏)操作和键盘操作。 @@ -397,10 +411,15 @@ HTML5魔塔一大亮点就是存在录像系统,可以很方便进行录像回 - **[B]** 打开数据统计 - **[H]** 打开帮助页面 - **[R]** 回放录像 +- **[E]** 显示光标 - **[SPACE]** 轻按(仅在轻按开关打开时有效) +- **[M]** 绘图模式 +- **[PgUp/PgDn]** 浏览地图 - **[1]** 快捷使用破墙镐 - **[2]** 快捷使用炸弹/圣锤 - **[3]** 快捷使用中心对称飞行器 +- **[4]** 快捷使用其他道具 +- **[Alt+0~9]** 快捷换装 以上快捷键也能在游戏菜单中的操作说明中看到。 diff --git a/docs/personalization.md b/docs/personalization.md index 30260cd3..2a6a15b0 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -496,34 +496,28 @@ this.useEquipment = function (itemId) { // 使用装备 如果需要绑定某个快捷键为处理一段事件,也是可行的。 -要修改按键,我们可以在`actions.js`的`keyUp`进行处理: +要修改按键,我们可以在脚本编辑的`onKeyUp`进行处理: -比如,我们设置一个快捷键进行绑定,比如`W`,其keycode是87。(有关每个键的keycode搜一下就能得到) +比如,我们设置一个快捷键进行绑定,比如`Y`,其keycode是89。(有关每个键的keycode搜一下就能得到) -然后在`actions.js`的`keyUp`函数的`switch`中进行处理。 +然后在脚本编辑的`onKeyUp`函数的`switch`中进行处理。 ``` js -case 87: // W - if (core.status.heroStop) { - // ... 在这里写你要执行脚本 - // 请使用同步脚本,请勿执行任何异步代码,否则可能导致游戏过程或录像出现问题。 - core.insertAction([...]) // 例如,插入一段自定义事件并执行。 - - // core.status.route.push("key:"+keyCode); // 录像的支持,这句话加不加最好仔细进行测试 +case 89: // 使用该按键的keyCode,比如Y键就是89 + // 还可以再判定altKey是否被按下,即 if (altKey) { ... + + // ... 在这里写你要执行脚本 + // **强烈建议所有新增的自定义快捷键均能给个对应的道具可点击,以方便手机端的行为** + if (core.hasItem('...')) { + core.useItem('...'); } + break; ``` +强烈建议所有新增的自定义快捷键均给个对应的永久道具可点击,以方便手机端的行为。 -在勇士处于停止的条件下,按下W键时,将执行你写的脚本代码。请只使用同步脚本而不要使用异步代码,不然可能导致游戏出现问题。 - -`core.status.route.push("key:"+keyCode);` 这句话是对录像的支持。 - -**录像的支持可能比较诡异,在不同条件下都是不同的;因此加不加最好分开独立进行测试。** - -!> H5不支持组合快捷键,所以不存在`W+1`这种组合快捷键的说法! - -!> 手机端可以通过长按任何位置调出虚拟键盘,再进行按键,和键盘按键是等价的效果! +可以使用altKey来判断Alt键是否被同时按下。 ## 公共事件 @@ -554,6 +548,8 @@ this.myfunc = function(x) { 通过这种,将脚本和自定义事件混用的方式,可以达到和RM中公共事件类似的效果,即一个调用触发一系列事件。 + + ## 技能塔的支持 其实,在HTML5上制作技能塔是完全可行的。 要支持技能塔,可能需要如下几个方面: -- 魔力(和上限)的定义添加 +- 魔力(和上限)的添加;技能的定义 - 状态栏的显示 - 技能的触发(按键与录像问题) - 技能的效果 -下面依次进行描述。 +从V2.5开始,内置了"二倍斩"技能,可以仿照其制作自己的技能。 -### 魔力的定义添加 +### 魔力的定义添加;技能的定义 -当我们定义了魔力的ID,比如`mana`后,要使用它,一般有两种方式:属性获取`status:mana`或者flag标记`flag:mana`。 +从V2.5开始,提供了status:mana选项,可以直接代表当前魔力值。 -如果要属性获取,则需要打开`data.js`文件,并在`hero`中添加定义。 +如果要启用,需要开启全塔属性的enableMana选项。 -通过这种方式定义的,可以通过`core.setStatus('mana', 0)`以及`core.getStatus('mana')`来设置或获取。 +如果需要魔力上限,则可以使用flag:manaMax来表示当前的魔力最大值。 -``` js -'hero': { - // ... 上略 - 'mana': 0, // 增添mana定义,可以放在experience之后。同理可定义manaMax表示当前最大魔力值。 -} -``` +同时,我们可以使用flag:skill表示当前开启的技能编号,flag:skillName表示当前开启的技能名称。 -如果要flag标记,则无需额外在任何地方进行定义。只需要在设置或取用的时候使用 `core.setFlag('mana', 0)` 或 `core.getFlag('mana', 0)` 即可。 - -下面我都使用属性获取的方式来进行说明。 +如果flag:skill不为0,则代表当前处于某个技能开启状态,且状态栏显示flag:skillName值。伤害计算函数中只需要对flag:skill进行处理即可。 ### 状态栏的显示 -首先我们需要额外新增一个状态栏;请参见[自定义状态栏(新增显示项)](#自定义状态栏(新增显示项))。 +从V2.5开始,魔力值和技能名的状态栏项目已经被添加,可以直接使用。 -我们可以在魔力那一行显示当前值和最大值: +在脚本编辑-updateStatusBar中,可以对状态栏显示内容进行修改。 ``` 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表示当前激活的技能名。 +// 设置魔力值 +if (core.flags.enableMana) { + // 也可以使用flag:manaMax来表示最大魔力值 + // core.status.hero.mana = Math.max(core.status.hero.mana, core.getFlag('manaMax', 10)); + // core.statusBar.mana.innerHTML = core.status.hero.mana + "/" + core.getFlag('manaMax', 10); +} +// 设置技能栏 +if (core.flags.enableSkill) { + // 可以用flag:skill表示当前开启的技能类型,flag:skillName显示技能名 + core.statusBar.skill.innerHTML = core.getFlag('skillName', '无'); +} ``` ### 技能的触发 @@ -687,11 +678,10 @@ else { // 关闭技能 下面是一个很简单的例子,当勇士按下W后,触发我们上面定义的二倍斩技能。 ``` js -case 87: // W - if (core.status.heroStop) { // 当前停止状态;这个if需要加,不能在行走过程中触发不然容易出错。 - if (core.hasItem('skill1')) { // 判定该技能道具是否存在 - core.useItem('skill1'); // 使用道具(该技能) - } +case 87: // W:开启技能“二倍斩” + // 检测技能栏是否开启,是否拥有“二倍斩”这个技能道具 + if (core.flags.enableSkill && core.hasItem('skill1')) { + core.useItem('skill1'); } break; ``` @@ -716,7 +706,7 @@ case 87: // W 举个例子,我设置一个勇士的技能:二倍斩,开启技能消耗5点魔力,下一场战斗攻击力翻倍。 -那么,直接在`getDamageInfo`中进行判断: +那么,直接在脚本编辑的`getDamageInfo`中进行判断: ``` js if (core.getFlag('skill', 0)==1) { // 开启了技能1 @@ -724,16 +714,24 @@ if (core.getFlag('skill', 0)==1) { // 开启了技能1 } ``` -然后在脚本编辑的战后事件中进行魔力值的扣除: +然后在脚本编辑的`afterBattle`中进行魔力值的扣除: ``` js -if (core.getFlag('skill', 0)==1) { // 开启了技能1 - core.status.hero.mana -= 5; // 扣除5点魔力值 - core.setFlag('skill', 0); // 自动关闭技能 +// 战后的技能处理,比如扣除魔力值 +if (core.flags.enableSkill) { + // 检测当前开启的技能类型 + var skill = core.getFlag('skill', 0); + if (skill==1) { // 技能1:二倍斩 + core.status.hero.mana-=5; // 扣除5点魔力值 + } + // 关闭技能 + core.setFlag('skill', 0); core.setFlag('skillName', '无'); } ``` +!> 开启技能后,建议将全塔属性的useLoop置为true,即改用循环计算临界值,这样临界计算才不会出问题! +   通过上述这几种方式,我们就能成功的让H5支持技能啦! diff --git a/editor-mobile.html b/editor-mobile.html index 161e7897..cd51f4ed 100644 --- a/editor-mobile.html +++ b/editor-mobile.html @@ -355,6 +355,10 @@

+
+ +

+

@@ -379,6 +383,10 @@

+
+ +

+
diff --git a/editor.html b/editor.html index cce7d36c..0ae3917b 100644 --- a/editor.html +++ b/editor.html @@ -341,6 +341,10 @@

+
+ +

+

@@ -365,6 +369,10 @@

+
+ +

+
diff --git a/index.html b/index.html index 62182c53..2a05eb95 100644 --- a/index.html +++ b/index.html @@ -62,6 +62,10 @@

+
+ +

+

@@ -86,6 +90,10 @@

+
+ +

+
diff --git a/libs/actions.js b/libs/actions.js index 5b6c1ee5..9c70ab75 100644 --- a/libs/actions.js +++ b/libs/actions.js @@ -8,7 +8,7 @@ function actions() { } actions.prototype.init = function () { - + this.actionsdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.actions; } ////// 按下某个键时 ////// @@ -282,152 +282,16 @@ actions.prototype.keyUp = function(keyCode, altKey) { this.keyUpCenterFly(keyCode); return; } + if (core.status.event.id=='paint') { + this.keyUpPaint(keyCode); + } return; } if(!core.status.played) return; - // 0~9的AltKey - if (altKey && keyCode>=48 && keyCode<=57 && core.status.heroStop) { - core.items.quickLoadEquip(keyCode-48); - return; - } - - switch (keyCode) { - case 27: // ESC - if (core.status.heroStop) - core.openSettings(true); - break; - case 71: // G - if (core.status.heroStop) - core.useFly(true); - break; - case 81: // Q - if (core.status.heroStop) - core.openEquipbox(true); - break; - case 88: // X - if (core.status.heroStop) - core.openBook(true); - break; - case 65: // A - if (core.status.heroStop) - core.doSL("autoSave", "load"); - break; - case 66: // B - if (core.status.heroStop) - core.ui.drawStatistics(); - break; - case 83: // S - if (core.status.heroStop) - core.save(true); - break; - case 68: // D - if (core.status.heroStop) - core.load(true); - break; - case 69: // E - if (core.status.heroStop) - core.ui.drawCursor(); - break; - case 84: // T - if (core.status.heroStop) - core.openToolbox(true); - break; - case 90: // Z - if (core.status.heroStop) - core.turnHero(); - break; - case 75: case 86: // K/V - if (core.status.heroStop) - core.openQuickShop(true); - break; - case 32: // SPACE - if (core.status.heroStop) - core.getNextItem(); - break; - case 72: // H - if (core.status.heroStop) - core.ui.drawHelp(); - break; - case 82: // R - 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) { - core.ui.drawMaps(); - } - break; - case 37: // UP - break; - case 38: // DOWN - break; - case 39: // RIGHT - break; - case 40: // DOWN - break; - case 49: // 快捷键1: 破 - if (core.status.heroStop && core.hasItem('pickaxe')) { - if (core.canUseItem('pickaxe')) { - core.useItem('pickaxe'); - } - else { - core.drawTip('当前不能使用破墙镐'); - } - } - break; - case 50: // 快捷键2: 炸 - if (core.status.heroStop) { - if (core.hasItem('bomb')) { - if (core.canUseItem('bomb')) { - core.useItem('bomb'); - } - else { - core.drawTip('当前不能使用炸弹'); - } - } - else if (core.hasItem('hammer')) { - if (core.canUseItem('hammer')) { - core.useItem('hammer'); - } - else { - core.drawTip('当前不能使用圣锤'); - } - - } - } - break; - case 51: // 快捷键3: 飞 - if (core.status.heroStop && core.hasItem('centerFly')) { - core.events.useItem('centerFly'); - } - break; - case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... - if (core.status.heroStop) { - var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; - for (var i=0;i0) { - text+="\n当前MAX为"+ending.max+",最早由 "+(ending.username||"匿名")+" 于"+core.formatDate(new Date(1000*ending.timestamp))+"打出。"; - } - }) - }) - core.drawText(text); - } - } - else { - core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:HTTP "+xhr.status); - } - }; - xhr.ontimeout = function() { - core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:Timeout"); - } - xhr.onerror = function() { - core.drawText("出错啦!\n无法拉取统计信息。\n错误原因:XHR Error"); - } - xhr.send(formData); - */ - break; case 6: - core.ui.drawHelp(); + core.ui.drawStatistics(); break; case 7: - core.ui.drawAbout(); + core.ui.drawHelp(); break; case 8: + core.ui.drawAbout(); + break; + case 9: core.ui.closePanel(); break; } @@ -2548,3 +2400,114 @@ actions.prototype.clickAbout = function () { else core.restart(); } + + +////// 绘图相关 ////// + +actions.prototype.ondownPaint = function (x, y) { + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (!core.status.event.data.erase) { + core.canvas.route.beginPath(); + core.canvas.route.moveTo(x, y); + } + core.status.event.data.x = x; + core.status.event.data.y = y; +} + +actions.prototype.onmovePaint = function (x, y) { + if (core.status.event.data.x==null) return; + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (core.status.event.data.erase) { + core.clearMap('route', x-10, y-10, 20, 20); + return; + } + var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; + core.canvas.route.quadraticCurveTo(midx, midy, x, y); + core.canvas.route.stroke(); + core.status.event.data.x = x; + core.status.event.data.y = y; +} + +actions.prototype.onupPaint = function (x,y) { + x+=core.bigmap.offsetX; + y+=core.bigmap.offsetY; + if (core.status.event.data.erase) { + core.clearMap('route', x-5, y-5, 10, 10); + } + else if (core.status.event.data.x!=null) { + var midx = (core.status.event.data.x+x)/2, midy = (core.status.event.data.y+y)/2; + core.canvas.route.quadraticCurveTo(midx, midy, x, y); + core.canvas.route.stroke(); + } + + core.status.event.data.x = null; + core.status.event.data.y = null; + // 保存 + core.paint[core.status.floorId] = LZString.compress(core.utils.encodeCanvas(core.canvas.route).join(",")); +} + +actions.prototype.setPaintMode = function (mode) { + if (mode == 'paint') core.status.event.data.erase = false; + else if (mode == 'erase') core.status.event.data.erase = true; + else return; + + core.drawTip("进入"+(core.status.event.data.erase?"擦除":"绘图")+"模式"); +} + +actions.prototype.savePaint = function () { + var data = {}; + for (var floorId in core.paint) { + if (core.isset(core.paint[floorId])) + data[floorId] = LZString.decompress(core.paint[floorId]); + } + core.download(core.firstData.name+".h5paint", JSON.stringify({ + 'name': core.firstData.name, + 'paint': data + })); +} + +actions.prototype.loadPaint = function () { + core.readFile(function (obj) { + if (obj.name!=core.firstData.name) { + alert("绘图文件和游戏不一致!"); + return; + } + if (!core.isset(obj.paint)) { + alert("无效的绘图文件!"); + return; + } + core.paint = {}; + for (var floorId in obj.paint) { + if (core.isset(obj.paint[floorId])) + core.paint[floorId] = LZString.compress(obj.paint[floorId]); + } + + core.clearMap('route'); + var value = core.paint[core.status.floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); + core.canvas.route.drawImage(core.bigmap.tempCanvas.canvas, 0, 0); + + core.drawTip("读取绘图文件成功"); + }) +} + +actions.prototype.exitPaint = function () { + core.clearMap('route'); + core.ui.closePanel(); + core.statusBar.image.shop.style.opacity = 1; + core.statusBar.image.toolbox.style.opacity = 1; + core.updateStatusBar(); + core.drawTip("退出绘图模式"); +} + +actions.prototype.keyUpPaint = function (keycode) { + if (keycode==27 || keycode==88 || keycode==77 || keycode==13 || keycode==32 || keycode==67) { + this.exitPaint(); + return; + } +} + +////// 绘图相关 END ////// diff --git a/libs/control.js b/libs/control.js index 0a6ce796..d69826b6 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2807,6 +2807,8 @@ control.prototype.resize = function(clientWidth, clientHeight) { if (!core.flags.enableKeys) count--; if (!core.flags.enablePZF) count--; if (!core.flags.enableName) count--; + if (!core.flags.enableMana) count--; + if (!core.flags.enableSkill) count--; var statusLineHeight = BASE_LINEHEIGHT * 9 / count; var statusLineFontSize = DEFAULT_FONT_SIZE; @@ -3086,6 +3088,12 @@ control.prototype.resize = function(clientWidth, clientHeight) { display: core.flags.enableHPMax ? 'block': 'none' } }, + { + id: 'manaCol', + rules: { + display: core.flags.enableMana ? 'block': 'none' + } + }, { id: 'mdefCol', rules: { @@ -3110,6 +3118,12 @@ control.prototype.resize = function(clientWidth, clientHeight) { display: core.flags.enableLevelUp ? 'block': 'none' } }, + { + id: 'skillCol', + rules: { + display: core.flags.enableSkill ? 'block': 'none' + } + }, { id: 'keyCol', rules: { diff --git a/libs/core.js b/libs/core.js index d15765ae..38f0f4d8 100644 --- a/libs/core.js +++ b/libs/core.js @@ -81,6 +81,7 @@ function core() { height: 13, tempCanvas: null, // A temp canvas for drawing } + this.paint = {} this.initStatus = { 'played': false, 'gameOver': false, @@ -395,18 +396,18 @@ core.prototype.keyUp = function(keyCode, altKey) { } ////// 点击(触摸)事件按下时 ////// -core.prototype.ondown = function (x ,y) { - return core.actions.ondown(x,y); +core.prototype.ondown = function (loc) { + return core.actions.ondown(loc); } ////// 当在触摸屏上滑动时 ////// -core.prototype.onmove = function (x ,y) { - return core.actions.onmove(x,y); +core.prototype.onmove = function (loc) { + return core.actions.onmove(loc); } ////// 当点击(触摸)事件放开时 ////// -core.prototype.onup = function () { - return core.actions.onup(); +core.prototype.onup = function (loc) { + return core.actions.onup(loc); } ////// 获得点击事件相对左上角的坐标(0到12之间) ////// diff --git a/libs/events.js b/libs/events.js index 56907f27..73397f40 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1532,6 +1532,8 @@ events.prototype.vibrate = function(time, callback) { events.prototype.openShop = function(shopId, needVisited) { var shop = core.status.shops[shopId]; shop.times = shop.times || 0; + if (shop.commonTimes) + shop.times = core.getFlag('commonTimes', 0); shop.visited = shop.visited || false; if (needVisited && !shop.visited) { if (shop.times==0) core.drawTip("该商店尚未开启"); diff --git a/libs/ui.js b/libs/ui.js index 381536ab..9636e447 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -836,7 +836,7 @@ ui.prototype.drawSettings = function () { core.status.event.id = 'settings'; this.drawChoices(null, [ - "系统设置", "快捷商店", "浏览地图", "同步存档", "返回标题", "数据统计", "操作帮助", "关于本塔", "返回游戏" + "系统设置", "快捷商店", "浏览地图", "打开画板", "同步存档", "返回标题", "数据统计", "操作帮助", "关于本塔", "返回游戏" ]); } @@ -1645,6 +1645,10 @@ ui.prototype.drawMaps = function (index, x, y) { core.clearMap('animate'); core.setOpacity('animate', 1); + var damage = (core.status.event.data||{}).damage, paint = (core.status.event.data||{}).paint; + if (core.isset(index.damage)) damage=index.damage; + if (core.isset(index.paint)) paint=index.paint; + if (core.isset(index.index)) { x=index.x; y=index.y; @@ -1661,19 +1665,27 @@ ui.prototype.drawMaps = function (index, x, y) { if (y<6) y=6; if (y>mh-7) y=mh-7; - core.status.event.data = {"index": index, "x": x, "y": y, "damage": (core.status.event.data||{"damage":false}).damage}; + core.status.event.data = {"index": index, "x": x, "y": y, "damage": damage, "paint": paint}; clearTimeout(core.interval.tipAnimate); core.clearMap('ui'); core.setAlpha('ui', 1); this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 0, 0, 416, x, y); + // 绘图 + if (core.status.event.data.paint) { + var offsetX = core.clamp(x-6, 0, mw-13), offsetY = core.clamp(y-6, 0, mh-13); + var value = core.paint[floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*mw, 32*mh); + core.canvas.ui.drawImage(core.bigmap.tempCanvas.canvas, offsetX*32, offsetY*32, 416, 416, 0, 0, 416, 416); + } + core.clearMap('data'); core.setOpacity('data', 0.2); core.canvas.data.textAlign = 'left'; core.setFont('data', '16px Arial'); - var text = core.status.maps[floorId].title; if (mw>13 || mh>13) text+=" ["+(x-6)+","+(y-6)+"]"; var textX = 16, textY = 18, width = textX + core.canvas.data.measureText(text).width + 16, height = 42; @@ -2417,28 +2429,64 @@ ui.prototype.drawAbout = function () { return this.uidata.drawAbout(); } +////// 绘制“画图”界面 ////// +ui.prototype.drawPaint = function () { + + core.drawText( + "\t[进入绘图模式]你可以在此页面上任意进行绘图和标记操作。\nM键可以进入或退出此模式。\n\n"+ + "绘图的内容会自动保存,且以页面为生命周期,和存读档无关,重新开始游戏或读档后绘制的内容仍有效,但刷新页面就会消失。\n"+ + "你可以将绘制内容保存到文件,也可以从文件读取保存的绘制内容。\n"+ + "浏览地图页面可以按楼传按钮或M键来开启/关闭该层的绘图显示。\n\n更多功能请详见文档-元件-绘图模式。", + function () { + core.drawTip("打开绘图模式,现在可以任意在界面上绘图标记"); + + core.lockControl(); + core.status.event.id = 'paint'; + core.status.event.data = {"x": null, "y": null, "erase": false}; + + core.clearMap('ui'); + core.clearMap('route'); + + core.setAlpha('route', 1); + core.setOpacity('route', 1); + + // 将已有的内容绘制到route上 + var value = core.paint[core.status.floorId]; + if (core.isset(value)) value = LZString.decompress(value).split(","); + core.utils.decodeCanvas(value, 32*core.bigmap.width, 32*core.bigmap.height); + core.canvas.route.drawImage(core.bigmap.tempCanvas.canvas, 0, 0); + + core.setLineWidth('route', 3); + core.setStrokeStyle('route', '#FF0000'); + + core.statusBar.image.shop.style.opacity = 0; + core.statusBar.image.toolbox.style.opacity = 0; + + core.statusBar.image.book.src = core.statusBar.icons.paint.src; + core.statusBar.image.fly.src = core.statusBar.icons.erase.src; + core.statusBar.image.settings.src = core.statusBar.icons.exit.src; + core.statusBar.image.book.style.opacity = 1; + core.statusBar.image.fly.style.opacity = 1; + } + ); +} + ////// 绘制帮助页面 ////// ui.prototype.drawHelp = function () { core.drawText([ "\t[键盘快捷键列表]"+ - "[CTRL] 跳过对话\n" + - "[Z] 转向\n" + - "[X] 打开/关闭怪物手册\n" + - "[G] 打开/关闭楼层传送器\n" + - "[A] 读取自动存档(回退)\n" + + "[CTRL] 跳过对话 [Z] 转向\n" + + "[X] 怪物手册 [G] 楼层传送\n" + + "[A] 读取自动存档 [S/D] 存读档页面\n" + "[S/D] 打开/关闭存/读档页面\n" + - "[K/V] 打开/关闭快捷商店选择列表\n" + - "[T] 打开/关闭工具栏\n" + - "[ESC] 打开/关闭系统菜单\n" + - "[B] 打开数据统计\n" + - // "[E] 显示光标\n" + - "[H] 打开帮助页面\n"+ - "[R] 回放\n"+ - "[SPACE] 轻按(仅在轻按开关打开时有效)\n" + + "[K/V] 快捷商店 [ESC] 系统菜单\n" + + "[T] 道具页面 [Q] 装备页面\n" + + "[B] 数据统计 [H] 帮助页面\n" + + "[R] 回放录像 [E] 显示光标\n" + + "[SPACE] 轻按 [M] 绘图模式\n" + "[PgUp/PgDn] 浏览地图\n"+ - "[1] 快捷使用破墙镐\n" + - "[2] 快捷使用炸弹/圣锤\n" + - "[3] 快捷使用中心对称飞行器", + "[1~4] 快捷使用破炸飞和其他道具\n"+ + "[Alt+0~9] 快捷换装", "\t[鼠标操作]"+ "点状态栏中图标: 进行对应的操作\n"+ "点任意块: 寻路并移动\n"+ @@ -2446,7 +2494,7 @@ ui.prototype.drawHelp = function () { "双击空地: 瞬间移动\n"+ "单击勇士: 转向\n"+ "双击勇士: 轻按(仅在轻按开关打开时有效)\n"+ - "长按任意位置:跳过剧情对话或打开虚拟键盘\n" + "长按任意位置:跳过剧情对话或打开虚拟键盘" ]); } diff --git a/libs/utils.js b/libs/utils.js index ee7a845c..b347f524 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -253,14 +253,14 @@ utils.prototype.cropImage = function (image, size) { ////// 格式化时间为字符串 ////// utils.prototype.formatDate = function(date) { if (!core.isset(date)) return ""; - return date.getFullYear()+"-"+core.setTwoDigits(date.getMonth()+1)+"-"+core.setTwoDigits(date.getDate())+" " + return ""+date.getFullYear()+"-"+core.setTwoDigits(date.getMonth()+1)+"-"+core.setTwoDigits(date.getDate())+" " +core.setTwoDigits(date.getHours())+":"+core.setTwoDigits(date.getMinutes())+":"+core.setTwoDigits(date.getSeconds()); } ////// 格式化时间为最简字符串 ////// utils.prototype.formatDate2 = function (date) { if (!core.isset(date)) return ""; - return date.getFullYear()+core.setTwoDigits(date.getMonth()+1)+core.setTwoDigits(date.getDate()) + return ""+date.getFullYear()+core.setTwoDigits(date.getMonth()+1)+core.setTwoDigits(date.getDate()) +core.setTwoDigits(date.getHours())+core.setTwoDigits(date.getMinutes())+core.setTwoDigits(date.getSeconds()); } @@ -460,6 +460,26 @@ utils.prototype.decodeBase64 = function (str) { }).join('')); } +////// 任意进制转换 ////// +utils.prototype.convertBase = function (str, fromBase, toBase) { + var map = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+={}[]\\|:;<>,.?/"; + if (fromBase==toBase) return str; + var len = str.length, ans=""; + var t = []; + for (var i=0;i0) { + for (var i=len; i>=1; i--) { + t[i-1]+=t[i]%toBase*fromBase; + t[i]=parseInt(t[i]/toBase); + } + ans+=map.charAt(t[0]%toBase); + t[0]=parseInt(t[0]/toBase); + while (len>0 && t[len-1]==0) len--; + } + return ans; +} + utils.prototype.__init_seed = function () { var rand = new Date().getTime()%34834795 + 3534; rand = this.__next_rand(rand); @@ -726,6 +746,58 @@ utils.prototype.hide = function (obj, speed, callback) { }, speed); } +utils.prototype.encodeCanvas = function (ctx) { + var list = []; + var width = ctx.canvas.width, height = ctx.canvas.height; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + var imgData = ctx.getImageData(0, 0, width, height); + for (var i=0;i threshold; diff --git a/main.js b/main.js index d411900b..cee1293a 100644 --- a/main.js +++ b/main.js @@ -55,6 +55,7 @@ function main() { 'nameCol': document.getElementById('nameCol'), 'lvCol': document.getElementById('lvCol'), 'hpmaxCol': document.getElementById('hpmaxCol'), + 'manaCol': document.getElementById('manaCol'), 'mdefCol': document.getElementById('mdefCol'), 'moneyCol': document.getElementById('moneyCol'), 'expCol': document.getElementById('expCol'), @@ -62,6 +63,7 @@ function main() { 'keyCol': document.getElementById('keyCol'), 'pzfCol': document.getElementById('pzfCol'), 'debuffCol': document.getElementById('debuffCol'), + 'skillCol': document.getElementById('skillCol'), 'hard': document.getElementById('hard'), }; this.mode = 'play'; @@ -82,12 +84,14 @@ function main() { 'lv': document.getElementById('img-lv'), 'hpmax': document.getElementById('img-hpmax'), 'hp': document.getElementById("img-hp"), + 'mana': document.getElementById("img-mana"), 'atk': document.getElementById("img-atk"), 'def': document.getElementById("img-def"), 'mdef': document.getElementById("img-mdef"), 'money': document.getElementById("img-money"), 'experience': document.getElementById("img-experience"), 'up': document.getElementById("img-up"), + 'skill': document.getElementById('img-skill'), 'book': document.getElementById("img-book"), 'fly': document.getElementById("img-fly"), 'toolbox': document.getElementById("img-toolbox"), @@ -122,18 +126,25 @@ function main() { 'speedUp': 21, 'rewind': 22, 'equipbox': 23, + 'mana': 24, + 'skill': 25, + 'paint': 26, + 'erase': 27, + 'exit': 28, }, 'floor': document.getElementById('floor'), 'name': document.getElementById('name'), 'lv': document.getElementById('lv'), 'hpmax': document.getElementById('hpmax'), 'hp': document.getElementById('hp'), + 'mana': document.getElementById('mana'), 'atk': document.getElementById('atk'), 'def': document.getElementById("def"), 'mdef': document.getElementById('mdef'), 'money': document.getElementById("money"), 'experience': document.getElementById("experience"), 'up': document.getElementById('up'), + 'skill': document.getElementById('skill'), 'yellowKey': document.getElementById("yellowKey"), 'blueKey': document.getElementById("blueKey"), 'redKey': document.getElementById("redKey"), @@ -317,8 +328,7 @@ main.dom.data.onmousedown = function (e) { } var loc = main.core.getClickLoc(e.clientX, e.clientY); if (loc == null) return; - var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); - main.core.ondown(x, y); + main.core.ondown(loc); } catch (ee) {} } @@ -328,15 +338,17 @@ main.dom.data.onmousemove = function (e) { e.stopPropagation(); var loc = main.core.getClickLoc(e.clientX, e.clientY); if (loc == null) return; - var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); - main.core.onmove(x, y); + main.core.onmove(loc); }catch (ee) {} } ////// 鼠标放开时 ////// -main.dom.data.onmouseup = function () { +main.dom.data.onmouseup = function (e) { try { - main.core.onup(); + e.stopPropagation(); + var loc = main.core.getClickLoc(e.clientX, e.clientY); + if (loc == null) return; + main.core.onup(loc); }catch (e) {} } @@ -356,9 +368,7 @@ main.dom.data.ontouchstart = function (e) { e.preventDefault(); var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; - var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); - //main.core.onclick(x, y, []); - main.core.ondown(x, y); + main.core.ondown(loc); }catch (ee) {} } @@ -368,15 +378,17 @@ main.dom.data.ontouchmove = function (e) { e.preventDefault(); var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); if (loc == null) return; - var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); - main.core.onmove(x, y); + main.core.onmove(loc); }catch (ee) {} } ////// 手指离开触摸屏时 ////// main.dom.data.ontouchend = function () { try { - main.core.onup(); + e.preventDefault(); + var loc = main.core.getClickLoc(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + if (loc == null) return; + main.core.onup(loc); } catch (e) { } } @@ -388,6 +400,11 @@ main.statusBar.image.book.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.setPaintMode('paint'); + return; + } + if (main.core.isPlaying()) main.core.openBook(true); } @@ -395,11 +412,27 @@ main.statusBar.image.book.onclick = function () { ////// 点击状态栏中的楼层传送器/装备栏时 ////// main.statusBar.image.fly.onclick = function () { + // 播放录像时 if (core.isset(core.status.replay) && core.status.replay.replaying) { core.stopReplay(); return; } + // 绘图模式 + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.setPaintMode('erase'); + return; + } + + // 浏览地图时 + if (main.core.isPlaying() && (core.status.event||{}).id=='viewMaps') { + if (core.isset(core.status.event.data)) { + core.status.event.data.paint = !core.status.event.data.paint; + core.ui.drawMaps(core.status.event.data); + } + return; + } + if (main.core.isPlaying()) { if (!main.core.flags.equipboxButton) { main.core.useFly(true); @@ -456,6 +489,11 @@ main.statusBar.image.save.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.savePaint(); + return; + } + if (main.core.isPlaying()) main.core.save(true); } @@ -468,6 +506,11 @@ main.statusBar.image.load.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.loadPaint(); + return; + } + if (main.core.isPlaying()) main.core.load(true); } @@ -480,6 +523,11 @@ main.statusBar.image.settings.onclick = function () { return; } + if (main.core.isPlaying() && (core.status.event||{}).id=='paint') { + core.actions.exitPaint(); + return; + } + if (main.core.isPlaying()) main.core.openSettings(true); } diff --git a/project/data.js b/project/data.js index 74c27764..15eba994 100644 --- a/project/data.js +++ b/project/data.js @@ -75,10 +75,11 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "lv": 1, "hpmax": 9999, "hp": 1000, + "mana": 0, "atk": 100, "def": 100, - "mdef": 100, - "money": 100, + "mdef": 0, + "money": 0, "experience": 0, "equipment": [], "items": { @@ -197,6 +198,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableName": false, "enableLv": false, "enableHPMax": false, + "enableMana": false, "enableMDef": true, "enableMoney": true, "enableExperience": false, @@ -204,6 +206,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "enableKeys": true, "enablePZF": false, "enableDebuff": false, + "enableSkill": false, "flyNearStair": true, "pickaxeFourDirections": false, "bombFourDirections": false, diff --git a/project/functions.js b/project/functions.js index fff7e6bb..59ad43f7 100644 --- a/project/functions.js +++ b/project/functions.js @@ -202,6 +202,19 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } // 增加仇恨值 core.setFlag('hatred', core.getFlag('hatred',0)+core.values.hatred); + + // 战后的技能处理,比如扣除魔力值 + if (core.flags.enableSkill) { + // 检测当前开启的技能类型 + var skill = core.getFlag('skill', 0); + if (skill==1) { // 技能1:二倍斩 + core.status.hero.mana-=5; // 扣除5点魔力值 + } + // 关闭技能 + core.setFlag('skill', 0); + core.setFlag('skillName', '无'); + } + core.updateStatusBar(); @@ -476,6 +489,11 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 对坚固模仿等处理扔到了脚本编辑-getEnemyInfo之中 var enemyInfo = core.enemys.getEnemyInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId); var mon_hp = enemyInfo.hp, mon_atk = enemyInfo.atk, mon_def = enemyInfo.def, mon_special = enemyInfo.special; + + // 技能的处理 + if (core.getFlag('skill', 0)==1) { // 开启了技能1:二倍斩 + hero_atk *= 2; // 计算时攻击力翻倍 + } // 如果是无敌属性,且勇士未持有十字架 if (this.hasSpecial(mon_special, 20) && !core.hasItem("cross")) @@ -563,6 +581,153 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } */ // 别忘了在事件中调用“更新怪物数据”事件! +} + }, + "actions": { + "onKeyUp": function (keyCode, altKey) { + // 键盘按键处理,可以在这里自定义快捷键列表 + // keyCode:当前按键的keyCode(每个键的keyCode自行百度) + // altKey:Alt键是否被按下,为true代表同时按下了Alt键 + // 可以在这里任意增加或编辑每个按键的行为 + + // 如果处于正在行走状态,则不处理 + if (!core.status.heroStop) + return; + + // Alt+0~9,快捷换上套装 + if (altKey && keyCode>=48 && keyCode<=57) { + core.items.quickLoadEquip(keyCode-48); + return; + } + + // 根据keyCode值来执行对应操作 + switch (keyCode) { + case 27: // ESC:打开菜单栏 + core.openSettings(true); + break; + case 88: // X:使用怪物手册 + core.openBook(true); + break; + case 71: // G:使用楼传器 + core.useFly(true); + break; + case 65: // A:读取自动存档(回退) + core.doSL("autoSave", "load"); + break; + case 83: // S:存档 + core.save(true); + break; + case 68: // D:独挡 + core.load(true); + break; + case 69: // E:打开光标 + core.ui.drawCursor(); + break; + case 84: // T:打开道具栏 + core.openToolbox(true); + break; + case 81: // Q:打开装备栏 + core.openEquipbox(true); + break; + case 90: // Z:转向 + core.turnHero(); + break; + case 75: case 86: // K/V:打开快捷商店列表 + core.openQuickShop(true); + break; + case 32: // SPACE:轻按 + core.getNextItem(); + break; + case 82: // R:回放录像 + if (core.hasFlag('debug')) { + core.drawText("\t[系统提示]调试模式下无法回放录像"); + } + else { + core.ui.drawReplay(); + } + break; + case 33: case 34: // PgUp/PgDn:浏览地图 + core.ui.drawMaps(); + break; + case 77: // M:绘图模式 + core.ui.drawPaint(); + break; + case 66: // B:打开数据统计 + core.ui.drawStatistics(); + break; + case 72: // H:打开帮助页面 + core.ui.drawHelp(); + break; + case 49: // 快捷键1: 破 + if (core.hasItem('pickaxe')) { + if (core.canUseItem('pickaxe')) { + core.useItem('pickaxe'); + } + else { + core.drawTip('当前不能使用破墙镐'); + } + } + break; + case 50: // 快捷键2: 炸 + if (core.hasItem('bomb')) { + if (core.canUseItem('bomb')) { + core.useItem('bomb'); + } + else { + core.drawTip('当前不能使用炸弹'); + } + } + else if (core.hasItem('hammer')) { + if (core.canUseItem('hammer')) { + core.useItem('hammer'); + } + else { + core.drawTip('当前不能使用圣锤'); + } + + } + break; + case 51: // 快捷键3: 飞 + if (core.hasItem('centerFly')) { + core.events.useItem('centerFly'); + } + break; + case 52: // 快捷键4:破冰/冰冻/地震/上下楼器/... 其他道具依次判断 + { + var list = ["icePickaxe", "snow", "earthquake", "upFly", "downFly", "jumpShoes", "lifeWand", "poisonWine", "weakWine", "curseWine", "superWine"]; + for (var i=0;i=5) { // 这里要写当前能否开技能的条件判断,比如魔力值至少要多少\n\t\tcore.setFlag('skill', 1); // 开技能1\n\t\tcore.setFlag('skillName', '二倍斩'); // 设置技能名\n\t}\n\telse {\n\t\tcore.drawTip(\"魔力不足,无法开启技能\");\n\t}\n}\nelse { // 关闭技能\n\tcore.setFlag('skill', 0); // 关闭技能状态\n\tcore.setFlag('skillName', '无');\n}" }, "canUseItemEffect": { "book": "true", @@ -401,6 +407,7 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "redJewel": "true", "blueJewel": "true", "greenJewel": "true", - "yellowJewel": "true" + "yellowJewel": "true", + "skill1": "true" } } \ No newline at end of file diff --git a/project/maps.js b/project/maps.js index 6fee9b19..66ef81d1 100644 --- a/project/maps.js +++ b/project/maps.js @@ -79,6 +79,7 @@ maps_90f36752_8815_4be8_b32b_d7fad1d0542e = '65':{'cls': 'items', 'id': 'hammer'}, // 圣锤 '68':{'cls': 'items', 'id': 'lifeWand'}, // 生命魔杖 '69':{'cls': 'items', 'id': 'jumpShoes'}, // 生命魔杖 + '70':{'cls': 'items', 'id': 'skill1'}, // 技能:二倍斩 ////////////////////////// 门、楼梯、传送点部分 //////////////////////////