Files
tankwar_proj/js/entities/PlayerTank.js
T
2026-04-10 22:59:39 +08:00

210 lines
4.6 KiB
JavaScript

/**
* PlayerTank.js
* Player-controlled tank with fire level, lives, shield, and respawn logic.
*/
const Tank = require('./Tank');
const {
TANK_TYPE,
TANK_CONFIG,
FIRE_LEVEL,
MAX_BULLETS_BY_LEVEL,
DIRECTION,
DEFAULT_LIVES,
SHIELD_DURATION,
INVINCIBLE_BLINK_INTERVAL,
TILE_SIZE,
MAP_OFFSET_X,
MAP_OFFSET_Y,
} = require('../base/GameGlobal');
class PlayerTank extends Tank {
/**
* @param {object} params
* @param {number} params.col - Spawn grid column.
* @param {number} params.row - Spawn grid row.
*/
constructor(params) {
const cfg = TANK_CONFIG[TANK_TYPE.PLAYER];
const spawnX = MAP_OFFSET_X + params.col * TILE_SIZE + TILE_SIZE / 2;
const spawnY = MAP_OFFSET_Y + params.row * TILE_SIZE + TILE_SIZE / 2;
super({
x: spawnX,
y: spawnY,
speed: cfg.speed,
hp: cfg.hp,
color: cfg.color,
size: cfg.size,
direction: DIRECTION.UP,
});
this.type = TANK_TYPE.PLAYER;
this.spawnCol = params.col;
this.spawnRow = params.row;
// Skin colors (reserved for future use)
this._skinColors = null;
// Fire level system
this.fireLevel = FIRE_LEVEL.LV1;
// Lives
this.lives = DEFAULT_LIVES;
// Shield (invincibility)
this._shieldTimer = 0; // ms remaining
this._shieldBlink = false;
this._blinkTimer = 0;
// Active bullets count (managed externally)
this.activeBullets = 0;
// Respawn invincibility (short shield on spawn)
this._respawnShieldDuration = 3000; // 3 seconds on respawn
}
/**
* Update player tank state.
* @param {number} dt - Delta time in seconds.
*/
update(dt) {
if (!this.alive) return;
// Shield timer
if (this._shieldTimer > 0) {
this._shieldTimer -= dt * 1000;
this._blinkTimer += dt * 1000;
if (this._blinkTimer >= INVINCIBLE_BLINK_INTERVAL) {
this._blinkTimer = 0;
this._shieldBlink = !this._shieldBlink;
}
if (this._shieldTimer <= 0) {
this._shieldTimer = 0;
this._shieldBlink = false;
}
}
}
/**
* Override takeDamage to check shield.
* @param {number} amount
* @returns {boolean} Whether destroyed.
*/
takeDamage(amount = 1) {
if (this._shieldTimer > 0) return false; // invincible
return super.takeDamage(amount);
}
/**
* Handle player death: lose a life and respawn, or game over.
* @returns {boolean} True if player has lives remaining and respawned.
*/
die() {
this.alive = false;
this.lives--;
if (this.lives > 0) {
this.respawn();
return true;
}
return false; // game over
}
/**
* Respawn at the spawn point with temporary invincibility.
*/
respawn() {
this.x = MAP_OFFSET_X + this.spawnCol * TILE_SIZE + TILE_SIZE / 2;
this.y = MAP_OFFSET_Y + this.spawnRow * TILE_SIZE + TILE_SIZE / 2;
this.direction = DIRECTION.UP;
this.hp = 1;
this.alive = true;
this.visible = true;
this.fireLevel = FIRE_LEVEL.LV1;
this.activeBullets = 0;
// Temporary shield on respawn
this.activateShield(this._respawnShieldDuration);
}
/**
* Activate shield (invincibility).
* @param {number} duration - Duration in ms.
*/
activateShield(duration) {
this._shieldTimer = duration;
this._blinkTimer = 0;
this._shieldBlink = false;
}
/**
* Upgrade fire level.
*/
upgradeFireLevel() {
if (this.fireLevel < FIRE_LEVEL.LV3) {
this.fireLevel++;
}
}
/**
* Add a life.
*/
addLife() {
this.lives++;
}
/**
* Check if the player can fire (based on active bullets and fire level).
* @returns {boolean}
*/
canFire() {
return this.alive && this.activeBullets < MAX_BULLETS_BY_LEVEL[this.fireLevel];
}
/**
* Whether the bullet should break steel.
* @returns {boolean}
*/
canBreakSteel() {
return this.fireLevel >= FIRE_LEVEL.LV3;
}
/**
* Render player tank with shield effect.
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx) {
if (!this.alive) return;
// Call base render
super.render(ctx);
// Draw shield effect
if (this._shieldTimer > 0 && this._shieldBlink) {
ctx.save();
ctx.strokeStyle = '#00FFFF';
ctx.lineWidth = 2;
ctx.globalAlpha = 0.6;
ctx.beginPath();
ctx.arc(this.x, this.y, this.halfSize + 4, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
}
/** Whether the player is currently shielded. */
get isShielded() {
return this._shieldTimer > 0;
}
/** Get max bullets allowed on screen. */
get maxBullets() {
return MAX_BULLETS_BY_LEVEL[this.fireLevel];
}
}
module.exports = PlayerTank;