function createMaps(mapCreationData, game) { var maps = new Array(); for(var i = 0;ix && (object.y+object.height)>y) return false; } } return true; } this.walkable = function(x,y,o) { if (o==null) return this.walkableInternal(x,y,null); for(var i = 0;ix && (object.y+object.height)>y) return false; } } return true; } } return false; } this.sailable = function(x,y,o) { if (o==null) return this.sailableInternal(x,y,null); for(var i = 0;i=0 && tmp2.seeThrough[tmp1]==1) return false; } } } return true; } this.canDig = function(x,y) { var idx = x + y*this.width; for(var i = 0;i x && bridge.y + bridge.height > y) return bridge; } return null; } this.getTakeable = function(x,y) { var l = []; for(var i = 0;i x && object.y + object.height > y) l.push(object); } } return l; } this.getBurrowed = function(x,y) { for(var i = 0;i x && object.y + object.height > y) return object; } } return null; } this.getDoor = function(x,y) { for(var i = 0;i x && object.y + object.height > y) return object; } } return null; } this.getPushableWall = function(x,y) { for(var i = 0;i x && object.y + object.height > y) return object; } } return null; } this.getDoorsWithID = function(ID) { var doors = []; for(var i = 0;i x && object.y + object.height > y) return object; } } return null; } this.getVehicle = function(x,y) { for(var i = 0;i x && object.y+object.height > y) return object; } return null; } this.removeTrigger = function(object) { var idx = this.triggers.indexOf(object); if (idx != -1) { this.triggers.splice(idx,1); } } this.getMessage = function(x,y) { for(var j = 0;j x && object.y+object.height > y) return object; } return null; } this.removeMessage = function(object) { var idx = this.messages.indexOf(object); if (idx != -1) { // this.layers[i].objects = this.messages.splice(idx,1); } } this.getCharacter = function(x,y) { for(var i = 0;ix && (object.y+object.height)>y && object instanceof Character) return object; } } return null; } this.getObjects = function(x,y) { var objects = []; for(var i = 0;ix && (object.y+object.height)>y) objects.push(object); } } return objects; } this.getObjectsOfType = function(type) { var objects = []; for(var i = 0;idy) { var incx = 1; if (x2ww) { this.desiredoffsetx = ww/2 - centerx*TILE_WIDTH; if (this.desiredoffsetx>0) this.desiredoffsetx = 0; if (ww-this.desiredoffsetx>this.width*TILE_WIDTH) this.desiredoffsetx = - (this.width*TILE_WIDTH - ww); } else { this.desiredoffsetx = (ww - (this.width*TILE_WIDTH))/2; } if (this.height*TILE_HEIGHT>wh) { this.desiredoffsety = wh/2 - centery*TILE_HEIGHT; if (this.desiredoffsety>0) this.desiredoffsety = 0; if (wh-this.desiredoffsety>this.height*TILE_HEIGHT) this.desiredoffsety = - (this.height*TILE_HEIGHT - wh); } else { this.desiredoffsety = (wh - (this.height*TILE_HEIGHT))/2; } if (this.offsetx!=this.desiredoffsetx) this.offsetx = Math.floor((this.offsetx*3 + this.desiredoffsetx)/4); if (this.offsety!=this.desiredoffsety) this.offsety = Math.floor((this.offsety*3 + this.desiredoffsety)/4); for(var i = 0;i 0) { drawTile(tile-1,x*TILE_WIDTH + this.offsetx,y*TILE_HEIGHT + this.offsety,1, tileEntry); } } } } // draw objects: for(var j = 0;j0 && this.visibility[offset-1]) || (x0 && this.visibility[offset-this.width]) || (y=game.characters.length) game.currentCharacter--; if (game.currentCharacter==-1) { // game over!!! return false; } } else if (game.currentCharacter>idx) { game.characters.splice(idx,1); game.currentCharacter--; } else { game.characters.splice(idx,1); } } } } } if (this.onStart.length>0) { var retVal = executeRuleEffect(this.onStart[0], null, this, game, null); if (retVal!=undefined) this.onStart.splice(0,1); } // check for story state rules: if (game.storyStateLastCycleUpdated > this.storyStateRulesLastCycleChecked || this.storyStateLastCycleUpdated > this.storyStateRulesLastCycleChecked) { for(var i = 0;i0) { var retVal = executeRuleEffect(this.actionsToExecute[0], null, this, game, null); if (retVal!=undefined) this.actionsToExecute.splice(0,1); } return true; } this.pushTextOverlay = function(text,x,y,r,g,b) { this.textOverlays.push({text:text, x:x, y:y, offy:0, r:r, g:g, b:b, cycle:0, duration:100, bubble:false}); } this.pushTextBubble = function(text,speaker,time) { this.textOverlays.push({text:text, r:255, g:255, b:255, cycle:0, duration:time, bubble:true, speaker:speaker, textColor:'#000000', bubbleColor:'#ffffff'}); } this.pushTextBubbleColor = function(text,speaker,time, textColor, bubbleColor) { this.textOverlays.push({text:text, r:255, g:255, b:255, cycle:0, duration:time, bubble:true, speaker:speaker, textColor:textColor, bubbleColor:bubbleColor}); } this.updateTextOverlays = function(game) { var toDelete = []; for(var i in this.textOverlays) { var msg = this.textOverlays[i]; msg.cycle++; if (!msg.bubble && (msg.cycle%2)==0) msg.offy--; if (msg.bubble) { if (msg.speaker.map!=this.ID) { // move this buble to the other map: game.maps[msg.speaker.map].textOverlays.push(msg); toDelete.push(msg); } else { if (msg.cycle>=msg.duration) toDelete.push(msg); } } else { if (msg.cycle>=msg.duration) toDelete.push(msg); } } for(var i in toDelete) { this.textOverlays.splice(this.textOverlays.indexOf(toDelete[i]),1); } } this.drawTextOverlays = function(alpha,offsetx,offsety) { ctx.font = "8px Emulogic"; for(var i = this.textOverlays.length-1;i>=0;i--) { var msg = this.textOverlays[i]; var f1 = msg.cycle/25.0; var f2 = (msg.duration-msg.cycle)/25.0; if (f1<0) f1 = 0; if (f1>1) f1 = 1; if (f2<0) f2 = 0; if (f2>1) f2 = 1; var f = f1*f2; var tmp = ctx.globalAlpha; ctx.globalAlpha = alpha*f; if (msg.bubble) { var speakerx = msg.speaker.getScreenX() + TILE_WIDTH/2 + offsetx; var speakery = msg.speaker.getScreenY() + offsety; this.drawTextBubble(alpha,speakerx,speakery,msg); } else { ctx.fillStyle = "rgb(" + msg.r + "," + msg.g + "," + msg.b + ")"; ctx.fillText(msg.text,msg.x*TILE_WIDTH+8+offsetx,msg.y*TILE_HEIGHT+offsety+msg.offy); } ctx.globalAlpha = tmp; } } this.drawTextBubble = function(alpha,speakerx,speakery,msg) { var lines = splitLineIntoLines(msg.text,24); var bubbleWidth = 0; var bubbleHeight = lines.length * 16; for(var idx in lines) { var l = lines[idx].length * 8; if (l>bubbleWidth) bubbleWidth = l; } bubbleWidth +=24; bubbleHeight += 16; msg.x = speakerx; if (y canvas.width-HUD_WIDTH) startx = (canvas.width-HUD_WIDTH) - bubbleWidth; var starty = msg.y - bubbleHeight; if (starty<0) starty = 0; if (starty + bubbleHeight > canvas.height-64) starty = (canvas.height-64) - bubbleHeight; // coordinates of the tip of the arrow towards the speaker: if (speakerx<0) speakerx = 0; if (speakerx> canvas.width-HUD_WIDTH) startx = (canvas.width-HUD_WIDTH); if (speakery<0) speakery = 0; if (speakery> canvas.height-64) starty = (canvas.height-64); // draw the bubble: drawBubble(startx, starty, bubbleWidth, bubbleHeight, speakerx, speakery, msg.bubbleColor); // draw the text: ctx.fillStyle = msg.textColor; for(var idx in lines) { ctx.fillText(lines[idx],startx+12,starty+20+idx*16); } } this.collectRespawnData = function() { this.respawnData = []; for(var i = 0;i " + obj.respawn); } } } } this.respawnEnemies = function(cycle) { // console.log("Respawning enemies in map " + this.name); for(var i=0;i0 && rd.lastCycleAlive0) { current = open.pop(); offset = current.x + current.y*this.width; closed[offset] = true; this.visibility[offset] = true; if (this.canSeeThrough(current.x,current.y,game)) { if (current.x>0 && !closed[offset-1]) open.push({x:current.x-1, y:current.y}); if (current.y>0 && !closed[offset-this.width]) open.push({x:current.x, y:current.y-1}); if (current.x0 && current.y>0 && !closed[offset-1-this.width]) open.push({x:current.x-1, y:current.y-1}); if (current.x0 && !closed[offset+1-this.width]) open.push({x:current.x+1, y:current.y-1}); if (current.x>0 && current.y0) { this.layers[layer].types[offset] = tileEntry.tileTypes[tile-1]; this.layers[layer].seeThrough[offset] = tileEntry.seeThrough[tile-1]; this.layers[layer].canDig[offset] = tileEntry.canDig[tile-1]; } else { this.layers[layer].types[offset] = 0; this.layers[layer].seeThrough[offset] = 0; this.layers[layer].canDig[offset] = 0; } } // save maps: this.generateSaveData = function(objects) { var mapdata = {}; mapdata.visibility = this.visibility; mapdata.messages = this.messages; mapdata.triggers = this.triggers; mapdata.respawnData = this.respawnData; var layersdata = []; for(var i = 0;i=tilesets[j].firstgid) tileset = j; } if (tileset!=null) { tmpStr -= tilesets[tileset].firstgid - 1; var layerId = layerTable[i + "-" + tileset]; if (layerId == undefined) { layerId = this.newLayer(tilesets[tileset].tileset); layerTable[i + "-" + tileset] = layerId; } // console.log(x + "," + y + " -> " + tmpStr); this.setTile(offset,layerId,tmpStr); // console.log(x + "," + y + " -> " + tmpStr + "(" + tileset + ")"); } if (i==0) this.visibility[offset] = false; } } } var objectlayers = mapElement.getElementsByTagName("objectgroup"); // console.log(TILE_WIDTH_ORIGINAL); if(DEBUG) console.log(objectlayers.length + " object layers..."); for(var i = 0;i-1 && name.indexOf("new")!=0 && classN!=null) { cname = classN.nodeValue; } var item = eval(cname); item.x = Math.floor(x/TILE_WIDTH_ORIGINAL); item.y = Math.floor(y/TILE_HEIGHT_ORIGINAL); // console.log("new item: " + name); initAIRules(item); parseAIRules(item,objectElement); this.addObject(item,1); } if (type=="character" || type=="enemy" || type=="npc") { var x = parseInt(objectElement.attributes.getNamedItem("x").nodeValue); var y = parseInt(objectElement.attributes.getNamedItem("y").nodeValue); var name = objectElement.attributes.getNamedItem("name").nodeValue; console.log("Creating character"); var item = eval(name); item.x = Math.floor(x/TILE_WIDTH_ORIGINAL); item.y = Math.floor(y/TILE_HEIGHT_ORIGINAL); /* // additional inventory: var inventoryElements = objectElement.getElementsByTagName("item"); if (inventoryElements!=null) { for(var k = 0;k " + width + "," + height); } if (type=="script") { var x = parseInt(objectElement.attributes.getNamedItem("x").nodeValue); var y = parseInt(objectElement.attributes.getNamedItem("y").nodeValue); var width = parseInt(objectElement.attributes.getNamedItem("width").nodeValue); var height = parseInt(objectElement.attributes.getNamedItem("height").nodeValue); var repeat = false; if (objectElement.attributes.getNamedItem("repeat")!=null && objectElement.attributes.getNamedItem("repeat").nodeValue=="true") repeat = true; // load the script var actions = []; for(var k = 0;k lineLength) { lines.push(currentLine); currentLine = currentWord; currentWord = ""; } else { if (currentLine == "") currentLine = currentWord; else currentLine = currentLine + " " + currentWord; currentWord = ""; } } else { currentWord = currentWord + text[i]; } } if (currentLine != "") lines.push(currentLine); return lines; } drawBubble = function(x, y, width, height, pointx, pointy, fillColor) { // make sure the point s not inside of the bubble: if (pointx>x && pointxy && pointy10) triangleWidth = 10; triangleWidth+=10; // 1) add the arrow tip to a list var points = [{x:pointx,y:pointy,type:0}]; // 2) Compute the 2 collision points of the arrow with the rectangle, and add them to a list points.push(lineRectangleCollision(pointx,pointy,Math.floor(cx + wx*triangleWidth), Math.floor(cy + wy*triangleWidth), x,y,x+width,y+height)); points.push(lineRectangleCollision(pointx,pointy,Math.floor(cx - wx*triangleWidth), Math.floor(cy - wy*triangleWidth), x,y,x+width,y+height)); // 3) Add all the 4 vertives of the rectangle: points.push({x:x, y:y, type:1}); points.push({x:x+width, y:y, type:1}); points.push({x:x+width, y:y+height, type:1}); points.push({x:x, y:y+height, type:1}); // 4) sort those vertices clock-wise for(var idx in points) { points[idx].angle = Math.atan2(points[idx].y - cy, points[idx].x - cx); } points = points.sort(function(p1,p2) {return p1.angle - p2.angle;}); // 5) if after sorting, any point of type 1 is isolated in between points of type 0, it is removed (that means that // such point is inside of the pointing triangle): { var toDelete = -1; var lastIndex = points.length-1; var last = points[points.length-1].type; var count = 0; for(var idx2 = points.length-1;points[idx2].type==last;idx2--) { count++; } for(var idx2 in points) { if (points[idx2].type==last) { count++; lastIndex = idx2; } else { if (last==1 && count==1) { toDelete = lastIndex; } last = points[idx2].type; count = 1; lastIndex = idx2; } } if (toDelete!=-1) { points.splice(toDelete,1); } } // 5) draw them ctx.fillStyle = fillColor; ctx.strokeStyle = '#000000'; ctx.beginPath(); ctx.moveTo(points[points.length-1].x, points[points.length-1].y); for(var idx in points) { ctx.lineTo(points[idx].x, points[idx].y); } ctx.closePath(); ctx.fill(); ctx.stroke(); } // This method assumes that (lp1x,lp1y) is outside of the rectangle, and (lp2x,lp2y) is inside lineRectangleCollision = function(lp1x,lp1y, lp2x, lp2y, rp1x, rp1y, rp2x, rp2y) { var points = []; var vx = lp2x-lp1x; var vy = lp2y-lp1y; // collision 1: left side if (lp1x<=rp1x && vx!=0) { var k = (rp1x - lp1x)/vx; var collission_y = lp1y + k*vy; if (collission_y>=rp1y && collission_y=rp2x && vx!=0) { var k = (rp2x - lp1x)/vx; var collission_y = lp1y + k*vy; if (collission_y>=rp1y && collission_y=rp1x && collission_x=rp2y && vy!=0) { var k = (rp2y - lp1y)/vy; var collission_x = lp1x + k*vx; if (collission_x>=rp1x && collission_x