/*global Quintus:false, module:false */ var quintus2D = function(Quintus) { "use strict"; Quintus["2D"] = function(Q) { Q.component('viewport',{ added: function() { this.entity.on('prerender',this,'prerender'); this.entity.on('render',this,'postrender'); this.x = 0; this.y = 0; this.offsetX = 0; this.offsetY = 0; this.centerX = Q.width/2; this.centerY = Q.height/2; this.scale = 1; }, extend: { follow: function(sprite,directions,boundingBox) { this.off('poststep',this.viewport,'follow'); this.viewport.directions = directions || { x: true, y: true }; this.viewport.following = sprite; if(Q._isUndefined(boundingBox) && this.lists.TileLayer !== undefined) { this.viewport.boundingBox = Q._detect(this.lists.TileLayer, function(layer) { return layer.p.boundingBox ? { minX: 0, maxX: layer.p.w, minY: 0, maxY: layer.p.h } : null; }); } else { this.viewport.boundingBox = boundingBox; } this.on('poststep',this.viewport,'follow'); this.viewport.follow(true); }, unfollow: function() { this.off('poststep',this.viewport,'follow'); }, centerOn: function(x,y) { this.viewport.centerOn(x,y); }, moveTo: function(x,y) { return this.viewport.moveTo(x,y); } }, follow: function(first) { var followX = Q._isFunction(this.directions.x) ? this.directions.x(this.following) : this.directions.x; var followY = Q._isFunction(this.directions.y) ? this.directions.y(this.following) : this.directions.y; this[first === true ? 'centerOn' : 'softCenterOn']( followX ? this.following.p.x - this.offsetX : undefined, followY ? this.following.p.y - this.offsetY : undefined ); }, offset: function(x,y) { this.offsetX = x; this.offsetY = y; }, softCenterOn: function(x,y) { if(x !== void 0) { var dx = (x - Q.width / 2 / this.scale - this.x)/3; if(this.boundingBox) { if(this.x + dx < this.boundingBox.minX) { this.x = this.boundingBox.minX / this.scale; } else if(this.x + dx > (this.boundingBox.maxX - Q.width) / this.scale) { this.x = Math.max(this.boundingBox.maxX - Q.width, this.boundingBox.minX) / this.scale; } else { this.x += dx; } } else { this.x += dx; } } if(y !== void 0) { var dy = (y - Q.height / 2 / this.scale - this.y)/3; if(this.boundingBox) { if(this.y + dy < this.boundingBox.minY) { this.y = this.boundingBox.minY / this.scale; } else if(this.y + dy > (this.boundingBox.maxY - Q.height) / this.scale) { this.y = Math.max(this.boundingBox.maxY - Q.height, this.boundingBox.minY) / this.scale; } else { this.y += dy; } } else { this.y += dy; } } }, centerOn: function(x,y) { if(x !== void 0) { this.x = x - Q.width / 2 / this.scale; } if(y !== void 0) { this.y = y - Q.height / 2 / this.scale; } }, moveTo: function(x,y) { if(x !== void 0) { this.x = x; } if(y !== void 0) { this.y = y; } return this.entity; }, prerender: function() { this.centerX = this.x + Q.width / 2 /this.scale; this.centerY = this.y + Q.height / 2 /this.scale; Q.ctx.save(); Q.ctx.translate(Math.floor(Q.width/2),Math.floor(Q.height/2)); Q.ctx.scale(this.scale,this.scale); Q.ctx.translate(-Math.floor(this.centerX), -Math.floor(this.centerY)); }, postrender: function() { Q.ctx.restore(); } }); Q.Sprite.extend("TileLayer",{ init: function(props) { this._super(props,{ tileW: 32, tileH: 32, blockTileW: 10, blockTileH: 10, type: 1, renderAlways: true }); if(this.p.dataAsset) { this.load(this.p.dataAsset); } this.setDimensions(); this.blocks = []; this.p.blockW = this.p.tileW * this.p.blockTileW; this.p.blockH = this.p.tileH * this.p.blockTileH; this.colBounds = {}; this.directions = [ 'top','left','right','bottom']; this.tileProperties = {}; this.collisionObject = { p: { w: this.p.tileW, h: this.p.tileH, cx: this.p.tileW/2, cy: this.p.tileH/2 } }; this.tileCollisionObjects = {}; this.collisionNormal = { separate: []}; this._generateCollisionObjects(); }, // Generate the tileCollisionObject overrides where needed _generateCollisionObjects: function() { var self = this; function returnPoint(pt) { return [ pt[0] * self.p.tileW - self.p.tileW/2, pt[1] * self.p.tileH - self.p.tileH/2 ]; } if(this.sheet() && this.sheet().frameProperties) { var frameProperties = this.sheet().frameProperties; for(var k in frameProperties) { var colObj = this.tileCollisionObjects[k] = { p: Q._clone(this.collisionObject.p) }; Q._extend(colObj.p,frameProperties[k]); if(colObj.p.points) { colObj.p.points = Q._map(colObj.p.points, returnPoint); } this.tileCollisionObjects[k] = colObj; } } }, load: function(dataAsset) { var fileParts = dataAsset.split("."), fileExt = fileParts[fileParts.length-1].toLowerCase(), data; if (fileExt === "json") { data = Q._isString(dataAsset) ? Q.asset(dataAsset) : dataAsset; } else { throw "file type not supported"; } this.p.tiles = data; }, setDimensions: function() { var tiles = this.p.tiles; if(tiles) { this.p.rows = tiles.length; this.p.cols = tiles[0].length; this.p.w = this.p.cols * this.p.tileW; this.p.h = this.p.rows * this.p.tileH; } }, getTile: function(tileX,tileY) { return this.p.tiles[tileY] && this.p.tiles[tileY][tileX]; }, getTileProperty: function(tile, prop) { if(this.tileProperties[tile] !== undefined) { return this.tileProperties[tile][prop]; } else { return; } }, getTileProperties: function(tile) { if(this.tileProperties[tile] !== undefined) { return this.tileProperties[tile]; } else { return {}; } }, getTilePropertyAt: function(tileX, tileY, prop) { return this.getTileProperty(this.getTile(tileX, tileY), prop); }, getTilePropertiesAt: function(tileX, tileY) { return this.getTileProperties(this.getTile(tileX, tileY)); }, tileHasProperty: function(tile, prop) { return(this.getTileProperty(tile, prop) !== undefined); }, setTile: function(x,y,tile) { var p = this.p, blockX = Math.floor(x/p.blockTileW), blockY = Math.floor(y/p.blockTileH); if(x >= 0 && x < this.p.cols && y >= 0 && y < this.p.rows) { this.p.tiles[y][x] = tile; if(this.blocks[blockY]) { this.blocks[blockY][blockX] = null; } } }, tilePresent: function(tileX,tileY) { return this.p.tiles[tileY] && this.collidableTile(this.p.tiles[tileY][tileX]); }, // Overload this method to draw tiles at frame 0 or not draw // tiles at higher number frames drawableTile: function(tileNum) { return tileNum > 0; }, // Overload this method to control which tiles trigger a collision // (defaults to all tiles > number 0) collidableTile: function(tileNum) { return tileNum > 0; }, getCollisionObject: function(tileX, tileY) { var p = this.p, tile = this.getTile(tileX, tileY), colObj; colObj = (this.tileCollisionObjects[tile] !== undefined) ? this.tileCollisionObjects[tile] : this.collisionObject; colObj.p.x = tileX * p.tileW + p.x + p.tileW/2; colObj.p.y = tileY * p.tileH + p.y + p.tileH/2; return colObj; }, collide: function(obj) { var p = this.p, objP = obj.c || obj.p, tileStartX = Math.floor((objP.x - objP.cx - p.x) / p.tileW), tileStartY = Math.floor((objP.y - objP.cy - p.y) / p.tileH), tileEndX = Math.ceil((objP.x - objP.cx + objP.w - p.x) / p.tileW), tileEndY = Math.ceil((objP.y - objP.cy + objP.h - p.y) / p.tileH), normal = this.collisionNormal, col, colObj; normal.collided = false; for(var tileY = tileStartY; tileY<=tileEndY; tileY++) { for(var tileX = tileStartX; tileX<=tileEndX; tileX++) { if(this.tilePresent(tileX,tileY)) { colObj = this.getCollisionObject(tileX, tileY); col = Q.collision(obj,colObj); if(col && col.magnitude > 0) { if(colObj.p.sensor) { colObj.tile = this.getTile(tileX,tileY); if(obj.trigger) { obj.trigger('sensor.tile',colObj); } } else if(!normal.collided || normal.magnitude < col.magnitude ) { normal.collided = true; normal.separate[0] = col.separate[0]; normal.separate[1] = col.separate[1]; normal.magnitude = col.magnitude; normal.distance = col.distance; normal.normalX = col.normalX; normal.normalY = col.normalY; normal.tileX = tileX; normal.tileY = tileY; normal.tile = this.getTile(tileX,tileY); if(obj.p.collisions !== undefined) { obj.p.collisions.push(normal); } } } } } } return normal.collided ? normal : false; }, prerenderBlock: function(blockX,blockY) { var p = this.p, tiles = p.tiles, sheet = this.sheet(), blockOffsetX = blockX*p.blockTileW, blockOffsetY = blockY*p.blockTileH; if(blockOffsetX < 0 || blockOffsetX >= this.p.cols || blockOffsetY < 0 || blockOffsetY >= this.p.rows) { return; } var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = p.blockW; canvas.height= p.blockH; this.blocks[blockY] = this.blocks[blockY] || {}; this.blocks[blockY][blockX] = canvas; for(var y=0;y