-- use unit vectors for trajectories
---> use matrix math for reflections
-- incremental calls to move
-- collision object for recalculating pathing
-
# Bugs
-- Have move() do incremental calls (consuming elapsed time? distance?) if a push results in a move of x or y larger than the bounding box.
# TODOs
- Move game objects into namespace `tanks`
# Notes
- TODO Replace *2 and /2 with shifts at compile-time
- Clipping will suck (masking is easy -- overflow:hidden)
+- Should use unit vectors for trajectories?
+--> matrix math for reflections?
+
// 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.boundingBox = this.boundingBox.add(pos.left,pos.top);
this.loc = this.boundingBox.p1;
this.css(pos);
return this;
+++ /dev/null
-(function(){
-
-var
-HALF_PI = Math.PI/2,
-SIN_HALF_PI = Math.sin(HALF_PI),
-COS_HALF_PI = Math.cos(HALF_PI);
-
-
-/**
- * A line in the cartesian plane.
- */
-math.Line = new Y.Class('Line', math.Vec, {
-
- init : function initLine(x1,y1, x2,y2, tdist){
- this.x1 = x1; this.y1 = y1;
- this.x2 = x2; this.y2 = y2;
-
- var xdelta = x2-x1, ydelta = y2-y1
- , m = this.slope = ydelta/xdelta
- , yi = this.yint = -x1*m + y1
- , xi = this.xint = -y1/m + x1
- ;
- math.Vec.init.call(this, xdelta, ydelta);
-
- this.p1 = new math.Vec(x1,y1);
- this.p2 = new math.Vec(x2,y2);
-
- this.theta = Math.atan2(ydelta, xdelta);
- this._cos = Math.cos(this.theta);
- this._sin = Math.sin(this.theta);
-
- this.setTScale(tdist);
- },
-
- clone : function clone(){
- return new math.Line(this.x1,this.y1, this.x2,this.y2, this.tdist);
- },
-
- equals : function equals(line){
- return ( this.slope === line.slope
- && this.x1 === line.x1 && this.y1 === line.y1
- && this.x2 === line.x2 && this.y2 === line.y2 );
- },
-
- intersects : function intersects(x,y){
- var o = x;
- if (o instanceof math.Line)
- return this.slope !== o.slope || this.equals(o);
-
- if (o instanceof math.Vec) {
- x = o.x;
- y = o.y;
- }
- 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;
- this.pa = tdist * this._cos;
- this.pb = tdist * this._sin;
- } else {
- this.tdist = this.y / this._sin;
- this.pa = this.x;
- this.pb = this.y;
- }
- return this;
- },
-
- parametric : function parametric(t){
- return new math.Vec( this.x1 + t*this.pa ,
- this.y1 + t*this.pb );
- },
-
- pointAtX : function pointAtX(x){
- return new math.Vec(x, this.calcY(x));
- },
-
- 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.xint ? 0 : x*this.slope + this.yint);
- },
-
- calcX : function calcX(y){
- return (y === this.yint ? 0 : y/this.slope + this.xint);
- },
-
- base : function base(){
- if (!this._base)
- this._base = new math.Line(0,0, this.x,this.y);
- return this._base;
- },
-
- tangent : function tangent(at){
- var slope = this.slope;
-
- if ( slope === 0 )
- return new math.Line(at.x,at.y, at.x,at.y+1, this.tdist);
-
- if ( !isFinite(slope) )
- return new math.Line(at.x,at.y, at.x+1,at.y, this.tdist);
-
- var x1 = at.x, y1 = at.y
- , x2 = at.x - at.y + (at.y !== this.y1 ? this.y1 : this.y2)
- , y2 = at.y + at.x - (at.x !== this.x1 ? this.x1 : this.x2) ;
- return new math.Line(x1,y1, x2,y2, this.tdist);
- },
-
- toString : function toString(){
- return '['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
- }
-
-});
-
-})();
\ No newline at end of file
traceTrajectories : true
},
debug : {
- projectiles : 1
+ projectiles : 2
}
};
\ No newline at end of file
var btank = null
, bullets = new Y.YArray()
;
-
-
(function(){
-
jQuery(main);
new Player(LBT, T);
setupUI();
- B = bullets.attr(0);
- T = B.trajectory;
+ // B = bullets.attr(0);
+ // T = B.trajectory;
}
Collision = new Y.Class('Collision', {
-
+ init : function initCollision(line, to){
+
+ },
});
\ No newline at end of file
attr : Y.attr.methodize(),
- moveTo : function moveTo(x,y){
+ add : function add(x,y){
return new Loc.BoundingBox(x,y, x+this.width,y+this.height);
},
return obj;
},
- attemptMove : function attemptMove(obj, trj, to){
- var wall, blocker, what
- , clamp = math.clamp
- , bb = obj.boundingBox
- , minW = 2, minH = 2
- , maxW = this.width-2, maxH = this.height-2
- , bw = bb.width, bh = bb.height
- , x1 = to.x, y1 = to.y
- , x2 = x1+bw, y2 = y1+bh
+ moveBlocked : function moveBlocked(agent, trj, to){
+ var blockers, blocker, msg
+ , side = null
+
+ , bb = agent.boundingBox
+ , bw = bb.width, bh = bb.height
+ , offX = agent.offsetX, offY = agent.offsetY
+
+ , x1 = to.x+offX, y1 = to.y+offY
+ , x2 = x1+bw, y2 = y1+bh
;
- // Check for collision with the walls to prevent teleporting units
- // if (x1 < minW || x2 > maxW || y1 < minH || y2 > maxH){
- // blocker = this.game.level;
- // what = 'LevelWall on the ';
- //
- // if (x1 < minW) {
- // what = 'left';
- // wall = this.walls.left;
- // to = trj.pointAtX(minW);
- //
- // } else if (x2 > maxW) {
- // what = 'right';
- // wall = this.walls.right;
- // to = trj.pointAtX(maxW-bw);
- //
- // } else if (y1 < minH) {
- // what = 'top';
- // wall = this.walls.top;
- // to = trj.pointAtY(minH);
- //
- // } else if (y2 > maxH) {
- // what = 'bottom';
- // wall = this.walls.bottom;
- // to = trj.pointAtY(maxH-bh);
- // }
- //
- // what += ' at '+wall;
- // return { 'ok':!wall, 'to':to, 'wall':wall, 'blocker':blocker, 'what':what };
- // }
+ blockers = this.get(x1,y1, x2,y2).remove(agent).end();
+ blocker = blockers[0];
- // Check for pathmap collisions
- var blockers = this.get(x1,y1, x2,y2).remove(obj);
- if (blockers.size() > 1){
- console.log('multiple blockers! '+blockers._o.slice(0));
- return { 'ok':false, 'corner':true };
- }
+ // Not blocked
+ if (!blocker) return false;
- blocker = blockers.shift();
- if ( blocker ) {
- what = blocker+' on the ';
+ // Literal corner case :P
+ // XXX: needs better detection of corner
+ if (blockers.length > 1) {
+ msg = 'corner of '+blockers.slice(0);
+ to = trj.p1;
+
+ // Normal reflection against one line
+ } else {
+ msg = blocker+' on the ';
var B = blocker.boundingBox;
if (bb.x2 <= B.x1 && x2 > B.x1) {
- what += 'left';
- wall = B.sides.left;
- to = trj.pointAtX(B.x1-bw);
+ msg += 'left';
+ side = B.sides.left;
+ to = trj.pointAtX(B.x1-bw-offX-1);
+
} else if (bb.x1 >= B.x2 && x1 < B.x2) {
- what += 'right';
- wall = B.sides.right;
- to = trj.pointAtX(B.x2);
+ msg += 'right';
+ side = B.sides.right;
+ to = trj.pointAtX(B.x2-offX+1);
+
} else if (bb.y2 <= B.y1 && y2 > B.y1) {
- what += 'top';
- wall = B.sides.top;
- to = trj.pointAtY(B.y1-bh);
+ msg += 'top';
+ side = B.sides.top;
+ to = trj.pointAtY(B.y1-bh-offY-1);
+
} else if (bb.y1 >= B.y2 && y1 < B.y2) {
- what += 'bottom';
- wall = B.sides.bottom;
- to = trj.pointAtY(B.y2);
+ msg += 'bottom';
+ side = B.sides.bottom;
+ to = trj.pointAtY(B.y2-offY+1);
}
+
+ msg += ' side';
}
- // to.setXY(
- // clamp(to.x, 2,maxW-2),
- // clamp(to.y, 2,maxH-2) );
-
- what += ' at '+wall;
- return { 'ok':!wall, 'to':to, 'wall':wall, 'blocker':blocker, 'what':what };
+ return { 'msg':msg, 'blockers':blockers, 'side':side, 'to':to };
},
;
do {
- var t, to, ng, x1,y1, x2,y2, bs, blocker, blockers, what, wall;
+ var t, to, _to, ng
+ , test, blockers, side
+ ;
t = Math.min(this.tBound, dt);
dt -= t;
this.elapsed += t;
- var _to = to = this.parametric(this.elapsed);
- x1 = to.x+offX; y1 = to.y+offY;
- x2 = x1+bw; y2 = y1+bh;
+ _to = to = this.parametric(this.elapsed);
+ test = this.pathmap.moveBlocked(o, this, to);
- bs = this.pathmap.get(x1,y1, x2,y2).remove(o).end();
- blocker = bs[0];
-
- // Literal corner case :P
- if (bs.length > 1) {
- console.log('multiple blockers! '+bs.slice(0));
- what = 'corner of '+bs.slice(0);
- to = loc;
- ng = this.p1; // XXX: recalculate?
-
- // Normal reflection against one line
- } else if ( blocker ) {
- what = blocker+' on the ';
+ // Blocked! Reflect trajectory
+ if ( test ) {
+ to = test.to;
+ side = test.side;
+ blockers = test.blockers;
- var B = blocker.boundingBox;
- if (bb.x2 <= B.x1 && x2 > B.x1) {
- what += 'left';
- wall = B.sides.left;
- to = this.pointAtX(B.x1-bw-offX-1);
-
- } else if (bb.x1 >= B.x2 && x1 < B.x2) {
- what += 'right';
- wall = B.sides.right;
- to = this.pointAtX(B.x2-offX+1);
-
- } else if (bb.y2 <= B.y1 && y2 > B.y1) {
- what += 'top';
- wall = B.sides.top;
- to = this.pointAtY(B.y1-bh-offY-1);
-
- } else if (bb.y1 >= B.y2 && y1 < B.y2) {
- what += 'bottom';
- wall = B.sides.bottom;
- to = this.pointAtY(B.y2-offY+1);
+ if ( blockers.length > 1 ) {
+ to = loc;
+ ng = this.p1; // XXX: recalculate?
+ } else {
+ ng = math.reflect(this.p2, side);
}
- ng = math.reflect(this.p2, wall);
- }
-
- // No collision: don't change trajectory
- if (!ng) {
- this.depth = 0;
-
- // New goal-point: change trajectory
- } else {
var og = this.p2;
this.reset(to.x,to.y, ng.x,ng.y);
console.log([
'['+TICKS+':'+_dt+' ('+this.depth+')] '+this.owner+' reflected!',
' wanted: '+_to+' x ('+(_to.x+bw)+','+(_to.y+bh)+')',
- ' blocker: '+what,
+ ' blocker: '+test.msg,
' old:',
' loc: '+bb.p1,
' goal: '+og,
ng = null;
this.owner.render(this.game.level);
- // this.owner.render(this.game.level).draw();
if (this.depth++ < 5) {
// dt += t; // do this step over again
LBT.stop();
throw new Error('Reflection limit reached!');
}
+
+
+ // No collision: don't change trajectory
+ } else {
+ this.depth = 0;
}
} while (dt > 0);
moveDirFromKey : {
37: "left", 38: "up", 39: "right", 40: "down",
- 65: "left", 87: "up", 83: "right", 68: "down"
+ 65: "left", 87: "up", 68: "right", 83: "down"
}
};
this.activeKeys = new Y.YArray();
- this.game = game;
this.tank = tank;
+ this.game = game;
+ this.pathmap = game.pathmap;
tank.act = this.act; // Override tank actions with player control
tank.move = this.move;
},
keydown : function keydown(evt){
- this.activeKeys.push(evt.which+'');
+ var k = evt.which+'';
+ if ( !this.activeKeys.has(k) )
+ this.activeKeys.push(k);
this.updateMeta(evt);
},
move : function move(dir){
var tank = this.tank
- , toLoc = tank.loc.moveByDir(dir, (tank.stats.move * SQUARETH));
- this.game.moveAgentTo(tank, toLoc.x, toLoc.y);
+ , toLoc = tank.loc.moveByDir(dir, (tank.stats.move * SQUARETH))
+
+ , x = toLoc.x, y = toLoc.y
+ , bb = tank.boundingBox.add(x,y)
+
+ , blockers = this.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(tank)
+ ;
+
+ if ( !blockers.size() )
+ this.game.moveAgentTo(tank, x,y);
}
});
\ No newline at end of file