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);
+ } else if (bb.x1 >= B.x2 && bb.x1 <= B.x2) {
this.side = B.rightSide;
+ to = tr.pointAtX(B.x2 + 1 + bb.originLeft);
- } else if (y2 <= B.y1 && bb.y2 >= B.y1) {
- this.to = to = traj.pointAtY(B.y1 - 1 - bb.originTop);
+ } else if (bb.y2 <= B.y1 && bb.y2 >= B.y1) {
this.side = B.topSide;
+ to = tr.pointAtY(B.y1 - 1 - bb.originTop);
- } else if (y1 >= B.y2 && bb.y1 <= B.y2) {
- this.to = to = traj.pointAtY(B.y2 + 1 + bb.originBottom );
+ } else if (bb.y1 >= B.y2 && bb.y1 <= B.y2) {
this.side = B.bottomSide;
+ to = tr.pointAtY(B.y2 + 1 + bb.originBottom );
} else {
- console.error('Null reflection line!', 'to:', to, 'blockers:', blockers);
- // throw new Error('Null reflection line! to:'+to+', blockers:'+blockers);
+ console.error('Null reflection line!', 'to:', to, 'blocker:', blocker);
+ // throw new Error('Null reflection line! to:'+to+', blocker:'+blocker);
}
- // Move bounding box to furthest non-overlapping point
- bb.relocate(to);
-
- // Return how much time consumed
- var consumed = traj.timeToMove(to.x-o.x, to.y-o.y);
- // console.log('stepTo('+t+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed);
-
- return consumed;
+ return to;
+ },
+
+ consumeTime : function consumeTime(t){
+ this.trajectory.tCurrent += t;
+ this.remaining = Math.max(0, this.remaining-t);
+ return this;
}
})
+++ /dev/null
-var Y = require('Y').Y
-, op = require('Y/op')
-, Rect = require('ezl/shape').Rect
-, Thing = require('tanks/thing/thing').Thing
-,
-
-
-Wall =
-exports['Wall'] =
-Thing.subclass('Wall', {
- isBoundary : false,
- blocking : true,
- active : false,
-
- originX : 0,
- originY : 0,
-
- stats : {
- hp : Infinity,
- move : 0,
- power : 0,
- speed : 0,
- shots : 0
- },
-
-
- init : function initWall(x,y, w,h, isBoundary){
- this.width = w;
- this.height = h;
- this.isBoundary = !!isBoundary;
- Thing.init.call(this);
- this.position(x,y);
- },
-
- // inactive
- createCooldowns : op.nop,
-
- // indestructable
- dealDamage : op.nop,
-
-
- render : function render(parent){
- if (this.isBoundary)
- return this;
-
- if (this.shape)
- this.shape.remove();
-
- this.shape =
- new Rect(this.width, this.height)
- .position(this.loc.x, this.loc.y)
- .fill('rgba(255,255,255, 0.25)')
- .appendTo( parent );
-
- return this;
- },
-
- toString : function(){
- var bb = this.boundingBox
- , x1,y1, x2,y2;
- if (bb){
- x1 = bb.x1; y1 = bb.y1;
- x2 = bb.x2; y2 = bb.y2;
- } else {
- x1=y1=x2=y2=NaN;
- }
- return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.width+', h='+this.height+')';
- }
-});
-
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
var Y = require('Y').Y
, op = require('Y/op')
+
, math = require('ezl/math')
, shape = require('ezl/shape')
+, Line = shape.Line
+, Circle = shape.Circle
+
, config = require('tanks/config').config
, thing = require('tanks/thing/thing')
-, Wall = require('tanks/map/wall').Wall
+, map = require('tanks/map/map')
+, Wall = require('tanks/thing/wall').Wall
, Trajectory = require('tanks/map/trajectory').Trajectory
, Traversal = require('tanks/map/traversal').Traversal
, Explosion = require('tanks/fx/explosion').Explosion
-
, fillStats = thing.fillStats
, Thing = thing.Thing
-, Line = shape.Line
-, Circle = shape.Circle
,
},
// Instance
- blocking : true,
+ blocking : map.BLOCKING,
+
bounces : 0,
bounceLimit : 1,
if (this.dead) return this;
var tvsl = new Traversal(this)
- , to = tvsl.step(elapsed)
+ , to = tvsl.traverse(elapsed)
;
if (tvsl.isBlocked && !this.dead) {
tvsl = new Traversal(this, this.trajectory);
- to = tvsl.step(tvsl.remaining);
+ to = tvsl.traverse(tvsl.remaining);
}
if (!this.dead)
onCollide : function onCollide(evt){
if ( this.dead ) return;
- var ng
- , tvsl = evt.data.traversal
- , traj = this.trajectory
+ var traj = this.trajectory
+
+ , ng, d = evt.data
+ , tvsl = d.traversal
, to = tvsl.to
+ , unit = d.blocker
- , unit = evt.trigger
- , hitAWall = unit instanceof Wall
+ , isReflective = unit instanceof Wall // XXX: unit.reflective
;
+ // Ignore collisions with zones
+ if (unit.blocking === map.ZONE)
+ return;
+
// Reflection!
- if ( hitAWall && this.bounceLimit >= ++this.bounces ) {
- if ( tvsl.isCorner ) {
- ng = to;
- } else {
- if (!tvsl.side) return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers);
- ng = math.reflect(traj.p2, tvsl.side);
- }
+ if ( isReflective && this.bounceLimit >= ++this.bounces ) {
+ if (!tvsl.side)
+ return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers);
+
+ ng = math.reflect(traj.p2, tvsl.side);
traj.reset(to.x,to.y, ng.x,ng.y);
- this.render(this.game.level); // to render the new reflection line
+
+ this.dirty = true; // to render the new reflection line, if it's visible
+
return;
}
, start = bsize, end = bsize
;
- if (hitAWall) {
+ if (isReflective) {
x = loc.x;
y = loc.y;
} else {
exports['Thing'] = require('tanks/thing/thing').Thing;
+exports['Wall'] = require('tanks/thing/wall').Wall;
exports['Bullet'] = require('tanks/thing/bullet').Bullet;
exports['Tank'] = require('tanks/thing/tank').Tank;
exports['PlayerTank'] = require('tanks/thing/player').PlayerTank;
+++ b/src/tanks/map/wall.cjs
var Y = require('Y').Y
, op = require('Y/op')
-, Thing = require('tanks/thing/thing').Thing
+
, shape = require('ezl/shape')
, Rect = shape.Rect
+, map = require('tanks/map/map')
+, Thing = require('tanks/thing/thing').Thing
+
, SIZE = REF_SIZE * 0.5
,
Item =
exports['Item'] =
Thing.subclass('Item', {
- blocking : true,
+ blocking : map.ZONE,
active : false,
width : SIZE,
//#ensure "jquery.hotkeys"
var Y = require('Y').Y
+, map = require('tanks/map/map')
, Tank = require('tanks/thing/tank').Tank
,
PlayerTank =
exports['PlayerTank'] =
Tank.subclass('PlayerTank', {
+ blocking : map.BLOCKING,
bodyColor : '#E73075',
turretColor : '#A72F5B',
, shape = require('ezl/shape')
, Cooldown = require('ezl/loop').Cooldown
, CooldownGauge = require('ezl/widget').CooldownGauge
+, Vec = vec.Vec
+, manhattan = vec.manhattan
+, Rect = shape.Rect
+, Circle = shape.Circle
+, map = require('tanks/map/map')
, Thing = require('tanks/thing/thing').Thing
, Bullet = require('tanks/thing/bullet').Bullet
, Trajectory = require('tanks/map/trajectory').Trajectory
, Traversal = require('tanks/map/traversal').Traversal
-, Vec = vec.Vec
-, manhattan = vec.manhattan
-, Rect = shape.Rect
-, Circle = shape.Circle
-
, isBullet = Y.is(Bullet)
, BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
, _X = 0, _Y = 1
Thing.subclass('Tank', function(Tank){
Y.core.descriptors(this, {
- projectile : Bullet,
- blocking : true,
+ blocking : map.BLOCKING,
bodyColor : '#83BB32',
turretColor : '#1C625B',
shootEnemy : 0.75 // shoot at enemy tank if in range
},
+ projectile : Bullet,
buffs : null,
nShots : 0,
this.elapsed = elapsed;
this.now = now;
- // this.continueMove();
- // return;
+ this.continueMove();
+ return;
// Check to see if we should obey our last decision, and not recalc
if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > now) {
this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs);
var tvsl = new Traversal(this)
- , to = tvsl.step(this.elapsed, x,y) ;
+ , to = tvsl.traverse(this.elapsed, x,y) ;
this.game.moveThingTo(this, to.x,to.y);
this.forceCurrentMove = false;
this.currentMoveLimit = -1;
- // console.log('calculatePath() moving toward:', end);
var pm = this.game.pathmap
, start = this.loc
, path = this.currentPath = pm.path(this, end)
, to = this.currentMove = path.shift()
;
- // console.log('--> next move:', this.currentMove);
+ console.log('calculatePath()', start, 'toward:', end, 'next:', this.currentMove, 'path: ['+path.invoke('toString')+']');
};
* or when the world needs to be redrawn from scratch.
*/
this['render'] =
- function render( parent ){
+ function render(parent){
if (this.shape) this.shape.remove();
var w = this.width, w2 = w/2
//#ensure "evt"
var Y = require('Y').Y
, op = require('Y/op')
+
, evt = require('evt')
+
, Loc = require('ezl/loc/loc').Loc
, BoundingBox = require('ezl/loc/boundingbox').BoundingBox
, Cooldown = require('ezl/loop').Cooldown
+
, config = require('tanks/config').config
+, map = require('tanks/map/map')
, THING_ID = 0
,
+
fillStats =
exports['fillStats'] =
function fillStats(stats){
Thing =
-exports['Thing'] =
-new evt.Class('Thing', {
+exports['Thing'] = new evt.Class('Thing', {
// Config
showAttackCooldown : null,
// AI "Cooldowns" (max frequency of each action per sec)
ai : {}, // see Tank
+
// *** Bookkeeping *** //
id : 0,
align : 0, // 0 reserved for neutral units
dead : false,
+ dirty : true,
- pathing : null,
- blocking : true, // Whether the agent obstructs pathing
+ blocking : map.BLOCKING, // Pathing type @see {map} for constant definitions
active : true, // Whether the agent takes actions
// Location
height : REF_SIZE,
// Accessors
- set : op.set.methodize(),
- attr : op.attr.methodize(),
+ 'set' : op.set.methodize(),
+ 'attr' : op.attr.methodize(),
get movePerMs(){ return this.stats.move*REF_SIZE/1000; },
* Sets up unit appearance for minimal updates. Called once at start,
* or when the world needs to be redrawn from scratch.
*/
- render : function render(){
+ render : function render(parent){
return this;
},
draw : function draw(){
- if (this.dead)
- this.shape.hide();
- else
+ if (!this.dead) {
+ if (this.dirty) {
+ this.dirty = false;
+ this.render(this.game.level);
+ }
this.shape.draw();
+ } else
+ this.shape.hide();
},
toString : function toString(){
<script src="build/jquery.hotkeys.js" type="text/javascript"></script>
<script src="build/Y/modules/y.kv.js" type="text/javascript"></script>
<script src="build/ezl/util/binaryheap.js" type="text/javascript"></script>
-<script src="build/ezl/util/astar.js" type="text/javascript"></script>
-<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
<script src="build/ezl/util/tree/quadtree.js" type="text/javascript"></script>
<script src="build/Y/modules/y.scaffold.js" type="text/javascript"></script>
<script src="build/Y/modules/y.config.js" type="text/javascript"></script>
<script src="build/Y/modules/y.cookies.js" type="text/javascript"></script>
+<script src="build/tanks/map/map.js" type="text/javascript"></script>
+<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
<script src="build/tanks/config.js" type="text/javascript"></script>
+<script src="build/tanks/map/pathmap.js" type="text/javascript"></script>
<script src="build/tanks/ui/configui.js" type="text/javascript"></script>
<script src="build/tanks/thing/thing.js" type="text/javascript"></script>
<script src="build/tanks/map/trajectory.js" type="text/javascript"></script>
-<script src="build/tanks/map/wall.js" type="text/javascript"></script>
+<script src="build/tanks/ui/pathmapui.js" type="text/javascript"></script>
+<script src="build/tanks/thing/wall.js" type="text/javascript"></script>
<script src="build/tanks/thing/item.js" type="text/javascript"></script>
<script src="build/tanks/ui/grid.js" type="text/javascript"></script>
<script src="build/tanks/fx/explosion.js" type="text/javascript"></script>
<script src="build/tanks/thing/bullet.js" type="text/javascript"></script>
<script src="build/tanks/thing/tank.js" type="text/javascript"></script>
-<script src="build/tanks/map/map.js" type="text/javascript"></script>
-<script src="build/tanks/map/pathmap.js" type="text/javascript"></script>
<script src="build/tanks/thing/player.js" type="text/javascript"></script>
<script src="build/tanks/map/level.js" type="text/javascript"></script>
-<script src="build/tanks/ui/pathmapui.js" type="text/javascript"></script>
<script src="build/tanks/thing.js" type="text/javascript"></script>
<script src="build/tanks/map.js" type="text/javascript"></script>
<script src="build/tanks/game.js" type="text/javascript"></script>