Tank =
exports['Tank'] =
-Thing.subclass('Tank', {
- projectile : Bullet,
- blocking : true,
-
- bodyColor : '#83BB32',
- turretColor : '#1C625B',
- barrelColor : '#D43B24',
-
-
- // Bounding box size
- width : REF_SIZE*0.55,
- height : REF_SIZE*0.55,
-
- // Attributes
- stats : {
- hp : 1, // health
-
- move : 0.75, // move speed (squares/sec)
- rotate : HALF_PI, // rotation speed (radians/sec)
-
- power : 1, // attack power
- speed : 0.5, // attack cool (sec)
- shots : 4 // max projectiles in the air at once
- },
-
- // AI "Cooldowns" (max frequency of each action per sec)
- ai : {
- path : 1.0, // calculate a path to enemy
- dodge : 1.0, // dodge an incoming bullet
- shootIncoming : 0.5, // shoot down incoming bullet
- shootEnemy : 0.75 // shoot at enemy tank if in range
- },
-
-
- nShots : 0,
- currentMove : null,
- forceCurrentMove : false,
- currentMoveLimit : -1,
-
-
-
- init : function initTank(align){
+Thing.subclass('Tank', function(Tank){
+
+ Y.core.extend(this, {
+ projectile : Bullet,
+ blocking : true,
+
+ bodyColor : '#83BB32',
+ turretColor : '#1C625B',
+ barrelColor : '#D43B24',
+
+
+ // Bounding box size
+ width : REF_SIZE*0.55,
+ height : REF_SIZE*0.55,
+
+ // Attributes
+ stats : {
+ hp : 1, // health
+ move : 0.75, // move speed (squares/sec)
+ rotate : HALF_PI, // rotation speed (radians/sec)
+ power : 1, // attack power
+ speed : 0.5, // attack cool (sec)
+ shots : 4 // max projectiles in the air at once
+ },
+
+ // AI "Cooldowns" (max frequency of each action per sec)
+ ai : {
+ path : 1.5, // calculate a path to enemy
+ dodge : 1.0, // dodge an incoming bullet
+ shootIncoming : 0.5, // shoot down incoming bullet
+ shootEnemy : 0.75 // shoot at enemy tank if in range
+ },
+
+ nShots : 0,
+ currentMove : null,
+ forceCurrentMove : false,
+ currentMoveLimit : -1
+ });
+
+
+ this['init'] =
+ function initTank(align){
Thing.init.call(this, align);
this.onBulletDeath = this.onBulletDeath.bind(this);
var self = this;
- this.addEventListener('destroy', function(){
- this.game.pathmap.destroyPath(this.id);
- });
- },
+ this.addEventListener('destroy', destroyPath);
+ };
- onBulletDeath : function onBulletDeath(evt){ this.nShots--; },
+ function destroyPath(){
+ this.game.pathmap.destroyPath(this.id);
+ }
+
+ this['onBulletDeath'] =
+ function onBulletDeath(evt){ this.nShots--; };
shootEnemy : 0.75 // shoot at enemy tank if in range
*/
- act : function act(allotment){
+ this['act'] =
+ function act(allotment){
var ai = this.ai;
// Check to see if we should obey our last decision, and not recalc
// Nothing to shoot at? Move toward something
this.continueMove();
return this;
- },
+ };
/**
* TODO: Get this.currentMove etc out of the calc methods.
*/
- moveAwayFrom : function moveAwayFrom(agent){
+ this['moveAwayFrom'] =
+ function moveAwayFrom(agent){
var mid = this.midpoint
, trj = agent.trajectory.tangent(mid)
// console.log(' --> Dodge', agent, 'away from', wall, 'to', to);
return to;
- },
+ };
- find : function find(x1,y1, x2,y2){
+ this['find'] =
+ function find(x1,y1, x2,y2){
return this.pathmap.get(x1,y1, x2,y2);
- },
+ };
+
- findNearLike : function findNearLike(ticks, fn){
- fn = (fn || Y.op.K(true)).toFunction();
+ var kTrue = Y.op.K(true);
+
+ this['findNearLike'] =
+ function findNearLike(ticks, fn){
+ fn = (fn || kTrue).toFunction();
var within = BULLET_MOVE_PER_FRAME*ticks
, bb = this.boundingBox
;
return this.game.pathmap.get(x1,y1, x2,y2).filter(fn, this);
- },
+ };
- findNearEnemies : function findNearEnemies(ticks, needLineOfSight){
- return this.findNearLike(ticks, function(agent){
+ this['findNearEnemies'] =
+ function findNearEnemies(ticks, needLineOfSight){
+ return this.findNearLike(ticks, function nearEnemyFilter(agent){
var am = agent.midpoint;
return ( agent.align !== this.align
&& Y.is(Tank, agent)
&& new Trajectory(this,this.midpoint,am).pathBlocked(agent))
);
});
- },
+ };
- closestOf : function closestOf(agents){
+ this['closestOf'] =
+ function closestOf(agents){
if ( !(agents && agents.size()) )
return null;
});
return agents.attr(0);
- },
+ };
- willCollide : function willCollide(bullets, wiggle){
+ this['willCollide'] =
+ function willCollide(bullets, wiggle){
bullets = ( Y.isArray(bullets) ? bullets : [bullets] );
wiggle = wiggle || 0;
&& trj.comesWithin(tank, w,h)
&& !trj.pathBlocked(tank) );
});
- },
+ };
/**
* Fires this agent's cannon. If a target location is omitted, the shot
* @param {Number} [x] Target X coordinate.
* @param {Number} [y] Target Y coordinate.
*/
- shoot : function shoot(x,y){
+ this['shoot'] =
+ function shoot(x,y){
var WIGGLE = 4 // additional space which must be clear in front of the barrel
, xydef = (x !== undefined && y !== undefined)
;
this.game.addUnit(p).render(this.game.level);
return p;
- },
+ };
/**
* @return {this}
*/
- move : function move(x,y){
+ this['move'] =
+ function move(x,y){
var bb = this.boundingBox
, w2 = bb.width/2, h2 = bb.height/2
, x2 = x-w2, y2 = y-h2
;
this.trajectory = new Trajectory(this, bb.x1,bb.y1, x2,y2, this.stats.move*REF_SIZE/1000);
return this.moveByAngle( this.angleTo(x,y), x2,y2 );
- },
+ };
/** @protected This method does not update this.trajectory -- call this.move(x,y) instead. */
- moveByAngle : function moveByAngle(theta, targetX,targetY){
+ this['moveByAngle'] =
+ function moveByAngle(theta, targetX,targetY){
var abs = Math.abs
- , bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2
+ , bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2
, loc = this.loc, mid = bb.midpoint()
, to = loc.moveByAngle(theta, this.stats.move * SQUARETH)
;
this.game.moveUnitTo(this, to.x, to.y);
return this;
- },
+ };
- continueMove : function continueMove(){
+ this['continueMove'] =
+ function continueMove(){
if ( !this.currentMove || this.currentMoveLimit <= NOW ){
var t = this.findNearEnemies(10000).shift();
if (t) this.calculatePath(t.midpoint);
this.move(to.x, to.y);
if ( this.midpoint.equals(to) ) {
- this.currentMove = null;
this.forceCurrentMove = false;
this.currentMoveLimit = -1;
+ this.currentMove = (this.lastPath ? this.lastPath.shift() : null);
}
- },
+ };
- calculatePath : function calculatePath(end){
- if (!end) return;
+ this['calculatePath'] =
+ function calculatePath(end){
+ if ( !(end && this.ai.path.activate(NOW)) ) return;
// this.forceCurrentMove = Y.op.K(true);
// this.currentMoveLimit = NOW + 750;
this.currentMoveLimit = -1;
// console.log(this, 'moving toward', t);
- var pm = this.game.pathmap
- , mid = this.midpoint, start = mid
+ var pm = this.game.pathmap
+ , start = this.midpoint
- , path = this.lastPath = pm.path(start, end, this.id)
- , to = this.currentMove = path[0]
+ , path = this.lastPath = pm.path(start, end, this.id)
+ , to = this.currentMove = path.shift()
;
// VVV We only really need this code if we're going to recalculate before we reach the currentMove
// }
// console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to);
- },
+ };
- ableToShoot : function ableToShoot(){
+ this['ableToShoot'] =
+ function ableToShoot(){
return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
- },
+ };
- getTurretLoc : function getTurretLoc(){
+ this['getTurretLoc'] =
+ function getTurretLoc(){
var loc = this.loc, mid = this.midpoint
, barrel = this.barrel
;
// console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]);
return new Vec(x,y);
- },
+ };
* Sets up unit appearance for minimal updates. Called once at start,
* or when the world needs to be redrawn from scratch.
*/
- render : function render( parent ){
+ this['render'] =
+ function render( parent ){
if (this.shape) this.shape.remove();
var w = this.width, w2 = w/2
.appendTo( this.shape ) ;
return this;
- },
+ };
- colors : function colors(bodyColor, turretColor, barrelColor){
+ this['colors'] =
+ function colors(bodyColor, turretColor, barrelColor){
var names = Y('bodyColor', 'turretColor', 'barrelColor');
if (arguments.length === 0)
if (turretColor) this.turretColor = turretColor;
if (barrelColor) this.barrelColor = barrelColor;
return this;
- },
+ };
- rotateBarrel : function rotateBarrel(x,y){
+ this['rotateBarrel'] =
+ function rotateBarrel(x,y){
this.barrel.rotate(this.angleTo(x,y));
return this;
- },
+ };
- rotateBarrelRelPage : function rotateBarrelRelPage(pageX, pageY){
+ this['rotateBarrelRelPage'] =
+ function rotateBarrelRelPage(pageX, pageY){
var shape = this.shape
, off = shape.offset()
, w = this.width, h = this.height
;
this.barrel.rotate(theta);
return this;
- }
+ };
});