Moves asset files.
*.md.html
*.pyc
www/versioned-deps.html
+google74dcf785afbc60e0.html
Y.extend(exports, {
'Map' : require('tanks/map/map').Map,
'PathMap' : require('tanks/map/pathmap').PathMap,
- 'Wall' : require('tanks/map/wall').Wall,
'Level' : require('tanks/map/level').Level,
'Traversal' : require('tanks/map/traversal').Traversal,
'Trajectory' : require('tanks/map/trajectory').Trajectory
, Item = require('tanks/thing/item').Item
, PlayerTank = require('tanks/thing/player').PlayerTank
, PathMap = require('tanks/map/pathmap').PathMap
-, Wall = require('tanks/map/wall').Wall
+, Wall = require('tanks/thing/wall').Wall
,
// game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
E =
- game.addThing(new Tank(2), 0,5);
- game.addThing(new Tank(2), 1,0);
- game.addThing(new Tank(2), 8,1);
+ game.addThing(new Tank(2), 0,7);
+ // game.addThing(new Tank(2), 1,0);
+ // game.addThing(new Tank(2), 8,1);
- I = game.addThing(new Item(), 8,8);
+ // I = game.addThing(new Item(), 8,8);
},
addWall : function addWall(x,y, w,h, isBoundary){
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
var Y = require('Y').Y
-
-, math = require('ezl/math')
-, vec = require('ezl/math/vec')
, QuadTree = require('ezl/util/tree/quadtree').QuadTree
-, astar = require('ezl/util/astar')
-, config = require('tanks/config').config
-, Bullet = require('tanks/thing/bullet').Bullet
-, Vec = vec.Vec
-, Line = math.Line
+/** Does not obstruct other objects. */
+, PASSABLE = exports['PASSABLE'] = 0
+
+/** Does not obstruct other objects, but still collides with them. */
+, ZONE = exports['ZONE'] = 1
+
+/** Obstructs other blockers with its BoundingBox. */
+, BLOCKING = exports['BLOCKING'] = 2
+
+/** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
+, IRREGULAR = exports['IRREGULAR'] = 3
,
delete obj.region;
}
return obj;
- },
-
- // moveBlocked : function moveBlocked(agent, trj, to, bb){
- // bb = bb || agent.boundingBox;
- // var blockers, blocker, msg
- // , side = null
- // , tobb = bb.relocated(to.x,to.y)
- // , x1 = tobb.x1, y1 = tobb.y1
- // , x2 = tobb.x2, y2 = tobb.y2
- //
- // , ro = bb.relOrigin
- // , bw = bb.width, bh = bb.height
- // , offX = bw - ro.x, offY = bh = ro.y
- //
- // // , x1 = to.x+offX, y1 = to.y+offY
- // // , x2 = x1+bw, y2 = y1+bh
- // ;
- //
- // blockers = this.get(x1,y1, x2,y2).remove(agent).end();
- // blocker = blockers[0];
- //
- // // Not blocked
- // if (!blocker) return false;
- //
- // // 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) {
- // msg += 'left';
- // side = B.leftSide;
- // to = trj.pointAtX(B.x1-offX-1);
- //
- // } else if (bb.x1 >= B.x2 && x1 <= B.x2) {
- // msg += 'right';
- // side = B.rightSide;
- // to = trj.pointAtX(B.x2+ro.x+1);
- //
- // } else if (bb.y2 <= B.y1 && y2 >= B.y1) {
- // msg += 'top';
- // side = B.topSide;
- // to = trj.pointAtY(B.y1-offY-1);
- //
- // } else if (bb.y1 >= B.y2 && y1 <= B.y2) {
- // msg += 'bottom';
- // side = B.bottomSide;
- // to = trj.pointAtY(B.y2+ro.y+1);
- // }
- //
- // msg += ' side';
- // }
- //
- // return { 'msg':msg, 'blockers':blockers, 'side':side, 'to':to };
- // },
-
+ }
});
, vec = require('ezl/math/vec')
, QuadTree = require('ezl/util/tree/quadtree').QuadTree
, BinaryHeap = require('ezl/util/binaryheap').BinaryHeap
-
-, config = require('tanks/config').config
-, Bullet = require('tanks/thing/bullet').Bullet
-, Map = require('tanks/map/map').Map
, Vec = vec.Vec
, Line = math.Line
+, config = require('tanks/config').config
+, map = require('tanks/map/map')
+, Map = map.Map
-, SQRT_TWO = Math.sqrt(2)
-, PASSABLE = 1 // Does not obstruct other objects
-, BLOCKING = 2 // Obstructs other blockers
-, ZONE = 3 // Does not obstruct other objects, but still collides with them
+, SQRT_TWO = Math.sqrt(2)
,
-
-// Would be nice if I could make this an inner class (for once)
-Square =
-exports['Square'] =
-Y.subclass('Square', new Vec(0,0), {
- pathId : null, // instance of A* being run, to track when to reset
-
- // resetable:
- dist : 0, // estimated distance
- startDist : 0, // distance from start
- endDist : 0, // estimated distance to end
- visited : false,
- closed : false,
- prev : null,
-
-
- /**
- * @param x Left coord of square.
- * @param y Top coord of square.
- */
- init : function initSquare(pathmap, x,y){
- Vec.init.call(this, x,y);
- this.pathmap = pathmap;
- this.reset();
- },
-
- reset : function reset(){
- var pathId = this.pathmap._pathId;
- if (this.pathId === pathId)
- return this;
-
- this.pathId = pathId;
- this.blocked = this._blocked();
-
- this.dist = 0;
- this.startDist = 0;
- this.endDist = 0;
- this.visited = false;
- this.closed = false;
- this.prev = null;
-
- return this;
- },
-
- /**
- * @private Calculates this.blocked value. Should only be called by reset() to cache the result.
- */
- _blocked : function blocked(){
- var pm = this.pathmap
- , agent = pm._agent
- // , bb = agent.boundingBox
-
- // , origin = bb.relOrigin
- // , left = origin.x, right = bb.width - left
- // , top = origin.y, bottom = bb.height - top
-
- , 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, x2 = x + SIZE
- , y1 = y, y2 = y + SIZE
-
- , blockers = pm.get(x1,y1, x2,y2)
- .remove(agent)
- .filter(this._filterBlocked)
- ;
-
- return !!blockers.length;
- },
-
- /**
- * @private
- */
- _filterBlocked : function filterBlocked(v, r){
- return !(v.isBoundary || v instanceof Bullet); // XXX: use PASSABLE
- },
-
- getNeighbors : function getNeighbors(){
- var neighbors = []
- , abs = Math.abs
- , pm = this.pathmap
- , agent = pm._agent
- , SIZE = pm.gridSquare
-
- , x = this.x, y = this.y
- , sq, cost, ix, iy, x1,y1, x2,y2
- ;
-
- for (ix=-1; ix<2; ix++) {
- for (iy=-1; iy<2; iy++) {
- // Skip self
- if (ix === 0 && iy === 0)
- continue;
-
- cost = 1;
- x1 = x + ix*SIZE; y1 = y + iy*SIZE;
- x2 = x1+SIZE; y2 = y1+SIZE;
-
- // filter squares outside bounds
- if ( pm.x1 >= x1 || pm.y1 >= y1 ||
- pm.x2 <= x2 || pm.y2 <= y2 )
- continue;
-
- // Diagonal square
- if ( abs(ix) === 1 && abs(iy) === 1 ) {
- cost = SQRT_TWO;
-
- // Ensure we don't cut a blocked corner
- if ( pm._getSquare(x1,y).blocked || pm._getSquare(x,y1).blocked )
- continue;
- }
-
- sq = pm._getSquare(x1,y1);
- 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;
- }
-
-})
-
-
-
/**
* A QuadTree which aids in pathing for AI.
* - QuadTree methods which took rect coords (x1,y1, x2,y2) will accept
return new Vec(floor(x)*size, floor(y)*size);
}
-});
+})
+,
+
+
+// Would be nice if I could make this an inner class (for once)
+Square =
+exports['Square'] =
+Y.subclass('Square', new Vec(0,0), {
+ pathId : null, // instance of A* being run, to track when to reset
+
+ // resetable:
+ dist : 0, // estimated distance
+ startDist : 0, // distance from start
+ endDist : 0, // estimated distance to end
+ visited : false,
+ closed : false,
+ prev : null,
+
+
+ /**
+ * @param x Left coord of square.
+ * @param y Top coord of square.
+ */
+ init : function initSquare(pathmap, x,y){
+ Vec.init.call(this, x,y);
+ this.pathmap = pathmap;
+ this.reset();
+ },
+
+ reset : function reset(){
+ var pathId = this.pathmap._pathId;
+ if (this.pathId === pathId)
+ return this;
+
+ this.pathId = pathId;
+ this.blocked = this._blocked();
+
+ this.dist = 0;
+ this.startDist = 0;
+ this.endDist = 0;
+ this.visited = false;
+ this.closed = false;
+ this.prev = null;
+
+ return this;
+ },
+
+ /**
+ * @private Calculates this.blocked value. Should only be called by reset() to cache the result.
+ */
+ _blocked : function blocked(){
+ var pm = this.pathmap
+ , agent = pm._agent
+ // , bb = agent.boundingBox
+
+ // , origin = bb.relOrigin
+ // , left = origin.x, right = bb.width - left
+ // , top = origin.y, bottom = bb.height - top
+
+ , 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, x2 = x + SIZE
+ , y1 = y, y2 = y + SIZE
+
+ , blockers = pm.get(x1,y1, x2,y2)
+ .remove(agent)
+ .filter(this._filterBlocked, this)
+ ;
+
+ return !!blockers.length;
+ },
+
+ /**
+ * @private
+ */
+ _filterBlocked : function filterBlocked(v, r){
+ return v.blocking === map.BLOCKING && !v.isBoundary;
+ },
+
+ getNeighbors : function getNeighbors(){
+ var neighbors = []
+ , abs = Math.abs
+ , pm = this.pathmap
+ , agent = pm._agent
+ , SIZE = pm.gridSquare
+
+ , x = this.x, y = this.y
+ , sq, cost, ix, iy, x1,y1, x2,y2
+ ;
+
+ for (ix=-1; ix<2; ix++) {
+ for (iy=-1; iy<2; iy++) {
+ // Skip self
+ if (ix === 0 && iy === 0)
+ continue;
+
+ cost = 1;
+ x1 = x + ix*SIZE; y1 = y + iy*SIZE;
+ x2 = x1+SIZE; y2 = y1+SIZE;
+
+ // filter squares outside bounds
+ if ( pm.x1 >= x1 || pm.y1 >= y1 ||
+ pm.x2 <= x2 || pm.y2 <= y2 )
+ continue;
+
+ // Diagonal square
+ if ( abs(ix) === 1 && abs(iy) === 1 ) {
+ cost = SQRT_TWO;
+
+ // Ensure we don't cut a blocked corner
+ if ( pm._getSquare(x1,y).blocked || pm._getSquare(x,y1).blocked )
+ continue;
+ }
+
+ sq = pm._getSquare(x1,y1);
+ 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;
+ }
+
+})
+;
// Determine how much time can pass before we risk teleporting
// We'll need to reset this whenever the bounding box changes size
resetBound : function resetBound(){
- var p = BOUND_SIZE_RATIO
- , abs = Math.abs
- , w = this.owner.width, h = this.owner.height;
- this.tBound = Math.min(abs(w/this.pa) * p, abs(h/this.pb) * p);
+ this.tBound =
+ BOUND_SIZE_RATIO * Math.min(
+ Math.abs(this.owner.width / this.pa),
+ Math.abs(this.owner.height / this.pb) );
return this;
},
return this.compare(o1,o2) === 1 ? o2 : o1;
},
- closest : function closest(o1, o2){
+ sortClosest : function sortClosest(o1, o2){
var things;
if (o1 instanceof Y.YArray)
things = o1;
else
things = new Y(arguments);
- return things.sort( this.compare ).shift();
+ return things.sort( this.compare );
+ },
+
+ closest : function closest(o1, o2){
+ return this.sortClosest.apply(this, arguments).shift();
},
comesWithin : function comesWithin(pt, w,h){
var Y = require('Y').Y
+, map = require('tanks/map/map')
,
Traversal =
exports['Traversal'] =
Y.subclass('Traversal', {
+ ignore : null, // objects to ignore when determining blockers
+
isBlocked : false,
- isCorner : false,
- remaining : 0,
- blocker : null,
- to : null,
- side : null,
+ to : null, // furthest point reached
+ remaining : 0, // time left unconsumed due to blocker
+ blocker : null, // blocking object
+ side : null, // collision side of blocker (Line)
- init : function initTraversal(thing, trajectory){
- this.thing = thing;
- this.game = thing.game;
- this.pathmap = thing.game.pathmap;
- this.bbox = thing.boundingBox.clone();
+ init : function initTraversal(thing, trajectory, ignore){
+ this.ignore = Y(ignore || []);
+
+ this.thing = thing;
+ this.game = thing.game;
+ this.pathmap = thing.game.pathmap;
+ this.bbox = thing.boundingBox.clone();
this.trajectory = trajectory || thing.trajectory;
},
- step : function step(dt, tx,ty){
- var t, traj = this.trajectory;
- // console.group('tvsl.step(dt='+dt+', tCurrent='+traj.tCurrent+', target=('+tx+','+ty+'))');
+ traverse : function traverse(t, tx,ty){
+ var tr = this.trajectory;
+ this.remaining = t;
do {
- t = this.stepTo( Math.min(traj.tBound,dt), tx,ty);
- traj.tCurrent += t;
- dt = Math.max(0, dt-t);
-
- if (this.isBlocked) {
- this.remaining = dt;
- var data = { 'traversal':this };
- this.thing.fire('collide', this.blocker, data);
- this.blocker.fire('collide', this.thing, data);
- break;
- }
-
- } while (dt > 0);
+ this.step(tx,ty);
+ } while (!this.isBlocked && this.remaining > 0);
- // console.groupEnd();
return this.to;
},
/**
* Checks for blockers and moves traversal bounds forward as much as possible.
- * @param {Number} t Time units to move this traversal forward.
- * @return {Number} Time units consumed by this step.
*/
- stepTo : function stepTo(t, tx,ty){
- var blockers, blocker, B
- , abs = Math.abs
- , traj = this.trajectory, thing = this.thing
-
- , bb = this.bbox
- , x1 = bb.x1, y1 = bb.y1
- , x2 = bb.x2, y2 = bb.y2
- , o = bb.absOrigin
-
- , to = traj.parametric(traj.tCurrent+t)
+ step : function step(tx,ty){
+ var tr = this.trajectory
+ , or = this.thing.loc
+ , dt = Math.min(tr.tBound, this.remaining)
+ , to = this.to = tr.parametric(tr.tCurrent+dt) // calc how far down the tr we go
;
// Don't overshoot the target
- if ( tx !== undefined && abs(o.x-to.x) > abs(o.x-tx) )
+ if ( tx !== undefined && Math.abs(or.x-to.x) > Math.abs(or.x-tx) )
to.x = tx;
- if ( ty !== undefined && abs(o.y-to.y) > abs(o.y-ty) )
+ if ( ty !== undefined && Math.abs(or.y-to.y) > Math.abs(or.y-ty) )
to.y = ty;
- // nb: BoundingBox.relocate() is in-place, so this.bbox are updated
- bb = bb.relocate(to);
- var oto = this.to = to;
- blockers = this.blockers = this.pathmap.get(bb).remove(thing).end();
+ this.to = to;
+ this.bbox.relocate(to); // BoundingBox.relocate() is in-place
+
+ this.blockers =
+ this.pathmap.get(this.bbox)
+ .remove(this.thing)
+ .sort(tr.compare);
- if (!blockers.length) {
+ // this.blocker = this.blockers.filter(this.checkBlocker, this).shift();
+ this.blockers.filter(this.checkBlocker, this);
+
+ if (this.blocker) {
+
+ // Move bounding box to furthest non-overlapping point
+ this.to = this.rewind(this.blocker);
+ this.bbox.relocate(this.to);
+
+ // Calculate how much time we actually used
+ var consumed = tr.timeToMove(to.x-or.x, to.y-or.y);
+ this.consumeTime(consumed);
+ // console.log('stepTo('+dt+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed);
+
+ // Fire events
+ this.collide(this.blocker);
+
+ } else {
+ this.consumeTime(dt);
// console.log('stepTo('+t+') --> '+to+' consuming '+t);
- return t; // All time consumed, yay
+ }
+ },
+
+ /**
+ * Filters found obstructions to keep only those not ignored and blocking.
+ */
+ checkBlocker : function checkBlocker(blocker){
+ var blocking = blocker.blocking;
+
+ // All blockers after the first are ignored
+ if ( this.ignore.has(blocker) || blocking === map.PASSABLE || this.isBlocked )
+ return false;
+
+ // Only fire collision with this zone once per traversal
+ // XXX: Zone will have to manage enterance/exit and provide a method for testing, hm -- this would obviate the main need for this.ignore
+ if ( blocking === map.ZONE ) {
+ this.ignore.push(blocker);
+ this.collide(blocker);
+ return false;
}
this.isBlocked = true;
+ this.blocker = blocker;
+ return true;
+ },
+
+ collide : function collide(blocker){
+ var thing = this.thing
+ , data = { 'traversal':this, 'thing':thing, 'blocker':blocker };
- // TODO: Either: this never happens (current belief) OR it needs better corner-detection
- // Literal corner case :P
- if (blockers.length > 1) {
- console.error('Corner!', 'to:', to, 'blockers:', blockers);
- // throw new Error('Corner! to:'+to+', blockers:'+blockers);
- this.isCorner = true;
- // to = traj.p1; // Turn around, go back whence you came
- }
-
- // blocker = traj.closest(blockers)
- blocker = this.blocker = blockers[0];
- B = blocker.boundingBox;
+ thing.fire('collide', blocker, data);
+ blocker.fire('collide', thing, data);
+ },
+
+ rewind : function rewind(blocker){
+ var tr = this.trajectory, bb = this.bbox
+ , B = blocker.boundingBox
+ , to = this.to ;
// Figure out which boundary of the blocker we crossed and calculate
// the furthest non-overlapping point.
- if (x2 <= B.x1 && bb.x2 >= B.x1) {
- this.to = to = traj.pointAtX(B.x1 - 1 - bb.originRight);
+ if (bb.x2 <= B.x1 && bb.x2 >= B.x1) {
this.side = B.leftSide;
+ to = tr.pointAtX(B.x1 - 1 - bb.originRight);
- } else if (x1 >= B.x2 && bb.x1 <= B.x2) {
- this.to = to = traj.pointAtX(B.x2 + 1 + bb.originLeft);
+