if (w === undefined)
return this.layerWidth;
+ this.layerWidth = w;
+ this.boundingBox = this.boundingBox.resize(w, this.layerHeight);
+
var nb = this.negBleed.x
, v = this.canvasWidth = w + nb + this.posBleed.x;
- this.layerWidth = w;
this.layer.width(w).css('margin-left', (-nb)+'px')
this.canvas.width(v);
// this.canvas.css({
// });
this.canvas[0].width = v;
+
return this;
},
if (h === undefined)
return this.layerHeight;
+ this.layerHeight = h;
+ this.boundingBox = this.boundingBox.resize(this.layerWidth, h);
+
var nb = this.negBleed.y
, v = this.canvasHeight = h + nb + this.posBleed.y;
- this.layerHeight = h;
this.layer.height(h).css('margin-top', (-nb)+'px')
this.canvas.height(v);
// this.canvas.css({
// if (pos.left !== undefined) pos.left -= this.offsetX;
// if (pos.top !== undefined) pos.top -= this.offsetY;
+ this.boundingBox = this.boundingBox.moveTo(pos.left,pos.top);
+ this.loc = this.boundingBox.p1;
this.css(pos);
return this;
},
o.x = x;
o.y = y;
- // this.dirty = true;
return this._applyTransforms();
},
if (arguments.length === 0)
return o.absolute(this.layerWidth, this.layerHeight);
- // Record my relative scaling...
o.x = sx;
o.y = sy;
this.dirty = true;
if (arguments.length === 0)
return o.absolute(this.layerWidth, this.layerHeight);
- // Record my relative scaling...
o.x = x;
o.y = y;
this.dirty = true;
return this;
},
-
- // origin : new Loc('50%','50%'),
- // rotate : 0,
- // scale : new Loc(1.0,1.0),
- // translate : new Loc(0,0)
_applyTransforms : function _applyTransforms(){
var t = this.transform, tfns = [];
},
tick : function tick(){
+ if (!this.running) return;
+
var lastTick = this.now;
this.now = new Date().getTime();
this.elapsed = this.now - lastTick;
},
tickTime : function tickTime(){
+ if (!this.loop.running) return;
+
var loop = this.loop
, buf = this.buffer
;
return this.calcY(x) === y;
},
+ isWithin : function isWithin(pt, w,h){
+ if ( !Y.isNumber(w) ){
+ h = w.height; w = w.width;
+ }
+ var dw = Math.abs(this.calcX(pt.y) - pt.x)
+ , dh = Math.abs(this.calcY(pt.x) - pt.y) ;
+ return dw <= w || dh <= h ;
+ },
+
setTScale : function setTScale(tdist){
if (tdist) {
this.tdist = tdist;
return this;
},
- pcalc : function parametric(t){
+ parametric : function parametric(t){
return new math.Vec( this.x1 + t*this.pa ,
this.y1 + t*this.pb );
},
- pointX : function pointX(x){
+ pointAtX : function pointAtX(x){
return new math.Vec(x, this.calcY(x));
},
- pointY : function pointY(y){
+ pointAtY : function pointAtY(y){
return new math.Vec(this.calcX(y), y);
},
+ near : function near(x,y){
+ if ( !isFinite(this.slope) )
+ return this.pointAtY(y);
+ else
+ return this.pointAtX(x);
+ },
+
calcY : function calcY(x){
- return x*this.slope + this.yint;
+ return (x === this.xint ? 0 : x*this.slope + this.yint);
},
calcX : function calcX(y){
- return y/this.slope + this.xint;
+ return (y === this.yint ? 0 : y/this.slope + this.xint);
},
base : function base(){
},
toString : function toString(){
- return 'Line( '+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+')';
+ return '['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
}
});
},
toString : function toString(){
- return '('+this.x.toFixed(3)+', '+this.y.toFixed(3)+')';
+ var p = 2, x = this.x, y = this.y;
+ x = ((x % 1 !== 0) ? x.toFixed(p) : x);
+ y = ((y % 1 !== 0) ? y.toFixed(p) : y);
+ return '('+x+', '+y+')';
}
});
y2 = -y2;
}
- try {
+ // try {
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
ctx.closePath();
- } catch(e) {
- if (window.console) {
- console.error(this+'.drawShape()');
- console.log(' points:', x1,y1, ' ', x2,y2);
- console.log(' bounds:', minW,minH, ' ', maxW,maxH);
- console.log(' ::', this, line);
- }
- }
+ // } catch(e) {
+ // if (window.console) {
+ // console.error(this+'.drawShape()');
+ // console.log(' points:', x1,y1, ' ', x2,y2);
+ // console.log(' bounds:', minW,minH, ' ', maxW,maxH);
+ // console.log(' ::', this, line);
+ // }
+ // }
// Show definition points
if ( this.drawDefinitionPoints ) {
Rect = new Y.Class('Rect', Shape, {
_cssClasses : 'portal layer shape rect',
- init : function initRect(w, h){
+ init : function initRect(w,h){
Layer.init.call(this);
this.width(w)
Y(Game.prototype).extend({
initMap : function initMap(){
- // var self = this;
+ var self = this;
this.byId = {};
this.units = new Y.YArray();
this.bullets = new Y.YArray();
this.blockers = new Y.YArray();
- this.pathmap = new PathMap(0,0, COLUMNS*REF_SIZE, ROWS*REF_SIZE, CAPACITY);
-
- var root =
this.root =
this.grid =
new Grid(COLUMNS,ROWS, CELL_SIZE)
.appendTo(this.viewport);
+
this.level =
- new Level(this, root.layerWidth, root.layerHeight )
+ new Level(this, COLUMNS*REF_SIZE, ROWS*REF_SIZE)
.appendTo(this.root);
+ this.pathmap = this.level.pathmap;
+
// Agent.addEventListener('create', function(evt){
// self.addAgent(evt.instance);
// });
},
- // *** Path Map Management *** //
-
- addBlocker : function addBlocker(agent){
- var bb = agent.boundingBox;
- if (agent.blocking && bb)
- agent.region = this.pathmap.set(bb.x1,bb.y1, bb.x2,bb.y2, agent);
- return agent;
- },
-
- removeBlocker : function removeBlocker(agent){
- if (agent.region)
- this.pathmap.remove(agent.region);
- return agent;
- },
-
- updateBlocker : function updateBlocker(agent){
- this.removeBlocker(agent);
- this.addBlocker(agent);
- },
-
-
// *** Agent Management *** //
addUnit : function addUnit(unit, col,row){
unit.render( this.level );
}
- this.addBlocker(unit);
+ this.pathmap.addBlocker(unit);
if ( !this.byId[unit.id] ) {
this.byId[unit.id] = unit;
agent.game = this;
if (agent.id === undefined) return agent;
- this.addBlocker(agent);
+ this.pathmap.addBlocker(agent);
if ( !this.byId[agent.id] ) {
this.byId[agent.id] = agent;
else
this.units.remove(agent);
- this.removeBlocker(agent);
+ this.pathmap.removeBlocker(agent);
return agent;
},
moveAgentTo : function moveAgentTo(agent, x,y){
- this.removeBlocker(agent);
+ this.pathmap.removeBlocker(agent);
agent.setLocation(x,y);
- this.addBlocker(agent);
+ this.pathmap.addBlocker(agent);
return agent;
},
function main(){
v = $('#viewport');
LBT = new Game();
-
+ ctx = LBT.level.ctx;
- var sq = REF_SIZE
- , wall = new Wall(6*sq,1*sq, 1*sq,4*sq);
- LBT.level.append(wall);
+ var sq = REF_SIZE;
+ LBT.level.append(
+ new Wall(6*sq,1*sq, 1*sq,4*sq),
+ new Wall(6*sq,7*sq, 1*sq,2*sq),
+ new Wall(1*sq,7*sq, 2*sq,2*sq),
+ new Wall(3*sq,6*sq, 1*sq,1*sq)
+ );
T = new Tank(0);
LBT.addUnit(T, 1,2);
- T.shape.hide();
- B = new Bullet(T, 5*REF_SIZE,5*REF_SIZE);
- LBT.addUnit(B);
- B.render( LBT.level );
+ T.fireProjectile(5*sq,5*sq);
+ T.fireProjectile(1*sq,3*sq);
+ T.fireProjectile(6*sq,1.5*sq);
+ T.fireProjectile(0.5*sq,0.5*sq);
- B = new Bullet(T, 25,25);
- LBT.addUnit(B);
- B.render( LBT.level );
-
- B = new Bullet(T, 1*REF_SIZE,3*REF_SIZE);
- LBT.addUnit(B);
- B.render( LBT.level );
+ T.shape.hide();
+ LBT.pathmap.removeBlocker(T);
P = new Player(LBT, T);
-
- ctx = LBT.level.ctx;
- tr = B.trajectory;
-
-
setupUI();
}
+Level = new Y.Class('Level', Rect, {
+
+ init : function init(game, w,h){
+ Rect.init.call(this, w,h);
+ this.game = game;
+ this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY);
+ },
+
+ append : function append(children){
+ Y(arguments).forEach(this.pathmap.addBlocker, this.pathmap);
+ return Rect.prototype.append.apply(this, arguments);
+ },
+
+ drawShape : Y.op.nop
+
+});
+
Wall = new Y.Class('Wall', Rect, {
fillStyle : 'rgba(255,255,255, 0.25)',
blocking : true,
var x1 = x, y1 = y
, x2 = x+w, y2 = y+h;
- this.boundingBox = new Loc.Rect(x1,y1, x2,y2);
this.position(x,y);
- }
-
-});
-
-Level = new Y.Class('Level', Rect, {
-
- init : function init(game, w,h){
- this.game = game;
- this.pathmap = game.pathmap;
- Rect.init.call(this, w,h);
},
- append : function append(children){
- Y(arguments).forEach(function(child){
- var bb = child.boundingBox;
- if (child.blocking && bb)
- child.region = this.pathmap.set(bb.x1,bb.y1, bb.x2,bb.y2, child);
- }, this);
- return Layer.prototype.append.apply(this, arguments);
+ appendTo : function appendTo(parent){
+ if (parent instanceof Level)
+ parent.pathmap.addBlocker(this);
+ return Rect.prototype.appendTo.call(this, parent);
},
- drawShape : Y.op.nop
+ toString : function(){
+ return this.className+'('+this.loc+', w='+this.layerWidth+', h='+this.layerHeight+')';
+ }
-});
\ No newline at end of file
+});
+
this.width = x2 - x1;
this.height = y2 - y1;
+ this.p1 = new Loc(x1,y1);
+ this.p2 = new Loc(x2,y2);
+
this.sides = {
top : new math.Line(x1,y1, x2,y1),
bottom : new math.Line(x1,y2, x2,y2),
default: this[k] = v;
}
+
+ this.width = this.x2 - this.x1;
+ this.height = this.y2 - this.y1;
+
+ return this._updateSides();
+ },
+
+ _updateSides : function _updateSides(){
+ var s = this.sides
+ , x1 = this.x1, y1 = this.y1
+ , x2 = this.x2, y2 = this.y2
+ , dx1 = x1 !== s.top.x1, dy1 = y1 !== s.top.y1
+ , dx2 = x2 !== s.top.x2, dy2 = y2 !== s.left.y2
+ ;
+
+ if ( dx1 || dy1 ) this.p1 = new Loc(x1,y1);
+ if ( dx2 || dy2 ) this.p2 = new Loc(x2,y2);
+
+ if ( dx1 || dy1 || dx2 ) s.top = new math.Line(x1,y1, x2,y1);
+ if ( dx1 || dx2 || dy2 ) s.bottom = new math.Line(x1,y2, x2,y2);
+ if ( dx1 || dy1 || dy2 ) s.left = new math.Line(x1,y1, x1,y2);
+ if ( dy1 || dy2 || dx2 ) s.right = new math.Line(x2,y1, x2,y2);
+
return this;
},
return new Loc.Rect(x,y, x+this.width,y+this.height);
},
+ resize : function resize(w,h){
+ var x = this.x, y = this.y;
+ return new Loc.Rect(x,y, x+w,y+h);
+ },
+
midpoint : function midpoint(){
return new Loc( this.x1 + this.width /2
, this.y1 + this.height/2 );
init : function init(game, x1,y1, x2,y2, capacity) {
QuadTree.init.call(this, x1,y1, x2,y2, capacity);
+ this.game = game;
var w = this.width, h = this.height;
- this.game = game;
this.walls = {
- top : new math.Line(0,0, w,0),
- bottom : new math.Line(0,h, w,h),
- left : new math.Line(0,0, 0,h),
- right : new math.Line(w,0, w,h)
+ top : new Wall(0,0, w,1),
+ bottom : new Wall(0,h, w,1),
+ left : new Wall(0,0, 1,h),
+ right : new Wall(w,0, 1,h)
};
+ Y(this.walls).forEach(this.addBlocker, this);
+ },
+
+ addBlocker : function addBlocker(obj){
+ var bb = obj.boundingBox;
+ if (obj.blocking && bb)
+ obj.region = this.set(bb.x1,bb.y1, bb.x2,bb.y2, obj);
+ return obj;
+ },
+
+ removeBlocker : function removeBlocker(obj){
+ if (obj.region) {
+ this.remove(obj.region);
+ delete obj.region;
+ }
+ return obj;
+ },
+
+ updateBlocker : function updateBlocker(obj){
+ this.removeBlocker(obj);
+ this.addBlocker(obj);
+ return obj;
},
attemptMove : function attemptMove(obj, trj, to){
- var line, bb = obj.boundingBox
+ var wall, blocker, what, bb = obj.boundingBox
+ , maxW = this.width, maxH = this.height
, w = bb.width, h = bb.height
, x1 = to.x, y1 = to.y
, x2 = x1+w, y2 = y1+h
- , maxW = this.width, maxH = this.height
;
// Check for collision with the walls
- if (x1 <= 0 || x2 >= maxW || y1 <= 0 || y2 >= maxH){
- if (x1 <= 0) {
- line = this.walls.left;
- to = trj.pointX(w/2);
-
- } else if (x2 >= maxW) {
- line = this.walls.right;
- to = trj.pointX(maxW-w/2);
-
- } else if (y1 <= 0) {
- line = this.walls.top;
- to = trj.pointY(h/2);
-
- } else if (y2 >= maxH) {
- line = this.walls.bottom;
- to = trj.pointY(maxH-h/2);
- }
- }
+ // if (x1 < 0 || x2 > maxW || y1 < 0 || y2 > maxH){
+ // blocker = this.game.level;
+ // what = 'LevelWall on the ';
+ //
+ // if (x1 < 0) {
+ // what = 'left';
+ // wall = this.walls.left;
+ // to = trj.pointAtX(0);
+ //
+ // } else if (x2 > maxW) {
+ // what = 'right';
+ // wall = this.walls.right;
+ // to = trj.pointAtX(maxW-w);
+ //
+ // } else if (y1 < 0) {
+ // what = 'top';
+ // wall = this.walls.top;
+ // to = trj.pointAtY(0);
+ //
+ // } else if (y2 > maxH) {
+ // what = 'bottom';
+ // wall = this.walls.bottom;
+ // to = trj.pointAtY(maxH-h);
+ // }
+ //
+ // }
- var blocker = this.get(x1,y1, x2,y2).remove(obj).shift();
+ // Check for pathmap collisions
+ blocker = this.get(x1,y1, x2,y2).remove(obj).shift();
if ( blocker ) {
+ what = blocker+' on the ';
+
var B = blocker.boundingBox;
- if (bb.x2 < B.x1 && x2 >= B.x1) {
- line = B.sides.left;
- to = trj.pointX(B.x1-w/2);
- } else if (bb.x1 > B.x2 && x1 <= B.x2) {
- line = B.sides.right;
- to = trj.pointX(B.x2+w/2);
- } else if (bb.y2 < B.y1 && y2 >= B.y1) {
- line = B.sides.top;
- to = trj.pointX(B.y1-h/2);
- } else if (bb.y1 > B.y2 && y1 <= B.y2) {
- line = B.sides.bottom;
- to = trj.pointX(B.y2+h/2);
+ if (bb.x2 <= B.x1 && x2 > B.x1) {
+ what += 'left';
+ wall = B.sides.left;
+ to = trj.pointAtX(B.x1-w);
+ } else if (bb.x1 >= B.x2 && x1 < B.x2) {
+ what += 'right';
+ wall = B.sides.right;
+ to = trj.pointAtX(B.x2);
+ } else if (bb.y2 <= B.y1 && y2 > B.y1) {
+ what += 'top';
+ wall = B.sides.top;
+ to = trj.pointAtY(B.y1-h);
+ } else if (bb.y1 >= B.y2 && y1 < B.y2) {
+ what += 'bottom';
+ wall = B.sides.bottom;
+ to = trj.pointAtY(B.y2);
}
}
- return { 'ok':!line, 'to':to, 'line':line };
+ what += ' at '+wall;
+ return { 'ok':!wall, 'to':to, 'wall':wall, 'blocker':blocker, 'what':what };
},
return this;
},
+ _depth : 0,
move : function move(){
- var p, wall, p2 = this.trajectory.p2
- , level = this.game.level, walls = level.walls
- , w = level.layerWidth, h = level.layerHeight
- , trj = this.trajectory
- , to = this.trajectory.pcalc(this.elapsed)
+ var trj = this.trajectory
+ , to = trj.parametric(this.elapsed)
, test = this.game.pathmap.attemptMove(this, trj, to)
;
- // Do we need to reflect because we've hit a wall?
- // if (to.x <= 0 || to.x >= w || to.y <= 0 || to.y >= h) {
- // if (to.x <= 0) {
- // wall = walls.left;
- // to = trj.pointX(0);
- //
- // } else if (to.x >= w) {
- // wall = walls.right;
- // to = trj.pointX(w);
- //
- // } else if (to.y <= 0) {
- // wall = walls.top;
- // to = trj.pointY(0);
- //
- // } else if (to.y >= h) {
- // wall = walls.bottom;
- // to = trj.pointY(h);
- // }
+ // Something obstructs us -- time to reflect!
if (!test.ok) {
to = test.to;
- wall = test.line;
-
- p = math.reflect(p2, wall.tangent(to));
+ var wall = test.wall, p2 = trj.p2
+ , bb = this.boundingBox
+ , goal = wall.isWithin(p2,bb) ? trj.near(p2.x+2*REF_SIZE, p2.y+2*REF_SIZE) : p2
+ , _ng, ng = _ng = math.reflect(goal, wall)
+ , x1 = bb.x1, y1 = bb.y1
+ , tx = to.x, ty = to.y
+ , cmp = Y.op.cmp
+ ;
// Are we pointed the wrong direction? Rotate 180 degrees around the bullet
- if ( (p.x <= 0 && to.x <= 0) || (p.y <= 0 && to.y <= 0)
- || (p.x >= w && to.x >= w) || (p.y >= h && to.y >= h)
- ){
- var x = to.x - (p.x - to.x)
- , y = to.y - (p.y - to.y);
- p = new math.Vec(x,y);
- }
+ if ( cmp(tx,x1) !== cmp(goal.x,x1) || cmp(ty,y1) !== cmp(goal.y,y1) )
+ ng = new math.Vec(tx - ng.x + tx, ty - ng.y + ty);
this.trajectory = new math.Line(
- math.clamp(to.x, 0.1, w-0.1), math.clamp(to.y, 0.1, h-0.1),
- p.x,p.y,
+ to.x,to.y, ng.x,ng.y,
this.stats.move * REF_SIZE/1000 );
- // console.log('p2='+p2+' reflected in '+wall.tangent(to)+' --> '+this.trajectory);
+ // console.log('['+this._depth+' '+TICKS+'] '+this+' reflected by', test.what);
+ // console.log(' to='+to+', goal='+goal+(p2.equals(goal) ? '' : ' <~ '+p2));
+ // console.log(' --> new goal='+ng+(ng.equals(_ng) ? '' : ' <~ '+_ng));
+ // console.log(' --> trajectory='+this.trajectory);
+
+ this.render(this.game.level).draw();
+ this.elapsed = ELAPSED;
+
+ if (this._depth++ < 3)
+ return this.move();
- this.render(this.game.level);
- this.elapsed = 0;
- this.dirty = true;
- return this.move();
+ console.log('['+this._depth+' '+TICKS+'] '+this+' reflected by', test.what);
+ console.log(' to='+to+', goal='+goal+(p2.equals(goal) ? '' : ' <~ '+p2));
+ console.log(' --> new goal='+ng+(ng.equals(_ng) ? '' : ' <~ '+_ng));
+ console.log(' --> trajectory='+this.trajectory);
+ throw new Error('Reflection error!');
}
+ // if (this._depth > 0)
+ // console.warn('['+this._depth+'@'+TICKS+'] ok!');
+
+ this._depth = 0;
this.game.moveAgentTo(this, to.x, to.y);
return this;
},
var t = this.trajectory;
this.tline = Line.fromPoints(t.x1,t.y1, t.x2,t.y2)
- .attr('strokeStyle', 'rgba(255,246,174, 0.05)')
+ .attr('drawDefinitionPoints', true)
+ .stroke('rgba(255,246,174, 0.05)')
.appendTo( parent );
this.shape = new Circle(3)
.position(this.loc.x, this.loc.y)
- .attr({
- 'fillStyle' : '#EC5B38',
- 'strokeStyle' : 'transparent',
- 'lineWidth' : 0
- })
+ .fill('#EC5B38')
.appendTo( parent );
return this;
},
toString : function toString(){
-
- return this.className+'(loc='+this.loc+', traj='+this.trajectory+', owner='+this.owner+')';
+ // return this.className+'(loc='+this.loc+', traj='+this.trajectory+', owner='+this.owner+')';
+ return this.className+'(loc='+this.loc+')';
}
});
var ProjectileType = this.projectile
, p = new ProjectileType(this, x,y);
this.bullets.push(p);
- this.game.addUnit(p);
+ this.game.addUnit(p).render(this.game.level);
return p;
},