diff --git a/README.md b/README.md index d2498933..1006b2c4 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,9 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! * [List / HTML5魔塔游戏列表](https://h5mota.com/) * [Demo / 样板效果](https://ckcz123.com/games/template/) * [Docs / 使用文档说明](https://ckcz123.github.io/mota-js/) + ![样板](./docs/img/sample0.png) ## 目录结构 @@ -53,6 +54,19 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! ## 更新说明 +### 2018.9.18 V2.4.1 + +* [x] 增加背景层和前景层的图块绘制,多层图块可叠加 +* [x] 背景层/前景层图块的显示、隐藏、修改等事件 +* [x] 专门的装备页面(Q键开启);装备系统大改造 +* [x] 灯光和漆黑层效果,通过插件函数方式给出 +* [x] 将状态栏更新和阻激夹域的计算移动到脚本编辑中 +* [x] 增加控制免疫阻激夹域的flag:no_zone等 +* [x] 打字机效果时点击显示全部文字 +* [x] 修复更改画面色调的Bug +* [x] 修复更改剧情文本属性后读档恢复原样的问题 +* [x] 部分细节优化 + ### 2018.8.28 V2.4 * [x] 大地图的支持 @@ -293,7 +307,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏! - [JS代码压缩工具](http://github.com/ckcz123/JSCompressor/):能对Javascript代码进行压缩和整合,从而减少IO请求量。 - [便捷PS工具](http://github.com/ckcz123/ps/):能只用复制和粘贴来快速对素材进行PS操作。 - [地图生成器](http://github.com/ckcz123/map_generator/):能从一张截图识别出来具体的数字数组,方便复刻已有的塔。 -- [怪物数据导出器](http://github.com/ckcz123/enemy_export/):能从RMXP中带出怪物数据,以供H5使用。 +- [怪物数据导出器](http://github.com/ckcz123/enemy_export/):能从RMXP中导出怪物数据,以供H5使用。 - [伤害和临界值计算器](http://github.com/ckcz123/magic-tower-calculator/):一个能帮助计算怪物的伤害和临界值的小工具。 ## 联系我们 diff --git a/docs/V2.0.md b/docs/V2.0.md index 2c87474d..0df0686a 100644 --- a/docs/V2.0.md +++ b/docs/V2.0.md @@ -1,6 +1,6 @@ # V2.0版本介绍 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 目前样板已经更新到V2.0版本以上,本章将对V2.0的一些内容进行介绍。 diff --git a/docs/api.md b/docs/api.md index e18f44cf..e411229f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ # 附录: API列表 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * **这里只列出所有可能会被造塔者用到的常用API,更多的有关内容请在代码内进行查询。** diff --git a/docs/element.md b/docs/element.md index fbc5304b..0fd64fc8 100644 --- a/docs/element.md +++ b/docs/element.md @@ -1,6 +1,6 @@ # 元件说明 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 在本章中,将对样板里的各个元件进行说明。各个元件主要包括道具、门、怪物、楼梯等等。 diff --git a/docs/event.md b/docs/event.md index 4786774d..bc1a9561 100644 --- a/docs/event.md +++ b/docs/event.md @@ -1,6 +1,6 @@ # 事件 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 本章内将对样板所支持的事件进行介绍。 diff --git a/docs/index.md b/docs/index.md index 4bfabadd..b4ca28de 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # HTML5 魔塔样板说明文档 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 众所周知,魔塔的趋势是向移动端发展,贴吧中也常常能见到“求手机魔塔”的帖子。然而现有的工具中,NekoRPG有着比较大的局限性,游戏感较差,更是完全没法在iOS上运行。而一些APP的魔塔虽然可用,但是必须要下载安装,对于Android和iOS还必须开发不同的版本,非常麻烦。 @@ -12,9 +12,9 @@ > 这个魔塔样板,可以让你在完全不懂任何编程的情况下,做出自己的H5魔塔。不会代码?没关系!只要你想做,就能做出来! 继续查看文档的详细介绍,让你学会如何使用这一个样板来制作属于自己的HTML5魔塔。 - + ========================================================================================== [继续阅读下一章:现在就做出自己的第一部H5魔塔!](start) diff --git a/docs/personalization.md b/docs/personalization.md index d454eb90..87280538 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -1,6 +1,6 @@ # 个性化 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 有时候只靠样板本身可能是不够的。我们需要一些个性化、自定义的素材,道具效果,怪物属性,等等。 diff --git a/docs/start.md b/docs/start.md index 6bbceee0..6e330a4a 100644 --- a/docs/start.md +++ b/docs/start.md @@ -1,6 +1,6 @@ # 快速上手 -?> 目前版本**v2.4**,上次更新时间:* {docsify-updated} * +?> 目前版本**v2.4.1**,上次更新时间:* {docsify-updated} * 在这一节中,将详细介绍做一部塔的流程。现在,让我们来做一部单层塔! diff --git a/libs/ui.js b/libs/ui.js index ac0bfedc..eb94dcdf 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -567,6 +567,8 @@ ui.prototype.drawTextBox = function(content, showAll) { ////// 绘制一个选项界面 ////// ui.prototype.drawChoices = function(content, choices) { + choices = choices || []; + var background = core.canvas.ui.createPattern(core.material.ground, "repeat"); core.clearMap('ui'); @@ -577,7 +579,15 @@ ui.prototype.drawChoices = function(content, choices) { // Step 1: 计算长宽高 var length = choices.length; - var left=85, width = 416-2*left; // 宽度 + + // 宽度计算:考虑选项的长度 + var width = 416 - 2*85; + core.setFont('ui', "bold 17px Verdana"); + for (var i = 0; i < choices.length; i++) { + width = Math.max(width, core.canvas.ui.measureText(core.replaceText(choices[i].text || choices[i])).width + 30); + } + + var left=parseInt((416 - width) / 2); // 左边界 // 高度 var height = 32*(length+2), bottom = 208+height/2; if (length%2==0) bottom+=16; @@ -693,8 +703,9 @@ ui.prototype.drawChoices = function(content, choices) { content_top = top+55; var title_offset = left+width/2; // 动画 + if (id=='hero' || core.isset(icon)) - title_offset += 22; + title_offset += 12; if (id == 'hero') { var heroHeight = core.material.icons.hero.height; @@ -1872,7 +1883,7 @@ ui.prototype.drawEquipbox = function(index) { // 个数 if (core.itemCount(ownEquip)>1) core.fillText('ui', core.itemCount(ownEquip), 16*(4*(i%6)+1)+40, 304+Math.floor(i/6)*54+38-ydelta, '#FFFFFF', "bold 14px Verdana"); - if (selectId == ownEquip) + if (index>=12 && selectId == ownEquip) core.strokeRect('ui', 16*(4*(i%6)+1)+1, 304+Math.floor(i/6)*54+1-ydelta, 40, 40, '#FFD700'); } diff --git a/main.js b/main.js index f93f1f33..b5720c43 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,7 @@ function main() { //------------------------ 用户修改内容 ------------------------// - this.version = "2.4"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 + this.version = "2.4.1"; // 游戏版本号;如果更改了游戏内容建议修改此version以免造成缓存问题。 this.useCompress = false; // 是否使用压缩文件 // 当你即将发布你的塔时,请使用“JS代码压缩工具”将所有js代码进行压缩,然后将这里的useCompress改为true。 diff --git a/project/data.js b/project/data.js index 3d95092b..8ddfa7b1 100644 --- a/project/data.js +++ b/project/data.js @@ -60,7 +60,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "firstData": { "title": "魔塔样板", "name": "template", - "version": "Ver 2.4", + "version": "Ver 2.4.1", "floorId": "sample0", "hero": { "name": "阳光", @@ -200,8 +200,8 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "bombFourDirections": false, "snowFourDirections": false, "bigKeyIsBox": false, - "equipment": true, - "equipboxButton": true, + "equipment": false, + "equipboxButton": false, "enableAddPoint": false, "enableNegativeDamage": false, "hatredDecrease": true, diff --git a/project/functions.js b/project/functions.js index 6f80e240..6ea42f21 100644 --- a/project/functions.js +++ b/project/functions.js @@ -1,7 +1,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { - "events": { - "initGame": function() { + "events": { + "initGame": function() { // 游戏开始前的一些初始化操作 // 根据flag来对道具进行修改 @@ -32,7 +32,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.material.items.shield5.cls = 'equips'; } }, - "setInitData": function (hard) { + "setInitData": function (hard) { // 不同难度分别设置初始属性 if (hard=='Easy') { // 简单难度 core.setFlag('hard', 1); // 可以用flag:hard来获得当前难度 @@ -52,7 +52,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } core.events.afterLoadData(); }, - "win": function(reason, norank) { + "win": function(reason, norank) { // 游戏获胜事件 core.ui.closePanel(); var replaying = core.status.replay.replaying; @@ -71,7 +71,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = }) }); }, - "lose": function(reason) { + "lose": function(reason) { // 游戏失败事件 core.ui.closePanel(); var replaying = core.status.replay.replaying; @@ -84,7 +84,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = }); }) }, - "afterChangeFloor": function (floorId, fromLoad) { + "afterChangeFloor": function (floorId, fromLoad) { // 转换楼层结束的事件 // floorId是切换到的楼层;fromLoad若为true则代表是从读档行为造成的楼层切换 if (!core.hasFlag("visited_"+floorId)) { @@ -92,7 +92,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.setFlag("visited_"+floorId, true); } }, - "addPoint": function (enemy) { + "addPoint": function (enemy) { // 加点事件 var point = enemy.point; if (!core.flags.enableAddPoint || !core.isset(point) || point<=0) return []; @@ -114,7 +114,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } ]; }, - "afterBattle": function(enemyId,x,y,callback) { + "afterBattle": function(enemyId,x,y,callback) { // 战斗结束后触发的事件 var enemy = core.material.enemys[enemyId]; @@ -235,7 +235,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (core.isset(callback)) callback(); }, - "afterOpenDoor": function(doorId,x,y,callback) { + "afterOpenDoor": function(doorId,x,y,callback) { // 开一个门后触发的事件 var todo = []; @@ -258,7 +258,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } if (core.isset(callback)) callback(); }, - "afterGetItem": function(itemId,x,y,callback) { + "afterGetItem": function(itemId,x,y,callback) { // 获得一个道具后触发的事件 var todo = []; @@ -275,11 +275,11 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = if (core.isset(callback)) callback(); }, - "afterChangeLight": function(x,y) { + "afterChangeLight": function(x,y) { // 改变亮灯之后,可以触发的事件 }, - "afterPushBox": function () { + "afterPushBox": function () { // 推箱子后的事件 var noBoxLeft = function () { @@ -302,7 +302,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ } }, - "afterUseBomb": function () { + "afterUseBomb": function () { // 使用炸弹/圣锤后的事件 // 这是一个使用炸弹也能开门的例子 @@ -317,19 +317,19 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ }, - "beforeSaveData": function(data) { + "beforeSaveData": function(data) { // 即将存档前可以执行的操作 }, - "afterLoadData": function(data) { + "afterLoadData": function(data) { // 读档事件后,载入事件前,可以执行的操作 // 怪物数据的动态修改迁移到了“脚本编辑 - updateEnemys”中,详见文档说明 core.enemys.updateEnemys(); } - }, - "enemys": { - "getSpecials": function() { + }, + "enemys": { + "getSpecials": function() { // 获得怪物的特殊属性,每一行定义一个特殊属性。 // 分为三项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述 // 可以直接写字符串,也可以写个function将怪物传进去 @@ -360,7 +360,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = [24, "激光", function (enemy) {return "经过怪物同行或同列时自动减生命"+(enemy.value||0)+"点";}] ]; }, - "getDamageInfo": function (enemy, hero_hp, hero_atk, hero_def, hero_mdef) { + "getDamageInfo": function (enemy, hero_hp, hero_atk, hero_def, hero_mdef) { // 获得战斗伤害信息(实际伤害计算函数) // 怪物生命,怪物攻击、防御、特殊属性 @@ -453,7 +453,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = "damage": damage }; }, - "updateEnemys": function () { + "updateEnemys": function () { // 更新怪物数据,可以在这里对怪物属性和数据进行动态更新,详见文档——事件——怪物数据的动态修改 // 比如下面这个例子,如果flag:xxx为真,则将绿头怪的攻击设为100,金币设为20 /* @@ -464,9 +464,9 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = */ // 别忘了在事件中调用“更新怪物数据”事件! } - }, - "control": { - "updateStatusBar": function () { + }, + "control": { + "updateStatusBar": function () { // 更新状态栏 // 检查等级 @@ -532,7 +532,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 更新全地图显伤 core.updateDamage(); }, - "updateCheckBlock": function () { + "updateCheckBlock": function () { // 领域、夹击、阻击等的伤害值计算 core.status.checkBlock = {}; @@ -652,7 +652,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 计算夹击伤害 if (has) { core.status.checkBlock.betweenAttack[x+core.bigmap.width*y]=true; - // 先扣除该点领域/阻击/激光造成的伤害,再算夹击 + // 先扣除该点领域/阻击/激光造成的伤害,再算夹击 var leftHp = core.status.hero.hp - core.status.checkBlock.damage[x+core.bigmap.width*y]; // 1血不夹;core.flags.betweenAttackCeil控制向上还是向下 if (leftHp>1) @@ -662,9 +662,9 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = } } } - }, - "ui": { - "drawAbout": function() { + }, + "ui": { + "drawAbout": function() { // 绘制“关于”界面 if (!core.isPlaying()) { core.status.event = {'id': null, 'data': null}; @@ -691,9 +691,9 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = core.fillText('ui', 'HTML5魔塔交流群:539113091', text_start, top+112+32); // TODO: 写自己的“关于”页面,每次增加32像素即可 } - }, - "plugins": { - "plugin": function () { + }, + "plugins": { + "plugin": function () { ////// 插件编写,可以在这里写自己额外需要执行的脚本 ////// // 在这里写的代码,在所有模块加载完毕后,游戏开始前会被执行 @@ -706,10 +706,74 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = // 如果不写this的话,函数将无法被外部所访问 this.test = function () { console.log("插件函数执行测试"); + }; + + + // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) + // 【参数说明】 + // color:可选,灯光以外部分的颜色,可以是一个四元数组,或者简单的一个0到1之间的数。忽略则默认为0.9。 + // 如果是四元数组,则代表RGBA值,如 [255,255,0,0.2] 就代表 #FFFF00 且不透明度0.2 + // 如果是一个数,则只是不透明度的值,RGB均为0,如 0.9 就代表 [0,0,0,0.9] + // lights:可选,一个数组,定义了每个独立的灯光。 + // 其中每一项是三元组 [x,y,r] 或者四元组 [x,y,r,o] + // x和y分别为该灯光的横纵坐标,r为该灯光的半径,o为该灯光中心的不透明度,可忽略默认为0。 + // lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。 + // 比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 + // 【调用样例】 + // core.plugin.drawLight(); // 绘制一个0.9的全图不透明度,等价于更改画面色调为[0,0,0,0.9]。 + // core.plugin.drawLight(0.95, [[25,11,46]]); // 全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 + // core.plugin.drawLight([255,255,0,0.2], [[25,11,46,0.1]]); // 全图为不透明度0.2的黄色,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 + // core.plugin.drawLight(0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 + // core.plugin.drawLight([0,0,255,0.3], [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,且40%后才开始衰减。 + // 【注意事项】 + // 此函数会和更改画面色调发生冲突,请只选择一个使用。 + this.drawLight = function (color, lights, lightDec) { + // 清空色调层 + var ctx = core.canvas.curtain; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + core.clearMap('curtain'); + core.setOpacity('curtain', 1); + core.setAlpha('curtain', 1); + + // 绘制色调层,默认不透明度 + if (!core.isset(color)) color = 0.9; + if (typeof color == "number") color = [0,0,0,color]; + core.fillRect('curtain', 0, 0, 416, 416, + 'rgba('+color[0]+','+color[1]+','+color[2]+','+core.clamp(color[3],0,1)+')'); + + // 绘制每个灯光效果 + if (!core.isset(lights) || lights.length==0) return; + lightDec = core.clamp(lightDec, 0, 1); + lights.forEach(function (light) { + // 坐标,半径,中心不透明度 + var x = light[0], y = light[1], r = light[2], o = 255 * (1 - core.clamp(light[3], 0, 1)); + // 计算衰减距离 + var decDistance = parseInt(r * lightDec), leftDistance = r - decDistance; + // 正方形区域的直径和左上角坐标 + var d = r * 2, sx = x - r, sy = y - r; + // 获得正方形区域的颜色信息 + var imageData = ctx.getImageData(sx, sy, d, d); + // 对每个像素点进行遍历 + for (var i = 0; i < imageData.data.length; i+=4) { + // 当前点的坐标 + var index = i / 4, cx = parseInt(index/d), cy = index%d; + // 当前点距离中心点的距离 + var dx = r - cx, dy = r - cy, distance = Math.sqrt(dx*dx+dy*dy); + if (distance >= r) continue; + // 计算当前点的alpha值 + var alpha = imageData.data[i+3] - (distance