var lastTick = this.now;
this.now = new Date().getTime();
this.elapsed = this.now - lastTick;
- this.perceived = this.elapsed/this.dilation;
+ this.perceived = this.elapsed/this.timeDilation;
clearTimeout(this.timer);
this.timer = null;
-var Y = require('Y').Y
-, Vec = require('ezl/math/vec').Vec
+var Y = require('Y').Y
+, Vec = require('ezl/math/vec').Vec
+, Line = require('ezl/math/line').Line
+
, X1 = 0, Y1 = 1
, X2 = 2, Y2 = 3
,
// game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
E =
- game.addThing(new Tank(2), 0,1);
- game.addThing(new Tank(2), 1,0);
+ game.addThing(new Tank(2), 0,0);
+ // game.addThing(new Tank(2), 1,0);
// game.addThing(new Tank(2), 8,1);
I = game.addThing(new Item(), 8,8);
// Would be nice if I could make this an inner class (for once)
Square =
exports['Square'] =
-Vec.subclass('Square', {
+Y.subclass('Square', new Vec(0,0), {
pathId : null, // instance of A* being run, to track when to reset
// resetable:
return this;
this.pathId = pathId;
- this._blocked();
+ this.blocked = this._blocked();
this.dist = 0;
this.startDist = 0;
},
/**
- * @private Caches this.blocked value. Should only be called by reset().
+ * @private Calculates this.blocked value. Should only be called by reset() to cache the result.
*/
_blocked : function blocked(){
var pm = this.pathmap
, SIZE = pm.gridSquare
, x = Math.floor(this.x)
, y = Math.floor(this.y)
- , x1 = x - left, x2 = x + SIZE + right
- , y1 = y - top, y2 = y + SIZE + bottom
+ // , x1 = x - left, x2 = x + SIZE + right
+ // , y1 = y - top, y2 = y + SIZE + bottom
+ , x1 = x, x2 = x + SIZE
+ , y1 = y, y2 = y + SIZE
, blockers = pm.get(x1,y1, x2,y2)
.remove(agent)
.filter(this._filterBlocked)
;
- this.blocked = !!blockers.length;
+ return !!blockers.length;
},
/**
, SIZE = pm.gridSquare
, x = this.x, y = this.y
- , sq, cost, ix, iy
+ , sq, cost, ix, iy, x1,y1, x2,y2
;
for (ix=-1; ix<2; ix++) {
for (iy=-1; iy<2; iy++) {
- // if (ix === 0 && iy === 0) // skipping diagonals
- if ( (ix === 0 && iy === 0) || (abs(ix) === 1 && abs(iy) === 1) )
+ // if (ix === 0 && iy === 0)
+ if ( (ix === 0 && iy === 0) || (abs(ix) === 1 && abs(iy) === 1) ) // skipping diagonals
+ continue;
+ x1 = x + ix*SIZE; y1 = y + iy*SIZE;
+ x2 = x1+SIZE; y2 = y1+SIZE;
+ if ( pm.x1 >= x1 || pm.y1 >= y1 ||
+ pm.x2 <= x2 || pm.y2 <= y2 )
continue;
// TODO: handle diagonals: need to ensure we don't try to move through a corner
// cost = ( abs(ix) === 1 && abs(iy) === 1 ) ? SQRT_TWO : 1;
cost = 1;
- sq = pm._getSquare(x + ix*SIZE, y + iy*SIZE);
- if ( !sq.blocked ) neighbors.push([ sq, cost*SIZE ]);
+ sq = pm._getSquare(x1,y1);
+ // if ( !sq.blocked ) neighbors.push([ sq, cost*SIZE ]);
+ neighbors.push([ sq, cost*SIZE ]);
}
}
return neighbors;
+ },
+
+ toString : function toString(){
+ var props = [];
+ if (this.blocked) props.push('blocked');
+ if (this.visited) props.push('dist='+this.dist);
+ if (this.closed) props.push('closed');
+ props = props.join(', ');
+ if (props) props = '('+props+')';
+ return '['+this.x+' '+this.y+']'+props;
}
})
*/
_getSquare : function getSquare(x,y){
if (x instanceof Array) { y=x[1]; x=x[0]; }
- x = Math.floor(x); y = Math.floor(y);
-
var cache = this._squares
- , key = x+"_"+y
+ , SIZE = this.gridSquare ;
+
+ x = Math.floor(x / SIZE) * SIZE;
+ y = Math.floor(y / SIZE) * SIZE;
+ var key = x+'_'+y
, sq = cache[key] ;
if (sq)
, start = this._getSquare(agent.loc)
, end = this._getSquare(x,y)
, open = new BinaryHeap('dist') // 'dist' is the scoring key on heap objects
+
+ , iterations = 0
;
open.push(start);
while (open.length) {
+ iterations++;
// Grab the lowest f(x) to process next. Heap keeps this sorted for us.
var current = open.pop();
// End case -- result has been found, return the traced path
if (current === end) {
var path = []
+ , mid = this.gridSquareMid
, node = current;
while( node.prev ) {
- path.push( node );
+ path.push( vec.sum(node,mid) );
node = node.prev;
}
return path.reverse();
} else
open.rescore(node);
}
+ var _node = node;
}
}
init : function initTraversal(thing, trajectory){
this.thing = thing;
this.game = thing.game;
- this.pathmap = thing.pathmap;
+ this.pathmap = thing.game.pathmap;
this.bbox = thing.boundingBox.clone();
this.trajectory = trajectory || thing.trajectory;
if (this.isBlocked) {
this.remaining = dt;
- this.thing.fire('collide', this.blocker);
- this.blocker.fire('collide', this.thing);
+ var data = { 'traversal':this };
+ this.thing.fire('collide', this.blocker, data);
+ this.blocker.fire('collide', this.thing, data);
break;
}
*/
stepTo : function stepTo(t, tx,ty){
var blockers, blocker, B
+ , abs = Math.abs
, traj = this.trajectory, thing = this.thing
, bb = this.bbox
// TODO: Either: this never happens (current belief) OR it needs better corner-detection
// Literal corner case :P
if (blockers.length > 1) {
- console.error('Corner! WHO KNEW.', 'to:', to, 'blockers:', blockers);
+ console.error('Corner!', 'to:', to, 'blockers:', blockers);
+ // throw new Error('Corner! to:'+to+', blockers:'+blockers);
this.isCorner = true;
- // to = trj.p1; // Turn around, go back whence you came
+ // to = traj.p1; // Turn around, go back whence you came
}
// blocker = traj.closest(blockers)
// Figure out which boundary of the blocker we crossed and calculate
// the furthest non-overlapping point.
- if (bb.x2 <= B.x1 && x2 >= B.x1) {
- this.to = to = trj.pointAtX(B.x1 - 1 - bb.originRight);
+ if (x2 <= B.x1 && bb.x2 >= B.x1) {
+ this.to = to = traj.pointAtX(B.x1 - 1 - bb.originRight);
this.side = B.leftSide;
- } else if (bb.x1 >= B.x2 && x1 <= B.x2) {
- this.to = to = trj.pointAtX(B.x2 + 1 + bb.originLeft);
+ } else if (x1 >= B.x2 && bb.x1 <= B.x2) {
+ this.to = to = traj.pointAtX(B.x2 + 1 + bb.originLeft);
this.side = B.rightSide;
- } else if (bb.y2 <= B.y1 && y2 >= B.y1) {
- this.to = to = trj.pointAtY(B.y1 - 1 - bb.originTop);
+ } else if (y2 <= B.y1 && bb.y2 >= B.y1) {
+ this.to = to = traj.pointAtY(B.y1 - 1 - bb.originTop);
this.side = B.topSide;
- } else if (bb.y1 >= B.y2 && y1 <= B.y2) {
- this.to = to = trj.pointAtY(B.y2 + 1 + bb.originBottom );
+ } else if (y1 >= B.y2 && bb.y1 <= B.y2) {
+ this.to = to = traj.pointAtY(B.y2 + 1 + bb.originBottom );
this.side = B.bottomSide;
+
} else {
console.error('Null reflection line!', 'to:', to, 'blockers:', blockers);
+ // throw new Error('Null reflection line! to:'+to+', blockers:'+blockers);
}
// Move bounding box to furthest non-overlapping point
bb.relocate(to);
// Return how much time consumed
- return trj.timeToMove(to.x-o.x, to.y-o.y);
+ return traj.timeToMove(to.x-o.x, to.y-o.y);
}
})
move : function move(elapsed, now){
if (this.dead) return this;
- var tvsl = new Traversal(this, this.trajectory)
+ var tvsl = new Traversal(this)
, to = tvsl.step(elapsed)
;
if ( this.dead ) return;
var ng
- , tvsl = evt.target
+ , tvsl = evt.data.traversal
, traj = this.trajectory
, to = tvsl.to
act : function act(elapsed, now){
+ this.elapsed = elapsed;
+ this.now = now;
+
if (this.dead)
return this;
if (!dir) return;
// @see Tank.move()
- this.move(this.loc.moveByDir(dir, this.stats.move * SQUARETH));
+ this.move( this.loc.moveByDir(dir, this.stats.move * SQUARETH) );
},
// _move : function _move(){
this['act'] =
function act(elapsed, now){
+ this.elapsed = elapsed;
+ this.now = now;
+
var ai = this.ai;
// Check to see if we should obey our last decision, and not recalc
, cur = this.currentMove
;
- if (cur.x !== x || cur.y !== y)
+ if ( !(this.trajectory && cur && cur.x === x && cur.y === y) )
this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs);
- var tvsl = new Traversal(this, this.trajectory)
- , to = tvsl.step(elapsed, x,y)
+ var tvsl = new Traversal(this)
+ , to = tvsl.step(this.elapsed, x,y)
;
this.game.moveThingTo(this, to.x,to.y);
* @protected
* This method does not update this.trajectory -- call this.move(x,y) instead.
*/
- this['moveByAngle'] =
- function moveByAngle(theta, targetX,targetY){
- var abs = Math.abs
- , bb = this.boundingBox
- , loc = this.loc
- , to = loc.moveByAngle(theta, this.stats.move * SQUARETH)
- , x = to.x, y = to.y
- ;
-
- // Don't overshoot the target
- if ( targetX !== undefined && abs(loc.x-x) > abs(loc.x-targetX) )
- x = targetX;
-
- if ( targetY !== undefined && abs(loc.y-y) > abs(loc.y-targetY) )
- y = targetY;
-
- var nbb = bb.relocated(x,y)
- , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this);
-
- if ( !blockers.size() )
- this.game.moveThingTo(this, x,y);
-
- return this;
- };
+ // this['moveByAngle'] =
+ // function moveByAngle(theta, targetX,targetY){
+ // var abs = Math.abs
+ // , bb = this.boundingBox
+ // , loc = this.loc
+ // , to = loc.moveByAngle(theta, this.stats.move * SQUARETH)
+ // , x = to.x, y = to.y
+ // ;
+ //
+ // // Don't overshoot the target
+ // if ( targetX !== undefined && abs(loc.x-x) > abs(loc.x-targetX) )
+ // x = targetX;
+ //
+ // if ( targetY !== undefined && abs(loc.y-y) > abs(loc.y-targetY) )
+ // y = targetY;
+ //
+ // var nbb = bb.relocated(x,y)
+ // , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this);
+ //
+ // if ( !blockers.size() )
+ // this.game.moveThingTo(this, x,y);
+ //
+ // return this;
+ // };
this['continueMove'] =
function continueMove(){
- if ( !this.currentMove || this.currentMoveLimit <= NOW ){
+ if ( !this.currentMove || this.ai.path.ready ){
var target = this.findNearEnemies(10000).shift();
if (target) this.calculatePath(target.loc);
}
var to = this.currentMove;
if (!to) return;
- this.move(to.x, to.y);
- if ( this.midpoint.equals(to) ) {
+ this.move(to);
+ if ( this.loc.equals(to) ) {
this.forceCurrentMove = false;
this.currentMoveLimit = -1;
this.currentMove = (this.lastPath ? this.lastPath.shift() : null);