+(function(){
+
+var BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
+, isBullet = Y.is(Bullet)
+;
+
Tank = Thing.subclass('Tank', {
projectile : Bullet,
- act : function act(){
+ act : function act(allotment){
// Check to see if we should obey our last decision, and not recalc
if ( this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > NOW ) {
this.forceCurrentMove = false;
- // Are we ready to fire?
+ // Try to shoot down nearby bullets
if ( this.ableToShoot() ) {
-
- // Try to shoot down nearby bullets
- var bs = this.willCollide( this.findNearLike(16, Y.is(Bullet)) );
+ var bs = this.willCollide( this.findNearLike(25, isBullet) );
+ // console.log('['+TICKS+':'+this.id, this, '] Shoot down bullets?', bs.size() && bs);
if ( bs.size() ) {
var b = this.closestOf(bs);
-
- // console.log('Incoming! Shoot it down!', b);
+ // console.log(' --> Incoming! Shoot it down!', b);
this.shoot(b.loc.x, b.loc.y);
return this;
}
-
- // Dodge incoming bullet
- var bs = this.willCollide( this.findNearLike(50, Y.is(Bullet)), 10 );
- if ( bs.size() ) {
- var b = this.closestOf(bs), bs = [b]
- , mid = this.midpoint
- , trj = b.trajectory.tangent(mid)
-
- , lvl = this.game.level, w = lvl.width, h = lvl.height
- , x = (mid.x > w/2 ? w : 0)
- , y = (mid.y > h/2 ? h : 0)
- , to = this.currentMove = trj.near(x,y);
-
- this.forceCurrentMove = this.willCollide.bind(this, bs, 10);
- this.currentMoveLimit = NOW + 750;
-
- this.move(to.x, to.y);
- return this;
- }
-
- // Try to blow up nearby tanks
- var t = this.findNearEnemies(66, true).shift();
+ }
+
+ // Dodge incoming bullet
+ var bs = this.willCollide(this.findNearLike(71, isBullet), 5);
+ // console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs);
+ if ( bs.size() ) {
+ var bullet = this.closestOf(bs);
+ this.moveAwayFrom(bullet);
+ return this;
+ }
+
+ // Try to blow up nearby tanks
+ if ( this.ableToShoot() ) {
+ var t = this.findNearEnemies(71, true).shift();
+ // console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t);
if (t) {
- // console.log('I gotcha!', t);
+ // console.log(' --> I gotcha!', t);
this.shoot(t.loc.x, t.loc.y);
return this;
}
// Nothing to shoot at? Move toward something
this.continueMove();
-
return this;
},
- ableToShoot : function ableToShoot(){
- return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
- },
- closestOf : function closestOf(agents){
- if ( !(agents && agents.size()) )
- return null;
-
- var manhattan = math.Vec.manhattan
- , bb = this.boundingBox, mid = this.midpoint ;
+ /**
+ * TODO: Get this.currentMove etc out of the calc methods.
+ */
+ moveAwayFrom : function moveAwayFrom(agent){
+ var mid = this.midpoint
+ , trj = agent.trajectory.tangent(mid)
- agents.sort(function(a,b){
- return Y.op.cmp(
- manhattan(a.loc,mid),
- manhattan(b.loc,mid) );
- });
+ , wall = this.closestOf(this.game.pathmap.allWalls)
+ , wmid = wall.midpoint
- return agents.attr(0);
- },
-
- willCollide : function willCollide(bullets, wiggle){
- wiggle = wiggle || 0;
- var tank = this, bb = this.boundingBox
- , w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2
- ;
- return bullets.filter(function(b){
- var trj = b.trajectory;
- return ( !b.dead
- && trj.comesWithin(tank, w,h)
- && !trj.pathBlocked(tank) );
- });
- },
-
- continueMove : function continueMove(){
- if ( !this.currentMove || this.currentMoveLimit <= NOW ){
- var t = this.findNearEnemies(10000).shift();
- if (t) this.calculatePath(t.midpoint);
- }
+ , lvl = this.game.level.boundingBox, w = lvl.width, h = lvl.height
+ , x = ((mid.x - wmid.x) > 0 ? w : 0)
+ , y = ((mid.y - wmid.y) > 0 ? h : 0)
+ , to = this.currentMove = trj.near(x,y);
- var to = this.currentMove;
- if (!to) return;
+ this.forceCurrentMove = this.willCollide.bind(this, [agent], 5);
+ this.currentMoveLimit = NOW + 1000;
this.move(to.x, to.y);
- if ( this.midpoint.equals(to) ) {
- this.currentMove = null;
- this.forceCurrentMove = false;
- this.currentMoveLimit = -1;
- }
- },
-
- calculatePath : function calculatePath(end){
- if (!end) return;
-
- var pm = this.game.pathmap;
-
- // this.forceCurrentMove = Y.op.K(true);
- // this.currentMoveLimit = NOW + 750;
- this.forceCurrentMove = false;
- this.currentMoveLimit = -1;
-
- // console.log(this, 'moving toward', t);
- var bb = this.boundingBox
- , mid = this.midpoint
- , start = mid
-
- , path = this.lastPath = pm.path(start, end, this.id)
- , to = this.currentMove = path[0]
- ;
-
- // VVV We only really need this code if we're going to recalculate before we reach the currentMove
-
- // we may need to move closer to start if we occupy multiple grid-squares
- // var tosq = pm.vec2Square(to), manhattan = pm.manhattan
- // , tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1)
- // , bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2)
- // , straddles = [tl, tr, bl, br]
- // ;
- //
- //
- // if ( !tl.equals(br) ) {
- // tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1);
- // bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y);
- // straddles.sort(function(a,b){
- // return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq));
- // });
- // to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt);
- // path.unshift(to);
- // }
- // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to);
+ // console.log(' --> Dodge', agent, 'away from', wall, 'to', to);
+ return to;
},
find : function find(x1,y1, x2,y2){
findNearLike : function findNearLike(ticks, fn){
fn = (fn || Y.op.K(true)).toFunction();
- var bMovePerTick = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
- , within = bMovePerTick*ticks
+
+ var within = BULLET_MOVE_PER_FRAME*ticks
, bb = this.boundingBox
, x1 = bb.x1 - within, y1 = bb.y1 - within
, x2 = bb.x2 + within, y2 = bb.y2 + within
;
+
return this.game.pathmap.get(x1,y1, x2,y2).filter(fn, this);
},
- findNearEnemies : function findNearEnemies(ticks, wallObs){
- var bb = this.boundingBox
- , mid = this.midpoint
- , pm = this.game.pathmap ;
+ findNearEnemies : function findNearEnemies(ticks, needLineOfSight){
return this.findNearLike(ticks, function(agent){
var am = agent.midpoint;
return ( agent.align !== this.align
&& Y.is(Tank, agent)
- && !(wallObs && new Trajectory(this,mid,am).pathBlocked(agent)) );
+ && !(needLineOfSight
+ && new Trajectory(this,this.midpoint,am).pathBlocked(agent))
+ );
+ });
+ },
+
+ closestOf : function closestOf(agents){
+ if ( !(agents && agents.size()) )
+ return null;
+
+ var manhattan = math.Vec.manhattan
+ , bb = this.boundingBox, mid = this.midpoint ;
+
+ agents.sort(function(a,b){
+ return Y.op.cmp(
+ manhattan(a.midpoint,mid),
+ manhattan(b.midpoint,mid) ); // FIXME: midpoint-to-midpoint is wrong -- should use pt on closest boundary-side of object
+ });
+
+ return agents.attr(0);
+ },
+
+ willCollide : function willCollide(bullets, wiggle){
+ bullets = ( !Y.isArray(bullets) ? [bullets] : bullets );
+ wiggle = wiggle || 0;
+
+ var tank = this, bb = this.boundingBox
+ , w = bb.width+wiggle, h = bb.height+wiggle ;
+ // , w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2 ;
+
+ return bullets.filter(function(b){
+ var trj = b.trajectory;
+ return ( !b.dead
+ && trj.comesWithin(tank, w,h)
+ && !trj.pathBlocked(tank) );
});
},
+ /**
+ * Fires this agent's cannon. If a target location is omitted, the shot
+ * will be fired in the direction of the tank's current barrel rotation.
+ *
+ * @param {Number} [x] Target X coordinate.
+ * @param {Number} [y] Target Y coordinate.
+ */
shoot : function shoot(x,y){
var WIGGLE = 4 // additional space which must be clear in front of the barrel
, xydef = (x !== undefined && y !== undefined)
, blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this)
;
- if ( blockers.size() ) return; // console.log('squelch!', blockers);
+ if ( blockers.size() )
+ return null; // console.log('squelch!', blockers);
if (!xydef) {
x = x0 + REF_SIZE*cos;
return p;
},
- getTurretLoc : function getTurretLoc(){
- var loc = this.loc, mid = this.midpoint
- , barrel = this.barrel
-
- , theta = barrel.transform.rotate
- , sin = Math.sin(theta), cos = Math.cos(theta)
- , len = barrel.boundingBox.width
-
- , x = mid.x + len*cos
- , y = mid.y + len*sin
- ;
- // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]);
- return new math.Vec(x,y);
- },
-
+ /**
+ * @return {this}
+ */
move : function move(x,y){
var bb = this.boundingBox
, w2 = bb.width/2, h2 = bb.height/2
return this.moveByAngle( this.angleTo(x,y), x2,y2 );
},
- /**
- * @protected This method does not update this.trajectory -- call this.move(x,y) instead.
- */
+ /** @protected This method does not update this.trajectory -- call this.move(x,y) instead. */
moveByAngle : function moveByAngle(theta, targetX,targetY){
var abs = Math.abs
, bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2
},
+ continueMove : function continueMove(){
+ if ( !this.currentMove || this.currentMoveLimit <= NOW ){
+ var t = this.findNearEnemies(10000).shift();
+ if (t) this.calculatePath(t.midpoint);
+ }
+
+ var to = this.currentMove;
+ if (!to) return;
+
+ this.move(to.x, to.y);
+ if ( this.midpoint.equals(to) ) {
+ this.currentMove = null;
+ this.forceCurrentMove = false;
+ this.currentMoveLimit = -1;
+ }
+ },
+
+ calculatePath : function calculatePath(end){
+ if (!end) return;
+
+ // this.forceCurrentMove = Y.op.K(true);
+ // this.currentMoveLimit = NOW + 750;
+ this.forceCurrentMove = false;
+ this.currentMoveLimit = -1;
+
+ // console.log(this, 'moving toward', t);
+ var pm = this.game.pathmap
+ , mid = this.midpoint, start = mid
+
+ , path = this.lastPath = pm.path(start, end, this.id)
+ , to = this.currentMove = path[0]
+ ;
+
+ // VVV We only really need this code if we're going to recalculate before we reach the currentMove
+
+ // we may need to move closer to start if we occupy multiple grid-squares
+ // var tosq = pm.vec2Square(to), manhattan = math.Vec.manhattan
+ // , bb = this.boundingBox
+ // , tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1)
+ // , bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2)
+ // , straddles = [tl, tr, bl, br]
+ // ;
+ //
+ //
+ // if ( !tl.equals(br) ) {
+ // tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1);
+ // bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y);
+ // straddles.sort(function(a,b){
+ // return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq));
+ // });
+ // to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt);
+ // path.unshift(to);
+ // }
+
+ // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to);
+ },
+
+
+ ableToShoot : function ableToShoot(){
+ return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
+ },
+
+ getTurretLoc : function getTurretLoc(){
+ var loc = this.loc, mid = this.midpoint
+ , barrel = this.barrel
+
+ , theta = barrel.transform.rotate
+ , sin = Math.sin(theta), cos = Math.cos(theta)
+ , len = barrel.boundingBox.width
+
+ , x = mid.x + len*cos
+ , y = mid.y + len*sin
+ ;
+ // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]);
+ return new math.Vec(x,y);
+ },
+
+
/// Rendering Methods ///
}
});
+
+
+})();
\ No newline at end of file