Finish sample1

This commit is contained in:
oc 2017-12-08 02:19:56 +08:00
parent 29b4789969
commit f176716816
11 changed files with 1183 additions and 806 deletions

View File

@ -18,8 +18,7 @@ function core() {
'items': {},
'enemys': {},
'icons': {},
'events': {},
'npcs': {}
'events': {}
}
this.timeout = {
'getItemTipTimeout': null
@ -83,7 +82,6 @@ function core() {
// event事件
'savePage': null,
'shops': {},
'npcs': {},
'event': {
'id': null,
'data': null
@ -113,10 +111,9 @@ core.prototype.init = function (dom, statusBar, canvas, images, sounds, floorIds
core[key] = coreData[key];
}
core.flags = core.data.flags;
// core.values = core.clone(core.data.values);
core.values = core.clone(core.data.values);
core.firstData = core.data.getFirstData();
core.initStatus.shops = core.firstData.shops;
core.initStatus.npcs = core.firstData.npcs;
core.dom.versionLabel.innerHTML = core.firstData.version;
core.dom.logoLabel.innerHTML = core.firstData.title;
core.material.items = core.items.getItems();
@ -124,7 +121,6 @@ core.prototype.init = function (dom, statusBar, canvas, images, sounds, floorIds
core.material.enemys = core.clone(core.enemys.getEnemys());
core.material.icons = core.icons.getIcons();
core.material.events = core.events.getEvents();
core.material.npcs = core.npcs.getNpcs();
// test if iOS
core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true);
@ -298,7 +294,7 @@ core.prototype.clearStatus = function() {
core.resize(main.dom.body.clientWidth, main.dom.body.clientHeight);
}
core.prototype.resetStatus = function(hero, hard, floorId, values, maps) {
core.prototype.resetStatus = function(hero, hard, floorId, maps) {
// 停止各个Timeout和Interval
for (var i in core.interval) {
@ -310,7 +306,6 @@ core.prototype.resetStatus = function(hero, hard, floorId, values, maps) {
core.status.played = true;
// 初始化maps
core.status.floorId = floorId;
core.values = core.clone(values);
core.status.maps = core.clone(maps);
// 初始化怪物
core.material.enemys = core.clone(core.enemys.getEnemys());
@ -327,8 +322,7 @@ core.prototype.resetStatus = function(hero, hard, floorId, values, maps) {
core.prototype.startGame = function (hard, callback) {
console.log('开始游戏');
core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, core.data.values,
core.initStatus.maps);
core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, core.initStatus.maps);
core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, function() {
core.setHeroMoveTriggerInterval();
@ -449,11 +443,17 @@ core.prototype.keyUp = function(keyCode) {
core.ui.closePanel();
if (core.status.event.id == 'load' && (keyCode==76 || keyCode==27))
core.ui.closePanel();
if ((core.status.event.id == 'shop' || core.status.event.id == 'settings'
|| core.status.event.id == 'selectShop') && keyCode==27)
if ((core.status.event.id == 'settings' || core.status.event.id == 'selectShop') && keyCode==27)
core.ui.closePanel();
if (core.status.event.id == 'selectShop' && keyCode==75)
core.ui.closePanel();
if (core.status.event.id == 'shop' && keyCode==27) {
core.status.boxAnimateObjs = [];
core.setBoxAnimate();
if (core.status.event.data.fromList)
core.ui.drawQuickShop();
else core.ui.closePanel();
}
if (core.status.event.id == 'toolbox' && (keyCode==84 || keyCode==27))
core.ui.closePanel();
if (core.status.event.id == 'about' && (keyCode==13 || keyCode==32))
@ -490,7 +490,7 @@ core.prototype.keyUp = function(keyCode) {
core.turnHero();
break;
case 75: // K
core.ui.drawSelectShop(true);
core.ui.drawQuickShop(true);
break;
case 37: // UP
break;
@ -675,12 +675,6 @@ core.prototype.onclick = function (x, y, stepPostfix) {
return;
}
// NPC
if (core.status.event.id == 'npc') {
core.events.clickNPC(x,y);
return;
}
// 同步存档
if (core.status.event.id == 'syncSave') {
if (x>=4 && x<=8) {
@ -1311,13 +1305,17 @@ core.prototype.drawHero = function (direction, x, y, status, offsetX, offsetY) {
// 开门
core.prototype.openDoor = function (id, x, y, needKey, callback) {
// 是否存在门
if (!core.doorExists(x, y, id)) return;
if (!core.terrainExists(x, y, id)) {
if (core.isset(callback)) callback();
return;
}
if (core.status.moveStepBeforeStop.length==0) {
core.status.moveStepBeforeStop=core.status.autoStepRoutes.slice(core.status.autoStep-1,core.status.autoStepRoutes.length);
if (core.status.moveStepBeforeStop.length>=1)core.status.moveStepBeforeStop[0].step-=core.status.movedStep;
}
core.stopHero();
core.stopAutomaticRoute();
var speed=30;
if (needKey) {
var key = id.replace("Door", "Key");
if (!core.rmItem(key)) {
@ -1325,13 +1323,19 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) {
core.drawTip("你没有" + core.material.items[key].name);
else core.drawTip("无法开启此门");
core.clearContinueAutomaticRoute();
if (core.isset(callback)) callback();
return;
}
}
// open
core.playSound("door", "ogg");
var state = 0;
var door = core.material.icons.animates[id];
var doorId = id;
if (!(doorId.substring(doorId.length-4)=="Door")) {
doorId=doorId+"Door";
speed=100;
}
var door = core.material.icons.animates[doorId];
core.interval.openDoorAnimate = window.setInterval(function () {
state++;
if (state == 4) {
@ -1342,7 +1346,7 @@ core.prototype.openDoor = function (id, x, y, needKey, callback) {
}
core.canvas.event.clearRect(32 * x, 32 * y, 32, 32);
core.canvas.event.drawImage(core.material.images.animates, 32 * state, 32 * door, 32, 32, 32 * x, 32 * y, 32, 32);
}, 30)
}, speed)
}
// 战斗
@ -1377,8 +1381,10 @@ core.prototype.battle = function (id, x, y, force, callback) {
if (core.hasFlag('curse')) experience=0;
core.status.hero.experience += experience;
core.updateStatusBar();
core.removeBlock(x, y);
core.canvas.event.clearRect(32 * x, 32 * y, 32, 32);
if (core.isset(x) && core.isset(y)) {
core.removeBlock(x, y);
core.canvas.event.clearRect(32 * x, 32 * y, 32, 32);
}
core.updateFg();
var hint = "打败 " + core.material.enemys[id].name + ",金币+" + money;
if (core.flags.enableExperience)
@ -1489,14 +1495,6 @@ core.prototype.strokeRect = function (map, x, y, width, height, style, lineWidth
core.canvas[map].strokeRect(x, y, width, height);
}
core.prototype.drawBlock = function (map, image, cutX, cutY, x, y, size, zoom, clear) {
zoom = zoom || 1;
if (core.isset(clear) && clear == true) {
core.canvas[map].clearRect(x * size, y * size, size, size);
}
core.canvas[map].drawImage(core.material.images[image], cutX * size, cutY * size, size, size, x * size, y * size, size * zoom, size * zoom);
}
core.prototype.setFont = function (map, font) {
core.canvas[map].font = font;
}
@ -1518,13 +1516,13 @@ core.prototype.loadCanvas = function (map) {
core.canvas[map].restore();
}
core.prototype.setOpacity = function (map, opacity) {
core.prototype.setAlpha = function (map, alpha) {
if (map == 'all') {
for (var m in core.canvas) {
core.canvas[m].globalAlpha = opacity;
core.canvas[m].globalAlpha = alpha;
}
}
core.canvas[map].globalAlpha = opacity;
core.canvas[map].globalAlpha = alpha;
}
core.prototype.setStrokeStyle = function (map, style) {
@ -1596,120 +1594,49 @@ core.prototype.drawMap = function (mapName, callback) {
core.addGlobalAnimate(mapBlocks[b].event.animate, mapBlocks[b].x * 32, mapBlocks[b].y * 32, blockIcon, blockImage);
}
}
core.setGlobalAnimate(core.firstData.animateSpeed);
core.setGlobalAnimate(core.values.animateSpeed);
if (core.isset(callback))
callback();
}
/**
* 是否存在不可通行节点
* @param x
* @param y
* @returns {boolean}
*/
core.prototype.noPassExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return core.isset(block.block.event.noPass) && block.block.event.noPass;
/*
var blocks = core.status.thisMap.blocks;
for (var n = 0; n < blocks.length; n++) {
if (blocks[n].x == x && blocks[n].y == y && core.isset(blocks[n].event)
&& !(core.isset(blocks[n].enable) && !blocks[n].enable)
&& core.isset(blocks[n].event.noPass) && blocks[n].event.noPass) {
return true;
}
}
return false;
*/
}
/**
* 是否存在NPC节点
* @param x
* @param y
* @returns {boolean}
*/
core.prototype.npcExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls == 'npcs';
/*
var blocks = core.status.thisMap.blocks;
for (var n = 0; n < blocks.length; n++) {
if (blocks[n].x == x && blocks[n].y == y && core.isset(blocks[n].event)
&& !(core.isset(blocks[n].enable) && !blocks[n].enable)
&& blocks[n].event.cls == 'npcs') {
return true;
}
}
return false;
*/
}
core.prototype.doorExists = function (x, y, id, floorId) {
core.prototype.terrainExists = function (x, y, id, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls=='terrains' && block.block.event.id==id;
/*
if (x > 12 || y > 12 || x < 0 || y < 0) {
return true;
}
if (core.stairExists(x, y)) {
return false;
}
var blocks = core.status.thisMap.blocks;
for (var t = 0; t < blocks.length; t++) {
if (blocks[t].x == x && blocks[t].y == y) {
if (core.isset(blocks[t][map]) && (blocks[t][map].cls == 'terrains' || (blocks[t][map].cls == 'animates' && core.isset(blocks[t][map].noPass) && blocks[t][map].noPass == true)) && ((core.isset(id) && core.isset(blocks[t][map].id)) ? blocks[t][map].id == id : true)) {
return true;
}
}
}
return false;
*/
}
/**
* 是否存在楼梯
* @param x
* @param y
* @returns {boolean}
*/
core.prototype.stairExists = function (x, y, floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls=='terrains' && (block.block.event.id=='upFloor' || block.block.event.id=='downFloor');
/*
var blocks = core.status.thisMap.blocks;
for (var s = 0; s < blocks.length; s++) {
if (blocks[s].x == x && blocks[s].y == y && core.isset(blocks[s].event) && blocks[s].event.cls == 'terrains' && core.isset(blocks[s].event.id) && (blocks[s].event.id == 'upFloor' || blocks[s].event.id == 'downFloor')) {
return true;
}
}
return false;
*/
}
core.prototype.nearStair = function() {
var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
return core.stairExists(x,y) || core.stairExists(x-1,y) || core.stairExists(x,y-1) || core.stairExists(x+1,y) || core.stairExists(x,y+1);
}
core.prototype.enemyExists = function (x, y, id,floorId) {
var block = core.getBlock(x,y,floorId);
if (block==null) return false;
return block.block.event.cls=='enemys' && block.block.event.id==id;
/*
var blocks = core.status.thisMap.blocks;
for (var e = 0; e < blocks.length; e++) {
if (blocks[e].x == x && blocks[e].y == y && core.isset(blocks[e].event) && blocks[e].event.cls == 'enemys' && ((core.isset(id) && core.isset(blocks[e].event.id)) ? blocks[e].event.id == id : true)) {
return true;
}
}
return false;
*/
}
core.prototype.getBlock = function (x, y, floorId, needEnable) {
floorId = floorId || core.status.floorId;
needEnable = needEnable || true;
if (!core.isset(floorId)) floorId=core.status.floorId;
if (!core.isset(needEnable)) needEnable=true;
var blocks = core.status.maps[floorId].blocks;
for (var n=0;n<blocks.length;n++) {
if (blocks[n].x==x && blocks[n].y==y && core.isset(blocks[n].event)) {
@ -1725,21 +1652,173 @@ core.prototype.removeBlockById = function (index, floorId) {
var blocks = core.status.maps[floorId].blocks;
var x=blocks[index].x, y=blocks[index].y;
// 检查该点是否是checkBlock
if (core.floors[floorId].checkBlock.indexOf(x+","+y)>=0) {
blocks[index] = {'x': x, 'y': y, 'event': {'cls': 'terrains', 'id': 'ground', 'noPass': false, 'trigger': 'checkBlock'}};
return;
}
// 检查该点是否存在事件
var event = core.floors[floorId].events[x+","+y];
if (!core.isset(event))
event = core.floors[floorId].changeFloor[x+","+y];
// 不存在事件,直接删除
if (!core.isset(event)) {
blocks.splice(index,1);
return;
}
// 如果该点事件是“checkBlock”则替换现有的事件
if ((event instanceof Object) && event.trigger == 'checkBlock') {
blocks[index] = {'x': x, 'y': y, 'event': {'cls': 'terrains', 'id': 'ground', 'noPass': false, 'trigger': 'checkBlock'}};
blocks[index].enable = false;
}
core.prototype.moveBlock = function(x,y,steps,time,disappear,callback) {
time = time || 500;
clearInterval(core.interval.tipAnimate);
core.saveCanvas('data');
core.clearMap('data', 0, 0, 416, 416);
var block = core.getBlock(x,y,core.status.floorId,false);
if (block==null) {// 不存在
if (core.isset(callback)) callback();
return;
}
// 否则则存在自定义事件。简单的将该点的enable置为false
blocks[index].enable = false;
// 需要删除该块
core.removeBlock(x,y);
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1.0);
block=block.block;
blockIcon = core.material.icons[block.event.cls][block.event.id];
blockImage = core.material.images[block.event.cls];
// 绘制data层
var opacityVal = 1;
core.setOpacity('data', opacityVal);
core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
// 要运行的轨迹将steps展开
var moveSteps=[];
steps.forEach(function (e) {
if (typeof e=="string") {
moveSteps.push(e);
}
else {
if (!core.isset(e.value)) {
moveSteps.push(e.direction)
}
else {
for (var i=0;i<e.value;i++) {
moveSteps.push(e.direction);
}
}
}
});
var nowX=32*x, nowY=32*y, step=0;
var scan = {
'up': {'x': 0, 'y': -1},
'left': {'x': -1, 'y': 0},
'down': {'x': 0, 'y': 1},
'right': {'x': 1, 'y': 0}
};
var animate=window.setInterval(function() {
// 已经移动完毕,消失
if (moveSteps.length==0) {
if (disappear) opacityVal=0;
else opacityVal -= 0.06;
core.setOpacity('data', opacityVal);
core.clearMap('data', nowX, nowY, 32, 32);
core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, nowX, nowY, 32, 32);
if (opacityVal<=0) {
clearInterval(animate);
core.loadCanvas('data');
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 1);
if (core.isset(callback)) callback();
}
}
else {
// 移动中
step++;
nowX+=scan[moveSteps[0]].x*2;
nowY+=scan[moveSteps[0]].y*2;
core.clearMap('data', nowX-32, nowY-32, 96, 96);
// 绘制
core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, nowX, nowY, 32, 32);
if (step==16) {
// 该移动完毕,继续
step=0;
moveSteps.shift();
}
}
}, time/16);
}
core.prototype.animateBlock = function (x,y,type,time,callback) {
if (type!='hide') type='show';
clearInterval(core.interval.tipAnimate);
core.saveCanvas('data');
core.clearMap('data', 0, 0, 416, 416);
var block = core.getBlock(x,y,core.status.floorId,false);
if (block==null) {// 不存在
if (core.isset(callback)) callback();
return;
}
// 清空UI
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1.0);
block=block.block;
blockIcon = core.material.icons[block.event.cls][block.event.id];
blockImage = core.material.images[block.event.cls];
var opacityVal = 0;
if (type=='hide') opacityVal=1;
core.setOpacity('data', opacityVal);
core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
var animate = window.setInterval(function () {
if (type=='show') opacityVal += 0.1;
else opacityVal -= 0.1;
core.setOpacity('data', opacityVal);
core.clearMap('data',block.x * 32, block.y * 32, 32, 32);
core.canvas.data.drawImage(blockImage, 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
if (opacityVal >=1 || opacityVal<=0) {
clearInterval(animate);
core.loadCanvas('data');
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 1);
if (core.isset(callback)) callback();
}
}, time/10);
}
// 显示一个事件
core.prototype.addBlock = function(x,y,floodId) {
floodId = floodId || core.status.floorId;
var block = core.getBlock(x,y,floodId,false);
if (block==null) return; // 不存在
block=block.block;
// 本身是禁用事件,启用之
if (core.isset(block.enable) && !block.enable) {
block.enable = true;
// 在本层,添加动画
if (floodId == core.status.floorId && core.isset(block.event)) {
blockIcon = core.material.icons[block.event.cls][block.event.id];
blockImage = core.material.images[block.event.cls];
core.canvas.event.drawImage(core.material.images[block.event.cls], 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
core.addGlobalAnimate(block.event.animate, block.x * 32, block.y * 32, blockIcon, blockImage);
core.setGlobalAnimate(core.values.animateSpeed);
}
}
}
core.prototype.removeBlock = function (x, y, floorId) {
@ -1758,52 +1837,17 @@ core.prototype.removeBlock = function (x, y, floorId) {
// 删除Index
core.removeBlockById(index, floorId);
/*
var mapBlocks = core.status.maps[floorId].blocks;
var blockIcon;
for (var b = 0; b < mapBlocks.length; b++) {
if (mapBlocks[b].x == x && mapBlocks[b].y == y) {
core.rmGlobalAnimate(x, y);
for (var m = 0; m < map.length; m++) {
if (!core.isset(mapBlocks[b][map[m]])) {
continue;
}
blockIcon = core.material.icons[mapBlocks[b][map[m]].cls][mapBlocks[b][map[m]].id];
core.canvas[map[m]].clearRect(x * 32, y * 32, 32, 32);
// delete core.status.thisMap.blocks[b][map[m]];
}
core.status.thisMap.blocks.splice(b, 1);
break;
}
}
*/
core.updateFg();
}
core.prototype.removeBlockByIds = function (floorId, ids) {
ids.sort(function (a,b) {return b-a}).forEach(function (id) {
// core.status.maps[floorId].blocks.splice(id, 1);
core.removeBlockById(id, floorId);
});
}
core.prototype.noPass = function (x, y) {
if (x > 12 || y > 12 || x < 0 || y < 0) {
return true;
}
return core.noPassExists(x,y);
/*
if (x > 12 || y > 12 || x < 0 || y < 0) {
return true;
}
var mapBlocks = core.status.thisMap.blocks;
var noPass;
for (var b = 0; b < mapBlocks.length; b++) {
if (mapBlocks[b].x == x && mapBlocks[b].y == y) {
return noPass = (mapBlocks[b].event && mapBlocks[b].event.noPass) || (mapBlocks[b].bg && mapBlocks[b].bg.noPass);
}
}
*/
return x<0 || x>12 || y<0 || y>12 || core.noPassExists(x,y);
}
core.prototype.trigger = function (x, y) {
@ -1820,8 +1864,8 @@ core.prototype.trigger = function (x, y) {
// 转换楼层能否穿透
if (trigger=='changeFloor' && (core.status.autoHeroMove || core.status.autoStep<core.status.autoStepRoutes.length)) {
var canCross = core.flags.portalWithoutTrigger;
if (core.isset(mapBlocks[b].event.portalWithoutTrigger))
canCross=mapBlocks[b].event.portalWithoutTrigger;
if (core.isset(mapBlocks[b].event.data) && core.isset(mapBlocks[b].event.data.portalWithoutTrigger))
canCross=mapBlocks[b].event.data.portalWithoutTrigger;
if (canCross) continue;
}
core.material.events[trigger](mapBlocks[b], core, function (data) {
@ -1907,7 +1951,7 @@ core.prototype.setBoxAnimate = function () {
core.drawBoxAnimate(background);
core.interval.boxAnimate = setInterval(function () {
core.drawBoxAnimate(background);
}, core.firstData.animateSpeed);
}, core.values.animateSpeed);
}
}
@ -2122,7 +2166,7 @@ core.prototype.drawTip = function (text, itemIcon) {
core.timeout.getItemTipTimeout = window.setTimeout(function () {
hide = true;
core.timeout.getItemTipTimeout = null;
}, 1000);
}, 750);
}
opacityVal = 0.6;
core.setOpacity('data', opacityVal);
@ -2173,157 +2217,36 @@ core.prototype.drawText = function (contents, callback) {
/////////// 地图相关 END ///////////
/*
* NPC事件
*/
core.prototype.visitNpc = function (npcId, x, y, callback) {
// 正在移动中...
if (!core.status.heroStop) {
setTimeout(function () {
core.visitNpc(npcId, x, y, callback);
}, 30);
return;
}
if (!core.isset(core.status.npcs[npcId]))
core.status.npcs[npcId] = 0;
var times=core.status.npcs[npcId];
var list = core.npcs.getEffect(npcId, times);
if (list.length==0) return;
core.status.event.data = {'x': x, 'y': y, 'id': npcId, 'list': list, 'callback': callback};
core.status.event.id = 'npc';
core.lockControl();
core.npcAction();
}
core.prototype.npcAction = function() {
if (core.status.event.data.list.length==0) {
if (core.isset(core.status.event.data.callback))
core.status.event.data.callback();
core.ui.closePanel(false);
return;
}
var data = core.status.event.data.list.shift();
core.status.event.data.current = data;
var id=core.status.event.data.id, x=core.status.event.data.x, y=core.status.event.data.y;
// 对话
if (data.action=='text') {
core.ui.drawTextBox(data.content, core.isset(data.isHero)&&data.isHero?'hero':data.id);
return;
}
// 显示选项
if (data.action=='choices') {
var npc = core.material.npcs[data.id];
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
clearInterval(core.interval.tipAnimate);
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 1);
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1);
core.setFillStyle('ui', background);
var left = 97, top = 64, right = 416 - 2 * left, bottom = 416 - 2 * top;
core.fillRect('ui', left, top, right, bottom, background);
core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2);
// 名称
core.canvas.ui.textAlign = "center";
core.fillText('ui', npc.name, left + 135, top + 34, '#FFFFFF', 'bold 19px Verdana');
// 动画
core.strokeRect('ui', left + 15 - 1, top + 30 - 1, 34, 34, '#DDDDDD', 2);
core.status.boxAnimateObjs = [];
core.status.boxAnimateObjs.push({
'bgx': left + 15, 'bgy': top + 30, 'bgsize': 32,
'image': core.material.images.npcs,
'x': left + 15, 'y': top + 30, 'icon': core.material.icons.npcs[npc.icon]
});
core.setBoxAnimate();
// 对话
core.canvas.ui.textAlign = "left";
var contents = data.hint.split('\n');
for (var i=0;i<contents.length;i++) {
core.fillText('ui', contents[i], left+60, top+65+18*i, '#FFFFFF', 'bold 14px Verdana');
}
// 选项
core.canvas.ui.textAlign = "center";
for (var i = 0; i < data.choices.length; i++) {
core.fillText('ui', data.choices[i].text, 208, top + 120 + 32 * i, "#FFFFFF", "bold 17px Verdana");
}
if (!(core.isset(data.cancel) && !data.cancel))
core.fillText('ui', "返回", 208, top + 248);
return;
}
// 添加访问次数
if (data.action == 'addtimes') {
core.status.npcs[id]++;
core.npcAction();
return;
}
// 设置访问次数
if (data.action == 'settimes') {
core.status.npcs[id]=data.times;
core.npcAction();
return;
}
// 消失
if (data.action == 'disappear') {
core.removeBlock(x, y);
core.npcAction();
return;
}
// 立刻再次访问
if (data.action == 'revisit') {
core.ui.closePanel(false);
core.visitNpc(id, x, y)
return;
}
// 自定义事件
if (data.action == 'custom') {
core.events.npcCustomAction(data);
return;
}
return;
}
core.prototype.npcEffect = function (effect, npc) {
// 自定义事件
if (effect.indexOf('custom:')==0) {
core.events.npcCustomEffect(effect.substr(7), npc);
return;
}
effects = effect.split(';');
effects.forEach(function (e) {
var ones = e.split(',');
var type = ones[0], key = ones[1], value = ones[2];
if (type == 'status') {
core.setStatus(key, core.getStatus(key)+parseInt(value));
}
else if (type == 'item') {
core.setItem(key, core.itemCount(key)+parseInt(value));
}
});
core.updateStatusBar();
}
/**
* 系统机制 start
*/
// 替换文本中的数为实际值
core.prototype.replaceText = function (text) {
return text.replace(/\${([^}]+)}/g, function (word, value) {
return core.calValue(value);
});
}
core.prototype.calValue = function (value) {
value=value.replace(/status:([\w\d_]+)/g, "core.getStatus('$1')");
value=value.replace(/item:([\w\d_]+)/g, "core.itemCount('$1')");
value=value.replace(/flag:([\w\d_]+)/g, "core.getFlag('$1', false)");
return eval(value);
}
core.prototype.unshift = function (a,b) {
if (!(a instanceof Array) || !core.isset(b)) return;
if (b instanceof Array) {
core.clone(b).reverse().forEach(function (e) {
a.unshift(e);
});
}
else a.unshift(b);
return a;
}
core.prototype.setLocalStorage = function(key, value) {
try {
localStorage.setItem(core.firstData.name + "_" + key, JSON.stringify(value));
@ -2362,6 +2285,10 @@ core.prototype.clone = function (data) {
}
return copy;
}
// 函数
if (data instanceof Function) {
return data;
}
// object
if (data instanceof Object) {
var copy={};
@ -2511,6 +2438,10 @@ core.prototype.doSL = function (id, type) {
core.drawTip("无效的存档");
return;
}
if (data.version != core.firstData.version) {
core.drawTip("存档版本不匹配");
return;
}
core.ui.closePanel();
core.loadData(data, function() {
core.setLocalStorage('savePage', core.status.savePage);
@ -2525,16 +2456,16 @@ core.prototype.saveData = function(dataId) {
'floorId': core.status.floorId,
'hero': core.clone(core.status.hero),
'hard': core.status.hard,
'values': core.clone(core.values),
'maps': core.maps.save(core.status.maps),
'shops': {},
'version': core.firstData.version,
"time": new Date().getTime()
};
// set shop times
for (var shop in core.status.shops) {
data.shops[shop]={
'times': core.status.shops[shop].times,
'visited': core.status.shops[shop].visited
'times': core.status.shops[shop].times || 0,
'visited': core.status.shops[shop].visited || false
}
}
core.events.beforeSaveData(data);
@ -2544,8 +2475,7 @@ core.prototype.saveData = function(dataId) {
core.prototype.loadData = function (data, callback) {
core.resetStatus(data.hero, data.hard, data.floorId, data.values,
core.maps.load(data.maps));
core.resetStatus(data.hero, data.hard, data.floorId, core.maps.load(data.maps));
// load shop times
for (var shop in core.status.shops) {
@ -2562,15 +2492,11 @@ core.prototype.loadData = function (data, callback) {
}
core.prototype.setStatus = function (statusName, statusVal) {
if (core.isset(core.status.hero[statusName])) {
core.status.hero[statusName] = statusVal;
}
core.status.hero[statusName] = statusVal;
}
core.prototype.getStatus = function (statusName) {
if (core.isset(core.status.hero[statusName])) {
return core.status.hero[statusName];
}
return core.status.hero[statusName];
}
core.prototype.setFlag = function(flag, value) {
@ -2610,6 +2536,7 @@ core.prototype.playSound = function (soundName, soundType) {
if (!core.musicStatus.soundStatus || !core.musicStatus.loaded) {
return;
}
if (!core.isset(core.material.sounds[soundType][soundName])) return;
core.musicStatus.playedSound = core.material.sounds[soundType][soundName];
core.musicStatus.playedSound.play();
}

View File

@ -4,88 +4,81 @@ function data() {
data.prototype.init = function() {
this.firstData = {
'title': '魔塔样板',
'name': 'template',
'version': 'Ver 1.0.0 (Beta)',
'floorId': 'sample0',
'hero': {
'id': 'hero1',
'name': '勇士',
'hp': 1000,
'atk': 100,
'def': 100,
'mdef': 0,
'money': 0,
'experience': 0,
'items': {
'keys': {
'yellowKey': 0,
'blueKey': 0,
'redKey': 0
"title": "魔塔样板", // 游戏名,将显示在标题页面以及切换楼层的界面中
"name": "template", // 游戏的唯一英文标识符。由英文、数字、下划线组成不能超过20个字符。
"version": "Ver 1.0.0 (Beta)", // 当前游戏版本;版本不一致的存档不能通用。
"floorId": "sample0", // 初始楼层ID
"hero": { // 勇士初始数据
"id": "hero1", // 此项关系到icons中的heroID一般不要改
"name": "勇士", // 勇士名;可以改成喜欢的
"hp": 1000, // 初始生命值
"atk": 100, // 初始攻击
"def": 100, // 初始防御
"mdef": 100, // 初始魔防
"money": 100, // 初始金币
"experience": 1000, // 初始经验
"items": { // 初始道具个数
"keys": {
"yellowKey": 0,
"blueKey": 0,
"redKey": 0
},
'constants': {},
'tools': {}
"constants": {},
"tools": {}
},
'flyRange': [],
'loc': {'direction': 'up', 'x': 6, 'y': 10},
'flags': {
// 游戏过程中的flag变量
"flyRange": [], // 初始可飞的楼层;一般留空数组即可
"loc": {"direction": "up", "x": 6, "y": 10}, // 勇士初始位置
"flags": { // 游戏过程中的变量或flags
"poison": false, // 毒
"weak": false, // 衰
"curse": false, // 咒
}
},
'startText': [
"startText": [ // 游戏开始前剧情。如果无剧情直接留一个空数组即可。
"Hi欢迎来到 HTML5 魔塔样板!\n\n本样板由艾之葵制作可以让你在不会写任何代码\n的情况下也能做出属于自己的H5魔塔",
"这里游戏开始时的剧情。\n定义在data.js的startText处。\n\n你可以在这里写上自己的内容。",
"赶快来试一试吧!"
],
'shops': {
'shop1': {
'id': 'shop1', 'title': '贪婪之神', 'name': '3楼金币商店', 'icon': 'blueShop',
'times': 0, 'need': "25", 'visited': false, 'use': 'money',
'choices': [
{'text': '生命+800', 'effect': 'status,hp,800'},
{'text': '攻击+4', 'effect': 'status,atk,4'},
{'text': '防御+4', 'effect': 'status,def,4'}
"shops": { // 定义全局商店(即快捷商店)
"moneyShop1": { // 商店唯一ID
"name": "贪婪之神", // 商店名称(标题)
"icon": "blueShop", // 商店图标blueShop为蓝色商店pinkShop为粉色商店
"textInList": "3楼金币商店", // 在快捷商店栏中显示的名称
"use": "money", // 商店所要使用的。只能是"money"或"experience"。
"need": "20+10*times*(times+1)", // 商店需要的金币/经验数值可以是一个表达式以times作为参数计算。
// 这里用到的times为该商店的已经的访问次数。首次访问该商店时times的值为0。
// 上面的例子是50层商店的计算公式。你也可以写任意其他的计算公式只要以times作为参数即可。
// 例如: "need": "25" 就是恒定需要25金币的商店 "need": "20+2*times" 就是第一次访问要20金币以后每次递增2金币的商店。
// 如果是对于每个选项有不同的计算公式,写 "need": "-1" 即可。可参见下面的经验商店。
"choices": [ // 商店的选项
{"text": "生命+800", "effect": "status:hp+=800"},
// 如果有多个effect以分号分开参见下面的经验商店
{"text": "攻击+4", "effect": "status:atk+=4"},
{"text": "防御+4", "effect": "status:def+=4"},
{"text": "魔防+10", "effect": "status:mdef+=10"}
// effect只能对status和items进行操作不能修改flag值。
// 中间只能用+=符号(也就是只能增加某个属性或道具)
// 其他effect样例
// "items:yellowKey+=1" 黄钥匙+1
// "items:pickaxe+=3" 破墙镐+3
]
},
'shop2': {
'id': 'shop2', 'title': '经验之神', 'name': '5楼经验商店', 'icon': 'redShop',
'times': 0, 'need': -1, 'visited': false, 'use': 'experience',
'choices': [
{'text': '攻击+5', 'effect': 'status,atk,5', 'need': '30'},
{'text': '防御+5', 'effect': 'status,def,5' ,'need': '30'},
{'text': '等级+1', 'effect': 'status,hp,1000;status,atk,7;status,def,7', 'need': '100'}
"expShop1": { // 商店唯一ID
"name": "经验之神",
"icon": "pinkShop",
"textInList": "5楼经验商店",
"use": "experience", // 该商店使用的是经验进行计算
"need": "-1", // 如果是对于每个选项所需要的数值不同,这里直接写-1然后下面选项里给定具体数值
"choices": [
// 在choices中写need可以针对每个选项都有不同的需求。
// 这里的need同样可以以times作为参数比如 "need": "100+20*times"
{"text": "等级+1", "need": "100", "effect": "status:hp+=1000;status:atk+=7;status:def+=7"},
// 多个effect直接以分号分开即可。如上面的意思是生命+1000攻击+7防御+7。
{"text": "攻击+5", "need": "30", "effect": "status:atk+=5"},
{"text": "防御+5", "need": "30", "effect": "status:def+=5"},
]
},
'shop3': {
'id': 'shop3', 'title': '钥匙商人', 'name': '6楼钥匙商人', 'icon': 'womanMagician',
'times': 0, 'need': -1, 'visited': false, 'use': 'money',
'choices': [
{'text': '黄钥匙+1', 'effect': 'item,yellowKey,1', 'need': '10'},
{'text': '蓝钥匙+1', 'effect': 'item,blueKey,1', 'need': '50'},
{'text': '红钥匙+1', 'effect': 'item,redKey,1', 'need': '100'}
]
},
'shop4': {
'id': 'shop4', 'title': '贪婪之神', 'name': '10楼金币商店', 'icon': 'blueShop',
'times': 0, 'need': "100", 'visited': false, 'use': 'money',
'choices': [
{'text': '生命+4000', 'effect': 'status,hp,4000'},
{'text': '攻击+20', 'effect': 'status,atk,20'},
{'text': '防御+20', 'effect': 'status,def,20'}
]
},
'shop5': {
'id': 'shop5', 'title': '经验之神', 'name': '15楼经验商店', 'icon': 'redShop',
'times': 0, 'need': -1, 'visited': false, 'use': 'experience',
'choices': [
{'text': '攻击+17', 'effect': 'status,atk,17', 'need': '95'},
{'text': '防御+17', 'effect': 'status,def,17' ,'need': '95'},
{'text': '等级+3', 'effect': 'status,hp,3000;status,atk,21;status,def,21', 'need': '270'}
]
},
}
},
'npcs': {},
'animateSpeed': 500,
}
// 各种数值;一些数值可以在这里设置
this.values = {
@ -114,19 +107,22 @@ data.prototype.init = function() {
"sword5": 160, // 神圣剑加攻数值
"shield5": 160, // 神圣盾加防数值
"moneyPocket": 500, // 金钱袋加金币的数值
/****** 道具相关 ******/
'animateSpeed': 500,
}
// 系统FLAG在游戏运行中中请不要修改它。
this.flags = {
/****** 角色状态相关 ******/
"enableMDef": true, // 是否涉及勇士的魔防值如果此项为false则状态栏不会显示勇士的魔防值
"enableExperience": false, // 是否涉及经验值如果此项为false则状态栏和怪物手册均将不会显示经验值
"enableMDef": false, // 是否涉及勇士的魔防值如果此项为false则状态栏不会显示勇士的魔防值
"enableExperience": true, // 是否涉及经验值如果此项为false则状态栏和怪物手册均将不会显示经验值
// 重要说明如果enableMDef和enableExperience均为true则在状态栏不会显示当前楼层 //
/****** 道具相关 ******/
"flyNearStair": false, // 是否需要在楼梯边使用传送器
"flyNearStair": true, // 是否需要在楼梯边使用传送器
"bombTrigger": true, // 使用炸弹后是否触发怪物事件(如开门)
"pickaxeFourDirections": true, // 使用破墙镐是否四个方向都破坏如果false则只破坏面前的墙壁
"bigKeyIsBox": false, // 如果此项为true则视为钥匙盒红黄蓝钥匙+1若为false则视为大黄门钥匙

View File

@ -23,9 +23,9 @@ enemys.prototype.init = function () {
'slimeMan': {'name': '影子战士', 'hp': 100, 'atk': 0, 'def': 0, 'money': 11, 'experience': 0, 'special': 10}, // 模仿怪的攻防设为0就好
'bluePriest': {'name': '初级法师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 3, 'experience': 0, 'special': 2},
'redPriest': {'name': '高级法师', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100},
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100}, // 领域怪需要加value表示领域伤害的数值
'redWizard': {'name': '高级巫师', 'hp': 1000, 'atk': 1200, 'def': 0, 'money': 160, 'experience': 0, 'special': 15, 'value': 200},
'yellowGuard': {'name': '初级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'yellowGuard': {'name': '初级卫兵', 'hp': 100, 'atk': 120, 'def': 0, 'money': 10, 'experience': 0, 'special': 0},
'blueGuard': {'name': '中级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'redGuard': {'name': '高级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'swordsman': {'name': '双手剑士', 'hp': 100, 'atk': 120, 'def': 0, 'money': 6, 'experience': 0, 'special': 5},
@ -33,7 +33,7 @@ enemys.prototype.init = function () {
'yellowKnight': {'name': '金骑士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'redKnight': {'name': '红骑士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'darkKnight': {'name': '黑骑士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'blackKing': {'name': '黑衣魔王', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0, 'bomb': false}, // 加入 'bomb': false 代表该怪物不可被炸弹或圣锤炸掉
'blackKing': {'name': '黑衣魔王', 'hp': 1000, 'atk': 500, 'def': 0, 'money': 1000, 'experience': 1000, 'special': 0, 'bomb': false}, // 加入 'bomb': false 代表该怪物不可被炸弹或圣锤炸掉
'yellowKing': {'name': '黄衣魔王', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'greenKing': {'name': '青衣武士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'blueKnight': {'name': '蓝骑士', 'hp': 100, 'atk': 120, 'def': 0, 'money': 9, 'experience': 0, 'special': 8},
@ -102,14 +102,12 @@ enemys.prototype.getDamage = function (monsterId) {
var hero_atk = core.status.hero.atk, hero_def = core.status.hero.def, hero_mdef = core.status.hero.mdef;
var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_special = monster.special;
var damage = this.calDamage(hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special);
if (damage == 999999999) return damage;
var extra_damage = 0;
if (monster.special == 11) { // 吸血
// 吸血的比例
extra_damage = core.status.hero.hp * monster.value;
if (core.status.hero.flags.hasShield5) // 存在神圣盾
extra_damage /= 2;
extra_damage = parseInt(extra_damage);
}
return damage + extra_damage;
@ -167,7 +165,7 @@ enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mo
mon_atk = hero_atk;
mon_def = hero_def;
}
if (hero_atk <= mon_def) return 999999999;
if (hero_atk <= mon_def) return 999999999; // 不可战斗时请直接返回999999999
var per_damage = mon_atk - hero_def;
if (per_damage < 0) per_damage = 0;

View File

@ -9,15 +9,6 @@ events.prototype.init = function () {
if (core.isset(callback))
callback();
},
'changeFloor': function (data, core, callback) {
// core.changeFloor(data.event.data.floorId, data.event.data.heroLoc);
var heroLoc = null;
if (core.isset(data.event.data.loc)) {
heroLoc = {'x': data.event.data.loc[0], 'y': data.event.data.loc[1]};
}
core.changeFloor(data.event.data.floorId, data.event.data.stair,
heroLoc, callback);
},
'getItem': function (data, core, callback) {
core.getItem(data.event.id, 1, data.x, data.y);
if (core.isset(callback))
@ -28,10 +19,13 @@ events.prototype.init = function () {
if (core.isset(callback))
callback();
},
'visitNpc': function (data, core, callback) {
core.visitNpc(data.event.npcid, data.x, data.y);
if (core.isset(callback))
callback();
'changeFloor': function (data, core, callback) {
var heroLoc = null;
if (core.isset(data.event.data.loc)) {
heroLoc = {'x': data.event.data.loc[0], 'y': data.event.data.loc[1]};
}
core.changeFloor(data.event.data.floorId, data.event.data.stair,
heroLoc, callback);
},
'openShop': function (data, core, callback) {
core.ui.drawShop(data.event.shopid);
@ -45,6 +39,8 @@ events.prototype.init = function () {
},
"checkBlock": function (data, core, callback) {
core.events.checkBlock(data.x, data.y);
if (core.isset(callback))
callback();
},
'action': function (data, core, callback) {
core.events.doEvents(data.event.data, data.x, data.y);
@ -68,13 +64,44 @@ main.instance.events = new events();
////// 游戏开始事件 //////
events.prototype.startGame = function (hard) {
if (core.status.isStarting) return;
core.status.isStarting = true;
core.hideStartAnimate(function() {
core.drawText(core.firstData.startText, function() {
core.drawText(core.clone(core.firstData.startText), function() {
core.startGame(hard);
// 可以在这里设置一些难度分歧
// 例如,简单难度下改变初始生命可以调用:
// if (hard=='Easy') core.setStatus("hp", 10000);
// 具体的各种API参见doc文档
});
})
}
////// 游戏结束事件 //////
events.prototype.win = function(reason) {
// 获胜
core.waitHeroToStop(function() {
core.rmGlobalAnimate(0,0,true);
core.clearMap('all'); // 清空全地图
core.drawText([
"\t[结局2]恭喜通关!你的分数是${status:hp}。"
], function () {
core.restart();
})
});
}
events.prototype.lose = function(reason) {
// 失败
core.waitHeroToStop(function() {
core.drawText('\t[结局1]你死了。', function () {
core.restart();
});
})
}
////// 检查领域、夹击事件 //////
events.prototype.checkBlock = function (x,y) {
var damage = 0;
@ -121,7 +148,7 @@ events.prototype.checkBlock = function (x,y) {
////// 转换楼层结束的事件 //////
events.prototype.afterChangeFloor = function (floorId) {
if (!core.hasFlag("visited_"+floorId)) {
this.doEvents(core.status.thisMap.firstArrive);
this.doEvents(core.floors[floorId].firstArrive);
core.setFlag("visited_"+floorId, true);
}
}
@ -136,13 +163,18 @@ events.prototype.doEvents = function (list, x, y, callback) {
}
core.lockControl();
core.status.event = {'id': 'action', 'data': {
'list': list, 'x': x, 'y': y, 'callback': callback
'list': core.clone(list), 'x': x, 'y': y, 'callback': callback
}}
core.events.doAction();
});
}
events.prototype.doAction = function() {
// 清空boxAnimate和UI层
clearInterval(core.interval.boxAnimate);
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1.0);
// 事件处理完毕
if (core.status.event.data.list.length==0) {
if (core.isset(core.status.event.data.callback))
@ -150,6 +182,7 @@ events.prototype.doAction = function() {
core.ui.closePanel(false);
return;
}
var data = core.status.event.data.list.shift();
core.status.event.data.current = data;
@ -168,14 +201,158 @@ events.prototype.doAction = function() {
case "text": // 文字/对话
core.ui.drawTextBox(data.data);
break;
case "show": // 显示
if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) {
core.animateBlock(data.loc[0],data.loc[1],'show', data.time, function () {
core.addBlock(data.loc[0],data.loc[1],data.floorId);
core.events.doAction();
});
}
else {
core.addBlock(data.loc[0],data.loc[1],data.floorId)
this.doAction();
}
break;
case "hide": // 消失
core.removeBlock(x, y);
var toX=x, toY=y, toId=core.status.floorId;
if (core.isset(data.loc)) {
toX=data.loc[0]; toY=data.loc[1];
}
if (core.isset(data.floorId)) toId=data.floorId;
core.removeBlock(toX,toY,toId)
if (core.isset(data.time) && data.time>0 && toId==core.status.floorId) {
core.animateBlock(toX,toY,'hide',data.time, function () {
core.events.doAction();
});
}
else this.doAction();
break;
case "move": // 移动事件
core.moveBlock(x,y,data.steps,data.time,data.disappear,function() {
core.events.doAction();
})
break;
case "changeFloor": // 楼层转换
core.changeFloor(data.floorId||core.status.floorId, null, {"x": data.loc[0], "y": data.loc[1]}, function() {
core.lockControl();
core.events.doAction();
});
break;
case "changePos": // 直接更换勇士位置,不切换楼层
core.clearMap('hero', 0, 0, 416, 416);
core.setHeroLoc('x', data.loc[0]);
core.setHeroLoc('y', data.loc[1]);
core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop');
this.doAction();
break;
case "openDoor": // 开一个门,包括暗墙
var floorId=data.floorId || core.status.floorId;
var block=core.getBlock(data.loc[0], data.loc[1], floorId);
if (block!=null) {
if (floorId==core.status.floorId)
core.openDoor(block.block.event.id, block.block.x, block.block.y, false, function() {
core.events.doAction();
})
else {
core.removeBlock(block.block.x,block.block.y,floorId);
this.doAction();
}
break;
}
this.doAction();
break;
case "openShop": // 打开一个全局商店
core.events.openShop(data.id);
break;
case "battle": // 强制战斗
core.battle(data.id,null,null,true,function() {
core.events.doAction();
})
break;
case "trigger": // 触发另一个事件;当前事件会被立刻结束。需要另一个地点的事件是有效的
var toX=data.loc[0], toY=data.loc[1], toId=data.floorId;
var block=core.getBlock(toX, toY, toId);
if (block!=null) {
block = block.block;
if (core.isset(block.event) && block.event.trigger=='action') {
// 触发
core.status.event = {'id': 'action', 'data': {
'list': core.clone(block.event.data), 'x': block.x, 'y': block.y, 'callback': core.status.event.data.callback
}}
}
}
this.doAction();
break;
case "playSound":
var name=data.name.split(".");
if (name.length==2)
core.playSound(name[0],name[1]);
this.doAction();
break;
case "setValue":
try {
var value=core.calValue(data.value);
// 属性
if (data.name.indexOf("status:")==0) {
value=parseInt(value);
core.setStatus(data.name.substring(7), value);
}
// 道具
if (data.name.indexOf("item:")==0) {
value=parseInt(value);
var itemId=data.name.substring(5);
if (value>core.itemCount(itemId)) // 效果
core.getItem(itemId,value-core.itemCount(itemId));
else core.setItem(itemId, value);
}
// flag
if (data.name.indexOf("flag:")==0) {
core.setFlag(data.name.substring(5), value);
}
}
catch (e) {console.log(e)}
core.updateStatusBar();
this.doAction();
break;
case "if": // 条件判断
if (core.calValue(data.condition))
core.events.insertAction(data.true)
else
core.events.insertAction(data.false)
this.doAction();
break;
case "choices": // 提供选项
core.ui.drawChoices(data.text, data.choices);
break;
case "win":
core.events.win(data.reason);
break;
case "lose":
core.events.lose(data.reason);
break;
case "function":
if (core.isset(data.function))
data.function();
this.doAction();
break;
case "update":
core.updateStatusBar();
this.doAction();
break;
case "sleep": // 等待多少毫秒
setTimeout(function () {
core.events.doAction();
}, data.data);
}, data.time);
break;
case "revisit": // 立刻重新执行该事件
var block=core.getBlock(x,y); // 重新获得事件
if (block!=null) {
block = block.block;
if (core.isset(block.event) && block.event.trigger=='action') {
core.status.event.data.list = core.clone(block.event.data);
}
}
this.doAction();
break;
case "exit": // 立刻结束事件
core.status.event.data.list = [];
@ -188,7 +365,51 @@ events.prototype.doAction = function() {
return;
}
////// 往当前事件列表之前添加一个或多个事件 //////
events.prototype.insertAction = function (action) {
core.unshift(core.status.event.data.list, action)
}
////// 打开商店 //////
events.prototype.openShop = function(shopId, needVisited) {
var shop = core.status.shops[shopId];
shop.times = shop.times || 0;
shop.visited = shop.visited || false;
if (needVisited && !shop.visited) {
if (shop.times==0) core.drawTip("该商店尚未开启");
else core.drawTip("该商店已失效");
return;
}
shop.visited = true;
core.ui.closePanel();
core.lockControl();
core.status.event = {'id': 'shop', 'data': {'id': shopId, 'shop': shop}};
// 拼词
var content = "\t["+shop.name+","+shop.icon+"]";
var times = shop.times, need=eval(shop.need);
if (need<0) need="若干";
var use=shop.use=="experience"?"经验":"金币";
content = content+"勇敢的武士啊,给我"+need+"\n"+use+",你就可以:"
var choices = [];
for (var i=0;i<shop.choices.length;i++) {
var choice = shop.choices[i];
var text = choice.text;
if (core.isset(choice.need))
text += ""+eval(choice.need)+use+""
choices.push({"text": text});
}
choices.push({"text": "离开"});
core.ui.drawChoices(content, choices);
}
events.prototype.disableQuickShop = function (shopId) {
core.status.shops[shopId].visited = false;
}
////// 降低难度 //////
/*
events.prototype.decreaseHard = function() {
if (core.status.hard == 0) {
core.drawTip("当前已是难度0不能再降低难度了");
@ -208,6 +429,7 @@ events.prototype.decreaseHard = function() {
core.ui.drawSettings(false);
});
}
*/
////// 能否使用快捷商店 //////
events.prototype.canUseQuickShop = function(shopIndex) {
@ -256,6 +478,12 @@ events.prototype.afterBattle = function(enemyId,x,y,callback) {
core.updateStatusBar();
}
// 如果已有事件正在处理中
if (core.status.lockControl) {
if (core.isset(callback)) callback();
return;
}
// 检查处理后的事件。
var event = core.floors[core.status.floorId].afterBattle[x+","+y];
if (core.isset(event)) {
@ -270,6 +498,13 @@ events.prototype.afterBattle = function(enemyId,x,y,callback) {
/****** 开完门 ******/
events.prototype.afterOpenDoor = function(doorId,x,y,callback) {
// 如果已有事件正在处理中
if (core.status.lockControl) {
if (core.isset(callback)) callback();
return;
}
// 检查处理后的事件。
var event = core.floors[core.status.floorId].afterOpenDoor[x+","+y];
if (core.isset(event)) {
@ -286,7 +521,7 @@ events.prototype.afterOpenDoor = function(doorId,x,y,callback) {
events.prototype.passNet = function (data) {
// 有鞋子
if (core.hasItem('shoes')) return;
if (data.event.id=='lavaNet') {
if (data.event.id=='lavaNet') { // 血网
core.status.hero.hp -= core.values.lavaDamage;
if (core.status.hero.hp<=0) {
core.status.hero.hp=0;
@ -294,41 +529,23 @@ events.prototype.passNet = function (data) {
core.events.lose('lava');
return;
}
core.updateStatusBar();
core.drawTip('经过血网,生命-'+core.values.lavaDamage);
}
if (data.event.id=='poisonNet') {
if (data.event.id=='poisonNet') { // 毒网
if (core.hasFlag('poison')) return;
core.setFlag('poison', true);
core.updateStatusBar();
}
if (data.event.id=='weakNet') {
if (data.event.id=='weakNet') { // 衰网
if (core.hasFlag('weak')) return;
core.setFlag('weak', true);
core.status.hero.atk-=core.values.weakValue;
core.status.hero.def-=core.values.weakValue;
core.updateStatusBar();
}
if (data.event.id=='curseNet') {
if (data.event.id=='curseNet') { // 咒网
if (core.hasFlag('curse')) return;
core.setFlag('curse', true);
core.updateStatusBar();
}
}
// NPC自定义操作
events.prototype.npcCustomAction = function (npcData) {
}
// 当点击(x,y)位置后自定义操作
events.prototype.npcCustomActionOnClick = function (npcData, x, y) {
}
// NPC自定义事件处理
events.prototype.npcCustomEffect = function (effect, npc) {
core.updateStatusBar();
}
// 存档事件前一刻的处理
@ -341,31 +558,6 @@ events.prototype.afterLoadData = function(data) {
}
events.prototype.win = function(reason) {
// 获胜
core.waitHeroToStop(function() {
core.clearMap('all');
core.rmGlobalAnimate(0,0,true);
core.drawText([
"\t[结局3]恭喜通关!"
], function () {
core.restart();
})
});
}
events.prototype.lose = function(reason) {
// 失败
core.waitHeroToStop(function() {
core.drawText('\t[结局1]你死了。', function () {
core.restart();
});
})
}
/******************************************/
/*********** 界面上的点击事件 ***************/
@ -379,7 +571,19 @@ events.prototype.clickAction = function (x,y) {
this.doAction();
return;
}
if (core.status.event.data.type=='choices') {
// 选项
var data = core.status.event.data.current;
var choices = data.choices;
if (choices.length==0) return;
if (x >= 5 && x <= 7) {
var topIndex = 6 - parseInt((choices.length - 1) / 2);
if (y>=topIndex && y<topIndex+choices.length) {
this.insertAction(choices[y-topIndex].action);
this.doAction();
}
}
}
}
// 怪物手册
@ -416,22 +620,19 @@ events.prototype.clickFly = function(x,y) {
// 商店
events.prototype.clickShop = function(x,y) {
if (core.status.event.data == null) {
console.log("发生错误,商店不存在?");
return;
}
var shop = core.status.event.data.shop;
var choices = shop.choices;
if (x >= 5 && x <= 7) {
if (y >= 5 && y <= 8) {
if (y >= 5 + core.status.event.data.choices.length) return;
var topIndex = 6 - parseInt(choices.length / 2);
if (y>=topIndex && y<topIndex+choices.length) {
//this.insertAction(choices[y-topIndex].action);
//this.doAction();
var money = core.getStatus('money'), experience = core.getStatus('experience');
var shop = core.status.event.data;
var times = shop.times, need = eval(shop.need);
var use = shop.use;
var use_text = use=='money'?"金币":"经验";
var choice = shop.choices[y-5];
var choice = choices[y-topIndex];
if (core.isset(choice.need))
need = eval(choice.need);
@ -441,22 +642,30 @@ events.prototype.clickShop = function(x,y) {
}
eval(use+'-='+need);
core.setStatus('money', money);
core.setStatus('experience', experience);
// 更新属性
choice.effect.split(";").forEach(function (t) {
if (t.indexOf("status:")==0) {
eval(t.replace("status:", "core.status.hero."));
}
else if (t.indexOf("item:")==0) {
eval(t.replace("item:", "core.getItem('").replace("+=", "', ")+")");
}
});
core.updateStatusBar();
core.npcEffect(choice.effect);
core.status.event.data.times++;
core.ui.openShop(core.status.event.data.id);
return;
shop.times++;
this.openShop(core.status.event.data.id);
}
// 退出商店
if (y == 9) {
core.status.event.data = null;
core.ui.closePanel();
return;
// 离开
else if (y==topIndex+choices.length) {
core.status.boxAnimateObjs = [];
core.setBoxAnimate();
if (core.status.event.data.fromList)
core.ui.drawQuickShop();
else core.ui.closePanel();
}
}
}
@ -474,13 +683,9 @@ events.prototype.clickSelectShop = function(x,y) {
core.drawText(reason);
return;
}
var shop=shopList[keys[y - topIndex]];
if (!shop.visited) {
if (shop.times==0) core.drawTip('该商店尚未开启');
else core.drawTip('该商店已失效');
return;
}
core.ui.drawShop(keys[y - topIndex]);
this.openShop(keys[y - topIndex], true);
if (core.status.event.id=='shop')
core.status.event.data.fromList = true;
}
if (y == exitIndex) {
core.ui.closePanel();
@ -564,11 +769,19 @@ events.prototype.clickSettings = function (x,y) {
core.changeSoundStatus();
core.ui.drawSettings(false);
}
if (y == 4) core.ui.drawSelectShop();
if (y == 5) this.decreaseHard();
if (y == 6) {
if (y == 4) core.ui.drawQuickShop();
// if (y == 5) this.decreaseHard();
if (y == 5) {
core.ui.drawSyncSave();
}
if (y == 6) {
core.ui.drawConfirmBox("你确定要清空所有本地存档吗?", function() {
localStorage.clear();
core.drawText("\t[操作成功]你的本地所有存档已被清空。");
}, function() {
core.ui.drawSettings(false);
})
}
if (y == 7) {
core.ui.drawConfirmBox("你确定要重新开始吗?", function () {
core.ui.closePanel();
@ -585,67 +798,4 @@ events.prototype.clickSettings = function (x,y) {
return;
}
events.prototype.clickNPC = function(x,y) {
var data = core.status.event.data.current;
if (core.isset(data)) {
// 对话,任意位置继续
if (data.action == 'text') {
core.npcAction();
return;
}
if (data.action == 'choices') {
if (x >= 5 && x <= 7) {
if (y >= 5 && y <= 8) {
if (y >= 5 + data.choices.length) return;
var choice = data.choices[y - 5];
if (core.isset(choice.need)) {
var able = true;
choice.need.split(';').forEach(function (e) {
var ones = e.split(',');
var type = ones[0], key = ones[1], value = ones[2];
if (type == 'status') {
if (core.getStatus(key)<parseInt(value)) able=false;
}
else if (type == 'item') {
if (core.itemCount(key)<parseInt(value)) able=false;
}
});
if (!able) {
core.drawTip("无法选择此项");
return;
}
choice.need.split(';').forEach(function (e) {
var ones = e.split(',');
var type = ones[0], key = ones[1], value = ones[2];
if (type == 'status') {
core.setStatus(key, core.getStatus(key)-parseInt(value));
}
else if (type == 'item') {
core.setItem(key, core.itemCount(key)-parseInt(value));
}
});
}
core.npcEffect(choice.effect);
core.npcAction();
return;
}
// 退出商店
if (y == 9 && !(core.isset(data.cancel) && !data.cancel)) {
core.status.event.data = null;
core.ui.closePanel();
return;
}
}
}
if (data.action == 'custom') {
core.events.npcCustomActionOnClick(data, x, y);
return;
}
}
}
/*********** 点击事件 END ***************/

View File

@ -1,30 +1,38 @@
function MT0() {}
// 这里需要改楼层名请和文件名及下面的floorId保持完全一致
main.floors.MT0 = {
'floorId': 'MT0', // 楼层唯一标识符,需要和名字完全一致
'title': "主塔 0 层", // 楼层中文名
'name': 0, // 显示在状态栏中的名称
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
MT0.prototype.init = function() {
this.data = {
'floorId': 'MT0', // 楼层唯一标识符
'title': "样板层", // 楼层中文名;转换楼层时显示
'name': 0, // 显示在状态栏中的楼层名称
"canFlyTo": true, // 该楼能否被飞行器飞到(不能的话在该楼也不允许使用)
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
],
"firstArrive": [ // 第一次到该楼层触发的事件
],
"firstArrive": [ // 第一次到该楼层触发的事件
],
"events": { // 该楼的所有可能事件列表NPC事件和楼层转换事件也需要包括在内
],
"events": [ // 该楼的所有可能事件列表
/****** NPC事件 ******/
],
"afterOpenDoor": [ // 开完门后可能触发的事件列表
],
"afterBattle": [ // 战斗后可能触发的事件列表
/****** 楼层转换事件 ******/
],
"afterGetItem": [ // 获得道具后可能触发的事件列表
]
};
/****** 领域、夹击检查事件 ******/
// 所有可能的领域、夹击点均需要加上 {"trigger": "ckeckBlock"},否则将不会触发检查事件
// 另外如果该点已经存在events事件上面有相同点位置定义则会被覆盖
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
},
"afterBattle": { // 战斗后可能触发的事件列表
},
"afterGetItem": { // 获得道具后可能触发的事件列表
}
}
main.floors.MT0 = new MT0();

View File

@ -14,114 +14,107 @@ main.floors.sample0 = {
[224, 254, 212, 232, 204, 5, 0, 1, 31, 32, 34, 33, 36],
[201, 205, 217, 215, 207, 5, 0, 1, 27, 28, 29, 30, 35],
[5, 5, 125, 5, 5, 5, 0, 1, 21, 22, 23, 24, 25],
[45, 0, 0, 0, 0, 0, 0, 1, 1, 1, 121, 1, 1],
[0, 0, 0, 0, 0, 0, 45, 1, 1, 1, 121, 1, 1],
[4, 4, 126, 4, 4, 4, 0, 0, 0, 0, 0, 85, 124],
[87, 11, 12, 13, 14, 4, 4, 2, 2, 2, 122, 2, 2],
[88, 89, 90, 91, 92, 93, 94, 2, 81, 82, 83, 84, 86],
],
"firstArrive": [ // 第一次到该楼层触发的事件
"\t[样板提示]首次到达某层可以触发 firstArrive 事件,\n该事件可类似于RMXP中的“自动执行脚本”。",
"本事件支持一切的事件类型,常常用来触发对话,\n例如",
"\t[样板提示]首次到达某层可以触发 firstArrive 事件,\n该事件可类似于RMXP中的“自动执行脚本”。\n\n本事件支持一切的事件类型常常用来触发对话\n例如",
"\t[hero]我是谁?\n我从哪来\n我又要到哪去",
"\t[仙子,fairy]你问我...?我也不知道啊...",
"本层主要对道具、门、怪物等进行介绍,\n有关事件的各种信息在下一层会有更为详细的说明。",
],
"events": { // 该楼的所有可能事件列表NPC事件和楼层转换事件也需要包括在内
"events": { // 该楼的所有可能事件列表
/****** NPC事件 ******/
"10,9": [ // 守着道具的老人
"\t[老人,man]这些是本样板支持的所有的道具。\n\n道具分为三类items, constants, tools。\nitems 为即捡即用类道具,例如宝石、血瓶、\n剑盾等。\nconstants 为永久道具,例如怪物手册、楼层\n传送器、幸运金币等。\ntools 为消耗类道具,例如破墙镐、炸弹、中\n心对称飞行器等。\n\n后两类道具在工具栏中可以看到并使用。",
"\t[老人,man]有关道具效果定义在items.js中。\n目前大多数道具已有默认行为如有自定义\n的需求则需在items.js中修改代码。",
"\t[老人,man]constants 和 tools 各最多只允许12种\n多了会导致图标溢出。",
"\t[老人,man]拾取道具结束后可触发 afterGetItem 事件。\n\n有关事件的各种信息在下一层会有更为详细的\n说明。",
{"type": "hide"} // 消失
{"type": "hide", "time": 500} // 消失
],
"10,11": [ // 守着门的老人
"\t[老人,woman]这些是门,需要对应的钥匙打开。\n机关门必须使用特殊的开法。",
"\t[老人,woman]开门后可触发 afterOpenDoor 事件。\n\n有关事件的各种信息在下一层会有更为详细的\n说明。",
{'type': 'hide'}
{'type': 'hide', "time": 500}
],
"2,10": [ // 守着楼梯、传送门、路障的老人
"\t[老人,womanMagician]这些是路障、楼梯、传送门。",
"\t[老人,womanMagician]血网的伤害数值、中毒后每步伤害数值、衰弱\n时攻防下降的数值都在 data.js 内定义。\n\n路障同样会尽量被自动寻路绕过。",
"\t[老人,womanMagician]楼梯和传送门需要在events中定义目标楼层\n和位置,可参见样板里已有的的写法。",
"\t[老人,womanMagician]楼梯和传送门需要在changeFloor中定义目标\n楼层和位置,可参见样板里已有的的写法。",
"\t[老人,womanMagician]楼梯和传送门是否可“穿透”由data.js中的\n全局变量所决定你也可以单独设置。\n穿透的意思是自动寻路得到的路径中间经\n过了楼梯行走时是否触发楼层转换事件。\n例如下面的“下箭头”就是不能穿透的。",
{"type": "hide"}
{"type": "hide", "time": 500}
],
"2,8": [ // 守着第一批怪物的老人
"\t[老人,magician]这些都是各种各样的怪物\n所有怪物的数据都在enemys.js中设置。\n\n每个怪物最多只能有一个特殊属性。",
"\t[老人,magician]这些都是各种各样的怪物\n所有怪物的数据都在enemys.js中设置。\n\n每个怪物最多只能有一个特殊属性。",
"\t[老人,magician]这批怪物分别为:普通、先攻、魔攻、坚固、\n2连击、3连击、4连击、破甲、反击、净化。",
"\t[老人,magician]打败怪物后可触发 afterBattle 事件。\n\n有关事件的各种信息在下一层会有更为详细的\n说明。",
{"type": "hide"}
{"type": "hide", "time": 500}
],
"2,5": [
"2,5": [ // 守着第二批怪物的老人
"\t[老人,magician]模仿、吸血、中毒、衰弱、诅咒。\n\n请注意吸血怪需要设置value为吸血数值\n可参见样板中黑魔法师的写法。",
{"type": "hide"}
{"type": "hide", "time": 500}
],
"2,3": [
"2,3": [ // 守着第三批怪物额老人
"\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值\n可参见样板中初级巫师的写法。",
"\t[老人,magician]出于游戏性能的考虑,我们不可能每走一步都\n对领域和夹击进行检查。\n因此我们需要在本楼层的 events 中指明哪些\n点可能会触发领域和夹击事件在这些点才会\n对领域和夹击进行检查和处理。\n\n具体可参见本层样板中events的做法。",
"\t[老人,magician]夹击和领域同时发生时先计算领域,再夹击。\n\n另本塔不支持阻击怪。",
"\t[老人,magician]自动寻路会尽量绕过你设置的这些点。",
{"type": "hide"}
"\t[老人,magician]出于游戏性能的考虑,我们不可能每走一步都\n对领域和夹击进行检查。\n因此我们需要在本楼层的 checkBlock 中指明\n哪些点可能会触发领域和夹击事件在这些点\n才会对领域和夹击进行检查和处理。\n具体可参见本层 checkBlock 的写法。",
"\t[老人,magician]夹击和领域同时发生时先计算领域,再夹击。\n自动寻路同样会尽量绕过你设置的这些点。\n\n另本塔不支持阻击怪。",
{"type": "hide", "time": 500}
],
"12,10": { // 隐藏的仙子
"trigger": "action", "enable": false,
"enable": false, // enable: false代表初始时禁用事件
"data": [
"\t[仙子,fairy]只有楼上启用事件后,才能看到我并可以和我\n对话来触发事件。"
"\t[仙子,fairy]只有楼上启用事件后,才能看到我并可以和我\n对话来触发事件。",
{"type": "hide", "time": 500}
]
},
/****** 楼层转换事件 ******/
"6,0": {"trigger": "changeFloor", "data": {"floorId": "sample0", "stair": "downFloor"}}, // 目标点sample0层的下楼梯位置
"0,11": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [0,12]}}, // 目标点sample0层的x=0,y=12位置
"0,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "stair": "upFloor"}}, // 注意目标层有多个楼梯的话写stair可能会导致到达位置不确定。这时候推荐写loc指明目标点位置。
"1,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [1,12]}},
"2,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [2,12]}},
"3,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [6,1]}},
"4,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [0,9]}},
"5,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [6,10]}, "portalWithoutTrigger": false}, // 不能穿透
"6,12": {"trigger": "changeFloor", "data": {"floorId": "sample0", "loc": [10,10]}},
/****** 领域、夹击检查事件 ******/
// 所有可能的的领域、夹击点均需要加上 {"trigger": "ckeckBlock"},否则将不会触发检查事件
// 另外如果该点已经存在events事件上面有相同点位置定义则会被覆盖
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
"1,0": {"trigger": "checkBlock"},
"3,0": {"trigger": "checkBlock"},
"0,1": {"trigger": "checkBlock"},
"2,1": {"trigger": "checkBlock"},
"4,1": {"trigger": "checkBlock"},
"1,2": {"trigger": "checkBlock"},
"3,2": {"trigger": "checkBlock"},
"6,8": {"trigger": "checkBlock"}
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
"11,12": "你开了一个绿门触发了一个afterOpenDoor事件"
"changeFloor": { // 楼层转换事件该事件不能和上面的events有冲突同位置点否则会被覆盖
"6,0": {"floorId": "sample1", "stair": "downFloor"}, // 目标点sample1层的下楼梯位置
"0,11": {"floorId": "sample0", "loc": [0,12]}, // 目标点sample0层的x=0,y=12位置
"0,12": {"floorId": "sample0", "stair": "upFloor"}, // 注意目标层有多个楼梯的话写stair可能会导致到达位置不确定。这时候推荐写loc指明目标点位置。
"1,12": {"floorId": "sample0", "loc": [1,12]},
"2,12": {"floorId": "sample0", "loc": [2,12]},
"3,12": {"floorId": "sample0", "loc": [6,1]},
"4,12": {"floorId": "sample0", "loc": [0,9]},
"5,12": {"floorId": "sample0", "loc": [6,10], "portalWithoutTrigger": false}, // 不能穿透
"6,12": {"floorId": "sample0", "loc": [10,10]},
},
"afterBattle": { // 战斗后可能触发的事件列表
"2,6": "\t[ghostSkeleton]不可能,你怎么可能打败我!\n一个打败怪物触发的事件"
"2,6": ["\t[ghostSkeleton]不可能,你怎么可能打败我!\n一个打败怪物触发的事件"]
},
"afterGetItem": { // 获得道具后可能触发的事件列表
"11,8": "由于状态栏放不下绿钥匙和铁门钥匙均视为tools\n放入工具栏中。\n碰到绿门和铁门仍然会自动使用开门。",
"8,6": "由于吸血和夹击等的存在,血瓶默认自动被绕路。\n你可以修改data.js中的系统Flag来设置这一项。",
"8,7": "如需修改消耗品的效果,请前往 data.js ,找到\n并修改values内对应的具体数值即可。\n\n如果有更高级的需求如每个区域宝石数值变化\n详见doc文档内的做法说明。",
"11,8": ["由于状态栏放不下绿钥匙和铁门钥匙均视为tools\n放入工具栏中。\n碰到绿门和铁门仍然会自动使用开门。"],
"8,6": ["由于吸血和夹击等的存在,血瓶默认自动被绕路。\n你可以修改data.js中的系统Flag来设置这一项。"],
"8,7": ["如需修改消耗品的效果,请前往 data.js ,找到\n并修改values内对应的具体数值即可。\n\n如果有更高级的需求如每个区域宝石数值变化\n详见doc文档内的做法说明。"],
"10,7": ["在 data.js 的系统Flag中设置是否启用魔防。\n如果不启用魔防则不会在状态栏显示。"],
"9,5": [
"每层楼的 canFlyTo 决定了该楼层能否被飞到。\n\n不能被飞到的楼层也无法使用楼层传送器。",
"飞行的楼层顺序由 main.js 中 floorIds 加载顺序\n所决定。\n\n是否必须在楼梯边使用楼传器由 data.js 中的系统\nFlag所决定。"
],
"10,5": "破墙镐是破面前的墙壁还是四个方向的墙壁,\n由data.js中的系统Flag所决定。",
"10,5": ["破墙镐是破面前的墙壁还是四个方向的墙壁,\n由data.js中的系统Flag所决定。"],
"8,4": [
"炸弹可以炸四个方向的怪物。\n如只需要炸前方怪物请使用上面的圣锤。",
"不能被炸的怪物在enemys中可以定义。\n可参见样板里黑衣魔王的写法。\n\n炸死怪物是否触发事件由 data.js 中的系统Flag\n所决定。"
],
"10,4": "“上楼”和“下楼”的目标层由 main.js 的 floorIds\n顺序所决定。",
"10,3": "十字架目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。",
"9,2": "该道具默认是大黄门钥匙,如需改为钥匙盒直接\n修改 data.js 中的系统Flag即可。",
"10,2": "屠龙匕首目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。",
}
"10,4": ["“上楼”和“下楼”的目标层由 main.js 的 floorIds\n顺序所决定。"],
"10,3": ["十字架目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。"],
"9,2": ["该道具默认是大黄门钥匙,如需改为钥匙盒直接\n修改 data.js 中的系统Flag即可。"],
"10,2": ["屠龙匕首目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。"],
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
"11,12": ["你开了一个绿门触发了一个afterOpenDoor事件"]
},
"checkBlock": [
/****** 领域、夹击检查事件 ******/
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
// 另外如果该点已经存在events事件或changeFloor事件即上面有相同点位置定义则会被覆盖
// afterBattle, afterGetItem, afterOpenDoor则不受影响仍能正常工作
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
"1,0", "3,0", "0,1", "2,1", "4,1", "1,2", "3,2"
]
}

View File

@ -1,30 +1,308 @@
function sample1() {}
// 这里需要改楼层名请和文件名及下面的floorId保持完全一致
main.floors.sample1 = {
'floorId': 'sample1', // 楼层唯一标识符,需要和名字完全一致
'title': "样板 1 层", // 楼层中文名
'name': 1, // 显示在状态栏中的名称
"canFlyTo": true, // 该楼能否被楼传器飞到(不能的话在该楼也不允许使用楼传器)
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
[7, 131, 8, 2, 9, 130, 10, 2, 0, 0, 132, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0],
[2, 2, 2, 2, 121, 2, 2, 2, 0, 0, 229, 0, 0],
[43, 33, 44, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0],
[21, 22, 21, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0],
[1, 245, 1, 1, 0, 87, 0, 2, 2, 2, 85, 2, 2],
[0, 246, 0, 1, 0, 0, 0, 2, 2, 221, 0, 221, 2],
[246, 0, 246, 1, 0, 0, 0, 121, 85, 0, 0, 0, 2],
[1, 246, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 123, 1, 0, 3, 124, 0, 121, 0, 122, 0, 126],
[1, 0, 0, 1, 88, 3, 86, 0, 0, 0, 0, 0, 0],
],
"firstArrive": [ // 第一次到该楼层触发的事件
sample1.prototype.init = function() {
this.data = {
'floorId': 'template', // 楼层唯一标识符,需要和名字完全一致
'title': "样板层", // 楼层中文名
'name': 0, // 显示在status bar中的名称
"canFlyTo": true, // 该楼能否被飞行器飞到(不能的话在该楼也不允许使用)
"map": [ // 地图数据需要是13x13建议使用地图生成器来生成
],
"events": { // 该楼的所有可能事件列表
"4,10": [ // 走到中间时的提示
"\t[样板提示]本层楼将会对各类事件进行介绍。",
"左边是一个仿50层的陷阱做法上方是商店、快捷\n商店的使用方法右上是一个典型的杀怪开门的例\n子右下是各类可能的NPC事件。",
"本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)\nopenShop: 打开一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置\nwin: 获得胜利(游戏通关)\nlose: 游戏失败",
"move: 移动事件效果\nplaySound: 播放某个音频\nif: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性、道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n\n更多支持的事件还在编写中欢迎您宝贵的意见。",
"有关各类事件的样例可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后需要调用\n{\"type\": \"hide\"}\n该事件才不会再次出现。",
{"type": "hide"}
],
"firstArrive": [ // 第一次到该楼层触发的事件
/****** 左边仿50F陷阱事件 ******/
"1,5": {"enable": false}, // 这几个是白衣武士等怪物,起始时需要隐藏起来
"1,6": {"enable": false},
"0,7": {"enable": false},
"2,7": {"enable": false},
"1,8": {"enable": false},
"1,7": [ // 走到白衣武士中间,触发陷阱事件
{"type": "show", "loc": [1,5], "time": 1500}, // 显示红衣魔王动画效果1500ms
{"type": "sleep", "time": 500}, // 等待500ms
"\t[redKing]欢迎来到魔塔,你是第一百位挑战者。\n若你能打败我所有的手下我就与你一对一\n的决斗。\n现在你必须接受我的安排。",
{"type": "show", "loc": [1,6], "time": 500}, // 显示四个白衣武士每个动画效果500ms
{"type": "show", "loc": [0,7], "time": 500},
{"type": "show", "loc": [1,8], "time": 500},
{"type": "show", "loc": [2,7], "time": 500},
"\t[hero]什么?",
{"type": "playSound", "name": "attack.ogg"}, // 播放战斗音频
{"type": "setValue", "name": "status:atk", "value": "status:atk/10"}, // 勇士的攻防变成原来的十分之一
{"type": "setValue", "name": "status:def", "value": "status:def/10"},
{"type": "hide", "loc": [1,6]}, // 直接隐藏四个白衣武士,没有动画效果
{"type": "hide", "loc": [0,7]},
{"type": "hide", "loc": [2,7]},
{"type": "hide", "loc": [1,8]},
{"type": "hide", "loc": [1,5], "time": 500}, // 隐藏红衣魔王动画500ms
{"type": "hide"}, // 隐藏本事件
{"type": "changeFloor", "floorId": "sample1", "loc": [1,11]}, // 楼层切换。changeFloor必须指定floorId和loc。
// 备注:这里也可以下面的这种写法:
// {"type": "changePos", "loc": [1,11]}
// 使用这种写法将不会有“楼层切换动画”而是直接让勇士到达本层的loc位置。
{"type": "trigger", "floodId": "sample1", "loc": [2,11]} // 立刻直接触发另一个事件(也就是下面的小偷事件);当前事件会被立刻结束
],
"events": [ // 该楼的所有可能事件列表
"2,11": [ // 小偷事件
"\t[杰克,thief]喂!醒醒!快醒醒!",
"\t[hero]额,我这是在什么地方?",
"\t[杰克,thief]你被魔王抓了起来扔进了监狱,和我关在了一\n起但是幸运的是我在昨天刚刚挖好一条越狱\n的暗道",
{"type": "openDoor", "loc": [3,11]}, // 开门或墙必须指定门/墙的名称,否则不会执行
{"type": "sleep", "time": 300}, // 等待300ms
"\t[杰克,thief]我先走了,祝你好运!",
{"type": "move", "time": 750, "steps": [ // 动画移动效果time为每步事件毫秒steps是个数组指定了移动的方位
{"direction": "right", "value": 2}, // 向右移动两步,再向下移动一步,并消失
"down" // 如果该方向只移动一步,可以直接这样简写。 这种写法等价于: {"direction":"down","value":1}
]},
// 调用move事件后hide事件也会被自动调用因此无需再手动调用 {"type":"hide"} 来隐藏本事件了
"上面是个move事件可以对NPC等进行移动。\n详见样板中小偷事件的写法。",
"\t[hero]怎么跑的这么快..."
],
"afterOpenDoor": [ // 开完门后可能触发的事件列表
/****** 上方商店事件相关 ******/
"4,2": [ // 商店门前的老人
"\t[老人,man]本塔的商店有两类,全局商店和非全局商店。\n\n所谓非全局商店就类似于右下角那个卖钥匙\n的老人一样一定要碰到才能触发事件。\n\n而全局商店则能在快捷商店中直接使用。",
"\t[老人,man]要注册一个全局商店,你需要在 data.js 中,\n找到 shops并在内添加你的商店信息。",
"\t[老人,man]商店信息添加后,可以在需要的事件处调用\n{\"type\": \"openShop\"}\n来打开你添加的全局商店。",
"\t[老人,man]在上面的例子里左边是一个仿50层的金币\n商店右边是一个仿24层的经验商店。\n\n商店被访问后即可在快捷商店中进行使用。",
{"type": "hide", "time": 500}
],
"afterBattle": [ // 战斗后可能触发的事件列表
"1,0": [ // 金币商店
// 打开商店前,你也可以添加自己的剧情
// 例如通过if来事件来判断是不是第一次访问商店是的则显示一段文字类似宿命的华音那样
{"type": "openShop", "id": "moneyShop1"} // 这里的id要和data.js中你定义的商店ID完全一致
// 调用openShop事件后所有当前事件都会被结束同exit事件然后打开一个全局商店
],
"5,0": [ // 经验商店
{"type": "openShop", "id": "expShop1"}
],
"afterGetItem": [ // 获得道具后可能触发的事件列表
/****** 右边陷阱、战斗相关 ******/
"7,7": [ // 门口老人的提示
"\t[老人,man]这是一个典型的杀怪开门、强制战斗事件。",
{"type": "hide"} // 不显示动画,直接消失
],
"8,7": {"enable": false}, // 门口的机关门,初始时是禁用状态
"9,7": [ // 当你刚进去后,触发机关门
{"type": "show", "loc": [8,7]}, // 显示机关门
{"type": "hide"} // 该事件消失
],
// 注意:初级卫兵打死后的开门事件是在 afterBattle 中调用
"10,4": [ // 开门后走进去的事件:强制战斗
"\t[blackKing]你终于还是来了。",
"\t[hero]放开我们的公主!",
"\t[blackKing]如果我不愿意呢?",
"\t[hero]无需多说,拔剑吧!",
{"type": "battle", "id": "blackKing"}, // 强制战斗
// 如果战斗失败直接死亡,不会继续触发接下来的剧情。
{"type": "hide", "loc": [10,2]}, // 战斗后需要手动使怪物消失战斗后不会引发afterBattle事件。
{"type": "openDoor", "loc": [8,7]}, // 开门口的机关门
"\t[blackKing]没想到你已经变得这么强大了... 算你厉害。\n公主就交给你了请好好对她。",
{"type": "hide"} // 隐藏本事件
],
"10,0": [ // 公主事件
"\t[hero]公主,我来救你了~",
"\t[公主,princess]快救我出去!我受够这里了!",
"\t[hero]公主别怕,我们走吧~",
{"type": "win", "reason": "救出公主"} // 获得胜利。此事件将显示获胜界面,并结束游戏。
// 该事件将直接调用events.js中的win()函数;如需修改获胜界面内容可前往修改。
// 下面这个是失败事件同样会直接调用events.js中的lose()函数;如需修改失败界面内容可以前往修改。
// {"type": "lose", "reason": "救了假公主"}
],
/****** 右下各种NPC事件相关 ******/
"6,12": {"enable":false}, // 仙子下面的铁门,初始时是禁用的
"6,11": [ // 仙子事件
"\t[仙子,fairy]通过调用 {\"type\": \"show\"} 可以使隐藏的\n事件显示出来。\n比如我下面这个机关门。",
{"type": "show", "loc": [6,12]}, // 使隐藏的铁门显示出来
"\t[仙子,fairy]通过调用 {\"type\": \"openDoor\"} 可以无需\n钥匙打开一扇门或暗墙。",
{"type": "openDoor", "loc": [6,12]}, // 开门
"\t[仙子,fairy]同时,也可以对其它层进行操作,比如楼下\n的机关门现在已经为你打开了。",
{"type": "openDoor", "loc": [11,10], "floorId": "sample0"}, // 打开其它层的门需要指定floorId
"\t[仙子,fairy]如果 show 或 hide 指定了 time 参数,则\n以动画效果显示指定的参数作为消失时间\n毫秒来计算。",
"\t[仙子,fairy]现在到楼下来找我吧~",
{"type": "show", "loc": [12,10], "floorId": "sample0"}, // 显示其它层的事件需要指定其floorId
{"type": "hide", "time": 500}
],
"8,11": [ // 老人事件,勇士状态的显示与变化
{"type": "setValue", "name": "flag:man_times", "value": "flag:man_times+1"}, // 设置这个老人的访问次数
"\t[老人,man]在文字中使用${' ${ '}和 } 可以计算并显示一个\n表达式的结果。\n",
"\t[老人,man]例如:\n你的当前攻击力是${status:atk},防御力是${status:def}。\n攻防和的十倍是${10*(status:atk+status:def)},攻防之积是${status:atk*status:def}。\n你有${item:yellowKey}把黄钥匙,${item:blueKey}把蓝钥匙,${item:redKey}把红钥匙。\n你有${item:pickaxe}个破,${item:bomb}个炸,${item:centerFly}个飞。\n这是你第${flag:man_times}次和我对话。",
"\t[老人,man]同时,你也可以通过\n{\"type\": \"setValue\"}\n来设置一个勇士的属性、道具或某个Flag。",
"\t[老人,man]例如:\n现在我将让你的攻防提升50%,再将攻防和\n的十倍加到生命值上。",
{"type": "setValue", "name": "status:atk", "value": "status:atk*1.5"}, // 攻击提升50%;注意不要加${}
{"type": "setValue", "name": "status:def", "value": "status:def*1.5"}, // 防御提升50%;注意不要加${}
{"type": "setValue", "name": "status:hp", "value": "status:hp+10*(status:atk+status:def)"}, //生命提升攻防和的十倍
"\t[老人,man]再送你500金币1000经验1破2炸3飞",
{"type": "setValue", "name": "status:money", "value": "status:money+500"},
{"type": "setValue", "name": "status:experience", "value": "status:experience+1000"},
{"type": "setValue", "name": "item:pickaxe", "value": "item:pickaxe+1"}, // 1破
{"type": "setValue", "name": "item:bomb", "value": "item:bomb+2"}, // 2炸
{"type": "setValue", "name": "item:centerFly", "value": "item:centerFly+3"}, // 3飞
"\t[老人,man]status:xxx 代表勇士的某个属性。\n其中xxx可取hp, atk, def, mdef, money,\nexperience这几项。\n\nitem:xxx 代表勇士的某个道具的个数。\nxxx为道具ID具体可参见items.js中的定义。\n\nflag:xxx 代表某个自定义Flag或变量。\nxxx为Flag/变量名,可以自行定义,由字母、\n数字和下划线组成。\n未定义过而直接取用的Flag默认值为false。",
// 如果老人不消失,则不要调用 {"type": "hide"}
"\t[老人,man]你现在可以重新和我进行对话,进一步看到\n属性值的改变。"
],
"10,11": [ // 商人事件if语句和choices语句的写法
// 这部分逻辑相对比较长,细心看,很容易看懂的。
{"type": "if", "condition": "flag:woman_times==0", // 条件判断:是否从未访问过此商人。
"true": [ // 如果从未访问过该商人,显示一段文字
"\t[老人,woman]这是个很复杂的例子,它将教会你如何使用\nif 语句进行条件判断,以及 choices 提供\n选项来供用户进行选择。",
"\t[老人,woman]第一次访问我将显示这段文字;从第二次开始\n将会向你出售钥匙。\n钥匙价格将随着访问次数递增。\n当合计出售了七把钥匙后将送你一把大黄门\n钥匙并消失不再出现。",
"\t[老人,woman]这部分的逻辑比较长,请细心看样板的写法,\n是很容易看懂并理解的。"
// 第一次访问结束
],
"false": [ // 如果已经访问过该商人
{"type": "if", "condition": "flag:woman_times==8", // 条件判断:是否已经出售七把钥匙
"true": [ // 如果已经出售过七把钥匙,则直接结束
"\t[老人,woman]你购买的钥匙已经够多了,再继续卖给你的话\n我会有危险的。",
"\t[老人,woman]看在你贡献给我这么多钱的份上,送你一把大\n黄门钥匙吧希望你能好好用它。",
{"type": "setValue", "name": "item:bigKey", "value": "item:bigKey+1"}, // 获得一把大黄门钥匙
"\t[老人,woman]我先走了,拜拜~",
{"type":"hide", "time": 500}, // 消失
{"type":"exit"} // 立刻结束当前事件。下面的 setValue 和 revisit 都不会再执行。
],
"false": [ // 否则,显示选择页面
{"type": "choices", "text": "\t[老人,woman]少年,你需要钥匙吗?\n我这里有大把的", // 显示一个选择页面
"choices": [ // 提供四个选项:黄钥匙、蓝钥匙、红钥匙、离开。前三个选项显示需要的金额
{"text": "黄钥匙(${9+flag:woman_times}金币)", "action": [ // 第一个选项,黄钥匙
// 选择该选项的执行内容
// 条件判断:钱够不够
{"type": "if", "condition": "status:money>=9+flag:woman_times",
"true": [
{"type": "setValue", "name": "status:money", "value": "status:money-(9+flag:woman_times)"}, // 扣减金钱
{"type": "setValue", "name": "item:yellowKey", "value": "item:yellowKey+1"}, // 增加黄钥匙
// 然后会继续执行下面的setValue来增加商人访问次数
],
"false": [
"\t[老人,woman]你的金钱不足!",
{"type": "revisit"} // 直接重新访问不执行下面的setValue来增加访问次数
]
}
]},
{"text": "蓝钥匙(${18+2*flag:woman_times}金币)", "action": [ // 第二个选项:蓝钥匙
// 逻辑和上面黄钥匙完全相同,不赘述
{"type": "if", "condition": "status:money>=18+2*flag:woman_times",
"true": [
{"type": "setValue", "name": "status:money", "value": "status:money-(18+2*flag:woman_times)"},
{"type": "setValue", "name": "item:blueKey", "value": "item:blueKey+1"},
],
"false": [
"\t[老人,woman]你的金钱不足!",
{"type": "revisit"}
]
}
]},
{"text": "红钥匙(${36+4*flag:woman_times}金币)", "action": [ // 第三个选项:红钥匙
// 逻辑和上面黄钥匙完全相同,不赘述
{"type": "if", "condition": "status:money>=36+4*flag:woman_times",
"true": [
{"type": "setValue", "name": "status:money", "value": "status:money-(36+4*flag:woman_times)"},
{"type": "setValue", "name": "item:redKey", "value": "item:redKey+1"},
],
"false": [
"\t[老人,woman]你的金钱不足!",
{"type": "revisit"}
]
}
]},
{"text": "离开", "action": [ // 第四个选项:离开
{"type": "exit"} // 立刻结束当前事件
]}
]
}
]
}
]
},
{"type": "setValue", "name": "flag:woman_times", "value": "flag:woman_times+1"}, // 增加该商人的访问次数。
{"type": "revisit"} // 立即重新开始这个事件
],
"12,11": [ // 自定义事件的老人
"\t[老人,womanMagician]使用 {\"type\":\"function\"} 可以写自定义的\nJS脚本。\n本塔支持的所有主要API会在doc文档内给出。",
"\t[老人,womanMagician]例如这个例子:即将弹出一个输入窗口,然后\n会将你的输入结果直接加到你的攻击力上。",
{"type": "function", "function": function() { // 自己写JS脚本并执行
var value = prompt("请输入你要加攻击力的数值:"); // 弹出一个输入框让用户输入数据
if (value!=null) {
value=parseInt(value);
if (value>0) { // 检查
core.setStatus("atk", core.getStatus("atk")+value);
// core.updateStatusBar(); // 和下面的 {"type": "update"} 等价,立即更新状态栏和地图显伤
core.drawTip("操作成功,攻击+"+value); // 左上角气泡提示
core.events.insertAction([ // 往当前事件列表前插入两条事件
{"type": "update"}, // 更新状态栏和地图显伤
"操作成功,攻击+"+value // 对话框提示
]);
}
}
}},
"\t[老人,womanMagician]具体可参见样板中本事件的写法。"
]
};
},
"changeFloor": { // 楼层转换事件该事件不能和上面的events有冲突同位置点否则会被覆盖
"4,12": {"floorId": "sample0", "loc": [6,0]} // 由于楼下有多个上楼梯,所以需指定位置而不是简单地写"stair": "upFloor"
},
"afterBattle": { // 战斗后可能触发的事件列表
"9,6": [ // 初级卫兵1
{"type": "setValue", "name": "flag:door", "value": "flag:door+1"}, // 将"door"这个自定义flag加一
{"type": "if", "condition": "flag:door==2", // 一个条件判断事件,条件是"door"这个flag值等于2
"true": [ // 如果条件成立:打开机关门
{"type": "openDoor", "loc": [10,5]}
],
"false": [] // 如果条件不成立则无事件触发
},
],
"11,6": [ // 初级卫兵2注意由于打怪顺序问题可能都得写一遍。
{"type": "setValue", "name": "flag:door", "value": "flag:door+1"}, // 将"door"这个自定义flag加一
{"type": "if", "condition": "flag:door==2", // 一个条件判断事件,条件是"door"这个flag值等于2
"true": [ // 如果条件成立:打开机关门
{"type": "openDoor", "loc": [10,5]}
],
"false": [] // 如果条件不成立则无事件触发
},
],
},
"afterGetItem": { // 获得道具后可能触发的事件列表
},
"afterOpenDoor": { // 开完门后可能触发的事件列表
},
"checkBlock": [
/****** 领域、夹击检查事件 ******/
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
// 另外如果该点已经存在events事件或changeFloor事件即上面有相同点位置定义则会被覆盖
// afterBattle, afterGetItem, afterOpenDoor则不受影响仍能正常工作
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
]
}
main.floors.sample1 = new sample1();

View File

@ -8,7 +8,6 @@ maps.prototype.loadFloor = function (floorId, map) {
content['name'] = floor.name;
content['title'] = floor.title;
content['canFlyTo'] = floor.canFlyTo;
content['firstArrive'] = floor.firstArrive;
if (!core.isset(map)) map=floor.map;
var blocks = [];
for (var i = 0; i < 13; i++) {
@ -36,6 +35,9 @@ maps.prototype.loadFloor = function (floorId, map) {
}
}
this.addEvent(block,j,i,floor.events[j+","+i])
this.addChangeFloor(block,j,i,floor.changeFloor[j+","+i]);
if (floor.checkBlock.indexOf(j+","+i)>=0)
this.addEvent(block,j,i,{"trigger":"checkBlock"});
if (core.isset(block.event)) blocks.push(block);
}
}
@ -57,6 +59,7 @@ maps.prototype.getBlock = function (x, y, id) {
enable = true;
}
}
id=parseInt(id);
var tmp = {'x': x, 'y': y, 'id': id};
if (enable!=null) tmp.enable = enable;
@ -232,9 +235,11 @@ maps.prototype.addEvent = function (block, x, y, event) {
else if (event instanceof Array) {
event = {"data": event};
}
if (!core.isset(event.data))
event.data = [];
// 覆盖enable
if (!core.isset(block.event.enable) && core.isset(event.enable)) {
if (!core.isset(block.enable) && core.isset(event.enable)) {
block.enable=event.enable;
}
// 覆盖trigger
@ -250,6 +255,11 @@ maps.prototype.addEvent = function (block, x, y, event) {
}
}
maps.prototype.addChangeFloor = function (block, x, y, event) {
if (!core.isset(event)) return;
this.addEvent(block, x, y, {"trigger": "changeFloor", "data": event});
}
maps.prototype.initMaps = function (floorIds) {
var maps = {};
for (var i=0;i<floorIds.length;i++) {
@ -260,20 +270,16 @@ maps.prototype.initMaps = function (floorIds) {
}
maps.prototype.save = function(maps, floorId) {
if (floorId==undefined || floorId==null) {
var map = [];
if (!core.isset(floorId)) {
var map = {};
for (var id in maps) {
// map[id] = this.save(maps, id);
map.push(this.save(maps, id));
map[id] = this.save(maps, id);
// map.push(this.save(maps, id));
}
return map;
}
var thisFloor = maps[floorId];
var floor = {};
floor.floorId = thisFloor.floorId;
floor.name = thisFloor.name;
floor.title = thisFloor.title;
floor.canFlyTo = thisFloor.canFlyTo;
var blocks = [];
for (var x=0;x<13;x++) {
@ -283,44 +289,24 @@ maps.prototype.save = function(maps, floorId) {
}
}
thisFloor.blocks.forEach(function (block) {
blocks[block.x][block.y] = block.id;
if (core.isset(block.enable)) {
if (block.enable) blocks[block.y][block.x] = block.id+":t";
else blocks[block.y][block.x] = block.id+":f";
}
else blocks[block.y][block.x] = block.id;
});
floor.blocks = blocks;
return floor;
return blocks;
}
maps.prototype.load = function (data, floorId) {
if (floorId == undefined) {
var map = [];
var map = {};
for (var id in data) {
map[data[id].floorId] = this.load(data, data[id].floorId);
map[id] = this.load(data, id);
}
return map;
}
var x = null;
for (var id in data) {
if (data[id].floorId == floorId) {
x = data[id];
break;
}
}
if (x==null) return {};
var content = {};
content['floorId'] = x.floorId;
content['name'] = x.name;
content['title'] = x.title;
content['canFlyTo'] = x.canFlyTo;
var blocks = [];
for (var i = 0; i < 13; i++) {
for (var j = 0; j < 13; j++) {
var id = x.blocks[i][j];
var block = this.getBlock(x.floorId, x.name, i, j, id);
if (block!=null) blocks.push(block);
}
}
content['blocks'] = blocks;
return this.updateNoPass(content);
return this.loadFloor(floorId, data[floorId]);
}
main.instance.maps = new maps();

View File

@ -1,71 +0,0 @@
function npcs() {
}
npcs.prototype.init = function () {
this.npcs = {
'npc1': {'id': 'npc1', 'name': '神秘老人', 'icon': 'magician'},
'npc2': {'id': 'npc2', 'name': '神秘老人', 'icon': 'magician'},
'npc3': {'id': 'npc3', 'name': '神秘老人', 'icon': 'womanMagician'},
'npc4': {'id': 'npc4', 'name': '神秘老人', 'icon': 'womanMagician'},
}
}
npcs.prototype.getNpcs = function (npcId) {
if (npcId == undefined) return this.npcs;
return this.npcs[npcId];
}
npcs.prototype.getEffect = function (npcid, times) {
switch (npcid) {
case 'npc1':
return [
{
'action': 'text', 'id': 'npc1',
'content': '提示:灰色的水泥墙比棕色的更为坚固。\n用破墙镐无法破坏水泥墙。\n例如本层墙内的宝物可以使用地震卷轴获取。'
},
];
break;
case 'npc2':
return [
{
'action': 'text', 'id': 'npc2',
'content': '提示14F位于神秘空间之中无法直接到达。\n只能使用特殊道具到达。\n类似14F的还有一层在0F。'
}
];
break;
case 'npc3':
return [
{
'action': 'choices', 'id': 'npc3', 'cancel': true, 'hint': '送你一件道具,你自己\n选吧',
'choices': [
{"text": '破墙镐', 'effect': 'item,pickaxe,1'},
{"text": '炸弹', 'effect': 'item,bomb,1'},
{"text": '中心对称飞行器', 'effect': 'item,centerFly,1'}
]
},
{
'action': 'text', 'id': 'npc3',
'content': '祝你好运,我先溜了~'
},
{'action': 'disappear'}
];
break;
case 'npc4':
return [
{
'action': 'choices', 'id': 'npc4', 'cancel': true, 'hint': '低价回收各种钥匙:',
'choices': [
{"text": '黄钥匙7金币', 'effect': 'status,money,7', 'need': 'item,yellowKey,1'},
{"text": '蓝钥匙35金币', 'effect': 'status,money,35', 'need': 'item,blueKey,1'},
{"text": '红钥匙70金币', 'effect': 'status,money,70', 'need': 'item,redKey,1'}
]
},
{'action': 'revisit'}
];
break;
}
return [];
}
main.instance.npcs = new npcs();

View File

@ -72,6 +72,8 @@ ui.prototype.drawTextBox = function(content) {
}
}
content = core.replaceText(content);
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
var contents = content.split('\n');
@ -130,6 +132,127 @@ ui.prototype.drawTextBox = function(content) {
core.fillText('ui', '<点击任意位置继续>', 270, top+height-13, '#CCCCCC', '13px Verdana');
}
// 绘制选项事件
ui.prototype.drawChoices = function(content, choices) {
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1);
core.setFillStyle('ui', background);
// Step 1: 计算长宽高
var length = choices.length;
var left=85, width = 416-2*left; // 宽度
// 高度
var height = 32*(length+2), bottom = 208+height/2;
if (length%2==0) bottom+=16;
var choice_top = bottom-height+56;
var id=null, name=null, image=null, icon=null;
if (core.isset(content)) {
// 获得name, image, icon
if (content.indexOf("\t[")==0) {
var index = content.indexOf("]");
if (index>=0) {
var str=content.substring(2, index);
content=content.substring(index+1);
var ss=str.split(",");
if (ss.length==1) {
// id
id=ss[0];
// monster
if (id!='hero') {
var enemys = core.material.enemys[id];
if (core.isset(enemys)) {
name = core.material.enemys[id].name;
image = core.material.images.enemys;
icon = core.material.icons.enemys[id];
}
else {
name=id;
id='npc';
image=null;
icon=null;
}
}
}
else {
id='npc';
name=ss[0];
image=core.material.images.npcs;
icon=core.material.icons.npcs[ss[1]];
}
}
}
content = core.replaceText(content);
// content部分高度
var cheight=0;
// 如果含有标题,标题高度
if (name!=null) cheight+=25;
cheight += content.split('\n').length*20;
height+=cheight;
}
var top = bottom-height;
core.fillRect('ui', left, top, width, height, background);
core.strokeRect('ui', left - 1, top - 1, width + 1, height + 1, '#FFFFFF', 2);
// 如果有内容
if (core.isset(content)) {
var content_left = left + 15, content_top = top + 35;
if (core.isset(id)) {
core.canvas.ui.textAlign = "center";
content_top = top+55;
var title_offset = left+width/2;
// 动画
if (id=='hero' || core.isset(icon)) {
core.strokeRect('ui', left + 15 - 1, top + 30 - 1, 34, 34, '#DDDDDD', 2);
content_left = left+60;
title_offset += 22;
}
if (id == 'hero') {
core.fillText('ui', core.status.hero.name, title_offset, top + 27, '#FFD700', 'bold 19px Verdana');
core.clearMap('ui', left + 15, top + 30, 32, 32);
core.fillRect('ui', left + 15, top + 30, 32, 32, background);
var heroIcon = core.material.icons.heros[core.status.hero.id]['down'];
core.canvas.ui.drawImage(core.material.images.heros, heroIcon.stop * 32, heroIcon.loc *32, 32, 32, left+15, top+30, 32, 32);
}
else {
core.fillText('ui', name, title_offset, top + 27, '#FFD700', 'bold 19px Verdana');
if (core.isset(icon)) {
core.status.boxAnimateObjs = [];
core.status.boxAnimateObjs.push({
'bgx': left + 15, 'bgy': top + 30, 'bgsize': 32,
'image': image, 'x': left + 15, 'y': top + 30, 'icon': icon
});
core.setBoxAnimate();
}
}
}
core.canvas.ui.textAlign = "left";
var contents=content.split("\n");
for (var i=0;i<contents.length;i++) {
core.fillText('ui', contents[i], content_left, content_top, '#FFFFFF', 'bold 15px Verdana');
content_top+=20;
}
}
// 选项
core.canvas.ui.textAlign = "center";
for (var i = 0; i < choices.length; i++) {
core.fillText('ui', core.replaceText(choices[i].text), 208, choice_top + 32 * i, "#FFFFFF", "bold 17px Verdana");
}
return;
}
/**
* 绘制确认/取消警告
* @param text
@ -187,8 +310,9 @@ ui.prototype.drawSettings = function (need) {
core.canvas.ui.textAlign = "center";
core.fillText('ui', "音乐: " + (core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), 208, top + 56, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "快捷商店", 208, top + 88, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "降低难度", 208, top + 120, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "同步存档", 208, top + 152, "#FFFFFF", "bold 17px Verdana");
//core.fillText('ui', "降低难度", 208, top + 120, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "同步存档", 208, top + 120, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "清空存档", 208, top + 152, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "重新开始", 208, top + 184, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "关于本塔", 208, top + 216, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', "返回游戏", 208, top + 248, "#FFFFFF", "bold 17px Verdana");
@ -199,7 +323,7 @@ ui.prototype.drawSettings = function (need) {
* 绘制选择商店窗口
* @param need
*/
ui.prototype.drawSelectShop = function (need) {
ui.prototype.drawQuickShop = function (need) {
if (core.isset(need) && !core.checkStatus('selectShop', need))
return;
@ -220,7 +344,7 @@ ui.prototype.drawSelectShop = function (need) {
core.canvas.ui.textAlign = "center";
for (var i = 0; i < keys.length; i++) {
core.fillText('ui', shopList[keys[i]].name, 208, top + 56 + 32 * i, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', shopList[keys[i]].textInList, 208, top + 56 + 32 * i, "#FFFFFF", "bold 17px Verdana");
}
core.fillText('ui', "返回游戏", 208, top + bottom - 40);
@ -672,7 +796,7 @@ ui.prototype.drawThumbnail = function(canvas, blocks, x, y, size, heroLoc, heroI
}
for (var b in blocks) {
var block = blocks[b];
if (core.isset(block.event)) {
if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable)) {
var i = block.x, j = block.y;
var blockIcon = core.material.icons[block.event.cls][block.event.id];
var blockImage = core.material.images[block.event.cls];

54
main.js
View File

@ -33,8 +33,7 @@ function main() {
// console.log('加载游戏容器和开始界面dom对象完成 如下');
// console.log(this.dom);
this.loadList = [
'items', 'icons', 'maps', 'enemys', 'events',
'npcs', 'data', 'ui', 'core'
'items', 'icons', 'maps', 'enemys', 'events', 'data', 'ui', 'core'
];
// console.log('加载js文件列表加载完成' + this.loadList);
this.images = [
@ -76,8 +75,10 @@ function main() {
'curse': document.getElementById('curse'),
'hard': document.getElementById("hard")
}
this.floorIds = [
"sample0"
this.useCompress = false; // 是否使用压缩文件发布前推荐使用“JS压缩工具”将所有js文件进行压缩它会将此项改成true。
// 只有useCompress是false时才会读取floors目录下的文件。如果要进行剧本的修改请务必将其改成false
this.floorIds = [ // 在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器的顺序和上楼器/下楼器的顺序
"sample0", "sample1"
]
this.floors = {}
this.instance = {};
@ -92,23 +93,11 @@ main.prototype.init = function () {
var coreData = {};
for (i = 0; i < main.loadList.length; i++) {
var name = main.loadList[i];
// end with ".min"
if (name.indexOf(".min")==name.length-4)
name=name.substring(0, name.length-4);
if (name === 'core') {
continue;
}
if (name === 'core') continue;
main[name].init(main.dom);
coreData[name] = main[name];
}
main.loaderFloors(function() {
// 处理.min
for (var i=0;i<main.floorIds.length;i++) {
if (name.indexOf(".min")==name.length-4) {
name = name.substring(0, name.length - 4);
main.floorIds[i] = name.substring(0, name.length - 4);
}
}
main.core.init(main.dom, main.statusBar, main.canvas, main.images, main.sounds, main.floorIds, main.floors, coreData);
main.core.resize(main.dom.body.clientWidth, main.dom.body.clientHeight);
})
@ -152,9 +141,6 @@ main.prototype.loaderFloors = function (callback) {
main.prototype.loadMod = function (modName, callback) {
var script = document.createElement('script');
var name = modName;
// end with ".min"
if (name.indexOf(".min")==name.length-4)
name=name.substring(0, name.length-4);
script.src = 'libs/' + modName + '.js?' + this.version;
main.dom.body.appendChild(script);
script.onload = function () {
@ -186,15 +172,17 @@ window.onresize = function () {
}
main.dom.body.onkeydown = function(e) {
if (main.core.isPlaying())
main.core.onkeyDown(e);
try {
if (main.core.isPlaying())
main.core.onkeyDown(e);
} catch (ee) {}
}
main.dom.body.onkeyup = function(e) {
try {
if (main.core.isPlaying())
main.core.onkeyUp(e);
} catch (e) {}
} catch (ee) {}
}
main.dom.body.onselectstart = function () {
@ -220,7 +208,7 @@ main.dom.data.onmousedown = function (e) {
if (loc == null) return;
var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size);
main.core.ondown(x, y);
} catch (e) {}
} catch (ee) {}
}
main.dom.data.onmousemove = function (e) {
@ -230,7 +218,7 @@ main.dom.data.onmousemove = function (e) {
if (loc == null) return;
var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size);
main.core.onmove(x, y);
}catch (e) {}
}catch (ee) {}
}
main.dom.data.onmouseup = function () {
@ -256,7 +244,7 @@ main.dom.data.ontouchstart = function (e) {
var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size);
//main.core.onclick(x, y, []);
main.core.ondown(x, y);
}catch (e) {}
}catch (ee) {}
}
main.dom.data.ontouchmove = function (e) {
@ -266,7 +254,7 @@ main.dom.data.ontouchmove = function (e) {
if (loc == null) return;
var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size);
main.core.onmove(x, y);
}catch (e) {}
}catch (ee) {}
}
main.dom.data.ontouchend = function () {
@ -281,32 +269,32 @@ main.statusBar.image.book.onclick = function () {
main.core.openBook(true);
}
main.statusBar.image.fly.onclick = function (e) {
main.statusBar.image.fly.onclick = function () {
if (main.core.isPlaying())
main.core.useFly(true);
}
main.statusBar.image.toolbox.onclick = function (e) {
main.statusBar.image.toolbox.onclick = function () {
if (main.core.isPlaying())
main.core.openToolbox(true);
}
main.statusBar.image.shop.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawSelectShop(true);
main.core.ui.drawQuickShop(true);
}
main.statusBar.image.save.onclick = function (e) {
main.statusBar.image.save.onclick = function () {
if (main.core.isPlaying())
main.core.save(true);
}
main.statusBar.image.load.onclick = function (e) {
main.statusBar.image.load.onclick = function () {
if (main.core.isPlaying())
main.core.load(true);
}
main.statusBar.image.settings.onclick = function (e) {
main.statusBar.image.settings.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawSettings(true);
}