/*global Quintus:false */ Quintus.Scenes = function(Q) { Q.scenes = {}; Q.stages = []; Q.Scene = Q.Class.extend({ init: function(sceneFunc,opts) { this.opts = opts || {}; this.sceneFunc = sceneFunc; } }); // Set up or return a new scene Q.scene = function(name,sceneObj,opts) { if(sceneObj === void 0) { return Q.scenes[name]; } else { if(Q._isFunction(sceneObj)) { sceneObj = new Q.Scene(sceneObj,opts); } Q.scenes[name] = sceneObj; return sceneObj; } }; Q._generatePoints = function(obj) { var p = obj.p, halfW = p.w/2, halfH = p.h/2; p.points = [ [ -halfW, -halfH ], [ halfW, -halfH ], [ halfW, halfH ], [ -halfW, halfH ] ]; }; Q._generateCollisionPoints = function(obj) { if(!obj.c || obj.c.angle !== obj.p.angle || obj.c.scale !== obj.p.scale) { if(!obj.c) { obj.c = { points: [] }; } obj.c.angle = obj.p.angle; obj.c.scale = obj.p.scale; var mat = Q.matrix2d(); if(obj.c.angle) { mat.rotateDeg(obj.c.angle); } if(obj.c.scale) { mat.scale(obj.c.scale); } for(var i=0;i 0) { normalX /= dist; normalY /= dist; } } function dotProductAgainstNormal(point) { return (normalX * point[0]) + (normalY * point[1]); } function collide(o1,o2,flip) { var min1,max1, min2,max2, d1, d2, offsetLength, tmp, i, j, minDist, minDistAbs, shortestDist = Number.POSITIVE_INFINITY, collided = false, p1, p2; var result = flip ? result2 : result1; p1 = o1.c ? o1.c.points : o1.p.points; p2 = o2.c ? o2.c.points : o2.p.points; o1 = o1.p; o2 = o2.p; offset[0] = o1.x + o1.cx - o2.x - o2.cx; offset[1] = o1.y + o1.cy - o2.y - o2.cy; for(i = 0;i max1) { max1 = tmp; } } min2 = dotProductAgainstNormal(p2[0]); max2 = min2; for(j = 1;j max2) { max2 = tmp; } } offsetLength = dotProductAgainstNormal(offset); min1 += offsetLength; max1 += offsetLength; d1 = min1 - max2; d2 = min2 - max1; if(d1 > 0 || d2 > 0) { return null; } minDist = (max2 - min1) * -1; if(flip) { minDist *= -1; } minDistAbs = Math.abs(minDist); if(minDistAbs < shortestDist) { result.distance = minDist; result.magnitude = minDistAbs; result.normalX = normalX; result.normalY = normalY; collided = true; shortestDist = minDistAbs; } } // Do return the actual collision return collided ? result : null; } function satCollision(o1,o2) { var result1, result2, result; // Don't compare a square to a square for no reason // if(!o1.p.points && !o2.p.points) return true; if(!o1.p.points) { Q._generatePoints(o1); } if(!o2.p.points) { Q._generatePoints(o2); } if(o1.c || o1.p.angle || o1.p.scale) { Q._generateCollisionPoints(o1); } if(o2.c || o2.p.angle || o2.p.scale) { Q._generateCollisionPoints(o2); } result1 = collide(o1,o2); if(!result1) { return false; } result2 = collide(o2,o1,true); if(!result2) { return false; } result = (result2.magnitude < result1.magnitude) ? result2 : result1; if(result.magnitude === 0) { return false; } result.separate[0] = result.distance * result.normalX; result.separate[1] = result.distance * result.normalY; return result; } return satCollision; }()); Q.overlap = function(o1,o2) { return !((o1.p.y+o1.p.ho2.p.y+o2.p.h) || (o1.p.x+o1.p.wo2.p.x+o2.p.w)); }; Q.Stage = Q.GameObject.extend({ // Should know whether or not the stage is paused defaults: { sort: false, gridW: 400, gridH: 400 }, init: function(scene,opts) { this.scene = scene; this.items = []; this.lists = {}; this.index = {}; this.removeList = []; this.grid = {}; this.options = Q._extend({},this.defaults); if(this.scene) { Q._extend(this.options,scene.opts); } if(opts) { Q._extend(this.options,opts); } if(this.options.sort && !Q._isFunction(this.options.sort)) { this.options.sort = function(a,b) { return ((a.p && a.p.z) || -1) - ((b.p && b.p.z) || -1); }; } }, destroyed: function() { this.trigger("destroyed"); }, // Needs to be separated out so the current stage can be set loadScene: function() { if(this.scene) { this.scene.sceneFunc(this); } }, each: function(callback) { for(var i=0,len=this.items.length;i= 0; i--) { if(func.call(this.items[i],arguments[1],arguments[2],arguments[3])) { return this.items[i]; } } return false; }, identify: function(func) { var result; for(var i = this.items.length-1;i >= 0; i--) { if(result = func.call(this.items[i],arguments[1],arguments[2],arguments[3])) { return result; } } return false; }, addToLists: function(lists,object) { for(var i=0;i 0 && (col = this._collisionLayer.collide(obj))) { col.obj = this._collisionLayer; obj.trigger('hit',col); obj.trigger('hit.collision',col); this.regrid(obj); maxCol--; } } col2 = this.gridTest(obj,collisionMask,this._collisionLayer); if(col2) { obj.trigger('hit',col2); obj.trigger('hit.sprite',col2); this.regrid(obj); } return col2 || col; }, delGrid: function(item) { var grid = item.grid; for(var y = grid.Y1;y <= grid.Y2;y++) { if(this.grid[y]) { for(var x = grid.X1;x <= grid.X2;x++) { if(this.grid[y][x]) { delete this.grid[y][x][item.p.id]; } } } } }, addGrid: function(item) { var grid = item.grid; for(var y = grid.Y1;y <= grid.Y2;y++) { if(!this.grid[y]) { this.grid[y] = {}; } for(var x = grid.X1;x <= grid.X2;x++) { if(!this.grid[y][x]) { this.grid[y][x] = {}; } this.grid[y][x][item.p.id] = item.p.type; } } }, // Add an item into the collision detection grid, // Ignore the collision layer or objects without a type regrid: function(item,skipAdd) { if(this._collisionLayer && item === this._collisionLayer) { return; } if(!item.p.type && !skipAdd) { return; } var gridX1 = Math.floor(item.p.x / this.options.gridW), gridY1 = Math.floor(item.p.y / this.options.gridH), gridX2 = Math.floor((item.p.x + item.p.w) / this.options.gridW), gridY2 = Math.floor((item.p.y + item.p.h) / this.options.gridH), grid = item.grid; if(grid.X1 !== gridX1 || grid.X2 !== gridX2 || grid.Y1 !== gridY1 || grid.Y2 !== gridY2) { if(grid.X1 !== void 0) { this.delGrid(item); } grid.X1 = gridX1; grid.X2 = gridX2; grid.Y1 = gridY1; grid.Y2 = gridY2; if(!skipAdd) { this.addGrid(item); } } }, stepChildren: function(dt) { var item; for(var i=0,len=this.items.length;i 0) { for(var i=0,len=this.removeList.length;i