diff --git a/docs/personalization.md b/docs/personalization.md index db2c17c2..bf4a377e 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -233,6 +233,35 @@ ID必须由数字字母下划线组成,数字在1000以内,且均不能和 因此,在你修改了icons.js和maps.js两个文件,也就是将素材添加到游戏后,地图生成器的对应关系也将同步更新。 +### 额外素材 + +从V2.4.2开始,HTML5魔塔样板开始支持额外素材。 + +具体而言,通过上面的“素材导入”的方式,确实可以有效地添加素材到游戏。但是,如果想增加大量自定义素材,需要通过便捷PS工具将这些素材全部导入到 +`terrains.png`中,并且全部是单列,极度不友好。这也导致了野外风的制作相对变得很困难,增加了大量素材处理的工作量。 + +额外素材就是为了解决这个问题而被提出。 + +所谓`额外素材`,即用户可以自定导入任意张素材图片,无需PS,无需注册,即可直接在游戏中使用。这一点已经十分向RM靠拢了。 + +要使用额外素材,请将你需要的素材图片放在`images`目录下,并在`全塔属性`的`tilesets`中定义图片名。 + +**该素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)。** + +```js +"tilesets": ["1.png", "2.png"] // 导入两个额外素材,文件名分别是1.png和2.png +``` + +刷新后,系统会自动加载该素材并添加到素材区。 + +额外素材无需导入,无需注册。在`tilesets`中定义了图片后,即可直接使用绘图,无需再注册其数字和ID。其ID、索引和数字均为系统自动分配,且不允许修改。 + +请注意,额外素材的ID、索引和数字,与该图片在tilesets数组中的index及该素材在图片上的位置都有关系。 + +!> **因此如果对`tilesets`数组随意删除或修改顺序,可能会导致所有额外素材全部发生变化!这点请务必注意!!!** + +除此之外,额外素材在游戏中的使用和正式素材都是一致的,也能在前景或背景图层绘制。 + ## 自定义道具效果 本节中将继续介绍如何自己编辑一个道具的效果。 diff --git a/docs/start.md b/docs/start.md index a9bad3e5..f819c427 100644 --- a/docs/start.md +++ b/docs/start.md @@ -178,6 +178,16 @@ 素材注册完毕后,即可在游戏中正常使用,也可以被地图生成器所识别(需要重开地图生成器)。 +### 额外素材 + +从2.4.2开始,H5魔塔样板支持额外素材,你可以导入任意个类似RM中的tilesets文件,且无需注册即可使用。 + +要使用额外素材,请在`全塔属性`中的`tilesets`项,添加额外素材的图片名称,刷新后即可在地图编辑器中使用。 + +额外素材不可注册,其数字、ID和索引都是和该图块在图片上的位置相关,不可编辑。 + +有关额外素材的更多说明参见[额外素材](personalization#额外素材) + ## 控制台调试 HTML5的塔都是可以进行控制台调试的。 diff --git a/index.html b/index.html index 56128d9d..7710ce4e 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - + HTML5魔塔 diff --git a/libs/core.js b/libs/core.js index f3bab34c..e049fe92 100644 --- a/libs/core.js +++ b/libs/core.js @@ -191,7 +191,6 @@ core.prototype.init = function (coreData, callback) { document.title = core.firstData.title + " - HTML5魔塔"; document.getElementById("startLogo").innerHTML = core.firstData.title; core.material.items = core.clone(core.items.getItems()); - core.initStatus.maps = core.maps.initMaps(core.floorIds); core.material.enemys = core.clone(core.enemys.getEnemys()); core.material.icons = core.icons.getIcons(); core.material.events = core.events.getEvents(); @@ -299,6 +298,7 @@ core.prototype.init = function (coreData, callback) { console.log(core.material); // 设置勇士高度 core.material.icons.hero.height = core.material.images.hero.height/4; + core.initStatus.maps = core.maps.initMaps(core.floorIds); core.setRequestAnimationFrame(); core.showStartAnimate(); diff --git a/libs/events.js b/libs/events.js index 357bb26a..0dfa58c4 100644 --- a/libs/events.js +++ b/libs/events.js @@ -373,14 +373,14 @@ events.prototype.doAction = function() { if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { core.animateBlock(data.loc,'show', data.time, function () { data.loc.forEach(function (t) { - core.showBlock(t[0],t[1],data.floorId) + core.showBlock(t[0],t[1],data.floorId); }) core.events.doAction(); }); } else { data.loc.forEach(function (t) { - core.showBlock(t[0],t[1],data.floorId) + core.showBlock(t[0],t[1],data.floorId); }) this.doAction(); } @@ -392,14 +392,22 @@ events.prototype.doAction = function() { && (typeof data.loc[1] == 'number' || typeof data.loc[1] == 'string')) data.loc = [[core.calValue(data.loc[0]), core.calValue(data.loc[1])]]; data.loc.forEach(function (t) { - core.removeBlock(t[0],t[1],data.floorId); + core.maps.eraseBlock(t[0],t[1],data.floorId); }) if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) { core.animateBlock(data.loc,'hide',data.time, function () { + data.loc.forEach(function (t) { + core.removeBlock(t[0],t[1],data.floorId) + }) core.events.doAction(); }); } - else this.doAction(); + else { + data.loc.forEach(function (t) { + core.removeBlock(t[0],t[1],data.floorId) + }) + this.doAction(); + } break; case "setBlock": // 设置某图块 { diff --git a/libs/icons.js b/libs/icons.js index f1a557a1..149c1361 100644 --- a/libs/icons.js +++ b/libs/icons.js @@ -5,8 +5,38 @@ function icons() { icons.prototype.init = function () { this.icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; //delete(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1); + + // tileset的起点 + this.tilesetStartOffset = 10000; } icons.prototype.getIcons = function () { return this.icons; +} + +////// 根据图块数字或ID获得所在的tileset和坐标信息 ////// +icons.prototype.getTilesetOffset = function (id) { + + if (typeof id == 'string') { + // Tileset的ID必须是 X+数字 的形式 + if (!/^X\d+$/.test(id)) return null; + id = parseInt(id.substring(1)); + } + else if (typeof id != 'number') { + return null; + } + + core.tilesets = core.tilesets || []; + var startOffset = this.tilesetStartOffset; + for (var i in core.tilesets) { + var imgName = core.tilesets[i]; + var img = core.material.images.tilesets[imgName]; + var width = Math.floor(img.width/32), height = Math.floor(img.height/32); + if (id>=startOffset && id32) { core.canvas.event2.clearRect(block.x * 32 + dx, block.y * 32 + 32 - height + dy, 32, height-32) - core.canvas.event2.drawImage(blockImage, animate * 32, blockIcon * height, 32, height-32, block.x * 32 + dx, block.y*32 + 32 - height + dy, 32, height-32); + core.canvas.event2.drawImage(image, x * 32, y * height, 32, height-32, block.x * 32 + dx, block.y*32 + 32 - height + dy, 32, height-32); } } @@ -379,6 +399,12 @@ maps.prototype.drawBgFgMap = function (floorId, canvas, name) { var id = block.event.id, cls = block.event.cls; if (cls == 'autotile') core.drawAutotile(canvas, arr, block, 32, 0, 0); + else if (cls == 'tileset') { + var offset = core.icons.getTilesetOffset(id); + if (offset!=null) { + canvas.drawImage(core.material.images.tilesets[offset.image], 32*offset.x, 32*offset.y, 32, 32, 32*block.x, 32*block.y, 32, 32); + } + } else canvas.drawImage(core.material.images[cls], 0, core.material.icons[cls][id] * 32, 32, 32, x * 32, y * 32, 32, 32); } @@ -390,6 +416,7 @@ maps.prototype.drawBgFgMap = function (floorId, canvas, name) { ////// 绘制某张地图 ////// maps.prototype.drawMap = function (mapName, callback) { + mapName = mapName || core.status.floorId; core.clearMap('all'); core.removeGlobalAnimate(null, null, true); var drawBg = function(){ @@ -465,11 +492,9 @@ maps.prototype.drawMap = function (mapName, callback) { if (block.event.cls == 'autotile') { core.drawAutotile(core.canvas.event, mapArray, block, 32, 0, 0); } - else { - if (block.event.id!='none') { - core.drawBlock(block); - core.addGlobalAnimate(block); - } + else if (block.event.id!='none') { + core.drawBlock(block); + core.addGlobalAnimate(block); } } } @@ -675,13 +700,32 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { core.setAlpha('ui', 1.0); block=block.block; - var blockIcon = core.material.icons[block.event.cls][block.event.id]; - var blockImage = core.material.images[block.event.cls]; - var height = block.event.height || 32; + + var image, bx, by, height = block.event.height || 32; + if (block.event.cls == 'tileset') { + var offset = core.icons.getTilesetOffset(block.event.id); + if (offset==null) { + if (core.isset(callback)) callback(); + return; + } + bx = offset.x; + by = offset.y; + image = core.material.images.tilesets[offset.image]; + } + // 不支持autotile + else if (block.event.cls == 'autotile') { + if (core.isset(callback)) callback(); + return; + } + else { + image = core.material.images[block.event.cls]; + bx = 0; + by = core.material.icons[block.event.cls][block.event.id]; + } var opacityVal = 1; core.setOpacity('route', opacityVal); - core.canvas.route.drawImage(blockImage, 0, blockIcon * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height); + core.canvas.route.drawImage(image, bx * 32, by * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height); // 要运行的轨迹:将steps展开 var moveSteps=[]; @@ -721,6 +765,9 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { animateTime = 0; if (animateCurrent>=animateValue) animateCurrent=0; } + if (block.event.cls=='tileset') { + animateCurrent = bx; + } // 已经移动完毕,消失 if (moveSteps.length==0) { @@ -728,7 +775,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { else opacityVal -= 0.06; core.setOpacity('route', opacityVal); core.clearMap('route', nowX, nowY-height+32, 32, height); - core.canvas.route.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, nowX, nowY-height+32, 32, height); + core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, nowX, nowY-height+32, 32, height); if (opacityVal<=0) { clearInterval(animate); core.clearMap('route'); @@ -749,7 +796,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,keep,callback) { nowY+=scan[moveSteps[0]].y*2; core.clearMap('route', nowX-32, nowY-32, 96, 96); // 绘制 - core.canvas.route.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, nowX, nowY-height+32, 32, height); + core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, nowX, nowY-height+32, 32, height); if (step==16) { // 该移动完毕,继续 step=0; @@ -778,13 +825,31 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { core.setAlpha('ui', 1.0); block=block.block; - var blockIcon = core.material.icons[block.event.cls][block.event.id]; - var blockImage = core.material.images[block.event.cls]; - var height = block.event.height || 32; + var image, bx, by, height = block.event.height || 32; + if (block.event.cls == 'tileset') { + var offset = core.icons.getTilesetOffset(block.event.id); + if (offset==null) { + if (core.isset(callback)) callback(); + return; + } + bx = offset.x; + by = offset.y; + image = core.material.images.tilesets[offset.image]; + } + // 不支持autotile + else if (block.event.cls == 'autotile') { + if (core.isset(callback)) callback(); + return; + } + else { + image = core.material.images[block.event.cls]; + bx = 0; + by = core.material.icons[block.event.cls][block.event.id]; + } var opacityVal = 1; core.setOpacity('route', opacityVal); - core.canvas.route.drawImage(blockImage, 0, blockIcon * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height); + core.canvas.route.drawImage(image, bx*32, by * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height); core.playSound('jump.mp3'); @@ -823,18 +888,21 @@ maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,keep,callback) { animateTime = 0; if (animateCurrent >= animateValue) animateCurrent = 0; } + if (block.event.cls=='tileset') { + animateCurrent = bx; + } if (jump_count>0) { core.clearMap('route', drawX(), drawY()-height+32, 32, height); updateJump(); - core.canvas.route.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, drawX(), drawY()-height+32, 32, height); + core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, drawX(), drawY()-height+32, 32, height); } else { if (keep) opacityVal=0; else opacityVal -= 0.06; core.setOpacity('route', opacityVal); core.clearMap('route', drawX(), drawY()-height+32, 32, height); - core.canvas.route.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, drawX(), drawY()-height+32, 32, height); + core.canvas.route.drawImage(image, animateCurrent * 32, by * height, 32, height, drawX(), drawY()-height+32, 32, height); if (opacityVal<=0) { clearInterval(animate); core.clearMap('route'); @@ -865,10 +933,29 @@ maps.prototype.animateBlock = function (loc,type,time,callback) { var block = core.getBlock(t[0],t[1],null,true); if (block==null) return; block=block.block; + var image, bx, by, height = block.event.height || 32; + if (block.event.cls == 'tileset') { + var offset = core.icons.getTilesetOffset(block.event.id); + if (offset==null) { + if (core.isset(callback)) callback(); + return; + } + bx = offset.x; + by = offset.y; + image = core.material.images.tilesets[offset.image]; + } + // 不支持autotile + else if (block.event.cls == 'autotile') { + return; + } + else { + image = core.material.images[block.event.cls]; + bx = 0; + by = core.material.icons[block.event.cls][block.event.id]; + } list.push({ - 'x': t[0], 'y': t[1], 'height': block.event.height||32, - 'blockIcon': core.material.icons[block.event.cls][block.event.id], - 'blockImage': core.material.images[block.event.cls] + 'x': t[0], 'y': t[1], 'height': height, + 'bx': bx, 'by': by, 'image': image }) }) @@ -880,7 +967,7 @@ maps.prototype.animateBlock = function (loc,type,time,callback) { core.status.replay.animate=true; var draw = function () { list.forEach(function (t) { - core.canvas.route.drawImage(t.blockImage, 0, t.blockIcon*t.height, 32, t.height, t.x*32, t.y*32+32-t.height, 32, t.height); + core.canvas.route.drawImage(t.image, t.bx*32, t.by*t.height, 32, t.height, t.x*32, t.y*32+32-t.height, 32, t.height); }) } @@ -923,6 +1010,27 @@ maps.prototype.showBlock = function(x, y, floodId) { } } +////// 只隐藏但不删除某块 ////// +maps.prototype.eraseBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + + var block = core.getBlock(x,y,floorId,true); + if (block==null) return; // 不存在 + + // 删除动画,清除地图 + if (floorId==core.status.floorId) { + core.removeGlobalAnimate(x, y); + core.canvas.event.clearRect(x * 32, y * 32, 32, 32); + var height = 32; + if (core.isset(block.block.event)) height=block.block.event.height||32; + if (height>32) + core.canvas.event2.clearRect(x * 32, y * 32 +32-height, 32, height-32); + } + + block.disable = true; + core.updateStatusBar(); +} + ////// 将某个块从启用变成禁用状态 ////// maps.prototype.removeBlock = function (x, y, floorId) { floorId = floorId || core.status.floorId; diff --git a/libs/ui.js b/libs/ui.js index 3a76ed11..eb6be6d8 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -2039,6 +2039,12 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente if (block.event.cls == 'autotile') { core.drawAutotile(tempCanvas, mapArray, block, 32, 0, 0); } + else if (block.event.cls == 'tileset') { + var offset = core.icons.getTilesetOffset(block.event.id); + if (offset!=null) { + tempCanvas.drawImage(core.material.images.tilesets[offset.image], 32*offset.x, 32*offset.y, 32, 32, 32*block.x, 32*block.y, 32, 32); + } + } else { if (block.event.id!='none') { var blockIcon = core.material.icons[block.event.cls][block.event.id]; diff --git a/main.js b/main.js index b5720c43..37534975 100644 --- a/main.js +++ b/main.js @@ -67,7 +67,7 @@ function main() { 'loader', 'control', 'utils', 'items', 'icons', 'maps', 'enemys', 'events', 'actions', 'data', 'ui', 'core' ]; this.pureData = [ - "data","enemys","icons","maps","items","functions" + 'data', 'enemys', 'icons', 'maps', 'items', 'functions' ]; this.materials = [ 'animates', 'enemys', 'hero', 'items', 'npcs', 'terrains', 'enemy48', 'npc48' @@ -182,7 +182,7 @@ main.prototype.init = function (mode, callback) { main.loaderFloors(function() { var coreData = {}; - ["dom", "statusBar", "canvas", "images", "materials", + ["dom", "statusBar", "canvas", "images", "tilesets", "materials", "animates", "bgms", "sounds", "floorIds", "floors"].forEach(function (t) { coreData[t] = main[t]; }) diff --git a/project/data.js b/project/data.js index 8ddfa7b1..8b487b92 100644 --- a/project/data.js +++ b/project/data.js @@ -11,6 +11,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "images": [ "bg.jpg" ], + "tilesets": [], "animates": [ "hand", "sword",