CanvasMark/scripts/asteroids_player.js
2013-07-23 23:57:10 +01:00

330 lines
10 KiB
JavaScript

/**
* Player actor class.
*
* @namespace Asteroids
* @class Asteroids.Player
*/
(function()
{
Asteroids.Player = function(p, v, h)
{
Asteroids.Player.superclass.constructor.call(this, p, v);
this.heading = h;
this.energy = this.ENERGY_INIT;
// setup SpriteActor values - used for shield sprite
this.animImage = g_shieldImg;
this.animLength = this.SHIELD_ANIM_LENGTH;
// setup weapons
this.primaryWeapons = [];
this.primaryWeapons["main"] = new Asteroids.PrimaryWeapon(this);
return this;
};
extend(Asteroids.Player, Game.SpriteActor,
{
MAX_PLAYER_VELOCITY: 10.0,
PLAYER_RADIUS: 10,
SHIELD_RADIUS: 14,
SHIELD_ANIM_LENGTH: 100,
SHIELD_MIN_PULSE: 20,
ENERGY_INIT: 200,
THRUST_DELAY: 1,
BOMB_RECHARGE: 20,
BOMB_ENERGY: 40,
/**
* Player heading
*/
heading: 0.0,
/**
* Player energy (shield and bombs)
*/
energy: 0,
/**
* Player shield active counter
*/
shieldCounter: 0,
/**
* Player 'alive' flag
*/
alive: true,
/**
* Primary weapon list
*/
primaryWeapons: null,
/**
* Bomb fire recharging counter
*/
bombRecharge: 0,
/**
* Engine thrust recharge counter
*/
thrustRecharge: 0,
/**
* True if the engine thrust graphics should be rendered next frame
*/
engineThrust: false,
/**
* Frame that the player was killed on - to cause a delay before respawning the player
*/
killedOnFrame: 0,
/**
* Power up setting - can fire when shielded
*/
fireWhenShield: false,
/**
* Player rendering method
*
* @param ctx {object} Canvas rendering context
*/
onRender: function onRender(ctx)
{
var headingRad = this.heading * RAD;
// render engine thrust?
if (this.engineThrust)
{
ctx.save();
ctx.translate(this.position.x, this.position.y);
ctx.rotate(headingRad);
ctx.globalAlpha = 0.4 + Math.random()/2;
if (BITMAPS)
{
ctx.fillStyle = "rgb(25,125,255)";
}
else
{
ctx.shadowColor = ctx.strokeStyle = "rgb(25,125,255)";
}
ctx.beginPath();
ctx.moveTo(-5, 8);
ctx.lineTo(5, 8);
ctx.lineTo(0, 15 + Math.random()*7);
ctx.closePath();
if (BITMAPS) ctx.fill(); else ctx.stroke();
ctx.restore();
this.engineThrust = false;
}
// render player graphic
if (BITMAPS)
{
var size = (this.PLAYER_RADIUS * 2) + 4;
var normAngle = this.heading % 360;
if (normAngle < 0)
{
normAngle = 360 + normAngle;
}
ctx.drawImage(g_playerImg, 0, normAngle * 16, 64, 64, this.position.x - (size / 2), this.position.y - (size / 2), size, size);
}
else
{
ctx.save();
ctx.shadowColor = ctx.strokeStyle = "rgb(255,255,255)";
ctx.translate(this.position.x, this.position.y);
ctx.rotate(headingRad);
ctx.beginPath();
ctx.moveTo(-6, 8);
ctx.lineTo(6, 8);
ctx.lineTo(0, -8);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
// shield up? if so render a shield graphic around the ship
if (this.shieldCounter > 0 && this.energy > 0)
{
if (BITMAPS)
{
// render shield graphic bitmap
ctx.save();
ctx.translate(this.position.x, this.position.y);
ctx.rotate(headingRad);
this.renderSprite(ctx, -this.SHIELD_RADIUS-1, -this.SHIELD_RADIUS-1, (this.SHIELD_RADIUS * 2) + 2);
ctx.restore();
}
else
{
// render shield as a simple circle around the ship
ctx.save();
ctx.translate(this.position.x, this.position.y);
ctx.rotate(headingRad);
ctx.shadowColor = ctx.strokeStyle = "rgb(100,100,255)";
ctx.beginPath();
ctx.arc(0, 2, this.SHIELD_RADIUS, 0, TWOPI, true);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
this.shieldCounter--;
this.energy--;
}
},
/**
* Execute player forward thrust request
* Automatically a delay is used between each application - to ensure stable thrust on all machines.
*/
thrust: function thrust()
{
// now test we did not thrust too recently - to stop fast key repeat issues
if (GameHandler.frameCount - this.thrustRecharge > this.THRUST_DELAY)
{
// update last frame count
this.thrustRecharge = GameHandler.frameCount;
// generate a small thrust vector
var t = new Vector(0.0, -0.55);
// rotate thrust vector by player current heading
t.rotate(this.heading * RAD);
// test player won't then exceed maximum velocity
var pv = this.vector.clone();
if (pv.add(t).length() < this.MAX_PLAYER_VELOCITY)
{
// add to current vector (which then gets applied during each render loop)
this.vector.add(t);
}
}
// mark so that we know to render engine thrust graphics
this.engineThrust = true;
},
/**
* Execute player active shield request
* If energy remaining the shield will be briefly applied - or until key is release
*/
activateShield: function activateShield()
{
// ensure shield stays up for a brief pulse between key presses!
if (this.energy > 0)
{
this.shieldCounter = this.SHIELD_MIN_PULSE;
}
},
isShieldActive: function isShieldActive()
{
return (this.shieldCounter > 0 && this.energy > 0);
},
radius: function radius()
{
return (this.isShieldActive() ? this.SHIELD_RADIUS : this.PLAYER_RADIUS);
},
expired: function expired()
{
return !(this.alive);
},
kill: function kill()
{
this.alive = false;
this.killedOnFrame = GameHandler.frameCount;
},
/**
* Fire primary weapon(s)
* @param bulletList {Array} to add bullet(s) to on success
*/
firePrimary: function firePrimary(bulletList)
{
// attempt to fire the primary weapon(s)
// first ensure player is alive and the shield is not up
if (this.alive && (!this.isShieldActive() || this.fireWhenShield))
{
for (var w in this.primaryWeapons)
{
var b = this.primaryWeapons[w].fire();
if (b)
{
if (isArray(b))
{
for (var i=0; i<b.length; i++)
{
bulletList.push(b[i]);
}
}
else
{
bulletList.push(b);
}
}
}
}
},
/**
* Fire secondary weapon.
* @param bulletList {Array} to add bullet to on success
*/
fireSecondary: function fireSecondary(bulletList)
{
// attempt to fire the secondary weapon and generate bomb object if successful
// first ensure player is alive and the shield is not up
if (this.alive && (!this.isShieldActive() || this.fireWhenShield) && this.energy > this.BOMB_ENERGY)
{
// now test we did not fire too recently
if (GameHandler.frameCount - this.bombRecharge > this.BOMB_RECHARGE)
{
// ok, update last fired frame and we can now generate a bomb
this.bombRecharge = GameHandler.frameCount;
// decrement energy supply
this.energy -= this.BOMB_ENERGY;
// generate a vector rotated to the player heading and then add the current player
// vector to give the bomb the correct directional momentum
var t = new Vector(0.0, -6.0);
t.rotate(this.heading * RAD);
t.add(this.vector);
bulletList.push(new Asteroids.Bomb(this.position.clone(), t));
}
}
},
onUpdate: function onUpdate()
{
// slowly recharge the shield - if not active
if (!this.isShieldActive() && this.energy < this.ENERGY_INIT)
{
this.energy += 0.1;
}
},
reset: function reset(persistPowerUps)
{
// reset energy, alive status, weapons and power up flags
this.alive = true;
if (!persistPowerUps)
{
this.primaryWeapons = [];
this.primaryWeapons["main"] = new Asteroids.PrimaryWeapon(this);
this.fireWhenShield = false;
}
this.energy = this.ENERGY_INIT + this.SHIELD_MIN_PULSE; // for shield as below
// active shield briefly
this.activateShield();
}
});
})();